Deep learning学习笔记

lec 1:Regression

1.5 Linear neural networks for regression线性神经网络的回归

I parameterizing output layer,
I handling data,
I specifying loss function,
I training model.
浅层网络包括线性模型,其中包含了许多经典的统计预测方法,如linear回归和softmax回归。

1.5.1 Linear regression

输入特征的仿射变换Affine transformation(特征的加权和的线性变换和附加偏差的变换)

损失函数为平方误差squared error

当训练模型时,找到在所有训练例子中使平均损失最小的参数(w∗,b∗):

 即使我们不能解析地求解模型,我们仍然可以使用梯度下降算法来训练模型(迭代更新参数的方向,逐步降低损失函数)。

损失函数的导数是数据集中每个示例的损失的平均值。非常慢,因为我们必须通过整个数据集来进行更新。

1.5.2 Minibatch stochastic gradient descent 小批量随机梯度下降

 策略:做一小批(Minibatch)观察。大小(size)取决于许多因素(内存、加速器的数量、图层的选择、总数据集的大小)。32到256之间,2^m(2的m次幂)这样的形式是一个好的开始。

●小批量随机梯度下降(SGD):

  初始化参数。在每次迭代t中,

  用|B|训练示例随机抽样一个小批量Bt

  计算小批量w.r.t.的平均损失梯度因素;

  使用η作为学习速率,执行更新:

  

小批量处理的大小和学习率是由用户定义的。这种在训练循环中没有更新的可调参数被称为超参数(hyperparameters)。

经过多次迭代训练后,我们记录了估计的参数(ˆw,ˆb)。这些不是损失的最小化。

1.5.3 Maximum likelihood estimation最大似然估计

最小化平方误差损失相当于线性模型在加性高斯噪声下的最大似然估计:

 线性回归是一个单层全连接的神经网络,有d个输入:x1,……,xd 和一个单一的输出。

 1.5.4 Synthetic data合成数据

从标准正态set(X∈R^1000×2)中生成1000个examples,用于训练集和验证集。

设置w = [2,−3.4]^T,b=4.。绘制ε~N(0,0.01²)。

#Synthetic data
import random
import torch
from d2l import torch as d2l
data = d2l.SyntheticRegressionData(w=torch.tensor([2,-3.4]), b=4.2, 
noise=0.01, num_train=1000, num_val=1000, batch_size=32)

模型训练需要多次传递一个数据集,每次使用一个小批处理来更新模型。数据处理器生成批量大小(batch_size)的小批量。每个小批处理都是一个特征和标签的元组。

在训练train模式下,我们以随机的顺序读取数据。在验证test模式下,按预定义的顺序读取数据可能对调试很重要。

X, y = next(iter(data.train_dataloader())) # inspect first minibatch
X.shape, y.shape # X: [32, 2], y: [32, 1]
len(data.train_dataloader()) # no. of batches: 32

1.5.5 Implementing linear regression 线性回归的实现

使用具有学习率lr=0.03max_epochs=3的小批量SGD训练线性回归模型。在每个epoch中,我们遍历整个训练数据集,并通过每个示例传递一次。在每个epoch结束时使用验证数据集来测量模型的性能。

通过从N(0,0.01²)采样来初始化权重,并将偏差设置为0。

model = d2l.LinearRegression(lr=0.03)
trainer = d2l.Trainer(max_epochs=3)
trainer.fit(model, data)
w, b = model.get_w_b()
#compute differences
data.w - w.reshape(data.w.shape)
data.b - b
# error in estimating w: tensor([0.0025, -0.0070])
# error in estimating b: tensor([0.0096])

 图中结果表明:估计的参数接近于真值。

•对于深度模型,不存在参数的唯一解。在机器学习中,我们不太关心恢复真正的基础参数,而是更关心能够导致准确预测的参数。

1.5.6 Generalization泛化

机器学习的基本问题是:发现能够泛化的模式。拟合更接近训练数据而不是基本分布的现象是过拟合overfitting,而对抗过拟合的技术被称为正则化regularization

在标准的监督学习中,我们假设训练数据和测试数据是从相同的分布中独立地抽取的。

训练误差Training error:根据训练集计算的统计量,

泛化错误Generalization error:一个期望的w.r.t.基础分布,

通过将模型应用于独立的测试集来估计泛化误差.

当我们有简单的模型和丰富的数据时,训练误差和泛化误差往往会很接近。当我们使用更复杂的模型或更少的例子时,我们期望训练误差会减少,但泛化差距会增加。

深度神经网络在实践中可以很好地推广,但它们太强大了,我们无法仅根据训练错误就得出结论。我们必须依靠验证(保留数据)错误来证明泛化。

——————————————————————————————————————————

当训练误差和验证误差很大,且它们之间的差距很小时,可能会出现不拟合的情况underfitting(模型表达不足)。

训练误差显著低于验证误差表示过拟合。

使用一个d度的多项式:来估计给定单一特征x的标签y。

高阶多项式的训练误差总是较低的。一个具有d = n的多项式可以完美地拟合训练集。

随着我们增加训练数据时,泛化误差通常会减少。如果有更多的数据,我们可以拟合更复杂的模型。只有当有成千上万的训练例子可用时,深度学习才优于线性模型。

K -fold  cross-validation

