pytorch 交叉验证_Pytorch中我们所经过的 “坑”

本文详细介绍了PyTorch中交叉验证的注意事项,包括在验证时取消梯度、显式指定训练和测试模式,以及如何在内存紧张情况下使用梯度累积进行大批次训练。文中强调了在有BN层或dropout层时,必须在训练和测试阶段正确切换train()和eval()状态,以确保模型表现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

a6dfa3098a7233be7218313dd3b148d7.png

一、 调皮的dropout

这个在利用torch.nn.functional.dropout的时候,其参数为:

torch.nn.functional.dropout(input, p=0.5, training=True, inplace=False) 

注意这里有个training指明了是否是在训练阶段,是否需要对神经元输出进行随机丢弃,这个是需要自行指定的,即便是用了model.train()或者model.eval()都是如此,这个和torch.nn.dropout不同,因为后者是一个 层(Layer),而前者只是一个函数,不能纪录状态。

二、pytorch中的交叉熵

pytorch的交叉熵nn.CrossEntropyLoss在训练阶段,里面是内置了softmax操作的,因此只需要喂入原始的数据结果即可,不需要在之前再添加softmax层。

这个和tensorflow的tf.softmax_cross_entropy_with_logits如出一辙. pytorch的交叉熵nn.CrossEntropyLoss在训练阶段,里面是内置了softmax操作的,因此只需要喂入原始的数据结果即可,不需要在之前再添加softmax层。这个和tensorflow的tf.softmax_cross_entropy_with_logits如出一辙.

三、验证时取消掉梯度(no_grad)

一般来说,我们在进行模型训练的过程中,因为要监控模型的性能,在跑完若干个epoch训练之后,需要进行一次在验证集上的性能验证。

一般来说,在验证或者是测试阶段,因为只是需要跑个前向传播(forward)就足够了,因此不需要保存变量的梯度。

保存梯度是需要额外显存或者内存进行保存的,占用了空间,有时候还会在验证阶段导致OOM(OutOfMemory)错误,因此我们在验证和测试阶段,最好显式地取消掉模型变量的梯度。pytroch 0.4及其以后的版本中,用torch.no_grad()这个上下文管理器就可以了

例子:

model.train()
# here train the model, just skip the codes
model.eval() 
# here we start to evaluate the model
with torch.no_grad(): 
for each in eval_data:  
data, label = each  
logit = model(data) 
... # here we just skip the codes 

如上,我们只需要在加上上下文管理器就可以很方便的取消掉梯度。这个功能在pytorch以前的版本中,通过设置volatile=True生效,不过现在这个用法已经被抛弃了。

四、显式指定model.train()model.eval()

我们的模型中经常会有一些子模型,其在训练时候和测试时候的参数是不同的,比如dropout中的丢弃率和Batch Normalization中的和等,这个时候我们就需要显式地指定不同的阶段(训练或者测试),在pytorch中我们通过model.train()model.eval()进行显式指定。

具体如:

model = CNNNet(params)
# here we start the training
model.train()
for each in train_data:
 data, label = each
 logit = model(data)
 loss = criterion(logit, label)
 ... # just skip
# here we start the evaluation

model.eval() 
with torch.no_grad(): # we dont need grad in eval phase
 for each in eval_data:
  data, label = each
  logit = model(data)
  loss = criterion(logit, label)
  ... # just skip

需要注意,在模型中有BN层或者dropout层时,在训练阶段和测试阶段必须显式指定train()和eval()

五、进行梯度累积,实现内存紧张情况下的大batch_size训练

在上面讨论的retain_graph参数中,还可以用于累积梯度,在GPU显存紧张的情况下使用可以等价于用更大的batch_size进行训练。

首先我们要明白,当调用.backward()时,其实是对损失到各个节点的梯度进行计算,计算结果将会保存在各个节点上,如果不用opt.zero_grad()对其进行清0,那么只要你一直调用.backward()梯度就会一直累积,相当于是在大的batch_size下进行的训练。

我们给出几个例子阐述我们的观点。

