本文已参加「新人创造礼」活动,一起开启创造之路。

一、pdb 有2种用法

pdb:python debugger

1、非侵入式办法 (不用额定修改源代码,在指令行下直接运转就能调试)

python3 -m pdb filename.py

2、侵入式办法 (需要在被调试的代码中增加以下代码然后再正常运转代码)

import pdb pdb.set_trace()

当你在指令行看到下面这个提示符时,阐明现已正确打开了pdb

(Pdb)

二、pdb 根本指令

指令 解释
break 或 b 设置断点
continue 或 c 继续履行程序
list 或 l 检查当前行的代码段
step 或 s 进入函数(进入 for 循环用 next 而不是用 step)
return 或 r 履行代码直到从当前函数返回
next 或 n 履行下一行
up 或 u 返回到上个调用点(不是上一行)
p x 打印变量x的值
exit 或 q 中止调试,退出程序
help 帮助

在实践运用中发现,用shell脚本运转python文件时,可能无法用pdb调试,会退出。此刻只能直接运转py文件来调试。

三、在指定文件的指定方位,用break指令设置断点

3.1 在本文件中的指定方位设置断点

比如下面的比如,要想进入到模型的 forward() 办法中检查前向传达进程中的数据处理进程,只能在 forward() 的榜首行(即26行)设置断点,pdb.set_trace()

但有时候模型很杂乱,用这种办法会导致程序报错直接退出(我也不知道是什么原因),那么咱们就能够考虑用 break 指令在这一行刺进断点,使得程序运转到 forward() 时就会停下来。

