读书笔记-卷积神经网络

1. 从全连接层到卷积

因为我们遵循平移不变形和局部性,所以我们在全连接层的基础上形成了卷积神经网络,故卷积神经网络是一种特殊的全连接层。

1.1 平移不变性

不管检测对象出现在图像的哪个位置,神经网络的前几层应该对相同的图像区域具有相似的反应。
我们定义两个变量:

  • [ X ] i , j [X]_{i,j} [X]i,j:表示输入图像的中位置 (i,j) 的像素
  • [ H ] i , j [H]_{i,j} [H]i,j:表示隐藏的中位置 (i,j) 的像素
    为了使每个隐藏神经元都能接收到每个输⼊像素的信息,我们将参数从权重矩阵(如同我们先前在多层感知机中所做的那样)替换为四阶权重张量 W。假设 U 包含偏置参数,我们可以将全连接层形式化地表⽰为:
    [ H ] i , j = [ U ] i , j + ∑ k ∑ l [ W ] i , j , k , l [ X ] k , l (1) [H]_{i,j}=[U]_{i,j}+\sum_k\sum_l[W]_{i,j,k,l}[X]_{k,l}\tag1 [H]i,j=[U]i,j+kl[W]i,j,k,l[X]k,l(1)
    [ H ] i , j = [ U ] i , j + ∑ a ∑ b [ V ] i , j , a , b [ X ] i + a , j + b (2) [H]_{i,j}=[U]_{i,j}+\sum_a\sum_b[V]_{i,j,a,b}[X]_{i+a,j+b}\tag2 [H]i,j=[U]i,j+ab[V]i,j,a,b[X]i+a,j+b(2)
    从 W到V之间的转换只是形式的转换,因为在这两个四阶张量的元素之间存在一一对应的关系,我们只需重新索引下标(k,l);k=i+a;l=j+b;因此可得如下:
    [ V ] i , j , a , b = [ W ] i , j , i + a , j + b (3) [V]_{i,j,a,b}=[W]_{i,j,i+a,j+b}\tag{3} [V]i,j,a,b=[W]i,j,i+a,j+b(3)
    索引 a,b 通过在正偏移和负偏移之间移动覆盖整个图像。对于隐藏表示中任意给定位置 (i,j)处的像素值 [ H ] i , j [H]_{i,j} [H]i,j,可以通过在 x 中以 (i,j) 为中心对像素进行加权求和得到,加权使用的权重为 [ V ] i , j , a , b [V]_{i,j,a,b} [V]i,j,a,b,平移不变性指的是在检测对象在输入 X 中的平移,应该仅仅导致隐藏表示 H中的平移。也就是 U 和 V实际上不依赖 i,j 的值。数学可表达为:
    [ V ] i , j , a , b = [ V ] a , b (4) [V]_{i,j,a,b}=[V]_{a,b}\tag{4} [V]i,j,a,b=[V]a,b(4)
    其中 U 可以看作是常数 u.则可以表示如下:
    [ H ] i , j = u + ∑ a ∑ b [ V ] a , b [ X ] i + a , j + b (5) [H]_{i,j}=u+\sum_a\sum_b[V]_{a,b}[X]_{i+a,j+b}\tag{5} [H]i,j=u+ab[V]a,b[X]i+a,j+b(5)
    上述为卷积,表示的是使用系数 [ V ] a , b [V]_{a,b} [V]a,b对位置 [i,j] 附近的像素(i+a,j+b)进行加权得到 [ H ] i , j [H]_{i,j} [H]i,j。因为卷积考虑到局部性和平移不变性,所以参数表示上小了很多,是巨大的进步,但是其本质上还是来自全连接层。

1.2 局部性

局部性指的是我们卷积只对局部部分进行加权计算,而不考虑局部以外的值。假设局部区间为△,我们希望: ∣ a ∣ > △ |a|>△ a>或者 ∣ b ∣ > △ |b|>△ b>时,其训练参数 [ V ] a , b = 0 [V]_{a,b}=0 [V]a,b=0,故卷积层表示如下:
[ H ] i , j = u + ∑ a = − △ △ ∑ b = − △ △ [ V ] a , b [ X ] i + a , j + b (6) [H]_{i,j}=u+\sum_{a=-△}^{△}\sum_{b=-△}^{△}[V]_{a,b}[X]_{i+a,j+b}\tag{6} [H]i,j=u+a=b=[V]a,b[X]i+a,j+b(6)

