热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

pytorchDebug—交互式调试工具Pdb(ipdb是增强版的pdb)1在pytorch中使用,深度学习框架PyTorch一书的学习第六章实战指南

参考深度学习框架pytorch:入门和实践一书第六章以深度学习框架PyTorch一书的学习-第六章-实战指南为前提在pytorch中Debug pytorch作为一个动态图框架,与

参考深度学习框架pytorch:入门和实践一书第六章

以深度学习框架PyTorch一书的学习-第六章-实战指南为前提

在pytorch中Debug

 pytorch作为一个动态图框架,与ipdb结合能为调试过程带来便捷

对tensorflow等静态图来说,使用python接口定义计算图,然后使用c++代码执行底层运算,在定义图的时候不进行任何计算,而在计算的时候又无法使用pdb进行调试,因为pdb调试只能挑事python代码,故调试一直是此类静态图框架的一个痛点

与tensorflow不同,pytorch可以在执行计算的同时定义计算图,这些计算定义过程是使用python完成的。虽然底层的计算也是用C/C++完成的但是我们能够查看python定义部分都变量值,这就足够了

下面我们将举例说明:

  • 如何在PyTorch中查看神经网络各个层的输出
  • 如何在PyTorch中分析各个参数的梯度
  • 如何动态修改PyTorch的训练流程

以第六章的猫狗分类为例,里面的train()函数中有一个设置:

# 进入debug模式
if os.path.exists(opt.debug_file):
import ipdb;
ipdb.set_trace()

即如果设置了这个文件,那么就进入了调试模式

即每次迭代训练到这里的时候就会进入debug模式

 

首先先将训练运行起来:

user@home:/opt/user/dogcat/chapter6$ python main.py train --env=main --train-data-root=./data/train/ --lr=0.005 --batch-size=32 --model='ResNet34' --max-epoch=100 --load-model-path=None --debug-file=./tmp/debug
user config:
env main
vis_port
8097
model ResNet34
train_data_root .
/data/train/
test_data_root .
/data/test1
load_model_path None
batch_size
32
use_gpu True
num_workers
4
print_freq
20
debug_file .
/tmp/debug
result_file result.csv
max_epoch
100
lr
0.005
lr_decay
0.5
weight_decay
0.0
WARNING:root:Setting up a
new session...
WARNING:visdom:Without the incoming socket you cannot receive events
from the server or register event handlers to your Visdom client.
/home/home/anaconda3/lib/python3.6/site-packages/torchvision/transforms/transforms.py:619: UserWarning: The use of the transforms.RandomSizedCrop transform is deprecated, please use transforms.RandomResizedCrop instead.
"please use transforms.RandomResizedCrop instead.")

然后这个时候在本地路径下创建tmp/debug文件夹:

user@home:/opt/user/dogcat/chapter6/tmp$ mkdir debug

那么就会因为检测到这个文件夹而进入到调试模式:

39it [00:15, 2.58it/s]> /opt/user/dogcat/chapter6/main.py(81)train()
80
---> 81 for ii,(data,label) in tqdm(enumerate(train_dataloader)):
82
ipdb
>

首先就可以使用l 90命令去查看第90行附近的代码,即上下五行的代码:

19it [00:05, 3.92it/s]> /opt/user/dogcat/chapter6/main.py(81)train()
80
---> 81 for ii,(data,label) in tqdm(enumerate(train_dataloader)):
82
ipdb
> l 90
85 target = label.to(opt.device)
86
87
88 optimizer.zero_grad()
89 score = model(input)
90 loss = criterion(score,target)
91 loss.backward()
92 optimizer.step()
93
94
95 # meters update and visualize

然后使用break 89在89行处设置断点:

ipdb> break 89
Breakpoint
1 at /opt/user/dogcat/chapter6/main.py:89

这个时候打印一下所有参数及其梯度的标准差:

