# 量子神经网络:示例 接下来,我们通过一个最小的可运行示例,完整地展现一个量子神经网络(Quantum Neural Network, QNN)从定义电路、训练到可视化的全过程。示例任务与正文相同:拟合一维函数 $y = \sin(x)$,其中 $x \in [0, 2\pi]$。文末我们还会给出这个小电路输出表达式的解析推导,说明它为什么有能力精确拟合 $\sin(x)$。 :::{admonition} 本课知识点 :class: tip 1. **[三层拟设电路与 QNode](#ansatz-qnode)**——能写出编码层、模型层、测量层各自的门序列,并解释 QNode 如何把设备、电路与测量绑定成一个可调用函数。 2. **[训练循环与参数平移梯度](#training-loop)**——能列出训练循环的训练数据、损失函数、参数初始化与优化器四个要素,并说明 Adam 如何借助参数平移法则得到解析梯度并更新参数。 3. **[拟合结果可视化与输出区间](#fit-visualization)**——能在更细的网格上计算预测值并绘制拟合曲线,并解释 $\langle Z_0\rangle$ 天然落在 $[-1,1]$ 使模型无需输出缩放即可匹配 $\sin(x)$ 的幅度。 4. **[训练前后的线路可视化](#circuit-visualization)**——能用 `qml.draw` 与 `qml.draw_mpl` 输出指定参数下的电路图,并比较两种绘图方式的用途。 5. **[测量化简与无效参数](#measurement-simplification)**——能证明 $Z_0$ 与 CNOT 及第 1 个量子比特上的旋转门对易,从而推出 $\langle Z_0\rangle$ 只依赖 $\theta_0,\theta_1$,并解释 $\theta_2,\theta_3$ 梯度恒为零的现象。 6. **[布洛赫球推导](#bloch-derivation)**——能用布洛赫球上的三次旋转推导输出公式 $\langle Z_0\rangle = -\sin x\,\sin\theta_1 + \cos x\,\cos\theta_0\cos\theta_1$。 7. **[精确拟合条件与周期性](#exact-fit-condition)**——能验证 $\theta_1 = 3\pi/2$ 时输出与 $\sin x$ 严格相等,并解释零损失全局最优可达与 $2\pi$ 周期性对训练的含义。 ::: (ansatz-qnode)= ## 1. 准备:设备、拟设电路与 QNode 我们使用两个量子比特的默认状态向量模拟器。电路分为三层:编码层把经典数据 $x$ 作为角度编码到第 0 个量子比特上;模型层由可学习参数控制的旋转门和一个 CNOT 组成;测量层返回第 0 个量子比特上 PauliZ 的期望值。 ```python import pennylane as qml from pennylane import numpy as np import matplotlib.pyplot as plt # 设备:2 个量子比特的默认模拟器 dev = qml.device("default.qubit", wires=2) def qnn_ansatz(params, x): """编码层 + 模型层 (Ansatz)""" # 编码层:把经典数据 x 作为角度编码到第 0 个量子比特 qml.RY(x, wires=0) # 模型层:由可学习参数 params 控制的旋转门与纠缠门 qml.RX(params[0], wires=0) qml.RY(params[1], wires=0) qml.CNOT(wires=[0, 1]) qml.RX(params[2], wires=1) qml.RY(params[3], wires=1) @qml.qnode(dev) def quantum_neural_net(params, x): qnn_ansatz(params, x) # 测量层:返回第 0 个量子比特上 PauliZ 的期望值 return qml.expval(qml.PauliZ(0)) def square_loss(labels, predictions): """均方误差损失""" return np.mean((labels - predictions) ** 2) ``` (training-loop)= ## 2. 训练 我们在 $[0, 2\pi]$ 上取 20 个训练点,随机初始化 4 个可学习参数,然后用 Adam 优化器训练 50 轮。每一步优化器都会通过参数平移法则计算解析梯度;同时我们把每轮更新后的参数快照保存下来,便于稍后回看电路。 ```python # 训练数据:在 [0, 2π] 上取 20 个点 X_train = np.linspace(0, 2 * np.pi, 20) Y_train = np.sin(X_train) # 随机初始化 QNN 的 4 个可学习参数 num_params = 4 params = np.random.uniform(0, 2 * np.pi, num_params, requires_grad=True) # 优化器:Adam,学习率 0.1 opt = qml.AdamOptimizer(stepsize=0.1) # 保存参数历史 params_history = [] epochs = 50 for i in range(epochs): # 优化器需要一个"只接受参数、返回损失"的函数 def cost_fn(p): predictions = np.array([quantum_neural_net(p, x) for x in X_train]) return square_loss(Y_train, predictions) # step_and_cost 内部用参数平移法则计算解析梯度并更新参数 params, cost = opt.step_and_cost(cost_fn, params) params_history.append(np.array(params, dtype=float)) if (i + 1) % 10 == 0: print(f"Epoch {i+1:2d}: Cost = {cost:.6f}") print("\n训练完成! 最终参数:", params) print("一共保存的参数快照数量:", len(params_history)) ``` 运行这段代码,你会看到损失值随迭代逐渐下降。在多数随机初始化下,损失会降到接近零——这个现象的原因在第 5 节中给出解析解释:存在使损失严格为零的参数组合。 (fit-visualization)= ## 3. 可视化拟合结果 训练结束后,我们在更细的网格上检验模型的拟合效果。 ```python # 在更细的网格上检验拟合效果 X_test = np.linspace(0, 2 * np.pi, 100) predictions_test = [quantum_neural_net(params, x) for x in X_test] plt.figure() plt.plot(X_train, Y_train, 'bo', label="Training data") plt.plot(X_test, predictions_test, 'r-', label="QNN predictions") plt.legend() plt.title("QNN Fit to sin(x)") plt.show() ``` 红色曲线是 QNN 的预测。由于电路输出 $\langle Z_0\rangle$ 天然落在 $[-1, 1]$ 区间内,与 $\sin(x)$ 的值域一致,模型无需任何输出缩放即可匹配目标函数的幅度。 (circuit-visualization)= ## 4. 线路可视化 下面我们单独定义一个结构相同的 QNode,用 PennyLane 自带的绘图工具查看训练前后的电路。 ```python # 单独为可视化定义一个 QNode(结构相同) vis_dev = qml.device("default.qubit", wires=2) @qml.qnode(vis_dev) def vis_circuit(params, x): qnn_ansatz(params, x) return qml.expval(qml.PauliZ(0)) def show_circuit_ascii(params, x): drawer = qml.draw(vis_circuit) print(drawer(params, x)) def show_circuit_mpl(params, x): fig, ax = qml.draw_mpl(vis_circuit)(params, x) ax.set_title("QNN Circuit") plt.tight_layout() plt.show() # 示例:用最终参数画线路 x_example = X_train[0] print("\nASCII 线路图:") show_circuit_ascii(params, x_example) print("\n显示 matplotlib 线路图...") show_circuit_mpl(params, x_example) # 示例:查看第 1 个 epoch 更新后的参数下的线路 first_epoch_params = params_history[0] print("\n第 1 个 epoch 的 ASCII 线路图:") show_circuit_ascii(first_epoch_params, x_example) ``` ## 5. 为什么这个小电路足以拟合 sin(x):解析验证 这一节我们把电路的输出显式地算出来。结论是:输出只依赖参数 $\theta_0, \theta_1$,且恰为 $\sin x$ 与 $\cos x$ 的线性组合;取 $\theta_1 = 3\pi/2$ 时,它与 $\sin x$ 严格相等。因此,零损失的全局最优解是可达的,训练确实"有解可学"。 (measurement-simplification)= ### 5.1 化简测量 我们说明 CNOT 以及作用在第 1 个量子比特上的 $R_x(\theta_2)$、$R_y(\theta_3)$ 都不影响 $\langle Z_0 \rangle$。首先,作用在不同量子比特上的算符显然对易,故 $R_x(\theta_2)$、$R_y(\theta_3)$ 与 $Z_0 = Z\otimes I$ 对易。其次,对 $\mathrm{CNOT} = |0\rangle\langle 0|\otimes I + |1\rangle\langle 1|\otimes X$,利用 $Z$ 是对角的且 $Z|0\rangle\langle 0| = |0\rangle\langle 0|$、$Z|1\rangle\langle 1| = -|1\rangle\langle 1|$,可得 $$ (Z\otimes I)\,\mathrm{CNOT} = |0\rangle\langle 0|\otimes I - |1\rangle\langle 1|\otimes X = \mathrm{CNOT}\,(Z\otimes I), $$ 即 $Z_0$ 与 CNOT 也对易。于是在海森堡绘景中把测量算符 $Z_0$ 向后移过这三个门,$\langle Z_0\rangle$ 就等于它在 CNOT 之前的值;而那时第 1 个量子比特仍处于 $|0\rangle$,其归一化因子为 $1$。最终 $$ \langle Z_0 \rangle = \langle\varphi| Z |\varphi\rangle, \qquad |\varphi\rangle = R_y(\theta_1)\, R_x(\theta_0)\, R_y(x)\,|0\rangle. $$ 这一化简也解释了一个现象:$\theta_2$ 和 $\theta_3$ 完全不出现在输出中,因此它们对损失的梯度恒为零,训练过程中这两个参数基本停留在初始值附近。 (bloch-derivation)= ### 5.2 用布洛赫球跟踪态矢量 $|0\rangle$ 的布洛赫矢量为 $(0, 0, 1)$。算符 $R_{\hat n}(\alpha) = e^{-i\alpha \hat n\cdot\vec\sigma/2}$ 把布洛赫矢量绕 $\hat n$ 轴旋转角 $\alpha$(右手法则),我们依次应用三次旋转: 1. 绕 $y$ 轴转 $x$:$(0,0,1) \mapsto (\sin x,\ 0,\ \cos x)$; 2. 绕 $x$ 轴转 $\theta_0$($y' = y\cos\theta_0 - z\sin\theta_0$,$z' = y\sin\theta_0 + z\cos\theta_0$):$(\sin x,\ 0,\ \cos x) \mapsto (\sin x,\ -\cos x\sin\theta_0,\ \cos x\cos\theta_0)$; 3. 绕 $y$ 轴转 $\theta_1$($z'' = -x\sin\theta_1 + z\cos\theta_1$)。 布洛赫矢量的 $z$ 分量就是 $\langle Z\rangle$,因此 $$ \langle Z_0 \rangle = -\sin x\,\sin\theta_1 + \cos x\,\cos\theta_0\cos\theta_1. $$ (exact-fit-condition)= ### 5.3 验证模型能精确表示 $\sin x$ 上式是 $\sin x$ 与 $\cos x$ 的线性组合,取 $\theta_1 = \frac{3\pi}{2}$(此时 $\sin\theta_1 = -1$、$\cos\theta_1 = 0$),得 $$ \langle Z_0 \rangle = \sin x, $$ 与目标函数严格相等,且 $\theta_0$(以及 $\theta_2, \theta_3$)可以任取。所以在参数空间中存在损失严格为零的全局最优点,训练的任务只是把 $\theta_1$ 学到 $\frac{3\pi}{2}$(模 $2\pi$)。此外,模型输出关于 $x$ 以 $2\pi$ 为周期,与 $\sin x$ 的周期一致,这使得训练区间上学到的拟合可以自然地周期延拓。 作为交叉验证,我们代入两个具体数值检验上式。取 $\theta_0 = 0$、$\theta_1 = \frac{\pi}{2}$、$x = \frac{\pi}{2}$:公式给出 $-\sin\frac{\pi}{2}\sin\frac{\pi}{2} + \cos\frac{\pi}{2}\cos 0\cos\frac{\pi}{2} = -1$;直接演算态矢量也得到 $\langle Z_0\rangle = -1$(态先后经 $R_y(\frac{\pi}{2})$、$R_x(0)$、$R_y(\frac{\pi}{2})$ 后布洛赫矢量转到 $(0, 0, -1)$)。再取 $\theta_0 = \frac{\pi}{2}$、$\theta_1 = 0$:公式给出 $\cos\frac{\pi}{2}\cos 0\cos x = 0$,即输出恒为零;直接演算可知此时态的布洛赫矢量始终落在赤道面上,$\langle Z_0\rangle = 0$。两条路径完全一致。 ## 练习题 **练习 1【三层拟设电路与 QNode】**(→ [第 1 节](#ansatz-qnode)) 1. 基础:指出 `qnn_ansatz` 与 `quantum_neural_net` 中的哪些语句分别实现编码层、模型层与测量层,并写出测量层返回的物理量。 2. 进阶:把编码层改为 `qml.RX(x, wires=0)`、其余保持不变:在运行训练之前判断,输出是否仍必然落在 $[-1,1]$ 内?给出理由。 > 提示:$\langle Z_0\rangle$ 是 $Z$ 的期望值,其上下界由 $Z$ 的本征值 $\pm 1$ 决定,与量子态如何制备无关。 **练习 2【训练循环与参数平移梯度】**(→ [第 2 节](#training-loop)) 1. 基础:列出本例训练循环的四个要素及各自的代码对应物:训练数据、损失函数、参数初始化、优化器。 2. 进阶:对单个训练点写出损失 $(y - f(x;\theta))^2$ 关于预测值 $f$ 的导数,并用链式法则说明它如何与电路参数的梯度(由参数平移法则给出)连接起来。 > 提示:$\frac{\partial L}{\partial\theta_j} = \frac{\partial L}{\partial f}\cdot\frac{\partial f}{\partial\theta_j}$。 **练习 3【拟合结果可视化与输出区间】**(→ [第 3 节](#fit-visualization)) 1. 基础:说明为什么训练只用 20 个点,检验却要在 100 个点的更细网格上进行。 2. 进阶:把目标函数改为 $y = 3\sin(x)$ 且不加任何输出缩放:判断零损失是否可达,并给出一种修复思路。 > 提示:$\langle Z_0\rangle \in [-1,1]$,而 $3\sin(x)$ 在 $x = \pi/2$ 处取到 $3$。 **练习 4【训练前后的线路可视化】**(→ [第 4 节](#circuit-visualization)) 1. 基础:分别说出 `show_circuit_ascii` 与 `show_circuit_mpl` 调用的 PennyLane 绘图函数,并指出各自适合的查看场景。 2. 进阶:比较用 `params_history[0]` 与最终 `params` 画出的 ASCII 线路图:门的种类和排列会改变吗?解释原因。 > 提示:拟设的结构由代码固定,可学习参数只改变各旋转门的角度。 **练习 5【测量化简与无效参数】**(→ [5.1 节](#measurement-simplification)) 1. 基础:由 $Z|0\rangle\langle 0| = |0\rangle\langle 0|$ 与 $Z|1\rangle\langle 1| = -|1\rangle\langle 1|$ 出发,复现正文 $(Z\otimes I)\,\mathrm{CNOT} = \mathrm{CNOT}\,(Z\otimes I)$ 的推导。 2. 进阶:证明对作用在第 1 个量子比特上的任意算符 $A$ 都有 $(Z\otimes I)(I\otimes A) = (I\otimes A)(Z\otimes I)$,并据此解释为什么 $\theta_2, \theta_3$ 对损失的梯度恒为零、训练中基本停留在初始值附近。 > 提示:利用 $(A\otimes B)(C\otimes D) = (AC)\otimes(BD)$,并注意 $R_x(\theta_2)$、$R_y(\theta_3)$ 都形如 $I\otimes(\cdot)$。 **练习 6【布洛赫球推导】**(→ [5.2 节](#bloch-derivation)) 1. 基础:取 $x = \pi$、$\theta_0 = \frac{\pi}{2}$、$\theta_1 = 0$,依次写出三次旋转后的布洛赫矢量,并给出 $\langle Z_0\rangle$。 2. 进阶:从 $R_y(x)$ 之后的矢量 $(\sin x,\ 0,\ \cos x)$ 出发,推导绕 $x$ 轴转 $\theta_0$ 的分量公式 $y' = y\cos\theta_0 - z\sin\theta_0$、$z' = y\sin\theta_0 + z\cos\theta_0$,并代入验证正文的第二步结果。 > 提示:绕 $x$ 轴的旋转保持 $x$ 分量不变,只混合 $y$、$z$ 分量。 **练习 7【精确拟合条件与周期性】**(→ [5.3 节](#exact-fit-condition)) 1. 基础:利用 $\sin\frac{3\pi}{2} = -1$、$\cos\frac{3\pi}{2} = 0$,把 $\theta_1 = \frac{3\pi}{2}$ 代入输出公式,验证输出严格等于 $\sin x$。 2. 进阶:求一组 $(\theta_0, \theta_1)$ 使输出恒等于 $\cos x$,并用布洛赫球上的旋转解释这组参数为什么有效。 > 提示:在输出公式中令 $\sin\theta_1 = 0$ 且 $\cos\theta_0\cos\theta_1 = 1$;几何上让后两次旋转退化为恒等。