量子机器学习这几个字,在我刚开始接触的时候,最大的障碍并不是数学,而是“想跑通一个实验却不知道从哪一行代码开始”。你搜到的资料要么是纯理论推导,要么直接甩一个几十步的 notebook,中间缺了太多“为什么这样做”的环节。这篇文章我想认真拆一个能直接跑起来的项目:用 Qiskit 实现量子支持向量机(QSVM),在经典 Iris 鸢尾花数据集上完成二分类。从环境搭建到特征映射,从量子核函数到最终的精度评估,全部用代码说话,并且把我在实际跑实验时踩过的坑一并写出来。
适合的读者:你已经写过 Python,用过 sklearn,了解 SVM 的基本概念,但对量子计算只有模糊印象。读完之后,你可以亲手把这条链路跑通,再根据自己的数据替换特征映射和分类器。量子计算真正难的不是那些好看的公式,而是文档里不会告诉你的版本差异和噪声带来的各种诡异现象——这部分,我会讲得比官方示例更直白。
1. 先对齐战场:QSVM 到底在解决什么问题
1.1 经典SVM的核函数困境
做分类任务时,SVM 是我用得很顺手的一个模型。它的核心思想很清晰:如果数据在当前维度下线性不可分,就通过一个核函数把它映射到更高维的空间,在高维空间找一个能把两类样本分开的超平面。
问题是:核函数的选择依赖经验。RBF 核、多项式核、sigmoid 核各有各的适用场景,同一个数据集换一个核函数,精度可能从 0.8 跳到 0.95。你很难事先判断哪个核最合适,很多时候只能靠交叉验证去试,本质上是碰运气。
更麻烦的是,经典核函数的表达能力是有限的。RBF 核本质上衡量的是两个样本在欧氏空间里的距离相似度,它对数据的内在结构感知比较单一。当数据存在周期性、对称性或者需要跨维度的特征交互时,经典核往往需要精心设计特征工程才能救回来。
量子机器学习提供了一个不同的思路:不要让核函数为人脑服务,而是让量子线路替我们生成特征空间。
1.2 量子核:用量子态做特征映射
量子核方法的基本逻辑是这样的:每个样本 x 不再直接喂给 SVM,而是先通过一个量子特征映射线路(feature map)编码成一个量子态 |φ(x)>,然后计算两个量子态的内积平方 |<φ(x_i)|φ(x_j)>|^2 作为核函数值。
这个值在物理上就是“态保真度”(state fidelity),量子计算机可以直接用线路测量算出来。从经典机器学习角度看,我们其实是在做一个隐式的特征映射:原始样本 x 被映射到了一个由量子比特张成的希尔伯特空间里。
量子态空间的规模非常惊人。n 个量子比特构成的态空间维度是 2^n,20 个 qubit 时就已经是百万维级别,30 个 qubit 时超过十亿维。经典核函数大多是把数据映射到有限维或者可数的无限维空间,而量子特征空间的理论容量完全不在一个量级上。
更重要的一点是,量子特征映射线路里可以加入纠缠门。纠缠意味着不同特征维度之间不再是简单的加权组合,而是产生了经典相关性难以模拟的联合分布。这是量子核与经典核在结构上的本质差异。
1.3 为什么QSVM是量子机器学习最合适的入门项目
和量子神经网络相比,QSVM 有一个非常大的优势:它把量子部分和经典部分拆得很干净。量子线路只负责计算核矩阵,训练分类器这件事还是交给 sklearn 的 SVC 来做。也就是说,你想验证量子特征映射到底有没有用,可以直接和经典核跑同一个 SVM 框架做对比,变量控制得很清晰。
我建议所有想入门量子机器学习的朋友,第一个实战项目就选 QSVM。理由有三个:第一,代码链路短,不需要自己去写梯度计算和参数更新;第二,核矩阵是中间产物,可以拿出来检查、可视化、调试;第三,它天然适合小数据集,而小数据集恰恰也是当前量子硬件能处理的规模。
2. 环境准备与第一个量子线路
2.1 安装与版本选型
跑实验之前先把环境讲清楚,因为在 Qiskit 1.0 之后,API 有过一轮比较大的变动,网上很多教程已经过时了。我用的环境是 Python 3.11,Qiskit 1.1.x,qiskit-machine-learning 0.7.x,qiskit-aer 0.15.x。
创建虚拟环境后,一次性安装所有依赖:
pip install qiskit qiskit-machine-learning qiskit-aer scikit-learn numpy这里最需要提醒的是:Qiskit 1.0 已经把BasicAer移除了。你如果在老教程里看到from qiskit import BasicAer或者from qiskit.providers.aer import AerSimulator这样的写法,可以直接跳过。现在统一使用新的 Primitive 接口,即Sampler和Estimator,后端选择上用AerSimulator或者 IBM Quantum 的SamplerV2。
版本不对的典型报错长这样:
ModuleNotFoundError: No module named 'qiskit.providers.basicaer' ImportError: cannot import name 'QuantumInstance' from 'qiskit.utils'遇到这一类错误,先别怀疑自己的代码,先检查 qiskit 版本。
2.2 第一个量子线路:验证环境
安装完成之后,跑一个最简单的 Bell 态线路来验证环境。Bell 态的意思是两个量子比特先做 Hadamard 门使第一个 qubit 进入叠加态,再用 CNOT 门把两个 qubit 纠缠起来,测量结果应该是 00 和 11 各占一半。
from qiskit import QuantumCircuit from qiskit.primitives import Sampler qc = QuantumCircuit(2) qc.h(0) qc.cx(0, 1) qc.measure_all() sampler = Sampler() result = sampler.run([qc], shots=1024).result() counts = result[0].data.meas.get_counts() print(counts)运行结果大概是:
{'00': 512, '11': 512}虽然每次跑出来的具体数字会有浮动,但 00 和 11 各占约一半这个规律是稳定的。这一步跑通,说明安装环境没问题,后面就可以放心构建更复杂的线路了。
2.3 特征映射:从经典数据到量子态
量子计算机没有办法直接读取浮点数,所以要把每个样本编码到量子态上。最直观的编码方式是角度编码:把特征缩放到一个合适的区间,然后作为旋转门的旋转角参数。
Qiskit 里最常用的就是ZZFeatureMap。它在角度编码的基础上加了对角纠缠门,让不同特征维度之间产生相互作用。构造一个两维的特征映射非常简单:
from qiskit.circuit.library import ZZFeatureMap feature_map = ZZFeatureMap(feature_dimension=2, reps=2, entanglement='full') print(feature_map.draw())你能看到这个线路包含 H 门、RZ 旋转门和 CNOT 纠缠门。其中的reps参数控制线路的重复次数,值越大,特征映射的表达能力越强,但线路越深,越容易受噪声影响。实际使用中,2 是一个比较稳妥的起点。
量子特征映射的思路可以这样理解:经典数据是低维空间里的点,量子特征映射把这些点投射到一个超高维的量子态空间,而投影的方式取决于线路结构。换一个角度说,QSVM 里的量子线路本质上是替代了经典机器学习里的“手工特征工程”。
3. 完整示例:QSVM 在 Iris 数据集上的二分类
3.1 准备数据:选特征和归一化
我用的是经典的 Iris 鸢尾花数据集,取 setosa 和 versicolor 两种花,各 50 个样本。Iris 有四个特征,全用的话特征维度有点高,对入门示例来说没必要,我选了前两个特征:花萼长度和花瓣长度。这两个特征在这个二分类任务上区分度很好。
注意,原始特征值的量纲差异很大,必须先做归一化。我习惯用MinMaxScaler把特征缩放到 0 到 π 之间。为什么不是 0 到 1?因为角度编码的旋转门输入是弧度,缩放范围选得不好会影响特征映射的表现。
import numpy as np from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.preprocessing import MinMaxScaler iris = load_iris() X = iris.data[iris.target != 2][:, [0, 2]] y = iris.target[iris.target != 2] X = MinMaxScaler(feature_range=(0, np.pi)).fit_transform(X) X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42 )这里有个我踩过的坑:如果直接把原始数据丢进ZZFeatureMap,不归一化,特征的数值范围可能远超 π 或者远小于 0。旋转门的参数在这个范围之外时,编码出的量子态差异不再直观,核矩阵会变得很奇怪。所以数据缩放这步不能省。
3.2 构建量子核并训练SVM
核心代码其实不长,分成三步:构建特征映射、基于特征映射计算量子核矩阵、把核矩阵喂给 sklearn 的 SVC。
from sklearn.svm import SVC from qiskit.circuit.library import ZZFeatureMap from qiskit.primitives import Sampler from qiskit.algorithms.state_fidelities import ComputeUncompute from qiskit_machine_learning.kernels import FidelityQuantumKernel feature_map = ZZFeatureMap(feature_dimension=2, reps=2, entanglement='full') fidelity = ComputeUncompute(sampler=Sampler(shots=1024)) kernel = FidelityQuantumKernel(fidelity=fidelity, feature_map=feature_map) kernel_matrix_train = kernel.evaluate(x_train=X_train) svc = SVC(kernel='precomputed') svc.fit(kernel_matrix_train, y_train) kernel_matrix_test = kernel.evaluate(x_train=X_train, x_test=X_test) y_pred = svc.predict(kernel_matrix_test) accuracy = np.mean(y_pred == y_test) print(f"QSVM Accuracy: {accuracy:.3f}")在我本机用 Aer 模拟器跑,输出大约是:
QSVM Accuracy: 0.967这个效果在 Iris 二分类上已经很不错了。作为对比,直接用 RBF 核跑同一个训练集测试集,精度大概在 0.95 上下。也就是说,一个未经任何超参调优的量子核分类器,已经和经典 RBF 核打平甚至略好。这还只是一个两比特的量子线路,能够说明量子特征映射在这种情况下确实捕获到了数据里的有效结构。
3.3 代码里每个参数到底在干什么
Sampler(shots=1024)中的 shots 是测量次数。量子线路每次运行得到的是一个概率分布,shots 越多,估计出的内积越准。我试过 shots=100 的时候,核矩阵的数值波动很大,精度也忽高忽低。1024 是个性价比比较高的值。
ZZFeatureMap里的entanglement='full'表示两两比特之间都建立纠缠。在二特征的情况下,full 和 linear 差别不大;但如果特征维度超过 4,full 会导致线路非常深,噪声影响急剧放大,此时我建议先用linear跑通再说。
FidelityQuantumKernel是 qiskit-machine-learning 库里的封装,它负责把两个量子态的内积转换成核矩阵。ComputeUncompute是计算保真度的算法实现,采用“先正变换再逆变换”的线路设计,把量子态的内积转化为测量概率。
3.4 核矩阵长什么样
代码跑完,建议把核矩阵打印出来看一眼:
print(kernel_matrix_train[:5, :5])你会看到它的主对角线元素接近 1,而不同类别样本之间的核函数值明显偏小。这其实就相当于在对角线上形成了“同类相聚、异类相斥”的结构,SVM 拿它做分类自然会容易不少。如果你发现核矩阵对角线都不接近 1,那多半是特征映射或者数据缩放出了问题。
4. 量子核为什么可能比经典核更能打
4.1 测量内积是量子硬件的天然优势
量子核函数有一个很优雅的性质:内积平方可以通过物理测量直接获得。具体来说,把两个对应的量子态线路正接再反接,然后测量全零态的概率,这个概率值就等于内积模长的平方。也就是说,量子计算机在“算核函数”这件事上不需要像经典那样进行繁重的浮点运算,只需要跑线路、做统计。
当然,现在量子硬件的误差远大于经典浮点运算的误差,所以这个优势目前在真机上还不能兑现。但从原理上说,量子核函数的计算路径是自然的、直接的,不是对某个经典函数的模拟。
4.2 量子特征空间的表达能力不同在哪
经典 RBF 核的特征映射是基于距离的,它对应的特征空间里,每个维度可以理解为某个中心点附近的“软指示”。这种表达方式对局部结构敏感,但对全局的结构关系比较弱。
量子特征映射则不同。以 ZZFeatureMap 为例,它把数据编码成多个量子比特上的旋转角,再通过纠缠门把这些角度进行非线性耦合。在量子态空间里,不同特征维度之间的相关性不局限于加权组合,而可以形成类似“特征A为高值时特征B必须为低值”的复杂约束。这类模式在一些特殊结构的数据里可能比经典核更容易被线性超平面分开。
这个差异很像选房子:经典核给你一个位置到市中心距离的评分,量子核给你一个综合考虑了朝向、楼层、周边设施和采光的联合评分——后者更复杂,但能不能用得好,取决于有没有吃透数据本身的结构。
4.3 一个简单的对比实验
我把同一个数据集换回 RBF 核跑了一遍:
svc_rbf = SVC(kernel='rbf', C=1.0, gamma='scale') svc_rbf.fit(X_train, y_train) y_pred_rbf = svc_rbf.predict(X_test) print(np.mean(y_pred_rbf == y_test))结果在 0.95 左右。这个对比不是为了证明谁更好,因为 Iris 数据太简单,两者的差异完全在噪声范围内。但这个对比的意义在于,它验证了量子核在小型分类任务上不会明显输给经典核,同时给你一个基线参考。
4.4 清醒一点:量子核不是万能的
我得泼一盆冷水。在目前这个阶段,QSVM 在大多数经典数据集上并不会真正超越 RBF 核。量子特征空间的维度虽然指数级增长,但量子线路的容量、测量次数限制和真机噪声都会把优势抵消掉。量子核的研究更多是在探索“什么样的数据模式是经典核难以表达、而量子核天然匹配的”,比如具备某种对称性的数据、周期性数据、和图结构相关的数据。
所以,我的建议是:如果你想做量子机器学习研究,QSVM 是理解原理的最佳支架;如果你想拿它解决实际生产问题,现阶段大概率还是经典模型更可靠。把量子核当成一个“核函数生成器”,在数据规模小、特征维度低、你有耐心调线路结构的前提下,可以认真试试。
5. 模拟器到真机:噪声会怎样毁掉实验
5.1 模拟器上的好结果有欺骗性
Aer 模拟器默认是理想无噪声的,所以核矩阵计算得非常干净,分类器精度高在预期之中。但我必须提醒你:这套代码如果原封不动提交到 IBM 真机上,结果会很难看。原因很直白:真机上有测量误差、门操作误差、退相干,线路执行 1024 次测量,每一次的量子态都已经偏离了理想状态。
一个很典型的现象是,量子核矩阵会变得“糊”。理想情况下,同类样本的核值应该明显大于异类样本;引入噪声后,所有核值都向 0.5 附近靠拢,SVM 的决策边界就失去了有效信息。这不是代码逻辑的问题,是物理层面的误差。
5.2 在Aer里模拟噪声实验
不一定要花真机的时间去体验这种差距,你可以在 Aer 里手动加噪声模型。比如 1% 的退极化噪声已经足以让精度掉一截:
from qiskit_aer import AerSimulator from qiskit_aer.noise import NoiseModel, depolarizing_error noise_model = NoiseModel() error = depolarizing_error(0.01, 1) noise_model.add_all_qubit_quantum_error(error, ['rz', 'ry', 'rx']) backend = AerSimulator(noise_model=noise_model) sampler_noisy = SamplerV2(backend=backend) fidelity_noisy = ComputeUncompute(sampler=sampler_noisy) kernel_noisy = FidelityQuantumKernel( fidelity=fidelity_noisy, feature_map=feature_map ) kernel_matrix_train_noisy = kernel_noisy.evaluate(x_train=X_train) svc_noisy = SVC(kernel='precomputed') svc_noisy.fit(kernel_matrix_train_noisy, y_train) kernel_matrix_test_noisy = kernel_noisy.evaluate(x_train=X_train, x_test=X_test) y_pred_noisy = svc_noisy.predict(kernel_matrix_test_noisy) print(f"Noisy QSVM Accuracy: {np.mean(y_pred_noisy == y_test):.3f}")在我本机的模拟测试里,无噪声精度 0.967,加了 1% 退极化噪声之后掉到了 0.80 左右。噪声再大一点,直接掉到 0.55,和随机猜测差不多了。这种对幅度对做量子机器学习的人来说必须刻在脑子里:线路越深,噪声影响越大;特征维度越高,线路越深,精度崩得越快。
5.3 真机调试的三个可行对策
第一个对策是增加 shots。噪声的本质是测量结果带有随机偏差,通过增加 shots 让统计平均逼近真实概率分布,可以一定程度上救回核矩阵质量。我把 shots 提高到 8192 之后,噪声下的精度回升了不少,但运行时间也成倍增加。
第二个对策是缩短线路。减少reps,把ZZFeatureMap的纠缠改成线性,或者选择更浅的量子线路。线路短了,噪声对结果的影响指数级下降。精度不一定变差,但稳定性显著提高。
第三个对策是用 IBM Quantum 的弹性运行时服务。Qiskit 1.x 里可以通过qiskit-ibm-runtime访问带错误缓解的SamplerV2,它会在后端替你做一些测量误差纠正。真机上跑之前,建议先在 Aer 里加噪声模拟器验证一下自己的算法对噪声的耐受度,再上真机排队。
5.4 我的实测感受
说句实在话,在真机上跑这种小实验的体验算不上愉快。排队时间几个小时甚至半天都是常态,真正运行只花几秒钟,但拿到手的精度往往还不如模拟器加 1% 噪声的结果。我个人现在的工作流是:所有算法验证都在 Aer 上完成,只有论文级的实验或者需要真实硬件数据做演示时,才去申请真机额度。这个观念能帮你节省大量时间和精力。
6. 进阶:变分量子分类器(VQC)和我踩过的坑
6.1 QSVM的局限性
QSVM 虽然好上手,但它有一个天然的短板:核矩阵的大小是样本数的平方。训练样本 100 个,核矩阵就是 100×100;训练样本 5000 个,就是 2500 万的矩阵,算得又慢又费量子资源。所以 QSVM 只适合数据规模很小的实验场景。
如果你想走得更远一点,应该了解变分量子分类器(VQC)。它的思路是把量子线路当作一个可训练的模型:输入特征经过特征映射后,再进入一组带参数的旋转门和纠缠门,最后测量输出并和标签计算损失,用优化器调节参数。
6.2 VQC代码示例
一个最简的 VQC 在 Iris 二分类上的实现大概是这样的:
from qiskit.circuit.library import TwoLocal, ZZFeatureMap from qiskit.algorithms.optimizers import COBYLA from qiskit.primitives import Sampler from qiskit_machine_learning.algorithms import VQC feature_map = ZZFeatureMap(feature_dimension=2, reps=1) ansatz = TwoLocal(2, ['ry', 'cz'], reps=1, entanglement='full') vqc = VQC( feature_map=feature_map, ansatz=ansatz, optimizer=COBYLA(maxiter=200), sampler=Sampler(shots=1024), ) vqc.fit(X_train, y_train) score = vqc.score(X_test, y_test) print(f"VQC Accuracy: {score:.3f}")VQC 的训练过程明显比 QSVM 慢,因为每一步参数更新都要重新跑多次线路。但它的优势是参数量可以控制,并且在理想模拟器上往往能达到比 QSVM 更紧凑的模型表现。如果你想在量子机器学习上做更深入的实战,VQC 是绕不开的下一步。
6.3 我的三个踩坑实录
第一个坑是 API 版本问题,我开头提过。Qiskit 1.0 大量旧接口失效,网上教程要么过时要么互相矛盾。我现在养成了习惯:任何量子机器学习教程,先看它是否基于 Qiskit 1.x 和 qiskit-machine-learning 0.7 及以上版本,不满足的直接放弃。
第二个坑是数据归一化区间。我最早直接用 MinMaxScaler 缩放到 0 到 1,然后把原始值喂给 ZZFeatureMap。结果核矩阵对角线都不太好,分类精度一直上不去。后来看文档才发现ZZFeatureMap对输入范围比较敏感,一般推荐在 [-π, π] 或者 [0, π] 之间,我改成 0 到 π 之后问题立刻消失。
第三个坑是 shots 太少导致核矩阵不再是正定矩阵。有一次我把 shots 调到 100,SVC 直接报错,错误信息提示核矩阵含有非有限值。量子测量本身有随机性,shots 太少时核函数估计的方差很大,导致矩阵出现奇异值。类似问题在生产环境里更难排查,因为报错来得莫名其妙。
6.4 项目选型建议
我把这次实验的几个关键对比整理成了表格,方便你选择适合自己项目的路线:
| 方案 | 量子部分 | 训练方式 | 优点 | 适合场景 |
|---|---|---|---|---|
| QSVM + 量子核 | 只有特征映射 | 经典SVC加载核矩阵 | 链路封闭、容易调试 | 小规模分类实验、对比研究 |
| VQC 变分分类器 | 特征映射+参数化线路 | 量子线路端到端训练 | 参数化可控、灵活 | 小数据、探索性研究 |
| 经典SVM/RBF | 无 | 纯经典 | 稳定、快、工具成熟 | 大多数实际生产任务 |
我的核心建议是:如果你只是想理解量子机器学习,QSVM 绝对是第一站;如果你在调研量子模型的表达能力,VQC 更值得深挖;如果你有真实业务需求,暂时还是以经典模型为主,量子线路最多当作特征增强模块去测试。
最后分享一个小技巧:我在比较不同特征映射对核矩阵的影响时,习惯先打印核矩阵的热力图,而不是直接看精度。热力图能直观展示同类和异类的数值间隔,如果间隔不明显,调线路结构往往比调 SVM 参数更有效。这个习惯帮我少走了很多弯路。