《动手学深度学习》学习笔记 第6章 卷积神经网络

本系列为《动手学深度学习》学习笔记
书籍链接:动手学深度学习

笔记是从第四章开始,前面三章为基础知道,有需要的可以自己去看看

关于本系列笔记: 书里为了让读者更好的理解,有大篇幅的描述性的文字,内容很多,笔记只保留主要内容,同时也是对之前知识的查漏补缺

6. 卷积神经网络

6.1 从全连接层到卷积

6.1.1 不变性

设计适合于计算机视觉的神经网络架构。

  1. 平移不变性(translation invariance):不管检测对象出现在图像中的哪个位置,神经网络的前面几层
    应该对相同的图像区域具有相似的反应,即为“平移不变性”。
  2. 局部性(locality):神经网络的前面几层应该只探索输入图像中的局部区域,而不过度在意图像中相隔
    较远区域的关系,这就是“局部性”原则。最终,可以聚合这些局部特征,以在整个图像级别进行预测。

6.1.2 多层感知机的限制

  多层感知机的输入是二维图像 X X X,其隐藏表示 H H H在数学上是一个矩阵,在代码中表示为二维张量。

  使用 [ X ] i , j [X]_{i,j} [X]i,j [ H ] i , j [H]_{i,j} [H]i,j分别表示输入图像和隐藏表示中位置 ( i , j ) {(i,j)} (i,j)处的像素。( [ X ] i , j [X]_{i,j} [X]i,j [ H ] i , j [H]_{i,j} [H]i,j是一样大的(感知机,全连接层))

  假设 U U U包含偏置参数,我们可以将全连接层形式化地表示为:
[ H ] i , j = [ U ] i , j + ∑ k ∑ l [ W ] i , j , k , l [ X ] k , l = [ U ] i , j + ∑ a ∑ b [ V ] i , j , a , b [ X ] i + a , j + b [H]_{i,j} = [U]_{i,j}+\sum_{k}\sum_{l}{[W]_{i,j,k,l}[X]_{k,l}} = [U]_{i,j}+\sum_{a}\sum_{b}{[V]_{i,j,a,b}[X]_{i+a,j+b}} [H]i,j=[U]i,j+kl[W]i,j,k,l[X]k,l=[U]i,j+ab[V]i,j,a,b[X]i+a,j+b

  上式中 [ V ] i , j , a , b = [ W ] i + a , j + b [V]_{i,j,a,b}=[W]_{i+a,j+b} [V]i,j,a,b=[W]i+a,j+b,索引a和b通过在正偏移和负偏移之间移动覆盖了整个图像。

  对于隐藏表示中任意给定位置 ( i , j ) {(i,j)} (i,j)处的像素值 [ X ] i , j [X]_{i,j} [X]i,j,可以通过在 X X X中以 ( i , j ) {(i,j)} (i,j)为中心对像素进行加权求和得到,加权使用的权重为 [ V ] i , j , a , b [V]_{i,j,a,b} [V]i,j,a,b( 到这里 [ V ] i , j , a , b [V]_{i,j,a,b} [V]i,j,a,b表示的还是跟 X X X一样大的张量,还不能理解为卷积 )。

平移不变性:
   V V V U U U实际上不依赖于 ( i , j ) (i, j) (i,j)的值,即 [ V ] i , j , a , b = [V] a , b [V]_{i,j,a,b }= \textbf{[V]}_{a,b} [V]i,j,a,b=[V]a,b。并且U是一个常数,比如u。因此,可以简化 H H H定义为:

[ H ] i , j = u + ∑ a ∑ b [V] a , b [ X ] i + a , j + b [H]_{i,j} = u+\sum_{a}\sum_{b}{\textbf{[V]}_{a,b}[X]_{i+a,j+b}} [H]i,j=u+ab[V]a,b[X]i+a,j+b
这就是卷积(convolution)。使用系数 [V] a , b \textbf{[V]}_{a,b} [V]a,b对位置 ( i , j ) (i, j) (i,j)附近的像素 ( i + a , j + b ) (i + a, j + b) (i+a,j+b)进行加权得到 [ H ] i , j [H]_{i,j} [H]i,j

