當前位置:
首頁 > 最新 > 如何僅使用TensorFlow C+來訓練深度神經網路

如何僅使用TensorFlow C+來訓練深度神經網路

作者|Florian Courtial

譯者|Debra

編輯|Emily

AI 前線導讀:訓練神經網路是一件十分複雜,難度非常大的工作,有沒有可能讓訓練的過程簡單便利一些呢?有人突發奇想,嘗試僅僅使用 TensorFlow C ++ 來進行這項工作。這樣做的效果如何呢?我們來看看 Florian Courtial 用 TensorFlow C ++ 構建 DNN 框架的示例來了解一下吧。

更多乾貨內容請關注微信公眾號「AI 前線」,(ID:ai-front)

正如你所知,TensorFlow(TF)的核心由 C ++ 構建,但是如今還是 Python API 使用起來比較便利一些。

我寫這篇博文的目標,是僅使用 TF C ++ API 來構建基礎的深度神經網路(DNN),然後再嘗試僅使用 CuDNN 實現這一功能。但從使用 TF C ++ 構建神經網路開始,我就意識到即使是在簡單的 DNN 中,也有很多東西會丟失。

請記住這一點,進行外部操作訓練網路肯定是不可行的,因為你很可能將丟失梯度運算。我目前正在嘗試將梯度運算從 Python 改為 C ++。

在這篇文章中,我們將示例如何建立一個深度神經網路,並通過車齡、里程和燃料類型來預測一輛寶馬 Serie 1 的價格。我們將僅使用 TensorFlow C ++,並描述缺失的訓練細節。目前 C ++ 中沒有優化器,所以現在訓練的代碼沒有那麼性感,但是將來我可能會添加。

所有代碼可以在 Github 上找到。

重建 TensorFlow

我們將用 TensorFlow C ++ 代碼進行編碼,雖然可以使用現成編譯的庫,但是我相信有些人在這個過程中會由於庫環境的特殊性而遇到麻煩。從頭開始構建 TensorFlow 會避免這些問題,而且需要確保使用的是最新版本的 API。

接下來只需要安裝 bazel構建工具就可以了,然後遵照你的操作系統指示進行操作。在 OSX上,使用 brew就足夠了:(左右滑動可看到全部代碼)

因為是從頭構建 TF,我們還需要張量源:

然後進行配置安裝,你可以選擇 GPU,也可以不選擇,要做到這一點需要運行配置腳本:

現在我們來創建將接收模型代碼的文件,並開始首次構建 TF。請注意,第一次構建需要相當長的時間(10 - 15分鐘)。

非核心的 C ++ TF代碼在 / tensorflow / cc中,這是我們創建模型文件的位置,另外還需要一個 BUILD文件,以便 bazel可以建立 model.cc。

我們把 bazel指示添加到 BUILD文件中:

一般它會使用 model.cc建立一個二元模型。現在,我們已經做好為模型編寫代碼的所有準備。

讀取數據

如果你還記得的話,這些數據是法國網站 leboncoin.fr報廢的,而不是經過清理和規範化,並保存到 CSV文件中的數據。我們的目標是讀取這些數據。用來規範化數據的元數據被保存在 CSV文件的第一行,我需要它們重新構建網路輸出的價格。我創建了一個 data_set.h和 data_set.cc文件,防止代碼被打亂。它們將從 CSV文件中生成一個二維數組,用來訓練神經網路。

我把代碼放在這裡,但因為它與我們的目標沒有多大相關性,所以無需在閱讀代碼上多花時間。

data_set.h

我們還需要將這兩個文件添加到 BUILD 文件中。

建模

第一步是將 CSV 文件讀取為兩個張量,x 為輸入,y 為預期結果。我們使用之前定義的 DataSet 類。您可以在這裡下載 CSV 數據集。

我們需要類型和形狀來定義一個張量。在 data_set 對象中,x 以扁平的方式保存,這就是為什麼我們將尺寸縮減至 3(每輛車有 3個特徵)。然後我們使用 std :: copy_n 將數據從 data_set 對象複製到 Tensor(Eigen :: TensorMap)的底層數據結構中。現在可以開始建模了。

使用以下方法,我們可以輕鬆地調試張量:

C ++ API 的獨特之處在於,我們需要一個 Scope 對象來保存圖構造的狀態,這個對象將在運算中傳遞。

