国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Tensorflow代碼解析(二)

zhigoo / 646人閱讀

摘要:為了進一步了解的邏輯,圖對和進行了展開分析。另外,在命名空間中還隱式聲明了控制依賴操作,這在章節控制流中相關說明。簡述是高效易用的開源庫,有效支持線性代數,矩陣和矢量運算,數值分析及其相關的算法。返回其中一塊給用戶,并將該內存塊標識為占用。

3. TF 代碼分析初步

3.1 TF總體概述

為了對TF有整體描述,本章節將選取TF白皮書[1]中的示例展開說明,如圖 3 1所示是一個簡單線性模型的TF正向計算圖和反向計算圖。圖中x是輸入,W是參數權值,b是偏差值,MatMul和Add是計算操作,dMatMul和dAdd是梯度計算操作,C是正向計算的目標函數,1是反向計算的初始值,dC/dW和dC/dx是模型參數的梯度函數。

圖 3 1 tensorflow計算流圖示例

以圖 3 1為例實現的TF代碼見圖 3 2。首先聲明參數變量W、b和輸入變量x,構建線性模型y=W*x+b,目標函數loss采用誤差平方和最小化方法,優化函數optimizer采用隨機梯度下降方法。然后初始化全局參數變量,利用session與master交互實現圖計算。

圖 3 2 TF線性模型示例的實現代碼

圖 3 2中summary可以記錄graph元信息和tensor數據信息,再利用tensorboard分析模型結構和訓練參數。

圖 3 3是上述代碼在Tensorboard中記錄下的Tensor跟蹤圖。Tensorboard可以顯示scaler和histogram兩種形式。跟蹤變量走勢可更方便的分析模型和調整參數。

圖 3 3 Tensorboard顯示的TF線性模型參數跟蹤

圖 3 4是圖 3 1示例在Tensorboard中顯示的graph圖。左側子圖描述的正向計算圖和反向計算圖,正向計算的輸出被用于反向計算的輸入,其中MatMul對應MatMul_grad,Add對應Add_grad等。右上側子圖指明了目標函數最小化訓練過程中要更新的模型參數W、b,右下側子圖是參數節點W、b展開后的結果。

圖 3 4 Tensorboard顯示的TF線性模型graph

圖 3 4中,參數W是命名空間(Namespace)類型,展開后的W主要由Assign和Read兩個OpNode組成,分別負責W的賦值和讀取任務。

命名空間gradients是隱含的反向計算圖,定義了反向計算的計算邏輯。從圖 3 1可以看出,更新參數W需要先計算dMatMul,即圖 3 4中的MatMul_grad操作,而Update_W節點負責更新W操作。為了進一步了解UpdateW的邏輯,圖 3 5對MatMul_grad和update_W進行了展開分析。

圖 3 5 MatMul_grad計算邏輯

圖 3 5中,子圖(a)描述了MatMul_grad計算邏輯,子圖(b)描述了MatMul_grad輸入輸出,子圖(c)描述了update_W的計算邏輯。首先明確MatMul矩陣運算法則,假設 z=MatMul(x, y),則有dx = MatMul(dz, y),dy = MatMul(x, dz),由此可以推出dW=MatMul(dAdd, x)。在子圖(a)中左下側的節點b就是輸入節點x,dAdd由Add_grad計算輸出。update_W的計算邏輯由最優化函數指定,而其中的minimize/update_W/ApplyGradientDescent變量決定,即子圖(b)中的輸出變量Outputs。

另外,在MatMul_grad/tuple命名空間中還隱式聲明了control dependencies控制依賴操作,這在章節2.4控制流中相關說明。

3.2 Eigen介紹

在Tensoflow中核心數據結構和運算主要依賴于Eigen和Stream Executor庫,其中Eigen支持CPU和GPU加速計算,Stream Executor主要用于GPU環境加速計算。下面簡單講述Eigen庫的相關特性,有助于進一步理解Tensorflow。

