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生成的图片保存
fake
的shape
是channels * 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是什么,怎么用?
可学习的变量,weight
和biases
,而且不仅模型有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.weight
和Linear.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)