PyTorch 对 Batch 中每个样本计算损失 Loss for each sample

本文介绍了如何在PyTorch中使用MSELoss获取每个样本的独立损失,而不是批量样本的平均损失。通过设置`reduction='none'`,可以避免对样本内部维度求平均,然后沿样本维度计算均值,从而得到每个样本的损失。这种方法适用于在评估阶段需要对每个样本损失进行单独处理的情况。同时,文章强调在训练阶段,通常关注的是批量的平均损失。

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

前言

PyTorch 的损失函数(这里我只使用与调研了 MSELoss)默认会对一个 Batch 的所有样本计算损失,并求均值。如果我需要每个样本的损失用于之后的一些计算(与优化模型参数,梯度下降无关),比如使用样本的损失做一些操作,那使用默认的损失函数做不到,搜了一下没有找到相关的资料,在 PyTorch 的论坛发现了相关的问题。

Loss for each sample in batch - PyTorch Forumshttps://discuss.pytorch.org/t/loss-for-each-sample-in-batch/36200此外,还参考了官方文档:

MSELoss — PyTorch 1.11.0 documentationhttps://pytorch.org/docs/stable/generated/torch.nn.MSELoss.html#torch.nn.MSELoss

解决方案

假设原来的损失函数实例化代码如下:

loss_fn = nn.MSELoss()

改为下面的代码:

loss_fn_each = nn.MSELoss(reduction ='none')  #MSE

假设原来计算损失阶段的代码如下:

loss = loss_fn(Ypred, Y)

则改为:

loss_each = torch.mean(loss_fn_each(Ypred, Y), 1)  # 这里算出来的损失是每个样本的
loss = torch.mean(loss_each)  # 这里算出来是整个 Batch 的平均损失

如果你需要用每个样本的损失做一些操作的话,使用 `loss_each` 即可。如果你预测的结果包括多个维度,那可能需要多次使用 mean 函数。如果需要转为 list 保存,可以直接使用 tolist() 方法。相关内容参见文档与网络。

通常情况下,如果你想要每个样本的损失,应该是在 Eval 阶段了,毕竟训练阶段每个样本损失不稳定也没有太大意义。如果在训练阶段,别忘了下面的代码。

total_loss.append(loss.item())  # 记录损失
optimizer.zero_grad()  # 训练阶段基本代码,清空梯度
loss.backward()  # 训练阶段基本代码,反向传播
optimizer.step()  # 训练阶段基本代码,优化模型参数

原理

在设置损失函数的时候,一般是下面的写法,没有任何参数。

loss_fn = nn.MSELoss()

其实参照官方文档,这个损失函数是可以传入参数 reduction 的。reduction 有三种,默认是 'mean' ,可以是 'none' | 'mean' | 'sum'。

从 none 开始说,none 是最完整的默认的损失,比如我计算出来的模型输出是 batch_size * output_dim 的 shape 的 Tensor(也就是说每个样本有 output_dim 维度的数据),那么送入 MSELoss 的两个输入(我的预测和目标)大小都是这样的, 而 MSELoss 的输出尺寸与输入尺寸是一模一样的,也是(batch_size * output_dim )。

但是我想要的是 (batch_size * 1)的损失,也就是说对于每个样本,样本内部的维度的损失求了均值,但是样本之间不能作平均。使用 mean 的话(即默认情况),得到的是一个( 1*1 )的 Tensor,把 Batch 维度和样本内部维度都做了平均。

所以思路就是直接用  `nn.MSELoss(reduction ='none')` 作为损失函数计算损失,得到的 Tensor 沿 dim=1 求均值,这样每个样本就都计算出了独立的损失,而样本之间并没有求均值。

