总结

行情

CV领域已经成熟了,最近都在搞产品,没搞CNN的设计

编码过程

数据
数据预处理
模型
参数,初始化参数
超参数
损失函数,先计算损失,清空梯度(防止有累积的梯度),再对损失后向传播计算损失关于参数的梯度
优化算法,使用优化算法更新参数
训练求参数

线性回归训练过程

# 开始训练
num_epochs=3
for epoch in range(num_epochs):# 获取小批量样本for X,y in data_iter:# net中带有w和b,传入x即可l=loss(net(X),y)# 梯度请0trainer.zero_grad()# 后向传播,内部torch帮助求sum()了l.backward()# 走一步 更新1次w和btrainer.step()l=loss(net(features),labels)# {1:f} 把l用浮点数方式表示print(f'epoch {epoch+1}, loss {l:f}')

深度学习代码过程

逻辑回归

##引入包##
import torch
from torch import nn
from d2l import torch as d2l##设置超参数##
# 超参数
num_epochs = 10
batch_size = 256##获取数据##
# 数据
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)##定义模型##
# pytorch不会调整input的形状
# 定义展平层(flatten)保留第0维,展开其他维度为1个向量,保留样本数,展开28*28=784,先展平再输入
net = nn.Sequential(nn.Flatten(), nn.Linear(784, 10))##初始化模型参数##
# 初始化参数
def init_weights(m):if type(m) == nn.Linear:nn.init.normal_(m.weight, std=0.01)# 应用到net中
net.apply(init_weights)##定义损失函数##
# 损失函数
loss = nn.CrossEntropyLoss()##定义优化算法##
# 优化算法 以一定的学习率去学习 更新参数
trainer = torch.optim.SGD(net.parameters(), lr=0.1)##开始训练##
# todo(训练的结果曲线没有 train loss)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

展平层

# pytorch不会调整input的形状
# 定义展平层(flatten)保留第0维,展开其他维度为1个向量,保留样本数,展开28*28=784,先展平再输入
net = nn.Sequential(nn.Flatten(), nn.Linear(784, 10))

MLP简介实现过程

##引入包##
import torch
from torch import nn
from d2l import torch as d2l##定义超参数##
batch_size,lr,num_epochs=256,0.1,10##获取数据##
# 数据
train_iter,test_iter=d2l.load_data_fashion_mnist(batch_size)##定义模型##
# 模型 超参数 激活函数(使用那种激活函数也可以认为是超参数)
net=nn.Sequential(nn.Flatten(),nn.Linear(784,256),nn.ReLU(),nn.Linear(256,10))##初始化参数##
# 参数 W和b
def init_weights(m):'''设置线性层的权重:param m 全连接层 线性层'''if type(m)==nn.Linear:nn.init.normal_(m.weight,std=0.01)# 模型应用参数初始化函数
net.apply(init_weights)##定义损失函数##
# 损失函数
loss=nn.CrossEntropyLoss(reduction='none')##定义优化算法##
# 优化算法
trainer=torch.optim.SGD(net.parameters(),lr=lr)##开始训练##
# 训练
d2l.train_ch3(net,train_iter,test_iter,loss,num_epochs,trainer)

数据处理

print(all_features.shape)
# 离散值处理,one-hot编码,将离散特征的值转为类别,列为离散类别,,行为1或0,行只有1个为1其余全0,行只有1个类别被激活(为1)其余都为0
# 参考网址 https://zhuanlan.zhihu.com/p/139144355
# 问题:会对number类型其作用吗?会对number其作用,为了统一处理
all_features=pd.get_dummies(all_features,dummy_na=True)
print(all_features.shape)

概念

似然函数

似然函数,最大似然估计?
答:最小化损失=最大化似然函数(最相似)
答:数据和模型给定的情况下,调整模型参数,使得模型与数据最贴合,最相似

常用优化算法

# 随机梯度下降
updater=torch.optim.SGD(params,lr=lr)# 优化算法,SGD,Adam
# adam对学习率没有SGD那么敏感
# adam优化算法,结合了AdaGrad和RMSProp两种优化算法的优点
# AdaGrad:学习率自适应,梯度自适应,Adaptive Gradient,自适应梯度
# RMSProp:学习率自适应,梯度自适应,AdaGrad的改进,克服AdaGrad梯度急剧减小的问题
optimizer=torch.optim.Adam(net.parameters(),lr=learning_rate,weight_decay=weight_decay)

问题

数据集类型不平衡。例如,二分类问题A类样本1个,B类样本9个
答:验证集的不同类别的样本数量做到相等,A类样本数:B类样本数=1:1,避免训练倾向没有通过验证集发现
答:先明确是不是真实世界就是这样的类别分布,还是采样每采样好。如果是采样没有采样好,则采用1方,使用权重平衡A类样本和B类型样本(loss中小的类别给更大的权重;简单点就直接把少样本类别复制到数量跟多样本类型差不多) 2方,验证集1:1使用卷积的目的
答:MLP的参数量与输入层单元数量即输入数据量强相关。如此,计算量也跟输入数据量强相关卷积原理
答:平移不变性 和 局部性原理使用小的卷积核,视野小
答:卷积核虽然小,但是每次看一局部信息,进而综合了局部信息,当神经网络很深时,往上抽象看到了全局信息

trick/正则化(惩罚项)

权重衰退

实际中权重衰退效果模型很复杂的情况下,权重衰退不会带来好的效果
实际中权重衰减取值
答:wd=lambda=1e-3,1e-2,1e-4
# 从0开始实现,权重衰退正则项在loss函数中。简洁实现,权重衰退正则项在优化函数中
# wd=权重衰退=lambda
trainer=torch.optim.SGD([{'params':net[0].weight,'weight_decay':wd},{'params':net[0].bias}],lr=lr)
# 权重衰退写在优化函数中
optimizer=torch.optim.Adam(net.parameters(),lr=learning_rate,weight_decay=weight_decay)