ipdb> model.named_parameters()
object Module.named_parameters at 0x7f18b61431a8>
ipdb
> for (name, p) in model.named_parameters(): print(name, p.data.std(), p.grad.data.std())
pre.
0.weight tensor(0.0541, device='cuda:0') tensor(0.0073, device='cuda:0')
pre.
1.weight tensor(0.2988, device='cuda:0') tensor(0.0037, device='cuda:0')
pre.
1.bias tensor(0.0238, device='cuda:0') tensor(0.0042, device='cuda:0')
layer1.
0.left.0.weight tensor(0.0358, device='cuda:0') tensor(0.0003, device='cuda:0')
layer1.
0.left.1.weight tensor(0.2853, device='cuda:0') tensor(0.0008, device='cuda:0')
layer1.
0.left.1.bias tensor(0.0272, device='cuda:0') tensor(0.0005, device='cuda:0')
layer1.
0.left.3.weight tensor(0.0313, device='cuda:0') tensor(9.1792e-05, device='cuda:0')
layer1.
0.left.4.weight tensor(0.2931, device='cuda:0') tensor(0.0010, device='cuda:0')
layer1.
0.left.4.bias tensor(0.0233, device='cuda:0') tensor(0.0005, device='cuda:0')
layer1.
0.right.0.weight tensor(0.0771, device='cuda:0') tensor(0.0011, device='cuda:0')
layer1.
0.right.1.weight tensor(0.2923, device='cuda:0') tensor(0.0012, device='cuda:0')
layer1.
0.right.1.bias tensor(0.0233, device='cuda:0') tensor(0.0005, device='cuda:0')
layer1.
1.left.0.weight tensor(0.0313, device='cuda:0') tensor(0.0002, device='cuda:0')
layer1.
1.left.1.weight tensor(0.2865, device='cuda:0') tensor(0.0005, device='cuda:0')
layer1.
1.left.1.bias tensor(0.0267, device='cuda:0') tensor(0.0003, device='cuda:0')
layer1.
1.left.3.weight tensor(0.0311, device='cuda:0') tensor(7.7873e-05, device='cuda:0')
layer1.
1.left.4.weight tensor(0.2890, device='cuda:0') tensor(0.0008, device='cuda:0')
layer1.
1.left.4.bias tensor(0.0260, device='cuda:0') tensor(0.0002, device='cuda:0')
layer1.
2.left.0.weight tensor(0.0313, device='cuda:0') tensor(0.0001, device='cuda:0')
layer1.
2.left.1.weight tensor(0.3063, device='cuda:0') tensor(0.0005, device='cuda:0')
layer1.
2.left.1.bias tensor(0.0272, device='cuda:0') tensor(0.0002, device='cuda:0')
layer1.
2.left.3.weight tensor(0.0312, device='cuda:0') tensor(6.5418e-05, device='cuda:0')
layer1.
2.left.4.weight tensor(0.2905, device='cuda:0') tensor(0.0006, device='cuda:0')
layer1.
2.left.4.bias tensor(0.0249, device='cuda:0') tensor(0.0001, device='cuda:0')
layer2.
0.left.0.weight tensor(0.0313, device='cuda:0') tensor(0.0001, device='cuda:0')
layer2.
0.left.1.weight tensor(0.2959, device='cuda:0') tensor(0.0001, device='cuda:0')
layer2.
0.left.1.bias tensor(0.0254, device='cuda:0') tensor(0.0001, device='cuda:0')
layer2.
0.left.3.weight tensor(0.0289, device='cuda:0') tensor(1.8391e-05, device='cuda:0')
layer2.
0.left.4.weight tensor(0.2847, device='cuda:0') tensor(0.0002, device='cuda:0')
layer2.
0.left.4.bias tensor(0.0261, device='cuda:0') tensor(7.7771e-05, device='cuda:0')
layer2.
0.right.0.weight tensor(0.0574, device='cuda:0') tensor(0.0002, device='cuda:0')
layer2.
0.right.1.weight tensor(0.2861, device='cuda:0') tensor(0.0002, device='cuda:0')
layer2.
0.right.1.bias tensor(0.0261, device='cuda:0') tensor(7.7771e-05, device='cuda:0')
...
layer4.
2.left.4.weight tensor(0.2819, device='cuda:0') tensor(0.0020, device='cuda:0')
layer4.
2.left.4.bias tensor(0.0155, device='cuda:0') tensor(0.0041, device='cuda:0')
fc.weight tensor(
0.0233, device='cuda:0') tensor(0.0978, device='cuda:0')
fc.bias tensor(
0.0338, device='cuda:0') tensor(0.3471, device='cuda:0')