我們將得到兩個佔位符,x 包含汽車功能和每輛車的相應價格。

該網路有兩個隱藏層,因此我們將得到三個權重矩陣和三個偏差矩陣。而 Python 是在 C ++ 下完成的,我們必須定義一個變數和一個 Assign 節點,以便為該變數分配一個默認值。通過使用 RandomNormal 來初始化變數,我們獲得正態分布的隨機值。

然後使用 Tanh 作為激活函數建立三個層。

添加一個 L2 正則化。

最後,我們計算一下損失,即預測和實際價格 y 之間的差異,再加上正則化。

至此,我們完成了正向傳播,並準備好啟動反向傳播部分。第一步是使用一個函數調用,將正向操作的梯度添加到圖形中。

我們將所有計算每個變數損失的梯度所需的運算都添加到圖中,初始化一個空的 grad_outputs 向量,當在 TensorFlow session 中使用時,它將保存為生成變數梯度的節點,grad_outputs [0] 將生成梯度損失 wrt w1,grad_outputs [1]grad 損失 wrt w2,按照 的順序,傳遞給 AddSymbolicGradients 。

現在,我們得到一個 grad_outputs 節點列表。在 TensorFlow session 中使用時,每個節點計算一個變數的損失梯度,之後被用來更新變數。每個變數設置為一行,使用最簡單的梯度下降來進行更新。

我們的網路已做好在 Session 中啟動的準備,Python 優化器 API 的最小化功能基本上包含了在函數調用中的計算和應用梯度。

我們對一個 ClientSession 和一個命名為 output 的 Tensor 進行初始化,使之接收網路的輸出。

然後初始化變數,在 Python 中,調用 tf.global_variables_initializer()就足夠了,因為在構建圖的過程中,我們保留了所有變數的列表。使用 C ++,我們必須保留變數列表。每個 RandomNormal 輸出將被分配給 Assign 節點中定義的變數。

現在,我們可以循環訓練步驟。在示例中,我們將做 5000 步訓練。第一步是使用損失節點進行正向傳播,輸出為網路損失。每隔 100 步,我們記錄下損失值,網路的強制性屬性會導致損失值減小。之後計算梯度節點並更新變數。如果你還記得,我們的梯度節點已被用作 ApplyGradientDescent 節點的輸入,所以為了運行 apply_ 節點,我們需要首先計算梯度,然後將其應用於正確的變數。

到這一步,該網路經過訓練,已經可以嘗試預測一輛車的價格,也就是所謂的推理。我們來預測一下一台柴油發動機,車齡為 7 年,里程 11 萬公里的寶馬 Seria 1 的價格。要做到這一點,我們需要使用 layer_3 節點,以汽車數據作為輸入 x(基本上是一個正向傳播)。因為我們此前曾經對網路進行過 5000步 的訓練,所以權重會有一個學習值,產生的結果是非隨機的。

我們不能直接使用汽車的屬性,因為我們的網路從規範化的屬性中學習,同樣還必須經過相同的規範化過程。鑒於此,DataSet 使用 CSV 讀取期間載入的數據集元數據來處理該步驟。

該網路生成一個介於 0和 1 之間的值,data_set 輸出還負責使用數據集元數據,將該值轉換回可讀的價格。這個模型可以使用命令 bazel run -c opt // tensorflow / cc / models:model 運行,如果 TensorFlow 是重建的,很快就可以得到以下輸出:

該模型預測的汽車價格為 13377.7 歐元。多次運行模型可能會得到不同的結果,有時差距非常大,如 8000€ 與 17000€。這是由於我們只用了三個屬性來描述汽車,而且網路架構也非常簡單。

正如我之前所說,C ++ API 還在不斷改進,我們在將來可以找到更簡單的方法。如果你知道能改善此方案的解決方法,歡迎留下評論。

https://matrices.io/training-a-deep-neural-network-using-only-tensorflow-c/


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 AI漫遊 的精彩文章:

AI前線一周熱聞盤點:Uber提出深度學習訓練新方式;谷歌發布Tacotron 2打造完美TTS
病毒安全女博士:基於深度學習的DGA惡意域名分類演算法
年度回顧:Uber2017年開源項目亮點概述
不止Google vs.Nvidia:深度學習引領AI晶元大戰
2017開發者盤點:是我在解決AI的問題,不是AI解決我的問題

TAG:AI漫遊 |