pytorch训练模型注意事项

1. 用torch tensor在GPU上运算来生成数据集,加速数据生成

  • 如果数据集需要在线生成(即在dataloader 里面 计算生成 feature 和label), 如果数据量比较大,并且涉及到矩阵运算,可以用torch tensor来计算。把矩阵放到GPU上计算的快。
  • 如果构造dataset 时 是通过cuda tensor计算得到的,那么在创造dataloader 通过多线程加载数据时,请注意加上 import multiprocessing as mp,mp.set_start_method(‘spawn’) 不然多线程加载会出现问题。
# 当用cuda 生成feature时,又用dataloader 多线程加载数据集时,需要添加 下面函数。否则pytorch 会出错。
import multiprocessing as mp
mp.set_start_method('spawn') 

2. 请注意检查输入数据集是否有脏数据比如nan,这个很容易导致你训练出现nan.

  • 如果数据集是 numpy 格式,可以通过 np.any(np.isnan(x)) 来判断是否有 nan,即:

    assert not np.any(np.isnan(x)),'nan exists!'
    
  • 如果是数据集是torch.tensor格式,可以通过torch.any(torch.isnan(x)) 来判断是否有nan

    assert not torch.any(torch.isnan(x)),'nan exists!'
    

3. 训练loss 出现nan 有可能的原因

1) 学习率太高,
2) loss函数不合适,
3) 数据本身,是否存在Nan
4) target本身应该是能够被loss函数计算的

注意几点:

数据集本身计算中产生nan
  • torch.acos(x)时,如果x的值接近-1或者1则会出现nan的现象,此处建议对x进行范围限制,比如 torch.clip(x, -1+1e-6, 1-1e-6)
  • 计算中的除法,一定要注意 分母是否为0的情况. 分母为0,也会产生nan的现象,可以通过加极小数来解决,如 1/(x+1e-6)
  • 如果计算中有涉及到求逆运算,torch.inv(x) 请确保矩阵可逆(满秩),如果担心x不是满秩 可以加一个对角阵 x+torch.eye(x.shape[-1])
  • 如果遇到先求acos, 然后求倒数,再求逆,可以对x 进行如下操作: x = torch.clip(x, -1+1e-6, 1-1e-6) , torch.acos(x)
x = torch.clip(x, -1+1e-6, 1-1e-6)
y = torch.acos(x)
z = 1/(y+1e-6)
w = torch.inv(z)
target 本身输入到loss函数时, 在计算loss过程中 产生nan,也就是target本身不能被loss函数计算
  • 比如手动写 loss函数,比如交叉熵损失,涉及到 -torch.log(x), 此处要保证x不能为0,所以可以加上1e-6,如 -torch.log(x+1e-6)
  • 比如sigmoid激活函数的target应该大于0

4. 当计算交叉熵时,如果你的pred结果 有mask,可以通过下面进行交叉熵计算


"""
此处只限于pytorch 的使用
默认mask 里面 是 1:有效值得位置,0:无效值的地方
- check_mask_valid(mask): 检查mask里面是否全为0,如果全为0,则整个预测结果也无效,没必要计算loss
- maskNLLLoss(pred, target, mask): 带mask的交叉熵损失,如果mask 全为0,则返回带梯度的tensor(0., requres_grad=True)
- calAcc(pred, target, mask): 带mask的ACC,如果mask全为0, 则返回1。(这里不需要梯度)
"""
def check_mask_valid(mask):
	# 检查 mask是否全为0, 如果全为0, 则说明整个target 都是无效的
	if torch.sum(mask.float()).item() == 0:
		return False
	else:
		return True
		
def maskNLLLoss(pred, target, mask):
	# 返回的loss 必须是一个带梯度的tensor,因为train函数里面要做loss.backward()
	if not check_mask_valid(mask):
		return torch.tensor(0., requres_grad=True)# 如果mask全为0,则值无效,loss 返回为0,这里最好返回一个tensor并且带有grad,不然backward时 会出问题
	target1hot = torch.nn.functional.one_hot(target.long(), pred.shape[1]).permuate(0,3,1,2).float()# B*C*H*W
	crossEntropy = -torch.log((target1hot * pred).sum(dim=1) + 1e-6) * mask.float()# B*C*H*W
	loss = crossEntropy.sum()/ mask.float().sum()
	return loss

def calAcc(pred, target, mask):
    # 返回的acc 可以是一个tensor 也可以是一个数值,因为acc不需要backward(),只是记录值
	if not check_mask_valid(mask):
		return 1
	pred_ = torch.argmax(pred, dim=1)
	equal = torch.eq(pred_.float(), target.float()).float()
	masked_equal = equal * mask.float()
	acc = torch.sum(masked_equal) / torch.sum(mask.float())
	return acc.item()

5. 数据不均衡时,可以用focal loss 或者 对loss 进行权重调整

Focal loss

6. 梯度累积时,记得要除以累积次数 ,不然梯度会太大导致训练异常

"""
设置梯度累积参数acc_freq,在每次得到loss后,都要做一次 loss/acc_freq,不然一次性optimize.step()时会因为梯度太大而导致训练异常
if acc_freq > 0:# 梯度累积
	loss = loss/acc_freq # 除以累积次数  
"""
import torch
import torch.nn as nn
from torch.utils.data import DataLoader, SubsetRandomSampler
import torch.optim as optim

val_split = 0.2
acc_freq = 4

# 数据集准备阶段
dataset = MyDataset()
dataset_size = len(dataset)
indices = list(range(dataset_size))
split = int(np.floor(val_split * dataset_size)) 
if shuffle_dataset:
    np.random.seed(random_seed)
    np.random.shuffle(indices)
train_indices, val_indices = indices[split:], indices[:split]
train_sampler = SubsetRandomSampler(train_indices)
valid_sampler = SubsetRandomSampler(val_indices)
train_loader = DataLoader(dataset, batch_size=4, sampler=train_sampler, num_workers=2) 

# 模型准备阶段
lr = 1e-4
weight_decay = 0.2
Model = Model()
optimizer = torch.optim.Adam(model.parameters(), weight_decay=weight_decay, lr=lr)

# 训练阶段
def train():
	for epoch in range(total_epoch):
		for batch, (features, labels, mask) in enmuerate(train_loader):
		    pred = Model(features) # 预测
		    loss = loss_fn(pred, labels, mask) # 计算loss
		    acc = cal_acc(pred, labels, mask) # 计算acc
		    if acc_freq > 0:# 梯度累积
		    	loss = loss/acc_freq # 除以累积次数  
		    loss.backward()# 计算梯度
		    if (batch+1) % acc_freq == 0:
		    	optimizer.step() # 反向传播
		    	optimizer.zero_grad() # 清空梯度  非常重要(只要做optimizer.step(),就要接着zero_grad())
		    # update loss and acc
		    # save model  	

学习pytorch

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值