丢弃法
xi′={0,probablitypxi1−p,otherwisex'_i= \begin{cases} 0,\ probablity\ p \\ \frac{x_i}{1-p},\ otherwise \end{cases} xi′​={0, probablity p1−pxi​​, otherwise​

  • dropout效果更好,因为更好调参,权重衰退效果不好
  • dropout常用值:0.1,0.5,0.9
  • 权重衰退应用范围广,dropout只能作用于全连接层激活函数之后,mini_batch_normal可作用于卷积层(输出通道上),也可作用于全连接层(特征上)
# nn.Dropout(dropout1)
dropout1,dropout2=0.2,0.5
net=nn.Sequential(nn.Flatten(),
nn.Linear(784,256),nn.ReLU(),nn.Dropout(dropout1),
nn.Linear(256,256),nn.ReLU(),nn.Dropout(dropout2),
nn.Linear(256,10))

trick/k折交叉验证

k折交叉验证过程:

  • 做k次交叉验证 叫k折交叉验证
  • 数据分成k组,每个子集做1次验证集,剩余k-1组做训练集,得到k个模型
  • 模型性能指标=k个模型最终的验证集的分类准确率的平均数

trick/数值稳定性

  • 答:数值稳定性作用,防止梯度消失和梯度爆炸,保证正向每层输出的稳定性和反向梯度的稳定性
  • 答:权重初始化作用,训练开始时数值稳定性,保证训练开始时输出的稳定性,不能保证训练中输出的稳定性
  • 答:使用靠近y=x的激活函数作用,保证正向每层输出的稳定性和反向梯度的稳定性
  • 答:归一化作用,训练中数值稳定性,训练中网络层输入数据的稳定性

trick/数值稳定性/权重初始化方法

权重初始化作用:

  • 训练开始,输出的稳定性
  • 训练开始时数值稳定性,保证训练开始时输出的稳定性,不能保证训练中输出的稳定性

xavier权重初始化方法

  • Xavier是常用的权重初始化方法,它表示权重和梯度期望=0,权重和梯度的方差由第t层输入和输出的神经元数量决定

trick/数值稳定性/激活函数

使用靠近y=x的激活函数作用:

  • 训练中,输出的稳定性,梯度的稳定性
  • 保证正向每层输出的稳定性和反向梯度的稳定性

trick/数值稳定性/批量归一化

归一化作用:

  • 训练中,输入的稳定性
  • 训练中数值稳定性,训练中网络层输入数据的稳定性
  • 学习输入层的时候,输入层改变时,避免变化输出层

使用:

  • 批量归一化固定小批量中的均值和方差,学习出合适的缩放gamma和偏移mean
  • 批量归一化层作用在全连接层的特征上:全连接层》批量归一化(作用在全连接层的特征上)》激活函数
  • 批量归一化层作用在卷积层的输出通道上:卷积层》批量归一化(作用在卷积层的输出通道上)》激活函数
  • 丢弃层:全连接层》批量归一化》激活函数》不使用丢弃层了

数据集划分

训练集,验证集,测试集的划分标准
答:深度学习,训练集一般大,不用k折交叉验证。传统机器学习一般会使用k折交叉验证
答:训练:测试=7:3,训练集上做5折交叉验证。数据集大的情况下,训练:测试=5:5
答:举例,imageNet,1000个类别,每个类别5000个样本,从每个类别中拿出50张图片样本作为测试集,共1000类别*50张图片样本/类=5万张图片做测试集样本

拟合情况判断

恰当拟合:训练误差和泛化误差(验证损失)曲线,都很小和紧密贴合(gap小)
欠拟合:训练误差和泛化误差曲线,都很大
欠拟合:训练精度和测试(验证)精度都很低
过拟合:训练误差和泛化误差曲线,训练误差一直减小,泛化误差是凹曲线。某个epoch之前贴合(gap小),之后分开(gap大)
过拟合:测试(验证)精度先上升再下降是过拟合

超参数

# 1个epoch包含多个batch_size
# 迭代次数,1个epoch会把数据都使用完
num_epochs=10# 批量大小,随机梯度下降
# 执行1次优化算法的样本数量
batch_size=256# 控制参数更新步长
lr=0.01# 正则项/权重衰退:用的少了
# 权重衰退可以放到loss函数或者优化函数中
# 常用值:wd=lambda=1e-3,1e-2,1e-4# 正则项/丢弃法:用批量归一化层后可以不用丢弃层
# 控制线性层输出丢弃概率
# 常用值0.1,0.5,0.9
dropout1,dropout2=0.2,0.5# 网络深度
# 计算量相比宽度小
# 学习就是不停的基于前面成果的向上抽象
# 过于具体,泛化能力就差# 卷积
# 原理 平移不变形 局部性
# 卷积核内容是训练求得的
# 卷积层的卷积核越大计算量越大
# 为什么使用卷积:参数量少。MLP的参数量与输入层单元数量即输入数据量强相关。如此,计算量也跟输入数据量强相关
# 卷积对位置信息很敏感,使用池化层让卷积对位置信息不那么敏感
# 1x1卷积核,相当于权重(c_o,c_i),输入(c_i,hw)的全连接层,y=wx+b
# 1x1卷积层作用,1x1的卷积不看空间信息,只看通道信息(看不到空间信息,只能看到通道信息),只抽取通道信息
# 1x1卷积层作用,放到3x3卷积层之前的目的是3x3卷积计算慢,减少输出通道,降低计算量。在ResNet中用于统一输出通道数量# 通道数
# 每个通道至少1个卷积核,卷积结果feature map再对应元素相加
# 输入通道是上层的输出通道
# 输出通道可以变多,多卷积几次即可
# 输出要减半的情况下,将通道数量加1倍,多1倍的卷积核,提取更多信息。空间信息减半,把更多的信息存储到更多的输出通道里
# 通道数量增加对计算量的影响不大
# 学习到了不同特征或模式,温故而知新
# 100万张图片,1000类别,最多1024个通道就够了。10万类别输出,可能最多2048个通道# 卷积核(特征提取器)的大小# 填充padding
# 防止输出shape太小
# 输出shape的行=(输入行数+padding*2-kernel行数+stride)/stride# 步幅stride
# 减少输出shape# 池化层:用的少了
# 分类:平均池化,最大池化
# 作用,卷积对位置信息不敏感
# 作用,stride>2减少输出的维数
# 池化层一般放到卷积层的后面
# 池化层对每个通道单独做池化,不改变通道数量
# 一般stride=池化层窗口大小,现在不=了,没有太多区别,池化层用的很少了
# 针对第1个作用对数据就做很多处理(平移,旋转,畸变)
# 针对第2个作用stride可以放到卷积层
# 全局平均池化层作用:对前一层输出的整张图片做平均池化
pool2d = nn.MaxPool2d((2,3), padding=(1,1),stride=(2, 3))
# AdaptiveAvgPool2d 全局平均池化层,1x1的宽高图片
nn.AdaptiveAvgPool2d((1,1)),# mini_batch_normal 批量归一化:用批量归一化层后可以不用丢弃层
#

常用激活函数

激活函数作用

  • 激活函数,非线性单元,可以由简单函数模拟复杂函数的关键

softmax

  • 每个x的
  • softmax(Xij)=exp(Xij)∑kexp(Xik)\rm{softmax}(\boldsymbol{\rm{X}}_{ij})=\frac{\boldsymbol{exp(\rm{X}_{ij}})}{\sum_kexp(\boldsymbol{\rm{X}_{ik}})}softmax(Xij​)=∑k​exp(Xik​)exp(Xij​)​,这里X是向量,X向量对每1个元素做softmax

常用损失函数

均方误差
MSE=1N(y^−y)2MSE=\frac{1}{N}(\hat{y}-y)^2MSE=N1​(y^​−y)2

# 损失函数 MSE 均方误差
loss=nn.MSELoss()

交叉熵
l(y,y^)=−∑iyilogyi^=−logy^yl(\boldsymbol{\rm{y}},\boldsymbol{\rm{\hat{y}}})=-\sum_i y_ilog\hat{y_i}=-log\hat{y}_yl(y,y^​)=−∑i​yi​logyi​^​=−logy^​y​


log RMSE(对数均方根误差)

  • 房价适合相对误差y−y^y\frac{y-\hat{y}}{y}yy−y^​​,因为不同房子的价格相差很大,1000万的房子产生的预测误差可能远远大于10万的房子产生的预测误差,本质是误差缩放(类比特征缩放)
  • y−y^y,取对数,log(y−y^)−logy\frac{y-\hat{y}}{y},取对数,log(y-\hat{y})-logyyy−y^​​,取对数,log(y−y^​)−logy,所以使用log_rmse(log均方根误差),RMSE均方根误差
  • logRMSE=1n∑(log(yp)−log(y))2log RMSE=\sqrt{\frac{1}{n}\sum(log(y_{p})-log(y))^2}logRMSE=n1​∑(log(yp​)−log(y))2​

常用评价指标

loss=$\hat{y}-y$
train loss=预测值-真实值
train accuracy=预测正确的样本数量/总样本数量
test accuracy=预测正确的样本数量/总样本数量
平均精度=总精度/输出元素数量

展示

模型shape

# !1个输入,1个通道,28*28的图片
# !4个括号4个维度,shape看括号里面的元素数量
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)

