- 批量归一化和残差网络

- 批量归一化

- 对输入的标准化(浅层模型)

处理后的任意一个特征在数据集中所有样本上的均值为0、标准差为1。
标准化处理输入数据使各个特征的分布相近

- 批量归一化(深度模型)

利用小批量上的均值和标准差,不断调整神经网络中间输出,从而使整个神经网络在各层的中间输出的数值更稳定。

- 对全连接层做批量归一化

位置:全连接层中的仿射变换和激活函数之间

- 全连接

全连接层的数学模型:
pytorch 归一化矩阵_神经网络
pytorch 归一化矩阵_神经网络_02

- 批量归一化

pytorch 归一化矩阵_机器学习_03

pytorch 归一化矩阵_深度学习_04

pytorch 归一化矩阵_pytorch 归一化矩阵_05

pytorch 归一化矩阵_神经网络_06

pytorch 归一化矩阵_深度学习_07

其中pytorch 归一化矩阵_pytorch 归一化矩阵_08,且pytorch 归一化矩阵_pytorch_09是一个很小的常数,这样能保证上式分母大于0
以此推出:
pytorch 归一化矩阵_深度学习_10

引入可学习参数:拉伸参数pytorch 归一化矩阵_深度学习_11和偏移参数pytorch 归一化矩阵_pytorch 归一化矩阵_12。若pytorch 归一化矩阵_pytorch_13pytorch 归一化矩阵_pytorch_14,批量归一化无效。

- 对卷积层做批量归一化

位置:卷积计算之后、应⽤激活函数之前。
如果卷积计算输出多个通道,我们需要对这些通道的输出分别做批量归一化,且每个通道都拥有独立的拉伸和偏移参数。 计算:对单通道,batchsize=m,卷积计算输出=pxq 对该通道中m×p×q个元素同时做批量归一化,使用相同的均值和方差。

- 预测时的批量归一化

训练:以batch为单位,对每个batch计算均值和方差。
预测:用移动平均估算整个训练数据集的样本均值和方差。

def batch_norm(is_training, X, gamma, beta, moving_mean, moving_var, eps, momentum):
    # 判断当前模式是训练模式还是预测模式
    if not is_training:
        # 如果是在预测模式下,直接使用传入的移动平均所得的均值和方差
        X_hat = (X - moving_mean) / torch.sqrt(moving_var + eps)
    else:
        assert len(X.shape) in (2, 4)
        if len(X.shape) == 2:
            # 使用全连接层的情况,计算特征维上的均值和方差
            mean = X.mean(dim=0)
            var = ((X - mean) ** 2).mean(dim=0)
        else:
            # 使用二维卷积层的情况,计算通道维上(axis=1)的均值和方差。这里我们需要保持
            # X的形状以便后面可以做广播运算
            mean = X.mean(dim=0, keepdim=True).mean(dim=2, keepdim=True).mean(dim=3, keepdim=True)
            var = ((X - mean) ** 2).mean(dim=0, keepdim=True).mean(dim=2, keepdim=True).mean(dim=3, keepdim=True)
        # 训练模式下用当前的均值和方差做标准化
        X_hat = (X - mean) / torch.sqrt(var + eps)
        # 更新移动平均的均值和方差
        moving_mean = momentum * moving_mean + (1.0 - momentum) * mean
        moving_var = momentum * moving_var + (1.0 - momentum) * var
    Y = gamma * X_hat + beta  # 拉伸和偏移
    return Y, moving_mean, moving_var
