反向传播算法

链式法则

求偏导

pytorch 参数初始化 pytorch sgd参数_数据

反向传播

pytorch 参数初始化 pytorch sgd参数_深度学习_02

Sigmoid函数举例

pytorch 参数初始化 pytorch sgd参数_深度学习_03

pytorch 参数初始化 pytorch sgd参数_数据_04


pytorch 参数初始化 pytorch sgd参数_深度学习_05


pytorch 参数初始化 pytorch sgd参数_pytorch 参数初始化_06


pytorch 参数初始化 pytorch sgd参数_数据_07


反向传播算法,这是深度学习中优化算法的核心,因为所有基于梯度的优化算法都需要计算每个参数的梯度

各种优化算法的变式

1.梯度下降法

pytorch 参数初始化 pytorch sgd参数_神经网络_08


2.SGD随机梯度下降法

就是每次使用一批(batch)数据进行梯度的计算,而不是计算全部数据的梯度。

pytorch 参数初始化 pytorch sgd参数_pytorch_09


公式是:更新的参数数据=参数数据-学习率*参数梯度

代码如下:

def sgd_update(parameters, lr):
    for param in parameters:
        param.data = param.data - lr * param.grad.data

详细代码:

import numpy as np
import torch
from torchvision.datasets import MNIST
from torch.utils.data import DataLoader
from torch import nn
from torch.autograd import Variable
import time
import matplotlib.pyplot as plt

import os
os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE'
# batch_size=1时

# 定义数据预处理函数
def data_tf(x):
    x = np.array(x, dtype='float32') / 255   # 将数据变到0-1
    x = (x - 0.5) / 0.5   # 标准化
    x = x.reshape((-1,))  # 拉平
    x = torch.from_numpy(x)   # 转化成Tensor
    return x

train_set = MNIST('./data', train=True, transform=data_tf, download=True)
test_set = MNIST('./data', train=False, transform=data_tf, download=True)

# 定义Loss函数
criterion = nn.CrossEntropyLoss()

# 定义梯度下降的函数
# 公式:参数数据 - 学习率 * 梯度
# 传入的是网络的参数,和学习率,输出梯度下降后的数据
def sgd_updata(parameters, lr):
    for param in parameters:
        param.data = param.data - lr * param.grad.data

# 定义训练集
train_data = DataLoader(train_set, batch_size =1, shuffle=True)

# 使用Sequential定义3层神经网络
net = nn.Sequential(
    nn.Linear(784, 200),
    nn.ReLU(),
    nn.Linear(200, 10)
)

# 开始训练
losses1 = []  # 空容器,循环次数
idx = 0  # 训练次数
start = time.time()  # 开始计时

for e in range(5):
    train_loss = 0  # 初始化训练损失为0
    for im, label in train_data:
        # 读取数据中的数据,存储在Variable中
        im = Variable(im)
        label = Variable(label)
        # 前向传播
        out = net(im)
        loss = criterion(out, label)
        # 反向传播
        net.zero_grad()  # 梯度清零
        loss.backward()  # 反向传播
        sgd_updata(net.parameters(), 1e-2)  # 梯度下降,使用0.01的学习率
        # 记录误差
        train_loss += loss.item()
        if idx % 30 == 0:
            losses1.append(loss.item())
        idx += 1
    print('epoch: {}, Train loss: {:.6f}'.format(e, train_loss / len(train_data)))
end = time.time()
print('使用时间:{:.5f} s'.format(end - start))

# 画出图形
x_axis = np.linspace(0, 5, len(losses1), endpoint=True)
plt.semilogx(x_axis, losses1, label = 'batch_size=1')
plt.legend(loc='best')
plt.show()

pytorch 参数初始化 pytorch sgd参数_数据_10


将batch_size改为64

pytorch 参数初始化 pytorch sgd参数_pytorch 参数初始化_11


学习率太大会使得损失函数不断回跳,从而无法让损失函数较好降低,所以我们一般都是用一个比较小的学习率

Pytorch中自带的函数

optimzier = torch.optim.SGD(net.parameters(), lr) 具体形式如下:

class torch.optim.SGD(params, lr=, momentum=0, dampening=0, weight_decay=0, nesterov=False)[source]
params (iterable) – 待优化参数的iterable或者是定义了参数组的dict
lr (float) – 学习率
momentum (float, 可选) – 动量因子(默认:0)
weight_decay (float, 可选) – 权重衰减(L2惩罚)(默认:0)
dampening (float, 可选) – 动量的抑制因子(默认:0)
nesterov (bool, 可选) – 使用Nesterov动量(默认:False)

3.Momentum动量

是在随机梯度下降的同时,增加动量
梯度下降可以想象成一个很扁的漏斗,这样在竖直方向上,梯度就非常大,在水平方向上,梯度就相对较小,所以我们在设置学习率的时候就不能设置太大,为了防止竖直方向上参数更新太过了,这样一个较小的学习率又导致了水平方向上参数在更新的时候太过于缓慢,所以就导致最终收敛起来非常慢。

pytorch 参数初始化 pytorch sgd参数_神经网络_12


pytorch 参数初始化 pytorch sgd参数_pytorch 参数初始化_13