2. 图像卷积

2.1 卷积计算图示

在这里插入图片描述
在这里插入图片描述

2.2 学习卷积核

关于卷积核的学习,详见下述链接。
深度学习到底是学习了什么?卷积核学习思考

2.3 小结

  • 二维卷积层的核心计算为二维互相关计算,跟数学上的卷积计算有所差别
  • 我们可以通过卷积核计算来对图像进行不同的处理,如锐化,高斯处理等
  • 我们可以通过输入数据X和最终数据Y,通过深度学习来学习到卷积核的值
  • 当需要检测输入特征中更广阔的区域,我们需要设计更深的网络。

3. 填充和步幅

3.1 填充

填充的意义在于,当我们的卷积核的大小大于1的时候,我们总会丢失部分的边缘像素,当神经网络比较浅的时候,我们可能看不出来。但当我们的网络非常深的时候,那么最终我们就会丢失掉很多信息,所以我们需要填充输入的矩阵。

  • 填充方式,对称填充
    在这里插入图片描述

如果我们添加 p h p_h ph行来填充(一半在顶部,一半在底部),添加 p w p_w pw列(一半在最左列,一半在最右列),输出形状为:
( n h − k h + p h + 1 ) × ( n w − k w + p w + 1 ) (7) (n_h-k_h+p_h+1)\times(n_w-k_w+p_w+1)\tag{7} (nhkh+ph+1)×(nwkw+pw+1)(7)

  • 注:
    n h : 输 入 矩 阵 行 数 ; k h : 卷 积 核 行 数 ; p h : 填 充 的 行 数 n_h:输入矩阵行数;k_h:卷积核行数;p_h:填充的行数 nh:;kh:;ph
    n w : 输 出 矩 阵 列 数 ; k w : 卷 积 核 列 数 ; p w : 填 充 的 列 数 n_w:输出矩阵列数;k_w:卷积核列数;p_w:填充的列数 nw:;kw:;pw

我们知道输入矩阵 X 的大小为 ( n h , n w ) (n_h,n_w) (nh,nw),输出矩阵 Y 的大小为 ( n h − k h + p h + 1 ) × ( n w − k w + p w + 1 ) (n_h-k_h+p_h+1)\times(n_w-k_w+p_w+1) (nhkh+ph+1)×(nwkw+pw+1);为了保证输出大小和输入大小不变,我们通常假设填充如下:
− k h + p h + 1 = 0 ; − k w + p w + 1 = 0 (8) -k_h+p_h+1=0;-k_w+p_w+1=0\tag{8} kh+ph+1=0;kw+pw+1=0(8)

  • 整理可得如下:
    p h = k h − 1 ; p w = k w − 1 (9) p_h=k_h-1;p_w=k_w-1\tag{9} ph=kh1;pw=kw1(9)

3.2 填充 - Pytorch

  • pytorch
    在 Pytorch 二维卷积 nn.Conv2d中的参数 padding 值是单边填充量,比如
conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1)

那么输入矩阵 X 的填充是 顶部 1,底部 1,所以输出矩阵大小应该为
( n h − k h + 2 ∗ p h + 1 ) × ( n w − k w + 2 ∗ p w + 1 ) (10) (n_h-k_h+2*p_h+1)\times(n_w-k_w+2*p_w+1)\tag{10} (nhkh+2ph+1)×(nwkw+2pw+1)(10)
代入可得:
( n h − 3 + 2 ∗ 1 + 1 ) × ( n w − 3 + 2 ∗ 1 + 1 ) (11) (n_h-3+2*1+1)\times(n_w-3+2*1+1)\tag{11} (nh3+21+1)×(nw3+21+1)(11)
输出矩阵Y的形状如下,这样输入矩阵X和输出矩阵Y的大小不变
n h × n w (12) n_h\times n_w\tag{12} nh×nw(12)

  • 代码
# -*- coding: utf-8 -*-
# @Project: zc
# @Author: zc
# @File name: padding
# @Create time: 2021/12/4 15:56


# 1. 导入相关数据库
import torch
from torch import nn
from d2l import torch as d2l