class BatchNorm(nn.Module):
    def __init__(self, num_features, num_dims):
        super(BatchNorm, self).__init__()
        if num_dims == 2:
            shape = (1, num_features) #全连接层输出神经元
        else:
            shape = (1, num_features, 1, 1)  #通道数
        # 参与求梯度和迭代的拉伸和偏移参数,分别初始化成0和1
        self.gamma = nn.Parameter(torch.ones(shape))
        self.beta = nn.Parameter(torch.zeros(shape))
        # 不参与求梯度和迭代的变量,全在内存上初始化成0
        self.moving_mean = torch.zeros(shape)
        self.moving_var = torch.zeros(shape)

    def forward(self, X):
        # 如果X不在内存上,将moving_mean和moving_var复制到X所在显存上
        if self.moving_mean.device != X.device:
            self.moving_mean = self.moving_mean.to(X.device)
            self.moving_var = self.moving_var.to(X.device)
        # 保存更新过的moving_mean和moving_var, Module实例的traning属性默认为true, 调用.eval()后设成false
        Y, self.moving_mean, self.moving_var = batch_norm(self.training, 
            X, self.gamma, self.beta, self.moving_mean,
            self.moving_var, eps=1e-5, momentum=0.9)
        return Y

再LeNet上的应用是这样的

net = nn.Sequential(
            nn.Conv2d(1, 6, 5), # in_channels, out_channels, kernel_size
            BatchNorm(6, num_dims=4),
            nn.Sigmoid(),
            nn.MaxPool2d(2, 2), # kernel_size, stride
            nn.Conv2d(6, 16, 5),
            BatchNorm(16, num_dims=4),
            nn.Sigmoid(),
            nn.MaxPool2d(2, 2),
            d2l.FlattenLayer(),
            nn.Linear(16*4*4, 120),
            BatchNorm(120, num_dims=2),
            nn.Sigmoid(),
            nn.Linear(120, 84),
            BatchNorm(84, num_dims=2),
            nn.Sigmoid(),
            nn.Linear(84, 10)
        )
print(net)

pytorch 归一化矩阵_机器学习_15

- 残差网络(ResNet)

深度学习的问题:深度CNN网络达到一定深度后再一味地增加层数并不能带来进一步地分类性能提高,反而会招致网络收敛变得更慢,准确率也变得更差。

- 残差块

- ResNet

- 稠密连接网络(DenseNet)

pytorch 归一化矩阵_机器学习_16

- 主要构建模块

稠密块(dense block): 定义了输入和输出是如何连结的。
过渡层(transition layer):用来控制通道数,使之不过大。

- 稠密块
def conv_block(in_channels, out_channels):
    blk = nn.Sequential(nn.BatchNorm2d(in_channels), 
                        nn.ReLU(),
                        nn.Conv2d(in_channels, out_channels, kernel_size=3, padding=1))
    return blk

class DenseBlock(nn.Module):
    def __init__(self, num_convs, in_channels, out_channels):
        super(DenseBlock, self).__init__()
        net = []
        for i in range(num_convs):
            in_c = in_channels + i * out_channels
            net.append(conv_block(in_c, out_channels))
        self.net = nn.ModuleList(net)
        self.out_channels = in_channels + num_convs * out_channels # 计算输出通道数

    def forward(self, X):
        for blk in self.net:
            Y = blk(X)
            X = torch.cat((X, Y), dim=1)  # 在通道维上将输入和输出连结
        return X
- 过渡层

pytorch 归一化矩阵_pytorch_17卷积层:来减小通道数
步幅为2的平均池化层:减半高和宽

def transition_block(in_channels, out_channels):
    blk = nn.Sequential(
            nn.BatchNorm2d(in_channels), 
            nn.ReLU(),
            nn.Conv2d(in_channels, out_channels, kernel_size=1),
            nn.AvgPool2d(kernel_size=2, stride=2))
    return blk

blk = transition_block(23, 10)
blk(Y).shape # torch.Size([4, 10, 4, 4])

pytorch 归一化矩阵_深度学习_18

- DenseNet模型
net = nn.Sequential(
        nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3),
        nn.BatchNorm2d(64), 
        nn.ReLU(),
        nn.MaxPool2d(kernel_size=3, stride=2, padding=1))
num_channels, growth_rate = 64, 32  # num_channels为当前的通道数
num_convs_in_dense_blocks = [4, 4, 4, 4]

