:從Jupyter Notebook到可訓(xùn)練模型)
簡介這份資源是量子LSTM的入門示例工程面向具備Python與深度學(xué)習(xí)基礎(chǔ)、希望了解量子機器學(xué)習(xí)實現(xiàn)方式的學(xué)習(xí)者與研究者幫助讀者在PyTorch經(jīng)典LSTM與量子LSTM之間建立直觀對比。壓縮包共10個文件以py腳本、png訓(xùn)練曲線圖、txt依賴清單為主另含一份ipynb交互式筆記本與md說明文檔整體約84KB體積輕便便于快速上手。資源圍繞詞性標(biāo)注任務(wù)展開提供經(jīng)典LSTM與量子LSTM兩條訓(xùn)練路徑并附訓(xùn)練歷史與損失對比圖方便讀者觀察兩者在收斂速度與精度上的差異。依賴清單區(qū)分了不同量子計算后端便于按需安裝環(huán)境。目前已有423人學(xué)習(xí)下載適合作為量子機器學(xué)習(xí)方向的練手項目幫助讀者理解量子線路與循環(huán)網(wǎng)絡(luò)結(jié)合的基本思路并在此基礎(chǔ)上開展自己的實驗與調(diào)參。1. 量子LSTM示例一個能跑起來的Jupyter Notebook項目第一次看到“量子LSTM”這個詞我下意識覺得是論文里的概念圖直到把這份 Jupyter Notebook 拉下來跑通才發(fā)現(xiàn)它其實是一個能直接上手改的工程示例。項目把量子計算里常用的變分線路和經(jīng)典 LSTM 單元拼在一起用 Notebook 的形式把數(shù)據(jù)準(zhǔn)備、模型定義、訓(xùn)練循環(huán)、結(jié)果可視化全流程攤開給你看。適合兩類人一類是想把量子層塞進(jìn)現(xiàn)有深度學(xué)習(xí)流程的算法工程師另一類是想找個能跑通的量子機器學(xué)習(xí) Demo 來改的學(xué)生或研究者。整個項目不依賴真實量子硬件本地模擬器就能跑對剛接觸量子機器學(xué)習(xí)的人來說門檻壓得比較低。2. 環(huán)境準(zhǔn)備與依賴安裝從零把 Notebook 跑起來2.1 依賴清單與版本選擇這個項目本質(zhì)是一個 Jupyter Notebook核心依賴分三塊量子計算框架、經(jīng)典深度學(xué)習(xí)框架、以及 Notebook 運行環(huán)境。常見做法是用qiskit或pennylane做量子線路模擬用torch或tensorflow做經(jīng)典 LSTM 部分。我一般會先建一個干凈的虛擬環(huán)境避免和系統(tǒng)里已有的包打架。# 創(chuàng)建虛擬環(huán)境Python 版本建議 3.9 到 3.11 python -m venv qlstm_env source qlstm_env/bin/activate # Windows 用 qlstm_env\Scripts\activate # 安裝核心依賴版本按項目實際 import 調(diào)整 pip install jupyter notebook pip install qiskit0.45.0 pip install torch2.1.0 pip install numpy pandas matplotlib scikit-learn這里把qiskit和torch的版本寫死是有原因的量子模擬器和自動微分框架之間的接口在不同版本里變動不小版本對不上很容易在反向傳播時報維度錯誤。如果你用的是pennylane那經(jīng)典框架可以換成torch或tensorflowpennylane對兩者的自動微分支持都比較成熟。提示如果安裝qiskit時編譯報錯先升級pip和setuptools再單獨裝qiskit-aer模擬器后端。2.2 啟動 Notebook 與首次運行檢查依賴裝完后在項目根目錄啟動 Jupyterjupyter notebook瀏覽器打開后找到主 Notebook 文件從上到下依次執(zhí)行單元格。第一次運行建議不要直接“全部運行”而是逐塊執(zhí)行觀察每個單元格的輸出。重點看三個地方量子線路的繪制輸出是否正常、模型參數(shù)量是否打印出來、訓(xùn)練損失是否在下降。# 檢查量子模擬器是否可用 from qiskit import Aer simulator Aer.get_backend(aer_simulator) print(simulator)如果這一步能打印出模擬器對象說明量子計算環(huán)境沒問題。如果報No module named qiskit_aer說明模擬器后端沒裝補一句pip install qiskit-aer即可。這個檢查步驟很多人會跳過結(jié)果跑到訓(xùn)練階段才發(fā)現(xiàn)量子層根本沒執(zhí)行白白浪費調(diào)試時間。3. 核心模塊拆解量子層與LSTM怎么拼在一起3.1 量子線路定義與參數(shù)化項目里最核心的部分是量子層的定義。它用變分量子線路VQC作為可訓(xùn)練層線路里的旋轉(zhuǎn)門角度就是需要優(yōu)化的參數(shù)。下面是一個典型的參數(shù)化量子線路寫法from qiskit import QuantumCircuit, ParameterVector from qiskit.circuit import Parameter def create_quantum_circuit(n_qubits, n_layers): 創(chuàng)建參數(shù)化量子線路 n_qubits: 量子比特數(shù)對應(yīng)特征維度 n_layers: 變分層數(shù)層數(shù)越多表達(dá)能力越強 params ParameterVector(θ, n_qubits * n_layers * 2) qc QuantumCircuit(n_qubits) param_idx 0 for layer in range(n_layers): # 旋轉(zhuǎn)層每個量子比特上加RY和RZ門 for qubit in range(n_qubits): qc.ry(params[param_idx], qubit) param_idx 1 qc.rz(params[param_idx], qubit) param_idx 1 # 糾纏層用CNOT門制造量子比特間的關(guān)聯(lián) for qubit in range(n_qubits - 1): qc.cx(qubit, qubit 1) return qc, params這段代碼的邏輯是每一層先對每個量子比特做獨立的旋轉(zhuǎn)操作再用 CNOT 門把相鄰量子比特糾纏起來。旋轉(zhuǎn)門負(fù)責(zé)引入可訓(xùn)練參數(shù)糾纏門負(fù)責(zé)讓量子比特之間產(chǎn)生關(guān)聯(lián)這樣線路才能表達(dá)復(fù)雜的函數(shù)關(guān)系。n_qubits通常等于輸入特征的維度n_layers控制模型容量層數(shù)太少欠擬合層數(shù)太多在模擬器上跑得慢且容易過擬合。參數(shù)說明ParameterVector創(chuàng)建的是一個符號向量實際訓(xùn)練時會被替換成具體數(shù)值。param_idx用來按順序取參數(shù)保證每個門拿到不同的角度。如果你把n_qubits設(shè)成 4、n_layers設(shè)成 2那總參數(shù)量就是 4×2×216 個。3.2 量子層與LSTM的拼接方式量子層本身不處理時序信息所以項目把它嵌在 LSTM 的輸入或輸出端。常見做法有兩種一種是把量子層放在 LSTM 前面做特征變換另一種是放在 LSTM 后面做輸出映射。下面這種是放在輸入端的寫法import torch import torch.nn as nn from qiskit import QuantumCircuit from qiskit.circuit import ParameterVector class QuantumLayer(nn.Module): def __init__(self, n_qubits, n_layers): super().__init__() self.n_qubits n_qubits self.qc, self.params create_quantum_circuit(n_qubits, n_layers) # 把量子參數(shù)注冊為PyTorch可訓(xùn)練參數(shù) self.theta nn.Parameter(torch.randn(len(self.params))) def forward(self, x): # x shape: (batch, n_qubits) # 這里用模擬器逐樣本計算期望值 # 實際項目中會做批量化或使用torch接口 return self._run_circuit(x) def _run_circuit(self, x): # 綁定輸入和參數(shù)執(zhí)行線路取Z方向期望值 # 具體實現(xiàn)依賴qiskit的backend.run pass class QLSTM(nn.Module): def __init__(self, input_dim, hidden_dim, n_qubits, n_layers): super().__init__() self.quantum QuantumLayer(n_qubits, n_layers) self.lstm nn.LSTM(input_dim, hidden_dim, batch_firstTrue) self.fc nn.Linear(hidden_dim, 1) def forward(self, x): # x shape: (batch, seq_len, input_dim) q_out self.quantum(x) # 量子特征變換 lstm_out, _ self.lstm(q_out) return self.fc(lstm_out[:, -1, :])這段代碼的關(guān)鍵在于QuantumLayer把量子線路的參數(shù)包裝成了nn.Parameter這樣 PyTorch 的優(yōu)化器就能更新它們。forward里先跑量子線路拿到期望值再送進(jìn) LSTM 做時序建模最后全連接層輸出預(yù)測。實際項目中量子線路的執(zhí)行通常會用qiskit的TorchConnector或者pennylane的qml.qnn.TorchLayer來打通自動微分手寫_run_circuit容易在梯度回傳時翻車。注意量子模擬器不支持批量并行時訓(xùn)練會非常慢。常見做法是把 batch size 設(shè)小一點或者用qiskit的StatevectorSampler做向量化。3.3 訓(xùn)練循環(huán)與損失函數(shù)訓(xùn)練部分和普通 PyTorch 項目差別不大核心是優(yōu)化器要同時更新量子參數(shù)和經(jīng)典參數(shù)model QLSTM(input_dim4, hidden_dim16, n_qubits4, n_layers2) optimizer torch.optim.Adam(model.parameters(), lr0.01) criterion nn.MSELoss() for epoch in range(50): model.train() optimizer.zero_grad() output model(train_x) loss criterion(output, train_y) loss.backward() # 梯度會回傳到量子參數(shù) optimizer.step() if epoch % 10 0: print(fEpoch {epoch}, Loss: {loss.item():.4f})這里model.parameters()會自動包含量子層里的theta所以不需要單獨寫量子優(yōu)化器。loss.backward()能不能成功回傳到量子參數(shù)取決于量子層是否用了支持自動微分的接口。如果用的是手寫的模擬器調(diào)用梯度會在量子層斷掉訓(xùn)練就變成了只更新 LSTM 部分量子層等于隨機初始化后固定不動這是新手最容易踩的坑。4. 避坑與常見問題跑不通時先查這幾處4.1 量子模擬器報錯“backend not found”現(xiàn)象執(zhí)行量子線路時拋出QiskitBackendNotFoundError或類似提示。原因通常是qiskit-aer沒裝或者Aer.get_backend的參數(shù)名寫錯。解決方法是先pip install qiskit-aer然后確認(rèn)后端名稱是aer_simulator而不是statevector_simulator后者在新版里已經(jīng)改名。4.2 梯度回傳失敗導(dǎo)致量子層不更新現(xiàn)象訓(xùn)練損失下降很慢打印量子層參數(shù)發(fā)現(xiàn)數(shù)值幾乎沒變。原因是量子線路的執(zhí)行沒有接入自動微分圖梯度在量子層斷開了。解決方法是改用qiskit的TorchConnector或pennylane的TorchLayer它們會把量子線路包裝成 PyTorch 可微分模塊。如果堅持手寫需要自己實現(xiàn)參數(shù)偏移規(guī)則parameter-shift rule來計算梯度。4.3 Notebook 單元格執(zhí)行順序混亂現(xiàn)象單獨運行某個單元格時報NameError提示變量未定義。原因是 Jupyter 的變量作用域依賴執(zhí)行順序跳著執(zhí)行會導(dǎo)致前面的變量沒初始化。解決方法是每次修改代碼后從第一個單元格重新執(zhí)行一遍或者用Kernel - Restart Run All。這個坑幾乎每個用 Notebook 做項目的人都踩過血淚經(jīng)驗就是別偷懶。4.4 訓(xùn)練集和測試集劃分不合理現(xiàn)象模型在訓(xùn)練集上損失很低但測試集上表現(xiàn)很差。原因是數(shù)據(jù)量小的時候隨機劃分可能導(dǎo)致分布偏移。解決方法是固定隨機種子并且用train_test_split的stratify參數(shù)保持類別比例。如果數(shù)據(jù)本身有時序依賴不能隨機打亂要按時間順序切分。4.5 量子比特數(shù)設(shè)置過大導(dǎo)致內(nèi)存溢出現(xiàn)象把n_qubits調(diào)到 10 以上后模擬器直接卡死或報內(nèi)存錯誤。原因是量子態(tài)向量的大小是 2 的 n 次方10 個量子比特就是 1024 維15 個就是 32768 維內(nèi)存消耗指數(shù)級增長。解決方法是在模擬器上把量子比特數(shù)控制在 8 以內(nèi)超過這個規(guī)模需要考慮用真實量子硬件或張量網(wǎng)絡(luò)模擬器。5. 二次開發(fā)與改造思路從示例到自己的項目5.1 替換數(shù)據(jù)集與調(diào)整輸入維度這個示例默認(rèn)用的可能是隨機生成的數(shù)據(jù)或某個小型公開數(shù)據(jù)集。要改成自己的數(shù)據(jù)第一步是確認(rèn)輸入特征維度然后同步修改n_qubits和 LSTM 的input_dim。比如你的數(shù)據(jù)每個時間步有 6 個特征那就把n_qubits設(shè)成 6input_dim也設(shè)成 6。如果特征維度不是 2 的冪可以補零或者用主成分分析降到合適維度。# 假設(shè)你的數(shù)據(jù)是 (樣本數(shù), 時間步, 特征數(shù)) import numpy as np data np.load(your_data.npy) n_features data.shape[2] n_qubits n_features # 量子比特數(shù)等于特征數(shù)改造時注意量子線路的輸入需要歸一化到旋轉(zhuǎn)門能接受的范圍通常是[-π, π]。常見做法是用MinMaxScaler把數(shù)據(jù)縮放到這個區(qū)間否則旋轉(zhuǎn)門的角度會周期性重復(fù)導(dǎo)致信息丟失。5.2 換用量子框架或經(jīng)典框架項目用的是qiskittorch的組合但你可以換成pennylanetensorflow。pennylane的優(yōu)勢是它對自動微分的支持更統(tǒng)一量子層和經(jīng)典層的拼接更順滑。下面是一個pennylane版本的量子層import pennylane as qml from pennylane import numpy as pnp n_qubits 4 dev qml.device(default.qubit, wiresn_qubits) qml.qnode(dev, interfacetorch) def quantum_circuit(inputs, weights): qml.templates.AngleEmbedding(inputs, wiresrange(n_qubits)) qml.templates.StronglyEntanglingLayers(weights, wiresrange(n_qubits)) return [qml.expval(qml.PauliZ(i)) for i in range(n_qubits)] weight_shapes {weights: (2, n_qubits, 3)} qlayer qml.qnn.TorchLayer(quantum_circuit, weight_shapes)這段代碼用AngleEmbedding把經(jīng)典數(shù)據(jù)編碼到量子態(tài)用StronglyEntanglingLayers做可訓(xùn)練變換最后取每個量子比特的 Z 方向期望值作為輸出。TorchLayer會自動處理梯度和參數(shù)注冊比手寫省事很多。換框架時主要改量子層的定義LSTM 和訓(xùn)練循環(huán)基本不用動。5.3 驗證改造是否成功的三個檢查點改完代碼后別急著跑完整訓(xùn)練。先做三個快速驗證第一打印模型參數(shù)量確認(rèn)量子參數(shù)和經(jīng)典參數(shù)都在model.parameters()里第二用一個 batch 的數(shù)據(jù)做前向傳播看輸出形狀是否符合預(yù)期第三執(zhí)行一次loss.backward()檢查量子參數(shù)的.grad是否非空。這三個檢查點過了再跑完整訓(xùn)練基本不會出大問題。# 檢查點示例 model QLSTM(input_dim4, hidden_dim16, n_qubits4, n_layers2) print(f總參數(shù)量: {sum(p.numel() for p in model.parameters())}) dummy_x torch.randn(2, 10, 4) # batch2, seq_len10, features4 dummy_y torch.randn(2, 1) out model(dummy_x) print(f輸出形狀: {out.shape}) loss nn.MSELoss()(out, dummy_y) loss.backward() for name, param in model.named_parameters(): if theta in name: print(f量子參數(shù)梯度非空: {param.grad is not None})從那以后我每次改量子層結(jié)構(gòu)都強制走一遍這三個檢查點確認(rèn)梯度鏈路沒斷再開訓(xùn)練。希望這個示例能幫你把量子 LSTM 從 Notebook 跑到自己的項目里。本文還有配套的精品資源點擊獲取