局部性:
  如上所述,为了收集用来训练参数 [ H ] i , j [H]_{i,j} [H]i,j的相关信息,不应偏离
到距 ( i , j ) (i, j) (i,j)很远的地方。这意味着在 ∣ a ∣ > ∆ |a| > ∆ a> ∣ b ∣ > ∆ |b| > ∆ b>的范围之外,我们可以设置 [V] a , b = 0 \textbf{[V]}_{a,b} = 0 [V]a,b=0。因此,将 [ H ] i , j [H]_{i,j} [H]i,j重写为:

[ H ] i , j = u + ∑ a = − ∆ ∆ ∑ b = − ∆ ∆ [V] a , b [ X ] i + a , j + b [H]_{i,j} = u+\sum_{a=−∆}^{∆}\sum_{b=−∆}^{∆}{\textbf{[V]}_{a,b}[X]_{i+a,j+b}} [H]i,j=u+a=b=[V]a,b[X]i+a,j+b
  简而言之,(6.1.3)是一个卷积层(convolutional layer)
  在深度学习研究社区中,V被称为卷积核(convolution kernel)或者滤波器(filter),亦或简单地称之为该卷积层的权重,通常该权重是可学习的参数。

6.1.3 卷积

  在进一步讨论之前,我们先简要回顾一下为什么上面的操作被称为卷积。在数学中,两个函数(比如 f , g : R d → R f, g :R^d → R f,g:RdR)之间的“卷积”被定义为:
( f ∗ g ) ( x ) = Z f ( z ) g ( x − z ) d z . (f ∗ g)(x) = Zf(z)g(x − z)dz. (fg)(x)=Zf(z)g(xz)dz.
  当为离散对象时,积分就变成求和。

6.1.4 “沃尔多在哪里”回顾

在这里插入图片描述
通道
  可以把隐藏表示想象为一系列具有二维张量的通道(channel)。这些通道有时也被称为特征映射(feature maps),因为每个通道都向后续层提供一组空间化的学习特征。(直观上可以想象在靠近输入的底层,一些通道专门识别边缘,而一些通道专门识别纹理。)

  为了支持输入 X X X和隐藏表示 H H H中的多个通道,我们可以在V中添加第四个坐标,即 [ V ] a , b , c , d [V]_{a,b,c,d} [V]a,b,c,d。综上所述

[ H ] i , j , d = ∑ a = − ∆ ∆ ∑ b = − ∆ ∆ [ V ] a , b , c , d [ X ] i + a , j + b , c [H]_{i,j,d} = \sum_{a=−∆}^{∆}\sum_{b=−∆}^{∆}{[V]_{a,b,c,d}[X]_{i+a,j+b,c}} [H]i,j,d=a=b=[V]a,b,c,d[X]i+a,j+b,c
  其中隐藏表示 H H H中的索引, d d d表示输出通道,而随后的输出将继续以三维张量 H H H作为输入进入下一个卷积层。

6.2 图像卷积

6.2.1 互相关运算

  严格来说,卷积层是个错误的叫法,因为它所表达的运算其实是互相关运算(cross‐correlation),而不是卷积运算。
在这里插入图片描述
  (0 × 0 + 1 × 1 + 3 × 2 + 4 × 3 = 19.)

在这里插入图片描述
  输出⼤小略小于输⼊⼤小。这是因为卷积核的宽度和⾼度⼤于1,而卷积核只与图像中每个⼤小完全适合的位置进⾏互相关运算
  所以,输出⼤小等于输⼊⼤小 n h × n w n_h × n_w nh×nw减去卷积核⼤小 k h × k w k_h × k_w kh×kw即:
