3. 量子特征映射中旋转门角度缩放因子的调参

为什么缩放因子会改变核矩阵的“形状”

在量子核方法中,经典样本 x 通过特征映射线路 U(x) 编码为量子态 |φ(x)⟩,核值定义为 K(x,x') = |⟨φ(x)|φ(x')⟩|²。若编码层使用 RY(θ) 或 RX(θ) 且角度取 θ = s·x_i,则缩放因子 s 直接控制样本在 Bloch 球上的展开程度:s 太小,所有样本态挤在一起,核矩阵接近全 1 矩阵,条件数极大;s 太大,角度在 2π 附近折叠,样本间相似度被周期性“混叠”,核矩阵出现近奇异结构。编码层重复次数 L 则通过多次旋转叠加,进一步放大或压缩这种展开。

特征映射线路(L 层重复) q0 q1 RY(s·x₀) RY(s·x₁) RY(s·x₀) RY(s·x₁) 第1层 第2层 … L 次重复,s 为角度缩放因子 编码 核矩阵条件数随 s 变化(示意) s κ 0.1 0.5 1.0 2.0 4.0 较优区间 s 过小 核矩阵≈全1 κ 极大 s 过大 角度折叠 κ 回升 分类边界随 s 的变化(二维示意) s 小:边界近似线性 s 适中:边界弯曲贴合 s 大:边界振荡过拟合

实验设置:固定数据与下游核方法

为了让缩放因子与重复次数的影响可比较,先固定其余变量。使用二维两类合成数据(每类 40 个样本,类间有轻微重叠),下游采用量子核 + 支持向量机(QSVM)。核矩阵条件数 κ = λ_max / λ_min 用核矩阵特征值计算,泛化性能用 5 折交叉验证准确率衡量。编码线路采用 RY 旋转 + 环形 CNOT 纠缠,重复 L 层。

import numpy as np
from sklearn.svm import SVC
from sklearn.model_selection import StratifiedKFold, cross_val_score
from sklearn.preprocessing import MinMaxScaler

# 1. 合成二维两类数据(示例数据,非实测)
rng = np.random.default_rng(7)
X0 = rng.normal(loc=[-0.6, -0.4], scale=0.45, size=(40, 2))
X1 = rng.normal(loc=[ 0.6,  0.5], scale=0.45, size=(40, 2))
X = np.vstack([X0, X1])
y = np.array([0]*40 + [1]*40)

# 缩放到 [0, 1],使角度缩放因子 s 的物理含义清晰
X = MinMaxScaler().fit_transform(X)

# 2. 特征映射:RY(s*x) + 环形 CNOT,重复 L 层
def feature_map_state(x, s, L, n_qubits=2):
    """返回 2 量子比特态向量(复数),使用 numpy 模拟。"""
    # 单比特 RY 门
    def ry(theta):
        c, sn = np.cos(theta/2), np.sin(theta/2)
        return np.array([[c, -sn], [sn, c]], dtype=complex)
    # CNOT(q0 控制,q1 目标)
    cnot = np.array([[1,0,0,0],[0,1,0,0],[0,0,0,1],[0,0,1,0]], dtype=complex)

    state = np.array([1,0,0,0], dtype=complex)  # |00>
    for _ in range(L):
        # 逐比特 RY
        g0 = np.kron(ry(s*x[0]), np.eye(2))
        g1 = np.kron(np.eye(2), ry(s*x[1]))
        state = g1 @ g0 @ state
        state = cnot @ state
    return state

def kernel_matrix(X, s, L):
    n = len(X)
    states = [feature_map_state(x, s, L) for x in X]
    K = np.zeros((n, n))
    for i in range(n):
        for j in range(n):
            K[i, j] = abs(np.vdot(states[i], states[j]))**2
    return K

def condition_number(K):
    eig = np.linalg.eigvalsh(K)
    eig = eig[eig > 1e-12]
    return eig.max() / eig.min() if len(eig) > 0 else np.inf

网格搜索:缩放因子 × 重复次数

在 s ∈ {0.1, 0.3, 0.5, 0.8, 1.0, 1.5, 2.0, 3.0} 与 L ∈ {1, 2, 3} 上做网格搜索。对每组参数计算核矩阵条件数,并用预计算核矩阵的 SVC 做 5 折交叉验证。注意:交叉验证时核矩阵需按训练/测试索引切分,避免信息泄漏。

from sklearn.base import BaseEstimator, ClassifierMixin

class PrecomputedKernelSVC(BaseEstimator, ClassifierMixin):
    """使用预计算核矩阵的 SVC 包装器,便于 cross_val_score。"""
    def __init__(self, K, C=1.0):
        self.K = K
        self.C = C
    def fit(self, X_idx, y):
        self.model_ = SVC(kernel='precomputed', C=self.C)
        self.model_.fit(self.K[np.ix_(X_idx, X_idx)], y)
        return self
    def predict(self, X_idx):
        return self.model_.predict(self.K[np.ix_(X_idx, X_idx)])

results = []
idx = np.arange(len(X))
skf = StratifiedKFold(n_splits=5, shuffle=True, random_state=42)

for s in [0.1, 0.3, 0.5, 0.8, 1.0, 1.5, 2.0, 3.0]:
    for L in [1, 2, 3]:
        K = kernel_matrix(X, s, L)
        kappa = condition_number(K)
        clf = PrecomputedKernelSVC(K, C=1.0)
        scores = cross_val_score(clf, idx, y, cv=skf, scoring='accuracy')
        results.append({
            's': s, 'L': L,
            'kappa': round(kappa, 2),
            'cv_acc': round(scores.mean(), 4),
            'cv_std': round(scores.std(), 4)
        })

# 按交叉验证准确率排序,展示前 6 组
results_sorted = sorted(results, key=lambda r: -r['cv_acc'])
for r in results_sorted[:6]:
    print(r)
上述代码中的准确率与条件数均为示例数据在给定随机种子下的预期输出,实际运行结果会因 numpy 版本与随机种子略有差异。判断依据应看趋势:条件数在 s 过小时极大,随 s 增大先降后升;交叉验证准确率在条件数适中的区间达到峰值。

结果判读与参数选择

典型输出会呈现如下规律(示例数据下的预期趋势):

sL核矩阵条件数 κ5 折 CV 准确率判读
0.11≈ 1.0×10³ 以上≈ 0.50核矩阵近全 1,无法区分样本
0.52≈ 10² 量级≈ 0.85展开适度,边界平滑
1.02≈ 10² 量级≈ 0.90较优区间,泛化好
2.03回升至 10³ 量级≈ 0.80角度折叠,边界振荡
3.03≈ 10⁴ 量级≈ 0.65过拟合,条件数恶化

选择策略:优先在 κ 处于 10² 量级且 CV 准确率最高的参数组合中挑选;若多个组合准确率接近,选 L 较小者以降低线路深度和硬件噪声影响。对示例数据,s ≈ 1.0、L = 2 是兼顾条件数与泛化的合理选择。若换用真实数据,应重新执行上述网格搜索,并以实际 CV 结果为准,不可直接套用示例数值。

缩放因子与数据归一化范围强耦合:若数据未缩放到 [0,1],s 的最优值会随特征量纲漂移。调参前务必固定预处理,并在报告中同时记录归一化方式与 s 的取值。