pytorch中torch.optim.SGD(net.parameters(), lr=1e-2, momentum=0.9) # 加动量

import numpy as np
import torch
from torchvision.datasets import MNIST # 导入 pytorch 内置的 mnist 数据
from torch.utils.data import DataLoader
from torch import nn
from torch.autograd import Variable
import time
import matplotlib.pyplot as plt
import os
os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE'

def data_tf(x):
    x = np.array(x, dtype='float32') / 255
    x = (x - 0.5) / 0.5 # 标准化,这个技巧之后会讲到
    x = x.reshape((-1,)) # 拉平
    x = torch.from_numpy(x)
    return x

train_set = MNIST('./data', train=True, transform=data_tf, download=True) # 载入数据集,申明定义的数据变换
test_set = MNIST('./data', train=False, transform=data_tf, download=True)

# 定义 loss 函数
criterion = nn.CrossEntropyLoss()

train_data = DataLoader(test_set, batch_size=64, shuffle=True)

# 定义网络模型
net = nn.Sequential(
    nn.Linear(784, 200),
    nn.ReLU(),
    nn.Linear(200, 10)
)
optimizer = torch.optim.SGD(net.parameters(), lr=1e-2, momentum=0.9)
losses = []
idx = 0
start = time.time()
for e in range(5):
    train_loss = 0
    for im, label in train_data:
        im = Variable(im)
        label = Variable(label)
        # 前向传播
        out = net(im)
        loss = criterion(out, label)
        # 反向传播
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        # 记录误差
        train_loss += loss.item()
        if idx % 30 == 0: # 30 步记录一次
            losses.append(loss.item())
        idx += 1
    print('epoch:{}, Train Loss: {:.6f}'.format(e, train_loss / len(train_data)))
end = time.time()
print('使用时间:{:.5f} s'.format(end - start))

x_axis = np.linspace(0, 5, len(losses), endpoint=True)
plt.semilogy(x_axis, losses, label='momentum: 0.9')
plt.legend(loc='best')
plt.show()

4.Adagrad自适应学习率(adaptive)

pytorch 参数初始化 pytorch sgd参数_数据_14


Adagrad 的想法非常简答,在每次使用一个 batch size 的数据进行参数更新的时候,我们需要计算所有参数的梯度,那么其想法就是对于每个参数,初始化一个变量 s 为 0,然后每次将该参数的梯度平方求和累加到这个变量 s 上,然后在更新这个参数的时候,学习率就变为

pytorch 参数初始化 pytorch sgd参数_深度学习_15


pytorch 参数初始化 pytorch sgd参数_pytorch 参数初始化_16

pytorch 参数初始化 pytorch sgd参数_神经网络_17


自己定义adagrad函数:

def sgd_adagrad(parameters, sqrs, lr):
    eps = 1e-10
    for param, sqr in zip(parameters, sqrs):
        sqr[:] = sqr + param.grad.data ** 2
        div = lr / torch.sqrt(sqr + eps) * param.grad.data
        param.data = param.data - div

pytorch中的命令optimizer = torch.optim.Adagrad(net.parameters(), lr=1e-2)

import numpy as np
import torch
from torchvision.datasets import MNIST # 导入 pytorch 内置的 mnist 数据
from torch.utils.data import DataLoader
from torch import nn
from torch.autograd import Variable
import time
import matplotlib.pyplot as plt
import os
os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE'

def data_tf(x):
    x = np.array(x, dtype='float32') / 255
    x = (x - 0.5) / 0.5 # 标准化,这个技巧之后会讲到
    x = x.reshape((-1,)) # 拉平
    x = torch.from_numpy(x)
    return x

train_set = MNIST('./data', train=True, transform=data_tf, download=True) # 载入数据集,申明定义的数据变换
test_set = MNIST('./data', train=False, transform=data_tf, download=True)

# 定义 loss 函数
criterion = nn.CrossEntropyLoss()

train_data = DataLoader(train_set, batch_size=64, shuffle=True)
# 使用 Sequential 定义 3 层神经网络
net = nn.Sequential(
    nn.Linear(784, 200),
    nn.ReLU(),
    nn.Linear(200, 10),
)

optimizer = torch.optim.Adagrad(net.parameters(), lr=1e-2)
# 开始训练

start = time.time()  # 记时开始
for e in range(5):
    train_loss = 0
    for im, label in train_data:
        im = Variable(im)
        label = Variable(label)
        # 前向传播
        out = net(im)
        loss = criterion(out, label)
        # 反向传播
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        # 记录误差
        train_loss += loss.item()
    print('epoch: {}, Train Loss: {:.6f}'
          .format(e, train_loss / len(train_data)))
end = time.time()  # 计时结束
print('使用时间: {:.5f} s'.format(end - start))

5.RMSprop自适应学习率的改进方法

pytorch 参数初始化 pytorch sgd参数_pytorch 参数初始化_18

class torch.optim.RMSprop(params, lr=0.01, alpha=0.99, eps=1e-08, weight_decay=0, momentum=0, centered=False)[source]
实现RMSprop算法。
由G. Hinton在他的课程中提出.中心版本首次出现在Generating Sequences With Recurrent Neural Networks.