当训练数据稀缺时,我们可能没有足够的保留数据。在K倍交叉验证中,数据被分成K个不重叠的子集。模型训练和验证执行K次,每次在K−1个子集上进行训练,并对未使用的子集进行验证。训练和验证误差的估计是平均了K个实验的结果。

1.5.7 Weight decay 权重衰减

权重衰减可以通过限制参数采取的值来缓解过拟合。为了将w缩小到零,将其范数作为惩罚项(penalty term)添加到最小化损失的问题中:

正则化常数λ≥0是使用验证集选择的一个超参数。越小的λ对应于约束越小的w。我们通常不规范(正则化)偏差项b。

w的小批量SGD更新变为:

从上述方程式中生成数据,为了使过拟合效果更明显,设置n = 20和d = 200。

定义其中的类、数据和权重衰减:

#如何使用权重衰减(weight decay)来正则化模型
#定义了一个L2正则化项的函数l2_penalty,用来计算权重w的L2范数的平方除以2
def l2_penalty(w): return (w**2).sum() / 2
#创建数据集data
data = Data(num_train=20, num_val=100,num_inputs=200, batch_size=5)
trainer = d2l.Trainer(max_epochs=10)
#创建了一个WeightDecay模型对象model,并将权重衰减参数wd设置为0,学习率lr设置为0.01。(对照)
model = WeightDecay(wd=0, lr=0.01)
#通过将model.board.yscale设置为'log',可以在训练过程中将损失函数的值以对数尺度显示
#有助于观察权重衰减的效果。
model.board.yscale='log'
trainer.fit(model, data)
l2_penalty(model.get_w_b()[0]) # 0.1318#重新创建了一个WeightDecay模型对象model,将权重衰减参数wd设置为3,学习率lr设置为0.01(实验)
model = WeightDecay(wd=3, lr=0.01)
model.board.yscale='log'
trainer.fit(model, data)
l2_penalty(model.get_w_b()[0]) # 0.0145 比0.1318降低很多

在没有使用权值衰减时,我们非常糟糕的过拟合了。 

随着权值的衰减,训练误差增加,而验证误差减小。

lec5:Convolutional neural networks

卷积神经网络

5.1 Padding and stride

在CNN中,经过多次连续卷积后,输出会比输入小得多。例如,10层5×5卷积将240×240图像减少到200×200像素,切割出30%的图像。

为了更多地控制输出大小,使用填充padding和分层卷积strided convolutions

应用卷积层时图像周长perimeter 像素的损失:图描述了像素利用作为卷积核大小和图像中位置的函数。角落的像素很少使用。

在输入图像的边界周围添加额外的填充filler像素。将额外的像素设置为零。

垫Pad 3 x 3的输入,将其大小增加到5 x 5。输出大小增加到4 x 4。

5.1.1 Padding填充

如果我们添加ph行的填充(∼一半在上,一半在下)和pw列的填充(∼一半在左,一半在右),输出形状为:

输出的高度和宽度分别增加了ph和pw。

输入和输出相同大小:设置ph = kh−1,pw = kw−1。

①在构建网络时,更容易预测每层的输出形状。

②如果kh为奇数,则在位于高度的两侧填充ph/2行。

③如果kh为偶数,顶部填充ph/2行,底部填充ph/2c行。

④以相同的方式填充宽度的两侧。

CNN通常使用高度和宽度都为奇数(1、3、5或7)的卷积核。

#使用PyTorch中的nn.LazyConv2d模块来进行二维卷积操作
import torch
from torch import nn
from d2l import torch as d2l#创建了一个随机初始化的8x8的张量X
X = torch.rand(size=(8, 8))
#形状变换为(1, 1, 8, 8),表示(batch_size, channels, height, width)的形状。
X = X.reshape((1, 1) + X.shape) # torch.Size([1, 1, 8, 8])#创建了一个nn.LazyConv2d对象conv2d,指定输入通道数为1,卷积核大小为3x3,填充(padding)为1
#即在图像周围补充1个像素。对输入X进行卷积操作conv2d(X),输出的形状为(1, 1, 8, 8),与输入形状相同
conv2d = nn.LazyConv2d(1, kernel_size=3, padding=1) # 3 x 3 kernel, padding: 1 (all sides)
conv2d(X).shape # torch.Size([1, 1, 8, 8])#创建了一个nn.LazyConv2d对象conv2d,指定输入通道数为1,卷积核大小为(5, 3),填充(padding)为(2, 1)
#即在高度上补充2个像素,在宽度上补充1个像素。输出的形状仍然为(1, 1, 8, 8),与输入形状相同。
conv2d = nn.LazyConv2d(1, kernel_size=(5,3), padding=(2,1)) # (5-1)/2 =2, (3-1)/2=1
conv2d(X).shape # torch.Size([1, 1, 8, 8])

case1:8×8→(四周+1pixel)→10×10

case2:8×8→(上下各加2pixel,左右各加1pixel)→12×10

5.1.2 Stride步幅

当计算互相关cross-correlation时,从输入张量左上角的卷积窗口开始,然后向下和向右滑动所有位置,每次一个元素

为了提高计算效率或降采样,一次移动窗口为多个元素,跳过中间位置。如果卷积内核很大,因为它捕获了一个大面积的底层图像。

No. of rows and columns traversed per slide: stride步幅。