# 2.矩阵大小计算
# 输入 conv2d: 卷积核,x :输入矩阵
# 返回 y:输出矩阵,y.shape :输出矩阵的形状
def comp_conv2d(conv2d, x):
	# 将矩阵 x 加入批量大小和通道数:(批量大小,通道数,输入矩阵)
	x = x.reshape((1, 1) + x.shape)
	# 卷积计算
	y = conv2d(x)
	# 去掉矩阵中的 (批量大小,通道数)
	y = y.reshape(y.shape[2:])
	return y, y.shape


# 3. 定义二维卷积运算
conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1)

# 4. 获取卷积矩阵的填充值(padding) ,卷积核大小(kernel_size)
padding = conv2d.padding
kernel_size = conv2d.kernel_size

# 5. 初始化 x ,
x = torch.randn(8, 8)
x_shape = x.shape

# 6. 获取相关参数
n_h, n_w = x_shape[0], x_shape[1]
k_h, k_w = kernel_size[0], kernel_size[1]
p_h, p_w = padding[0], padding[1]
y_h, y_w = x_shape[0], x_shape[1]

# 7. 卷积计算,得到输出矩阵及形状
y, y_shape = comp_conv2d(conv2d, x)

# 8. 打印相关数据
print(f'x_shape={x_shape},kernel_size={kernel_size},padding={padding},y_shape={y_shape}')
print(f'{y_h}:(y_h)={n_h}:(n_h)-{k_h}:(k_h)+{2*p_h}:(2*p_h)+1')
print(f'{y_w}:(y_w)={n_w}:(n_w)-{k_w}:(k_w)+{2*p_w}:(2*p_w)+1')

  • 结果
x_shape=torch.Size([8, 8]),kernel_size=(3, 3),padding=(1, 1),y_shape=torch.Size([8, 8])
8:(y_h)=8:(n_h)-3:(k_h)+2:(2*p_h)+1
8:(y_w)=8:(n_w)-3:(k_w)+2:(2*p_w)+1

3.3 步幅 stride

步幅指的是卷积核每次滑动元素的数量。步幅的作用是为了采样的压缩,以前是一步一步的移动卷积核,如果像素之间相似东西太多,我们其实没必要进行一步一步的移动采集,我们完全可以跳着采集嘛,所以一般步幅的作用为了压缩数据,步幅一般是2,比如,输入矩阵是(8,8),当我们的步幅stride = 2的时候,我们得到的矩阵大小一般是(4,4),具体公式如下:

  • 输出矩阵:
    ⌊ ( n h − k h + p h + s h ) / s h ⌋ × ⌊ ( n w − k w + p w + s w ) / s w ⌋ (13) \lfloor (n_h-k_h+p_h+s_h)/s_h\rfloor \times \lfloor (n_w-k_w+p_w+s_w)/s_w\rfloor\tag{13} (nhkh+ph+sh)/sh×(nwkw+pw+sw)/sw(13)
  • 设置卷积核大小
    p h = k h − 1 ; p w = k w − 1 (14) p_h=k_h-1;p_w=k_w-1\tag{14} ph=kh1;pw=kw1(14)
  • 输出矩阵更新为:
    ⌊ ( n h + s h − 1 ) / s h ⌋ × ⌊ ( n w + s w − 1 ) / s w ⌋ (15) \lfloor (n_h+s_h-1)/s_h\rfloor \times \lfloor (n_w+s_w-1)/s_w\rfloor\tag{15} (nh+sh1)/sh×(nw+sw1)/sw(15)
  • 一般能被整除,最后简化为:
    ( n h / s h ) × ( n w / s w ) (16) (n_h/s_h) \times (n_w/s_w)\tag{16} (nh/sh)×(nw/sw)(16)

4. 多输入和多输出通道

4.1 多通道

以前我们常常进行的是矩阵运算,其有长宽两个参数,一般为二维通道,当我们的图片是彩色的.而颜色是由RGB三种颜色组成的.所以每个RGB输入图像具有 3 × h × w 3 \times h \times w 3×h×w,那么这个3就是通道的意思。

4.2 多输入通道 -> 单输出通道

假设我们输入为 c i × n h × n w c_i\times n_h \times n_w ci×nh×nw;卷积核为 c i × k h × k w c_i \times k_h \times k_w ci×kh×kw,输出为 o h × o w o_h \times o_w oh×ow;为了保证单通道输出,我们可以将每个通道卷积计算出来的结果求和。具体如下
在这里插入图片描述
解析:

  • input 输入大小为:2 x 3 x 3
  • kernel 核大小为: 2 x 2 x 2
  • padding 为:0;
  • output 输出大小为: 1 x 2 x 2注: 3-2+1=2; 1 表示的是求和;