使用MNIST数据集中的0,1数据完成logistic regression 任务 导入相关包 In [1]: import torch import torchvision as tv import torchvision.transforms as transforms import torch.nn as nn In [2]: # 定义是否使用GPU device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 定义数据预处理方式 transform = transforms.ToTensor() 利用框架自带数据集读取方式读取 MNIST数据集 In [3]: # download 第一次可设置为Ture trainset = tv.datasets.MNIST( root='./data/', train=True, download=True, transform=transform) 从训练集中取出0、1标签的数据构成此次任务的训练集(train_input,train_label) In [4]: train_input = [] train_label = [] for data, label in trainset: if label == 0 or label == 1: train_input.append(data) train_label.append(label) 创建自己的训练数据集,需要补充完成 In [5]: class TensorDataset(torch.utils.data.Dataset): """ 创建自己的dataset类 继承torch.utils.data.Dataset。 Dataset wrapping data and target tensors. Each sample will be retrieved by indexing both tensors along the first dimension. Arguments: data_tensor (Tensor): contains sample data. target_tensor (Tensor): contains sample targets (labels). """ ​ def __init__(self, data_tensor, target_tensor): self.data_tensor = data_tensor self.target_tensor = target_tensor # 这个方法是必须要有的,用于按照索引读取每个元素的具体内容 def __getitem__(self, index): return self.data_tensor[index], self.target_tensor[index] # #return很关键,return回哪些内容,那么我们在训练时循环读取每个batch时, # 就能获得哪些内容 ##这个函数也必须要写,它返回的是数据集的长度 def __len__(self): return self.data_tensor.size(0) 定义训练批处理数据 In [ ]: # batch_size 根据自己计算资源设计,shuffle设置为True,查询设置的原因。 trainloader = torch.utils.data.DataLoader( TensorDataset(train_input, train_label), batch_size = 128, shuffle = True, ) 利用pytorch框架的nn.Module 模块完成logistic regression class logistic_regression(nn.Module): ''' 逻辑回归模型 ''' def __init__(self): super(logistic_regression, self).__init__() " 添加logistic regression层 " def forward(self,
最新发布
03-24
### 构建基于PyTorch的Logistic Regression模型并完成MNIST数据集上的二分类任务 #### 数据加载与预处理 为了实现针对MNIST数据集中仅包含0和1标签的数据进行二分类的任务,可以利用`torchvision.datasets.MNIST`来加载原始数据,并通过自定义过滤函数筛选出只含0和1标签的样本。以下是具体方法: ```python from torchvision import datasets, transforms import torch.utils.data as data_utils transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) # 加载完整的 MNIST 训练和测试数据集 train_dataset_full = datasets.MNIST(root='./data', train=True, transform=transform, download=True) test_dataset_full = datasets.MNIST(root='./data', train=False, transform=transform) # 自定义过滤器以保留只有 0 和 1 的标签 def filter_zero_one(dataset): indices = [] for i in range(len(dataset)): label = dataset[i][1] if label == 0 or label == 1: indices.append(i) return data_utils.Subset(dataset, indices) train_dataset_filtered = filter_zero_one(train_dataset_full) test_dataset_filtered = filter_zero_one(test_dataset_full) # 创建 DataLoader batch_size = 64 train_loader = data_utils.DataLoader(train_dataset_filtered, batch_size=batch_size, shuffle=True) test_loader = data_utils.DataLoader(test_dataset_filtered, batch_size=batch_size, shuffle=False) ``` 上述代码实现了对MNIST数据集的加载、标准化以及按需过滤的功能[^1]。 --- #### 定义 Logistic Regression 模型 在 PyTorch 中,可以通过继承 `nn.Module` 类来自定义模型结构。对于逻辑回归而言,其核心是一个线性层加上激活函数(通常为 Sigmoid)。这里我们只需定义一个简单的单层全连接网络即可满足需求。 ```python import torch.nn as nn import torch.nn.functional as F class LogisticRegressionModel(nn.Module): def __init__(self, input_dim, output_dim): super(LogisticRegressionModel, self).__init__() self.linear = nn.Linear(input_dim, output_dim) # 单一的线性变换 def forward(self, x): out = self.linear(x) # 输出未经激活函数处理的结果用于交叉熵损失计算 return out ``` 此部分展示了如何创建适用于该场景下的基本逻辑回归架构[^2]。 --- #### 设置 Loss Function 及 Optimizer 由于这是一个多类别分类问题(尽管当前简化成了两个类别),推荐采用 Cross Entropy Loss 来衡量预测值与真实值之间的差异度量;同时选用 SGD 或 Adam 等优化算法来进行参数更新迭代过程管理。 ```python model = LogisticRegressionModel(input_dim=28*28, output_dim=2) # 输入维度为图片展平后的大小,输出维度对应两类 criterion = nn.CrossEntropyLoss() # 结合 LogSoftmax 和 NLLoss 的单一类标准 optimizer = torch.optim.SGD(model.parameters(), lr=0.01) # 使用随机梯度下降法作为优化策略之一 ``` 这部分解释了为何选择特定类型的损失函数及其背后原理[^3]。 --- #### 开始训练循环 最后一步就是编写实际执行前向传播、反向传播及权重调整的核心逻辑流程如下所示: ```python num_epochs = 5 for epoch in range(num_epochs): model.train() running_loss = 0.0 for i, (images, labels) in enumerate(train_loader): images = images.view(-1, 28 * 28).requires_grad_() optimizer.zero_grad() outputs = model(images) loss = criterion(outputs, labels) loss.backward() optimizer.step() running_loss += loss.item() print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {running_loss/(i+1):.4f}') print('Finished Training') ``` 以上片段体现了整个训练阶段的操作细节[^4]。 --- #### 测试模型性能 当完成了所有的训练之后还需要验证最终成果的好坏程度可通过下面这段脚本轻松达成目标: ```python correct = 0 total = 0 with torch.no_grad(): for images, labels in test_loader: images = images.view(-1, 28 * 28) outputs = model(images) _, predicted = torch.max(outputs.data, 1) total += labels.size(0) correct += (predicted == labels).sum().item() print(f'Test Accuracy of the Model on Test Images: {(100 * correct / total)}% ') ``` 这一步骤用来评估经过训练后的模型表现情况。 --- ### 总结 综上所述,本文介绍了从头到尾搭建一个专门解决MNIST数据集中关于零和壹这两个数字之间区分工作的逻辑回归解决方案全过程。涵盖了必要的组件如数据准备、模型建立直至评测环节等内容介绍。
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值