趋近智
让我们将变分量子算法的理论付诸实践。在本节中,我们将使用 Python 和 Pennylane 库实现一个变分量子分类器 (VQC)。这个动手练习结合了本章前面讨论的想法:设计参数化量子电路 (PQC)、根据测量结果定义合适的成本函数,以及运用基于梯度的优化技术来训练电路参数以完成分类任务。
我们的目标是训练一个量子电路,使其能对二维特征空间中属于两个不同类别的点进行分类。我们将通过迭代调整 PQC 参数来运用变分原理,以使量化分类错误的成本函数达到最小。
首先,确保您已安装所需的库。您主要需要 Pennylane 来进行量子计算,以及 NumPy 来进行数值操作。Scikit-learn 对于生成样本数据以及可能的数据预处理很有用。
# 必要导入
import pennylane as qml
from pennylane import numpy as np # 使用 PennyLane 封装的 NumPy
import matplotlib.pyplot as plt
from sklearn.datasets import make_moons
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
# 生成一个合成数据集
X, y = make_moons(n_samples=100, noise=0.15, random_state=42)
# 对特征进行缩放以提升性能
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# 为方便一些成本函数处理,将标签从 {0, 1} 转换为 {-1, 1}
y_signed = 2 * y - 1
# 划分为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(
X_scaled, y_signed, test_size=0.3, random_state=42
)
print(f"Number of training samples: {len(X_train)}")
print(f"Number of testing samples: {len(X_test)}")
print(f"Data shape: {X_train.shape}") # 针对 2 个特征,形状应为 (n_samples, 2)
我们使用 make_moons 来生成一个非线性可分数据集,这提供了一个中等难度的任务,适合展示 VQC 的效用。数据经过缩放,标签被转换为 y∈{−1,1},这简化了将测量结果(通常在 [-1, 1] 范围内)与目标标签对齐的操作。
经过特征缩放后的合成“月亮”数据集。这两个类别明显是非线性可分的。
我们需要将经典数据点 x=(x1,x2) 编码为量子态。如第二章所述,存在多种策略。在这里,我们将使用角度编码,将每个特征映射到特定门的旋转角度。由于我们有 2 个特征,我们将使用 2 个量子位。
num_qubits = 2
def data_encoding_circuit(features):
"""将 2 个特征编码为 2 个量子位的状态。"""
qml.AngleEmbedding(features, wires=range(num_qubits), rotation='Y')
# 如果需要,您可以在此处添加纠缠,例如:qml.CNOT(wires=[0, 1])
# 但目前我们保持编码简单。
此函数使用 qml.AngleEmbedding 对每个量子位施加 RY(ϕ) 旋转,其中 ϕ 是对应的缩放特征值。这是一种常用且相对简单的编码方法。
现在,我们设计核心 PQC 或 ansatz,我们将对其参数 θ 进行优化。我们将使用一个包含单量子位旋转层和纠缠 CNOT 门构成的结构。这遵循了前面讨论的 PQC 设计策略(第 4.2 节)。
num_layers = 3 # ansatz 中的层数
def ansatz(params):
"""参数化量子电路 (ansatz)。"""
for l in range(num_layers):
# 单量子位旋转层(参数化)
for i in range(num_qubits):
qml.RX(params[l, i, 0], wires=i)
qml.RZ(params[l, i, 1], wires=i)
# 纠缠门层
for i in range(num_qubits - 1):
qml.CNOT(wires=[i, i+1])
# 如果需要更多纠缠,可在最后一个和第一个量子位之间添加一个 CNOT
if num_qubits > 1:
qml.CNOT(wires=[num_qubits-1, 0])
这个 ansatz 由 num_layers 个模块组成。每个模块对所有量子位施加参数化的 RX 和 RZ 旋转,然后是一系列 CNOT 门以引入纠缠。params 数组的形状将取决于 num_layers、num_qubits 以及每层每个门参数的数量(此处为每层每个量子位 2 个参数)。
我们将数据编码和 ansatz 结合成一个完整的量子电路。测量结果将是第一个量子位上泡利-Z算符的期望值 ⟨σz(0)⟩。该值介于 -1 和 1 之间,与我们的标签 y∈{−1,1} 自然吻合。
我们定义一个量子设备(此处为模拟器),并创建一个 QNode,它封装了量子电路逻辑。
# 选择一个量子设备(模拟器)
dev = qml.device("default.qubit", wires=num_qubits)
@qml.qnode(dev)
def quantum_classifier_circuit(params, features):
"""完整量子电路:编码 + ansatz + 测量。"""
data_encoding_circuit(features)
ansatz(params)
# 测量第一个量子位上泡利 Z 的期望值
return qml.expval(qml.PauliZ(0))
接下来,我们定义成本函数(第 4.3 节)。对于标签为 {−1,1} 且预测结果 p∈[−1,1] 的二分类问题,常用的选择是平方损失:L(θ)=N1∑i=1N(yi−pi(θ))2,其中 pi(θ) 是量子电路对输入 xi 在参数 θ 下的输出。
def square_loss(labels, predictions):
"""计算均方损失。"""
return np.mean((labels - predictions)**2)
def cost_function(params, features_batch, labels_batch):
"""待最小化的成本函数。"""
predictions = [quantum_classifier_circuit(params, f) for f in features_batch]
return square_loss(labels_batch, predictions)
这个成本函数接收一批特征和标签,使用当前 params 为每个特征向量通过 quantum_classifier_circuit 计算预测值,然后计算均方误差。
我们需要一个优化器来根据成本函数的梯度更新电路参数 θ。Pennylane 可以与 Adam 等优化器(第 4.5 节)结合使用,并且默认情况下,对于兼容的门,它会自动使用参数偏移规则(第 4.4 节)等方法计算梯度。
# 随机初始化参数
param_shape = (num_layers, num_qubits, 2) # 由我们的 ansatz 结构定义
initial_params = np.random.uniform(low=0, high=2 * np.pi, size=param_shape)
# 选择一个优化器
optimizer = qml.AdamOptimizer(stepsize=0.05)
# 训练参数
epochs = 30
batch_size = 10
# 训练循环
params = initial_params
cost_history = []
print("开始训练...")
for epoch in range(epochs):
# 创建批次
permutation = np.random.permutation(len(X_train))
X_train_perm = X_train[permutation]
y_train_perm = y_train[permutation]
batch_costs = []
for i in range(0, len(X_train), batch_size):
X_batch = X_train_perm[i : i + batch_size]
y_batch = y_train_perm[i : i + batch_size]
# 梯度下降步骤
params, current_cost = optimizer.step_and_cost(
lambda p: cost_function(p, X_batch, y_batch), params
)
batch_costs.append(current_cost)
epoch_cost = np.mean(batch_costs)
cost_history.append(epoch_cost)
# 可选:在训练期间计算训练集上的准确率
predictions_train = [np.sign(quantum_classifier_circuit(params, f)) for f in X_train]
accuracy_train = np.mean(predictions_train == y_train)
print(f"Epoch {epoch+1}/{epochs} - Cost: {epoch_cost:.4f} - Train Accuracy: {accuracy_train:.4f}")
print("训练完成。")
在这个循环中,我们遍历训练轮次 (epochs),打乱数据,分批处理,并使用优化器的 step_and_cost 方法。该方法隐含地计算成本及其相对于 params 的梯度(在幕后使用参数偏移规则),并根据 Adam 优化算法更新 params。我们记录每个训练轮次的成本。
训练结束后,我们评估 VQC 在未见过测试集上的性能。我们使用最终训练好的参数 (params) 来预测测试数据的标签并计算准确率。
# 在测试集上进行预测
predictions_test = [np.sign(quantum_classifier_circuit(params, f)) for f in X_test]
# 计算测试准确率
accuracy_test = np.mean(predictions_test == y_test)
print(f"\nTest Set Accuracy: {accuracy_test:.4f}")
# 可选:绘制成本历史曲线
plt.figure(figsize=(8, 4))
plt.plot(range(1, epochs + 1), cost_history)
plt.xlabel("Epoch")
plt.ylabel("Cost (Mean Squared Loss)")
plt.title("VQC Training Cost History")
plt.grid(True, linestyle='--', alpha=0.6)
plt.show()
示例训练成本曲线,显示了均方损失随训练轮次的减少情况。
我们还可以可视化分类器学习到的决策边界。这包括创建一个覆盖特征空间点的网格,使用训练好的 VQC 预测每个点的类别,并绘制结果。
# 为绘制决策边界创建网格
x_min, x_max = X_scaled[:, 0].min() - 0.5, X_scaled[:, 0].max() + 0.5
y_min, y_max = X_scaled[:, 1].min() - 0.5, X_scaled[:, 1].max() + 0.5
xx, yy = np.meshgrid(np.arange(x_min, x_max, 0.05),
np.arange(y_min, y_max, 0.05))
# 预测网格中每个点的类别
grid_points = np.c_[xx.ravel(), yy.ravel()]
Z = np.array([quantum_classifier_circuit(params, p) for p in grid_points])
Z = Z.reshape(xx.shape) # 预测值(-1 到 1)
# 绘制决策边界和数据点
plt.figure(figsize=(8, 6))
contour = plt.contourf(xx, yy, Z, levels=np.linspace(-1, 1, 3), cmap='coolwarm', alpha=0.8)
plt.colorbar(contour, ticks=[-1, 0, 1])
# 绘制训练数据
plt.scatter(X_train[:, 0], X_train[:, 1], c=y_train, cmap='coolwarm', edgecolors='k', marker='o', s=50, label='Train Data')
# 绘制测试数据
plt.scatter(X_test[:, 0], X_test[:, 1], c=y_test, cmap='coolwarm', edgecolors='k', marker='^', s=60, label='Test Data')
plt.xlabel("Feature 1 (Scaled)")
plt.ylabel("Feature 2 (Scaled)")
plt.title("VQC Decision Boundary and Data")
plt.legend()
plt.grid(True, linestyle='--', alpha=0.3)
plt.show()
生成的图显示了 VQC 如何根据学习到的参数划分特征空间。
本次实践练习展示了构建和训练变分量子分类器的端到端过程。我们成功实现了数据编码,设计了 PQC ansatz,定义了基于量子测量的成本函数,并使用带有参数偏移规则的梯度下降进行优化。
所达到的准确率很大程度上取决于数据集的复杂性、所选 ansatz 的表达能力(层数、量子位连接性)、编码策略、优化器设置以及训练轮次数量。
可能进行的下一步:
qml.StronglyEntanglingLayers)。注意更深电路中出现“贫瘠高原”(第 4.7 节)的可能性。qml.QNGOptimizer(量子自然梯度,第 4.6 节)等优化器,尽管其计算量可能更大。比较收敛速度和最终准确率。此实现为理解 VQA 提供了扎实的根基。通过修改 ansatz、优化器或成本函数等组成部分,您可以研究这些强大混合算法的设计空间。
这部分内容有帮助吗?
© 2026 ApX Machine Learning用心打造