动画展示

class Animator:'''动画类动画展示训练过程'''def __init__(self,xlabel=None,ylabel=None,legend=None,xlim=None,ylim=None,xscale='linear',yscale='linear',fmts=('-','m--','g--','r:'),nrows=1,ncols=1,figsize=(3.5,2.5)):''':param xlabel x轴标签控制 字体大小类型:param legend 标记:param xlim x轴范围:param xscale x轴缩放类型 这里线性缩放:param fmts 线条样式:nrows 坐标轴行数:figsize 画布大小'''if legend is None:legend=[]# 使用svg格式展示的更加清晰d2l.use_svg_display()# 获取画布和轴self.fig,self.axes=d2l.plt.subplots(nrows,ncols,figsize=figsize)if nrows*ncols==1:self.axes=[self.axes,]# 配置轴# lambda函数,匿名函数,sum=lambda 入参: 函数体,例子 sum=lambda x,y : x+y# 使用lambda函数捕获参数self.config_axes=lambda: d2l.set_axes(self.axes[0],xlabel,ylabel,xlim,ylim,xscale,yscale,legend)self.X,self.Y,self.fmts=None,None,fmtsdef add(self,x,y):'''向图中增加数据点todo'''if not hasattr(y,'__len__'):y=[y]n=len(y)if not hasattr(x,'__len__'):x=[x]*nif not self.X:self.X=[[] for _ in range(n)]if not self.Y:self.Y=[[] for _ in range(n)]# 1个x对应1个yifor i,(a,b) in enumerate(zip(x,y)):if a is not None and b is not None:self.X[i].append(a)self.Y[i].append(b)# cla()清除当前轴self.axes[0].cla()for x,y,fmt in zip(self.X,self.Y,self.fmts):self.axes[0].plot(x,y,fmt)self.config_axes()display.display(self.fig)display.clear_output(wait=True)

python

基础

# 获取list类型变量
indices=list(range(num_examples))

生成器

# 生成器(生成数据、可迭代对象)
# 每次取i到i+batch_size的数据
for i in range(0,num_examples, batch_size):# min防止越界batch_indices=torch.tensor(indices[i:min(i+batch_size,num_examples)])# yield 产出yield features[batch_indices],labels[batch_indices]

with as操作上下文管理器

# 资源管理 防止资源泄漏
# __enter__()魔法方法,进入with时调用open()
# __exit__()魔法方法,退出with时调用close()
with open('xcrj.txt', 'a') as f:f.write("\nxcrj")

random

import random
# 操作索引好处 后面可以操作features和label
random.shuffle(indices)

2李沐动手学深度学习v2/ndarray

创建张量

torch.arange(12)
torch.tensor([[],[]])
torch.ones((1,3))
torch.zeros((1,3))
X=torch.arange(12,dtype=torch.float32).reshape((3,4))
# 符合normal正态分布的随机值。0均值,1方差的随机数
X=torch.normal(0,1,(num_examples,len(w)))

