PyTorch使用的一些小技巧

Pytorch

训练桶状网络

使用nn.Sequential(nn.Conv2d(),nn.BatchNorm(),nn.ReLU(),...)网络就按照次序建立好了。

什么时候使用.to(device)

如果没有一个变量没有显示的复制到显存上,比如初始化的时候,我们就需要使用.to(device)将其复制到显存,但是有一种情况不需要复制到显存,就是如果数据是由原来在显存上的{程序|网络|张量}生成的那么默认这个生成的数据与原数据同在显存中,所以不用.to(device)

使用tensorboad显示训练训练过程中的损失

from tensorboardX import SummaryWriter
"""
参数是logs的地址
"""
writer = SummaryWriter(log_dir=os.path.join(ROOT_PATH,'logs'))
"""
writer.add_scalar的三个参数依次是,y/x轴的名字,y轴的数值,x轴的数值"
"""
writer.add_scalar('loss/iter',print_loss_avg,iter)
writer.close()#使用完毕要关掉

训练完成以后保存模型

第一个参数是模型,第二个参数是模型保存的地址

torch.save(encoder1, os.path.join(ROOT_PATH,'models','encoder1_translation_180920.pkl')
torch.save(attn_decoder1,os.path.join(ROOT_PATH,'models',"attn_decoder1_translation_180920.pkl")

torch.randn(a,b,c,d,device)的含义

batch_size * channels * weight * height,在device设备上随机生成一个这样size的四维张量。

lossD.item()

lossD这个一维tensor变成python中的数字,注意这个tensor中只能有一个数字。

为什么有的地方要写成(4,)的形式,而不写成(4)

例如torch.full((4,),1,device)表示将生成一个[4]维度的填充为1的tensor如果写成(4)那么就会报错,第一个参数应该是一个tuple而不是一个int

使用多卡训练

首先使用CUDA_VISIBLE_DEVICES指示哪些GPU可见,一般设置没有人用的GPU可见就行了。

CUDA_VISIBLE_DEVICES=1,2,3 python example.py

将网络放在不同的GPU上

netG=Generator().to(device)
if device.type=='cuda' and WORKER_NUM>0:
    netG=nn.DataParallel(netG,WORKER)
netG.apply(weight_init)
netD=Discriminator().to(device)
if device.type=='cuda' and WORKER_NUM>0:
    netD=nn.DataParallel(netD,WORKER)
netD.apply(weight_init)

将数据加载到不同的GPU上

def dataloader():
    ...
    dataloader = torch.utils.data.DataLoader(dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=WORKER_NUM)
    print(" %d images were found there!  "%len(dataloader))  #输出图片的数目
    return dataloader

将PyTorch生成的图片保存

fakeshapechannels * height * weight而cv2要写入的图片是height * weight * channels所以要进行一次转置,同时将tensor变为numpy类型,因为这个tensor的数据范围是[0,1],图片颜色的范围是[0,256],所以还要乘以256(要不是实习过知道点图像方面的知识,训出来的生成图片全是黑的,这个bug可能怎么都找不出来)。

fake = netG(noise).detach().cpu()[0]
print(fake.shape)
cv2.imwrite('%d.jpg'%iters, np.transpose(fake.numpy()*256,(1,2,0))

导入头文件

使用了from __future__ import xxx可以在python2,python3环境下运行同一份代码而不出错,编写的时候使用python3规范即可。

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals

保存模型

将所有模型参数保存成了一个压缩包。

if (iteration % save_every == 0):
            directory = os.path.join(save_dir, model_name, corpus_name, '{}-{}_{}'.format(encoder_n_layers, decoder_n_layers, hidden_size))
            if not os.path.exists(directory):
                os.makedirs(directory)
            torch.save({
                'iteration': iteration,
                'en': encoder.state_dict(),
                'de': decoder.state_dict(),
                'en_opt': encoder_optimizer.state_dict(),
                'de_opt': decoder_optimizer.state_dict(),
                'loss': loss,
                'voc_dict': voc.__dict__,
                'embedding': embedding.state_dict()
            }, os.path.join(directory, '{}_{}.tar'.format(iteration, 'checkpoint')))

state_dict是什么,怎么用?

可学习的变量,weightbiases,而且不仅模型有state_dict,optimizer也有,并且还包含一些超参数。state_dict实际上是一个字典,保存着模型对应层与tensor

(A state_dict is simply a Python dictionary object that maps each layer to its parameter tensor. Note that only layers with learnable parameters (convolutional layers, linear layers, etc.) have entries in the model’s state_dict.Optimizer objects (torch.optim) also have a state_dict, which contains information about the optimizer’s state, as well as the hyperparameters used.)

print("Model's state_dict:")
for param_tensor in model.state_dict():
    print(param_tensor, "\t", model.state_dict()[param_tensor].size())
Model's state_dict:
conv1.weight     torch.Size([6, 3, 5, 5])
conv1.bias   torch.Size([6])
conv2.weight     torch.Size([16, 6, 5, 5])
conv2.bias   torch.Size([16])
fc1.weight   torch.Size([120, 400])
fc1.bias     torch.Size([120])
fc2.weight   torch.Size([84, 120])
fc2.bias     torch.Size([84])
fc3.weight   torch.Size([10, 84])
fc3.bias     torch.Size([10])

# Print optimizer's state_dict
print("Optimizer's state_dict:")
for var_name in optimizer.state_dict():
    print(var_name, "\t", optimizer.state_dict()[var_name])
Optimizer's state_dict:
state    {}
param_groups     [{'lr': 0.001, 'momentum': 0.9, 'dampening': 0, 'weight_decay': 0, 'nesterov': False, 'params': [4675713712, 4675713784, 4675714000, 4675714072, 4675714216, 4675714288, 4675714432, 4675714504, 4675714648, 4675714720]}]

在加载模型以后使用model.eval()的原因

dropout层和BN层设置为评估模式,否则运行的结果会与之前的不一样。

Remember that you must call model.eval() to set dropout and batch normalization layers to evaluation mode before running inference. Failing to do this will yield inconsistent inference results.

# Model class must be defined somewhere
model = torch.load(PATH)
model.eval()

保存模型后缀

A common PyTorch convention is to save models using either a .pt or .pth file extension.

example

存储成checkpoint

torch.save({
            'epoch': epoch,
            'model_state_dict': model.state_dict(),
            'optimizer_state_dict': optimizer.state_dict(),
            'loss': loss,
            ...
            }, PATH)

加载整个checkpoint使用torch.load(),给模型优化器或者其他加载具体的值时使用torch.load_state_dict()

model = TheModelClass(*args, **kwargs)
optimizer = TheOptimizerClass(*args, **kwargs)

checkpoint = torch.load(PATH)
model.load_state_dict(checkpoint['model_state_dict'])
optimizer.load_state_dict(checkpoint['optimizer_state_dict'])
epoch = checkpoint['epoch']
loss = checkpoint['loss']

model.eval()
# - or -
model.train()

获取Linear层的参数

使用Linear.weightLinear.bias就可以获得参数了。

self.linear1.weight = torch.nn.Parameter(torch.zeros(in_dim,hid))
self.linear1.bias = torch.nn.Parameter(torch.ones(hid))

在训练过程中使得学习率进行衰减

设置好exp_lr_sheduler之后只用在训练的每一步打开即可。

Sets the learning rate of each parameter group to the initial lr decayed by gamma every step_size epochs. When last_epoch=-1, sets initial lr as lr.

from torch.optim import lr_sheduler
optimizer_ft = optim.SGD(model_ft.parameters(), lr=0.001, momentum=0.9)
exp_lr_sheduler=lr_sheduler.StepLR(optimizer_ft,step_size=7000,gamma=0.1)
>>> # Assuming optimizer uses lr = 0.05 for all groups
>>> # lr = 0.05     if epoch < 30
>>> # lr = 0.005    if 30 <= epoch < 60
>>> # lr = 0.0005   if 60 <= epoch < 90
>>> # ...
>>> scheduler = StepLR(optimizer, step_size=30, gamma=0.1)
>>> for epoch in range(100):
>>>     scheduler.step()
>>>     train(...)
>>>     validate(...)

分开进行训练和验证

  • 因为训练集和验证集需要加载不同的数据,同时对数据的预处理方式也不同,所以写成字典的形式,训练的话就取train,测试的话就取val
  • 两种对图片的处理方式,所以是两个transforms.Copose,两个图片的文件夹所以是两个datasets.ImageFolder,两个数据加载器所以是两个torch,utils.data.DataLoader
  • image_datasets=datasets.ImageFolder取出来的数据可以使用len(image_datasets)获得图片的数目。
data_transforms = {
    'train': transforms.Compose([
        transforms.RandomResizedCrop(224),
        transforms.RandomHorizontalFlip(),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
    'val': transforms.Compose([
        transforms.Resize(256),
        transforms.CenterCrop(224),
        transforms.ToTensor(),
        transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
    ]),
}

data_dir = 'hymenoptera_data'
image_datasets = {x: datasets.ImageFolder(os.path.join(data_dir, x),
                                          data_transforms[x])
                  for x in ['train', 'val']}
dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x], batch_size=4,
                                             shuffle=True, num_workers=4)
              for x in ['train', 'val']}
dataset_sizes = {x: len(image_datasets[x]) for x in ['train', 'val']}
  • 使用循环设置两个阶段,一个是train一个是val然后不同的阶段将模型设置为不同的模式,model.train()model.eval(),训练阶段打开学习率调度器,验证的时候不打开,然后不同的阶段从不同的数据加载器上获得数据,不同的阶段设置不同的任务,比如训练阶段就loss.backward(),optimizer.step(),测试阶段就保存准确率最高的模型。
  • 总结:基本上只需要设置不同的加载器,然后训练的时候指定model不同的模式即可。
def train_model(model, criterion, optimizer, scheduler, num_epochs=25):
    for epoch in range(num_epochs):
        for phase in ['train', 'val']:
            if phase == 'train':
                scheduler.step()
                model.train()  # Set model to training mode
            else:
                model.eval()   # Set model to evaluate mode
            # Iterate over data.
            for inputs, labels in dataloaders[phase]:
                inputs = inputs.to(device)
                labels = labels.to(device)
                optimizer.zero_grad()

使用torch.sum()知道自己预测对了多少

running_corrects += torch.sum(preds == labels.data)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343