import torch
import torch.nn as nn
import pdb
class EncoderLayer(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(4, 10, (3, 3))
        self.conv2 = nn.Conv2d(10, 4, (3, 3))
        self.relu = nn.ReLU()
    def forward(self, x):
        x=self.relu(self.conv1(x))
        return self.relu(self.conv2(x))
class Encoder(nn.Module):
    def __init__(self,num_layers):
        super().__init__()
        # encoders 由 num_layers个 EncoderLayer子层组成,每个子层结构相同,但参数不必定相同。
        self.ModelList = nn.ModuleList([EncoderLayer() for _ in range(num_layers)])
    def forward(self, x):
        # ModuleList是一个list,只能经过list的操作办法(如用for循环、下标索引等)进行forward计算。
        for layer in self.ModelList:
            x = layer(x)
        return x
if __name__=="__main__":
    pdb.set_trace()   
    input = torch.rand(5, 4, 30, 30)
    model = Encoder(num_layers=4)
    output = model(input)

具体办法: (1)首先在前面的任意一行设置 pdb.set_trace() ,使得程序停下来。 (2)输入 break 26 就能够了。如图:

python 调试工具 pdb 的基本用法(Python Debugger)
这样断点就设置成功了,程序运转到forward()就会停下来。

这儿的26是行数,需要注意的是 断点方位不能是注释,比如咱们在25行(注释行)设置断点,就会失败:

python 调试工具 pdb 的基本用法(Python Debugger)
总结一下,在同一个文件中设置断点的指令是:

break line

3.2 在其他文件中的指定方位设置断点

如果想要设置的断点不在初始运转文件里边呢,怎样在其他文件顶用break指令设置断点呢?咱们看这个比如:

把3.1的代码分为三个py文件,放下同一途径下:

  ![在这儿刺进图片描绘](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/4b5d476ba5b14b0ba541d78930b9704a~tplv-k3u1fbpfcp-zoom-1.image)

看一下每个文件的内容:

run.py:

初始的 pdb.set_trace() 设置在run.py中。

import torch
from encoder import Encoder
import pdb
if __name__=="__main__":
    pdb.set_trace()
    input = torch.rand(5, 4, 30, 30)
    model = Encoder(num_layers=4)
    output = model(input)

encoder.py:

from encoder_layer import EncoderLayer
import torch.nn as nn
class Encoder(nn.Module):
    def __init__(self,num_layers):
        super().__init__()
        # encoders 由 num_layers个 EncoderLayer子层组成,每个子层结构相同,但参数不必定相同。
        self.ModelList = nn.ModuleList([EncoderLayer() for _ in range(num_layers)])
    def forward(self, x):
        # ModuleList是一个list,只能经过list的操作办法(如用for循环、下标索引等)进行forward计算。
        for layer in self.ModelList:
            x = layer(x)
        return x

encoder_layer.py:

import torch.nn as nn
class EncoderLayer(nn.Module):
    def __init__(self):
        super().__init__()
        self.conv1 = nn.Conv2d(4, 10, (3, 3))
        self.conv2 = nn.Conv2d(10, 4, (3, 3))
        self.relu = nn.ReLU()
    def forward(self, x):
        x=self.relu(self.conv1(x))
        return self.relu(self.conv2(x))

现在咱们运转 run.py ,然后在 encoder.py 的第12行 设置断点,即

for layer in self.ModelList:

指令为:

break encoder.py:12

即 break filename:line

python 调试工具 pdb 的基本用法(Python Debugger)
咱们能够看到,程序能够从 output = model(input) 进入到 forward() 中:
python 调试工具 pdb 的基本用法(Python Debugger)
这样能够很方便地进行调试。

如果初始断点与目标断点不在同一个目录下的文件中,也能够经过相对途径下的文件名设置断点,如:

(Pdb) break ../transformer/asr_model.py:91
Breakpoint 1 at /local/wenet/examples/aishell/s0/wenet/transformer/asr_model.py:91
(Pdb)

四、运用 pdb 时发现的问题

4.1 运用软链接时,pdb 显现的文件途径与实践途径不一致的问题

如图能够发现,pdb 有三行组成,榜首行时文件途径,第二行是当前履行的代码行,第三行是输入指令行。

在存在软链接时,pdb显现的途径是软链接指向的途径,但实践上的代码途径是拷贝了软连接内容的途径,这两个途径不一样,必定要注意。

python 调试工具 pdb 的基本用法(Python Debugger)

4.2 pdb有时候无法在模型的 forward() 办法中加入断点

pdb有时候无法用 pdb.set_trace() 在模型的 forward() 办法中加入断点,报错内容为:

Compiled functions can’t take variable number of arguments or use keyword-only arguments with defaul

大概意思是 “编译后的函数不能接受数量可变的参数,也不能在default中运用仅关键字参数。”

不懂啥意思,这个问题也没有处理。

五、程序奔溃后的过后调试:pdb.pm()

前面所述都是在程序开始运转时就刺进断点,用pdb进行调试,即事前调试。其实 pdb 还能够进行过后调试,即在程序有bug运转奔溃后用python调试器进行检查。

比如 test.py 显然是有 bug 的:

# test.py
def add(n):
    return n+1
add("hello")

直接运转:

python test.py

程序奔溃:

F:\PycharmProjects\pytorch_practice>python test.py
Traceback (most recent call last):
  File "test.py", line 4, in <module>
    add("hello")
  File "test.py", line 2, in add
    return n+1
TypeError: can only concatenate str (not "int") to str

这样咱们是无法用pdb进行调试的。那么当程序溃散后,咱们该怎样去调试呢?

咱们能够用下面这个指令进行简略调试:

python -i test.py

-i 选项能够让程序完毕后打开一个交互式shell,如下:

F:\PycharmProjects\pytorch_practice>python -i test.py
Traceback (most recent call last):
  File "test.py", line 4, in <module>
    add("hello")
  File "test.py", line 2, in add
    return n+1
TypeError: can only concatenate str (not "int") to str
>>>

现在咱们发现程序完毕后出现了 >>> 符号,这就是python调试器。

输入指令:

import pdb pdb.pm()

其中 pdb.pm() 用于程序发生反常导致奔溃后的过后调试,能够跟踪反常程序最后的堆在信息。

履行指令后得到:

TypeError: can only concatenate str (not "int") to str
>>> import pdb
>>> pdb.pm()
> f:\pycharmprojects\pytorch_practice\test.py(2)add()
-> return n+1
(Pdb)

能够发现,pdb.pm() 现已追踪到了导致程序奔溃的句子:return n+1

此刻能够打印 n 的值进行检查:

(Pdb) p n
'hello'
(Pdb) q
>>> quit()
F:\PycharmProjects\pytorch_practice>

q 表明退出pdb调试,quit() 表明退出 python 调试器。