常用

X.shape
# number of elements
X.numel()
X.reshape((3,4))
# -1这一维由计算得出
y.reshape((-1,1))
X.sum()
X.mean()
type(X)
len(X)

普通求和

A=torch.arange(24).reshape(2,3,4)
print(A)
# axis代表第i维,n维数组的第i个括号
# 消除第1个括号,把第1个括号内的n个记录中的对应位置元素相加
print(A.sum(axis=0))
print(A.sum(axis=0).shape)
# 消除第2个括号,把第2个括号内的n个记录中的对应位置元素相加
print(A.sum(axis=1))
print(A.sum(axis=1).shape)
# 消除第1个和第2个括号,先把第1个括号内的n个记录中的对应位置元素相加,再把第2个括号内的n个记录中的对应位置元素相加
print(A.sum(axis=[0,1]))
print(A.sum(axis=[0,1]).shape)# 保留维度
sum_A=A.sum(axis=1,keepdims=True)

累加求和

# cumulation
A.cumsum(axis=0)

基本运算

# 所有的运算都是基于元素的, **是幂运算,//是地板除法
x+y, x-y, x*y, x/y, x**y, x//y# e的x次方
torch.exp(x)

拼接

# 不增加新的维度,在第0维拼接
torch.cat((X,Y),dim=0), torch.cat((X,Y),dim=1)# +1元素值加法,c_o=3,在第0个维度堆叠,增加第0维
K = torch.stack((K, K+1, K+2), 0)

类型转换

# tensor>numpy
X.numpy()# 在计算图中需要先detach()出去再转成numpy()
d2l.plt.scatter(features[:,1].detach().numpy(),labels.detach().numpy(),1);# tensor>基本数据类型
x.item()

对象实体与对象引用

# 更倾向于创建一个新的对象
# 指针变化
# id获取指针
before=id(Y)
# 创建了1个新的对象Y
Y=Y+X
id(Y)==before# 存在优化
# 指针不变
before=id(X)
X+=Y
id(X)==before# 细节创建方式
A=torch.arange(20,dtype=torch.float32).reshape(5,4)# 深拷贝
B=A.clone()
A,A+B# copy不一定是深拷贝
B=A.copy()

3李沐动手学深度学习v2/线性代数

矩阵操作

A.T

乘法

# 乘积:元素按位置相乘
m_h=x*y# 点乘
# 向量.向量
# 点积:元素按位置相乘再求和,就是数学的矩阵运算
m_d=torch.dot(x,y)# 点乘
# matrix.vector=矩阵.向量
torch.mv(A,x)# 点乘
# 二维矩阵相乘
# matrix.matrix=矩阵.矩阵
torch.mm(A,B)# 点乘
# 多维矩阵相乘
# matrix multiple
y=torch.matmul(X,w)+b# 点乘
# @矩阵乘法,不是元素相乘
H=relu(X@W1+b1)

范数

# L1范数=向量元素绝对值求和
x=torch.tensor([3.0,4.0])
l1=torch.abs(x).sum()
l1# L2范数=向量元素平方和开根号
x=torch.tensor([3.0,4.0])
# norm规范
l2=torch.norm(x)
l2# F范数=矩阵元素平方和开根号
x=torch.arange(6,dtype=torch.float32).reshape(2,3)
torch.norm(x)

4李沐动手学深度学习v2/自动求导

梯度


# 存储梯度
# 等价于x=torch.arange(4.0,requires_grad=True)
x.requires_grad_(True)# 访问梯度 默认None
x.grad# 清空梯度,pytorch模型进行梯度累积
x.grad.zero_()# 计算梯度,反向传播,y对x的每个分量求梯度
y.backward()# 不计算梯度
# detach离开,移出梯度计算图
u=y.detach()
z=u*x
# z.sum()对x的各个分量求梯度
z.sum().backward()
# u被移出计算图,u是常数,所以相等
print(x.grad==u)

问题

为什么loss是标量
答:因为标量对矩阵或向量求导的结果shape不会变大

5李沐动手学深度学习v2/线性回归-从0开始实现

移出计算图再转ndarray

# features[:,1].detach().numpy()
# 要转为numpy类型需要先从计算图中移除detach(隐式构造计算图)
d2l.plt.scatter(features[:,1].detach().numpy(),labels.detach().numpy(),1);

不计算梯度的时机

# 优化算法更新参数时不需要计算梯度,需要清空梯度
# 展示n个epoch的训练情况时不需要计算梯度,不需要情况梯度# 定义优化函数
# 随机梯度下降,随机从样本中选取batch_size的数据,所有样本都会取到。知识对所有样本进行了shuffle
def sgd(params,lr,batch_size):'''小批量梯度下降:param params 初始化参数:param lr learning rate:param batch_size''''''with A:block进入block时,执行A的__enter__()退出block时,执行A的__exit__()'''# 进入环境管理器时记录梯度状态和禁止梯度计算, 退出环境管理器时还原# 为什么使用with语句。因为 更新param时不需要梯度计算with torch.no_grad():for param in params:# batch_size本来放到squared_loss中,线性回归模型放到这里也可以param-=lr*param.grad/batch_size# 清空梯度param.grad.zero_() # 训练多少轮,每轮有多个小批量
for epoch in range(num_epochs):for X,y in data_iter(batch_size,features,labels):# 小批量损失l=loss(net(X,w,b),y)# l shape batch_size*1所以要sum()弄成标量求梯度# 标量对向量求梯度 向量只是转置了。向量对向量求梯度成矩阵了,矩阵对向量求梯度成三维张量了# 计算关于x,关于b的梯度l.sum().backward()# 使用优化函数更新w, bsgd([w,b],lr,batch_size)# 展示1个epoch(1轮)的训练过程with torch.no_grad():# 1个epoch后,使用被更新的w, b得到的损失train_1=loss(net(features,w,b),labels)print(f'epoch {epoch+1}, loss {float(train_1.mean()):f}')

梯度归0的时机