( n h − k h + 1 ) × ( n w − k w + 1 ) (n_h-k_h+1 )× (n_w-k_w+1) (nhkh+1)×(nwkw+1)
  在corr2d函数中实现如上过程,该函数接受输⼊张量 X X X和卷积核张量 K K K,并返回输出张量 Y Y Y

	import torchfrom torch import nnfrom d2l import torch as d2ldef corr2d(X, K): #@save"""计算二维互相关运算"""h, w = K.shapeY = torch.zeros((X.shape[0] - h + 1, X.shape[1] - w + 1))for i in range(Y.shape[0]):for j in range(Y.shape[1]):Y[i, j] = (X[i:i + h, j:j + w] * K).sum()return Y

6.2.2 卷积层

  基于上⾯定义的corr2d函数实现⼆维卷积层。在__init__构造函数中,将weight和bias声明为两个模型参数。前向传播函数调⽤corr2d函数并添加偏置。

	class Conv2D(nn.Module):def __init__(self, kernel_size):super().__init__()self.weight = nn.Parameter(torch.rand(kernel_size))self.bias = nn.Parameter(torch.zeros(1))def forward(self, x):return corr2d(x, self.weight) + self.bias

6.2.3 图像中目标的边缘检测

  如下是卷积层的一个简单应用:通过找到像素变化的位置,来检测图像中不同颜色的边缘。
  构建卷积核:构造一个6 × 8像素的黑白图像。中间四列为黑色(0),其余像素为白色(1)。

	X = torch.ones((6, 8))X[:, 2:6] = 0X=========================================tensor([[1., 1., 0., 0., 0., 0., 1., 1.],[1., 1., 0., 0., 0., 0., 1., 1.],[1., 1., 0., 0., 0., 0., 1., 1.],[1., 1., 0., 0., 0., 0., 1., 1.],[1., 1., 0., 0., 0., 0., 1., 1.],[1., 1., 0., 0., 0., 0., 1., 1.]])

  接下来,构造一个高度为1、宽度为2的卷积核K。当进行互相关运算时,如果水平相邻的两元素相同,则输出为零,否则输出为非零

	K = torch.tensor([[1.0, -1.0]])

  现在,对参数X(输入)K(卷积核) 执行互相关运算。如下所示,

  • 输出Y中的1代表从白色到黑色的边缘
  • ‐1代表从黑色到白色的边缘
  • 其他情况的输出为0
	Y = corr2d(X, K)Y======================================tensor([[ 0., 1., 0., 0., 0., -1., 0.],[ 0., 1., 0., 0., 0., -1., 0.],[ 0., 1., 0., 0., 0., -1., 0.],[ 0., 1., 0., 0., 0., -1., 0.],[ 0., 1., 0., 0., 0., -1., 0.],[ 0., 1., 0., 0., 0., -1., 0.]])

  现在将输入的二维图像转置,再进行如上的互相关运算。

  其输出如下,之前检测到的垂直边缘消失了。

  不出所料,这个卷积核K只可以检测垂直边缘,无法检测水平边缘

tensor([[0., 0., 0., 0., 0.],[0., 0., 0., 0., 0.],[0., 0., 0., 0., 0.],[0., 0., 0., 0., 0.],[0., 0., 0., 0., 0.],[0., 0., 0., 0., 0.],[0., 0., 0., 0., 0.],[0., 0., 0., 0., 0.]])

6.2.4 学习卷积核

  是否可以学习由X生成Y的卷积核呢?

  • 先构造一个卷积层,并将其卷积核初始化为随机张量。
  • 接下来,在每次迭代中,我们比较Y与卷积层输出的平方误差,然后计算梯度来更新卷积核。

  为了简单起见,在此使用内置的二维卷积层,并忽略偏置。

	# 构造一个二维卷积层,它具有1个输出通道和形状为(1,2)的卷积核conv2d = nn.Conv2d(1,1, kernel_size=(1, 2), bias=False)# 这个二维卷积层使用四维输入和输出格式(批量大小、通道、高度、宽度),# 其中批量大小和通道数都为1X = X.reshape((1, 1, 6, 8))Y = Y.reshape((1, 1, 6, 7))lr = 3e-2 # 学习率for i in range(10):Y_hat = conv2d(X)l = (Y_hat - Y) ** 2conv2d.zero_grad()l.sum().backward()# 迭代卷积核conv2d.weight.data[:] -= lr * conv2d.weight.gradif (i + 1) % 2 == 0:print(f'epoch {i+1}, loss {l.sum():.3f}')

  在10次迭代之后,误差已经降到足够低。(大家可以自己试试)

