解决pytorch下只打印tensor的数值不打印出device等信息的问题

torch.Tensor类型的数据loss和acc打印时

如果写成以下写法

print('batch_loss: '+str(loss.data)+'batch acc: '+str(acc.data))

则不仅会打印出loss和acc的值,还会打印出device信息和 tensor字样,如下:

如果仅想打印出数值,使得打印出的信息更加简洁

则要用以下写法

print('batch_loss: {:.3f} batch acc: {:.3f}'.format(loss.data, acc.data))

该写法还可控制输出格式,结果类似如下:

补充:关于pytorch中用plt显示tensor的问题(tensor转numpy)

问题

图像的张量结构为(C,H,W),而plt可以显示的图片格式要求(H,W,C),C为颜色通道数,可以没有。

所以问题就是将Tensor(C,H,W)=> numpy(H,W,C)

解决办法

def transimg(img):
    img = img / 2 + 0.5 # unnormalize
    npimg = img.numpy()
    npimg1 = np.transpose(npimg,(1,2,0)) # C*H*W => H*W*C
    return npimg1

补充:PyTorch代码调试利器: 自动print每行代码的Tensor信息

本文介绍一个用于 PyTorch 代码的实用工具 TorchSnooper。作者是TorchSnooper的作者,也是PyTorch开发者之一。

GitHub 项目地址: https://github.com/zasdfgbnm/TorchSnooper

大家可能遇到这样子的困扰:比如说运行自己编写的 PyTorch 代码的时候,PyTorch 提示你说数据类型不匹配,需要一个 double 的 tensor 但是你给的却是 float;再或者就是需要一个 CUDA tensor, 你给的却是个 CPU tensor。

比如下面这种:

RuntimeError: Expected object of scalar type Double but got scalar type Float

这种问题调试起来很麻烦,因为你不知道从哪里开始出问题的。比如你可能在代码的第三行用 torch.zeros 新建了一个 CPU tensor, 然后这个 tensor 进行了若干运算,全是在 CPU 上进行的,一直没有报错,直到第十行需要跟你作为输入传进来的 CUDA tensor 进行运算的时候,才报错。要调试这种错误,有时候就不得不一行行地手写 print 语句,非常麻烦。

再或者,你可能脑子里想象着将一个 tensor 进行什么样子的操作,就会得到什么样子的结果,但是 PyTorch 中途报错说 tensor 的形状不匹配,或者压根没报错但是最终出来的形状不是我们想要的。这个时候,我们往往也不知道是什么地方开始跟我们「预期的发生偏离的」。我们有时候也得需要插入一大堆 print 语句才能找到原因。

TorchSnooper 就是一个设计了用来解决这个问题的工具。TorchSnooper 的安装非常简单,只需要执行标准的 Python 包安装指令就好:

pip install torchsnooper

安装完了以后,只需要用 @torchsnooper.snoop() 装饰一下要调试的函数,这个函数在执行的时候,就会自动 print 出来每一行的执行结果的 tensor 的形状、数据类型、设备、是否需要梯度的信息。

安装完了以后,下面就用两个例子来说明一下怎么使用。

例子1

比如说我们写了一个非常简单的函数:

def myfunc(mask, x):
    y = torch.zeros(6)
    y.masked_scatter_(mask, x)
    return y

我们是这样子使用这个函数的:

mask = torch.tensor([0, 1, 0, 1, 1, 0], device='cuda')
source = torch.tensor([1.0, 2.0, 3.0], device='cuda')
y = myfunc(mask, source)

上面的代码看起来似乎没啥问题,然而实际上跑起来,却报错了:

RuntimeError: Expected object of backend CPU but got backend CUDA for argument #2 'mask'

问题在哪里呢?让我们 snoop 一下!用 @torchsnooper.snoop() 装饰一下 myfunc 函数:

import torch
import torchsnooper
@torchsnooper.snoop()
def myfunc(mask, x):
    y = torch.zeros(6)
    y.masked_scatter_(mask, x)
    return y

mask = torch.tensor([0, 1, 0, 1, 1, 0], device='cuda')
source = torch.tensor([1.0, 2.0, 3.0], device='cuda')
y = myfunc(mask, source)

然后运行我们的脚本,我们看到了这样的输出:

Starting var:.. mask = tensor<(6,), int64, cuda:0>
Starting var:.. x = tensor<(3,), float32, cuda:0>
21:41:42.941668 call 5 def myfunc(mask, x):
21:41:42.941834 line 6 y = torch.zeros(6)
New var:....... y = tensor<(6,), float32, cpu>
21:41:42.943443 line 7 y.masked_scatter_(mask, x)
21:41:42.944404 exception 7 y.masked_scatter_(mask, x)

结合我们的错误,我们主要去看输出的每个变量的设备,找找最早从哪个变量开始是在 CPU 上的。我们注意到这一行:

New var:....... y = tensor<(6,), float32, cpu>

这一行直接告诉我们,我们创建了一个新变量 y, 并把一个 CPU tensor 赋值给了这个变量。这一行对应代码中的 y = torch.zeros(6)。于是我们意识到,在使用 torch.zeros 的时候,如果不人为指定设备的话,默认创建的 tensor 是在 CPU 上的。我们把这一行改成 y = torch.zeros(6, device='cuda'),这一行的问题就修复了。

这一行的问题虽然修复了,我们的问题并没有解决完整,再跑修改过的代码还是报错,但是这个时候错误变成了:

RuntimeError: Expected object of scalar type Byte but got scalar type Long for argument #2 'mask'

好吧,这次错误出在了数据类型上。这次错误报告比较有提示性,我们大概能知道是我们的 mask 的数据类型错了。再看一遍 TorchSnooper 的输出,我们注意到:

Starting var:.. mask = tensor<(6,), int64, cuda:0>

果然,我们的 mask 的类型是 int64, 而不应该是应有的 uint8。我们把 mask 的定义修改好:

mask = torch.tensor([0, 1, 0, 1, 1, 0], device='cuda', dtype=torch.uint8)

然后就可以运行了。

例子 2

这次我们要构建一个简单的线性模型:

model = torch.nn.Linear(2, 1)

我们想要拟合一个平面 y = x1 + 2 * x2 + 3,于是我们创建了这样一个数据集:

x = torch.tensor([[0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0]])
y = torch.tensor([3.0, 5.0, 4.0, 6.0])

我们使用最普通的 SGD 优化器来进行优化,完整的代码如下:

import torch
model = torch.nn.Linear(2, 1)
x = torch.tensor([[0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0]])
y = torch.tensor([3.0, 5.0, 4.0, 6.0])

optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
for _ in range(10):
    optimizer.zero_grad()
    pred = model(x)
    squared_diff = (y - pred) ** 2
    loss = squared_diff.mean()
    print(loss.item())
    loss.backward()
    optimizer.step()

然而运行的过程我们发现,loss 降到 1.5 左右就不再降了。这是很不正常的,因为我们构建的数据都是无误差落在要拟合的平面上的,loss 应该降到 0 才算正常。

乍看上去,不知道问题在哪里。抱着试试看的想法,我们来 snoop 一下子。这个例子中,我们没有自定义函数,但是我们可以使用 with 语句来激活 TorchSnooper。把训练的那个循环装进 with 语句中去,代码就变成了:

import torch
import torchsnooper
model = torch.nn.Linear(2, 1)
x = torch.tensor([[0.0, 0.0], [0.0, 1.0], [1.0, 0.0], [1.0, 1.0]])
y = torch.tensor([3.0, 5.0, 4.0, 6.0])
optimizer = torch.optim.SGD(model.parameters(), lr=0.1)

with torchsnooper.snoop():
    for _ in range(10):
        optimizer.zero_grad()
        pred = model(x)
        squared_diff = (y - pred) ** 2
        loss = squared_diff.mean()
        print(loss.item())
        loss.backward()
        optimizer.step()

运行程序,我们看到了一长串的输出,一点一点浏览,我们注意到

New var:....... model = Linear(in_features=2, out_features=1, bias=True)
New var:....... x = tensor<(4, 2), float32, cpu>
New var:....... y = tensor<(4,), float32, cpu>
New var:....... optimizer = SGD (Parameter Group 0 dampening: 0 lr: 0....omentum: 0 nesterov: False weight_decay: 0)
02:38:02.016826 line 12 for _ in range(10):
New var:....... _ = 0
02:38:02.017025 line 13 optimizer.zero_grad()
02:38:02.017156 line 14 pred = model(x)
New var:....... pred = tensor<(4, 1), float32, cpu, grad>
02:38:02.018100 line 15 squared_diff = (y - pred) ** 2
New var:....... squared_diff = tensor<(4, 4), float32, cpu, grad>
02:38:02.018397 line 16 loss = squared_diff.mean()
New var:....... loss = tensor<(), float32, cpu, grad>
02:38:02.018674 line 17 print(loss.item())
02:38:02.018852 line 18 loss.backward()
26.979290008544922
02:38:02.057349 line 19 optimizer.step()