# 优化算法使用梯度更新完参数之后,需要重新计算梯度,先要清空梯度

6李沐动手学深度学习v2/线性回归的简洁实现

全连接层

# 全连接层就是线性层。入参(输入神经元个数,输出神经元个数)
# linear层 全连接层 2,1 输入维度,输出维度
# Sequential 神经网络层的容器
net=nn.Sequential(nn.Linear(2,1))

网络参数

# 获取神经网络第1层的权重参数
net[0].weight
# 获取神经网络第1层的偏置参数
net[0].bias

参数初始化方法

# net[0]表示神经网络的第1层
# 初始化模型参数
net[0].weight.data.normal_(0,0.01)
net[0].bias.data.fill_(0)

7李沐动手学深度学习v2/图像分类数据集

8李沐动手学深度学习v2/逻辑回归(softmax回归(分类))从0开始实现

9李沐动手学深度学习v2/逻辑回归(softmax回归(分类))简洁实现

逻辑回归

##引入包##
import torch
from torch import nn
from d2l import torch as d2l##设置超参数##
# 超参数
num_epochs = 10
batch_size = 256##获取数据##
# 数据
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)##定义模型##
# pytorch不会调整input的形状
# 定义展平层(flatten)保留第0维,展开其他维度为1个向量,保留样本数,展开28*28=784,先展平再输入
net = nn.Sequential(nn.Flatten(), nn.Linear(784, 10))##初始化模型参数##
# 初始化参数
def init_weights(m):if type(m) == nn.Linear:nn.init.normal_(m.weight, std=0.01)# 应用到net中
net.apply(init_weights)##定义损失函数##
# 损失函数
loss = nn.CrossEntropyLoss()##定义优化算法##
# 优化算法 以一定的学习率去学习 更新参数
trainer = torch.optim.SGD(net.parameters(), lr=0.1)##开始训练##
# todo(训练的结果曲线没有 train loss)
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)

展平层

# pytorch不会调整input的形状
# 定义展平层(flatten)保留第0维,展开其他维度为1个向量,保留样本数,展开28*28=784,先展平再输入
net = nn.Sequential(nn.Flatten(), nn.Linear(784, 10))

问题

10李沐动手学深度学习v2/MLP (Multilayer Perceptron) 多层感知机从0开始实现

11李沐动手学深度学习v2/MLP (Multilayer Perceptron) 多层感知机简洁实现

MLP简介实现过程

##引入包##
import torch
from torch import nn
from d2l import torch as d2l##定义超参数##
batch_size,lr,num_epochs=256,0.1,10##获取数据##
# 数据
train_iter,test_iter=d2l.load_data_fashion_mnist(batch_size)##定义模型##
# 模型 超参数 激活函数(使用那种激活函数也可以认为是超参数)
net=nn.Sequential(nn.Flatten(),nn.Linear(784,256),nn.ReLU(),nn.Linear(256,10))##初始化参数##
# 参数 W和b
def init_weights(m):'''设置线性层的权重:param m 全连接层 线性层'''if type(m)==nn.Linear:nn.init.normal_(m.weight,std=0.01)# 模型应用参数初始化函数
net.apply(init_weights)##定义损失函数##
# 损失函数
loss=nn.CrossEntropyLoss(reduction='none')##定义优化算法##
# 优化算法
trainer=torch.optim.SGD(net.parameters(),lr=lr)##开始训练##
# 训练
d2l.train_ch3(net,train_iter,test_iter,loss,num_epochs,trainer)

12李沐动手学深度学习v2/数据复杂度与模型容量选择不当造成的 过拟合和欠拟合现象

13李沐动手学深度学习v2/权重衰退从0开始实现

总结

  • 正则项(惩罚项):λ\lambdaλ越大,www选择范围越小,降低模型复杂度,避免过拟合
  • 一般lambda=1e-3=0.001,lambda不会寻到1等等大的值
  • 正则项(惩罚项)效果一般,权重衰退用的已经很少了

14李沐动手学深度学习v2/权重衰退简洁实现

15李沐动手学深度学习v2/丢弃法 (dropout) 从0开始实现

17李沐动手学深度学习v2/实战kaggle比赛,房价预测

18李沐动手学深度学习v2/自定义网络,使用层自定义网络

总结

  • 任何层或者网络都是nn.Module的子类

自定义网络MLP

class MLP(nn.Module):'''神经网络是块(nn.Module)的子类'''def __init__(self):'''使用层自定义网络'''super().__init__()self.hidden=nn.Linear(20,256)self.out=nn.Linear(256,10)def forward(self,X):'''定义前向传播如何运算'''return self.out(F.relu(self.hidden(X)))net=MLP()
# 魔法方法会自动调用forward()
net(X)

自定义顺序网络

class MySequential(nn.Module):'''神经网络是块(nn.Module)的子类实现nn.Sequential()'''def __init__(self,*args):'''将层放入self._modules:param *args 列表入参 神经网络层'''super().__init__()# block 层for block in args:# !self._modules[block]=blockdef forward(self,X):'''为self._modules的每层赋值'''# 遍历层,和输入顺序一致for block in self._modules.values():# 输出层的输出作为下一层的输入X=block(X)return Xnet=MySequential(nn.Linear(20,256),nn.ReLU(),nn.Linear(256,10))
net(X)

自定义固定隐藏层网络

class FixedHiddenMLP(nn.Module):'''隐藏层被固定,两次调用同1个隐藏层'''def __init__(self):super().__init__()# 随机初始化权重,不参与梯度计算self.rand_weight=torch.rand((20,20),requires_grad=False)self.linear=nn.Linear(20,20)def forward(self,X):'''在正向传播中执行代码'''# !调用第1次X=self.linear(X)# 自行计算转换函数# torch.mm(a,b) 矩阵乘法,数学矩阵乘法# +1是偏置X=F.relu(torch.mm(X,self.rand_weight)+1)# !调用第2次X=self.linear(X)while X.abs().sum()>1:X/=2return X.sum()net=FixedHiddenMLP()
net(X)

自定义嵌套网络

