数据
数据预处理
模型
参数,初始化参数
超参数
损失函数,先计算损失,清空梯度(防止有累积的梯度),再对损失后向传播计算损失关于参数的梯度
优化算法,使用优化算法更新参数
训练求参数
# 开始训练
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
权重衰退
实际中权重衰退效果模型很复杂的情况下,权重衰退不会带来好的效果
实际中权重衰减取值
答: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 p1−pxi, otherwise
# 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))
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(对数均方根误差)
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)
# 获取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]
# 资源管理 防止资源泄漏
# __enter__()魔法方法,进入with时调用open()
# __exit__()魔法方法,退出with时调用close()
with open('xcrj.txt', 'a') as f:
f.write("\nxcrj")
import random
# 操作索引好处 后面可以操作features和label
random.shuffle(indices)
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()
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)
# 存储梯度
# 等价于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不会变大
# 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}')
# 优化算法使用梯度更新完参数之后,需要重新计算梯度,先要清空梯度
# 全连接层就是线性层。入参(输入神经元个数,输出神经元个数)
# 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)
##引入包##
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)
总结