参考文档链接:损失函数
参考文档链接:【英文】优化器的使用
参考文档链接:【中文】优化器的使用
损失函数的基本用法
criterion = LossCriterion() # 构造函数有自己的参数
loss = criterion(x,y) # 调用标准时也有参数
!!! 得到的loss结果已经对mini-batch进行取平均操作
使用
CLASS torch.nn.BCELoss(weight=None, size_average=None, reduce=None, reduction='mean')
'''
# BCELoss二分类
# 创建一个衡量目标和输出之间二进制交叉熵的criterion
参数:
weight(Tensor,可选) 每批元素损失的手工重标权重。如果给定,必须是一个大小为nbatch大小的张量
size_average(bool,可选)
弃用(见reduction参数)。默认情况下,设置为True,即对批处理中的每个损失元素进行平均。注意,对于某些损失,每个样本有多个元素。如果字段size_average设置为False,则对每个小批的损失求和。当reduce为False时,该参数被忽略。默认值:True
reduce(bool,可选)
弃用(见reduction参数)。默认情况下,设置为True,即根据size_average参数的值决定对每个小批的观察值是进行平均或求和。如果reduce为False,则返回每个批处理元素的损失,不进行平均和求和操作,即忽略size_average参数。默认值:True
reduction(string,可选)
指定要应用于输出的reduction,操作'none|mean|sum'。 默认值mean
'none':表示不进行任何reduction操作
'mean':求平均值,输出的和 除以输出中的元素数
'sum':输出求和
!!!指定任何size_average和reduce参数都将使reduction参数失效
形状:
输入:(N,*)
目标: (N,*) 与输入相同维度
输出:标量scalar。如果reduce为false,不做任何处理则为(N,*)
'''
其中N代表batch size,xn为输出,yn为目标。
当reduction不同设置时,如mean和sum,返回值的变化:
进行二分类问题时 即激活函数采用sigmoid时,常使用交叉熵作为损失函数。能够解决因sigmoid函数导致的梯度消失问题
如果使用MSELoss作为损失函数,会产生梯度消失问题。
import torch.nn as nn
import torch
m = nn.Sigmoid()
loss = nn.BCELoss()
input = torch.randn(3,requires_grad=True)
print("input: ",input)
target = torch.empty(3).random_(2) # torch.empty()创建一个使用未初始化值填满的tensor
print("target: ",target)
output = loss(m(input),target)
print("m(input): ",m(input))
print("output: ",output)
output.backward()
print("output after backward: ",output)
结果:
input: tensor([-0.7129, 1.3618, 0.6582], requires_grad=True)
target: tensor([1., 0., 1.])
m(input): tensor([0.3289, 0.7961, 0.6588], grad_fn=<SigmoidBackward0>) 通过激活函数sigmoid后输出的值
output: tensor(1.0397, grad_fn=<BinaryCrossEntropyBackward0>)
output的计算 = (-1)[1*math.log(0.3289)+ 0*math.log(1-0.3289)
0*math.log(0.7961)+1*math.log(1-0.7961)+
1*math.log(0.6588)+0*math.log(1-0.6588)]/3 = 1.0397
CLASS torch.nn.BCEWithLogitsLoss(weight=None, size_average=None, reduce=None, reduction='mean', pos_weight=None)
上一个损失函数的例子等价于
loss = nn.BCEWithLogicLoss()
input = nn.randn(3,requires_grad=True)
target = torch.empty(3).random_(2)
output = loss(input,target)
'''
区别 无需自定义m函数对input进行操作(nn.sigmoid())
'''
使用:
CLASS torch.nn.NLLLoss(weight=None, size_average=None, ignore_index=-100, reduce=None, reduction='mean')
通过转发调用给出的输入 (即nn.LogSoftmax()后的输出) 应该包含每个类的log-probability。输入要么是大小为(minibatch,C)或大小(minibatch,C,d1,d2,…,dK)的Tensor,k>=1表示k维的输入
通过在网络的最后一层添加LogSoftmax层,可以很容易地获得神经网络中的log-probability。如果不喜欢添加额外的层,可以使用CrossEntropyLoss损失函数来替代。
损失预期的目标应该是[0,c - 1]范围内的类索引,其中C =类的数量;如果指定ignore_index参数,该损失函数也接受这个类索引(这个索引不一定在类范围内)。
# 多分类问题 低维举例
m = nn.LogSoftmax(dim=1)
loss = nn.NLLLoss()
# input is of size N*c = 3*5
input = torch.randn(3,5,requires_grad=True)
print("input : ",input)
print("m(input): ",m(input)) # 对input先进行softmax计算(转化成0-1之间的数值) 再取log 得到的都是负数
target = torch.tensor([1,0,4])
print("target: ",target)
output = loss(m(input),target)
print("output: ",output)
结果:
input : tensor([[ 0.1047, -0.5505, -0.3458, -0.1031, -0.7769],
[-0.3304, -0.7051, 0.5704, 1.2910, -0.1523],
[ 0.5005, 1.4218, 0.9723, 0.4399, -2.0973]], requires_grad=True)
m(input): tensor([[-1.2188, -1.8741, -1.6693, -1.4266, -2.1004],
[-2.3422, -2.7169, -1.4414, -0.7208, -2.1641],
[-1.8133, -0.8921, -1.3416, -1.8739, -4.4112]],
grad_fn=<LogSoftmaxBackward0>)
target: tensor([1, 0, 4])
output: tensor(2.8758, grad_fn=<NllLossBackward0>)
# 多分类问题 高维举例
N,C = 5,4
loss = nn.NLLLoss()
data = torch.randn(N,16,10,10)
print("data: ",data)
conv = nn.Conv2d(16,C,(3,3)) # 输出为5*8*8
print("conv: ",conv)
m = nn.LogSoftmax(dim=1)
# each element in target has to in 0<=value<=C
target = torch.empty(N,8,8,dtype=torch.long).random_(0,C)
print("target:",target)
print("conv(data):",conv(data))
print("m(conv(data)):",m(conv(data)))
print("data shape : ",data.shape)
print("target shape",target.shape)
print("conv(data).shape:",conv(data).shape)
print("m(conv(data)).shape:",m(conv(data)).shape)
output = loss(m(conv(data)),target)
print("output : ",output)
print("output shape : ",output.shape)
结果:
conv: Conv2d(16, 4, kernel_size=(3, 3), stride=(1, 1))
data shape : torch.Size([5, 16, 10, 10])
conv(data).shape: torch.Size([5, 4, 8, 8])
m(conv(data)).shape: torch.Size([5, 4, 8, 8])
使用:
loss = nn.CrossEntropyLoss()
当用C类训练分类问题时,它是有用的。如果提供了,可选的参数weight权重应该是一个一维张量,为每个类分配权重。当你有一个不平衡的训练集时,这是特别有用的。
每个类的输入应该包含原始的、未标准化的分数。
输入应该是大小为(minibatch,C)或大小(minibatch,C,d1,d2,…,dK)的Tensor,k>=1表示k维的输入
该criterion期望在[0,c - 1]范围内的一个类指标作为小batch大小的一维张量的每个值的目标值;如果指定ignore_index,该criterion也接受这个类索引值(这个索引不一定在类范围内)。
上面先使用nn.LogSoftmax(dim=1),再使用nn.NLLLoss()的等价于下面这段代码
!!注意事项:nn.CrossEntropyLoss() 是一个类,应该先行进行实例化。否则会报错“RuntimeError: Boolean value of Tensor with more than one value is ambiguous”
loss = nn.CrossEntropyLoss()
input = torch.randn(3,5,requires_grad=True)
print("input:",input)
target = torch.empty(3,dtype=torch.long).random_(5)
print("target:",target)
output = loss(input,target)
print("output:",output)
使用:计算input x和target y中每个元素的平均绝对误差MAE
CLASS torch.nn.L1Loss(size_average=None, reduce=None, reduction='mean')
reduction=none
reduction = mean/sum
loss = nn.L1Loss()
input = torch.randn(1,2,requires_grad=True)
print("input:",input)
target = torch.randn(1,2)
print("target:",target)
output = loss(input,target)
print("output:",output)
结果:
input: tensor([[-0.3543, -0.8127]], requires_grad=True)
target: tensor([[-1.0530, -1.4032]])
output: tensor(0.6446, grad_fn=<L1LossBackward0>)
output = (abs(-0.3543+1.0530) + abs(-0.8127+1.4032))/2
使用:计算input x和target y中每个元素的均方误差MAE
CLASS torch.nn.MSELoss()
loss = nn.MSELoss()
reduction = none
reduction = mean/sum
loss = nn.MSELoss()
input = torch.randn(1,2,requires_grad=True)
print("input: ",input)
target = torch.rand(1,2)
print("target: ",target)
output = loss(input,target)
print("output: ",output)
结果:
input: tensor([[-0.5910, 0.0345]], requires_grad=True)
target: tensor([[0.9664, 0.4840]])
output: tensor(1.3138, grad_fn=<MseLossBackward0>)
output = (pow((-0.5910-0.9664),2) + pow((0.0345-0.4840),2))/2 = 1.313772505
使用:对异常值敏感度较低
CLASS torch.nn.SmoothL1Loss(size_average=None, reduce=None, reduction='mean')
loss = nn.SmoothL1Loss()
input = torch.randn(3,2,requires_grad=True)
print("input: ",input)
target = torch.rand(3,2)
print("target: ",target)
output = loss(input,target)
print("output: ",output)
结果:
input: tensor([[ 1.2615, -0.8428],
[-1.0714, -0.4162],
[-0.4159, -1.0941]], requires_grad=True)
target: tensor([[0.0581, 0.2579],
[0.1083, 0.9786],
[0.5719, 0.9244]])
output: tensor(0.8142, grad_fn=<SmoothL1LossBackward0>)
output计算验证函数
l = input
t = target
>>> s = 0
>>> for i in range(len(l)):
... for j in range(len(l[0])):
... if abs(l[i][j] - t[i][j]) < 1:
... s+=0.5*pow((l[i][j] - t[i][j]),2)
... else:
... s += abs(l[i][j] - t[i][j]) - 0.5
... print(abs(l[i][j] - t[i][j]))
>>> output = s/6
>>> output
> 0.8141624033333335
损失函数 | 名称 | 适用场景 |
---|---|---|
torch.nn.BCELoss() | 二分类交叉损失函数 | 二分类问题(激活函数sigmoid) |
torch.nn.BCEWithLogitsLoss() | 多分类交叉损失函数 | 多分类问题(sigmoid和BCELoss结合) |
torch.nn.NLLLoss() | 多分类的负对数似然损失函数 | 多分类问题 |
torch.nn.CrossEntropyLoss() | 交叉熵损失函数 | 多分类问题 |
torch.nn.L1Loss() | 平均绝对误差 L1 norm | 回归 |
torch.nn.MSELoss() | 均方误差L2 norm | 回归 |
torch.nn.SmoothL1Loss() | 平滑的L1损失 | 回归 |
1、loss.backward()使用前不要忘记设置loss保存梯度
2、在使用CrossEntropyLoss()交叉熵损失函数中遇到的问题
'''
关于使用torch.nn.CrossEntropyLoss()函数出现IndexError的问题详细了解
'''
# 正确的示例
import torch.nn as nn
import torch
func = nn.CrossEntropyLoss()
a = torch.Tensor([[ 0.0606,0.1610,0.2990,0.2101, 0.5104],
[0.6388,0.4053, 0.4196, 0.7060, 0.2793],
[ 0.3973,0.6114, 0.1127, 0.7732, 0.0592]])
b = torch.Tensor([3,1,0])
loss = func(a,b.long())
print(loss)
结果:
loss = tensor(1.6690)
a.shape torch.Size([3, 5])
错误代码
a1 = torch.Tensor([ 0.0606,0.1610,0.2990,0.2101, 0.5104])
a2 = torch.Tensor([0.6388,0.4053, 0.4196, 0.7060, 0.2793])
a3 = torch.Tensor([ 0.3973,0.6114, 0.1127, 0.7732, 0.0592])
b1 = torch.Tensor([3])
b2 = torch.Tensor([1])
b3 = torch.Tensor([0])
loss_1 = func(a1,b1.long())
print(loss_1)
loss_2 = func(a2,b2.long())
print(loss_2)
loss_3 = func(a3,b3.long())
print(loss_3)
报错了!!
检查一下a1.shape torch.Size([5]) 维度不对了 因此需要升维
a1.unsqueeze(0) tensor([[0.0606, 0.1610, 0.2990, 0.2101, 0.5104]])
a1.unsqueeze(0).shape torch.Size([1, 5])
正确代码
a1 = torch.Tensor([ 0.0606,0.1610,0.2990,0.2101, 0.5104])
a2 = torch.Tensor([0.6388,0.4053, 0.4196, 0.7060, 0.2793])
a3 = torch.Tensor([ 0.3973,0.6114, 0.1127, 0.7732, 0.0592])
b1 = torch.Tensor([3])
b2 = torch.Tensor([1])
b3 = torch.Tensor([0])
loss_1 = func(a1.unsqueeze(0),b1.long())
print(loss_1)
loss_2 = func(a2.unsqueeze(0),b2.long())
print(loss_2)
loss_3 = func(a3.unsqueeze(0),b3.long())
print(loss_3)
结果:
tensor(1.6595)
tensor(1.7065)
tensor(1.6410)
参考博文1:
参考博文2:
torch.optim包是一个实现了各种优化算法的库。构建一个optimizer对象
常用优化器SGD/Adam/RMSprop
import torch.optim as optim
optimizer = optim.SGD(model.parameters(),lr=0.01,momentum=0.9)
optimizer = optim.Adam([var1,var2],lr=0.00001)
class torch.optim.SGD(params, lr=, momentum=0, dampening=0, weight_decay=0, nesterov=False)
注意事项:
pytroch中使用SGD十分需要注意的是,更新公式与其他框架略有不同!
optimizer = optim.SGD(model.parameters(),lr=0.01,momentum=0.9)
optimizer.zero_grad()
loss_fn(model(input),target).backward()
optimizer.step()
可以理解为往标准动量中添加了一个校正因子
**思想:**梯度震动较大的项,在下降时,减小其下降速度;对于震动幅度小的项,在下降时,加速其下降速度
对于RNN有很好的效果
torch.optim.RMSprop(params, lr=0.01, alpha=0.99, eps=1e-08, weight_decay=0, momentum=0, centered=False)
torch.optim.Adam(params, lr=0.001, betas=(0.9, 0.999), eps=1e-08, weight_decay=0)
设置不同的优化器和学习率,重复任务2的回归过程
1. 损失函数MSE、优化器SGD、学习率0.1
2. 损失函数MSE、优化器SGD、学习率0.5
3. 损失函数MSE、优化器SGD、学习率0.01
import torch
import torch.nn as nn
import numpy as np
class Net(nn.Module):
def __init__(self):
super(Net,self).__init__()
self.w = torch.nn.Parameter(torch.tensor([7.]))
self.b = torch.nn.Parameter(torch.tensor([6.]))
def forward(self,x):
return self.w * x+self.b
net = Net()
optimizer = torch.optim.SGD(net.parameters(),lr=0.1)
loss_func = torch.nn.MSELoss()
for step in range(1000):
output = net(x)
loss = loss_func(output,y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if step%10 == 0:
print(loss)
net = Net()
optimizer = torch.optim.SGD(net.parameters(),lr=0.5)
loss_func = torch.nn.MSELoss()
for step in range(1000):
output = net(x)
loss = loss_func(output,y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if step%10 == 0:
print(loss)
net = Net()
optimizer = torch.optim.SGD(net.parameters(),lr=0.01)
loss_func = torch.nn.MSELoss()
for step in range(1000):
output = net(x)
loss = loss_func(output,y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
if step%10 == 0:
print(loss)