代码如下:

# -*- coding: utf-8 -*-
# @Project: zc
# @Author: zc
# @File name: channel
# @Create time: 2021/12/4 21:28


import torch
from torch import nn
from torch.nn import functional as F
from d2l import torch as d2l


def corr2d_multi_in(x, k):
	return sum(d2l.corr2d(x, k) for x, k in zip(x, k))


x = torch.tensor([[[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]],
				  [[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0]]])
k = torch.tensor([[[0.0, 1.0], [2.0, 3.0]], [[1.0, 2.0], [3.0, 4.0]]])

y = corr2d_multi_in(x, k)

print(f'y={y}')

  • 结果
y=tensor([[ 56.,  72.],
        [104., 120.]])

4.3 多个输入通道,单输出通道

输入 X: c i × n h × n w c_i \times n_h \times n_w ci×nh×nw;
核 w: c i × k h × k w c_i \times k_h \times k_w ci×kh×kw
输出 Y: m h × m w m_h \times m_w mh×mw
Y = ∑ i = 0 c i X i , : , : ∗ W i , : , : (17) Y=\sum_{i=0}^{c_i}X_{i,:,:} * W_{i,:,:}\tag{17} Y=i=0ciXi,:,:Wi,:,:(17)

4.4 多输入通道,多输出通道

输入 X: c i × n h × n w c_i \times n_h \times n_w ci×nh×nw;
核 w: c o × c i × k h × k w c_o \times c_i \times k_h \times k_w co×ci×kh×kw
输出 Y: c o × m h × m w c_o \times m_h \times m_w co×mh×mw
Y = X i , : , : ∗ W i , : , : , : ; f o r i = 1 , 2 , . . . , c o (18) Y=X_{i,:,:} * W_{i,:,:,:} \quad ;for \quad i = 1,2,...,c_o\tag{18} Y=Xi,:,:Wi,:,:,:;fori=1,2,...,co(18)

4.5 输出通道的意义

  • 每个输出通道可以识别特定模式;
    比如说,我们输入有三个通道,可能 A 通道是识别猫的脚, B 通道是识别猫的颜色,C 通道是识别猫的尾巴,最后通过融合我们就知道了图片是一个猫。
  • 输入通道核识别并组合输入中的模式

4.6 1 X 1 卷积层

当卷积核的大小由原来的 k h × k w k_h \times k_w kh×kw变成 1 X 1 的时候,那么卷积核就失去了原来的作用,以前我们通过卷积核来抽取卷积核范围内的信息,现在变成了 1 X 1 ,所以在使用 1 X 1 卷积时候,我们只作用到了通道里面。
在这里插入图片描述
注:当以每像素为基础应⽤时,1 X 1 卷积层相当于全连接层

5. 汇聚层

5.1 池化层

池化层(pooling)有两重作用:

  • 降低卷积层对位置的敏感性
  • 降低对空间降采样表示的敏感性
    注:汇聚层是没有可以学习的参数的层

5.2 最大汇聚层

  • 定义:计算池化窗口中所有元素的最大值
    在这里插入图片描述
    m a x ( 0 , 1 , 3 , 4 ) = 4 (19) max(0,1,3,4)=4\tag{19} max(0,1,3,4)=4(19)
    m a x ( 1 , 2 , 4 , 5 ) = 5 max(1,2,4,5)=5 max(1,2,4,5)=5
    m a x ( 3 , 4 , 6 , 7 ) = 7 max(3,4,6,7)=7 max(3,4,6,7)=7
    m a x ( 4 , 5 , 7 , 8 ) = 8 max(4,5,7,8)=8 max(4,5,7,8)=8
  • 代码
# 1. 导入数据库
import torch
from torch import nn
from d2l import torch as d2l


# 2. 定义池化层 ,mode = 'max': 最大池化层,mode ='avg':平均池化层
def pool2d(x, pool_size, mode='max'):
	p_h, p_w = pool_size
	y = torch.zeros((x.shape[0] - p_h + 1, x.shape[1] - p_w + 1))
	for i in range(y.shape[0]):
		for j in range(y.shape[1]):
			if mode == 'max':
				y[i, j] = x[i:i + p_h, j:j + p_w].max()
			elif mode == 'avg':
				y[i, j] = x[i:i + p_h, j:j + p_w].mean()
	return y