3.2.1 Eigen簡述

Eigen是高效易用的C++開源庫,有效支持線性代數,矩陣和矢量運算,數值分析及其相關的算法。不依賴于任何其他依賴包,安裝使用都很簡便[8]。具有如下特性:

? ?支持整數、浮點數、復數,使用模板編程,可以為特殊的數據結構提供矩陣操作。比如在用ceres-solver進行做優化問題(比如bundle adjustment)的時候,有時候需要用模板編程寫一個目標函數,ceres可以將模板自動替換為內部的一個可以自動求微分的特殊的double類型。而如果要在這個模板函數中進行矩陣計算,使用Eigen就會非常方便。

? ?支持逐元素、分塊、和整體的矩陣操作。

? ?內含大量矩陣分解算法包括LU,LDLt,QR、SVD等等。

? ?支持使用Intel MKL加速

? ?部分功能支持多線程

? ?稀疏矩陣支持良好,到今年新出的Eigen3.2,已經自帶了SparseLU、SparseQR、共軛梯度(ConjugateGradient solver)、bi conjugate gradient stabilized solver等解稀疏矩陣的功能。同時提供SPQR、UmfPack等外部稀疏矩陣庫的接口。

? ?支持常用幾何運算,包括旋轉矩陣、四元數、矩陣變換、AngleAxis(歐拉角與Rodrigues變換)等等。

? ?更新活躍,用戶眾多(Google、WilliowGarage也在用),使用Eigen的比較著名的開源項目有ROS(機器人操作系統)、PCL(點云處理庫)、Google Ceres(優化算法)。OpenCV自帶到Eigen的接口。

Eigen庫包含 Eigen模塊和unsupported模塊,其中Eigen模塊為official module,unsupported模塊為開源貢獻者開發的。

Eigen unsupported 模塊中定義了數據類型Tensor及相關函數,包括Tensor的存儲格式,Tensor的符號表示,Tensor的編譯加速,Tensor的一元運算、二元運算、高維度泛化矩陣運算,Tensor的表達式計算。本章后續所述Tensor均為Eigen::Tensor

Eigen運算性能評估如圖 3 6所示[9],eigen3的整體性能比eigen2有很大提升,與GOTO2、INTEL_MKL基本持平。

圖 3 6矩陣運算常用庫比較

3.2.2 Eigen 存儲順序

Eigen中的Tensor支持兩種存儲方式:

? ?Row-major表示矩陣存儲時按照row-by-row的方式。

? ?Col-major表示矩陣存儲時按照column-by-column的方式。

Eigen默認采用Col-major格式存儲的(雖然也支持Row-major,但不推薦),具體采用什么存儲方式取決于算法本身是行遍歷還是列遍歷為主。例如:A=[[a11, a12, a13], [a21, a22, a23]]的存儲序列見圖 3 7。

圖 3 7 Row-major和Column-major存儲順序

3.2.3 Eigen 惰性求值

在編程語言理論中,存在及早求值(Eager Evaluation) 和惰性求值(Lazy Evaluation)

? ?及早求值:大多數編程語言所擁有的普通計算方式

? ?惰性求值:也認為是“延遲求值”,可以提高計算性能,最重要的好處是它可以構造一個無限的數據類型。

關于惰性求值,舉例如下:

Vec3 = vec1 + vec2;

及早求值形式需要臨時變量vec_temp存儲運算結果,再賦值給vec3,計算效率和空間效率都不高:

Vec_temp = vec1 + vec2;

Vec3 = vec_temp

而惰性求值不需要臨時變量保存中間結果,提高了計算性能:

Vec_symbol_3 = (vec_symbol_1 + vec_symbol_2);

Vec3 = vec_symbol_3.eval(vec1, vec2)

由于Eigen默認采用惰性計算,如果要求表達式的值可以使用Tensor::eval()函數。Tensor::eval()函數也是session.run()的底層運算。例如:

Tensor result = ((t1 + t2).eval() * 0.2f).exp();