到目前为止,我们用步幅为1来表示高度和宽度。

Figure: 2-dim cross-correlation with stride of 3 vertically and 2 horizontally.

垂直3步,水平2步。

当生成第一列的第2个元素时,卷积窗口会向下滑动3行。

当生成第一行的第2个元素时,卷积窗口会向右滑动2列。当卷积窗口继续向右滑动2列时,就没有输出。

当高度步幅为sh,宽度步幅为sw时,输出形状为:

如果并且

输出是

如果输入的高度和宽度可按高度和宽度上的步幅整除,则输出形状为 

>将高度和宽度上的步幅设置为2,从而将输入的高度和宽度减半:

conv2d = nn.LazyConv2d(1, kernel_size=3, padding=1, stride=2) # (8-3+1+2)/2 = 4
conv2d(X).shape 
# output:
# torch.Size([1, 1, 4, 4])

 >更复杂的例子:

conv2d = nn.LazyConv2d(1, kernel_size=(3,5), padding=(0,1), stride=(3,4))
conv2d(X).shape # floor((8-3+0+3)/3)=2, floor((8-5+1+4))/4)=2
# output:
# torch.Size([1, 1, 2, 2])

5.2channels 

5.2.1 Multiple input channels 多输入通道

如果卷积核的窗口是kh×kw,我们需要一个包含每个输入通道的kh×kw张量的核(当输入通道的数目 ci > 1)。连接ci张量得到一个ci×kh×kw卷积核。

由于输入核和卷积核都有ci通道,对每个通道的2维输入张量和2维卷积核张量进行互相关,并将ci个结果(通道之和sum over channels)相加得到2维张量。

#定义了一个函数corr2d_multi_in,用于计算多输入通道的二维互相关运算。
#函数接受两个输入:X表示输入张量,K表示卷积核张量,两者都是三维张量。
#函数的实现通过使用zip(X, K)将输入张量和卷积核张量按通道组合起来,
#然后对每对通道进行二维互相关运算,并将结果求和。
#最终返回的是一个二维张量,表示所有通道的互相关运算结果的和。
def corr2d_multi_in(X, K):return sum(d2l.corr2d(x, k) for x, k in zip(X, K))#输入X是一个形状为(2, 3, 3)的张量,表示有两个通道,每个通道的大小为3x3。
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是一个形状为(2, 2, 2)的张量,表示有两个通道,每个通道的卷积核大小为2x2。
K = torch.tensor([[[0.0, 1.0], [2.0, 3.0]], [[1.0, 2.0], [3.0, 4.0]]])#调用corr2d_multi_in(X, K)将对X的每个通道与K的对应通道进行二维互相关运算
#并将结果相加,最终返回一个形状为(2, 2)的张量,表示最终的互相关运算结果。
corr2d_multi_in(X, K) # tensor([[ 56., 72.], [104., 120.]])

到目前为止,我们只有一个输出通道,但在每一层都有多个通道是必要的。随着我们在网络中的深入,我们增加信道维度,降采样downsampling以权衡空间分辨率换取更大的信道深度。

让ci和co分别我输入通道和输出通道的个数,kh和kw分别为内核的高度和宽度。为了获得多个通道的输出,为每个输出通道创建一个ci×kh×kw核张量,并将它们连接concatenate 到输出通道维度上,得到一个卷积核。

每个输出通道的互相关结果是由输出通道的卷积核计算,并从输入张量的所有通道获取输入。

通过将K的核张量与K+1和K+2连接起来,构造具有3个输出通道的平凡卷积核。

#定义了一个函数corr2d_multi_in_out,用于计算多输入通道和多输出通道的二维互相关运算。
def corr2d_multi_in_out(X, K): # calculate output of multiple channels
#使用torch.stack将每个卷积核对应的互相关运算结果堆叠起来
#最终得到一个四维张量,表示多输出通道的互相关运算结果。return torch.stack([corr2d_multi_in(X, k) for k in K], 0)
K = torch.stack((K, K + 1, K + 2), 0)
K.shape # torch.Size([3, 2, 2, 2])
X.shape # torch.Size([2, 3, 3])
#调用corr2d_multi_in_out(X, K)将对X的每个通道与K的对应通道进行二维互相关运算,并将结果堆叠起来
#最终返回一个形状为(3, 2, 2)的张量,表示三个输出通道的互相关运算结果。
corr2d_multi_in_out(X, K) # output contains 3 channels# tensor([[[ 56., 72.], [104., 120.]], # result of 1st channel is consistent
# [[ 76., 100.], [148., 172.]],
# [[ 96., 128.], [192., 224.]]])

5.2.3 1 x 1 convolutional layer  1×1卷积层

1 x 1的卷积最初可能没有意义,因为卷积关联了相邻的像素,但在深度网络中很流行。

由于使用最小窗口,1 x 1卷积不能识别高度和宽度维度上相邻元素之间的交互模式。在信道维度上只进行1 x 1卷积的计算。

图:使用1 x 1卷积核计算3个输入通道和2个输出通道计算互相关。输入和输出具有相同的高度和宽度。输出中的每个元素都是由输入图像中相同位置的元素的线性组合推导出来的。

一个1 x 1的卷积层构成一个在每个像素位置应用的全连接层fully connected layer,将ci输入值转换为co输出值,权重在像素位置之间绑定。它需要共同的权重(加上偏差)。

