前情提要

梯度下降算法 Gradient Descent

Gradient梯度就是对cost(w)求倒数 g = ∂cost(w)/∂w ,代表当前函数cost(w)的趋势

Update w = w - a * g(a --- 学习率)= w - a * ∂cost(w)/∂w 梯度开始下降,迭代过程就是根据梯度实现

若g<0,此时cost(w)单调减,则w👉;若g>0,cost(w)单调增,则w👈(都是往下降方向走)

由于本质是贪心,所以不一定得到全局最优而是局部最优两者。

x_data = [1.0, 2.0, 3.0]
y_data = [2.0, 4.0, 6.0]

w = 1
def forward(x):
    return w * x

def cost(xs, ys): # 传进来的是x_data和y_data的列表
    cost = 0
    for x, y in zip(xs, ys): # 遍历两个列表
        y_pred = forward(x)
        cost += (y_pred - y) ** 2
    return cost / len(xs)

def gradient(xs, ys):
    grad = 0
    for x, y in zip(xs, ys):
        grad += 2 * x * (x * w - y) # 对cost求导数 化简后 得
    return grad / len(xs)

a = 0.01 # 学习率
for epoch in range(1, 10, 1):
    cost_val = cost(x_data, y_data)
    grad_val = gradient(x_data, y_data)
    w = w - a * grad_val
    print(f"Epoch = {epoch}, w = {w}, loss = {cost_val}")

print("-------------------------------------------")

随机梯度下降Stochastic Gradient Descent --- SGD

该算法源于梯度下降算法中的求和导致难以并行运算,只求损失loss可以提高效率。

∂loss/∂w = d(y_pred - y)^2 / dw = 2 * x * (x * w - y)

w = w - a * ∂loss/∂w

x_data = [1.0, 2.0, 3.0]
y_data = [2.0, 4.0, 6.0]

w = 1.0

def forward(x):
    return x * w

def loss(x, y):
    y_pred = forward(x)
    return (y_pred - y) ** 2 # y_pred与实际y比较 来算loss

def gradient(x, y):
    return 2.0 * x * (x * w - y) # 对loss求导数化简后 得

for epoch in range(1, 10, 1):
    for x, y in zip(x_data, y_data):
        grad_val = gradient(x, y)
        w = w - 0.01 * grad_val # update
    print(f"Epoch = {epoch}, w = {w}, loss = {loss(x, y)}")

反向传播 Back Propagation

前提

        多层神经网络的局部梯度传递,目的是为了求loss关于w的导数来算梯度即 ∂loss/∂w

数学原理

        链式求导

对于两层神经网络:

y_pred = W2(W1*X + b1) + b2

                         第一层        第二层

             = W2*W1*X + (W2*b1 + b2)

             = W*X + b

注:根据以上推导,无论几层都能线性表示,所以就没有意义了,于是引入nonlinear function。

计算步骤:

1、create computational graph(forward)

        x,w -> f -> f(x,w) -> z -> ... -> loss

2、local gradient 局部梯度 --- 正向目标算出损失

        loss -> z ( ∂L/∂z ) -> f( ∂z/∂x , ∂z/∂w ) -> x( ∂L/∂x = ∂L/∂z * ∂z/∂x ) ,w( ∂L/∂w = ... )

3、backward --- 反向目标求梯度

通过反向传播,进行链式求导,更新梯度

以上步骤的进行目的是求 ∂loss/∂w

x_data = [1.0, 2.0, 3.0]
y_data = [2.0, 4.0, 6.0]

w1 = torch.Tensor([1.0]) # 创建一个张量w,Tensor中包含data和grad,data和grad也是Tensor
w1.requires_grad = True # 需要计算梯度
w2 = torch.Tensor([1.0])
w2.requires_grad = True
b = torch.Tensor([1.0])
b.requires_grad = True

def forward(x):
    return w1 * x ** 2 + w2 * x + b

def loss(x, y): # 构建计算图, tensor 做计算就会构建计算图
    y_pred = forward(x)
    return (y_pred - y) ** 2

def gradient(x, y): # ∂loss/∂w
    return 2.0 * x * (x * w - y)

print('Predict (before training)',4,forward(4))
a = 0.01 # 设置学习率
for epoch in range(1, 100, 1):
    for x, y in zip(x_data, y_data):
        l = loss(x, y)
        l.backward() # 不断计算梯度
        print('\tgrad:', x, y, w1.grad.item(), w2.grad.item(), b.grad.item())#梯度值的标量数值,.item() 方法的作用是将只包含一个元素的 PyTorch tensor 转换为普通的 Python 数值类型(如 float)。这样做是为了打印时更清晰,显示的是具体的数字而不是 tensor 对象。
        # 注意这里的grad是一个tensor,所以要取他的data
        w1.data = w1.data - a * w1.grad.data
        w2.data = w2.data - a * w2.grad.data
        b.data = b.data - a * b.grad.data
        # 释放之前计算的梯度
        w1.grad.data.zero_()
        w2.grad.data.zero_()
        b.grad.data.zero_()
    print('Epoch:', epoch, l.item())

print("predict (after training)", 4, forward(4).item())

Logo

免费领 50 小时云算力,进群参与显卡、AI PC 幸运抽奖

更多推荐