class NestMLP(nn.Module):def __init__(self):super().__init__()self.net=nn.Sequential(nn.Linear(20,64),nn.ReLU(),nn.Linear(64,32),nn.ReLU())self.linear=nn.Linear(32,16)def forward(self,X):return self.linear(self.net(X))# 把MLP net套到了nn.Sequential中
chimera=nn.Sequential(NestMLP(),nn.Linear(16,20),FixedHiddenMLP())
chimera(X)

19李沐动手学深度学习v2/自定义层,使用参数自定义层

创建带有参数层

class MyLinear(nn.Module):def __init__(self,in_units,units):'''使用参数自定义层使用Parameter类自定义线性层:param in_units 前一层神经元数量:param units 后一层神经元数量'''super().__init__()# !nn.Parameterself.weight=nn.Parameter(torch.randn(in_units,units))self.bias=nn.Parameter(torch.randn(units,))def forward(self,X):linear=torch.matmul(X,self.weight.data)+self.bias.datareturn F.relu(linear)dense=MyLinear(5,3)
dense.weight# 使用自定义层直接执行前向传播计算
dense(torch.rand(2,5))# 使用自定义层构建网络
net=nn.Sequential(MyLinear(64,8),MyLinear(8,1))
net(torch.rand(2,64))

20李沐动手学深度学习v2/参数管理

参数访问

# 访问指定层所有参数
# net[2]拿到0,1,2。拿到nn.Linear(8,1)
# nn.Sequential(nn.Linear(4,8),nn.ReLU(),nn.Linear(8,1))
# state?权重从自动机角度来看就是状态机
# OrderedDict?有weight和bias
print(net[2].state_dict())# 网络的所有参数的第3层的偏置的数据
# !神经元+输出算作1层(神经元和突起),激活函数算作1层
net.state_dict()['2.bias'].data# 访问指定层的指定参数
# torch.nn.parameter.Parameter定义的是可以优化的参数
print(type(net[2].bias))
# 访问参数
print(net[2].bias)
# 访问参数的数据
print(net[2].bias.data)# 访问参数的梯度
# 还没有做反向传播,所以=None
net[2].weight.grad==None# 访问神经网络中的所有参数
# named_parameters含有name和parameter
# *[] 展开list
print(*[(name,param.shape) for name,param in net[0].named_parameters()])
print(*[(name,param.shape) for name,param in net.named_parameters()])

参数初始化方法

# 内置初始化函数
def init_normal(m):''':param m nn.Module类型'''if type(m)==nn.Linear:# 正态分布# !替换函数,normal_后下划线的写法表示这个函数是替换函数,替换m.weight# !所有替换函数都在initModule里面nn.init.normal_(m.weight,mean=0,std=0.01)nn.init.zeros_(m.bias)# net中所有的层都应用这个初始化函数
net.apply(init_normal)
net[0].weight.data[0],net[0].bias.data[0]# 内置初始化函数
def init_constant(m):''':param m nn.Module类型'''if type(m)==nn.Linear:# 正态分布# !替换函数,normal_后下划线的写法表示这个函数是替换函数,替换m.weight# !所有替换函数都在initModule里面nn.init.constant_(m.weight,1)nn.init.zeros_(m.bias)# net中所有的层都应用这个初始化函数
net.apply(init_constant)
net[0].weight.data[0],net[0].bias.data[0]

21李沐动手学深度学习v2/读写文件,加载和保存张量

加载和保存一个张量

# 1个张量
import torch
from torch import nn
from torch.nn import functional as Fx=torch.arange(4)
torch.save(x,'x-file')x2=torch.load('x-file')
x2

保存和加载网络

# 存储权重参数
torch.save(net.state_dict(),'mlp.params')
# 加载权重参数
clone=MLP()
clone.load_state_dict(torch.load('mlp.params'))
# evaluation 评估模式,对评估存在优化
clone.eval()

22李沐动手学深度学习v2/GPU

注意

  • GPU内存上的张量运算,需要保证张量在同一张GPU上
  • 确定模型和参数存储在同一个gpu上

常用

# GPU使用情况
!nvidia-smi查询可用GPU数量
torch.cuda.device_count()# 可以使用cpu
X = torch.ones(2,3,device=torch.device('cuda:0'))
X = torch.ones(2,3,device=torch.device('cpu'))
# 只能使用gpu
X = torch.ones(2,3).cuda(0)# 查看张量在什么设备上
print(x.device)# 张量深拷贝
# GPU内存上tensor运算
# X.cuda(1),把张量X深拷贝到第2张GPU# 将神经网络模型深拷贝一份到gpu0设备上
net=net.to(device=try_gpu())

try_gpu() try_all_gpus()

# 定义函数,允许GPU不存在时,也能运行代码
def try_gpu(i=0):''':return 如果存在gpu(i),则返回gpu(i),否则返回cpu()'''if torch.cuda.device_count()>=i+1:# !不要写成torch.cuda.device(f'cuda:{i}')return torch.device(f'cuda:{i}')return torch.device('cpu')def try_all_gpus():''':return 返回所有可用gpu,否则返回[cpu(),]'''devices=[torch.device(f'cuda:{i}') for i in range(torch.cuda.device_count())]return devices if devices else [torch.device('cpu')]

24李沐动手学深度学习v2/图像卷积,二维卷积

25李沐动手学深度学习v2/填充和步幅

# 1输入通道数,1输出通道数,核大小3*3。padding是超参数,padding=1,上下左右都1像素
conv2d=nn.Conv2d(1,1,kernel_size=3,padding=1)conv2d=nn.Conv2d(1,1,kernel_size=(3,5),padding=(0,1),stride=(3,4))
# (8+0+0-3+3)/3=2,(8+1+1-5+4)/4=2

27李沐动手学深度学习v2/池化层

单通道二维池化