# 3. 定义初始化参数
x = torch.tensor([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]])
pool_size = (2, 2)

# 4. 初始化最大池化层后的矩阵,平均池化层后的矩阵
y_max_pooling = pool2d(x, pool_size, 'max')
y_avg_pooling = pool2d(x, pool_size, 'avg')

# 5. 输出相关结果
print(f'y_max_pooling={y_max_pooling}')
print(f'y_avg_pooling={y_avg_pooling}')
  • 结果
y_max_pooling=tensor([[4., 5.],
        [7., 8.]])
y_avg_pooling=tensor([[2., 3.],
        [5., 6.]])
  • 分析:代码结果跟我们手算的结果一致。
  • 代码 : 引用 nn.MaxPool2d
# 1. 导入数据库
import torch
from torch import nn

# 2. 定义初始化参数
x = torch.tensor([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]])

# 3. 形状变化为 4 维
x = x.reshape(1, 1, 3, 3)

# 4. 实例化最大池化层
pool2d = nn.MaxPool2d(2,padding=0,stride=1)

# 5. 对张量 X 进行最大池化层计算
y = pool2d(x)

# 6. 将 Y 的形状改变
y = y.reshape(2,2)

# 7. 输出相关参数
print(f'x={x}')
print(f'x_shape={x.shape}')
print(f'y={y}')
print(f'y_shape={y.shape}')
  • 结果:
x=tensor([[[[0., 1., 2.],
          [3., 4., 5.],
          [6., 7., 8.]]]])
x_shape=torch.Size([1, 1, 3, 3])
y=tensor([[4., 5.],
        [7., 8.]])
y_shape=torch.Size([2, 2])

5.3 平均汇聚层

  • 定义:计算池化窗口中所有元素的平均值

6. 卷积神经网络 LeNet

注:本文代码来自 李沐的书籍,这里只做学习笔记。

  • 运行环境:
  • Pytorch GPU-1.9.1 ;Pycharm

6.1 LeNet 思路简图

在这里插入图片描述

6.2 LeNet 卷积网络大小分析

Tensor [output_shape]: torch.Size([1, 1, 28, 28])
Conv2d [output_shape]: torch.Size([1, 6, 28, 28])
Sigmoid [output_shape]: torch.Size([1, 6, 28, 28])
AvgPool2d [output_shape]: torch.Size([1, 6, 14, 14])
Conv2d [output_shape]: torch.Size([1, 16, 10, 10])
Sigmoid [output_shape]: torch.Size([1, 16, 10, 10])
AvgPool2d [output_shape]: torch.Size([1, 16, 5, 5])
Flatten [output_shape]: torch.Size([1, 400])
Linear [output_shape]: torch.Size([1, 120])
Sigmoid [output_shape]: torch.Size([1, 120])
Linear [output_shape]: torch.Size([1, 84])
Sigmoid [output_shape]: torch.Size([1, 84])
Linear [output_shape]: torch.Size([1, 10])

6.3 代码

# -*- coding: utf-8 -*-
# @Project: zc
# @Author: zc
# @File name: Lenet
# @Create time: 2021/12/6 6:39


# 1. 导入相关数据库
import torch
from torch import nn
from d2l import torch as d2l
import matplotlib.pyplot as plt


# 2. 定义整形类,主要是把原始图片变成(28,28)的大小
class Reshape(nn.Module):
	def forward(self, x):
		return x.view(-1, 1, 28, 28)


# 3. 定义网络 LeNet
net = nn.Sequential(
	Reshape(),
	nn.Conv2d(1, 6, kernel_size=5, padding=2), nn.Sigmoid(),
	nn.AvgPool2d(kernel_size=2, stride=2),
	nn.Conv2d(6, 16, kernel_size=5), nn.Sigmoid(),
	nn.AvgPool2d(kernel_size=2, stride=2),
	nn.Flatten(),
	nn.Linear(16 * 5 * 5, 120), nn.Sigmoid(),
	nn.Linear(120, 84), nn.Sigmoid(),
	nn.Linear(84, 10))

# 4. 加载数据
batch_size = 256  # 批量大小
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size=batch_size)  # 训练集和测试集