6.2.5 互相关和卷积

  它们差别不大,只需水平和垂直翻转二维卷积核张量,然后对输入张量执行互相关运算。
  为了与深度学习文献中的标准术语保持一致,将继续把“互相关运算”称为卷积运算,尽管严格地说,它们略有不同。
  此外,对于卷积核张量上的权重,我们称其为元素。

6.2.6 特征映射和感受野

  中输出的卷积层有时被称为特征映射(feature map),因为它可以被视为一个输入映射到下一层的空间维度的转换器
  在卷积神经网络中,对于某一层的任意元素x,其感受野(receptive field)是指在前向传播期间可能影响x计算的所有元素(来自所有先前层)。

6.3 填充和步幅

6.3.1 填充

  如上所述,在应用多层卷积时,我们常常丢失边缘像素。随着应用许多连续卷积层,累积丢失的像素数就多了。解决这个问题的简单方法即为填充(padding)在输入图像的边界填充元素(通常填充元素是0)
在这里插入图片描述

  • 卷积神经网络中卷积核的高度和宽度通常为奇数,例如1、3、5或7。

  选择奇数的好处是:保持空间维度的同时,我们可以在顶部和底部填充相同数量的行,在左侧和右侧填充相同数量的列。

  • 此外,使用奇数的核大小和填充大小也提供了书写上的便利。对于任何二维张量X,当满足:
  1. 卷积核的大小是奇数;
  2. 所有边的填充行数和列数相同;
  3. 输出与输入具有相同高度和宽度则可以得出:输出 Y [ i , j ] Y[i, j] Y[i,j]是通过以输入 X [ i , j ] X[i, j] X[i,j]为中心,与卷积核进行互相关计算得到的。

  例:创建一个高度和宽度为3的二维卷积层,并在所有侧边填充1个像素。给定高度和宽度为8的输入,则输出的高度和宽度也是8。

import torchfrom torch import nn# 为了方便起见,我们定义了一个计算卷积层的函数。# 此函数初始化卷积层权重,并对输入和输出提高和缩减相应的维数def comp_conv2d(conv2d, X):# 这里的(1,1)表示批量大小和通道数都是1X = X.reshape((1, 1) + X.shape)Y = conv2d(X)# 省略前两个维度:批量大小和通道return Y.reshape(Y.shape[2:])# 请注意,这里每边都填充了1行或1列,因此总共添加了2行或2列conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1)X = torch.rand(size=(8, 8))comp_conv2d(conv2d, X).shape

6.3.2 步幅

  将每次滑动元素的数量称为步幅(stride)
  当垂直步幅为 s h s_h sh、水平步幅为 s w s_w sw时,输出形状为
⌊ ( n h − k h + p h + s h ) / s h ⌋ × ⌊ ( n w − k w + p w + s w ) / s w ⌋ . ⌊(n_h − k_h + p_h + s_h)/s_h⌋ × ⌊(n_w − k_w + p_w + s_w)/s_w⌋. ⌊(nhkh+ph+sh)/sh×⌊(nwkw+pw+sw)/sw.
  将高度和宽度的步幅设置为2,从而将输入的高度和宽度减半。

	conv2d = nn.Conv2d(1, 1, kernel_size=3, padding=1, stride=2)comp_conv2d(conv2d, X).shape

  看一个稍微复杂的例子。

	conv2d = nn.Conv2d(1, 1, kernel_size=(3, 5), padding=(0, 1), stride=(3, 4))comp_conv2d(conv2d, X).shape

6.4 多输入多输出通道

  当输入包含多个通道时,需要构造一个与输入数据具有相同输入通道数的卷积核,以便与输入数据进行互相关运算。