# 1输入通道数,1输出通道数
X=torch.arange(16,dtype=torch.float32).reshape((1, 1, 4, 4))
print(X.shape)# 使用默认值,步幅=池化窗口大小
# (4+0+0-3+4)/4=1,(4+0+0-3+4)/4=1
pool2d=nn.MaxPool2d(3)
print(pool2d(X).shape)# 手动设定填充和步幅
# (4+1+1-3+2)/2=2, (4+1+1-3+2)/2=2
pool2d = nn.MaxPool2d(3, padding=1, stride=2)
print(pool2d(X).shape)# (4+1+1-2+2)/2=3, (4+1+1-3+3)/3=2
pool2d = nn.MaxPool2d((2,3), padding=(1,1),stride=(2, 3))
print(pool2d(X).shape)

多通道二维池化

print(X.shape)
# !cat是沿维数直接拼接,stack是沿着维数产生维度再进行拼接
# cat拼接之后的维度=序列长度*要拼接维度原来值
# stack堆叠之后的维度=序列长度
# +1元素加1,序列长度=2,第1维 1*2
X=torch.cat((X,X+1),1)
print(X.shape)
# 会在每个通道单独做池化
pool2d=nn.MaxPool2d(3,padding=1,stride=2)
print(pool2d(X).shape)

28李沐动手学深度学习v2/卷积神经网络,LeNet

# batch_size, 1输入通道
# view和reshape的区别:reshape的功能比view更强大。view视图是原有tensor的视图,不开辟新的存储空间,返回原有存储空间的引用。view只适用于连续性的tensor
# view用来reshape,-1这一维由计算得出,1单通道
return x.view(-1,1,28,28)# 将模型放到GPU上
net.to(device)# !数据放到gpu上
# 在计算之前放到gpu上
X, y = X.to(device), y.to(device)

29李沐动手学深度学习v2/深度卷积神经网络,AlexNet

对比LeNet

  • 更大,更深
  • ReLU 激活函数
  • dropout 正则化

30李沐动手学深度学习v2/使用块的网络,VGG

对比AlexNet

  • 更大更深的AlexNet
  • VGG块,模型架构更加规范

结构:

  • VGG块

VGG

  • 使用可重复使用的卷积块,构建更大更深的网络

类比:

  • 学习的套路,AlexNet学习没有套路可言

31李沐动手学深度学习v2/网络中的网络,NiN

为什么出现:

  • 由于全连接层的参数量过大,考虑完全不使用全连接层,使用1x1的卷积层替代全连接层

结构:NiN块

NiN

  • 3部分组成,1个卷积层,2个1x1的卷积层(相当于全连接层,比全连接层的参数量少,不容易过拟合)
  • 最后1个NiN块的输出通道数等于类别数
  • 最后1个池化层是平均池化层,kernel_size=最后1个NiN块输出的图片大小
  • 最后使用展平层,可以获得所有通道的最大值
  • NiN块网络中没有使用任何全连接层

32李沐动手学深度学习v2/含并行连结的网络,GoogLeNet

  • idea:难以寻找合适的卷积层,我全都要,inception块各种方式的大杂烩
  • inception块用4条有不同超参数的卷积层和池化层来抽取不同的信息
  • GoogleNeti使用了9个Inception:块,是第一个达到上百层的网络
  • GoogLeNet实现复杂,不受欢迎
  • GoogLeNet V3,V4常使用

结构:

  • inception块,学习方式
  • stage,学习阶段

GoogLeNet总结

  • 学习分阶段,每个阶段做每个阶段的事情
  • 每个阶段以多种方式进行学习,多种学习方式并行
  • 一般,inception块,不改变宽高,改变通道数量

33李沐动手学深度学习v2/批量归一化 mini-batch normalization

uB=1B∑i∈Bxiu_B=\frac{1}{B}\sum\limits_{i \in B}x_iuB​=B1​i∈B∑​xi​
σB2=1∣B∣∑i∈B(xi−uB)2+ϵ\sigma^2_B=\frac{1}{|B|}\sum\limits_{i\in B}(x_i-u_B)^2+\epsilonσB2​=∣B∣1​i∈B∑​(xi​−uB​)2+ϵ, ϵ\epsilonϵ 是1个很小的数,防止方差为0
xi+1=γxi−μ^Bσ^B+βx_{i+1}=\gamma\frac{x_i-\hat{\mu}_B}{\hat{\sigma}_B}+\betaxi+1​=γσ^B​xi​−μ^​B​​+β, B is mini_batch_data, γ\gammaγ是需要学习的方差, β\betaβ是需要学习的期望, μ^B\hat{\mu}_Bμ^​B​ is mean, σ^B\hat{\sigma}_Bσ^B​ is var,

总结数值稳定性:

  • 答:数值稳定性作用,防止梯度消失和梯度爆炸,保证正向每层输出的稳定性和反向梯度的稳定性
  • 答:权重初始化作用,训练开始时数值稳定性,保证训练开始时输出的稳定性,不能保证训练中输出的稳定性
  • 答:使用靠近y=x的激活函数作用,保证正向每层输出的稳定性和反向梯度的稳定性
  • 答:归一化作用,训练中数值稳定性,训练中网络层输入数据的稳定性

总结批量归一化

  • 批量归一化的作用:学习输入层的时候,输入层改变时,避免变化输出层
  • 批量归一化的作用:网络层输入数据的稳定性
  • 批量归一化固定小批量中的均值和方差,学习出合适的缩放gamma和偏移mean
  • 批量归一化层作用在全连接层的特征上:全连接层》批量归一化(作用在全连接层的特征上)》激活函数
  • 批量归一化层作用在卷积层的输出通道上:卷积层》批量归一化(作用在卷积层的输出通道上)》激活函数
  • 丢弃层:全连接层》批量归一化》激活函数》不使用丢弃层了
  • 各种归一化:https://blog.csdn.net/u013289254/article/details/99690730

34李沐动手学深度学习v2/残差网络,ResNet

必须要学习的神经网络
可实现千层网络

idea:

  • 把握学习的方向
  • 越复杂的模型不一定月靠近最优理想模型
  • 简单模型学习到的东西,复杂的模型一定能够学习到
  • 将简单的模型学习到的东西保留到复杂的模型中

