我在询问关于NLLLoss损失函数的C类。
文档中说明:
负对数似然损失。它对于训练具有C个类别的分类问题很有用。
基本上,之后的所有内容都取决于你是否知道什么是C类,我以为我知道什么是C类,但文档对我来说不太清楚。特别是当它描述期望的输入为(N, C) 其中C = 类别数
时。我感到困惑,因为我以为C类仅指输出。我的理解是C类是一个分类的一热向量。我经常在教程中发现NLLLoss
通常与LogSoftmax
配对来解决分类问题。
我期望在以下示例中使用NLLLoss
:
# 一些随机训练数据input = torch.randn(5, requires_grad=True)print(input) # tensor([-1.3533, -1.3074, -1.7906, 0.3113, 0.7982], requires_grad=True)# 构建我的神经网络(这里只是一个LogSoftmax)m = nn.LogSoftmax(dim=0)# 使用数据训练我的神经网络output = m(input)print(output) # tensor([-2.8079, -2.7619, -3.2451, -1.1432, -0.6564], grad_fn=<LogSoftmaxBackward>)loss = nn.NLLLoss()print(loss(output, torch.tensor([1, 0, 0])))
上述代码在最后一行引发以下错误:
ValueError: 期望2个或更多维度(得到1)
我们可以忽略这个错误,因为显然我不明白我在做什么。在这里我将解释上述源代码的意图。
input = torch.randn(5, requires_grad=True)
随机一维数组与一热向量[1, 0, 0]
配对用于训练。我试图将二进制位转换为一热向量的十进制数。
m = nn.LogSoftmax(dim=0)
LogSoftmax
的文档说明输出将与输入的形状相同,但我只看到LogSoftmax(dim=1)
的示例,因此我一直试图让它工作,因为我找不到相关的示例。
print(loss(output, torch.tensor([1, 0, 0])))
所以现在我有神经网络的输出,我想知道我的分类[1, 0, 0]
的损失。在这个例子中,数据的任何内容都不重要。我只想要一个代表分类的热向量的损失。
此时,我在试图解决损失函数相关的预期输出和输入结构错误时遇到了困难。我尝试在输出和输入上使用view(...)
来修正形状,但那只会导致其他错误。
所以这回到了我的原始问题,我将展示文档中的示例来解释我的困惑:
m = nn.LogSoftmax(dim=1)loss = nn.NLLLoss()input = torch.randn(3, 5, requires_grad=True)train = torch.tensor([1, 0, 4])print('input', input) # input tensor([[...],[...],[...]], requires_grad=True)output = m(input)print('train', output, train) # tensor([[...],[...],[...]],grad_fn=<LogSoftmaxBackward>) tensor([1, 0, 4])x = loss(output, train)
同样,我们在LogSoftmax
上使用dim=1
,这让我现在感到困惑,因为看看input
数据。它是一个3x5
的张量,我迷失了。
这是NLLLoss
函数的第一个输入的文档:
输入:(N, C)(N,C) 其中C = 类别数
输入是按类别数分组的吗?
所以张量输入的每一行都与训练张量的每一元素相关联吗?
如果我更改输入张量的第二维度,那么什么都不会坏,我不知道发生了什么。
input = torch.randn(3, 100, requires_grad=True)# 3 x 100 仍然有效?
所以我不知道这里的C类是什么,我以为C类是分类(如标签)且仅在神经网络的输出上有意义。
我希望你能理解我的困惑,因为神经网络输入的形状不应该是独立于用于分类的热向量的形状吗?
代码示例和文档都说输入的形状由分类数定义,我不太明白为什么会这样。
我尝试研究文档和教程来理解我错过了什么,但在几天无法突破这个点后,我决定提出这个问题。这让我感到谦卑,因为我以为这是最容易学的东西之一。
回答:
基本上你缺少batch
的概念。
长话短说,每个输入到损失函数(以及通过网络传递的输入)都需要batch
维度(即使用了多少样本)。
一步一步分解:
你的示例与文档对比
每一步都将进行比较以使其更清晰(文档在上,你的示例在下)
输入
input = torch.randn(3, 5, requires_grad=True)input = torch.randn(5, requires_grad=True)
在第一种情况(文档),创建了具有5
个特征的输入,并使用了3
个样本。在你的情况下只有batch
维度(5
个样本),你没有特征这是必需的。如果你打算有一个具有5
个特征的样本,你应该这样做:
input = torch.randn(1, 5, requires_grad=True)
LogSoftmax
LogSoftmax
是在特征维度上进行的,你是在批次上进行的。
m = nn.LogSoftmax(dim=1) # 在特征上应用m = nn.LogSoftmax(dim=0) # 在批次上应用
这种操作通常对样本没有意义,因为样本是相互独立的。
目标
由于这是多类分类,每个向量中的元素代表一个样本,可以传递任意数量的数字(只要它小于特征数,在文档示例中是5
,所以[0-4]
是可以的)。
train = torch.tensor([1, 0, 4])train = torch.tensor([1, 0, 0])
我假设你也想传递一热向量作为目标。PyTorch不是这样工作的,因为它内存效率低(为什么要将一切存储为一热编码,当你可以只精确定位类别时,在你的情况下它将是0
)。
只有神经网络的输出是一热编码的,以便通过所有输出节点反向传播错误,目标不需要这样做。
最终
你不应该在这个任务中使用torch.nn.LogSoftmax
完全。只需使用torch.nn.Linear
作为最后一层,并使用torch.nn.CrossEntropyLoss
与你的目标。