for i, num_convs in enumerate(num_convs_in_dense_blocks):
    DB = DenseBlock(num_convs, num_channels, growth_rate)
    net.add_module("DenseBlosk_%d" % i, DB)
    # 上一个稠密块的输出通道数
    num_channels = DB.out_channels
    # 在稠密块之间加入通道数减半的过渡层
    if i != len(num_convs_in_dense_blocks) - 1:
        net.add_module("transition_block_%d" % i, transition_block(num_channels, num_channels // 2))
        num_channels = num_channels // 2
net.add_module("BN", nn.BatchNorm2d(num_channels))
net.add_module("relu", nn.ReLU())
net.add_module("global_avg_pool", d2l.GlobalAvgPool2d()) # GlobalAvgPool2d的输出: (Batch, num_channels, 1, 1)
net.add_module("fc", nn.Sequential(d2l.FlattenLayer(), nn.Linear(num_channels, 10))) 

X = torch.rand((1, 1, 96, 96))
for name, layer in net.named_children():
    X = layer(X)
    print(name, ' output shape:\t', X.shape)
#batch_size = 256
batch_size=16
# 如出现“out of memory”的报错信息,可减小batch_size或resize
train_iter, test_iter =load_data_fashion_mnist(batch_size, resize=96)
lr, num_epochs = 0.001, 5
optimizer = torch.optim.Adam(net.parameters(), lr=lr)
d2l.train_ch5(net, train_iter, test_iter, batch_size, optimizer, device, num_epochs)

- 凸优化

- 优化与深度学习

- 优化与估计

尽管优化方法可以最小化深度学习中的损失函数值,但本质上优化方法达到的目标与深度学习的目标并不相同。

  • 优化方法目标:训练集损失函数值
  • 深度学习目标:测试集损失函数值(泛化性)
def f(x): return x * np.cos(np.pi * x)
def g(x): return f(x) + 0.2 * np.cos(5 * np.pi * x)

d2l.set_figsize((5, 3))
x = np.arange(0.5, 1.5, 0.01)
fig_f, = d2l.plt.plot(x, f(x),label="train error")
fig_g, = d2l.plt.plot(x, g(x),'--', c='purple', label="test error")
fig_f.axes.annotate('empirical risk', (1.0, -1.2), (0.5, -1.1),arrowprops=dict(arrowstyle='->'))
fig_g.axes.annotate('expected risk', (1.1, -1.05), (0.95, -0.5),arrowprops=dict(arrowstyle='->'))
d2l.plt.xlabel('x')
d2l.plt.ylabel('risk')
d2l.plt.legend(loc="upper right")

pytorch 归一化矩阵_深度学习_19

- 优化在深度学习中的挑战

  • 局部最小值
  • 鞍点
  • 梯度消失

例如:
pytorch 归一化矩阵_神经网络_20

- 局部最小值

def f(x):
    return x * np.cos(np.pi * x)

d2l.set_figsize((4.5, 2.5))
x = np.arange(-1.0, 2.0, 0.1)
fig,  = d2l.plt.plot(x, f(x))
fig.axes.annotate('local minimum', xy=(-0.3, -0.25), xytext=(-0.77, -1.0),
                  arrowprops=dict(arrowstyle='->'))
fig.axes.annotate('global minimum', xy=(1.1, -0.95), xytext=(0.6, 0.8),
                  arrowprops=dict(arrowstyle='->'))
d2l.plt.xlabel('x')
d2l.plt.ylabel('f(x)');

pytorch 归一化矩阵_pytorch 归一化矩阵_21

- 鞍点

x = np.arange(-2.0, 2.0, 0.1)
fig, = d2l.plt.plot(x, x**3)
fig.axes.annotate('saddle point', xy=(0, -0.2), xytext=(-0.52, -5.0),
                  arrowprops=dict(arrowstyle='->'))
d2l.plt.xlabel('x')
d2l.plt.ylabel('f(x)');

pytorch 归一化矩阵_深度学习_22

x, y = np.mgrid[-1: 1: 31j, -1: 1: 31j]
z = x**2 - y**2

d2l.set_figsize((6, 4))
ax = d2l.plt.figure().add_subplot(111, projection='3d')
ax.plot_wireframe(x, y, z, **{'rstride': 2, 'cstride': 2})
ax.plot([0], [0], [0], 'ro', markersize=10)
ticks = [-1,  0, 1]
d2l.plt.xticks(ticks)
d2l.plt.yticks(ticks)
ax.set_zticks(ticks)
d2l.plt.xlabel('x')
d2l.plt.ylabel('y');

pytorch 归一化矩阵_神经网络_23

- 梯度消失

x = np.arange(-2.0, 5.0, 0.01)
fig, = d2l.plt.plot(x, np.tanh(x))
d2l.plt.xlabel('x')
d2l.plt.ylabel('f(x)')
fig.axes.annotate('vanishing gradient', (4, 1), (2, 0.0) ,arrowprops=dict(arrowstyle='->'))0

pytorch 归一化矩阵_神经网络_24

- 凸性(Convexity)

- 基础

- 集合

pytorch 归一化矩阵_机器学习_25

- 函数

pytorch 归一化矩阵_机器学习_26

def f(x):
    return 0.5 * x**2  # Convex

def g(x):
    return np.cos(np.pi * x)  # Nonconvex

def h(x):
    return np.exp(0.5 * x)  # Convex

x, segment = np.arange(-2, 2, 0.01), np.array([-1.5, 1])
d2l.use_svg_display()
_, axes = d2l.plt.subplots(1, 3, figsize=(9, 3))

for ax, func in zip(axes, [f, g, h]):
    ax.plot(x, func(x))
    ax.plot(segment, func(segment),'--', color="purple")
    # d2l.plt.plot([x, segment], [func(x), func(segment)], axes=ax)

pytorch 归一化矩阵_pytorch 归一化矩阵_27

- Jense不等式

pytorch 归一化矩阵_机器学习_28

- 性质
  1. 无局部极小值
  2. 与凸集的关系
  3. 二阶条件
- 无局部最小值

证明:假设存在pytorch 归一化矩阵_神经网络_29是局部最小值,则存在全局最小值pytorch 归一化矩阵_pytorch 归一化矩阵_30, 使得pytorch 归一化矩阵_机器学习_31, 则对pytorch 归一化矩阵_深度学习_32:
pytorch 归一化矩阵_pytorch_33

- 与凸集的关系

对于凸函数pytorch 归一化矩阵_机器学习_34,定义集合pytorch 归一化矩阵_pytorch_35,则集合pytorch 归一化矩阵_机器学习_36为凸集。

证明:对于点pytorch 归一化矩阵_pytorch 归一化矩阵_37,pytorch 归一化矩阵_神经网络_38pytorch 归一化矩阵_机器学习_39,故pytorch 归一化矩阵_神经网络_40

对于函数pytorch 归一化矩阵_深度学习_41

def f(x):
    return 0.5 * x**2

x = np.arange(-2, 2, 0.01)
axb, ab = np.array([-1.5, -0.5, 1]), np.array([-1.5, 1])

d2l.set_figsize((3.5, 2.5))
fig_x, = d2l.plt.plot(x, f(x))
fig_axb, = d2l.plt.plot(axb, f(axb), '-.',color="purple")
fig_ab, = d2l.plt.plot(ab, f(ab),'g-.')

fig_x.axes.annotate('a', (-1.5, f(-1.5)), (-1.5, 1.5),arrowprops=dict(arrowstyle='->'))
fig_x.axes.annotate('b', (1, f(1)), (1, 1.5),arrowprops=dict(arrowstyle='->'))
fig_x.axes.annotate('x', (-0.5, f(-0.5)), (-1.5, f(-0.5)),arrowprops=dict(arrowstyle='->'))

pytorch 归一化矩阵_深度学习_42

- 凸函数与二阶导数

pytorch 归一化矩阵_深度学习_43是凸函数
必要性(pytorch 归一化矩阵_pytorch 归一化矩阵_44):
对于凸函数:
pytorch 归一化矩阵_机器学习_45

可以得到:
pytorch 归一化矩阵_pytorch 归一化矩阵_46

pytorch 归一化矩阵_pytorch 归一化矩阵_47
充分性(pytorch 归一化矩阵_深度学习_48):
pytorch 归一化矩阵_神经网络_49pytorch 归一化矩阵_机器学习_34上的三个点,由拉格朗日中值定理:
pytorch 归一化矩阵_深度学习_51

根据函数单调性原理:
pytorch 归一化矩阵_深度学习_52
所以可以得到:
pytorch 归一化矩阵_pytorch_53

- 限制条件

pytorch 归一化矩阵_pytorch 归一化矩阵_54

- 拉格朗日乘子法

pytorch 归一化矩阵_pytorch 归一化矩阵_55

- 惩罚项

欲使pytorch 归一化矩阵_pytorch 归一化矩阵_56,将项pytorch 归一化矩阵_深度学习_57加入目标函数,如多层感知机章节中的pytorch 归一化矩阵_深度学习_58

- 投影

pytorch 归一化矩阵_pytorch_59

pytorch 归一化矩阵_pytorch_60

- 梯度下降

- 一维梯度下降

- 沿梯度反方向移动自变量可以减小函数值

泰勒展开:
pytorch 归一化矩阵_pytorch_61
代入沿梯度方向的移动量 pytorch 归一化矩阵_pytorch 归一化矩阵_62:
pytorch 归一化矩阵_pytorch 归一化矩阵_63
pytorch 归一化矩阵_机器学习_64
pytorch 归一化矩阵_pytorch 归一化矩阵_65

如果用pytorch 归一化矩阵_深度学习_66作为例子

def f(x):
    return x**2  # Objective function

def gradf(x):
    return 2 * x  # Its derivative

def gd(eta):
    x = 10
    results = [x]
    for i in range(10):
        x -= eta * gradf(x)
        results.append(x)
    print('epoch 10, x:', x)
    return results

res = gd(0.2)

pytorch 归一化矩阵_pytorch 归一化矩阵_67

def show_trace(res):
    n = max(abs(min(res)), abs(max(res)))
    f_line = np.arange(-n, n, 0.01)
    d2l.set_figsize((3.5, 2.5))
    d2l.plt.plot(f_line, [f(x) for x in f_line],'-')
    d2l.plt.plot(res, [f(x) for x in res],'-o')
    d2l.plt.xlabel('x')
    d2l.plt.ylabel('f(x)')
    

show_trace(res)

pytorch 归一化矩阵_机器学习_68

- 学习率

show_trace(gd(0.05))

pytorch 归一化矩阵_pytorch_69

show_trace(gd(1.1))

pytorch 归一化矩阵_机器学习_70

- 局部极小值

c = 0.15 * np.pi

def f(x):
    return x * np.cos(c * x)

def gradf(x):
    return np.cos(c * x) - c * x * np.sin(c * x)

show_trace(gd(2))

pytorch 归一化矩阵_pytorch_71

- 多维梯度下降

eta = 0.1

def f_2d(x1, x2):  # 目标函数
    return x1 ** 2 + 2 * x2 ** 2

def gd_2d(x1, x2):
    return (x1 - eta * 2 * x1, x2 - eta * 4 * x2)

show_trace_2d(f_2d, train_2d(gd_2d))

pytorch 归一化矩阵_机器学习_72

- 自适应方法

- 牛顿法

pytorch 归一化矩阵_深度学习_66pytorch 归一化矩阵_pytorch_74处泰勒展开:
pytorch 归一化矩阵_pytorch 归一化矩阵_75
最小值点处保证pytorch 归一化矩阵_pytorch 归一化矩阵_76这也就是说,需要满足pytorch 归一化矩阵_神经网络_77
,对上式关于pytorch 归一化矩阵_pytorch_09求偏导,如果忽略高阶无穷小有:
pytorch 归一化矩阵_神经网络_79

c = 0.5

def f(x):
    return np.cosh(c * x)  # Objective

def gradf(x):
    return c * np.sinh(c * x)  # Derivative

def hessf(x):
    return c**2 * np.cosh(c * x)  # Hessian

# Hide learning rate for now
def newton(eta=1):
    x = 10
    results = [x]
    for i in range(10):
        x -= eta * gradf(x) / hessf(x)
        results.append(x)
    print('epoch 10, x:', x)
    return results

show_trace(newton())

pytorch 归一化矩阵_pytorch_80

c = 0.15 * np.pi

def f(x):
    return x * np.cos(c * x)

def gradf(x):
    return np.cos(c * x) - c * x * np.sin(c * x)

def hessf(x):
    return - 2 * c * np.sin(c * x) - x * c**2 * np.cos(c * x)

show_trace(newton())

pytorch 归一化矩阵_深度学习_81

show_trace(newton(0.5))

pytorch 归一化矩阵_pytorch_82

- 收敛性分析

只考虑在函数为凸函数, 且最小值点上pytorch 归一化矩阵_机器学习_83时的收敛速度:
pytorch 归一化矩阵_pytorch_84为第pytorch 归一化矩阵_pytorch_85次迭代后pytorch 归一化矩阵_pytorch 归一化矩阵_37的值,pytorch 归一化矩阵_机器学习_87表示 到最小值点pytorch 归一化矩阵_pytorch_88的距离,由pytorch 归一化矩阵_深度学习_89:

pytorch 归一化矩阵_神经网络_90
两边除以pytorch 归一化矩阵_pytorch_91,有:

pytorch 归一化矩阵_pytorch 归一化矩阵_92

代入更新方程pytorch 归一化矩阵_深度学习_93, 得到:

pytorch 归一化矩阵_pytorch_94

pytorch 归一化矩阵_pytorch 归一化矩阵_95

pytorch 归一化矩阵_pytorch_96时,有:

pytorch 归一化矩阵_机器学习_97

- 预处理(Heissan阵辅助梯度下降)

pytorch 归一化矩阵_神经网络_98

- 梯度下降和线性搜索(共轭梯度法)

- 随机梯度下降

- 随机梯度下降参数更新

对于有pytorch 归一化矩阵_神经网络_99个样本对训练数据集,设pytorch 归一化矩阵_神经网络_100是第pytorch 归一化矩阵_机器学习_101个样本的损失函数, 则目标函数为:
pytorch 归一化矩阵_深度学习_102
其梯度为:
pytorch 归一化矩阵_机器学习_103
使用该梯度的一次更新的时间复杂度为pytorch 归一化矩阵_神经网络_104
随机梯度下降更新公式pytorch 归一化矩阵_pytorch_105:
pytorch 归一化矩阵_pytorch 归一化矩阵_106
且有:
pytorch 归一化矩阵_pytorch 归一化矩阵_107

举个例子,例如函数pytorch 归一化矩阵_pytorch 归一化矩阵_108

def f(x1, x2):
    return x1 ** 2 + 2 * x2 ** 2  # Objective

def gradf(x1, x2):
    return (2 * x1, 4 * x2)  # Gradient

def sgd(x1, x2):  # Simulate noisy gradient
    global lr  # Learning rate scheduler
    (g1, g2) = gradf(x1, x2)  # Compute gradient
    (g1, g2) = (g1 + np.random.normal(0.1), g2 + np.random.normal(0.1))
    eta_t = eta * lr()  # Learning rate at time t
    return (x1 - eta_t * g1, x2 - eta_t * g2)  # Update variables

eta = 0.1
lr = (lambda: 1)  # Constant learning rate
show_trace_2d(f, train_2d(sgd, steps=50))

pytorch 归一化矩阵_深度学习_109

- 动态学习率

def exponential():
    global ctr
    ctr += 1
    return math.exp(-0.1 * ctr)

ctr = 1
lr = exponential  # Set up learning rate
show_trace_2d(f, train_2d(sgd, steps=1000))

pytorch 归一化矩阵_神经网络_110

def polynomial():
    global ctr
    ctr += 1
    return (1 + 0.1 * ctr)**(-0.5)

ctr = 1
lr = polynomial  # Set up learning rate
show_trace_2d(f, train_2d(sgd, steps=50))

pytorch 归一化矩阵_机器学习_111

- 小批量随机梯度下降

- 从零开始实现

def train_ch7(optimizer_fn, states, hyperparams, features, labels,
              batch_size=10, num_epochs=2):
    # 初始化模型
    net, loss = d2l.linreg, d2l.squared_loss
    
    w = torch.nn.Parameter(torch.tensor(np.random.normal(0, 0.01, size=(features.shape[1], 1)), dtype=torch.float32),
                           requires_grad=True)
    b = torch.nn.Parameter(torch.zeros(1, dtype=torch.float32), requires_grad=True)

    def eval_loss():
        return loss(net(features, w, b), labels).mean().item()

    ls = [eval_loss()]
    data_iter = torch.utils.data.DataLoader(
        torch.utils.data.TensorDataset(features, labels), batch_size, shuffle=True)
    
    for _ in range(num_epochs):
        start = time.time()
        for batch_i, (X, y) in enumerate(data_iter):
            l = loss(net(X, w, b), y).mean()  # 使用平均损失
            
            # 梯度清零
            if w.grad is not None:
                w.grad.data.zero_()
                b.grad.data.zero_()
                
            l.backward()
            optimizer_fn([w, b], states, hyperparams)  # 迭代模型参数
            if (batch_i + 1) * batch_size % 100 == 0:
                ls.append(eval_loss())  # 每100个样本记录下当前训练误差
    # 打印结果和作图
    print('loss: %f, %f sec per epoch' % (ls[-1], time.time() - start))
    d2l.set_figsize()
    d2l.plt.plot(np.linspace(0, num_epochs, len(ls)), ls)
    d2l.plt.xlabel('epoch')
    d2l.plt.ylabel('loss')
def train_sgd(lr, batch_size, num_epochs=2):
    train_ch7(sgd, None, {'lr': lr}, features, labels, batch_size, num_epochs)
train_sgd(1, 1500, 6)

pytorch 归一化矩阵_pytorch_112

train_sgd(0.005, 1)

pytorch 归一化矩阵_机器学习_113

train_sgd(0.05, 10)

pytorch 归一化矩阵_神经网络_114

- 简洁实现

# 本函数与原书不同的是这里第一个参数优化器函数而不是优化器的名字
# 例如: optimizer_fn=torch.optim.SGD, optimizer_hyperparams={"lr": 0.05}
def train_pytorch_ch7(optimizer_fn, optimizer_hyperparams, features, labels,
                    batch_size=10, num_epochs=2):
    # 初始化模型
    net = nn.Sequential(
        nn.Linear(features.shape[-1], 1)
    )
    loss = nn.MSELoss()
    optimizer = optimizer_fn(net.parameters(), **optimizer_hyperparams)

    def eval_loss():
        return loss(net(features).view(-1), labels).item() / 2

    ls = [eval_loss()]
    data_iter = torch.utils.data.DataLoader(
        torch.utils.data.TensorDataset(features, labels), batch_size, shuffle=True)

    for _ in range(num_epochs):
        start = time.time()
        for batch_i, (X, y) in enumerate(data_iter):
            # 除以2是为了和train_ch7保持一致, 因为squared_loss中除了2
            l = loss(net(X).view(-1), y) / 2 
            
            optimizer.zero_grad()
            l.backward()
            optimizer.step()
            if (batch_i + 1) * batch_size % 100 == 0:
                ls.append(eval_loss())
    # 打印结果和作图
    print('loss: %f, %f sec per epoch' % (ls[-1], time.time() - start))
    d2l.set_figsize()
    d2l.plt.plot(np.linspace(0, num_epochs, len(ls)), ls)
    d2l.plt.xlabel('epoch')
    d2l.plt.ylabel('loss')
train_pytorch_ch7(optim.SGD, {"lr": 0.05}, features, labels, 10)

pytorch 归一化矩阵_机器学习_115