3.2.4 Eigen 編譯加速

編譯加速可以充分發揮計算機的并行計算能力,提高程序運行速度。

舉例如下:

普通的循環相加運算時間復雜度是O(n):

如果指令集支持128bit并行計算,則時間復雜度可縮短為O(n/4):

Eigen編譯時使用了SSE2加速。假設處理float32類型,指令集支持128bit并行計算,則一次可以計算4個float32類型,速度提升4倍。

3.2.5 Eigen::half

Tensorflow支持的浮點數類型有float16, float32, float64,其中float16本質上是Eigen::half類型,即半精度浮點數[10]。關于半精度浮點數,英偉達2002年首次提出使用半精度浮點數達到降低數據傳輸和存儲成本的目的。

在分布式計算中,如果對數據精度要求不那么高,可以將傳輸數據轉換為float16類型,這樣可以大大縮短設備間的數據傳輸時間。在GPU運算中,float16還可以減少一般的內存占用。

在Tensorflow的分布式傳輸中,默認會將float32轉換為float16類型。Tensorflow的轉換方式不同于nvidia的標準,采用直接截斷尾數的方式轉化為半精度浮點數,以減少轉換時間。

圖 3 8是雙精度浮點數(float64)存儲格式。

圖 3 8 雙精度浮點數

圖 3 9是單精度浮點數(float32)存儲格式。

圖 3 9 單精度浮點數

圖 3 10是半精度浮點數(float16)存儲格式。

圖 3 10 半精度浮點數

浮點數存儲格式分成3部分,符號位,指數和尾數。不同精度是指數位和尾數位的長度不一樣。

3.3 設備內存管理

TF設備內存管理模塊利用BFC算法(best-fit with coalescing)實現。BFC算法是Doung Lea’s malloc(dlmalloc)的一個非常簡單的版本。它具有內存分配、釋放、碎片管理等基本功能[11]。

BFC將內存分成一系列內存塊,每個內存塊由一個chunk數據結構管理。從chunk結構中可以獲取到內存塊的使用狀態、大小、數據的基址、前驅和后繼chunk等信息。整個內存可以通過一個chunk的雙鏈表結構來表示。

圖 3 11內存分塊結構圖

用戶申請一個內存塊(malloc)。根據建立的chunk雙鏈表找到一個合適的內存塊(后面會說明什么是合適的內存塊),如果該內存塊的大小是用戶申請大小的兩倍以上,那么將該內存塊切分成兩塊,這就是split操作。返回其中一塊給用戶,并將該內存塊標識為占用。Spilt操作會新增一個chunk,所以需要修改chunk雙鏈表以維持前驅和后繼關系。

用戶釋放一個內存塊(free)。先將該塊標記為空閑。然后根據chunk數據結構中的信息找到其前驅和后繼內存塊。如果前驅和后繼塊中有空閑的塊,那么將剛釋放的塊和空閑的塊合并成一個更大的chunk(這就是merge操作,合并當前塊和其前后的空閑塊)。再修改雙鏈表結構以維持前驅后繼關系。這就做到了內存碎片的回收。

BFC的核心思想是:將內存分塊管理,按塊進行空間分配和釋放;通過split操作將大內存塊分解成小內存塊;通過merge操作合并小的內存塊,做到內存碎片回收。

但是還留下許多疑問。比如說申請內存空間時,什么樣的塊算合適的內存塊?如何快速管理這種塊?

BFC算法采取的是被動分塊的策略。最開始整個內存是一個chunk,隨著用戶申請空間的次數增加,最開始的大chunk會被不斷的split開來,從而產生越來越多的小chunk。當chunk數量很大時,為了尋找一個合適的內存塊而遍歷雙鏈表無疑是一筆巨大的開銷。為了實現對空閑塊的高效管理,BFC算法設計了bin這個抽象數據結構。