实现

  • f(x)=x+g(x)
  • 本层 上层 下层,下层的输入=上层输出+本层输出
  • ResNet块参数的 输出通道数变成输入通道数的两倍,步幅=2,通道数翻倍,宽高减半,需要1x1的卷积来统一通道数量

为什么叫残差

  • 在前一层的基础上,一层层叠加训练。先得到一个简单的函数,再一层层的叠加,最终生成一个复杂的函数

结构

  • Residual_block块
  • ResNet_block块
  • 分stage

类比

  • 保留之前的知识
  • 把握学习的方向
  • 把握学习的阶段

35李沐动手学深度学习v2/多GPU训练,数据并行,从0开始实现

李沐动手学深度学习v2/总结3相关推荐

  1. 李沐动手学深度学习v2/总结1

    总结 编码过程 数据 数据预处理 模型 参数,初始化参数 超参数 损失函数,先计算损失,清空梯度(防止有累积的梯度),再对损失后向传播计算损失关于参数的梯度 优化算法,使用优化算法更新参数 训练求参数 ...

  2. 14李沐动手学深度学习v2/权重衰退简洁实现

    # 权重衰退是广泛应用的正则化技术 %matplotlib inline import torch from torch import nn from d2l import torch as d2l ...

  3. 李沐动手学深度学习V2-全卷积网络FCN和代码实现

    一.全卷积网络FCN 1. 介绍 语义分割是对图像中的每个像素分类,全卷积网络(fully convolutional network,FCN)采用卷积神经网络实现了从图像像素到像素类别的变换 ,与前 ...

  4. 李沐动手学深度学习(pytorch版本)d2lzh_pytorch包的缺少安装问题

    学习深度学习时候,很多人参考的是李沐的动手学深度学习Pytorch版本(附上官方地址:https://tangshusen.me/Dive-into-DL-PyTorch/#/). 在学习3.5.1节 ...

  5. 【李沐动手学深度学习】读书笔记 01前言

    虽然之前已经学过这部分内容和深度学习中的基础知识,但总觉得学的不够系统扎实,所以希望再通过沐神的课程以及书籍,系统条理的学习一遍.在读书过程中,利用导图做了一下梳理,形成了这个读书笔记.如有侵权,请联 ...

  6. 关于李沐动手学深度学习(d2l)pytorch环境本地配置

    本地安装d2l 由于之前试了很多次d2l课本的安装方法失败了,这里提供一种我可以成功安装d2l包的方法. pytorch安装 首先安装cuda.cudnn.pytroch(gpu版本).可以参考这篇文 ...

  7. 李沐动手学深度学习:08 线性回归(代码逐行理解)

    目录 一.相关资料连接 1.1 李沐视频 1.2 代码.PPT 二.代码及笔记(使用Jupyter Notebook) 2.1 线性回归从零开始实现 2.1.1 基本概念 2.1.2 基础优化算法 2 ...

  8. windows上配置深度学习(李沐-动手学深度学习)

    1.安装miniconda windows下安装,去清华大学开源镜像下载,速度比较快. 选中Miniconda3-latest-Windos-x86_64.exe下载安装包(目前最新的是py3.9) ...

  9. 李沐动手学深度学习第四章-4.9.环境和分布偏移

    我们从来没有想过数据最初从哪里来?以及我们计划最终如何处理模型的输出? 根据测试集的精度衡量,模型表现得非常出色. 但是当数据分布突然改变时,模型在部署中会出现灾难性的失败. 解决方案很简单(要求&q ...

  10. 李沐动手学深度学习V2-图像增广和代码实现

    图像增广 大型数据集是成功应用深度神经网络的先决条件,因为解决了大型复杂网络的过拟合性. 图像增广在对训练图像进行一系列的随机变化之后,生成相似但不同的训练样本,从而扩大了训练集的规模. 此外,应用图 ...

最新文章

  1. Android工程目录
  2. mininet编程实现交换机规则的插入、删除与修改。_Mysql事务隔离以及MVCC实现原理...
  3. app android de,Android Deobfuscation
  4. 作者:邓景文(1982-),女,中国联合网络通信集团有限公司电子商务部工程师...
  5. 前端:JS/38/canvas状态的保存和恢复(canvas常用状态大全),canvas画布中图像的变形
  6. html点线面制作,openlayers 添加点线面 Demo(可直接运行)
  7. Python Django 初试手记
  8. ENVI裁剪遥感图像
  9. ios微信支付失败 php,iOS微信支付的那些坑
  10. 国外11个高质量免费的3D素材网站-建筑设计/室内设计/效果图渲染
  11. CAD-Cass小结(6)——dat数据格式与展点(显示属性,更改点符号)
  12. vscode如何打开html
  13. 推荐书籍:软件定义网络 SDN与OpenFlow解析
  14. 关于大数据相关的问答汇总,每天持续更新中哦~
  15. PPT居然还可以一键换色!学会这4招再也不怕色了……
  16. 杭银消费金融拟增资扩股:杭州银行认购3.7亿股,曾被罚50万元
  17. KDD 2011 最佳工业论文中机器学习的实践方法-翻译
  18. 加密与解密的基本概念--GPG加密工具的使用
  19. 第七周 任务一
  20. SpringCloud---熔断器Hystrix的作用--解决灾难性雪崩效应

热门文章

  1. linux shell sed快速开始-(添加文本到文件的第一行和最后一行、文本的行尾添加字符)
  2. 树莓派使用USB摄像头和motion实现监控
  3. 苹果微信分身版ios_香草直播苹果版下载-香草直播ios苹果版「精彩直播」
  4. bzoj4399 魔法少女LJJ
  5. 申请 app store 退款
  6. Barracudanbsp;VSnbsp;antelope
  7. 腾讯计算机安全实验室,TRP-AI反病毒引擎创新:腾讯安全最新成果入围顶级学术会议...
  8. 高等数学(第七版)同济大学 总习题四(后半部分) 个人解答
  9. Ubuntu 下图像标注工具 labelImg 的安装及使用
  10. 自然人股东分红必须要缴纳20%个税吗?有三种真不用