4. 数据重上传线路中编码层插入位置的调参

问题定位:重上传结构中的“编码位置”变量

数据重上传(data re-uploading)线路的核心思想是:把同一组经典输入 x 多次编码进线路,并在两次编码之间插入可训练变分层。与“一次性编码 + 深变分层”相比,重上传用更少的可训练参数换取更强的函数拟合能力。但编码层插在哪里、插几次,会同时改变两件事:表达力(能否拟合目标函数)与梯度行为(是否出现梯度衰减)。本例固定参数预算,只调“插入位置与次数”这一个结构变量。

为便于比较,先约定三个结构记号(以 3 个编码块 E、2 个变分层 V 为例):

  • 前置式 E-V-E-V-E:编码块与变分层严格交替,编码次数 = 变分层数 + 1,是典型重上传结构。
  • 后置式 V-E-V-E-E:首个变分层在编码之前,编码集中在后段。
  • 集中式 E-E-E-V-V:所有编码先完成,再堆叠变分层,等价于“单次编码 + 深变分层”的退化情形。

参数预算固定为:可训练参数总数 P = 12,编码层总次数 N_E 在 1~4 之间变化,变分层数 N_V 由预算反推。这样“编码次数”成为唯一自变量,避免参数数量差异干扰结论。

可复现的线路构造与参数预算

采用 4 量子比特、硬件高效风格的旋转层 + 环形 CNOT 纠缠层。每个变分层含 4 个 RY 旋转参数(每比特 1 个),每个编码层对每比特施加 RY(x_i·s),其中 s 为角度缩放因子(本例固定 s = 1.0,避免与前一例的缩放调参混淆)。

import numpy as np

N_QUBIT = 4
PARAM_BUDGET = 12          # 固定可训练参数总数
SCALE = 1.0                # 编码角度缩放,本例固定

def build_schedule(n_encode, n_var, mode="interleave"):
    """返回由 'E' 与 'V' 组成的结构序列。"""
    if mode == "interleave":          # 前置式:E V E V ... E
        seq = []
        for i in range(n_var):
            seq += ["E", "V"]
        seq.append("E")
        return seq
    if mode == "post":                # 后置式:V E V E ... E
        seq = []
        for i in range(n_var):
            seq += ["V", "E"]
        seq.append("E")
        return seq
    if mode == "block":               # 集中式:E...E V...V
        return ["E"] * n_encode + ["V"] * n_var
    raise ValueError(mode)

def count_params(seq):
    return seq.count("V") * N_QUBIT   # 每变分层 4 个 RY 参数

# 在固定预算下枚举可行的 (N_E, N_V) 组合
candidates = []
for n_e in range(1, 5):
    n_v = PARAM_BUDGET // N_QUBIT
    if n_v * N_QUBIT != PARAM_BUDGET:
        continue
    for mode in ("interleave", "post", "block"):
        seq = build_schedule(n_e, n_v, mode)
        if count_params(seq) == PARAM_BUDGET:
            candidates.append((n_e, n_v, mode, "".join(seq)))

for c in candidates:
    print(c)

运行后可得到一组结构候选,例如 (3, 3, 'interleave', 'EVEVEVE')、(3, 3, 'post', 'VEVEVEE')、(3, 3, 'block', 'EEEVVV') 等。注意:N_V = 3 时参数恰为 12,因此 N_E 可在 1~4 间自由变化而不改变预算,这正是本例要利用的自由度。

拟合能力与梯度行为的对照实验

目标函数取一个对 4 比特输入具有中等非线性的示例函数(仅作教学示例,非实测结果):

def target(x):
    # x: shape (4,), 取值 {-1, +1}
    return np.sin(np.pi * x[0] * x[1]) + 0.5 * x[2] * x[3]

def dataset(n=200, seed=0):
    rng = np.random.default_rng(seed)
    X = rng.choice([-1.0, 1.0], size=(n, N_QUBIT))
    y = np.array([target(x) for x in X])
    return X, y

对每个候选结构,用同一优化器(Adam,学习率 0.05,迭代 200 步)训练,记录两项指标:

  • 最终训练损失:反映拟合能力,越小越好。
  • 初始梯度范数:在参数随机初始化后、第一步更新前计算 ||∂L/∂θ||₂,反映梯度是否健康。若该值随 N_E 增大而急剧下降,说明出现梯度衰减。

下表为教学示例中的预期趋势(非实测数据,用于说明判读逻辑):

结构N_EN_V参数数最终损失(示例趋势)初始梯度范数(示例趋势)
E-V-V-V1312偏高较大
E-V-E-V-V2312中等中等
E-V-E-V-E-V-E4312较低偏小
E-E-E-E-V-V-V4312偏高较小

判读要点:编码次数增加通常提升拟合能力,但交替插入比集中插入更能保持梯度;当 N_E 过大时,梯度范数下降可能抵消拟合收益。因此“最优插入位置”不是越多越好,而是在当前数据维度下找到损失与梯度的折中点。

编码层插入位置与次数:结构 → 拟合 / 梯度 结构方案(N_V=3,参数=12) 前置交替 E V E V E V E 后置交替 V E V E V E E 集中编码 E E E E V V V E:经典数据编码层 V:可训练变分层 随编码次数 N_E 变化的趋势(示意) 编码次数 N_E(1 → 4) 指标值 1 2 3 4 拟合损失↓ 梯度范数↓ 折中区 N_E ≈ 2~3 判读:交替插入提升拟合;N_E 过大时梯度衰减抵消收益;集中编码梯度最弱 选择原则:在损失下降趋缓前停止增加 N_E,优先保证初始梯度范数不塌缩

如何确定当前数据维度下的合适结构

上面的趋势图给出的是定性判读。实际调参时,按以下顺序执行即可得到可复现的结论:

  1. 固定预算:先确定 PARAM_BUDGET,由 N_V = PARAM_BUDGET / N_QUBIT 得到变分层数,保证所有候选结构参数数相同。
  2. 枚举结构:对 N_E = 1..4 与三种插入模式做笛卡尔积,得到候选序列。
  3. 统一训练协议:同一初始化种子集合(如 5 个种子)、同一优化器与步数,记录最终损失均值与初始梯度范数均值。
  4. 判读与选择:若某结构的损失显著更低且梯度范数未低于基线的一半,则选它;若损失相近但梯度更小,选梯度更大的结构,因为它在更大迭代预算下更可能继续下降。
数据维度较低(如本例 4 维二值输入)时,N_E = 2~3 的交替结构通常已足够;继续增加 N_E 带来的拟合收益递减,而梯度衰减风险上升。若输入维度更高或目标函数更复杂,可适当右移折中点,但仍应以梯度范数作为约束条件。
不要用“训练损失最低”作为唯一标准。重上传结构中,损失最低的候选往往也是梯度最小的候选,在含噪或迭代预算受限的场景下容易训练停滞。务必同时报告初始梯度范数。