Bin數據結構中,每個bin都有一個size屬性,一個bin是一個擁有chunk size >= bin size的空閑chunk的集合。集合中的chunk按照chunk size的升序組織成單鏈表。BFC算法維護了一個bin的集合:bins。它由多個bin以及從屬于每個bin的chunks組成。內存中所有的空閑chunk都由bins管理。

圖 3 12 bins集合的結構圖

圖 3 12中每一列表示一個bin,列首方格中的數字表示bin的size。bin size的大小都是256的2^n的倍。每個bin下面掛載了一系列的空閑chunk,每個chunk的chunk size都大于等于所屬的bin的bin size,按照chunk size的升序掛載成單鏈表。BFC算法針對bins這個集合設計了三個操作:search、insert、delete。

Search 操作:給定一個chunk size,從bins中找到大于等于該chunk size的最小的那個空閑chunk。Search操作具體流程如下。如果bin以數組的形式組織,那么可以從index = chunk size /256 >>2的那個bin開始查找。較好的情況是開始查找的那個bin的chunk鏈表非空,那么直接返回鏈表頭即可。這種情況時間復雜度是常數級的。最壞的情況是遍歷bins數組中所有的bin。對于一般大小的內存來說,bins數組元素非常少,比如4G空間只需要23個bin就足夠了(256 * 2 ^ 23 > 4G),因此也很快能返回結果。總體來說search操作是非常高效的。對于固定大小內存來說,查找時間是常數量級的。

Insert 操作:將一個空閑的chunk插入到一個bin所掛載的chunk鏈表中,同時需要維持chunk鏈表的升序關系。具體流程是直接將chunk插入到index = chunk size /256 >>2的那個bin中即可。

Delete操作:將一個空閑的chunk從bins中移除。

TF中內存分配算法實現文件core/common_runtime/bfc_allocator.cc,GPU內存分配算法實現文件core/common_runtime/gpu/gpu_bfc_allocator.cc。

3.4 TF開發工具介紹

TF系統開發使用了bazel工具實現工程代碼自動化管理,使用了protobuf實現了跨設備數據傳輸,使用了swig庫實現python接口封裝。本章將從這三方面介紹TF開發工具的使用。

3.4.1 Swig封裝

Tensorflow核心框架使用C++編寫,API接口文件定義在tensorflow/core/public目錄下,主要文件是tensor_c_api.h文件,C++語言直接調用這些頭文件即可。

Python通過Swig工具封裝TF庫包間接調用,接口定義文件tensorflow/python/ tensorflow.i。其中swig全稱為Simplified Wrapper and Interface Generator,是封裝C/C++并與其它各種高級編程語言進行嵌入聯接的開發工具,對swig感興趣的請參考相關文檔。

在tensorflow.i文件中包含了若干個.i文件,每個文件是對應模塊的封裝,其中tf_session.i文件中包含了tensor_c_api.h,實現client向session發送請求創建和運行graph的功能。

3.4.2 Bazel編譯和調試

Bazel是Google開源的自動化構建工具,類似于Make和CMake工具。Bazel的目標是構建“快速并可靠的代碼”,并且能“隨著公司的成長持續調整其軟件開發實踐”。

TF中幾乎所有代碼編譯生成都是依賴Bazel完成的,了解Bazel有助于進一步學習TF代碼,尤其是編譯測試用例進行gdb調試。

Bazel假定每個目錄為[package]單元,目錄里面包含了源文件和一個描述文件BUILD,描述文件中指定了如何將源文件轉換成構建的輸出。

以圖 3 13為例,左子圖為工程中不同模塊間的依賴關系,右子圖是對應模塊依賴關系的BUILD描述文件。

圖 3 13中name屬性來命名規則,srcs屬性為模塊相關源文件列表,deps屬性來描述規則之間的依賴關系。”//search: google_search_page”中”search”是包名,”google_search_page”為規則名,其中冒號用來分隔包名和規則名;如果某條規則所依賴的規則在其他目錄下,就用"http://"開頭,如果在同一目錄下,可以忽略包名而用冒號開頭。

