PyTorch的强大使得深度学习任务像搭积木一样。
机器学习流程:一般分为这几个步骤:数据加载—数据预处理—划分数据集—选择模型—定义损失函数,优化器,超参数—验证评估。
ML与DL的不同:
1,数据:由于DL所需数据量大,所以需要按批(batch)读取;
2,模型:DL的网络层数较深,且有特定功能的网络层,就跟盖房子一样,需要逐层搭建,或预定义模块再组装;
3,加速:DL由于数据大,模型深,所以需要加速,需要GPU,并行运算等。
深度学习流程:流程类似于ML,按批读入数据—模型构建—放入GPU中训练—损失函数反向传播—优化器—训练/验证—调整评估。
导包:涉及表格的可能用pandas,图片可能用到cv2,可视化可能用matplotlib,seaborn等。但是以下包基本是深度学习项目必须的;
import os
import numpy as np
import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader #数据加
import torch.optim as optimizer #优化器
超参数设置:超参数统一设置,方便后续调试修改:
GPU配置:常见有两种方式
# 方案一:使用os.environ,这种情况如果使用GPU不需要设置,0表示显卡0,1表示显卡1.用几块写几块。
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'
# 方案二:使用“device”,后续对要使用GPU的变量用.to(device)即可,model.to_device(device)
device = torch.device("cuda:1" if torch.cuda.is_available() else "cpu")
PyTorch通过Dataset+DataLoader读取数据。
Dataset定义好数据的格式和数据变换形式,DataLoader用iterative的方式不断读入批次数据。
Dataset:有两种数据读取方式:
1,下载并使用Pytorch提供的内置数据集,需要继承PyTorch自身的Dataset类。
2,定义自己的Dataset类实现灵活数据读取。同时还要对数据进行必要的变换,比如说将图片统一成一致的大小。需要将图片转化为Tensor类,具体可以看实战部分。
主要包含三个函数:
DataLoader:构建好Dataset后,就可以使用DataLoader来按批次读入数据了,实现代码如下:
from torch.utils.data import DataLoader
train_loader = torch.utils.data.DataLoader(train_data, batch_size=batch_size, num_workers=4, shuffle=True, drop_last=True)
val_loader = torch.utils.data.DataLoader(val_data, batch_size=batch_size, num_workers=4, shuffle=False)
PyTorch中神经网络构造基于** Module 类**的模型来完成的,Module 类是 nn 模块里提供的一个模型构造类,是所有神经⽹络模块的基类。神经网络通过层定义+层顺序的方式进行构建。
不含模型参数的层: MyLayer 类通过继承 Module 类自定义了一个将输入减掉均值后输出的层,并将层的计算定义在了 forward 函数里。这个层里不含模型参数。
import torch
from torch import nn
class MyLayer(nn.Module):
def __init__(self, **kwargs):
super(MyLayer, self).__init__(**kwargs)
def forward(self, x):
return x - x.mean()
含模型参数的层:自定义含模型参数的自定义层。其中的模型参数可以通过训练学出。Parameter 类其实是 Tensor 的子类,如果一 个 Tensor 是 Parameter ,那么它会⾃动被添加到模型的参数列表里。
class MyListDense(nn.Module):
def __init__(self):
super(MyListDense, self).__init__()
self.params = nn.ParameterList([nn.Parameter(torch.randn(4, 4)) for i in range(3)])
self.params.append(nn.Parameter(torch.randn(4, 1)))
def forward(self, x):
for i in range(len(self.params)):
x = torch.mm(x, self.params[i])
return x
net = MyListDense()
print(net)
关键参数解释:
一个好的权重值,会使模型收敛速度提高,使模型准确率更精确。使用torch.nn.init。
torch.nn.init使用:根据实际模型来使用torch.nn.init进行初始化,通常使用isinstance来进行判断模块属于什么类型。
import torch
import torch.nn as nn
conv = nn.Conv2d(1,3,3)
linear = nn.Linear(10,1)
isinstance(conv,nn.Conv2d)
isinstance(linear,nn.Conv2d)
对于不同的类型层,我们就可以设置不同的权值初始化的方法。
# 查看随机初始化的conv参数
conv.weight.data
# 查看linear的参数
linear.weight.data
# 对conv进行kaiming初始化
torch.nn.init.kaiming_normal_(conv.weight.data)
conv.weight.data
# 对linear进行常数初始化
torch.nn.init.constant_(linear.weight.data,0.3)
linear.weight.data
初始化函数封装:将各种初始化方法定义为一个initialize_weights()的函数并在模型初始后进行使用。如下代码遍历当前模型的每一层,然后判断各层属于什么类型,然后根据不同类型层,设定不同的权值初始化方法。
def initialize_weights(self):
for m in self.modules():
# 判断是否属于Conv2d
if isinstance(m, nn.Conv2d):
torch.nn.init.xavier_normal_(m.weight.data)
# 判断是否有偏置
if m.bias is not None:
torch.nn.init.constant_(m.bias.data,0.3)
elif isinstance(m, nn.Linear):
torch.nn.init.normal_(m.weight.data, 0.1)
if m.bias is not None:
torch.nn.init.zeros_(m.bias.data)
elif isinstance(m, nn.BatchNorm2d):
m.weight.data.fill_(1)
m.bias.data.zeros_()
二分类交叉熵损失函数:计算二分类任务时的交叉熵(Cross Entropy)函数。在二分类中,label是{0,1}。对于进入交叉熵函数的input为概率分布的形式。一般来说,input为sigmoid激活层的输出,或者softmax的输出。
torch.nn.BCELoss(weight=None, size_average=None, reduce=None, reduction='mean')
# weight:每个类别的loss设置权值
# size_average:数据为bool,为True时,返回的loss为平均值;为False时,返回的各样本的loss之和
# reduce:数据类型为bool,为True时,loss的返回是标量。
交叉熵损失函数:交叉熵函数
torch.nn.CrossEntropyLoss(weight=None, size_average=None, ignore_index=-100, reduce=None, reduction='mean')
ignore_index:忽略某个类的损失函数
L1损失函数:计算输出y和真实标签target之间的差值的绝对值。
torch.nn.L1Loss(size_average=None, reduce=None, reduction='mean')
# reduction有三种计算模式可选
# none:逐个元素计算。sum:所有元素求和,返回标量。mean:加权平均,返回标量。默认求平均
MSE损失函数:计算输出y和真实标签target之差的平方。
torch.nn.MSELoss(size_average=None, reduce=None, reduction='mean')
# reduction参数与L1一样
平滑L1 (Smooth L1)损失函数: L1的平滑输出,其功能是减轻离群点带来的影响
torch.nn.SmoothL1Loss(size_average=None, reduce=None, reduction='mean', beta=1.0)
目标泊松分布的负对数似然损失: 泊松分布的负对数似然损失函数
torch.nn.PoissonNLLLoss(log_input=True, full=False, size_average=None, eps=1e-08, reduce=None, reduction='mean')
# log_input:输入是否为对数形式,决定计算公式
# full:计算所有 loss,默认为 False
# eps:修正项,避免 input 为 0 时,log(input) 为 nan 的情况
KL散度:计算KL散度,也就是计算相对熵。用于连续分布的距离度量,并且对离散采用的连续输出空间分布进行回归通常很有用。
torch.nn.KLDivLoss(size_average=None, reduce=None, reduction='mean', log_target=False)
# reduction:计算模式,可为 none/sum/mean/batchmean
MarginRankingLoss:计算两个向量之间的相似度,用于排序任务。该方法用于计算两组数据之间的差异。
torch.nn.MarginRankingLoss(margin=0.0, size_average=None, reduce=None, reduction='mean')
多标签边界损失函数: 对于多标签分类问题计算损失函数
torch.nn.MultiLabelMarginLoss(size_average=None, reduce=None, reduction='mean')
二分类损失函数:计算二分类的 logistic 损失
torch.nn.SoftMarginLoss(size_average=None, reduce=None, reduction='mean')torch.nn.(size_average=None, reduce=None, reduction='mean')
多分类的折页损失:计算多分类的折页损失
torch.nn.MultiMarginLoss(p=1, margin=1.0, weight=None, size_average=None, reduce=None, reduction='mean')
三元组损失: 计算三元组损失
torch.nn.TripletMarginLoss(margin=1.0, p=2.0, eps=1e-06, swap=False, size_average=None, reduce=None, reduction='mean')
HingEmbeddingLoss:对输出的embedding结果做Hing损失计算
torch.nn.HingeEmbeddingLoss(margin=1.0, size_average=None, reduce=None, reduction='mean')
余弦相似度:对两个向量做余弦相似度
torch.nn.CosineEmbeddingLoss(margin=0.0, size_average=None, reduce=None, reduction='mean')
CTC损失函数:用于解决时序类数据的分类
torch.nn.CTCLoss(blank=0, reduction='mean', zero_infinity=False)
注:具体公式及理解后续赘述。
训练:训练状态,那么模型的参数应该支持反向传播的修改
model.train() # 训练状态
评估:验证/测试状态,则不应该修改模型参数
model.eval() # 验证/测试状态
训练流程:
# 用for循环读取DataLoader中的全部数据
for data, label in train_loader:
# 将数据放到GPU上用于后续计算
data, label = data.cuda(), label.cuda()
# 用当前批次数据做训练时,应当先将优化器的梯度置零
optimizer.zero_grad()
# 将data送入模型中训练
output = model(data)
# 预先定义的criterion计算损失函数
loss = criterion(output, label)
# 将loss反向传播回网络:
loss.backward()
# 使用优化器更新模型参数:
optimizer.step()
评估流程:验证/测试的流程基本与训练过程一致,不同点在于:
一般为了便于观察,通常会使用可视化的方式,将结果更直观的展现出来。比如分类的ROC曲线,以及训练验证过程中的损失函数曲线等。
注:关于可视化,后续会专门出一节。
文章链接:基础实战——Fashion-mnist时装分类。