Pytorch:主要组成模块 -学习笔记

基本配置

1.导入必须的包。

2.超参数的统一设置。

  • batch size

  • 初始学习率(初始)

  • 训练次数(max_epochs)

  • GPU配置

batch_size = 16
lr = 1e-4
max_epochs = 100

#GPU配置
# 方案一:使用os.environ,这种情况如果使用GPU不需要设置
os.environ['CUDA_VISIBLE_DEVICES'] = '0,1'

# 方案二:使用“device”,后续对要使用GPU的变量用.to(device)即可
device = torch.device("cuda:1" if torch.cuda.is_available() else "cpu")

数据读入

PyTorch数据读入是通过Dataset+Dataloader的方式完成的,Dataset定义好数据的格式和数据变换形式,Dataloader用iterative的方式不断读入批次数据。

用Dataset进行数据读取

继承Dataset 类需要override 以下⽅法:

from torch.utils.data import Dataset
class trainDataset(Dataset):
    def __init__(self):
    #用于向类中传入外部参数,同时定义样本集

    def __getitem__(self,index):
    #用于逐个读取样本集合中的元素,可以进行一定的变换,并将返回训练/验证所需的数据

    def __len__(self):
    #用于返回数据集的样本数

用DataLoader来按批次读入数据

val_loader=torch.utils.data.DataLoader(train_data, batch_size=batch_size, num_workers=4, shuffle=True, drop_last=True)

# batch_size:样本是按“批”读入的,batch_size就是每次读入的样本数
# num_workers:有多少个进程用于读取数据
# shuffle:是否将读入的数据打乱
# drop_last:对于样本最后一部分没有达到批次数的样本,不再参与训练

PyTorch中的DataLoader的读取可以使用next和iter来完成

import matplotlib.pyplot as plt
images, labels = next(iter(val_loader))
print(images.shape)
plt.imshow(images[0].transpose(1,2,0))
plt.show()

模型的构建

PyTorch中神经网络构造一般是基于 Module 类的模型来完成的。Module 类是 nn 模块里提供的一个模型构造类,是所有神经⽹网络模块的基类,我们可以继承它来定义我们想要的模型。

所有的模型都需要继承torch.nn.Module , 需要实现以下⽅法:

class MyModel(torch.nn.Module):
    def __init__(self):
        super(MyModel, self).__init__()
        # 声明带有模型参数的层(神经网络)
    def forward(self,x):
        # 定义模型的前向计算,即如何根据输入x计算返回所需要的模型输出
        return

model = MyModel()

其中forward() ⽅法是前向传播的过程。在实现模型时,我们不需要考虑反向传播。系统将通过⾃动求梯度⽽自动⽣成反向传播所需的 backward 函数。

损失函数

PyTorch中一些常用的损失函数(一般通过torch.nn调用),及每个损失函数的功能介绍、数学公式和调用代码。

L1损失函数

功能: 计算输出y和真实标签target之间的差值的绝对值。

torch.nn.L1Loss(size_average=None, reduce=None, reduction='mean')`
​
# ‘reduction’参数决定了计算模式。一共有三种模式:
# none:逐个元素计算。
# sum:所有元素求和,返回标量。
# mean:加权平均,返回标量。 
# 如果选择‘none’,那么返回的结果是和输入元素相同尺寸的。默认计算方式是求平均。

计算公式

L_{n} = | x_{n}-y_{n}|

实现代码

loss = nn.L1Loss()
input = torch.randn(3, 5, requires_grad=True)
target = torch.randn(3, 5)
output = loss(input, target)
output.backward()
print('L1损失函数的计算结果为',output)
L1损失函数的计算结果为 tensor(1.5729, grad_fn=<L1LossBackward>)

MSE损失函数

功能:计算输出y和真实标签target之差的平方。

torch.nn.MSELoss(size_average=None, reduce=None, reduction='mean')

计算公式:

l_{n}=\left(x_{n}-y_{n}\right)^{2}

实现代码:

loss = nn.MSELoss()
input = torch.randn(3, 5, requires_grad=True)
target = torch.randn(3, 5)
output = loss(input, target)
output.backward()
print('MSE损失函数的计算结果为',output)
MSE损失函数的计算结果为 tensor(1.6968, grad_fn=<MseLossBackward>)

平滑L1 (Smooth L1)损失函数

功能: L1的平滑输出,其功能是减轻离群点带来的影响

torch.nn.SmoothL1Loss(size_average=None, reduce=None, reduction='mean', beta=1.0)`

计算公式:

\operatorname{loss}(x, y)=\frac{1}{n} \sum_{i=1}^{n} z_{i}