在这里插入图片描述

	import torchfrom d2l import torch as d2ldef corr2d_multi_in(X, K):# 先遍历“X”和“K”的第0个维度(通道维度),再把它们加在一起return sum(d2l.corr2d(x, k) for x, k in zip(X, K))

  corr2实现过程见前文:6.2.1 互相关运算

	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]]])
	corr2d_multi_in(X, K)tensor([[ 56., 72.],[104., 120.]])

6.4.2 多输出通道

  为了获得多个通道的输出,可以为每个输出通道创建一个形状为 c i × k h × k w c_i × k_h × k_w ci×kh×kw的卷积核张量,这样卷积核的形状是 c o × c i × k h × k w c_o × c_i × k_h × k_w co×ci×kh×kw

  实现一个计算多个通道的输出互相关函数

	def corr2d_multi_in_out(X, K):# 迭代“K”的第0个维度,每次都对输入“X”执行互相关运算。# 最后将所有结果都叠加在一起return torch.stack([corr2d_multi_in(X, k) for k in K], 0)

6.4.3 1 × 1 卷积层

  因为使用了最小窗口,1 × 1卷积失去了卷积层的特有能力——在高度和宽度维度上,识别相邻元素间相互作用的能力。其实1 × 1卷积的唯一计算发生在通道上。

  下图展示了使用1×1卷积核与3个输入通道2个输出通道的互相关计算。

  可以将1 × 1卷积层看作在每个像素位置应用的全连接层,以 c i c_i ci个输入值转换为 c o c_o co个输出值。

在这里插入图片描述
  下面,我们使用全连接层实现1 × 1卷积。请注意,我们需要对输入和输出的数据形状进行调整。

	def corr2d_multi_in_out_1x1(X, K):c_i, h, w = X.shapec_o = K.shape[0]X = X.reshape((c_i, h * w))K = K.reshape((c_o, c_i))# 全连接层中的矩阵乘法Y = torch.matmul(K, X)return Y.reshape((c_o, h, w))

6.5 汇聚层

  通常当处理图像时,希望逐渐降低隐藏表示的空间分辨率、聚集信息,这样随着我们在神经网络中层叠的上升,每个神经元对其敏感的感受野(输入)就越大。
  本节将介绍汇聚(pooling)层,它具有双重目的:降低卷积层对位置的敏感性,同时降低对空间降采样表示的敏感性。

6.5.1 最大汇聚层和平均汇聚层

  与卷积层类似,汇聚层运算符由一个固定形状的窗口组成,该窗口根据其步幅大小在输入的所有区域上滑动,为固定形状窗口(有时称为汇聚窗口)遍历的每个位置计算一个输出。

  然而,不同于卷积层中的输入与卷积核之间的互相关计算,汇聚层不包含参数

池运算是确定性的,我们通常计算汇聚窗口中所有元素的最大值或平均值。这些操作分别称为最大汇聚层(maximum pooling)平均汇聚层(average pooling)

  在汇聚窗口到达的每个位置,它计算该窗口中输入子张量的最大值或平均值。计算最大值或平均值是取决于使用了最大汇聚层还是平均汇聚层。
在这里插入图片描述
  图6.5.1: 汇聚窗口形状为 2 × 2 的最大汇聚层。着色部分是第一个输出元素,以及用于计算这个输出的输入元素: max(0, 1, 3, 4) = 4.

  在下面的代码中的pool2d函数,实现汇聚层的前向传播。这类似于 6.2节中的corr2d函数。然而,这里没有卷积核,输出为输入中每个区域的最大值或平均值。

	import torchfrom torch import nnfrom d2l import torch as d2ldef pool2d(X, pool_size, mode='max'):p_h, p_w = pool_sizeY = 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