使用完全连接的图层实现1×1卷积。需要对矩阵乘法前后的数据形状进行调整。

X = torch.normal(0, 1, (3, 3, 3)) # X.shape: torch.Size([3, 3, 3])
K = torch.normal(0, 1, (2, 3, 1, 1)) # K.shape: torch.Size([2, 3, 1, 1])#函数corr2d_multi_in_out_1x1,用于实现1x1卷积操作。
#1x1卷积是指卷积核大小为1x1的卷积操作,通常用于调整通道数或者进行特征融合。
#函数接受两个输入:X表示输入张量,K表示卷积核张量,两者都是四维张量。
def corr2d_multi_in_out_1x1(X, K):#函数首先获取输入张量X的通道数c_i,以及卷积核张量K的输出通道数c_oc_i, h, w = X.shapec_o = K.shape[0]#将输入张量Xreshape为(c_i, h * w)的形状,其中h * w表示每个通道展平后的长度#将卷积核张量Kreshape为(c_o, c_i)的形状,以便进行矩阵乘法操作X = X.reshape((c_i, h * w)) # X.shape: torch.Size([3, 9])K = K.reshape((c_o, c_i)) # K.shape: torch.Size([2, 3])#使用torch.matmul进行矩阵乘法,得到一个形状为(c_o, h * w)的中间结果张量YY = torch.matmul(K, X) # Y.shape: torch.Size([2, 9])#将Yreshape为(c_o, h, w)的形状,即为最终的输出张量。return Y.reshape((c_o, h, w)) # Y.shape: torch.Size([2, 3, 3]) 即返回形状为(2, 3, 3)

当执行1×1卷积时,上述函数相当于之前实现的互相关函数corr2d_multi_in_out

#使用corr2d_multi_in_out_1x1和corr2d_multi_in_out两个函数分别计算输入X和卷积核K的卷积结果
#并将结果分别存储在Y1和Y2中。
Y1 = corr2d_multi_in_out_1x1(X, K)
Y2 = corr2d_multi_in_out(X, K)
#使用torch.abs计算Y1和Y2之间的绝对差值,并使用sum将所有元素相加得到总的差值
#使用assert语句检查总的差值是否小于1e-6,如果满足条件,则不会抛出异常,表示两种卷积方法得到的结果在误差范围内一致。
assert float(torch.abs(Y1 - Y2).sum()) < 1e-6

5.3 Pooling 池化

终极任务经常会问一些关于图像的全局问题(它是否包含一只猫?)。因此,最后一层final layer的单位应该对整个输入都很敏感。

通过逐步聚合信息,我们学习了一个全局表示global representation,同时在处理的中间层保留了卷积层的优势。

网络越深,每个隐藏节点所敏感的感受域就越大。降低空间分辨率加速了这一过程,因为卷积核覆盖了一个更大的有效区域。

当检测低级特征(边)时,表示应该对平移不变。在现实中,物体很难出现在同一个地方。相机的振动可能会使一切改变一个像素。池化层Pooling layers降低了卷积层对位置和空间降样本表示的敏感性。

与卷积层一样,池运算符由一个固定形状的窗口组成,根据其步幅滑动到所有输入区域上,为池窗口pooling window遍历的每个位置计算输出。

与卷积层不同,池化层没有参数。池运算符计算池窗口中元素的最大(最大池max-pooling)或平均(平均池average pooling)。

5.3.1 Maximum pooling and average pooling 最大池化和平均池化

平均池化类似于对图像的降采样。由于我们结合了来自多个相邻像素的信息,因此我们对相邻像素进行平均,以获得信噪比更好的图像。

最大池化通常是首选的,因为它从图像中选择更亮的像素。平均池化平滑的图像和尖锐的特征可能不会被识别出来。

>最大池与2 x 2池窗口。

    输出张量的高度和宽度均为2。这4个元素来自于每个池化窗口中的最大值。

#使用PyTorch中的nn.MaxPool2d和nn.AvgPool2d模块实现二维最大池化和平均池化操作。
X = torch.tensor([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0], [6.0, 7.0, 8.0]])
X = X.reshape((1,1,3,3)) # input format: (batchsize, no. of channels, height, width)#创建了一个nn.MaxPool2d对象pool2d,指定池化核大小为2x2,步幅为1。
#调用pool2d(X)进行最大池化操作,得到tensor([[[[4., 5.], [7., 8.]]]]),表示经过最大池化后的输出。
pool2d = nn.MaxPool2d(2, stride=1) # Max-pooling
pool2d(X) # tensor([[[[4., 5.], [7., 8.]]]])#创建了一个nn.AvgPool2d对象pool2d,指定池化核大小为2x2,步幅为1。
#调用pool2d(X)进行平均池化操作,得到tensor([[[[2., 3.], [5., 6.]]]]),表示经过平均池化后的输出。
pool2d = nn.AvgPool2d(2, stride=1) # Average pooling
pool2d(X) # tensor([[[[2., 3.], [5., 6.]]]])#最大池化和平均池化是常用的池化操作,用于降低特征图的空间维度,减少计算量,并且具有一定的平移不变性。

池化图层会改变输出端的形状。通过填充输入和调整步幅来实现期望的输出形状。

