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