6.5.2 填充和步幅

  与卷积层一样,汇聚层也可以改变输出形状。和以前一样,我们可以通过填充和步幅以获得所需的输出形状。

	X = torch.arange(16, dtype=torch.float32).reshape((1, 1, 4, 4))X====================================tensor([[[  [ 0., 1., 2., 3.],[ 4., 5., 6., 7.],[ 8., 9., 10., 11.],[12., 13., 14., 15.]]]])

  默认情况下,深度学习框架中的步幅与汇聚窗口的大小相同。

  填充和步幅可以手动设定。

	pool2d = nn.MaxPool2d(3, padding=1, stride=2)pool2d(X)====================================tensor([[[  [ 5., 7.],[13., 15.]]]])

  也可以设定一个任意大小的矩形汇聚窗口,并分别设定填充和步幅的高度和宽度。

	pool2d = nn.MaxPool2d((2, 3), stride=(2, 3), padding=(0, 1))pool2d(X)===============================tensor([[[  [ 5., 7.],[13., 15.]]]])

6.5.3 多个通道

  在处理多通道输入数据时,汇聚层在每个输入通道上单独运算,而不是像卷积层一样在通道上对输入进行汇总。这意味着汇聚层的输出通道数与输入通道数相同。

6.6 卷积神经网络(LeNet)

  本节将介绍LeNet,它是最早发布的卷积神经网络之一,因其在计算机视觉任务中的高效性能而受到广泛关注。这个模型是由AT&T贝尔实验室的研究员Yann LeCun在1989年提出的(并以其命名),目的是识别图像(LeCun et al., 1998)中的手写数字。当时,Yann LeCun发表了第一篇通过反向传播成功训练卷积神经网络的研究,这项工作代表了十多年来神经网络研究开发的成果。

6.6.1 LeNet

  总体来看,LeNet(LeNet‐5)由两个部分组成:

  • 卷积编码器:由两个卷积层组成;
  • 全连接层密集块:由三个全连接层组成。

  该架构如 图6.6.1所示。
在这里插入图片描述
  图6.6.1: LeNet中的数据流。输入是手写数字,输出为10种可能结果的概率。

  为了将卷积块的输出传递给稠密块,必须在小批量中展平每个样本。换之,将这个四维输入转换成全连接层所期望的二维输入

  这里的二维表示第一个维度索引小批量中的样本第二个维度给出每个样本的平面向量表示

  LeNet的稠密块有三个全连接层,分别有120、84和10个输出。因为在执行分类任务,所以输出层的10维对应于最后输出结果的数量。

  通过下面的LeNet代码,可以看出用深度学习框架实现此类模型非常简单。只需要实例化一个Sequential块并将需要的层连接在一起。

	import torchfrom torch import nnfrom d2l import torch as d2lnet = nn.Sequential(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))

  对原始模型做了一点小改动,去掉了最后一层的高斯激活。除此之外,这个网络与最初的LeNet‐5一致。

  下面,将一个大小为28 × 28的单通道(黑白)图像通过LeNet。通过在每一层打印输出的形状,可以检查模型,以确保其操作与期望的 图6.6.2一致。
在这里插入图片描述

	X = torch.rand(size=(1, 1, 28, 28), dtype=torch.float32)for layer in net:X = layer(X)print(layer.__class__.__name__,'output shape: \t',X.shape)==============================================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.6.2 模型训练

  现在已经实现了LeNet,看看LeNet在Fashion‐MNIST数据集上的表现。

	batch_size = 256train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size=batch_size)

  为了进行评估,需要对 3.6节中描述的evaluate_accuracy函数进行轻微的修改。由于完整的数据集位于内存中,因此在模型使用GPU计算数据集之前,需要将其复制到显存中。

	def evaluate_accuracy_gpu(net, data_iter, device=None): #@save"""使用GPU计算模型在数据集上的精度"""if isinstance(net, nn.Module):net.eval() # 设置为评估模式if not device:device = next(iter(net.parameters())).device# 正确预测的数量,总预测的数量metric = d2l.Accumulator(2)with torch.no_grad():for X, y in data_iter:if isinstance(X, list):# BERT微调所需的(之后将介绍)X = [x.to(device) for x in X]else:X = X.to(device)y = y.to(device)metric.add(d2l.accuracy(net(X), y), y.numel())return metric[0] / metric[1]

  为了使用GPU,我们还需要一点小改动。需要将每一小批量数据移动到指定的设备(例如GPU)上。

  如下所示,由于将实现多层神经网络,因此将主要使用高级API。以下训练函数假定从高级API创建的模型作为输入,并进行相应的优化。

  使用在4.8.2节中介绍的Xavier随机初始化模型参数。与全连接层一样,使用交叉熵损失函数小批量随机梯度下降

	#@savedef 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)net.apply(init_weights)print('training on', device)net.to(device)optimizer = torch.optim.SGD(net.parameters(), lr=lr)loss = nn.CrossEntropyLoss()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):timer.start()optimizer.zero_grad()X, y = X.to(device), y.to(device)y_hat = net(X)l = loss(y_hat, y)l.backward()optimizer.step()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)}')

  现在,训练和评估LeNet‐5模型。

	lr, num_epochs = 0.9, 10train_ch6(net, train_iter, test_iter, num_epochs, lr, d2l.try_gpu())====================================loss 0.469, train acc 0.823, test acc 0.77955296.6 examples/sec on cuda:0

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/592108.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