z_{i}=\left\{\begin{array}{ll} 0.5\left(x_{i}-y_{i}\right)^{2}, & \text { if }\left|x_{i}-y_{i}\right|<1 \\ \left|x_{i}-y_{i}\right|-0.5, & \text { otherwise } \end{array}\right.

代码实现:

loss = nn.SmoothL1Loss()
input = torch.randn(3, 5, requires_grad=True)
target = torch.randn(3, 5)
output = loss(input, target)
output.backward()
print('SmoothL1Loss损失函数的计算结果为',output)
SmoothL1Loss损失函数的计算结果为 tensor(0.7808, grad_fn=<SmoothL1LossBackward>)

目标泊松分布的负对数似然损失

功能: 泊松分布的负对数似然损失函数

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 的情况。

数学公式:

  • 当参数log_input=True

\operatorname{loss}\left(x_{n}, y_{n}\right)=e^{x_{n}}-x_{n} \cdot y_{n}

  • 当参数log_input=False

    \operatorname{loss}\left(x_{n}, y_{n}\right)=x_{n}-y_{n} \cdot \log \left(x_{n}+\text { eps }\right)

loss = nn.PoissonNLLLoss()
log_input = torch.randn(5, 2, requires_grad=True)
target = torch.randn(5, 2)
output = loss(log_input, target)
output.backward()
​print('PoissonNLLLoss损失函数的计算结果为',output)
PoissonNLLLoss损失函数的计算结果为 tensor(0.7358, grad_fn=<MeanBackward0>)

KL散度

功能: 计算KL散度,也就是计算相对熵。用于连续分布的距离度量,并且对离散采用的连续输出空间分布进行回归通常很有用。

torch.nn.KLDivLoss(size_average=None, reduce=None, reduction='mean', log_target=False)

# reduction:计算模式,可为 none/sum/mean/batchmean。

# none:逐个元素计算。
​
# sum:所有元素求和,返回标量。
​
# mean:加权平均,返回标量。
​
# batchmean:batchsize 维度求平均值。

计算公式:

\begin{aligned} D_{\mathrm{KL}}(P, Q)=\mathrm{E}_{X \sim P}\left[\log \frac{P(X)}{Q(X)}\right] &=\mathrm{E}_{X \sim P}[\log P(X)-\log Q(X)] \\ &=\sum_{i=1}^{n} P\left(x_{i}\right)\left(\log P\left(x_{i}\right)-\log Q\left(x_{i}\right)\right) \end{aligned}

inputs = torch.tensor([[0.5, 0.3, 0.2], [0.2, 0.3, 0.5]]) target = torch.tensor([[0.9, 0.05, 0.05], [0.1, 0.7, 0.2]], dtype=torch.float) loss = nn.KLDivLoss() output = loss(inputs,target)​
print('PoissonNLLLoss损失函数的计算结果为',output)
PoissonNLLLoss损失函数的计算结果为 tensor(-0.3335)

MarginRankingLoss

功能: 计算两个向量之间的相似度,用于排序任务。该方法计算两组数据之间的差异。

torch.nn.MarginRankingLoss(margin=0.0, size_average=None, reduce=None, reduction='mean')

# margin:边界值,x{1}与x{2}之间的差异值。

# reduction:计算模式,可为 none/sum/mean。

计算公式:
\operatorname{loss}(x 1, x 2, y)=\max (0,-y *(x 1-x 2)+\operatorname{margin})
 

loss = nn.MarginRankingLoss() 
input1 = torch.randn(3, requires_grad=True) 
input2 = torch.randn(3, requires_grad=True) 
target = torch.randn(3).sign() 
output = loss(input1, input2, target) output.backward()​

print('MarginRankingLoss损失函数的计算结果为',output)
MarginRankingLoss损失函数的计算结果为 tensor(0.7740, grad_fn=<MeanBackward0>)

多标签边界损失函数

功能: 对于多标签分类问题计算损失函数。

torch.nn.MultiLabelMarginLoss(size_average=None, reduce=None, reduction='mean')

# reduction:计算模式,可为 none/sum/mean。

计算公式:

\operatorname{loss}(x, y)=\sum_{i j} \frac{\max (0,1-x[y[j]]-x[i])}{x \cdot \operatorname{size}(0)}

loss = nn.MultiLabelMarginLoss() 
x = torch.FloatTensor([[0.9, 0.2, 0.4, 0.8]]) 
# for target y, only consider labels 3 and 0, not after label -1 
y = torch.LongTensor([[3, 0, -1, 1]]) # 真实的分类是,第3类和第0类 
output = loss(x, y)​
print('MarginRankingLoss损失函数的计算结果为',output)
MarginRankingLoss损失函数的计算结果为 tensor(0.4500)

二分类损失函数

torch.nn.SoftMarginLoss(size_average=None, reduce=None, reduction='mean')torch.nn.(size_average=None, reduce=None, reduction='mean')

功能: 二分类的 logistic 损失。

计算公式:


\operatorname{loss}(x, y)=\sum_{i} \frac{\log (1+\exp (-y[i] \cdot x[i]))}{x \cdot \operatorname{nelement}()}

inputs = torch.tensor([[0.3, 0.7], [0.5, 0.5]])  # 两个样本,两个神经元
target = torch.tensor([[-1, 1], [1, -1]], dtype=torch.float) 
 # 该 loss 为逐个神经元计算,需要为每个神经元单独设置标签
​
loss_f = nn.SoftMarginLoss()
output = loss_f(inputs, target)
​
print('SoftMarginLoss损失函数的计算结果为',output)
SoftMarginLoss损失函数的计算结果为 tensor(0.6764)

优化器

Pytorch很人性化的给我们提供了一个优化器的库torch.optim,在这里面给我们提供了十种优化器。

  • torch.optim.ASGD

  • torch.optim.Adadelta

  • torch.optim.Adagrad

  • torch.optim.Adam

  • torch.optim.AdamW

  • torch.optim.Adamax

  • torch.optim.LBFGS

  • torch.optim.RMSprop

  • torch.optim.Rprop

  • torch.optim.SGD

  • torch.optim.SparseAdam

而以上这些优化算法均继承于Optimizer,下面我们先来看下所有优化器的基类Optimizer。定义如下:

class Optimizer(object):
    def __init__(self, params, defaults):        
        self.defaults = defaults
        self.state = defaultdict(dict)
        self.param_groups = []

# `defaults`:存储的是优化器的超参数
{'lr': 0.1, 'momentum': 0.9, 'dampening': 0, 'weight_decay': 0, 'nesterov': False}

# `state`:参数的缓存

# `param_groups`:管理的参数组,是一个list,其中每个元素是一个字典,顺序是params,lr,momentum,dampening,weight_decay,nesterov,

Optimizer还有以下的方法

zero_grad()清空所管理参数的梯度,Pytorch的特性是张量的梯度不自动清零,因此每次反向传播后都需要清空梯度。
step()执行一步梯度更新,参数更新
add_param_group()添加参数组
load_state_dict()加载状态参数字典,可以用来进行模型的断点续训练,继续上次的参数进行训练
state_dict()获取优化器当前状态信息字典

注意:

  1. 每个优化器都是一个类,我们一定要进行实例化才能使用,比如下方实现:

    class Net(nn.Moddule):
        ···
    net = Net()
    optim = torch.optim.SGD(net.parameters(),lr=lr)
    optim.step()
  2. optimizer在一个神经网络的epoch中需要实现下面两个步骤:

    1. 梯度置零

    2. 梯度更新

optimizer = torch.optim.SGD(net.parameters(), lr=1e-5)
for epoch in range(EPOCH):
	...
	optimizer.zero_grad()  #梯度置零
	loss = ...             #计算loss
	loss.backward()        #BP反向传播
	optimizer.step()       #梯度更新

训练和评估

完成了上述设定后就可以加载数据开始训练模型了。首先应该设置模型的状态:如果是训练状态,那么模型的参数应该支持反向传播的修改;如果是验证/测试状态,则不应该修改模型参数。在PyTorch中,模型的状态设置非常简便,如下的两个操作二选一即可:

model.train()   # 训练状态
model.eval()   # 验证/测试状态

我们前面在DataLoader构建完成后介绍了如何从中读取数据,在训练过程中使用类似的操作即可,区别在于此时要用for循环读取DataLoader中的全部数据。

for data, label in train_loader:

之后将数据放到GPU上用于后续计算,此处以.cuda()为例

data, label = data.cuda(), label.cuda()

开始用当前批次数据做训练时,应当先将优化器的梯度置零:

optimizer.zero_grad()

之后将data送入模型中训练:

output = model(data)

根据预先定义的criterion计算损失函数:

loss = criterion(output, label)

将loss反向传播回网络:

loss.backward()

使用优化器更新模型参数:

optimizer.step()

这样一个训练过程就完成了,后续还可以计算模型准确率等指标。

验证/测试的流程基本与训练过程一致,不同点在于:

  • 需要预先设置torch.no_grad,以及将model调至eval模式

  • 不需要将优化器的梯度置零

  • 不需要将loss反向回传到网络

  • 不需要更新optimizer

一个完整的训练过程如下所示:

def train(epoch):
    model.train()
    train_loss = 0
    for data, label in train_loader:
        data, label = data.cuda(), label.cuda()
        optimizer.zero_grad()
        output = model(data)
        loss = criterion(label, output)
        loss.backward()
        optimizer.step()
        train_loss += loss.item()*data.size(0)
    train_loss = train_loss/len(train_loader.dataset)
    print('Epoch: {} \tTraining Loss: {:.6f}'.format(epoch, train_loss))
​

对应的,一个完整的验证过程如下所示:

def val(epoch):       
    model.eval()
    val_loss = 0
    with torch.no_grad():
        for data, label in val_loader:
            data, label = data.cuda(), label.cuda()
            output = model(data)
            preds = torch.argmax(output, 1)
            loss = criterion(output, label)
            val_loss += loss.item()*data.size(0)
            running_accu += torch.sum(preds == label.data)
    val_loss = val_loss/len(val_loader.dataset)
    print('Epoch: {} \tTraining Loss: {:.6f}'.format(epoch, val_loss))

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值