分类任务 | 输出层激活函数 | 损失函数 |
---|---|---|
二分类 | s i g m o i d ( ) sigmoid() sigmoid() | b i n a r y _ c r o s s e n t r o p y ( ) binary\_crossentropy() binary_crossentropy() |
多分类 | s o f t m a x ( ) softmax() softmax() | c a t e g o r i c a l _ c r o s s e n t r o p y categorical\_crossentropy categorical_crossentropy |
多标签二分类 | s i g m o i d ( ) sigmoid() sigmoid() | b i n a r y _ c r o s s e n t r o p y ( ) binary\_crossentropy() binary_crossentropy() |
BCELoss
import torch
output = torch.full([10, 64], 1.5) # (batch_size, nums_classes)
target = torch.ones([10, 64], dtype=torch.float32)
criterion = torch.nn.BCELoss()(
torch.nn.Sigmoid()(output),
target
)
print(criterion) # tensor(0.2014)
criterion = torch.nn.BCEWithLogitsLoss()(output, target)
print(criterion) # tensor(0.2014)
$$
假设x为模型预测输出,y为样本标签(第n个样本的模型预测输出x_n与样本标签y_n) \
l(x,y) = L = { l_1, \dots, l_N }^T,\
\begin{equation}
l(x,y)=
\begin{cases}
L& \text{if reduction is None}\
mean(L)& \text{if reduction is ‘mean’}\
sum(L)& \text{if reduction is ‘sum’}
\end{cases}
\end{equation} \
l_n = -w_n[y_n \cdot log\sigma(x_n) + (1-y_n)log\sigma(1-x_n)]为第n个样本的loss \
\
l_{n,c} = -w_{n,c}[p_cy_{n,c} \cdot log\sigma(x_{n,c}) + (1-y_{n,c}\cdot log(1-\sigma(x_{n,c})))] \
$$
c c c: class number (c=1 for single-label binary classification, c>1 for multi-label binary classification.)
n n n: the number of sample in the batch
p c p_c pc: the weight of the positive answer for the class c c c ( p c > 1 p_c>1 pc>1 increase the recall, p c < 1 p_c<1 pc<1 increase the precision.)
样本不均衡问题
多标签间的样本不均衡问题 weight
weight = torch.ones(64)
weight[1] *= 3 # 假设标签1的样本数量同其他标签的样本数量差三倍
criterion = torch.nn.BCEWithLogitsLoss(weight=weight)(output, target)
print(criterion) # tensor(0.2077)
每类标签的正负样本不均衡问题 pos_weight
当数据集中某个单类的正负样本比例为
n
u
m
s
_
p
o
s
i
t
i
v
e
n
u
m
s
_
n
e
g
a
t
i
v
e
=
1
R
\frac{nums\_positive}{nums\_negative}=\frac{1}{R}
nums_negativenums_positive=R1,并将pos_weight
向量中对应该类的值设为
R
R
R时,表示在计算loss时该类的
n
u
m
s
_
p
o
s
i
t
i
v
e
=
n
u
m
s
_
p
o
s
i
t
i
v
e
×
R
nums\_positive = nums\_positive \times R
nums_positive=nums_positive×R。
pos_weight = torch.ones(64) * 2 # 假设所有标签的正样本是其负样本数量的1/2
criterion = torch.nn.BCEWithLogitsLoss(pos_weight=pos_weight)(output, target)
print(criterion) # tensor(0.4028)
PyTorch自动混合精度(AMP)
PyTorch1.6起内置
torch.cuda.amp
启用自动混合精度训练,无需加载第三方NVIDIA的apex库(仅启用在支持tensor core的CUDA硬件)
- 自动:Tensor的dtype自动变换
- 混合精度:一个Tensor的dtype可能同时包含
torch.FloatTensor, torch.HalfTensor
FP16, FP32
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PHX2SW83-1663843108368)(C:\Users\ww0054688\AppData\Roaming\Typora\typora-user-images\image-20220922171730143.png)]
FP16的优劣:
-
优势
- 减少显存占用
- 加快训练和推断的计算(提速一倍)
- 张量核心(NVIDIA tensor core)的普及(低精度计算将是未来趋势)
-
劣势
-
溢出错误:FP16的动态范围<<FP32,在计算过程中易出现上溢出、下溢出后导致
NaN
。由于激活函数的梯度往往比权重梯度小,故更易出现下溢出。[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MYczJXgR-1663843108370)(C:\Users\ww0054688\AppData\Roaming\Typora\typora-user-images\image-20220922173141258.png)]
-
舍入误差:当梯度小于FP16表示的最小固定间隔时,该次梯度更新可能失败。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lcziv9eJ-1663843108370)(C:\Users\ww0054688\AppData\Roaming\Typora\typora-user-images\image-20220922173351860.png)]
-
改进
-
混合精度训练:在内存中仅用FP16做储存、乘法,用FP32做累加以避免舍入误差。
在pytorch1.6的amp上下文中,以下操作中Tensor会自动转为Fp16:
__matmul__
addbmm addmm addmv addr baddbmm bmm chain_matmul conv1d conv2d conv3d conv_transpose1d conv_transpose2d conv_transpose3d linear matmul mm mv prelu
-
损失放大:由于激活梯度的值太小导致的下溢出,通过放大loss的值防止梯度underflow(只在BP传递梯度信息时发生,真正更新权重时要将放大的梯度再unscale回去)
BP前:放大损失值 2 k 2^k 2k倍,此时反向传播中的激活函数梯度信息为放大过后的中间变量,防止下溢出
BP后:将权重梯度缩小 2 k 2^k 2k倍,恢复正常值。
动态损失放大:为了充分利用FP16的范围,缓解舍入误差,尽量使用最高放大倍数 2 24 2^{24} 224。此时若产生上溢出,则跳出参数更新、缩小放大倍数使其不溢出,并在一定步数后再次尝试大scale以充分利用Fp16的范围。
用例
NVIDIA apex.amp
from apex import amp
... # define model, criterion, optimizer
model, optimizer = amp.initial(model, optimizer, opt_level='O1')
...
with amp.scale_loss(criterion, optimizer) as scaled_loss:
scaled_loss.backward()
# the above statement replace 'loss.backward()'
opt_level
'O0'
:纯FP32训练,可作为accuracy的baseline'O1'
:amp训练,根据黑白名单自动决定使用FP16还是FP32计算'O2'
:几乎FP16训练,不存在黑白明白,除了bacthnorm几乎都是FP16计算'O3'
:纯FP16计算,不稳定,可作为speed的baseline
PyTorch1.6+
autocast
from torch.cuda.amp import autocast
... # define model, criterion, optimizer, data
for input, target in data:
optimizer.zero_grad()
with autocast(): # autocast的context managers语义
output = model(input)
loss = criterion(output, target)
loss.backward()
optimizer.step()
from torch.cuda.amp import autocast
@autocast() # autocast的decorators语义
def train(data, model):
for input, target in data:
optimizer.zero_grad()
output = model(input)
loss = criterion(output, target)
loss.backward()
optimizer.step()
autocast的context managers语义,只能包含网络的前向过程(包括loss的计算),不能包含反向传播???
Gradscaler
from torch.cuda.amp import GradScaler, autocast
... # define model, criterion, optimizer, data
scaler = GradScaler()
for input, target in data:
optimizer.step()
with autocast:
output = model(input)
loss = criterion(output, target)
scaler.scale(loss).backward() #
scaler.step(optimizer) #
scaler.update()
ref:Pytorch自动混合精度(AMP)介绍与使用 - jimchen1218 - 博客园 (cnblogs.com)