194.【2023年华为OD机试真题(C卷)】单行道汽车通行时间(迭代计算—JavaPythonC++JS实现)

请到本专栏顶置查阅最新的华为OD机试宝典 点击跳转到本专栏-算法之翼:华为OD机试 🚀你的旅程将在这里启航!本专栏所有题目均包含优质解题思路,高质量解题代码,详细代码讲解,助你深入学习,深度掌握! 文章目录 【2023年华为OD机试真题(C卷)】单行道汽车通行时间(…

基于SpringBoot的智慧生活商城系统

文章目录 项目介绍主要功能截图:部分代码展示设计总结项目获取方式🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目介绍 基于SpringBoot的智慧生活商城系统,java…

[足式机器人]Part2 Dr. CAN学习笔记-自动控制原理Ch1-9PID控制器

本文仅供学习使用 本文参考: B站:DR_CAN Dr. CAN学习笔记-自动控制原理Ch1-9PID控制器) P —— Proportional I —— Integral D —— Derivative 当前误差/过去误差/误差的变化趋势 K p ⋅ e K_{\mathrm{p}}\cdot e Kp​⋅e:比…

golang并发编程-channel

在golang 并发编程里,经常会听到一句话:不要通过共享内存进行通信,通过通信来共享内存。下面我们会介绍下channel, 通过源码的方式去了解channel是怎么工作的。 基本结构 流程图 代码解读 type hchan struct {qcount uint // …

Qt(三):udp组播的发送与接收

1. 创建UDP套接字 使用QUdpSocket类创建一个UDP套接字。 udpSendnew QUdpSocket(this);udpRecenew QUdpSocket(this); 2. 绑定套接字 绑定套接字到一个本地地址和端口。可以使用bind()函数来完成。 如果要在组播中发送数据,可以将套接字绑定到一个通配符地址&#…

Ubuntu安全重启方法

装Ubuntu系统时,经常会遇到各种各样的问题导致系统崩溃、卡死、黑屏等情况,新手或者小白可能直接选择长按电源键强制重启了 建议不要长按电源强制重启,很有可能会损坏磁盘文件,如果硬重启的时候,硬件还在读写数据的话&…

uniapp中uview组件丰富的Code 验证码输入框的使用方法

目录 基本使用 #自定义提示语 #保持倒计时 API #Props #Methods #Event 基本使用 通过ref获取组件对象,再执行后面的操作,见下方示例。 通过seconds设置需要倒计的秒数(默认60)通过ref调用组件内部的start方法,开始倒计时通过监听cha…

智慧旅游手机APP开发解决方案

我国的旅游市场已经逐渐地走向饱和,想要发展,就必须要寻求新的发展模式。本项目就是抓住贵州的交通飞速发展的契机,以高速为主线,高速周边的景点、酒店为依托,高速维修为辅线,借助今天得到广泛应用的智能移…

SpringBoot整合多数据源,并支持动态新增与切换

SpringBoot整合多数据源,并支持动态新增与切换 一、概述 在项目的开发过程中,遇到了需要从数据库中动态查询新的数据源信息并切换到该数据源做相应的查询操作,这样就产生了动态切换数据源的场景。为了能够灵活地指定具体的数据库&#xff0…

【深度学习:SENet】信道注意力和挤压激励网络(SENet):图像识别的新突破

【深度学习:SENet】信道注意力和挤压激励网络(SENet):图像识别的新突破 为什么有效如何实现工作原理应用案例 挤压和激励网络(SENets)为卷积神经网络(CNN)引入了一个新的构建模块&am…

2024年1月1日答案

a)i. V B B V C C 16 V V_{BB} V_{CC} 16V VBB​VCC​16V R t h R B R E R B R E 10 k Ω 3 k Ω 10 k Ω 3 k Ω ≈ 2.31 k Ω R_{th} \frac{R_B \times R_E}{R_B R_E} \frac{10k\Omega \times 3k\Omega}{10k\Omega 3k\Omega} \approx 2.31k\Omega Rth​RB​R…

