『教程』TensorFlow代碼調試模塊tfdbg


轉載自公眾號機器之心

 

Debugging 是程序員必備技能,TensorFlow 程序員也不例外。然而 TensorFlow 的運行模式是先構造一張 graph,再執行 session.run(),這就為調試帶來一些困難。普通調試工具如 pdb 只能看到 graph 外部的變量和控制流程,無法深入 graph 內部一探究竟。

 

TensorFlow debugger(簡稱 tfdbg)是 TensorFlow 的專用調試器,它可以讓你看到運行 TensorFlow graph 時的內部結構體和狀態。有了這個神器就如同調試普通 Python 程序一樣調試 TensorFlow 程序了!

 

這里看個簡單例子(lms.py),使用 LMS 算法估計線性濾波器權值。LMS 是數字信號處理中最基本的自適應濾波算法,結構簡單,計算量低,便於硬件實現,適用於實時信號處理場景,但學習速率選取不當時容易造成發散,出現 Inf 和 NaN 值。以下程序就存在這個問題:

import numpy as np
import tensorflow as tf
from tensorflow.python import debug as tfdbg

H = 128    # 濾波器長度
L = 10240  # 輸入信號長度
miu = 1    # 學習速率

# 載入濾波器權值
filter = np.random.randn(H)
# 載入信號值
sig_in = np.random.randn(L)

weights = tf.Variable(tf.zeros([H], dtype=tf.float32))  # 訓練權值
input_vec = tf.placeholder(tf.float32, shape=(H))       # 輸入信號窗口
prod = tf.reduce_sum(tf.multiply(input_vec, weights))   # 輸出信號
desired = tf.reduce_sum(tf.multiply(input_vec, filter)) # 期望信號
err = tf.nn.l2_loss(desired - prod)                     # 誤差信號
opt = tf.train.GradientDescentOptimizer(miu).minimize(err) # 梯度下降優化器
mse = tf.nn.l2_loss(weights - filter)                   # 訓練權值與預期權值的 MSE

with tf.Session() as sess:
  sess = tfdbg.LocalCLIDebugWrapperSession(sess)                      # 被調試器封裝的會話
  sess.add_tensor_filter("has_inf_or_nan", tfdbg.has_inf_or_nan)      # 調試器添加過濾規則
  tf.global_variables_initializer().run()                             # 變量初始化
  print('Initialized!')  for step in xrange(L - H):
    sess.run(opt, feed_dict={input_vec:sig_in[step:(step+H)]})        # 輸入信號窗口每次滑動一個單位
    print sess.run(mse)                                               # 打印輸出,觀察訓練權值是否收斂到與預期權值一致

使用 TensorFlow 1.3.0 運行上述程序。

 

進入交互式調試界面,這里可以輸入調試命令,常用的有:

tfdbg> run

運行一次 session.run(),並將所有內部 tensor 導出,界面如下:

這時可以用鼠標點擊“Tensor name”,查看相應的 tensor 詳情(等價為輸入命令:pt tensor_name)。下圖為點擊“Mul_1/y:0”之后的界面:

可以按鍵盤上“Page Up/Page Down”按鍵來翻頁顯示,tfdbg> 后輸入“@數字”可以直接跳到對應的元素位置,如 “@125” 后,界面跳轉如下:

這時可以查看 Mu_1/y:0[125] 元素的值。

 

我們如果對 graph 中某個 node 感興趣,可以使用 ni 命令查看:

上圖顯示 Mul:0 節點對應 Op 為 Mul,運行時使用 gpu:0 設備,有兩個輸入,一個為 placeholder,一個為 Variable;下一個節點為 Sum。通過這些信息可以辨識調試器中某個特定節點對應程序中代碼位置。為了提高辨識度,在程序中使用 tf api 構建 graph 時,最好加入 name 參數。

 

通過 ls 命令可以查看創建節點的框架源碼:

從上圖看到源碼和創建的節點統計值,鼠標點擊源碼則自動跳轉到創建代碼處,適合跟蹤框架代碼。

 

前面手動查找 Tensor 值的方式比較低效,還可以使用過濾器幫助查找異常值。在代碼中我們已經創建了一個名為“has_inf_or_nan” 的過濾器,在運行時指定參數:run -f has_inf_or_nan 則使用該過濾器,檢查運行時的 tensor 出現 NaN 或 Inf 的情況。

調試器運行到第 30 輪時發現 L2Loss_1:0 tensor 中出現了 NaN/Inf,程序中斷,我們可以輸入“pt L2Loss_1:0” 打印這個 tensor:

它的值為 Inf,為什么會變為 Inf 呢?我們繼續跟蹤它前一級 tensor(通過命令 “ni L2Loss_1:0” 得知前一個 tensor 為 sub_1,再使用 “pt sub_1” 命令打印 sub_1 的值)

發現這些值都非常大,再計算 L2Loss 會超過 float32 能表示的最大值,所以生成了 Inf。

繼續向前跟蹤 tensor,可以找到這些異常大的值來源為 Variable,即訓練的權值。這些值為什么會變得這么大?一定是迭代更新的步子太大造成發散。找到了原因,解決起來就很簡單了,我們將代碼第 7 行學習速率(miu = 1)調小一些,比如 0.01,保存后再次運行(先 python lms.py 進入 tfdbg> ,再運行 run -f has_inf_or_nan),這時會發現程序一直運行到結束,不會中斷,說明沒有 NaN/Inf 產生,bug 已經消除。

 

通過上述例子,相信童鞋們可以掌握 tfdbg 基本用法,並可以舉一反三調試自己程序中的 bug。

 

 

最近 TensorFlow 又有一大利好,開始支持 Eager Execution 模式,可以直接執行 tf api,無需使用 session.run(),這樣方便了程序調試。不過該功能尚在開發階段,不可避免會踩到一些坑。願意嘗鮮的童鞋可以體驗下:

 

Docker 安裝:docker pull tensorflow/tensorflow:nightly

PIP 安裝:pip install tf-nightly

測試程序:

import tensorflow as tf
import tensorflow.contrib.eager as tfe
tfe.enable_eager_execution()
x = tf.add(1, 1)
y = tf.constant([1, 2, 3])
z = x + y
print(z)
 

參考

【1】Debugging TensorFlow Programs, https://www.tensorflow.org/programmers_guide/debugger

【2】TensorFlow Eager Execution, https://github.com/tensorflow/tensorflow/blob/master/tensorflow/contrib/eager/python/g3doc/guide.md

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM