深度学习:关于损失函数的一些前置知识(PyTorch Loss)

2024-06-23 16:28

本文主要是介绍深度学习:关于损失函数的一些前置知识(PyTorch Loss),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在之前进行实验的时候发现:调用 Pytorch 中的 Loss 函数之前如果对其没有一定的了解,可能会影响实验效果和调试效率。以 CrossEntropyLoss 为例,最初设计实验的时候没有注意到该函数默认返回的是均值,以为是总和,于是最后计算完 Loss 之后,手动做了个均值,导致实际 Loss 被错误缩放,实验效果不佳,在后来 Debug 排除代码模型架构问题的时候才发觉这一点,着实花费了不少时间。

所以闲暇时准备写一下 Pytorch 中 Loss 函数相关的知识,希望能对初入深度学习的学子们有所帮助,少踩点坑。

这篇文章是用于后续理解的前置知识,在之后有提到新的专业名词时会进行补充。
文章大多以分类模型为例进行叙述。

文章目录

  • 什么是 Logits?
    • Logits 和 Softmax
  • 什么是 One-Hot 编码?
    • 类别不是整数怎么办?

什么是 Logits?

Logits 是指神经网络的最后一个线性层(全连接层)的未经过任何激活函数(例如 softmax 或 sigmoid)处理的输出,可以是任意实数,在分类的任务中,logits 通常是在进行多类别分类任务时的原始输出。

Logits 和 Softmax

在多类别分类问题中,logits 通常会被传递给 softmax 函数,softmax 函数将这些 logits 转换为概率分布:将任意实数的 logits 转换为 [0, 1] 之间的概率值,并且这些概率值的和为 1。

代码示例

为了更好地理解 logits 和 softmax 之间的关系,下面是一个简单的代码示例:

import torch
import torch.nn.functional as F# 样例:分类神经网络,便于对照理解
class Classifier(nn.Module):def __init__(self, input_size, hidden_size, num_classes=3):super(Classifier, self).__init__()self.fc1 = nn.Linear(input_size, hidden_size)  # 输入层到隐藏层self.fc2 = nn.Linear(hidden_size, num_classes)  # 隐藏层到输出层def forward(self, x):out = self.fc1(x)out = F.relu(out)  # ReLU 激活函数logits = self.fc2(out)  # 输出层,不经过 softmaxreturn logits# 假设这是分类神经网络的输出 logits
logits = torch.tensor([[2.0, 1.0, 0.1], [1.0, 3.0, 0.2]])# 使用 softmax 函数将 logits 转换为概率分布
probabilities = F.softmax(logits, dim=1)print("Logits:")
print(logits)
print("\nProbabilities after applying softmax:")
print(probabilities)
>>> Logits:
>>> tensor([[2.0000, 1.0000, 0.1000],
>>>         [1.0000, 3.0000, 0.2000]])>>> Probabilities after applying softmax:
>>> tensor([[0.6590, 0.2424, 0.0986],
>>>         [0.1131, 0.8360, 0.0508]])

输出解释

  1. Logits: [[2.0, 1.0, 0.1], [1.0, 3.0, 0.2]] 是神经网络的输出,未经过 softmax 处理。
  2. Softmax: softmax 函数将 logits 转换为概率分布,每个分布的概率值和为 1。

什么是 One-Hot 编码?

初入深度学习领域的人大多都会有这个疑问:这些所说的类别,究竟是怎么表示成向量的?

One-Hot 是一个很直观的形容,但我当时看到并猜测到相应概念的时候,还是不敢确定,因为太直白了,总觉得编码成向量的过程应该没有这么简单,然而 One-Hot 就是如此,深度学习不是一蹴而就的,看似复杂的概念最初也是由一个个直白的想法发展得来。

具体来说,One-Hot 编码对于每个类别,使用一个与类别数相同长度二进制向量,每个位置对应一个类别。其中,只有一个位置的值为 1(这就是 “One-Hot” 的含义),表示属于该类别,其余位置的值为 0。

例如,对于三个类别的分类问题(类别 A、B 和 C),使用 One-Hot 编码可得:

  • 类别 A: [1, 0, 0]
  • 类别 B: [0, 1, 0]
  • 类别 C: [0, 0, 1]

代码示例

import torch# 假设我们有三个类别:0, 1, 2
num_classes = 3# 样本标签
labels = torch.tensor([0, 2, 1, 0])# 将标签转换为 One-Hot 编码
one_hot_labels = torch.nn.functional.one_hot(labels, num_classes)print("Labels:")
print(labels)
print("\nOne-Hot Encoded Labels:")
print(one_hot_labels)
>>> Labels:
>>> tensor([0, 2, 1, 0])>>> One-Hot Encoded Labels:
>>> tensor([[1, 0, 0],
>>>         [0, 0, 1],
>>>         [0, 1, 0],
>>>         [1, 0, 0]])

输出解释

  1. Labels: [0, 2, 1, 0] 是我们初始的类别标签。
  2. One-Hot Encoded Labels: [[1, 0, 0], [0, 0, 1], [0, 1, 0], [1, 0, 0]] 是将标签转换为 One-Hot 编码后的结果。每个向量中只有一个位置的值为 1(One-Hot)。

类别不是整数怎么办?

看了代码示例,可能会有一个疑问:类别大多不会是整数而是字符,应该怎么编码?或许你心中已经有了一个很直白的答案:那就做一个映射,将类别用整数编码,然后再将这些整数标签转换为 One-Hot 编码。

的确可以这样。

代码示例

