Pytorch-训练学习
Autograd
autograd类为计算导数的引擎(雅可比向量积),雅可比矩阵的概念参考这篇博文,autograd记录了梯度张量上所有操作的一个图,并创建了一个称为动态计算图的非循环图。这个图的叶节点是输入张量,根节点是输出张量。梯度是通过跟踪从根到叶的图形,并使用链式法则将每个梯度相乘来计算的。
1.跟踪tensor上的所有操作,设置属性requires_grad=True
x = torch.randn(3,3,requires_grad=True)
2.自动计算所有梯度,调用.backward()
y = (x*2).sum()
y.backward()
print(y.requires_grad) # True
3.停止跟踪tensor,调用.detach()
或使用代码块with torch.no_grad()
with torch.no_grad():
print((x*2).requires_grad) # False
y = y.detach()
print(y.requires_grad) # False
4.若tensor不仅仅是标量,需要靠gradient参数来说明张量的形式
y_ = x*2
v = torch.randn(3,3)
y_.backward(v)
print(x.grad) # x的导数
5..grad_fn属性是对tensor计算一次就引用一次Function对象,来产生运算结果,记录运算的发生,保存记录运算的输入,tensor使用.grad.fn属性记录这个计算图的入口,反向传播中,autograd引擎会按照逆序,通过function的backward依次计算梯度,grad_fn相当于指针,指向的是Function对象的地址。
DP模式训练(DataParallel)
pytorch中的GPU操作默认是异步的,当调用一个使用GPU的函数时,这些操作会在特定设备上排队但不一定在稍后执行。这使用pytorch具有并行计算的能力。
在平时训练中使用最多的就是多GPU并行计算,pytorch中的多GPU并行计算是数据级并行,相当于开了多个进程,每个进程自己独立运行,最后再整合在一起。
from torch.nn import DataParallel as DP
device_ids = [0,1,2]
net = DP(net,device_ids=device_ids)
上面提到的DataParallel函数,通过源码来看一下它的作用
DataParallel(module,device_ids=None,output_device=None,dim=0)
module:每个模型都要拷贝一份数据
device_ids:存储gpu的列表
output_device:输出gpu,默认为第一块卡(index=0),虽然输入计算由几块卡均分,但是输出loss的计算由这一张卡独自承担,这造成了这张卡所承受的计算量要大于其他参与训练的卡
下面来说下DP是如何进行并行计算的
在前向传播中,输入batch会被划分成多个子batch并送到不同的gpu(device)中进行计算,而你的module是在每个gpu上进行复制,简而该之,输入的batch会被平均分配到每个gpu中,但是module要拷贝到每个gpu中,每个gpu中的module只需要处理它被分配到的子batch即可(注意:你的batch要大于gpu个数)。再在反向传播过程中,每个子batch的梯度被累加到原始模块中(汇总的过程)。
注意:在运行DP模块之前,并行化模块必须在device_ids[0]上具有其参数和缓冲区。在执行DP前,会先把模型的参数放在device_ids[0]上。但是实际开发中,假如八卡服务器的0号显卡被人占用,那么你只能用2,3号卡来训练,但是你不能直接指定device_ids=[2,3],这样会出现模型初始化的bug,也就是你的module没有复制到device_ids[0]上。所以你必须在训练前添加如下两句话
os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID"
os.environ["CUDA_VISIBLE_DEVICES"] = "2,3"
此时device_ids[0]默认的就是2号卡了