在程式世界裡,抓蟲(debug)是個不可避免的坑,能不能高效的除錯技巧更是每位工程師和資料科學家痛 🥲。最近因為工作的關係,接手程式需要進行優化,在嘗試理解的過程中碰到一個除錯強大工具 ipdb,跟大家分享!
ipdb 是一個第三方互動式除錯工具。ipdb 建立了一個互動式 shell 讓我們可以輕鬆地進行程式測試與除錯,非常方便。

How to Debug

相信大家都有遇過:「程式有 error 但不知道為什麼出錯 … 程式跑起來了!… WHY!!!」的囧境 Orz

初學者在 debug 時可能會使用 printlogging 打印出段落中的訊息以判斷程式是否如預期運作,或是加入 Exception Handlingassertions 做異常處理,更有經驗的工程師可能能更善用如 PyCharm、VSCode 這些 IDE 所提供的除錯功能或是撰寫單元測試。而我們也可以透過 ipdb 這個工具來幫助我們了解目前環境變數的變化。那,ipdb 究竟是什麼?

What’s ipdb

ipdb 全名 IPython-enables python debugger,其透過 IPython 擴展了 pdb 原有的基本功能,讓我們可以藉由 ipdb 建立的互動式 shell 執行 python 程式,不論是想看函數參數傳入內容是否正確、dataframe 或是 tensor 的 shape,甚至想寫額外測試的程式都可以輕鬆做到。

使用 ipdb 就跟使用 pdb 時一樣簡單,將套件載入後,於想設置斷點的地方添加以下程式片段:

python
1
2
3
!pip install ipdb
import ipdb
ipdb.set_trace()

也可以直接在命令列輸入以下指定啟動 ipdb 調校模式:

python
1
python -m ipdb script.py

ipdb cheatsheet

  • h:print 出可用的命令或特定命令
  • q:退出程式
  • n:繼續執行,直到達到單前函數中的下一行,或著他返回
  • c:繼續執行直到下個斷點或結束
  • a:print 出當前函數的參數
  • p:print 出變數的值

當進入 ipdb 的互動式環境後,可以調用以上指令進行操作,例如執行下一行或是退出整個程式。

ipdb 實戰演練

這裡示範使用ipdb.set_trace() 該如何進行除錯,通常會是在命令列作使用,而我們也可以在 jupyter notebook 做簡單測試,
我們使用 PyTorch 與 iris 資料集,建立一個很簡單的神經網路做預測模型。

python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import ipdb
import pandas as pd
import numpy as np
import statsmodels.api as sm
from sklearn.preprocessing import LabelEncoder
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import TensorDataset, DataLoader
le = LabelEncoder()

iris = sm.datasets.get_rdataset('iris').data
iris.iloc[:, -1] = le.fit_transform(iris.iloc[:, -1])
iris = np.array(iris)
dataset = TensorDataset(torch.tensor(iris[:, :-1], dtype=torch.float), torch.tensor(iris[:, -1], dtype=torch.long))
dataloader = DataLoader(dataset, batch_size=2, shuffle=True)

class myNN(nn.Module):
def __init__(self):
super(myNN, self).__init__()
self.fc1 = nn.Linear(4, 10)
self.fc2 = nn.Linear(10, 10)
self.fc3 = nn.Linear(10, 3)

def forward(self, x):
x = torch.relu(self.fc1(x))
x = torch.relu(self.fc2(x))
x = self.fc3(x)
return x

model = myNN()
loss_func = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)

for epoch in range(100):
for i, data in enumerate(dataloader):
inputs, labels = data
ipdb.set_trace()
outputs = model(inputs)
loss = loss_func(outputs.squeeze(), labels)
optimizer.zero_grad()
loss.backward()
optimizer.step()

print(f'epoch: {i}')

在訓練迴圈中,每個 batch 會作為 inputs 餵給模型輸出預測結果 ouputs,我們可以在 outputs = model(inputs) 前利用 ipdb.set_trace() 設置斷點,
目的是為了查看 inputs 資料長什麼樣子,因為在 CV、NLP 模型中,資料時常會先做許多 transform 函數才進入模型,此時就可能會想要確認模型輸入的資料細節。

當程式碼跑到該行時,就會看到跳出 ipdb 的互動式 shell,可以在裡面下 cheatsheet 提到的參數,或是寫 Python 程式,
例如可以查看 inputs.shape 或是直接打印出資料內容。而輸入 c 會跳到下一個 epoch,n 會執行下一行,q 則會直接退處整個程式。

ipdb 缺點

  1. Limited Debugging:ipdb 雖然具有互動式調校非常方便,但對於更複雜的問題(例如:multithreaded application),ipdb 的調校能力將會受到限制。
  2. Slow:因為是逐步執行,可能會較耗時,因此可能不適合大型的應用產品、程式

總結來說,ipdb 提供非常直觀、輕鬆的方式,用交互模式讓開發人員可以進行除錯,雖然在大型、複雜程式的情境可能沒那麼適合,但依舊是一個非常強大方便得工具。

參考資料

freeCodeCamp Python Debugging Handbook - How to Debug Your Python Code
台大資訊 深度學習之應用|ADL TA Recitation: PyTorch Debugging 有BUG怎麼辦!?