import torch# 类别映射:A -> 0, B -> 1, C -> 2
category_map = {'A': 0, 'B': 1, 'C': 2}# 样本类别标签
labels = ['A', 'C', 'B', 'A']# 将类别标签转换为整数标签
integer_labels = torch.tensor([category_map[label] for label in labels])# 将整数标签转换为 One-Hot 编码
num_classes = len(category_map)
one_hot_labels = torch.nn.functional.one_hot(integer_labels, num_classes)print("Labels:")
print(labels)
print("\nInteger Labels:")
print(integer_labels)
print("\nOne-Hot Encoded Labels:")
print(one_hot_labels)
>>> Labels:
>>> ['A', 'C', 'B', 'A']>>> Integer Labels:
>>> tensor([0, 2, 1, 0])>>> One-Hot Encoded Labels:
>>> tensor([[1, 0, 0],
>>>         [0, 0, 1],
>>>         [0, 1, 0],
>>>         [1, 0, 0]])

解释

  1. Labels: ['A', 'C', 'B', 'A'] 是我们初始的类别标签。
  2. Integer Labels: [0, 2, 1, 0] 是将类别标签映射到整数后的结果。A 对应 0,B 对应 1,C 对应 2。
  3. One-Hot Encoded Labels: [[1, 0, 0], [0, 0, 1], [0, 1, 0], [1, 0, 0]] 是将整数标签转换为 One-Hot 编码后的结果。每个向量中只有一个位置的值为 1,表示该样本的类别,其余位置的值为 0。

这篇关于深度学习:关于损失函数的一些前置知识(PyTorch Loss)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1087670

相关文章

C++统计函数执行时间的最佳实践

《C++统计函数执行时间的最佳实践》在软件开发过程中,性能分析是优化程序的重要环节,了解函数的执行时间分布对于识别性能瓶颈至关重要,本文将分享一个C++函数执行时间统计工具,希望对大家有所帮助... 目录前言工具特性核心设计1. 数据结构设计2. 单例模式管理器3. RAII自动计时使用方法基本用法高级用法

深度解析Python中递归下降解析器的原理与实现

《深度解析Python中递归下降解析器的原理与实现》在编译器设计、配置文件处理和数据转换领域,递归下降解析器是最常用且最直观的解析技术,本文将详细介绍递归下降解析器的原理与实现,感兴趣的小伙伴可以跟随... 目录引言:解析器的核心价值一、递归下降解析器基础1.1 核心概念解析1.2 基本架构二、简单算术表达

深度解析Java @Serial 注解及常见错误案例

《深度解析Java@Serial注解及常见错误案例》Java14引入@Serial注解,用于编译时校验序列化成员,替代传统方式解决运行时错误,适用于Serializable类的方法/字段,需注意签... 目录Java @Serial 注解深度解析1. 注解本质2. 核心作用(1) 主要用途(2) 适用位置3

Java MCP 的鉴权深度解析

《JavaMCP的鉴权深度解析》文章介绍JavaMCP鉴权的实现方式,指出客户端可通过queryString、header或env传递鉴权信息,服务器端支持工具单独鉴权、过滤器集中鉴权及启动时鉴权... 目录一、MCP Client 侧(负责传递,比较简单)(1)常见的 mcpServers json 配置

Maven中生命周期深度解析与实战指南

《Maven中生命周期深度解析与实战指南》这篇文章主要为大家详细介绍了Maven生命周期实战指南,包含核心概念、阶段详解、SpringBoot特化场景及企业级实践建议,希望对大家有一定的帮助... 目录一、Maven 生命周期哲学二、default生命周期核心阶段详解(高频使用)三、clean生命周期核心阶

GO语言中函数命名返回值的使用

《GO语言中函数命名返回值的使用》在Go语言中,函数可以为其返回值指定名称,这被称为命名返回值或命名返回参数,这种特性可以使代码更清晰,特别是在返回多个值时,感兴趣的可以了解一下... 目录基本语法函数命名返回特点代码示例命名特点基本语法func functionName(parameters) (nam

Python Counter 函数使用案例

《PythonCounter函数使用案例》Counter是collections模块中的一个类,专门用于对可迭代对象中的元素进行计数,接下来通过本文给大家介绍PythonCounter函数使用案例... 目录一、Counter函数概述二、基本使用案例(一)列表元素计数(二)字符串字符计数(三)元组计数三、C

深度剖析SpringBoot日志性能提升的原因与解决

《深度剖析SpringBoot日志性能提升的原因与解决》日志记录本该是辅助工具,却为何成了性能瓶颈,SpringBoot如何用代码彻底破解日志导致的高延迟问题,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言第一章:日志性能陷阱的底层原理1.1 日志级别的“双刃剑”效应1.2 同步日志的“吞吐量杀手”

Unity新手入门学习殿堂级知识详细讲解(图文)

《Unity新手入门学习殿堂级知识详细讲解(图文)》Unity是一款跨平台游戏引擎,支持2D/3D及VR/AR开发,核心功能模块包括图形、音频、物理等,通过可视化编辑器与脚本扩展实现开发,项目结构含A... 目录入门概述什么是 UnityUnity引擎基础认知编辑器核心操作Unity 编辑器项目模式分类工程

Python中的filter() 函数的工作原理及应用技巧

《Python中的filter()函数的工作原理及应用技巧》Python的filter()函数用于筛选序列元素,返回迭代器,适合函数式编程,相比列表推导式,内存更优,尤其适用于大数据集,结合lamb... 目录前言一、基本概念基本语法二、使用方式1. 使用 lambda 函数2. 使用普通函数3. 使用 N