18_pytorch

240 阅读4分钟

变量

tensor变量:torch.tensor、torch.Tensor、Variable

1. torch.tensor(data) 是torch的一个函数,根据传入的data类型(比如是Long),生成对应的torch.LongTensor对象
2. torch.Tensor(data)是默认的Float对象,不管data是什么类型都会生成torch.FloatTensor
3. Variable是旧版本的torch.Tensor

tensor变量的声明与属性

1. 声明:
	都用 torch.tensor(data, dtype=torch.float, device='cuda:0', requires_grad=False) 格式
2. 属性:
	a.requires_grad 表示是否有跟踪梯度信息,自动求梯度
    	a.requires_grad_ 表示具体的梯度值

pipeline

- 基础模型,定义模型内部参数,定义模型前向传播方式: nn.Module, nn.Parameter(), forward()
- 定义损失函数和优化器
    - loss是计算pred和true, criterion
    - 优化器是定义梯度和参数如何更新,一般是 新参数=原参数-梯度x步长, optimizer
- 循环
    - 前向传播 y' = model(x)
    - 计算损失 loss = criterion(y', y)
    - 梯度清零 optimizer.zero_grad()  # 优化器里面的梯度清零,当前batch更新参数只用当前batch的梯度
    - 反向传播 loss.backward()  # 即根据计算图,计算每个参数的梯度,类似求导计算
    - 梯度更新 optimizer.step() # 即根据优化器的类型,来更新参数
    
    
1. 训练的数据是不需要 requires_grad, 模型的参数是需要 requires_grad

train和eval和torch.no_grad()

- model.train()
- model.eval()
- with torch.no_grad()

在PyTorch中进行validation/test时,会使用model.eval()切换到测试模式,在该模式下:

    1. 主要用于通知 dropout层 和 batchnorm层 在train和validation/test模式间切换
    
    2. 在train模式下,dropout网络层会按照设定的参数p设置保留激活单元的概率(保留概率=p); 
       batchnorm层会继续计算数据的mean和var等参数并更新。
       
    3.eval模式下,dropout层会让所有的激活单元都通过,而batchnorm层会停止计算和更新mean和var,
       直接使用在训练阶段已经学出的mean和var值。该模式不会影响各层的gradient计算行为,
       即gradient计算和存储与training模式一样,只是不进行反传(backprobagation)。

    4.with torch.no_grad()则主要是用于停止autograd模块的工作,以起到加速和节省显存的作用,具体行为就是停止gradient计算,
       从而节省了GPU算力和显存,但是并不会影响dropout和batchnorm层的行为。

    5. 如果不在意显存大小和计算时间的话,仅仅使用model.eval()已足够得到正确的validation/test的结果;
       而with torch.no_grad()则是更进一步加速和节省gpu空间(因为不用计算和存储gradient),
       从而可以更快计算,也可以跑更大的batch来测试。
       

item(), detach(), data(), cpu(), numpy()

- .item(), 只返回tensor变量中的值,且只返回一个值,不返回tensor,适用于计算loss后得到最终的loss具体数值
- .detach(), 返回值为tensor, 同时阻断反向传播, 即loss.backend()调用时,不计算使用了此函数的变量的梯度
- .data, 返回值为tensor, 不阻断反向传播,现在推荐使用 detach(), 更加安全,浅拷贝改变后反向会报错提醒
- .cpu(), 将数据从gpu转到cpu
- .numpy(), tensor转为numpy, 注意gpu上的变量只能是tensor

dataset和dataloader

- torch.utils.data.DataSet(), 需要定义好 `__getitem__`
- torch.utils.data.DataLoader(data, batch_size, shuffle, sampler, num_workers)

自定义数据集
1. 分别定义train和set的dataset和dataloader (已知trian和test分开情况下)
    train_ds = dataset(train)
    test_ds = dataset(test)
    train_loader = dataloader(train_ds)
    test_loader = dataloader(test_ds)
    

2. 利用sample进行划分,(随机划分), 用pytorch自带的sample, 如果用其他的,shuffle要设置为False
    n_train = len(sentiment_train_set)
    split = n_train // 3
    indices = list(range(n_train))
    train_sampler = torch.utils.data.sampler.SubsetRandomSampler(indices[split:])
    valid_sampler = torch.utils.data.sampler.SubsetRandomSampler(indices[:split])
    train_loader = DataLoader(sentiment_train_set, sampler=train_sampler, shuffle=False)
    valid_loader = DataLoader(sentiment_train_set, sampler=valid_sampler, shuffle=False)

模型保存

1. 一般保存为 state_dict, 需要保存 net.state_dict() 和 optimizer.state_dict()

PATH = "state_dict_model.pt"
# save
torch.save(net.state_dict(), PATH)
# load
model = Net()
model.load_state_dict(torch.load(PATH))
model.eval()

2. checkpoint, 每个epoch都保存一次state_dict

3. different device model
- Save on GPU, Load on CPU
- Save on a GPU, load on a GPU
- Save on a CPU, load on a GPU
- Saving and loading DataParallel models

分布式训练

分为多卡多机分布式训练和多卡单机并行训练

- nn.DataParallel, 适用于多卡单机,即单机多GPU训练
基本流程:
	1. replicate,复制module到多个device上
	2. scatter,将input data按照first-dimension根据device数量进行划分
	3. parallel_apply,将划分的data并行进行forward,
	4. gather,按照first-dimension将各个device上的result进行汇总,然后计算整体loss

注意事项:
	1. nn.DataParallel  wrappe过的model,不能直接调用内部定义的函数,要用model.module.func来调用
	2. 定义batch size时,注意和device数量结合,减少资源不协调
	3. nn.DataParallel无法避免资源,因为是在master device进行分发,其他device仅并行计算,最后汇总和计算loss还是在master device
   	4. 使用nn.DataParallel后,记得调整batch size, 如果还是原来的batch size,比如100,只是把100分层几份来同时训练。时间反而会更久。如果是3个GPU,batch size应该改为300. 这样才利用上了nn.DataParallel。
    5. nn.DataParallel速度可能还会变慢。。。。

- nn.distributed, 多卡多机和多卡单机都适用, 基本用这个

模型部署

- 基于flask接口:
  1. 加载模型
  2. 定义预处理函数
  3. 定义 inference 函数
  4. 定义接口整合上述函数