# 5. 使用GPU计算模型在数据集上的精度
# net.eval() 推断模式的情况下,不启用 BatchNormalization 和 Dropout
def evaluate_accuracy_gpu(net, data_iter, device=None):  # @save
	"""使用GPU计算模型在数据集上的精度。"""
	if isinstance(net, nn.Module):
		net.eval()  # 设置为评估模式
		if not device:  # 如果没有 GPU ,就查看下网路参数在哪里,将 Device设置为 'CPU'
			device = next(iter(net.parameters())).device
	# 正确预测的数量,总预测的数量
	metric = d2l.Accumulator(2)
	with torch.no_grad():  # 被with torch.no_grad()包住的代码,不用跟踪反向梯度计算
		for X, y in data_iter:  # 从 data_iter 中拿出数据
			if isinstance(X, list):
				# BERT微调所需的(之后将介绍)
				X = [x.to(device) for x in X]  # 将数据 x 转到 GPU 中
			else:
				X = X.to(device)
			y = y.to(device)  # 将数据 y 转到 GPU 中
			metric.add(d2l.accuracy(net(X), y), y.numel())
	return metric[0] / metric[1]


# 6.定义训练模型函数
def train_ch6(net, train_iter, test_iter, num_epochs, lr, device):
	"""用GPU训练模型(在第六章定义)。"""

	# 初始化权重参数
	def init_weights(m):
		if type(m) == nn.Linear or type(m) == nn.Conv2d:
			nn.init.xavier_uniform_(m.weight)  # 如果是卷积或者是全连接层,那么就用 xavier 进行初始化权重

	net.apply(init_weights)  # 网络参数初始化
	print('training on', device)
	net.to(device)  # 将网络转移到 GPU 上
	optimizer = torch.optim.SGD(net.parameters(), lr=lr)  # 设置神经网络优化器 SGD 随机梯度下降
	loss = nn.CrossEntropyLoss()  # 设置交叉熵损失来判断 y 和 y_hat 的差距
	animator = d2l.Animator(xlabel='epoch', xlim=[1, num_epochs],
							legend=['train loss', 'train acc', 'test acc'])
	timer, num_batches = d2l.Timer(), len(train_iter)
	for epoch in range(num_epochs):
		# 训练损失之和,训练准确率之和,范例数
		metric = d2l.Accumulator(3)
		net.train()  # 开启网络训练模式
		for i, (X, y) in enumerate(train_iter):  # 开始抽取相关数据 (x,y)
			timer.start()  # 开始计时
			optimizer.zero_grad()  # 优化器清零
			X, y = X.to(device), y.to(device)  # 将数据放在 GPU 上训练
			y_hat = net(X)
			# 这里有三个值,X,y 都是样本给的,X表示输入,
			# 我们希望通过网络得到 y_hat ,最后比较 y 和 y_hat
			l = loss(y_hat, y)  # 比较 y_hat 和 y 的差异
			l.backward()  # 损失回传
			optimizer.step()  # 优化器通过梯度来更新参数
			with torch.no_grad():  # 被with torch.no_grad()包住的代码,不用跟踪反向梯度计算
				metric.add(l * X.shape[0], d2l.accuracy(y_hat, y), X.shape[0])
			timer.stop()  # 计时结束
			train_l = metric[0] / metric[2]  # 训练损失值
			train_acc = metric[1] / metric[2]  # 训练精确度
			if (i + 1) % (num_batches // 5) == 0 or i == num_batches - 1:
				animator.add(epoch + (i + 1) / num_batches,
							 (train_l, train_acc, None))
		test_acc = evaluate_accuracy_gpu(net, test_iter)
		animator.add(epoch + 1, (None, None, test_acc))
	print(f'loss {train_l:.3f}, train acc {train_acc:.3f}, '
		  f'test acc {test_acc:.3f}')
	print(f'{metric[2] * num_epochs / timer.sum():.1f} examples/sec '
		  f'on {str(device)}')


# 7. 定义超参数
lr, num_epochs = 0.1, 50

# 8. 开始训练模型
train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())

# 9. 显示结果
plt.show()

  • 结果
loss 0.494, train acc 0.819, test acc 0.807
54179.3 examples/sec on cuda:0

在这里插入图片描述

6.4 小结

  • 在训练模式的情况时我们需要用 net.train();在预测模式的情况下我们需要用 net.eval()
net.train()		# 训练模式
net.eval()      # 预测模式
  • 需要将数据 X,Y, net 放到 GPU 上
X, y = X.to(device), y.to(device)  # 将数据放在 GPU 上训练
net.to(device)  # 将神经网络转移到 GPU 上			
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值