Pytorch实践--eval 模式,每次预测相同的数据得到的概率不同

本文探讨了在使用PyTorch训练模型并进行预测时遇到的问题:即使设置了eval()模式,相同的输入数据仍会产生不同的预测概率。文章深入分析了原因,并强调了正确使用Dropout和BatchNorm层的重要性。

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

描述:使用pytorch训练好的模型对数据进行预测时,将model设置为eval()模式之后,发现同样的数据每次预测时的概率值会变动。

分析:eval()可以将模型切换为 evaluation 模式,(仅仅当模型中含有Dropout和BatchNorm才起作用)。这里所说的 “模型中含有Dropout和BatchNorm ” 是指 包含在*_modules* 中(当类成员的type派生于Module时,该成员会自动划归为 _modules)。

查看源码发现,在某个成员函数里使用了 nn.Dropout ,但是 不属于 _modules 所以出现每次预测相同的数据出现不同的概率的现象。
在这里插入图片描述
故:使用pytorch的Dropout 或 BN 构建网络时,一定要把该神经网络层放入 _modules 否则不起作用。

解释一下以下代码:def train_model(model, criterion, optimizer, scheduler, num_epochs=25): since = time.time() # 返回当前间的间戳 # deepcopy为深拷贝,即创建一个新的对象,完全复制原始对象及其所有子对象。 # model.state_dict是PyTorch中的一个方法,返回一个字典对象,将模型的所有参数映射到它们的张量值 best_model_wts = copy.deepcopy(model.state_dict()) best_acc = 0.0 # 显示当前的训练进度 for epoch in range(num_epochs): print('Epoch {}/{}'.format(epoch, num_epochs - 1)) # 输出训练进度 print('-' * 10) # 每个epoch都有一个训练和验证阶段 for phase in ['train', 'val']: if phase == 'train': # 调用Optimizer的step函数使它所有参数更新 optimizer.step() # PyTorch中用于更新优化器的学习率 scheduler.step() model.train() # 设置模型的工作模式:训练模式,模型会更新参数 else: model.eval() # 设置模型的工作模式:评估模式,模型不会更新参数 running_loss = 0.0 # 用于记录当前epoch的累计损失 running_corrects = 0 # 用于记录当前正确分类的数量 # 迭代数据 for inputs, labels in dataloaders[phase]: # GPU加速,inputs、labels用于将输入数据和标签数据移动到指定的设备上,例如CPU或GPU inputs = inputs.to(device) labels = labels.to(device) # 清空梯度,在每次优化前都需要进行此操作 optimizer.zero_grad() # torch.set_grad_enabled在训练模式下启用梯度计算,而在评估模式下禁用梯度计算 with torch.set_grad_enabled(phase == 'train'): outputs = model(inputs) _, preds = torch.max(outputs, 1) # 返回输入张量在指定维度上的最大值及其对应的索引 loss = criterion(outputs, labels) # 后向 + 仅在训练阶段进行优化 if phase == 'train': # 反向传播:根据模型的参数计算loss的梯度 loss.backward() # 调用Optimizer的step函数使它所有参数更新 optimizer.step() running_loss += loss.item() * inputs.size(0) # 获取损失值和输入数据的数量 running_corrects += torch.sum(preds == labels.data) # 记录当前epoch中正确分类的数量 # dataset_sizes[phase]:该变量用于获取数据集在当前阶段中的大小 epoch_loss = running_loss / dataset_sizes[phase] #当前epoch的平均损失 epoch_acc = running_corrects.double() / dataset_sizes[phase] # 当前epoch的准确率 # 打印当前epoch的损失和准确率,以实监控模型的训练状态 print('{} Loss: {:.4f} Acc: {:.4f}'.format( phase, epoch_loss, epoch_acc)) # 前阶段的名称、当前epoch的平均损失、当前epoch的准确率 if phase == 'val' and epoch_acc > best_acc: #更新最佳模型参数 best_acc = epoch_acc best_model_wts = copy.deepcopy(model.state_dict()) print() time_elapsed = time.time() - since # 输出整个训练过程的间 print('Training complete in {:.0f}m {:.0f}s'.format( time_elapsed // 60, time_elapsed % 60)) # 用于输出最佳验证准确率,best_acc是训练过程中被更新为模型在验证集上的最佳准确率 print('Best val Acc: {:4f}'.format(best_acc)) # 用于加载模型的状态字典,即将最佳模型的参数设置为给定的参数字典 model.load_state_dict(best_model_wts) return model
最新发布
08-07
<think>我们正在解释一个典型的PyTorch模型训练和验证代码结构。代码通常包含以下关键部分: 1. 训练循环(Training Loop) 2. 验证循环(Validation Loop) 3. 优化器(Optimizer) 4. 学习率调度器(Learning Rate Scheduler) 5. 损失函数(Loss Function) 6. 准确率计算(Accuracy Calculation) 7. 模型参数保存(Model Checkpoint Saving) 下面将逐一解释这些部分。 ### 1. 训练循环(Training Loop) 训练循环是模型训练的核心,通常在每个epoch中遍历整个训练数据集。主要步骤包括: - 将模型设置为训练模式(`model.train()`) - 遍历训练数据加载器(`train_loader`) -数据移至GPU(如果可用) - 前向传播(计算模型输出) - 计算损失(比较模型输出和真实标签) - 反向传播(计算梯度) - 优化器更新参数(根据梯度更新模型权重) - 记录训练损失和准确率 代码框架: ```python for epoch in range(num_epochs): model.train() # 设置模型为训练模式 running_loss = 0.0 running_corrects = 0 for inputs, labels in train_loader: inputs, labels = inputs.to(device), labels.to(device) # 前向传播 outputs = model(inputs) loss = criterion(outputs, labels) # 反向传播和优化 optimizer.zero_grad() # 清除之前的梯度 loss.backward() # 反向传播,计算梯度 optimizer.step() # 更新参数 # 统计 _, preds = torch.max(outputs, 1) # 获取预测类别 running_loss += loss.item() * inputs.size(0) running_corrects += torch.sum(preds == labels.data) epoch_loss = running_loss / len(train_dataset) epoch_acc = running_corrects.double() / len(train_dataset) # 注意使用double()避免整数除法 ``` ### 2. 验证循环(Validation Loop) 验证循环用于评估模型在验证集上的性能,通常在训练一个epoch后进行。主要步骤包括: - 将模型设置为评估模式(`model.eval()`) - 不计算梯度(使用`torch.no_grad()`) - 遍历验证数据加载器(`val_loader`) - 计算验证损失和准确率 代码框架: ```python model.eval() # 设置模型为评估模式 val_running_loss = 0.0 val_running_corrects = 0 with torch.no_grad(): for inputs, labels in val_loader: inputs, labels = inputs.to(device), labels.to(device) outputs = model(inputs) loss = criterion(outputs, labels) _, preds = torch.max(outputs, 1) val_running_loss += loss.item() * inputs.size(0) val_running_corrects += torch.sum(preds == labels.data) val_loss = val_running_loss / len(val_dataset) val_acc = val_running_corrects.double() / len(val_dataset) ``` ### 3. 优化器(Optimizer) 优化器用于更新模型参数以最小化损失函数。PyTorch提供了多种优化器,如SGD、Adam等。 ```python import torch.optim as optim optimizer = optim.Adam(model.parameters(), lr=0.001) ``` ### 4. 学习率调度器(Learning Rate Scheduler) 学习率调度器用于在训练过程中动态调整学习率,以提高模型性能或加快收敛。常见的调度器有`StepLR`、`ReduceLROnPlateau`等。 ```python # 每7个epoch将学习率乘以0.1 scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1) # 在训练循环的每个epoch后更新学习率 scheduler.step() # 注意:通常放在每个epoch的末尾[^2] ``` ### 5. 损失函数(Loss Function) 损失函数用于衡量模型输出与真实标签的差异。分类任务常用交叉熵损失(CrossEntropyLoss)。 ```python criterion = torch.nn.CrossEntropyLoss() ``` ### 6. 准确率计算(Accuracy Calculation) 准确率是分类任务中常用的评估指标。计算方式为正确预测的样本数除以总样本数。注意使用`.double()`避免整数除法问题。 ```python _, preds = torch.max(outputs, 1) # 获取预测类别(最大概率的索引) corrects = torch.sum(preds == labels.data) accuracy = corrects.double() / len(labels) # 转换为double计算浮点数结果 ``` ### 7. 模型参数保存(Model Checkpoint Saving) 通常在验证集上性能提升保存模型参数,或者定期保存。保存的内容包括模型参数、优化器状态、epoch等,以便恢复训练。 ```python # 保存最佳模型 if val_acc > best_acc: best_acc = val_acc torch.save({ 'epoch': epoch, 'model_state_dict': model.state_dict(), 'optimizer_state_dict': optimizer.state_dict(), 'scheduler_state_dict': scheduler.state_dict() if scheduler is not None else None, 'loss': criterion, 'best_acc': best_acc, }, 'best_model.pth') ``` ### 完整训练流程示例 ```python import torch from torch import nn, optim # 初始化模型、优化器、损失函数等 model = ... # 定义模型 device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') model = model.to(device) criterion = nn.CrossEntropyLoss() optimizer = optim.Adam(model.parameters(), lr=0.001) scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1) best_acc = 0.0 for epoch in range(num_epochs): # 训练阶段 model.train() running_loss = 0.0 running_corrects = 0 for inputs, labels in train_loader: inputs, labels = inputs.to(device), labels.to(device) optimizer.zero_grad() outputs = model(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() _, preds = torch.max(outputs, 1) running_loss += loss.item() * inputs.size(0) running_corrects += torch.sum(preds == labels.data) epoch_loss = running_loss / len(train_dataset) epoch_acc = running_corrects.double() / len(train_dataset) # 验证阶段 model.eval() val_running_loss = 0.0 val_running_corrects = 0 with torch.no_grad(): for inputs, labels in val_loader: inputs, labels = inputs.to(device), labels.to(device) outputs = model(inputs) loss = criterion(outputs, labels) _, preds = torch.max(outputs, 1) val_running_loss += loss.item() * inputs.size(0) val_running_corrects += torch.sum(preds == labels.data) val_loss = val_running_loss / len(val_dataset) val_acc = val_running_corrects.double() / len(val_dataset) # 更新学习率 scheduler.step() # [^2] # 保存最佳模型 if val_acc > best_acc: best_acc = val_acc torch.save({ 'epoch': epoch, 'model_state_dict': model.state_dict(), 'optimizer_state_dict': optimizer.state_dict(), 'best_acc': best_acc, }, 'best_model.pth') print(f'Epoch {epoch}: Train Loss: {epoch_loss:.4f}, Acc: {epoch_acc:.4f}, Val Loss: {val_loss:.4f}, Val Acc: {val_acc:.4f}') ``` ### 关键点总结 - **训练模式与评估模式**:使用`model.train()`和`model.eval()`切换,影响特定层(如Dropout和BatchNorm)的行为。 - **梯度清零**:每次迭代前使用`optimizer.zero_grad()`避免梯度累积。 - **损失计算与反向传播**:`loss.backward()`计算梯度,`optimizer.step()`更新参数。 - **学习率调度**:在epoch结束后调用`scheduler.step()`[^2]。 - **准确率计算**:使用`torch.max`获取预测类别,并注意使用`.double()`避免整数除法。 - **模型保存**:保存最佳模型或定期保存,包括模型参数和优化器状态。 通过以上步骤,可以构建一个完整的PyTorch模型训练和验证流程。 §§相关问题§§ 1. 为什么在训练循环中需要调用`optimizer.zero_grad()`? 2. 学习率调度器应该在什么候调用`step()`方法?[^2] 3. 如何选择适合的损失函数? 4. 模型在验证阶段为什么需要设置`model.eval()`? 5. 如何避免准确率计算中的整数除法问题? 6. 保存模型,除了模型参数,为什么还要保存优化器状态?</think>下面我将详细解释 PyTorch 模型训练代码的各个核心组件及其作用,帮助您理解完整的训练与验证流程: ### 1. 训练循环(Training Loop) 训练循环是模型学习的核心框架,包含以下关键步骤: ```python model.train() # 设置为训练模式 for inputs, labels in train_loader: # 数据加载 inputs, labels = inputs.to(device), labels.to(device) # 前向传播 outputs = model(inputs) # 损失计算 loss = criterion(outputs, labels) # 反向传播 optimizer.zero_grad() # 清除历史梯度 loss.backward() # 计算新梯度 # 参数更新 optimizer.step() # 更新权重 ``` 作用: - `model.train()`:启用 Dropout/BatchNorm 等训练专用层 - 前向传播:计算模型预测- 损失计算:量化预测与真实标签的差异 - 反向传播:通过链式法则计算梯度 $\frac{\partial loss}{\partial w}$ - 参数更新:根据梯度调整权重[^3] ### 2. 优化器(Optimizer) 优化器决定如何利用梯度更新参数: ```python optimizer = torch.optim.Adam(model.parameters(), lr=0.001) ``` 常见类型: - **SGD**:基础梯度下降 $w_{t+1} = w_t - \eta \nabla f(w_t)$ - **Adam**:自适应学习率(推荐首选) - **RMSProp**:适合非平稳目标 作用: - 管理参数更新策略 - 处理动量(Momentum)加速收敛 - 自适应调整学习率[^3] ### 3. 学习率调度器(LR Scheduler) 动态调整学习率提升收敛性: ```python scheduler = torch.optim.lr_scheduler.StepLR( optimizer, step_size=10, # 每10个epoch调整 gamma=0.1 # 学习率乘以0.1 ) # 在epoch循环末尾调用 scheduler.step() # [^2] ``` 常见策略: | 类型 | 特点 | 适用场景 | |------|------|----------| | `StepLR` | 阶梯式下降 | 稳定收敛 | | `ReduceLROnPlateau` | 基于验证损失调整 | 避免过拟合 | | `CosineAnnealing` | 余弦退火 | 跳出局部最优 | 作用:平衡收敛速度与精度,避免振荡[^2][^3] ### 4. 损失计算(Loss Calculation) 损失函数量化模型错误程度: ```python # 分类任务常用交叉熵损失 criterion = nn.CrossEntropyLoss() # 回归任务常用MSE criterion = nn.MSELoss() ``` 数学原理: - 交叉熵:$H(p,q) = -\sum p(x)\log q(x)$ - MSE:$\frac{1}{n}\sum(y_{pred} - y_{true})^2$ 作用:为反向传播提供优化方向[^3] ### 5. 准确率计算(Accuracy) 验证模型性能的核心指标: ```python model.eval() # 设置为评估模式 with torch.no_grad(): outputs = model(inputs) _, preds = torch.max(outputs, 1) # 获取预测类别[^1] corrects = (preds == labels).sum().item() accuracy = corrects / len(labels) # 基础计算 # 推荐使用: accuracy = torch.sum(preds == labels).double() / len(labels) # 避免整数除法 ``` 关键点: - `model.eval()`:禁用Dropout等训练层 - `torch.no_grad()`:关闭梯度计算节省内存 - `.double()`:确保浮点数除法精度[^1] ### 6. 模型参数保存 保存训练成果的两种策略: ```python # 保存最佳模型 if val_acc > best_acc: best_acc = val_acc torch.save({ 'epoch': epoch, 'model_state_dict': model.state_dict(), 'optimizer_state_dict': optimizer.state_dict(), }, 'best_model.pth') # 定期保存 if epoch % 10 == 0: torch.save(model.state_dict(), f'checkpoint_{epoch}.pth') ``` 保存内容: - `state_dict`:模型参数字典 - 优化器状态:恢复训练所需 - 训练元数据:epoch/最佳准确率等 ### 7. 完整训练-验证流程 ```python for epoch in range(epochs): # 训练阶段 model.train() for inputs, labels in train_loader: ... # 训练步骤 # 验证阶段 model.eval() val_corrects = 0 with torch.no_grad(): for inputs, labels in val_loader: outputs = model(inputs) _, preds = torch.max(outputs, 1) val_corrects += (preds == labels).sum() # 计算指标 val_acc = val_corrects.double() / len(val_dataset) # [^1] # 学习率调整 scheduler.step() # [^2] # 保存模型 if val_acc > best_acc: ... # 保存最佳模型 ``` ### 核心概念关系图 ```mermaid graph LR A[数据加载] --> B[前向传播] B --> C[损失计算] C --> D[反向传播] D --> E[优化器更新参数] E --> F[学习率调整] F --> G[验证集评估] G --> H[模型保存] ``` ### 最佳实践建议 1. **学习率预热**:前几个epoch使用较小学习率 2. **梯度裁剪**:防止梯度爆炸 `torch.nn.utils.clip_grad_norm_` 3. **早停机制**:验证损失不再下降终止训练 4. **混合精度**:使用`torch.cuda.amp`加速训练 这些组件共同构成了PyTorch模型训练的完整生命周期,理解它们的协作关系是掌握深度学习的关键[^1][^2][^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值