仔细观察这里面各个 tensor 的形状,我们不难发现,y 的形状是 (4,),而 pred 的形状却是 (4, 1),他们俩相减,由于广播的存在,我们得到的 squared_diff 的形状就变成了 (4, 4)。

这自然不是我们想要的结果。这个问题修复起来也很简单,把 pred 的定义改成 pred = model(x).squeeze() 即可。现在再看修改后的代码的 TorchSnooper 的输出:

New var:....... model = Linear(in_features=2, out_features=1, bias=True)
New var:....... x = tensor<(4, 2), float32, cpu>
New var:....... y = tensor<(4,), float32, cpu>
New var:....... optimizer = SGD (Parameter Group 0 dampening: 0 lr: 0....omentum: 0 nesterov: False weight_decay: 0)
02:46:23.545042 line 12 for _ in range(10):
New var:....... _ = 0
02:46:23.545285 line 13 optimizer.zero_grad()
02:46:23.545421 line 14 pred = model(x).squeeze()
New var:....... pred = tensor<(4,), float32, cpu, grad>
02:46:23.546362 line 15 squared_diff = (y - pred) ** 2
New var:....... squared_diff = tensor<(4,), float32, cpu, grad>
02:46:23.546645 line 16 loss = squared_diff.mean()
New var:....... loss = tensor<(), float32, cpu, grad>
02:46:23.546939 line 17 print(loss.item())
02:46:23.547133 line 18 loss.backward()
02:46:23.591090 line 19 optimizer.step()

