标题来自张玉宏老师书的目录《深度学习之美》
之前的推文中使用最小二乘法求损失函数的最小值,但是在机器学习和深度学习中更通用的方法是使用梯度下降方法找到最优解。
名词解释
梯度下降:核心就是希望能够通过数学意义上的迭代运算,从一个随机点出发,一步步逼近最优解。
梯度:有大小和方向,用导数求得大小,目标函数变化最快的方向的反方向作为移动的方向
方向:往哪个方向走
步长:每一步走多远
学习率:在梯度下降过程中,学习率乘以梯度大小为步长。学习率如果过小,损失函数的变化速度很慢,会大大增加网络的收敛复杂度,并且很容易被困在局部最小值或者鞍点
迭代:数学上的迭代,第一步运算的结果带入到第二步的函数中,以此类推
用代数表示梯度下降迭代过程
目标方程(多元一次方程)
令
那么多元一次方程可以写为(矩阵形式表示)
令其损失函数为
其实是预测值减去真实值的平方除以2倍的样本数m(即0.5*MSE),多乘以0.5可以消除掉平方求导后的系数2
迭代过程
对任意一个有,即在某个具体的点上带入w值,对损失函数求导
计算步长
是学习率。步长 = 梯度 * 学习率
更新所有参数w
移动的方向从减号表示出来,减号表示梯度的反方向
简单的示例
x是特征,只有一个,y是真实标签
如果需要对建模的话,需要找到一个a和b,使得损失函数L最小
x y
1 2
2 4
3 6
所以特征矩阵X为(多出来的全为1的一列为截距b)
真实标签矩阵y为
那么参数矩阵w为
令损失函数为
一阶导数
函数实现
设步长, 初始点,迭代次数为10000
import torch
def g(X, y, alpha = torch.tensor(0.01, requires_grad = True), n = 10000):
b=torch.ones(X.shape[0], requires_grad = True,dtype=torch.float32).reshape(-1,1)
X = torch.cat([X,b],1)
m, nc = X.shape
w = torch.zeros(nc, 1, requires_grad = True)
for _ in range(n):
grad = torch.mm(X.t(), (torch.mm(X, w) - y))/m
w = w - alpha * grad
return w
因为函数g中,可以给X增加一列全为1所以输入的时候就不需要写了
X = torch.tensor([[1],[2],[3]], requires_grad = True,dtype=torch.float32)
X
# tensor([[1.],
# [2.],
# [3.]], requires_grad=True)
y = torch.tensor([2,4,6], requires_grad = True,dtype=torch.float32).reshape(-1,1)
y
# tensor([[2.],
# [4.],
# [6.]], grad_fn=<ReshapeAliasBackward0>)
计算结果,其实用最小二乘法令一阶导数为0即可求的a=2,b=0
g(X,y, n = 10000)
# tensor([[2.0000e+00],
# [1.9127e-05]], grad_fn=<SubBackward0>)
计算此时的损失函数值,改写原来的g函数
def g(X, y, alpha = torch.tensor(0.01, requires_grad = True), n = 10000):
b=torch.ones(X.shape[0], requires_grad = True,dtype=torch.float32).reshape(-1,1)
X = torch.cat([X,b],1)
m, nc = X.shape
w = torch.zeros(nc, 1, requires_grad = True)
for _ in range(n):
grad = torch.mm(X.t(), (torch.mm(X, w) - y))/m
w = w - alpha * grad
loss = torch.mm((torch.mm(X,w)-y).t(), torch.mm(X,w)-y)/m*0.5
return w,loss
此时的损失函数非常接近于0
w,loss=g(X,y)
loss
# tensor([[2.9644e-11]], grad_fn=<MulBackward0>))