圖 3 13中cc_binary表示編譯目標是生成可執行文件,cc_library表示編譯目標是生成庫文件。如果要生成google_search_page規則可運行

如果要生成可調試的二進制文件,可運行

圖 3 13 Bazel BUILD文件示例

TF中首次運行bazel時會自動下載很多依賴包,如果有的包下載失敗,打開tensorflow/workspace.bzl查看是哪個包下載失敗,更改對應依賴包的new_http_archive中的url地址,也可以把new_http_archive設置為本地目錄new_local_repository。

TF中測試用例跟相應代碼文件放在一起,如MatMul操作的core/kernels/matmul_op.cc文件對應的測試用例文件為core/kernels/matmul_op_test.cc文件。運行這個測試用例需要查找這個測試用例對應的BUILD文件和對應的命令規則,如matmul_op_test.cc文件對應的BUILD文件為core/kernels/BUILD文件,如下

其中tf_cuda_cc_test函數是TF中自定義的編譯函數,函數定義在/tensorflow/ tensorflow.bzl文件中,它會把matmul_op_test.cc放進編譯文件中。要生成matmul_op_test可執行文件可運行如下腳本:

3.4.3 Protobuf序列化

Protocol Buffers 是一種輕便高效的結構化數據存儲格式,可以用于結構化數據串行化,或者說序列化。它很適合做數據存儲或 RPC 數據交換格式。可用于通訊協議、數據存儲等領域的語言無關、平臺無關、可擴展的序列化結構數據格式。

Protobuf對象描述文件為.proto類型,編譯后生成.pb.h和.pb.cc文件。

Protobuf主要包含讀寫兩個函數:Writer(序列化)函數SerializeToOstream() 和 ?Reader(反序列化)函數 ParseFromIstream()。

Tensorflow在core/probobuf目錄中定義了若干與分布式環境相關的.proto文件,同時在core/framework目錄下定義了與基本數據類型和結構的.proto文件,在core/util目錄中也定義部分.proto文件,感覺太隨意了。

在分布式環境中,不僅需要傳輸數據序列化,還需要數據傳輸協議。Protobuf在序列化處理后,由gRPC完成數據傳輸。gRPC數據傳輸架構圖見圖 3 14。

圖 3 14 gRPC數據傳輸架構

gRPC服務包含客戶端和服務端。gRPC客戶端調用stub 對象將請求用 protobuf 方式序列化成字節流,用于線上傳輸,到 server 端后調用真正的實現對象處理。gRPC的服務端通過observer觀察處理返回和關閉通道。

TF使用gRPC完成不同設備間的數據傳輸,比如超參數、梯度值、graph結構。

作者簡介:

姚健,畢業于中科院計算所網絡數據實驗室,畢業后就職于360天眼實驗室,主要從事深度學習和增強學習相關研究工作。目前就職于騰訊MIG事業部,從事神經機器翻譯工作。聯系方式: yao_62995@163.com

歡迎加入本站公開興趣群

商業智能與數據分析群

興趣范圍包括各種讓數據產生價值的辦法,實際應用案例分享與討論,分析工具,ETL工具,數據倉庫,數據挖掘工具,報表系統等全方位知識

QQ群:81035754

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/4499.html

相關文章

  • 深度學習

    摘要:深度學習在過去的幾年里取得了許多驚人的成果,均與息息相關。機器學習進階筆記之一安裝與入門是基于進行研發的第二代人工智能學習系統,被廣泛用于語音識別或圖像識別等多項機器深度學習領域。零基礎入門深度學習長短時記憶網絡。 多圖|入門必看:萬字長文帶你輕松了解LSTM全貌 作者 | Edwin Chen編譯 | AI100第一次接觸長短期記憶神經網絡(LSTM)時,我驚呆了。原來,LSTM是神...

    Vultr 評論0 收藏0

發表評論

0條評論

最新活動
閱讀需要支付1元查看
<