当池聚合来自一个区域的信息时,深度学习框架默认为匹配池窗口的大小和步幅matching pooling window sizes and stride。如果我们使用(3,3)池化窗口,默认情况下我们会得到一个(3,3)个步幅。可以手动指定过渡段和填充,以覆盖默认值。

#手动修改默认值
X = torch.arange(16,dtype=torch.float32).reshape((1,1,4,4))
# tensor([[[[ 0., 1., 2., 3.],
# [ 4., 5., 6., 7.],
# [ 8., 9., 10., 11.],
# [12., 13., 14., 15.]]]])
pool2d = nn.MaxPool2d(3)
pool2d(X) # tensor([[[[10.]]]])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.]]]])

5.3.2 Multiple channels 多通道

在处理多通道输入数据时,池化层将每个输入通道单独池化,而不是像卷积层那样在通道上的输入求和。因此池化层的输出通道的大小与输入通道的大小相同。

我们将张量X和X + 1连接在通道维数上,构造一个有2个通道的输入。池化后输出通道的大小仍然是2。

X = torch.cat((X, X + 1), 1)
X

pool2d = nn.MaxPool2d(3, padding=1, stride=2) 
pool2d(X)

练习:输入图像的大小为3x1024x1024,应用于其上的卷积核为5(out_channelsx3×2X2。输出的大小是多少?

5X1023X1023

3消掉因为channels求和,1023=1024-2+1

5.4 LeNet

现在我们有了组装全功能CNN的所有成分。之前,我们将MLP应用于Fashion-MNIST数据。

用卷积层代替全连接的层,我们保留了图像中的空间结构,并以更少的参数享受了更简洁的模型。

LeNet(1998)是计算机视觉中第一个引起关注的CNN之一。它是由Yann LeCun介绍的,用于识别图像中的手写数字。

LeNet-5有两部分: (i)具有2个卷积层的卷积编码器和(ii)具有3个完全连接层的密集块。

每个卷积层使用一个5 x 5的核和sigmoid激活函数。第一卷积层有6个输出通道,第二层有16个输出通道。每个2 x 2的平均池(2步)减少4倍(ReLUs,尚未发现的最大池)。

卷积块输出形状:(batch size, no. of channel, height, width).

要将输出从卷积块convolutional block传递到密集块dense block,在小批量中将每个例子变平flatten(将4维输入转换为完全连接层期望的2维输入)。2-dim表示使用第一个dim来索引小批中的示例,第2个dim来给出每个示例的向量表示。

密集块有3个完全连接的层(分别为120、84和10个输出)。

LeNet模型可以使用d2l.Lenet()来实现,它使用Xavier初始化和通过顺序块将层链连接在一起。

要可视化LeNet,将一个单通道(黑白)28x28图像的形状通过它,并在每一层打印输出形状。

#打印LeNet模型在输入为(1, 1, 28, 28)时各层的输出形状信息
model = d2l.LeNet()
model.layer_summary((1, 1, 28, 28))

output:

第一个卷积层使用2的填充来补偿由5×5内核导致的高度和宽度的减少。第二个卷积层放弃了填充物。因此,高度和宽度都减少了4个像素。

当我们往层的上方走时,在第1卷积层后,通道数从1增加到6,在第2卷积层后增加16,但是每个池化层的高度和宽度都减半了。每一层完全连接的层降低维数,最终输出的维度与用于分类的类相匹配。

>>>在Fashion-MNIST上应用LeNet。与MLPs一样,损失函数是交叉熵cross-entropy的,我们通过小批量SGD将其最小化。

#应用LeNet
#使用了d2l库中的Trainer来训练LeNet模型在FashionMNIST数据集上#创建了一个Trainer对象trainer,设置最大迭代次数为10,使用1个GPU进行训练
trainer = d2l.Trainer(max_epochs=10, num_gpus=1)#创建了一个FashionMNIST数据集对象data,设置批量大小为128
data = d2l.FashionMNIST(batch_size=128)#创建了一个LeNet模型对象model,并设置学习率为0.1
model = d2l.LeNet(lr = 0.1)#调用data.get_dataloader(True)获取训练数据集的一个批量样本,
#然后将该批量样本作为参数调用model.apply_init方法,
#传入d2l.init_cnn函数,用于初始化LeNet模型的参数
model.apply_init([next(iter(data.get_dataloader(True)))[0]], d2l.init_cnn)
#使用trainer.fit方法训练模型
trainer.fit(model, data)

CNN具有更少的参数,但它们的计算成本可能比类似的深度MLPs更昂贵,因为每个参数都参与了更多的乘法。

访问GPU可以加速训练(d2l.Trainer 类处理所有细节,并在默认情况下初始化可用设备上的模型参数)。

通过对CNN基础的理解,我们研究了现代的CNN架构,它们也可以作为高级任务(分割、目标检测、风格转换)的特征生成器。

5.5 Modern convolutional neural networks  现代卷积神经网络

深度神经网络的概念很简单(将一堆层堆叠在一起),但性能因架构和超参数选择而不同。

AlexNet、VGG网络、网络网络(NiN)、谷歌网络和剩余网络(ResNet)是计算机视觉中流行的体系结构。

cnn在LeNet之后就广为人知了,但并没有立即主导该领域。在大数据集上训练cnn的可行性尚未建立,计算机视觉集中于手工工程特征提取管道。

神经网络训练的技巧(参数初始化,非压缩激活函数,有效的正则化,SGD变体)仍然缺失。

直到2012年,表示都是机械计算的,但一些研究人员认为,特征应该由多层分层组成,每个层都有可学习的参数。

对于图像来说,最低的层可以检测边缘、颜色和纹理,类似于动物的视觉系统处理其输入的方式。

5.6 Representation learning

5.6.1 Representation learning 表示学习

第一个现代CNN以其发明者之一亚历克斯·克里热夫斯基的名字命名为AlexNet ,是对LeNet的进化改进。它在2012年的ImageNet挑战中取得了优异的性能,并首次展示了学习到的特征可以超越手工设计的特征,打破了计算机视觉中的范式。

在最低层中,模型学习到了与传统过滤器相似的特征提取器。

更高的层次建立在这些表示形式之上,以代表更大的结构,比如眼睛、鼻子。

甚至更高的层次也代表了整个物体,如人、飞机、狗。

最终隐藏状态学习图像的紧凑表示,总结其内容,这样可以很容易地分离来自不同类别的数据。

AlexNet和LeNet有许多相同的架构元素,但AlexNet要大得多,接受了更多的数据和更快的gpu训练。

5.6.2 Missing ingredients 缺少成分

Data

具有许多层次的深度模型需要大量的数据才能显著优于传统方法(线性方法和核方法)。

鉴于20世纪90年代计算机的存储空间有限,研究依赖于微小的数据集。

ImageNet数据集于2009年发布,挑战研究人员从10^6个例子中学习模型,每个1000个来自1000个不同类别的物体的1000个。

这种规模是前所未有的,图像的高分辨率为224 x 224像素,具有更多的视觉细节,允许形成更高层次的特征。

相关竞赛(ImageNet大规模视觉识别)推动了计算机视觉和机器学习研究的向前发展。

Hardware
训练深度学习模型可能需要数百个时代。每次迭代都将数据通过多层计算成本昂贵的线性代数来传递。
gpu使深度学习成为可能。这些芯片(用于计算机游戏)为计算机图形任务所需的高通量矩阵向量产品进行了优化。这个数学方法类似于计算卷积层。
2004年,NVIDIA开始为一般计算操作优化gpu。
每个CPU核心都很强大,非常适合运行各种具有复杂控制流的程序,但构建成本很高。
相比之下,gpu有数千个小的处理元素,它们被分成更大的组。每个核心都很弱,但总的核心都没有。这种核心使gpu比cpu快几个数量级。
Krizhevesty等人(2012)在CNN中的硬件卷积和矩阵乘法实现并行化,实现了在gpu上运行的深度CNN,取得了重大突破。

5.6.4 AlexNet

AlexNet和LeNet的架构是相似的,但有显著的差异。

1.AlexNet比LeNet5更深,有8层:5个卷积层,2个完全连接的隐藏层(4096个单元)和1个完全连接的输出层。

2.AlexNet使用ReLU代替sigMIU作为激活函数。

在AlexNet的第一层中,卷积窗口是11 x 11,在第二层中减少到5 x 5,然后是3 x 3。
ImageNet中的图像比MNIST更高、更宽。因此,对象倾向于占据更多的像素,需要更大的卷积窗口来捕获对象。
在第1、第2和第5个卷积层之后,AlexNet添加了最大池化层,窗口为3 x 3,步幅为2。
AlexNet通过退出来控制全连接层的模型复杂度,而LeNet只使用权值衰减。
Activation functions
AlexNet将sigmoid激活函数更改为ReLU。
①ReLU的计算更简单,在sigmoid符号中没有指数化。
②ReLU使模型在不同参数初始化下的训练更容易。
③当sigmoid模型的输出非常接近于0或1时,梯度几乎为0,因此反向传播不能继续更新一些模型参数。因此,如果模型参数没有正确初始化,sigmoid函数的梯度几乎为0,模型无法有效训练。
④而ReLU在正区间内的梯度始终为1。
AlexNet的训练循环增加了大量的图像增强 image augmentation(翻转、剪切、颜色变化),使模型更加健壮。较大的样本量有效地减少了过拟合。
#定义了一个AlexNet模型,继承自d2l库中的Classifier类。
#AlexNet是一个经典的卷积神经网络模型,常用于图像分类任务。
class AlexNet(d2l.Classifier):def __init__(self, lr=0.1, num_classes=10):#在初始化方法__init__中,调用了父类的__init__方法,并使用save_hyperparameters保存了超参数。super().__init__()self.save_hyperparameters()#定义了一个包含多层卷积、激活函数、池化、全连接层和Dropout的网络结构self.net = nn.Sequential(#第一层卷积:96个输出通道,卷积核大小为11x11,步幅为4,填充为1nn.LazyConv2d(96, kernel_size=11, stride=4, padding=1),#ReLU激活函数;nn.ReLU(),#最大池化层:池化核大小为3x3,步幅为2nn.MaxPool2d(kernel_size=3, stride=2),#第二层卷积:256个输出通道,卷积核大小为5x5,填充为2nn.LazyConv2d(256, kernel_size=5, padding=2), #ReLU激活函数nn.ReLU(),#最大池化层:池化核大小为3x3,步幅为2nn.MaxPool2d(kernel_size=3, stride=2),#第三层卷积:384个输出通道,卷积核大小为3x3,填充为1nn.LazyConv2d(384, kernel_size=3, padding=1), #ReLU激活函数nn.ReLU(),#第四层卷积:384个输出通道,卷积核大小为3x3,填充为1nn.LazyConv2d(384, kernel_size=3, padding=1), #ReLU激活函数nn.ReLU(),#第五层卷积:256个输出通道,卷积核大小为3x3,填充为1nn.LazyConv2d(256, kernel_size=3, padding=1), #ReLU激活函数nn.ReLU(),#最大池化层:池化核大小为3x3,步幅为2nn.MaxPool2d(kernel_size=3, stride=2), #将特征图展平为一维向量nn.Flatten(),#全连接层:4096个神经元nn.LazyLinear(4096), #ReLU激活函数nn.ReLU(), #Dropout:丢弃概率为0.5nn.Dropout(p=0.5),#全连接层:4096个神经元nn.LazyLinear(4096), #ReLU激活函数nn.ReLU(),#Dropout:丢弃概率为0.5nn.Dropout(p=0.5),#全连接层:输出层,神经元数量为num_classes,即类别数nn.LazyLinear(num_classes))

我们构建了一个高度和宽度均为224的单通道数据示例来观察每一层的输出形状。
虽然AlexNet是在ImageNet上训练的,但我们在这里使用Fashion-MNIST来训练ImageNet模型,甚至在现代GPU上也需要几个小时/天的时间。
为了将AlexNet应用于Fashion-MNIST中的低分辨率图像,我们使用  d2l.FashionMNIST 中的 resize将它们从28×28上采样到224×224。这不是一个聪明的做法,因为它在不添加信息的情况下增加了计算的复杂性。
与LeNet相比,AlexNet训练的主要变化是使用更低的学习率和更慢的训练,这是由于更深更宽的网络,更高的图像分辨率和更昂贵的卷积。
#使用了定义的AlexNet模型在FashionMNIST数据集上进行训练。
#首先,创建了一个AlexNet模型对象model,并设置学习率为0.01。
model = AlexNet(lr=0.01)
#创建了一个FashionMNIST数据集对象data,设置批量大小为128,
#并调整图像大小为(224, 224)以适应AlexNet的输入尺寸要求。
data = d2l.FashionMNIST(batch_size=128, resize=(224, 224))
#创建了一个Trainer对象trainer,设置最大迭代次数为10,使用1个GPU进行训练。
trainer = d2l.Trainer(max_epochs=10, num_gpus=1)
#对模型进行训练。
trainer.fit(model, data)

 练习:

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

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

相关文章

C++中的拷贝构造函数

一、拷贝构造函数的概念 拷贝构造函数用于创建一个与已有对象相同的对象&#xff0c;本质上也是构造函数的重载 拷贝构造函数只有一个类型为 const 类类型引用的形参&#xff0c;当我们要创建一个与已存在对象相同的对象时&#xff0c;由编译器自动调用拷贝构造函数。 clas…

简单的edge浏览器插件开发记录

今天在浏览某些网页的时候&#xff0c;我想要屏蔽掉某些信息或者修改网页中的文本的颜色、背景等等。于是在浏览器的控制台中直接输入JavaScript操作dom完成了我想要的功能。但是每次在网页之间跳转该功能都会消失&#xff0c;我需要反复复制粘贴js脚本&#xff0c;无法实现自动…

MATLAB知识点:exprnd函数(★★☆☆☆)生成指数分布的随机数

讲解视频&#xff1a;可以在bilibili搜索《MATLAB教程新手入门篇——数学建模清风主讲》。​ MATLAB教程新手入门篇&#xff08;数学建模清风主讲&#xff0c;适合零基础同学观看&#xff09;_哔哩哔哩_bilibili 节选自第3章&#xff1a;课后习题讲解中拓展的函数 在讲解第三…

【革新你的社交形象】用AI创意头像应用,让你的头像独一无二!

在这个数字化时代&#xff0c;社交媒体已经成为我们生活中不可或缺的一部分。你是否曾经为了找到一个既能表达自己个性&#xff0c;又足够吸引眼球的头像而苦恼&#xff1f;现在&#xff0c;有了我们全新推出的AI创意头像应用&#xff0c;你的这一困扰将成为过去&#xff01; …

React入门到精通:掌握前端开发的必备技能!

介绍&#xff1a;React是一个由Facebook开发和维护的JavaScript库&#xff0c;用于构建用户界面&#xff0c;特别是用于构建单页应用程序和移动应用程序的用户界面。以下是对React的详细介绍&#xff1a; 虚拟DOM&#xff1a;React通过使用虚拟DOM&#xff08;Document Object …

【Deep Learning 2】神经网络的优化

&#x1f31e;欢迎来到PyTorch的世界 &#x1f308;博客主页&#xff1a;卿云阁 &#x1f48c;欢迎关注&#x1f389;点赞&#x1f44d;收藏⭐️留言&#x1f4dd; &#x1f31f;本文由卿云阁原创&#xff01; &#x1f4c6;首发时间&#xff1a;&#x1f339;2024年2月16日&a…

java8-重构、测试、调试

8.1.1 改善代码的可读性 改善代码的可读性到底意味着什么?我们很难定义什么是好的可读性&#xff0c;因为这可能非常主观。通常的理解是&#xff0c;“别人理解这段代码的难易程度”。改善可读性意味着你要确保你的代码能非常容易地被包括自己在内的所有人理解和维护。为了确保…

阿里云“BGP(多线)”和“BGP(多线)_精品”区别价格对比

阿里云香港等地域服务器的网络线路类型可以选择BGP&#xff08;多线&#xff09;和 BGP&#xff08;多线&#xff09;精品&#xff0c;普通的BGP多线和精品有什么区别&#xff1f;BGP&#xff08;多线&#xff09;适用于香港本地、香港和海外之间的互联网访问。使用BGP&#xf…

悦纳自己:拥抱个人局限,开启成长之旅

悦纳自己&#xff1a;拥抱个人局限&#xff0c;开启成长之旅 在人生的旅途中&#xff0c;我们每个人都会面临无数的挑战和选择。有时我们会因为这些挑战而感到焦虑和不安&#xff0c;因为我们害怕失败&#xff0c;害怕无法达到预期的目标。然而&#xff0c;真正重要的是我们如何…

Selenium实战教程系列(三)--- Selenium中的动作

Selenium中针对元素进行的动作在代码中可以分为两类&#xff1a; Selenium::WebDriver::ActionBuilder类中的动作方法Selenium::WebDriver::Element类中的动作方法 其中ActionBuilder类中的动作方法比较丰富&#xff0c;基本涵盖了所有可以进行的操作。 而Element类的动作比较…

Linux目录结构

Linux常用目录结构 /&#xff1a;根目录存放在系统的所有文件 ~&#xff1a;一般特指当前用户的家目录。root用户一般为&#xff1a;/root&#xff0c;普通用户为&#xff1a;/home/用户名 /home&#xff1a;新用户新建时&#xff0c;会在此目录建立新的用户文件&#xff0c;…

C#根据权重抽取随机数

&#xff08;游戏中一个很常见的简单功能&#xff0c;比如抽卡抽奖抽道具&#xff0c;或者一个怪物有多种攻击动作&#xff0c;按不同的权重随机出个攻击动作等等……&#xff09; 假如有三种物品 A、B、C&#xff0c;对应的权重分别是A&#xff08;50&#xff09;&#xff0c…

积分(二)——复化Simpson(C++)

前言 前言 simpson积分 simpson积分公式 ∫ a b f ( x ) d x ≈ b − a 6 [ f ( a ) f ( b ) 4 f ( a b 2 ) ] \int_{a}^{b}f(x)dx \approx \frac{b-a}{6}[f(a)f(b)4f(\frac{ab}{2})] ∫ab​f(x)dx≈6b−a​[f(a)f(b)4f(2ab​)] 与梯形积分类似&#xff0c;当区间[a,b]较…

浅析Linux追踪技术之ftrace:综述

文章目录 概述Ftrace工作原理Ftrace追踪器Ftrace探测技术GCC的profile特性 tracefs文件系统控制文件trace信息 Ftrace使用tracer配置步骤function tracer使用示例 相关参考 概述 Ftrace&#xff0c;全称Function Tracer&#xff0c;是一个内部跟踪器&#xff0c;旨在帮助系统的…

关于项目中websocket的socket.io客户端js库的应用

1.如何使用客户端js库? pnpm add socket.io-client2.如何建立连接&#xff1f; import io from socket.io-client // 参数1&#xff1a;不传默认是当前服务域名&#xff0c;开发中传入服务器地址 // 参数2&#xff1a;配置参数&#xff0c;根据需要再来介绍 const socket i…

JavaScript中null和undefined的区别

JavaScript中null和undefined是两个特殊的值&#xff0c;经常在编程中遇到。虽然它们经常被混淆&#xff0c;但它们有着不同的含义和用法。本文将详细介绍JavaScript中null和undefined的区别&#xff0c;帮助开发者更好地理解和使用它们。 首先&#xff0c;让我们来了解一下nu…

EF Core 模型优先——根据类对象创建数据表

需要的nuget包&#xff1a; Microsoft.EntityframeworkCore.SqlServer &#xff08;根据自己的数据库类型选择对应的nuget包&#xff09; Microsoft.EntityframeworkCore.Tools Microsoft.VisualStudio.Web.CodeGeneration.Design 说明&#xff1a; &#xff08;1&#xf…

[java基础揉碎]数组 值拷贝和引用拷贝的赋值方式

目录 数组的介绍 为什么有数组 数组的三种使用方式 动态初始化: 静态初始化: 数组使用注意事项和细节 值拷贝和引用拷贝的赋值方式 数组反转: 数组拷贝: 数组的介绍 数组可以存放多个同一类型的数据。数组也是一种数据类型&#xff0c;是引用类型。 即&#xff1a;数组…

算法刷题day13

目录 引言一、蜗牛 引言 今天时间有点紧&#xff0c;只搞了一道题目&#xff0c;不过确实搞了三个小时&#xff0c;才搞完&#xff0c;主要是也有点晚了&#xff0c;也好累啊&#xff0c;不过也还是可以的&#xff0c;学了状态DP&#xff0c;把建图和spfa算法熟悉了一下&#…

WEB APIs(2)

应用定时器可以写一个定时轮播图&#xff0c;如下 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport&qu…