import torch
import torch.nn as nn
import numpy as np
class net(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc1 = nn.Linear(10,2)
        self.act = nn.ReLU()
    def forward(self,inputv):
        return self.act(self.fc1(inputv))
n = net()
inputv = torch.tensor(np.random.normal(size=(4,10))).float()
output = n(inputv)
target = torch.tensor(np.ones((4,2))).float()
loss = nn.functional.mse_loss(output, target)
loss.backward(retain_graph=True)
opt = torch.optim.Adam(n.parameters(),lr=0.01)
for each in n.parameters():
    print(each.grad)

第一次输出:

tensor([[ 0.0493, -0.0581, -0.0451, 0.0485, 0.1147, 0.1413, -0.0712, -0.1459,

0.1090, -0.0896],

[ 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,

0.0000, 0.0000]])

tensor([-0.1192, 0.0000])

在运行一次loss.backward(retain_graph=True),输出为:

tensor([[ 0.0987, -0.1163, -0.0902, 0.0969, 0.2295, 0.2825, -0.1424, -0.2917,

0.2180, -0.1792],

[ 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,

0.0000, 0.0000]])

tensor([-0.2383, 0.0000])

第三次输出:

tensor([[ 0.1480, -0.1744, -0.1353, 0.1454, 0.3442, 0.4238, -0.2136, -0.4376,

0.3271, -0.2688],

[ 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,

0.0000, 0.0000]])

tensor([-0.3575, 0.0000])

运行一次opt.zero_grad(),输出为:

tensor([[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.],

[0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]])

tensor([0., 0.])

现在明白为什么我们一般在求梯度时要用opt.zero_grad()了吧,那是为什么不要这次的梯度结果被上一次给影响,但是在某些情况下这个‘影响’是可以利用的。

< 完 >

ps: 想学习人工智能的小伙伴,这里有相关的资料;

点击 加群 获取资料 备注来源

人工智能课程第一节 机器学习是什么?

尚学堂人工智能课程 第二节 线性回归 梯度下降

PyTorch并没有内置的交叉验证函数,但你可以使用PyTorch和其他Python库来执行交叉验证。以下是一个基本的示例代码,演示如何使用PyTorch进行交叉验证: ```python import torch import torch.nn as nn import torch.optim as optim from sklearn.model_selection import KFold # 定义你的模型类 class MyModel(nn.Module): def __init__(self): super(MyModel, self).__init__() # 定义模型结构 def forward(self, x): # 定义前向传播逻辑 return x # 创建数据集和标签 dataset = ... labels = ... # 定义超参数和训练参数 num_epochs = 10 batch_size = 32 learning_rate = 0.001 # 初始化交叉验证对象 kfold = KFold(n_splits=5) # 进行交叉验证 for train_indices, val_indices in kfold.split(dataset): # 划分训练集和证集 train_dataset = dataset[train_indices] train_labels = labels[train_indices] val_dataset = dataset[val_indices] val_labels = labels[val_indices] # 创建模型实例 model = MyModel() # 定义损失函数和优化器 criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(model.parameters(), lr=learning_rate) # 训练模型 for epoch in range(num_epochs): # 在训练集上进行训练 model.train() for i in range(0, len(train_dataset), batch_size): batch_data = train_dataset[i:i+batch_size] batch_labels = train_labels[i:i+batch_size] # 前向传播 outputs = model(batch_data) # 计算损失 loss = criterion(outputs, batch_labels) # 反向传播和优化 optimizer.zero_grad() loss.backward() optimizer.step() # 在证集上进行评估 model.eval() with torch.no_grad(): val_outputs = model(val_dataset) val_loss = criterion(val_outputs, val_labels) # 输出当前轮次的损失 print('Epoch [{}/{}], Validation Loss: {:.4f}'.format(epoch+1, num_epochs, val_loss.item())) ``` 在这个例子中,我们使用了`KFold`来划分数据集为训练集和证集。然后,我们在每个fold上训练模型,并在证集上评估模型。你可以根据需要修改代码,以适应你的数据和模型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值