ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

量子K-means聚类实战:文本嵌入、量子距离与V-measure评估

量子K-means聚类实战:文本嵌入、量子距离与V-measure评估 简介一份围绕量子K-means聚类算法QK-means的Python实现与实验笔记合集面向对量子机器学习、文本嵌入聚类应用感兴趣的研究者、算法工程师及高校学生。内容聚焦将传统K-means迁移到量子计算框架涵盖量子线路CMP电路、距离计算、自动化输出与V-measure评估并针对MTEB基准测试与自建数据集提供可复现的Notebook方案。压缩包共20个文件以14个Jupyter Notebook为主体覆盖qiskit环境搭建、CMP线路实现、数据保存与结果可视化等环节另含JSON配置、CSV数据、说明TXT、README与附赠Word资料整体仅1.06MB便于快速下载和本地运行。目前已有81人学习尤其适合具备Python基础、希望入门量子聚类或复现文本嵌入评测流程的读者。通过对照各Notebook的分步实现与注释可掌握量子态表示数据、CMP线路计算距离的核心思路并在自建数据集上用V-measure验证算法效果。1. 量子K-means聚类从理论到文本嵌入评估的完整实现量子K-means聚类并不是把K-means跑在量子计算机上的花哨噱头而是一套把经典距离计算替换成量子线路内积测量、再用V-measure闭环验证的工程实验项目。它解决三个具体问题高维文本向量做聚类的距离矩阵瓶颈、嵌入结果到底聚得好不好的量化评估、以及量子机器学习从纸面算法到可复现实现的链路补齐。适合正在做文本聚类但被算力卡住的工程师也适合想入门量子机器学习、缺一个完整项目练手的研究者。下面我用实际跑通的路子把这个项目从原理到参数、从数据到坑位拆开讲。2. 从经典距离到量子线路QK-means的算法改动与选择理由经典K-means每轮迭代要计算样本到质心的所有距离复杂度是O(nkd)。n是样本数k是聚类数d是特征维度。文本嵌入模型的输出通常是768维甚至更高十万条文本一轮迭代的距离计算量超过百亿次浮点运算这就是K-means在文本聚类场景里最主要的卡点。量子K-means的思路非常直接不改变K-means的迭代骨架只把“两个向量之间的距离”这一步换到量子线路上做。2.1 经典K-means的计算瓶颈在哪经典K-means每轮迭代分为两步分配阶段计算每个样本到每个质心的距离把样本归到最近的簇更新阶段把簇内样本均值作为新质心。分配阶段要构建一个n×k的距离矩阵单次欧氏距离在d维空间里大约要d次乘法和2d次加减法。d768时一次距离就是两千多次浮点运算n10000、k10时一轮迭代两亿多次迭代20轮就是几十亿次运算。这个规模在单机上已经能感觉到明显卡顿。传统加速手段不是没有Elkan的三角不等式剪枝、Ball Tree索引、Mini-Batch采样都能缓解压力但它们改变不了最坏情况复杂度。文本嵌入向量是典型的高维稠密向量维度灾难让KD-Tree这类空间索引在高维下基本退化成线性扫描剪枝效率远不如低维数据。所以在文本聚类场景里距离计算就是那个绕不开的瓶颈。量子路线提供了一个不同视角如果把d维向量编码进nceil(log2(d))个qubit的量子态再用量子线路同时处理整个向量的内积单次距离的理论复杂度就从O(d)降到O(poly(log d))。这就是量子机器学习介入K-means的核心动机——不换算法骨架只换距离原语。2.2 量子线路振幅编码、内积计算与距离度量QK-means的第一步是振幅编码amplitude encoding。把向量x归一化后按分量顺序填入2^n个振幅nceil(log2(d))。如果向量维度不是2的幂要在末尾补零到2^n维。编码完成后n个qubit的量子态就同时携带了d维向量的全部信息。这一步是把经典向量“装进”量子线路的前提也是工程上最容易出错的地方——忘记归一化、补零顺序写反后面的所有测量都会失真。内积计算最常用的线路是swap test。准备一个辅助比特和两个数据寄存器依次执行H门、一组受控SWAP门、再执行H门最后测量辅助比特。测量结果为0的概率与两个态的内积模方成正比精确关系是P(0)(1|φ|ψ|^2)/2。对实数向量内积模方就是余弦相似度。归一化向量之间的欧氏距离平方等于2-2φ|ψ所以从一次swap test的统计概率可以直接换算距离fidelity 2 * P(0) - 1 distance 2 - 2 * fidelity这才是QK-means的核心改动经典K-means里的距离算子被替换成“量子态内积测量经典换算”。质心更新仍然是经典算术平均值标签分配仍然是argmin。这是一个典型的量子-经典混合算法也是当前工程上最容易落地的一类方案。2.3 为什么选swap test而不是直接算欧氏距离为什么不直接写一个量子版本的欧氏距离线路第一swap test只需要两个寄存器加一个辅助比特线路深度浅对NISQ硬件友好。第二量子欧氏距离线路本质上也需要先算内积swap test就是最简的构建块。第三文本聚类场景下归一化后的余弦相似度本身就是比原始欧氏距离更合理的语义度量丢掉模长信息换来的恰好是语义空间里常用的方向相似度。这个选择也有代价。swap test一次只比较一对向量整个聚类过程要做n×k×t次测量测量成本是经典的几千倍。所以在模拟器上跑QK-means不会比经典K-means快甚至慢很多。它的价值在真实量子硬件上单次距离的log级加速潜力以及端到端流程的验证意义。理解这一点才能看懂后面的参数为什么那样调。量子K-means文献里更激进的版本会引入Grover搜索和振幅估计来加速“找最近质心”这一步典型代表是q-means那套方案。但那需要对数据结构做额外假设比如量子随机存取存储器QRAM工程门槛高。这个项目采用的swap test路线保留了K-means的经典控制流把量子部分压缩到距离原语是一个在模拟器和低噪硬件上都容易复现的折中方案。3. 文本嵌入与数据集准备MTEB基准怎么用、自建数据集怎么造QK-means的输入不是原始文本而是文本嵌入向量。嵌入这一步决定了聚类的上限向量里没有的语义信息量子计算不可能凭空补回来。所以数据集准备阶段要做两件事选一个合适的嵌入模型把文本编码成单位向量再准备一份带真实标签的小规模自建数据集用V-measure验证整个链路是通的。3.1 文本嵌入的选择从分类到聚类的通用做法我一般直接用sentence-transformers库加载一个小型多语言模型连接效率高、维度适中。encode时一定要开normalize_embeddingsTrue这不仅是聚类时的习惯更是量子振幅编码的硬要求——振幅编码的前提是向量模长为1。不开归一化量子距离算出来的值和经典的余弦相似度对不上后续所有调试都会变成玄学。from sentence_transformers import SentenceTransformer model SentenceTransformer(paraphrase-multilingual-MiniLM-L12-v2) texts [ 量子比特的退相干时间是当前硬件的主要瓶颈, 全球芯片代工产能持续紧张, 短道速滑混合接力项目成夺金热点 ] vectors model.encode(texts, normalize_embeddingsTrue) print(vectors.shape, vectors[0][:5])逻辑说明模型把每条文本映射成固定维度的稠密向量normalize_embeddingsTrue确保输出向量的二范数为1也就是放在单位球面上。打印的shape表明了每条文本的向量维数这个维数直接决定后面振幅编码需要的qubit数量。嵌入维度和qubit数量的对应关系值得记一下384维向量需要ceil(log2(384))9个qubit1024维需要10个qubit。维度增加一倍qubit数量只加1这是振幅编码的压缩红利但换来的代价是模拟器内存随2^n指数增长所以后面我会建议对大规模文本先降维。3.2 MTEB基准测试跑聚类子集、看V-measureMTEBMassive Text Embedding Benchmark是做嵌入模型体检的地方。它不是单独一个数据集而是几十个任务的集合覆盖分类、聚类、检索、语义相似度、重排等任务族。聚类这一族用的评估指标恰好就是V-measure和调整兰德指数ARI。| 任务族 | 典型数据集片段 | 主要评估指标 | | 分类 | AmazonPolarity、Imdb | Accuracy | | 聚类 | StackExchange、Arxiv | V-measure、ARI | | 语义相似度 | STSBenchmark | Spearman相关系数 | | 检索 | NQ、MIRACL | NDCG10、Recallk |在这个项目里跑MTEB的目的不是刷榜而是给嵌入模型做选型先在MTEB的聚类子集上看哪个模型的聚类分数靠前再把它换进QK-means的文本编码环节。要注意的是MTEB的评测脚本对数据采样数量、提示模板都有预设跑单任务时这些配置必须固定否则拿到手的V-measure既不能和排行榜比也不能复现。3.3 自建数据集构造、清洗与标签设计自建数据集的作用是快速验证实现正确性。MTEB的聚类数据集中英文语料居多、数据量大在量子模拟器上一轮迭代就可能跑很久自建一个3类、每类200条标题级别的小数据集几秒一轮调参和排错效率高得多。构造步骤建议这样走选3个语义差异足够大的主题例如体育、财经、科技每条文本控制在20到80个字符避免长文本稀释语义去除重复标题和特殊符号统一处理大小写文本和标签分别保存标签只在评估时使用。主题之间差异越大自建数据跑出来的V-measure越容易稳定。如果两类的样本只差一两个词嵌入距离会非常接近聚类结果就会在两类边界上抖动但它不代表QK-means有bug而是数据本身的可分性问题。这个区分意识在用V-measure评价一个算法时会省掉大量无意义的debug时间。4. 项目实现QK-means的训练流程、V-measure评估与参数对照这章是能直接抄作业的部分。整个实现分成三层数据编码层振幅编码、距离原语层swap test、聚类控制层K-means迭代。评估环节用V-measure和经典K-means做对照。代码按Python写量子部分依赖Qiskit和Aer模拟器。4.1 环境与依赖先装跑通的最小集最小依赖如下pip install qiskit qiskit-aer numpy scikit-learn sentence-transformersqiskit负责构建线路qiskit-aer提供模拟后端scikit-learn用于经典KMeans对照和指标计算sentence-transformers用于文本嵌入。如果你只在模拟器上做验证这套依赖足够了。注意先不急着升级到最新版装完固定住版本再往下走。模拟器有两个选择。statevector_simulator直接返回精确的量子态没有测量噪声适合验证算法逻辑qasm_simulator走完整的测量采样流程结果带统计波动更接近真实硬件的行为。我一般先在statevector上跑通再切到qasm看稳定性。注意statevector_simulator 没有测量噪声qasm_simulator 才有。先用 statevector 验证逻辑再用 qasm 测稳定性能省一半排查时间。4.2 数据编码与量子距离计算实现振幅编码函数负责把向量装进量子线路。代码第一件事是补零到2^n维第二件事是归一化顺序不能反——先补零再归一化才能保证分量的相对比例不被破坏。量子线路准备好一个辅助比特和两个数据寄存器辅助比特用H门进入叠加态然后对两个寄存器做受控SWAP最后再对辅助比特做H门并测量。测量结果为0的概率通过公式换算成内积再换算成距离。import numpy as np from qiskit import QuantumCircuit, Aer, execute def amplitude_encode(vec): n int(np.ceil(np.log2(len(vec)))) dim 2 ** n padded np.zeros(dim) padded[:len(vec)] vec padded padded / np.linalg.norm(padded) qc QuantumCircuit(n) qc.initialize(padded.tolist(), range(n)) return qc def swap_test_distance(a, b, shots4096): qc_a amplitude_encode(a) qc_b amplitude_encode(b) n qc_a.num_qubits qc QuantumCircuit(2 * n 1, 1) qc.h(0) qc.compose(qc_a, range(1, n 1), inplaceTrue) qc.compose(qc_b, range(n 1, 2 * n 1), inplaceTrue) for i in range(n): qc.cswap(0, i 1, n i 1) qc.h(0) qc.measure(0, 0) counts execute(qc, Aer.get_backend(qasm_simulator), shotsshots).result().get_counts() p0 counts.get(0, 0) / shots fidelity 2 * p0 - 1 return 2 - 2 * fidelity逻辑说明amplitude_encode把向量归一化到单位范数再按2^n的维度补零初始化量子线路swap_test_distance构建一个辅助比特加两个数据寄存器的线路受控SWAP把两个态的叠加信息汇聚到辅助比特上最终测量值换算出的fidelity就是归一化向量的余弦相似度距离值等于2减两倍fidelity。这个距离在数学上等价于归一化向量的欧氏距离平方。参数说明shots是采样次数4096意味着辅助比特被测量4096次p0的统计误差大约在1%量级。shots越大距离越稳但耗时线性增长。线路里n是数据维度对应的qubit数两个数据寄存器的qubit数必须一致维度不齐的两个向量要提前统一长度。4.3 聚类主循环与V-measure评估聚类主循环沿用K-means骨架初始化质心、算距离矩阵、分簇、更新质心、检查收敛。唯一的改动是把经典距离换成swap_test_distance。质心更新仍然用簇内均值但质心向量要继续喂给振幅编码如果簇内均值不在单位球面上需要重新归一化——这一点容易漏。import numpy as np from sklearn.metrics import v_measure_score from sklearn.cluster import KMeans class QKMeans: def __init__(self, n_clusters3, max_iter10, shots4096, seed42): self.n_clusters n_clusters self.max_iter max_iter self.shots shots self.seed seed self.labels_ None def fit(self, X): rng np.random.default_rng(self.seed) centers X[rng.choice(len(X), self.n_clusters, replaceFalse)] for _ in range(self.max_iter): dist np.zeros((len(X), self.n_clusters)) for i, x in enumerate(X): for j, c in enumerate(centers): dist[i, j] swap_test_distance(x, c, shotsself.shots) labels np.argmin(dist, axis1) new_centers np.array([X[labels k].mean(axis0) for k in range(self.n_clusters)]) new_centers new_centers / np.linalg.norm(new_centers, axis1, keepdimsTrue) if np.allclose(new_centers, centers, atol1e-3): break centers new_centers self.labels_ labels return self逻辑说明fit方法先随机抽n_clusters个样本做初始质心进入迭代后用swap_test_distance填距离矩阵argmin给出每个样本最近的簇。质心更新后立刻做一次归一化保证下一轮振幅编码的输入依然在单位球面上。收敛判断用质心差值小于阈值。评估环节要同时跑经典KMeans和QKMeans用V-measure对比qkm QKMeans(n_clusters3, shots4096).fit(embeddings) km KMeans(n_clusters3, n_init10, random_state42).fit(embeddings) v_q v_measure_score(true_labels, qkm.labels_) v_k v_measure_score(true_labels, km.labels_) print(fQKMeans V-measure: {v_q:.4f}) print(fKMeans V-measure: {v_k:.4f})说明V-measure是聚类结果和真实标签之间的一致性度量由同质性homogeneity和完整性completeness的调和平均而来值域0到1越接近1说明聚类出来的簇和真实类别越吻合。V-measure的公式是h1-H(C,K)/H(C)c1-H(K,C)/H(K)v2hc/(hc)。在模拟器上理想结果是QKMeans的分数接近KMeans差距在0.05以内如果差太远优先检查嵌入向量是否归一化以及shots是否太低。4.4 参数调节shots、迭代次数与维度裁剪| 参数 | 推荐值 | 说明 | | shots | 4096起步 | 低于1024距离噪声过大分类边界抖动明显 | | max_iter | 10~15 | 文本嵌入收敛很快超过15轮收益很低 | | 嵌入维度 | ≤256 | 超过256先PCA降维否则模拟器内存和测量时间爆炸 | | seed | 固定 | 量子测量有随机性固定seed才能复现实验结果 | | 模拟器 | 先statevector后qasm | 验证逻辑用无噪声后端测稳定性用qasm |经验上小数据集500条以内、维度64~128qasm模拟器一次迭代的耗时可以接受样本量到几千单轮距离矩阵的测量次数就是几千乘k建议直接降维到32~64维或者改用statevector后端。维度裁剪是模拟器场景性价比最高的提速手段PCA降到64维通常能保留文本聚类90%以上的可分性。5. QK-means避坑手册模拟器噪声、评估偏差与版本迁移量子K-means的实验结果飘忽不定很多时候不是算法错了而是模拟器采样、数据归一化、评估方式里藏着细节。我把自己跑这个项目时踩过的坑按现象、原因、解决整理成几条每一条都能在实际输出里看到对应症状。5.1 模拟器上的翻车现场结果差、结果飘现象一QK-means的V-measure大幅低于经典K-means聚类标签一片混乱。原因直接在qasm_simulator上跑且shots设成1024或更低测量概率的统计波动把距离排序打乱。解决先用statevector_simulator验证算法逻辑再切回qasm并拉高shots到4096以上。如果statevector上结果正常而qasm上崩坏就可以确定是采样噪声问题。现象二同样数据和参数两次运行结果明显不一样。原因量子测量的概率性、质心随机初始化叠加在一起随机性被放大。解决固定seed并用同一份配置跑10次报告V-measure均值和标准差而不是挑最好的一次。我一般在每次实验开始前把seed、shots、数据集文件路径写进一个配置字典避免“这次结果怎么和上次不一样”的口水账。5.2 数据与评估指标的问题分数失真、聚类成一团现象三V-measure直接返回0.0或者NaN。原因预测标签只有一种时完整性为0调和平均的分母直接为0另一种情况是真实标签泄漏进训练流程比如初始化质心时用了带标签的样本V-measure虚高。解决先打印np.unique(labels_)检查簇数再检查数据流里真实标签是否只进入评估函数。现象四自建数据集无论怎么调参都聚成一团。原因嵌入模型对短文本区分度有限或者向量没归一化就进了振幅编码量子距离退化成模长主导的量。解决先PCA降到2维可视化数据分布确认样本在语义空间里是否真的分离再用一个区分度更高的嵌入模型重新编码。如果可视化里两个类就叠在一起那就不是QK-means的问题是数据本身可分性差。5.3 依赖与复现问题API迁移把实验记录拖下水现象五Qiskit升级后execute或Aer报错以前能跑的脚本突然废掉。原因新版本把模拟器拆到独立包、运行接口向primitives迁移基础API执行路径变了。解决装完依赖后记录版本号代码里只用execute一个入口调模拟器升级时只改封装层。我是在一次环境重建时踩到的从那以后依赖清单都跟着实验配置一起进版本管理换机器重建环境时不会再花半天在API兼容性上。6. 进阶验证用经典对照和归一化NMI把QK-means的结论钉死跑通只是第一步验证这个项目有没有意义需要三件事。第一机制验证在相同初始化下量子版和经典版的聚类标签一致性要高。第二收敛验证迭代过程中V-measure曲线要在有限轮内稳定。第三规模验证确认模拟器在多大样本量下还能承受并找到合理的降维策略。机制验证的做法是计算归一化互信息NMI。NMI不受标签编号方式影响适合对比两个聚类结果。QK-means和经典KMeans在同样seed下的NMI如果接近1说明量子距离没有把样本的邻近关系破坏掉如果NMI只有0.5左右说明距离度量或测量噪声已经改变了聚类结构需要回头查振幅编码的归一化。from sklearn.metrics import normalized_mutual_info_score nmi normalized_mutual_info_score(qkm.labels_, km.labels_) print(f量子版与经典版 NMI: {nmi:.4f})收敛验证我习惯记录每轮迭代后的V-measure画一条随迭代次数变化的曲线。文本嵌入向量的簇结构通常比较清晰3到5轮就能收敛如果10轮后还在震荡多半是shots太低导致距离排序不稳定先拉高shots再谈其他。规模验证的经验值是维度128、样本量1000以内qasm_simulator还能接受再往上翻测量次数随距离矩阵规模平方增长建议先PCA降到64维或只在statevector后端上验证算法逻辑。真实硬件场景里shots可以压到512用多次运行取质心均值来抵消噪声这是NISQ设备上性价比最高的做法。从那以后我每次跑量子聚类实验都强制走一遍同一套流程先跑一轮经典KMeans做基线再固定seed和shots跑量子版并计算两者NMI最后才看V-measure。没有基线对照的量子聚类分数我一个字都不会信。希望帮到你。本文还有配套的精品资源点击获取
返回列表