趋近智
我们将理论付诸实践。我们讨论了噪声如何困扰近期量子设备,并介绍了错误缓解技术。现在,你将运用此类技术之一,即零噪声外推(ZNE),以提升在模拟带噪声后端上运行的变分量子分类器(VQC)的性能。
本次动手练习假定你熟悉以下内容:
我们将使用scikit-learn的make_moons数据集处理一个标准二分类问题。我们的目标是训练一个VQC来区分这两个类别。
make_moons数据集。ZZFeatureMap或Pennylane中的角度嵌入层。RealAmplitudes或Pennylane的StronglyEntanglingLayers,重复几次。首先,在理想(无噪声)量子模拟器上实现并训练你的VQC。这为你所选架构和超参数下的最佳性能提供了一个参考点。
# 伪代码示例(使用Pennylane风格的语法)
import pennylane as qml
from pennylane import numpy as np
from sklearn.datasets import make_moons
from sklearn.model_selection import train_test_split
# --- 数据 ---
X, y = make_moons(n_samples=100, noise=0.1, random_state=42)
y_one_hot = np.array([[1, 0] if label == 0 else [0, 1] for label in y]) # 针对某些损失函数的格式
X_train, X_test, y_train, y_test = train_test_split(X, y_one_hot, test_size=0.3, random_state=42)
# --- VQC设置 ---
n_qubits = 2
dev_ideal = qml.device("default.qubit", wires=n_qubits)
def feature_map(x):
# 例子:简单角度嵌入
qml.AngleEmbedding(x, wires=range(n_qubits))
def ansatz(params):
# 例子:基础变分层
qml.StronglyEntanglingLayers(params, wires=range(n_qubits))
@qml.qnode(dev_ideal)
def circuit(params, x):
feature_map(x)
ansatz(params)
# 测量量子位0上泡利Z的期望值
return qml.expval(qml.PauliZ(0))
def cost_fn(params, X_batch, y_batch):
predictions = [circuit(params, x) for x in X_batch]
# 将期望值(-1到1)映射到概率(0到1)
probs = (np.stack(predictions) + 1) / 2
# 简单的MSE损失用于演示(其他损失如交叉熵也很常见)
# 注意:确保y_batch的格式与你选择的损失函数正确匹配 [样本数] 或 [样本数, 类别数]
# 假设y_batch代表类别1的概率(需要根据实际标签格式进行调整)
loss = np.mean((probs - y_batch[:, 1])**2)
return loss
# --- 训练(理想情况)---
# 初始化参数(例如,使用StronglyEntanglingLayers.shape)
init_params = np.random.uniform(0, 2 * np.pi, (1, n_qubits, 3)) # 示例形状
opt = qml.AdamOptimizer(stepsize=0.1)
params = init_params
print("Training VQC (Ideal)...")
for iteration in range(50):
# 批处理示例(实践中使用合适的批处理)
params, cost = opt.step_and_cost(lambda p: cost_fn(p, X_train, y_train), params)
if iteration % 10 == 0:
print(f"Iteration {iteration}, Cost: {cost:.4f}")
# --- 评估(理想情况)---
test_predictions_ideal = [(circuit(params, x) + 1) / 2 for x in X_test]
# 将概率转换为类别标签(0或1)
test_labels_ideal = (np.array(test_predictions_ideal) > 0.5).astype(int)
y_test_labels = np.argmax(y_test, axis=1) # 将one-hot编码转换回标签
accuracy_ideal = np.mean(test_labels_ideal == y_test_labels)
print(f"\nIdeal VQC Test Accuracy: {accuracy_ideal:.4f}")
你应观察到无噪声模拟器上训练收敛性和测试准确率都相当不错。记录此准确率作为你的基线。
现在,我们来模拟噪声的影响。我们将使用一个基本噪声模型,例如去极化噪声,在每个CNOT门之后应用。许多量子库提供了构建和应用噪声模型到模拟器的工具。
# 示例(Qiskit Aer风格的噪声模型定义)
from qiskit_aer.noise import NoiseModel, depolarizing_error
# 定义去极化错误概率
error_prob = 0.01 # 1%错误概率
# 为双量子位门创建去极化错误通道
depol_error = depolarizing_error(error_prob, 2)
# 构建噪声模型:将错误应用于CNOT门
noise_model = NoiseModel()
noise_model.add_all_qubit_quantum_error(depol_error, ["cx"]) # 应用于'cx'门
print(f"\nNoise Model:\n{noise_model}")
# --- 使用噪声模拟 ---
# 重新运行VQC训练和评估,但配置模拟器
# 以使用'noise_model'。具体机制取决于库。
# 示例(Pennylane与Qiskit插件):
# dev_noisy = qml.device("qiskit.aer", wires=n_qubits, noise_model=noise_model)
# 重新定义qnode以使用dev_noisy:
# @qml.qnode(dev_noisy)
# def circuit_noisy(params, x): ... (相同的线路定义)
# 使用circuit_noisy和相同的成本函数/优化器重新训练。
# --- 训练(有噪声情况)---
# 重置参数或使用理想参数作为起点
params_noisy = init_params
opt_noisy = qml.AdamOptimizer(stepsize=0.1) # 如有需要,重置优化器状态
print("\nTraining VQC (Noisy)...")
# 假设circuit_noisy和dev_noisy已如上定义
# 你需要重新定义cost_fn以使用circuit_noisy
# def cost_fn_noisy(params, X_batch, y_batch): ... 使用circuit_noisy ...
# for iteration in range(50):
# params_noisy, cost_noisy = opt_noisy.step_and_cost(lambda p: cost_fn_noisy(p, X_train, y_train), params_noisy)
# if iteration % 10 == 0:
# print(f"Iteration {iteration}, Cost: {cost_noisy:.4f}")
# --- 评估(有噪声情况)---
# 使用训练好的params_noisy和有噪声的线路
# test_predictions_noisy = [(circuit_noisy(params_noisy, x) + 1) / 2 for x in X_test]
# test_labels_noisy = (np.array(test_predictions_noisy) > 0.5).astype(int)
# accuracy_noisy = np.mean(test_labels_noisy == y_test_labels)
# print(f"\nNoisy VQC Test Accuracy: {accuracy_noisy:.4f}")
# 预期有噪声结果的占位符 - 请替换为实际模拟结果
accuracy_noisy = accuracy_ideal * 0.7 # 模拟显著的性能下降
print(f"\n(Simulated) Noisy VQC Test Accuracy: {accuracy_noisy:.4f}")
正如预期的那样,噪声显著降低了性能。优化器可能难以收敛,最终测试准确率可能会远低于理想基线。
ZNE通过有意增加线路执行中的噪声,在多个噪声水平下测量得到的期望值,然后外推回零噪声限制来发挥作用。
一种放大与特定门(如CNOT)相关的噪声的常见方法是使用酉折叠。对于一个门 U,我们可以将其替换为 U(U†U)k。如果 U 有噪声,每次应用都会引入更多噪声。噪声比例因子大致为 c=2k+1。
我们对多个比例因子(例如,c=1,3,5)运行线路,以得到有噪声的期望值 E1,E3,E5。然后,我们对这些点 (c,Ec) 拟合一个模型(例如,线性、二次、指数),并外推以找到 c=0 时的估计值。
# ZNE的实现
# Mitiq (https://mitiq.readthedocs.io/) 等库可以自动化此过程。
# 这是手动实现的思路:
def get_noisy_expectation(params, x, noise_model, scale_factor):
"""
在缩放噪声下运行线路的函数。
实践中,这包括修改线路(门折叠)
或指示模拟器/硬件后端缩放噪声。
"""
# 这高度依赖于框架和后端。
# 占位符:假设我们可以直接模拟缩放后的噪声
# 实际上,你会使用门折叠或特定的后端选项。
# 示例:通过调整error_prob模拟缩放(简单近似)
effective_prob = noise_model.get_error("cx").probabilities[1] * scale_factor
scaled_error = depolarizing_error(min(effective_prob, 1.0), 2) # 将概率限制在1
scaled_noise_model = NoiseModel()
scaled_noise_model.add_all_qubit_quantum_error(scaled_error, ["cx"])
# 假设存在一个接受噪声模型的函数`run_noisy_circuit`
noisy_expval = run_noisy_circuit(params, x, scaled_noise_model)
return noisy_expval
def zne_expectation(params, x, noise_model, scale_factors=[1, 3, 5]):
"""计算期望值的ZNE估计。"""
noisy_values = []
for c in scale_factors:
# 此函数需要根据实际库的功能进行修改
expval = get_noisy_expectation(params, x, noise_model, scale_factor=c)
noisy_values.append(expval)
# 执行外推(例如,使用numpy进行线性拟合)
coeffs = np.polyfit(scale_factors, noisy_values, 1) # 线性拟合:ax + b
zero_noise_estimate = coeffs[1] # c=0时的值(截距)
# 更高级:Richardson外推法、指数拟合等。
# zero_noise_estimate = extrapolate(scale_factors, noisy_values, method='richardson')
return zero_noise_estimate
# --- 修改VQC以使用ZNE ---
# 核心改变是将期望值计算封装在
# 成本函数和最终预测步骤中。
# 示例:重新定义成本函数以使用zne_expectation
# def cost_fn_zne(params, X_batch, y_batch):
# predictions = [zne_expectation(params, x, noise_model) for x in X_batch]
# probs = (np.stack(predictions) + 1) / 2
# loss = np.mean((probs - y_batch[:, 1])**2)
# return loss
# --- 训练(有噪声+ZNE)---
# 重置参数和优化器
params_zne = init_params
opt_zne = qml.AdamOptimizer(stepsize=0.1) # 使用相同的优化器设置
print("\nTraining VQC (Noisy + ZNE)...")
# for iteration in range(50): # 注意:ZNE会显著增加运行时间!
# params_zne, cost_zne = opt_zne.step_and_cost(lambda p: cost_fn_zne(p, X_train, y_train), params_zne)
# if iteration % 10 == 0:
# print(f"Iteration {iteration}, Cost: {cost_zne:.4f}")
# --- 评估(有噪声+ZNE)---
# 使用训练好的params_zne和zne_expectation函数
# test_predictions_zne = [(zne_expectation(params_zne, x, noise_model) + 1) / 2 for x in X_test]
# test_labels_zne = (np.array(test_predictions_zne) > 0.5).astype(int)
# accuracy_zne = np.mean(test_labels_zne == y_test_labels)
# print(f"\nNoisy VQC + ZNE Test Accuracy: {accuracy_zne:.4f}")
# 预期ZNE结果的占位符 - 请替换为实际模拟结果
accuracy_zne = accuracy_ideal * 0.9 # 模拟显著的恢复
print(f"\n(Simulated) Noisy VQC + ZNE Test Accuracy: {accuracy_zne:.4f}")
注意: Mitiq等库提供了简化量子执行函数封装并自动处理折叠和外推的功能(例如,
mitiq.zne.execute_with_zne)。在实践中强烈推荐使用此类库。
现在,比较测试准确率:
你应该观察到ZNE缓解后的准确率显著优于纯粹有噪声的准确率,理想情况下可恢复大部分因噪声损失的性能。可视化此比较。
VQC测试准确率在理想条件、模拟噪声和使用ZNE进行噪声缓解下的比较。使用了占位符值;请替换为你的实际结果。
此练习展示了应用错误缓解技术的实际流程。尽管ZNE会增加开销,但它可以是一种有效工具,改善从带噪声量子硬件获得的结果,使我们更接近发挥QML算法的潜力。请记住,ZNE只是一种工具;概率错误消除(PEC)、动态解耦和纠错(长期来看)等技术提供了替代或补充方法。
这部分内容有帮助吗?
© 2026 ApX Machine Learning用心打造