参数:
params (iterable) – 待优化参数的iterable或者是定义了参数组的dict
lr (float, 可选) – 学习率(默认:1e-2)
momentum (float, 可选) – 动量因子(默认:0)
alpha (float, 可选) – 平滑常数(默认:0.99)
eps (float, 可选) – 为了增加数值计算的稳定性而加到分母里的项(默认:1e-8)
centered (bool, 可选) – 如果为True,计算中心化的RMSProp,并且用它的方差预测值对梯度进行归一化
weight_decay (float, 可选) – 权重衰减(L2惩罚)(默认: 0)
import numpy as np
import torch
from torchvision.datasets import MNIST # 导入 pytorch 内置的 mnist 数据
from torch.utils.data import DataLoader
from torch import nn
from torch.autograd import Variable
import time
import matplotlib.pyplot as plt
import os
os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE'

def data_tf(x):
    x = np.array(x, dtype='float32') / 255
    x = (x - 0.5) / 0.5 # 标准化,这个技巧之后会讲到
    x = x.reshape((-1,)) # 拉平
    x = torch.from_numpy(x)
    return x

train_set = MNIST('./data', train=True, transform=data_tf, download=True) # 载入数据集,申明定义的数据变换
test_set = MNIST('./data', train=False, transform=data_tf, download=True)

# 定义 loss 函数
criterion = nn.CrossEntropyLoss()

train_data = DataLoader(train_set, batch_size=64, shuffle=True)
# 使用 Sequential 定义 3 层神经网络
net = nn.Sequential(
    nn.Linear(784, 200),
    nn.ReLU(),
    nn.Linear(200, 10),
)

optimizer = torch.optim.RMSprop(net.parameters(), lr=1e-3, alpha=0.9)

# 开始训练

start = time.time()  # 记时开始
for e in range(5):
    train_loss = 0
    for im, label in train_data:
        im = Variable(im)
        label = Variable(label)
        # 前向传播
        out = net(im)
        loss = criterion(out, label)
        # 反向传播
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        # 记录误差
        train_loss += loss.item()
    print('epoch: {}, Train Loss: {:.6f}'
          .format(e, train_loss / len(train_data)))
end = time.time()  # 计时结束
print('使用时间: {:.5f} s'.format(end - start))

Adam

RMSprop加上动量(Momentum)

达到比RMSProp更好的效果

pytorch 参数初始化 pytorch sgd参数_数据_19


pytorch中torch.optim.Adam()

import numpy as np
import torch
from torchvision.datasets import MNIST # 导入 pytorch 内置的 mnist 数据
from torch.utils.data import DataLoader
from torch import nn
from torch.autograd import Variable
import time
import matplotlib.pyplot as plt
import os
os.environ['KMP_DUPLICATE_LIB_OK'] = 'TRUE'

def data_tf(x):
    x = np.array(x, dtype='float32') / 255
    x = (x - 0.5) / 0.5 # 标准化,这个技巧之后会讲到
    x = x.reshape((-1,)) # 拉平
    x = torch.from_numpy(x)
    return x

train_set = MNIST('./data', train=True, transform=data_tf, download=True) # 载入数据集,申明定义的数据变换
test_set = MNIST('./data', train=False, transform=data_tf, download=True)

# 定义 loss 函数
criterion = nn.CrossEntropyLoss()

train_data = DataLoader(train_set, batch_size=64, shuffle=True)
# 使用 Sequential 定义 3 层神经网络
net = nn.Sequential(
    nn.Linear(784, 200),
    nn.ReLU(),
    nn.Linear(200, 10),
)

optimizer = torch.optim.Adam(net.parameters(), lr=1e-3)

# 开始训练
start = time.time()  # 记时开始
for e in range(5):
    train_loss = 0
    for im, label in train_data:
        im = Variable(im)
        label = Variable(label)
        # 前向传播
        out = net(im)
        loss = criterion(out, label)
        # 反向传播
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()
        # 记录误差
        train_loss += loss.item()
    print('epoch: {}, Train Loss: {:.6f}'
          .format(e, train_loss / len(train_data)))
end = time.time()  # 计时结束
print('使用时间: {:.5f} s'.format(end - start))

Adam作为默认的优化算法,往往能够达到比较好的效果,同时SGD+Momentum的方法也值得尝试

Adadelta
pytorch中torch.optim.Adadelta(net.parameters(), rho=0.9)

class torch.optim.Adadelta(params, lr=1.0, rho=0.9, eps=1e-06, weight_decay=0)[source]
实现Adadelta算法。

它在ADADELTA: An Adaptive Learning Rate Method.中被提出。

参数:

params (iterable) – 待优化参数的iterable或者是定义了参数组的dict
rho (float, 可选) – 用于计算平方梯度的运行平均值的系数(默认:0.9)
eps (float, 可选) – 为了增加数值计算的稳定性而加到分母里的项(默认:1e-6)
lr (float, 可选) – 在delta被应用到参数更新之前对它缩放的系数(默认:1.0)
weight_decay (float, 可选) – 权重衰减(L2惩罚)(默认: 0)