查看变量——如学习率:

ipdb> opt.lr
0.005

然后修改学习率,同时更改优化器中的学习率,并将参数保存

ipdb> opt.lr = 0.001
ipdb
> opt.lr
0.001
ipdb
> for p in optimizer.param_groups: p['lr']=opt.lr
ipdb
> model.save()
'checkpoints/resnet34_0417_14:55:38.pth'

然后c继续运行,会运行到之前89行的断点处

ipdb> c
20it [
48:31, 872.13s/it]> /opt/user/dogcat/chapter6/main.py(89)train()
88 optimizer.zero_grad()
1--> 89 score = model(input)
90 loss = criterion(score,target)

然后调用s进入model(input)内部,即model.__call__(input)

然后一直向下运行,找到调用内部forward函数的地方:

ipdb> s
--Call--
> /home/home/anaconda3/lib/python3.6/site-packages/torch/nn/modules/module.py(483)__call__()
482
--> 483 def __call__(self, *input, **kwargs):
484 for hook in self._forward_pre_hooks.values():
ipdb
> n
> /home/home/anaconda3/lib/python3.6/site-packages/torch/nn/modules/module.py(484)__call__()
483 def __call__(self, *input, **kwargs):
--> 484 for hook in self._forward_pre_hooks.values():
485 hook(self, input)
ipdb
>
> /home/home/anaconda3/lib/python3.6/site-packages/torch/nn/modules/module.py(486)__call__()
485 hook(self, input)
--> 486 if torch._C._get_tracing_state():
487 result = self._slow_forward(*input, **kwargs)
ipdb
>
> /home/home/anaconda3/lib/python3.6/site-packages/torch/nn/modules/module.py(489)__call__()
488 else:
--> 489 result = self.forward(*input, **kwargs)
490 for hook in self._forward_hooks.values():

然后再调用s进入forward函数内部

然后向下运行,运行过第一层pre层后查看它们的输出的平均值和标准差

ipdb> s
--Call--
> /opt/user/dogcat/chapter6/models/resnet34.py(71)forward()
70
---> 71 def forward(self, x):
72 x = self.pre(x)
ipdb
> n
> /opt/user/dogcat/chapter6/models/resnet34.py(72)forward()
71 def forward(self, x):
---> 72 x = self.pre(x)
73
ipdb
>
> /opt/user/dogcat/chapter6/models/resnet34.py(74)forward()
73
---> 74 x = self.layer1(x)
75 x = self.layer2(x)
ipdb
> x.data.mean(), x.data.std()
(tensor(
0.2565, device='cuda:0'), tensor(0.3837, device='cuda:0'))

然后使用u跳回上一层,直到model(input)处:

ipdb> u
> /home/home/anaconda3/lib/python3.6/site-packages/torch/nn/modules/module.py(489)__call__()
488 else:
--> 489 result = self.forward(*input, **kwargs)
490 for hook in self._forward_hooks.values():
ipdb
> u
> /opt/user/dogcat/chapter6/main.py(89)train()
88 optimizer.zero_grad()
1--> 89 score = model(input)
90 loss = criterion(score,target)

然后清除所有断点:

ipdb> clear
Clear all breaks
? y
Deleted breakpoint
1 at /opt/user/dogcat/chapter6/main.py:89

然后这就实现了在训练中间对参数进行更改然后再继续进行训练的操作

然后就能够使用命令c再继续运行

⚠️当然,这个时候要记得将之前创建的./tmp/debug文件夹删除,否则下次迭代又会进入debug模式

ipdb> c
30it [
1:16:42, 45.41s/it]

 

总结

1)调试

所以当我们想要进入debug模式,修改程序中的某些参数值或者想要分析程序时,就可以在运行是添加参数:–debug-file=./tmp/debug

然后就能够在训练过程中通过创建./tmp/debug文件夹,这样程序就会进入调试模式

调试完成后就能够删除./tmp/debug文件夹并在ipdb调试接口输入c继续运行训练程序

2)退出程序

如果想要退出程序,也可以使用这种方法,先创建./tmp/debug文件夹进入调试模式,然后输入quit在退出debug的同时退出程序

这种退出程序的方法,与ctrl+c的方法相比更加安全,因为这能够保证数据加载的多进程程序也能正确地退出,并释放内存、显存等资源

 

pytorch和ipdb结合能够完成很多其他框架不能完成或很难实现的功能,主要有下面的几部分:

1)通过debug暂停程序:当程序进入debug模式之后,将不再执行GPU和CPU运算,但是内存和显存集相应的堆栈空间不会释放

2)通过debug分析程序,查看每个层的输出,查看网络的参数情况:通过u\d\s等命令,能够进入指定的代码,通过n可以进行单步执行,从而可以看见每一层的运算结果,便于分析网络的数值分布等信息

3)作为动态图框架,pytorch拥有python动态语言解释执行的优点,我们能够在运行程序时,通过ipdb修改某些变量的值或属性,这些修改能够立即生效。例如可以在训练开始不久后根据损失函数调整学习率,不必重启程序

4)如果在IPython中通过%run魔法方法运行程序,那么在程序异常退出时,可以使用%debug命令,直接进入debug模式,通过u和d调到报错的地方,查看对应的变量。然后找出原因后修改相应的代码即可。

因为有时模型训练好几个小时后,却在要保存模型之前,因为一个小小的拼写错误异常退出。这时候最好的办法就是利用%debug进入调试模式,在调试模式中直接运行model.save()保存模型

在ipython中,%pdb魔术方法能够使得程序出现问题后,不用手动输入%debug而自动进入调试模式,建议使用

 

pytorch调用cuDNN报错时,报错信息诸如CUDNN_STATUS_BAD_PARAM,从这些报错信息内容很难得到有用的帮助信息,最好先利用CPU运行代码,此时一般会得到相对友好的报错信息。

常见的错误有如下几种:

1)类型不匹配问题:如CrossEntropyLoss的输入target应该是一个LongTensor,而很多人输入FloatTensor

2)部分数据忘记从CPU转到GPU:例如当model存放与GPU时,输入input耶需要转移到GPU才能输入到model中

还有可能是把多个model存放在一个list对象,而在执行model.cuda()时,这个list中的对象是不会被转移到CUDA上的,正确的用法是使用ModuleList替代

3)Tensor形状不匹配:此类问题一般是输入数据形状不对,或是网络结构设计有问题,一般通过u命令跳到指定代码,查看输入和模型参数的形状即可得知

4)程序正常运行、没有报错,但是模型无法收敛的问题:例如二分类问题,交叉熵损失一直徘徊在0.69附近(ln2),或是数值出现溢出等问题

此时可以进入debug模式,用单步执行看看每一层输出的均值或方差,观察从哪一层开始出现数值异常。还要查看每个参数梯度的均值和方差,看看是否出现梯度消失或梯度爆炸的问题。

但是一般在激活函数前增加BatchNorm层、合理的参数初始化、使用Adam优化器,学习率设为0.001,基本上就能确保模型在一定程度上收敛

 


