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

总结

编码过程

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

线性回归训练过程

# 开始训练
num_epochs=3
for epoch in range(num_epochs):
    # 获取小批量样本
    for X,y in data_iter:
        # net中带有w和b,传入x即可
        l=loss(net(X),y)
        # 梯度请0
        trainer.zero_grad()
        # 后向传播,内部torch帮助求sum()了
        l.backward()
        # 走一步 更新1次w和b
        trainer.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

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)

丢弃法

x i ′ = { 0 ,   p r o b a b l i t y   p x i 1 − p ,   o t h e r w i s e x'_i= \begin{cases} 0,\ probablity\ p \\ \frac{x_i}{1-p},\ otherwise \end{cases} xi={0, probablity p1pxi, otherwise

  • dropout效果更好,因为更好调参,权重衰退效果不好
  • dropout常用值:0.1,0.5,0.9
  • 权重衰退应用范围广,dropout只能作用于全连接层激活函数之后,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个模型最终的验证集的分类准确率的平均数

数据集划分

训练集,验证集,测试集的划分标准
答:深度学习,训练集一般大,不用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

常用激活函数

softmax
s o f t m a x ( X i j ) = X i j ∑ k e x p ( X i k ) \rm{softmax}(\boldsymbol{\rm{X}}_{ij})=\frac{\boldsymbol{\rm{X}_{ij}}}{\sum_kexp(\boldsymbol{\rm{X}_{ik}})} softmax(Xij)=kexp(Xik)Xij,这里X是向量,X向量对每1个元素做softmax


常用损失函数

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

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

交叉熵
l ( y , y ^ ) = − ∑ i y i l o g y i ^ = − l o g y ^ y l(\boldsymbol{\rm{y}},\boldsymbol{\rm{\hat{y}}})=-\sum_i y_ilog\hat{y_i}=-log\hat{y}_y l(y,y^)=iyilogyi^=logy^y


log RMSE(对数均方根误差)

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

常用评价指标

loss= y ^ − y \hat{y}-y y^y
train loss=预测值-真实值
train accuracy=预测正确的样本数量/总样本数量
test accuracy=预测正确的样本数量/总样本数量

动画展示

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,fmts
        
    def add(self,x,y):
        '''
        向图中增加数据点
        todo
        '''
        if not hasattr(y,'__len__'):
            y=[y]
        n=len(y)
        if not hasattr(x,'__len__'):
            x=[x]*n
        if not self.X:
            self.X=[[] for _ in range(n)]
        if not self.Y:
            self.Y=[[] for _ in range(n)]
        # 1个x对应1个yi
        for 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)

类型转换

# tensor>numpy
X.numpy()

# 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_d=torch.dot(x,y)
# 乘积:元素按位置相乘
m_h=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, b
        sgd([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开始实现

逻辑回归

##引入包##
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))

问题

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

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 λ越大, w w w选择范围越小,降低模型复杂度,避免过拟合
  • 一般lambda=1e-3=0.001,lambda不会寻到1等等大的值
  • 正则项(惩罚项)效果一般用的已经很少了

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

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

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

你可能感兴趣的:(深度学习,深度学习,python,机器学习)