> 文章列表 > PyTorch笔记

PyTorch笔记

PyTorch笔记

Tensor

torch中的Tensor是一种数据结构,使用上与Python的list、numpy的array、ndarray等数据结构类似,可以当成一个多维数组来用。 数学上对张量有特定定义,但通常理解为多维数组即可。

生成Tensor:torch包中提供了直接生成Tensor的函数,如 zeros()、ones()、rand() 等。 还可以用 tensor(data) 函数直接将表示数组的数据(如:list、numpy.ndarray等格式)转换为Tensor。 可通过 from_numpy(data) 函数将numpy.ndarray格式的数据转换为Tensor。 也可生成一个与其他Tensor具有相同dtype和device等属性的Tensor, 使用torch的 ones_like(data) 或 rand_like(data) 等函数,或Tensor的 new_ones() 等函数。

Tensor的属性: shape(返回torch.Size格式)(也可以用size()函数),dtypedevice。

Tensor的操作

        类似numpy的API;改变原数据的原地操作在函数后面加_就可以(一般不建议这么操作)

         • 索引

         • 切片 • join:cat(tensors)或stack(tensors)

         • 加法:add()或+ • 乘法:对元素层面的乘法mul()或*,矩阵乘法matmul()或@

         • resize :

                (1)reshape()或view(),建议使用reshape(),仅使用view()可能会造成Tensor不contiguous的问题

                (2)squeeze()去掉长度为1的维度

                (3)unsqueeze()增加一个维度(长度为1)

                (4)transpose()转置2个维度

5. Tensor.numpy() 可将Tensor转换为numpy数据。 注意这两方向的转换的数据对象都是占用同一储存空间,修改后变化也会体现在另一对象上。

6. item()函数返回仅有一个元素的Tensor的该元素值。

Autograd

        torch.autograd 是PyTorch提供的自动求导包,神经网络由权重、偏置等参数决定的函数构成,这些参数在PyTorch中都储存在 Tensor 里,神经网络的训练包括前向传播和反向传播两部分,前向传播就是用函数计算预测值,反向传播通过预测值产生的 error/loss 更新参数(通过梯度下降的方式)。

        神经网络的一轮训练:

                前向传播:prediction = model(data)

                反向传播:

                (1)计算loss

                (2)loss.backward()(autograd会在这一步计算参数的梯度,存在相应参数Tensor的grad属性中)

                (3)更新参数

                      1)加载optimizer(通过torch.optim)

                      2)optimizer.step() 使用梯度下降更新参数(梯度来源自参数的grad属性)

        Tensor的requires_grad属性设为False,可以将其排除在DAG之外,autograd就不会计算它的梯度。在神经网络中,不需要计算梯度的参数叫frozen parameters。可以冻结不需要知道梯度的参数(节省计算资源),也可以在微调预训练模型时使用(此时往往冻结绝大多数参数,仅调整classifier layer参数,以在新标签上做预测),类似功能也用于 torch.no_grad() 的实现。

Neutral Network

  • 神经网络可以通过torch.nn包搭建(torch.nn 预定义的层调用 torch.nn.functional包的函数)
  • nn.Module包含了网络层
  • forward(input)方法返回输出结果

 网络训练流程:

        (1)前向传播

        (2)计算loss

        (3)计算梯度

        (4)使用梯度下降法更新参数

        模型的可学习参数存储在model.parameters()中,其返回值是一个迭代器,包含模型及其所有子模型的参数。

定义网络:只需要定义forward() 方法,backward()方法会自动定义(用 autograd),在forward()方法中可以进行任何 Tensor 操作。

        前向传播:out = net(input)

        反向传播:先将参数梯度缓冲池清零(否则梯度会累加),再反向传播(此处使用一个随机矩阵),model.zero_grad(),out.backward(torch.randn(1, 10)),如果有计算出损失函数,上一行代码应为:loss.backward()

        注意:torch.nn只支持mini-batch,如果只有一个输入数据可用 input.unsqueeze(0) 创造一个伪batch维度。

        损失函数torch.nn包中定义的损失函数文档:Page Redirection

                以MSELoss为例:criterion = nn.MSELoss(),loss = criterion(output, target)

                        得到的loss,其grad_fn组成的DAG:

调用loss.backward()后,所有张量的梯度都会得到更新:

print(loss.grad_fn)  # MSELoss
print(loss.grad_fn.next_functions[0][0])  # Linear
print(loss.grad_fn.next_functions[0][0].next_functions[0][0])  # ReLU

多GPU并行训练

设置使用GPU:device = 'cuda' if torch.cuda.is_avaiable() else 'cpu'

DataParallel 和 DistributedDataParallel 两个类可用于GPU并行;

        以 DataParallel 为例:model = nn.DataParallel(model)

        在单卡上写好的 model 直接调用,别的都跟单卡形式一样,程序会自动把数据拆分放到所有已知的GPU上来运行,数据是直接从第一维拆开平均放到各个GPU上,相当于每个GPU放 batch_size / gpu_num 个样本。设置已知的GPU,可以在运行代码的 python 加上 CUDA_VISIBLE_DEVICES 参数,CUDA_VISIBLE_DEVICES=0,1,2,3 python example.py,如果要使用nohup的话,参数要加在nohup的前面,CUDA_VISIBLE_DEVICES=0,1,2,3 nohup python -u example.py >> nohup_output.log 2;如果不设置则默认为所有GPU,对GPU数量计数:torch.cuda.device_count() 代码。直接用 DataParallel 可能导致各卡空间不均衡的问题,建议使用 DistributedDataParallel。

Torch.squeeze()

Squeeze() 中如果没有指定删除的维度,则会将维度为 1 的都删除。
arr = np.array([1, 2, 3])
arr = arr.reshape(-1, 3, 1)
ta = torch.tensor(arr)ta = torch.tensor(arr)
ta.squeeze() -> tensor([1, 2, 3], dtype=torch.int32)
ta.squeeze(-1) -> tensor([[1, 2, 3]], dtype=torch.int32)

网络报错

RuntimeError: one of the variables needed for gradient computation has been modified by an inplace operation: [torch.FloatTensor [8, 250, 7]], which is output 0 of ReluBackward0, is at version 1; expected version 0 instead.

可使用下面的方法追踪问题:

import torch.autograd as autograd
# set anomaly detection mode
autograd.set_detect_anomaly(True)

需要更新的参数被原地操作更改了,可能是以下几种情况导致
1)找到网络模型中的 inplace 操作,将inplace=True改成 inplace=False,例如torch.nn.ReLU(inplace=False)

2)将代码中的“a+=b”之类的操作改为“c = a + b”,a=b改成a=ab,a/=b改成a=a/b。

3)训练代码的optimizer.step()函数放到loss.backward()后面

4)pytorch降版本到1.4或1.5

参考:

60分钟闪击速成PyTorch(Deep Learning with PyTorch: A 60 Minute Blitz)学习笔记-阿里云开发者社区

Note-of-PyTorch-60-Minutes-Tutorial/tensor_tutorial.ipynb at master · PolarisRisingWar/Note-of-PyTorch-60-Minutes-Tutorial · GitHub