推荐阅读
  • Mysql MySqlBulkLoader在.NET平台下的批量插入
    批量导入publicboolTranBatchImpo ... [详细]
  • (一)javax.mail.Session:Session类代表JavaMail中的一次邮件会话.每个基于JavaMail的应用程序至少有一次会话,也可以产生多次会话.发送邮件之前 ... [详细]
  • 关于如何快速定义自己的数据集,可以参考我的前一篇文章PyTorch中快速加载自定义数据(入门)_晨曦473的博客-CSDN博客刚开始学习P ... [详细]
  • XShell连接不了虚拟机
    本机安装好虚拟机和centeros;使用xshell连接:linuxCouldnotconnectto'127.0.0.1'(por ... [详细]
  • 自定义RecyclerView添加EmptyView
    你知道RecyclerView里没有Em ... [详细]
  • nginx+tomcat session 共享
    *tomcat1192.168.10.153*tomcat2192.168.10.154Tomcat工作模式必须为Nio模式。##添加如下内容,注意更换address ... [详细]
  • 目录读写文本格式序列化使用数据库合并数据集重塑和轴向旋转数据转换读写文本格式CSV(Comma-SeparatedValues)格式的文件是指以纯文本形式存储的表格数据,这意味着不 ... [详细]
  • 元类print(type(abc))print(type(True))print(type(100))print(type([1,2,3]))print(type({na ... [详细]
  • NSSROUND#8[Basic]
    文章目录一、[NSSRound#8Basic]MyDoor二、[NSSRound#8Basic]Upload_gogoggo三、[NSSRound#8Basic]MyPage四、[ ... [详细]
  • mysql oneproxy稳定吗_Mysql 中间件 oneProxy总结
    建议使用之前把官方的文档全部通读一遍这里提供一个我的网盘地址oneproxy百度网盘0.先对oneproxy有个大概的了解,知道他所处的位置1.MySQL服务器创建t ... [详细]
  • 本文介绍了在Python3中如何使用选择文件对话框的格式打开和保存图片的方法。通过使用tkinter库中的filedialog模块的asksaveasfilename和askopenfilename函数,可以方便地选择要打开或保存的图片文件,并进行相关操作。具体的代码示例和操作步骤也被提供。 ... [详细]
  • 本文介绍了如何使用python从列表中删除所有的零,并将结果以列表形式输出,同时提供了示例格式。 ... [详细]
  • 服务器上的操作系统有哪些,如何选择适合的操作系统?
    本文介绍了服务器上常见的操作系统,包括系统盘镜像、数据盘镜像和整机镜像的数量。同时,还介绍了共享镜像的限制和使用方法。此外,还提供了关于华为云服务的帮助中心,其中包括产品简介、价格说明、购买指南、用户指南、API参考、最佳实践、常见问题和视频帮助等技术文档。对于裸金属服务器的远程登录,本文介绍了使用密钥对登录的方法,并提供了部分操作系统配置示例。最后,还提到了SUSE云耀云服务器的特点和快速搭建方法。 ... [详细]
  • python3 nmap函数简介及使用方法
    本文介绍了python3 nmap函数的简介及使用方法,python-nmap是一个使用nmap进行端口扫描的python库,它可以生成nmap扫描报告,并帮助系统管理员进行自动化扫描任务和生成报告。同时,它也支持nmap脚本输出。文章详细介绍了python-nmap的几个py文件的功能和用途,包括__init__.py、nmap.py和test.py。__init__.py主要导入基本信息,nmap.py用于调用nmap的功能进行扫描,test.py用于测试是否可以利用nmap的扫描功能。 ... [详细]
  • 基于词向量计算文本相似度1.测试数据:链接:https:pan.baidu.coms1fXJjcujAmAwTfsuTg2CbWA提取码:f4vx2.实验代码:imp ... [详细]
author-avatar
qaqa
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有