Web网页开发-盒模型-笔记

1.CSS的三种显示方式 (1)块级元素:标签所占区域默认为一行 特点:一行一个 可设宽高 (2)行内元素:标签所占区域由内容顶开,行内元素无法使用text-align 特点:一行多个 不可设宽高,margin上下和padding上下都不能改变位…

克服幻觉:提升语言模型在自然语言处理中的准确性与可靠性

随着语言模型(LLM)在自然语言处理(NLP)中的应用日益普及,它们在文本生成、机器翻译、情感分析等许多任务中展现出惊人的能力。然而,这些模型也常常显示出一个被称作“幻觉”(hallucination&…

扫拖一体机哪个牌子好用?2024旗舰洗地机总结

近年来,家庭清洁的方式发生了翻天覆地的变化。在这场前所未有的洗地机创新浪潮中,消费者们迎来了更为便捷高效的家庭清洁解决方案。然而,随着市场竞争的激烈,面对众多品牌和型号的家用洗地机,究竟哪款扫拖一体机好用呢…

实现区域地图散点图效果,vue+echart地图+散点图

需求:根据后端返回的定位坐标数据实现定位渲染 1.效果图 2.准备工作,在main.js和index.js文件中添加以下内容 main.js app.use(BaiduMap, {// ak 是在百度地图开发者平台申请的密钥 详见 http://lbsyun.baidu.com/apiconsole/key */ak: sRDDfAKpCSG5iF1rvwph4Q95M…

在spring boot中集成druid的数据监控页面

参考例子见这里,亲测有效! 核心就是要加入下面的配置code Configuration public class DruidDataMonitorConfig {Beanpublic ServletRegistrationBean statViewServlet() {ServletRegistrationBean bean new ServletRegistrationBean(new StatViewServ…

使用 go-elasticsearch v8 基本请求

使用 go-elasticsearch 请求示例 你可以通过参考Go 官方文档找到简单的示例,所以我认为先看看这个是个好主意。 连接客户端有两种方式,如下图。 至于两者的特点,TypedClient有类型,更容易编写,但文档较少。另外&…

以 RoCE+软件定义存储同时实现信创转型与架构升级

目前,不少企业数据中心使用 FC 交换机和集中式 SAN 存储(以下简称“FC-SAN 架构”),支持核心业务系统、数据库、AI/ML 等高性能业务场景。而在开展 IT 基础架构信创转型时,很多用户受限于国外交换机:FC 交换…

往期精彩推荐

所有的内容都在这个博客中,此博客为推广导航博客,过后会删掉https://blog.csdn.net/weixin_41620184/article/details/135042416 往期精彩:快来学习吧~~~ 机器学习算法应用场景与评价指标机器学习算法—分类机器学习算法—回归PySpark大数据处…

SVM 保存和加载模型参数

在 Python 中,你可以使用 scikit-learn 库中的 joblib 或 pickle 模块来保存和加载 SVM 模型的参数。以下是一个简单的示例代码,演示了如何使用 joblib 模块保存和加载 SVM 模型的参数: 保存模型参数: from sklearn import svm …