文档 / ArcQML / 训练与分析
损失函数与优化器
适用版本 · ArcQML 0.1.0
本页目录 10.1 损失函数:输入约束与前向公式
10.2 优化器
10.3 推荐训练循环
10.1 损失函数:输入约束与前向公式
函数
关键约束
mse_loss
prediction 与 target 均为非空 F32/F64;底层逐元素运算可广播。
l1_loss
prediction 与 target 均为非空 F32/F64。
binary_nll_loss
prediction 与 labels 均为一维且长度相同;标签只能取 0 或 1。
BCE with logits
logits 与 targets 的 shape 和 dtype 相同;target 取值位于 0 到 1 之间。
cross_entropy_loss
logits 为二维 Tensor;labels 长度等于 batch 大小,每个标签都是合法类别索引。
设样本数为 N,prediction 和 target 的第 i 个元素分别记为预测值与目标值。均方误差损失定义为:
L M S E = 1 2 N ∑ i = 1 N ( y ^ i − y i ) 2 . L_{\mathrm{MSE}}
=\frac{1}{2N}\sum_{i=1}^{N}
\left(\hat{y}_i-y_i\right)^2. L MSE = 2 N 1 i = 1 ∑ N ( y ^ i − y i ) 2 .
L1 损失定义为:
L L 1 = 1 N ∑ i = 1 N ∣ y ^ i − y i ∣ . L_{\mathrm{L1}}
=\frac{1}{N}\sum_{i=1}^{N}
\left\lvert\hat{y}_i-y_i\right\rvert. L L1 = N 1 i = 1 ∑ N ∣ y ^ i − y i ∣ .
binary_nll_loss 的 prediction 表示 Pauli-Z 期望值,而不是普通概率或 logit。框架先对期望值执行截断:
z i c l i p = clamp ( z i , − 1 + 2 ε , 1 − 2 ε ) , ε = 10 − 12 . z_i^{\mathrm{clip}}
=\operatorname{clamp}\!\left(
z_i,-1+2\varepsilon,1-2\varepsilon
\right),
\qquad
\varepsilon=10^{-12}. z i clip = clamp ( z i , − 1 + 2 ε , 1 − 2 ε ) , ε = 1 0 − 12 .
随后,将截断后的期望值转换为两个类别的概率:
p i ( 0 ) = 1 + z i c l i p 2 , p i ( 1 ) = 1 − z i c l i p 2 . p_i(0)=\frac{1+z_i^{\mathrm{clip}}}{2},
\qquad
p_i(1)=\frac{1-z_i^{\mathrm{clip}}}{2}. p i ( 0 ) = 2 1 + z i clip , p i ( 1 ) = 2 1 − z i clip .
对应的二分类负对数似然损失为:
L b i n a r y N L L = − 1 N ∑ i = 1 N [ ( 1 − y i ) log p i ( 0 ) + y i log p i ( 1 ) ] , y i ∈ { 0 , 1 } . L_{\mathrm{binary\ NLL}}
=-\frac{1}{N}\sum_{i=1}^{N}
\left[
(1-y_i)\log p_i(0)+y_i\log p_i(1)
\right],
\qquad
y_i\in\{0,1\}. L binary NLL = − N 1 i = 1 ∑ N [ ( 1 − y i ) log p i ( 0 ) + y i log p i ( 1 ) ] , y i ∈ { 0 , 1 } .
直接接收 logits 的二元交叉熵采用数值稳定形式:
L B C E = 1 N ∑ i = 1 N [ max ( x i , 0 ) + log ( 1 + e − ∣ x i ∣ ) − y i x i ] . L_{\mathrm{BCE}}
=\frac{1}{N}\sum_{i=1}^{N}
\left[
\max(x_i,0)+\log\!\left(1+e^{-\lvert x_i\rvert}\right)-y_ix_i
\right]. L BCE = N 1 i = 1 ∑ N [ max ( x i , 0 ) + log ( 1 + e − ∣ x i ∣ ) − y i x i ] .
对于包含 B 个样本和 C 个类别的多分类 logits,交叉熵损失定义为:
L C E = − 1 B ∑ b = 1 B log [ softmax ( X b ) y b ] . L_{\mathrm{CE}}
=-\frac{1}{B}\sum_{b=1}^{B}
\log\!\left[
\operatorname{softmax}(X_b)_{y_b}
\right]. L CE = − B 1 b = 1 ∑ B log [ softmax ( X b ) y b ] .
10.2 优化器
SGD:实现是带耦合 L2 项的更新
Sgd::step 对 trainable 且 requires_grad、并且已有 grad 的 Parameter 更新。对于 F32 或 F64,每个元素执行以下更新:
θ ← θ − η ( g + λ θ ) . \theta\leftarrow\theta-\eta\left(g+\lambda\theta\right). θ ← θ − η ( g + λ θ ) .
η = 学习率 , g = 当前梯度 , λ = 权重衰减系数 . \eta=\text{学习率},
\qquad
g=\text{当前梯度},
\qquad
\lambda=\text{权重衰减系数}. η = 学习率 , g = 当前梯度 , λ = 权重衰减系数 .
无梯度参数计入 skipped_no_grad,冻结或 requires_grad=false 参数计入 skipped_frozen。
Adam:偏置校正与权重衰减位置
Adam 在首次 step 时按参数切片中的位置建立状态槽位,后续调用必须保持参数数量和顺序不变。优化器保存迭代步数、一阶矩和二阶矩。每次更新时,首先把耦合式 L2 项加入当前梯度:
g ~ t = g t + λ θ t − 1 . \widetilde{g}_t
=g_t+\lambda\theta_{t-1}. g t = g t + λ θ t − 1 .
随后更新一阶矩和二阶矩:
m t = β 1 m t − 1 + ( 1 − β 1 ) g ~ t , v t = β 2 v t − 1 + ( 1 − β 2 ) g ~ t 2 . \begin{aligned}
m_t&=\beta_1m_{t-1}+(1-\beta_1)\widetilde{g}_t,\\
v_t&=\beta_2v_{t-1}+(1-\beta_2)\widetilde{g}_t^2.
\end{aligned} m t v t = β 1 m t − 1 + ( 1 − β 1 ) g t , = β 2 v t − 1 + ( 1 − β 2 ) g t 2 .
完成偏置校正后,再更新参数:
m ^ t = m t 1 − β 1 t , v ^ t = v t 1 − β 2 t , θ t = θ t − 1 − η m ^ t v ^ t + ε . \begin{aligned}
\widehat{m}_t&=\frac{m_t}{1-\beta_1^t},\\
\widehat{v}_t&=\frac{v_t}{1-\beta_2^t},\\
\theta_t&=\theta_{t-1}
-\eta\frac{\widehat{m}_t}{\sqrt{\widehat{v}_t}+\varepsilon}.
\end{aligned} m t v t θ t = 1 − β 1 t m t , = 1 − β 2 t v t , = θ t − 1 − η v t + ε m t .
因此,这里的 weight_decay 会进入矩估计,不等同于 AdamW 的解耦权重衰减。
构造器要求 learning_rate、weight_decay 为有限非负数,beta1 和 beta2 位于 [0,1),epsilon 为有限正数。训练中必须复用同一个 Adam 实例;重新创建会丢失动量与步数。
10.3 推荐训练循环
rust 复制
let mut optimizer = Adam::new(0.01, 0.9, 0.999, 1e-8, 0.0)?;
for _ in 0..steps {
let prediction = simulator.run(&circuit, &observable)?;
let loss = mse_loss(&prediction, &target)?;
loss.backward()?;
optimizer.step(circuit.parameters())?;
optimizer.zero_grad(circuit.parameters());
}
建议在每轮优化完成之后调用 zero_grad,清空当前梯度防止下一个训练循环的梯度累积。不要在 backward 之前替换或原地修改参与前向计算的 Parameter Tensor,否则版本检查可能报告前向数据已被修改。参数更新应交给 optimizer.step 完成。
结合示例阅读 VQE 教程 QNN 教程