pytorch备忘录

Posted anime-enjoy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了pytorch备忘录相关的知识,希望对你有一定的参考价值。

0.前言

这篇文章是最近学习pytorch的一点经验总结,整体来说是备忘录性质的随笔。

1.GPU利用率过低(2020年7月20日)

问题描述
跑数据时发现GPU利用率几乎为0,CPU占用倒是很高,一度怀疑是CUDA没配置好。然而做了profile之后发现大多数时间都用在了读取数据上。
读取数据是下面这个模块

batch_size=1024
num=8

class Diabetes(Dataset):
    def __init__(self,path):
        self.bits=np.loadtxt(path,delimiter=‘,‘,dtype=np.float32)
        self.data=self.bits[:,:-1]
        self.label=self.bits[:,[-1]]
        self.len=self.bits.shape[0]
    def __getitem__(self, item):
        return self.data[item],self.label[item]
    def __len__(self):
        return self.len


train_loader=DataLoader(dataset=dataset,
                        batch_size=batch_size,
                        shuffle=True,
                        num_workers=num)

于是开始调整,发现线程数调低反而更快。num_works=8时耗时前三的模块如下表,第一行是python中用于序列化的模块。

num_works=8)名称 调用次数 占用时间/ms
method ‘dump‘ of ‘_pickle.Pickler‘ objects 800 437621
built-in method _winapi.WaitForSingleObject 1000 18122
built-in method _winapi.CreateProcess 400 1363

num_works=0时如下表:

num_works=0) 名称 调用次数 占用时间/ms
method ‘cuda‘ of ‘torch._C._TensorBase‘ objects 206 1151
built-in method stack 200 797
built-in method as_tensor 151800 439

可以看到现在主要消耗的时间在计算而不是序列化和等待

1.1 结论

事后思考认为可能是对于较小的数据集(这个数据集一共只有749个sample)多线程读取的效率不高,大多数时间都花在了序列化和等待上,导致读取数据变慢,但同时GPU计算速度很快,数据一来就算完了,最终使得GPU的利用率很低。
可以考虑先使用默认参数(num_works=0),如果发现读取速度不足再考虑调高。

2.卷积神经网络tensor操作(torchsnooper工具)

现有这样一段程序,如果它是错误的,而且知道问题在于tensor的大小对不上号。
很自然的我们会想到写一堆print(x.shape),那么有没有更快捷的方法呢?torchsnooper就是一个这样的工具。

from torchsnooper import snoop
# pip install torchsnooper(conda的清华源中没有这个包)
@snoop() #装饰器
    def forward(self,x):
        x = self.sigmod(self.linear1(x))
        x = self.sigmod(self.linear2(x))
        x = self.sigmod(self.linear3(x))
        return x

可以得到如下的输出:

Source path:... C:/Users/Jrzih/iCloudDrive/code/Conv_network/pytorch-tutorial/Dataload.py
Starting var:.. self = Net(  (linear1): Linear(in_features=8, out_featu... out_features=1, bias=True)  (sigmod): Sigmoid())

Starting var:.. x = tensor<(759, 8), float32, cuda:0>
23:41:40.072532 call        37     def forward(self,x):

23:41:40.073532 line        38         x = self.sigmod(self.linear1(x))
Modified var:.. x = tensor<(759, 6), float32, cuda:0, grad>

23:41:40.271252 line        39         x = self.sigmod(self.linear2(x))
Modified var:.. x = tensor<(759, 2), float32, cuda:0, grad>

23:41:40.272253 line        40         x = self.sigmod(self.linear3(x))
Modified var:.. x = tensor<(759, 1), float32, cuda:0, grad>

23:41:40.273252 line        41         return x
23:41:40.273252 return      41         return x
Return value:.. tensor<(759, 1), float32, cuda:0, grad>
Elapsed time: 00:00:00.201720

3.模型保存和读取

#--------save---------
best_loss=float(‘inf‘)
model=Net()
if loss < best_loss:
    best_loss = loss
    state = {‘net‘:model.state_dict(), ‘optimizer‘:optimizer.state_dict(), ‘epoch‘:epoch}
    torch.save(model.state_dict(), ‘best_model.pth‘)
#-------load----------
checkpoint = torch.load(dir)
model.load_state_dict(checkpoint[‘net‘])
optimizer.load_state_dict(checkpoint[‘optimizer‘])
start_epoch = checkpoint[‘epoch‘] + 1

4.tensorboard

from torch.utils.tensorboard import SummaryWriter
#                        路径
writer = SummaryWriter(‘./log‘)
#                 图表名      纵轴       横轴
writer.add_scalar(‘loss‘, loss.item(), epoch)
# bash中输入
tensorboard --logdir=./log --port xxxx

5.torch.nn.CrossEntropyLoss()简析

CrossEntropyLoss(model(X),Y)
值得注意的是CrossEntropyLossLogSoftmaxnn.NLLLoss的结合
文档原文:This criterion combines :func:nn.LogSoftmax and :func:nn.NLLLoss in one single class.
源码中也有体现

def cross_entropy(input, target, weight=None, size_average=None, ignore_index=-100,
                  reduce=None, reduction=‘mean‘):
                  ...
    return nll_loss(log_softmax(input, 1), target, weight, None, ignore_index, None, reduction)

LogSoftmax是Softmax的结果取对数,那nn.NLLLoss是什么?
通过查阅资料可知,可以实现如下(源码中的计算是由torch._C._nn.nll_loss来做的)

import torch
from torch import nn
from torch.nn import functional as F

pre=torch.tensor([[ 1.1569,  0.9973, -0.9057, -0.7047,  0.8989],
                    [ 0.2700, -0.7745,  0.2419,  0.2702,  1.2151],
                    [ 0.4620,  0.8975, -1.9262, -0.0024,  1.5386]])
label=torch.tensor([0,4,4])
#--------CrossEntropy的二维实现------
# 1.对预测矩阵取softmax
# 2.取出label对应位置的值构成行向量
# 3.取log之后取相反数并求平均值
res = F.softmax(pre,dim=1)
ans = torch.empty(label.shape)
for n,i in enumerate(res,0) :
    ans[n]=i[label[n].long()]
ans = torch.mean(-torch.log(ans))
print(ans)
#----------------------------------
loss=nn.CrossEntropyLoss()
print(loss(pre,label))

顺便一提对于softmax,如果不给出dim,也就是沿着哪条轴计算,那么_get_softmax_dim函数会自动给出dim,源码如下

def softmax(input, dim=None, _stacklevel=3, dtype=None):
    ...
    if dim is None:
        dim = _get_softmax_dim(‘softmax‘, input.dim(), _stacklevel)
    ...
def _get_softmax_dim(name, ndim, stacklevel):
    # type: (str, int, int) -> int
    warnings.warn("Implicit dimension choice for {} has been deprecated. "
                  "Change the call to include dim=X as an argument.".format(name), stacklevel=stacklevel)
    if ndim == 0 or ndim == 1 or ndim == 3:
        ret = 0
    else:
        ret = 1
    # 综合起来就是对于二维矩阵以外的输入,默认的dim都是0,即沿着第一根轴计算(对于二维矩阵来说是列的方向),而对于二维矩阵,dim=1,即行的方向。
    return ret








以上是关于pytorch备忘录的主要内容,如果未能解决你的问题,请参考以下文章

Pytorch 备忘清单_开发速查表分享

Flutter 布局备忘录

Pytorch用自动微分求sin(x)的导数

Pytorch用自动微分求sin(x)的导数

常用python日期日志获取内容循环的代码片段

2D TOOLKIT备忘录