|
注意:本文方法僅限于調(diào)試安裝時附帶py源碼的庫,如sklearn。
引入
用sklearn中的sklearn.feature_extraction.text.TfidfTransformer來獲取TF特征,但發(fā)現(xiàn)sklearn的計算結(jié)果與我手工計算結(jié)果不一樣。雖然能在github上找到sklearn的源碼。但不能動態(tài)調(diào)試,就無法直觀的看到結(jié)果。
那么問題來了,我們怎么樣才能動態(tài)調(diào)試Python的第三方庫(比如sklearn)呢?怎么樣才能看到第三方庫中源碼動態(tài)運(yùn)行的中間結(jié)果?
假設(shè)我的代碼如下:
# 原始語料,3個文本
strs_train =[
'God is love',
'OpenGL on the GPU is fast',
'Doctor David is PHD']
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.feature_extraction.text import CountVectorizer
# 先提取 Bags of words特征
count_vect = CountVectorizer()
X_train_counts = count_vect.fit_transform(strs_train)
# 再基于Bags of words特征,變換為TF特征
tf_transformer = TfidfTransformer(use_idf=False).fit(X_train_counts)
X_train_tf = tf_transformer.transform(X_train_counts)
print(X_train_tf.todense())
我怎么樣才能看到函數(shù)sklearn.feature_extraction.text.TfidfTransformer.transform()計算的中間結(jié)果呢?
Python調(diào)試基礎(chǔ)
Python自帶了一個用于調(diào)試代碼的模塊pdb。它支持?jǐn)帱c設(shè)置,單步調(diào)試,進(jìn)入函數(shù)調(diào)試,查看代碼片段,查看變量值,動態(tài)改變變量值。
下面兩行代碼就能給程序加斷點:
import pdb
pdb.set_trace()
加了斷點,運(yùn)行程序,當(dāng)程序停下,就可以用下面幾個命令,在SHELL中調(diào)試代碼。
| 命令 |
含義 |
| c |
繼續(xù)執(zhí)行代碼 |
| n |
下一步 |
| r |
執(zhí)行代碼,從當(dāng)前函數(shù)返回 |
| s |
進(jìn)入函數(shù) |
| b |
下斷點 |
調(diào)試Python第三方庫
我們用pdb,就可以在第三方庫中下斷點,并進(jìn)行調(diào)試。這里以調(diào)試sklearn中的sklearn.feature_extraction.text.TfidfTransformer為例,給出如下步驟。
先利用如下Python代碼找到sklearn源碼位置。我的位置在C:\\Users\\biny\\Anaconda3\\lib\\site-packages\\sklearn。
import sklearn, os
path = os.path.dirname(sklearn.__file__)
- (2)刪掉Python預(yù)編譯的
字節(jié)碼
Python程序在運(yùn)行時,為了提高運(yùn)行速度,Python解釋器先將.py代碼編譯為byte code(字節(jié)碼),再有Python虛擬機(jī)來執(zhí)行字節(jié)碼。
下次再運(yùn)行同一程序時,若.py代碼沒有改變,則省略將.py代碼編譯為字節(jié)碼的步驟,直接運(yùn)行上次已編譯好的字節(jié)碼。
這些字節(jié)碼,會被存于__pycache__文件夾下,和.pyc文件。按照原理,這個步驟是不需要做的,不過刪掉字節(jié)碼在運(yùn)行自己的程序,如果不會出現(xiàn)新的字節(jié)碼文件,說明你的第三方庫位置找錯了。這樣能方便我們發(fā)現(xiàn)錯誤。
根據(jù)第三方庫的位置,找到sklearn.feature_extraction.text.TfidfTransformer.transform()函數(shù)所在.py文件。并用pdb在函數(shù)開頭加上斷點(如下)。
def transform(self, X, copy=True):
import pdb
pdb.set_trace()
if hasattr(X, 'dtype') and np.issubdtype(X.dtype, np.float):
# preserve float family dtype
X = sp.csr_matrix(X, copy=copy)
else:
# convert counts or binary occurrences to floats
X = sp.csr_matrix(X, dtype=np.float64, copy=copy)
運(yùn)行我的代碼,停在第三方庫中,就可以用pdb命令調(diào)試第三方代碼了。
此時代碼已經(jīng)運(yùn)行并進(jìn)入第三方庫中,停止在斷點處:
C:\mine\tmp\debug_py_3rd_lib>python main.py
c:\users\biny\anaconda3\lib\site-packages\sklearn\feature_extraction\text.py(1018)transform()
-> if hasattr(X, ‘dtype’) and np.issubdtype(X.dtype, np.float):
(Pdb)
用n命令(next),讓代碼單步運(yùn)行到關(guān)鍵點:
c:\users\biny\anaconda3\lib\site-packages\sklearn\feature_extraction\text.py(1042)transform()
-> if self.norm:
(Pdb) n
直接輸入要查看的中間變量(X.data),停下的這行代碼是即將執(zhí)行的,我們可以看到執(zhí)行前的變量值:
c:\users\biny\anaconda3\lib\site-packages\sklearn\feature_extraction\text.py(1043)transform()
-> X = normalize(X, norm=self.norm, copy=False)
(Pdb) X.data
array([ 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.])
繼續(xù)執(zhí)行代碼(n命令),然后可以看到中間變量值被改變。也能看到這個改變是因為做了normalize。
(Pdb) n
c:\users\biny\anaconda3\lib\site-packages\sklearn\feature_extraction\text.py(1045)transform()
-> return X
(Pdb) X.data
array([ 0.57735027, 0.57735027, 0.57735027, 0.40824829, 0.40824829,
0.40824829, 0.40824829, 0.40824829, 0.40824829, 0.5 ,
0.5 , 0.5 , 0.5 ])
記住調(diào)試結(jié)束后,一定要在第三方源碼中刪掉pdb斷點那兩行代碼!
參考
|