基于自动微分的基态变分算法
对于优化问题,可以使用自动微分技术求解,该技术在机器学习中具有广泛的应用,被用于计算模型变分参数关于损失函数的梯度(程序中自我计算的),称之为反向传播算法(back propagation,BP)
例:利用自动微分求解实对称矩阵最大本征向量,即求解如下极大值问题
max
∣
v
∣
=
1
∣
v
T
M
v
∣
\max_{|v| = 1}|v^TMv|
∣v∣=1max∣vTMv∣
定义损失函数
f
=
−
v
T
M
v
∣
v
∣
2
f = -\frac{v^TMv}{|v|^2}
f=−∣v∣2vTMv,则上述极大化问题被化为损失函数的极小化问题。
计算v关于的
f
f
f 梯度
d
f
d
v
,
\frac{d f}{d v},
dvdf, 要使得
f
f
f 减小, 需将v沿负梯度方向更新:
v
←
v
−
η
d
f
d
v
v \leftarrow v-\eta \frac{d f}{d v}
v←v−ηdvdf其中,
η
\eta
η 为人为给定的常数,被称为更新步长或学习率。
进行多次迭代更新之后,得到收敛的𝑣,则最大本征向量
v
ˇ
\check{v}
vˇ与最大本征值𝜆
满足:
v
~
=
v
∣
v
∣
,
λ
=
v
~
T
M
v
~
\tilde{v}=\frac{v}{|v|}, \quad \lambda=\tilde{v}^{\mathrm{T}} M \tilde{v}
v~=∣v∣v,λ=v~TMv~
其中,
d
f
d
v
\frac{d f}{d v}
dvdf 梯度可直接使用自动微分技术计算
可使用Pytorch实现自动微分
更新过程中,可使用优化器(optimizer,例如Adam),让程序自适应地控制学习率,以达到较好的稳定性和收敛速率。
上述例子太过简单,其效率显然不如传统的本征值分解算法。但在更为复杂的问题中,自动微分的优势就会被体现出来。
代码
import torch as tc
from scipy.sparse.linalg import eigsh
from BasicFun import eigs_AD
def eigs_AD(mat, lr=1e-2, v0=None, it_time=500, tol=1e-15):
"""
:param mat: 带分解矩阵
:param lr: 初始学习率
:param v0: 初始向量
:param it_time: 最大迭代次数
:param tol: 收敛阈值
:return lm: 最大本征值
:return v0: 最大本征向量
"""
# 初始化及预处理v0
if v0 is None:
v0 = tc.randn(mat.shape[0], )
v0 /= v0.norm()
v0.requires_grad = True#v0是否请求梯度
# 建立优化器,这里使用Adam优化器,可自动动态控制学习率
optimizer = tc.optim.Adam([v0], lr=lr)
v1 = copy.deepcopy(v0.data)#判断收敛性
# 进入主循环
for t in range(it_time):
# 计算损失函数
f = -v0.matmul(mat).matmul(v0) / v0.matmul(v0)
f.backward() # 进行反向传播,计算梯度
optimizer.step() # 更新参数
optimizer.zero_grad() # 清空梯度
conv = tc.norm(v0.data - v1) / v1.numel() # 计算收敛性
if conv < tol:
break
else:
v1 = copy.deepcopy(v0.data)
with tc.no_grad():
v0 = v0 / tc.norm(v0) # 归一化,计算本征向量
lm = v0.matmul(mat).max() / v0.max() # 计算本征值
return lm, v0
# 自动微分求解实对称矩阵的本征值与本征向量
# 构建随机实对称矩阵
dim = 6
M = tc.randn(dim, dim)
M = M + M.t()
print('利用scipy中的本征值分解求解最大本征值与本征向量')
lm0, v0 = eigsh(M.numpy(), k=1, which='LA')
print('矩阵的最大本征值为:')
print(lm0[0])
print('矩阵的最大本征向量为:')
print(v0.reshape(-1, ))
print('\n利用自动微分求解最大本征值与本征向量')
lm1, v1 = eigs_AD(M)
print('矩阵的最大本征值为:')
print(lm1.item())
print('矩阵的最大本征向量为:')
print(v1.data.to('cpu').numpy().reshape(-1, ))
总结为三步:
1.把需要变分的参数v0设好,把requires_grad打开,把需要变分的东西放入优化器中
2.定义损失函数
3.f.backward() # 进行反向传播,计算梯度
optimizer.step() # 更新参数
optimizer.zero_grad() # 清空梯度
根据最大本征值问题与基态问题的等价性,显然,自动微分方法可用于求解多体系统基态,对应的优化问题可写为:
E
=
min
{
A
(
n
)
}
⟨
φ
∣
H
^
∣
φ
⟩
⟨
φ
∣
φ
⟩
E=\min _{\left\{A^{(n)}\right\}} \frac{\langle\varphi|\widehat{H}| \varphi\rangle}{\langle\varphi \mid \varphi\rangle}
E={A(n)}min⟨φ∣φ⟩⟨φ∣H
∣φ⟩其中,变分参数为MPS中的各个张量
{
A
(
n
)
}
,
\left\{A^{(n)}\right\},
{A(n)}, 梯度更新公式为:
A
(
n
)
←
A
(
n
)
−
η
∂
E
∂
A
(
n
)
A^{(n)} \leftarrow A^{(n)}-\eta \frac{\partial E}{\partial A^{(n)}}
A(n)←A(n)−η∂A(n)∂E相比于DMRG中MPS的“重整化群”解释,这里可以更加直接地将MPS看作
是对基态的一种特殊的参数化形式,能量即为变分的损失函数。
可使用BP算法及各种优化器进行梯度更新。
第八节为前面的知识总结