现在这个结果看起来就正常了。并且经过测试,loss 现在已经可以降到很接近 0 了。大功告成。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • pytorch查看torch.Tensor和model是否在CUDA上的实例

    今天训练faster R-CNN时,发现之前跑的很好的程序(是指在运行程序过程中,显卡利用率能够一直维持在70%以上),今天看的时候,显卡利用率很低,所以在想是不是我的训练数据torch.Tensor或者模型model没有加载到GPU上训练,于是查找如何查看tensor和model所在设备的命令. import torch import torchvision.models as models model=models.vgg11(pretrained=False) print(next(mod

  • 详解PyTorch中Tensor的高阶操作

    条件选取:torch.where(condition, x, y) → Tensor 返回从 x 或 y 中选择元素的张量,取决于 condition 操作定义: 举个例子: >>> import torch >>> c = randn(2, 3) >>> c tensor([[ 0.0309, -1.5993, 0.1986], [-0.0699, -2.7813, -1.1828]]) >>> a = torch.ones(2,

  • pytorch 实现打印模型的参数值

    对于简单的网络 例如全连接层Linear 可以使用以下方法打印linear层: fc = nn.Linear(3, 5) params = list(fc.named_parameters()) print(params.__len__()) print(params[0]) print(params[1]) 输出如下: 由于Linear默认是偏置bias的,所有参数列表的长度是2.第一个存的是全连接矩阵,第二个存的是偏置. 对于稍微复杂的网络 例如MLP mlp = nn.Sequential

  • 对Pytorch中Tensor的各种池化操作解析

    AdaptiveAvgPool1d(N) 对一个C*H*W的三维输入Tensor, 池化输出为C*H*N, 即按照H轴逐行对W轴平均池化 >>> a = torch.ones(2,3,4) >>> a[0,1,2] = 0 >>>> a tensor([[[1., 1., 1., 1.], [1., 1., 0., 1.], [1., 1., 1., 1.]], [[1., 1., 1., 1.], [1., 1., 1., 1.], [1.,

  • 解决pytorch下只打印tensor的数值不打印出device等信息的问题

    torch.Tensor类型的数据loss和acc打印时 如果写成以下写法 print('batch_loss: '+str(loss.data)+'batch acc: '+str(acc.data)) 则不仅会打印出loss和acc的值,还会打印出device信息和 tensor字样,如下: 如果仅想打印出数值,使得打印出的信息更加简洁 则要用以下写法 print('batch_loss: {:.3f} batch acc: {:.3f}'.format(loss.data, acc.dat

  • 解决pytorch下出现multi-target not supported at的一种可能原因

    在使用交叉熵损失函数的时候,target的形状应该是和label的形状一致或者是只有batchsize这一个维度的. 如果target是这样的[batchszie,1]就会出现上述的错误. 改一下试试,用squeeze()函数降低纬度, 如果不知道squeeze怎么用的, 可以参考这篇文章.pytorch下的unsqueeze和squeeze用法 这只是一种可能的原因. 补充:pytorch使用中遇到的问题 1. load模型参数文件时,提示torch.cuda.is_available() i

  • 解决tensorflow打印tensor有省略号的问题

    先上代码: import tensorflow as tf x = tf.ones(shape=[100, 200], dtype=tf.int32, name='x') y = tf.zeros(shape=[2, 3], dtype=tf.float32, name='y') with tf.Session() as sess: print(sess.run([x, y])) 输出结果如下: 如果我调试的时候想查看省略号代表的值是什么 只需要改成如下代码就行: import tensorfl

  • 解决pytorch多GPU训练保存的模型,在单GPU环境下加载出错问题

    背景 在公司用多卡训练模型,得到权值文件后保存,然后回到实验室,没有多卡的环境,用单卡训练,加载模型时出错,因为单卡机器上,没有使用DataParallel来加载模型,所以会出现加载错误. 原因 DataParallel包装的模型在保存时,权值参数前面会带有module字符,然而自己在单卡环境下,没有用DataParallel包装的模型权值参数不带module.本质上保存的权值文件是一个有序字典. 解决方法 1.在单卡环境下,用DataParallel包装模型. 2.自己重写Load函数,灵活.

  • 解决引用slf4j中Logger.info只打印出文字没有数据的问题

    目录 slf4j Logger.info只打印出文字没有数据 解决方案 启用设置org.slf4j.Logger打印并输出日志 pom.xml配置 logback.xml配置 程序调用 slf4j Logger.info只打印出文字没有数据 引的是 slf4j 包 import org.slf4j.Logger; import org.slf4j.LoggerFactory; private static Logger logger = LoggerFactory.getLogger(TsfTe

  • 解决pytorch GPU 计算过程中出现内存耗尽的问题

    Pytorch GPU运算过程中会出现:"cuda runtime error(2): out of memory"这样的错误.通常,这种错误是由于在循环中使用全局变量当做累加器,且累加梯度信息的缘故,用官方的说法就是:"accumulate history across your training loop".在默认情况下,开启梯度计算的Tensor变量是会在GPU保持他的历史数据的,所以在编程或者调试过程中应该尽力避免在循环中累加梯度信息. 下面举个栗子: 上代

  • 解决windows下python3使用multiprocessing.Pool出现的问题

    例如: from multiprocessing import Pool def f(x): return x*x pool = Pool(processes=4) r=pool.map(f, range(100)) pool.close() pool.join() 在spyder里运行直接没反应:在shell窗口里,直接报错,如下: Process SpawnPoolWorker-15: Traceback (most recent call last): File "C:\Anaconda3

  • 解决linux下vim中文乱码的方法

    Vim编码的详细介绍 Vim和所有的流行文本编辑器一样,Vim 可以很好的编辑各种字符编码的文件,这当然包括 UCS-2.UTF-8 等流行的 Unicode 编码方式. Vim 有四个跟字符编码方式有关的选项,encoding.fileencoding.fileencodings.termencoding (这些选项可能的取值请参考 Vim 在线帮助  :help encoding-names),它们的意义如下: 1.encoding: Vim 内部使用的字符编码方式,包括 Vim 的 buf

  • 解决IE下AjaxSubmit上传文件提示下载文件问题

    springmvc后端: @RequestMapping(value="scoreFileUpload",produces = "text/html; charset=utf-8") @ResponseBody public String upload(HttpSession session,@RequestParam("file1") MultipartFile file,@RequestParam("paperId") S

  • 解决Mac下使用python的坑

    操作系统:macOS High Sierra 10.13.3 Python3.6 因为此版本自带python2.7,就下载并安装了anaconda2的版本,结果使用的时候系统自带的python和anaconda里的python冲突,也就是装包的时候只在一处装了,另一处却不能使用.因此要配置环境变量,替代到系统自带的版本. python2.7总会发生中文字体乱码的情况,果断放弃,准备使用anaconda3. 1.安装anaconda3 在官网下载,并安装,直接都是下一步. 2.修改环境变量 在命令

随机推荐