摘要:所有操作都是節點形式表示的,包括計算節點和非計算節點。采用回合通信機制,類似生產者消費者的消息信箱。解析器將協議內存塊解析為張量,放入隊列中,其中命名和類型要與寫入的一致。目前就職于騰訊事業部,從事神經機器翻譯工作。
4. ?TF – Kernels模塊
TF中包含大量Op算子,這些算子組成Graph的節點集合。這些算子對Tensor實現相應的運算操作。圖 4 1列出了TF中的Op算子的分類和舉例。
圖 4 1 TensorFlow核心庫中的部分運算
4.1 ? OpKernels 簡介
OpKernel類(core/framework/op_kernel.h)是所有Op類的基類。繼承OpKernel還可以自定義新的Op類。用的較多的Op如(MatMul, ?Conv2D, ?SoftMax, ?AvgPooling, Argmax等)。
所有Op包含注冊(Register Op)和實現(正向計算、梯度定義)兩部分。
所有Op類的實現需要overide抽象基函數 void Compute(OpKernelContext* context),實現自身Op功能。用戶可以根據需要自定義新的Op操作,參考[12]。
TF中所有Op操作的屬性定義和描述都在 ops/ops.pbtxt。如下Add操作,定義了輸入參數x、y,輸出參數z。
4.2 UnaryOp & BinaryOp
UnaryOp和BinaryOp定義了簡單的一元操作和二元操作,類定義在/core/kernels/ cwise_ops.h文件,類實現在/core/kernels/cwise_op_*.cc類型的文件中,如cwise_op_sin.cc文件。
一元操作全稱為Coefficient-wise unary operations,一元運算有abs, sqrt, exp, sin, cos,conj(共軛)等。如abs的基本定義:
二元操作全稱為Coefficient-wise binary operations,二元運算有add,sub, div, mul,mod等。如sum的基本定義:
4.3 MatMul
4.3.1 Python相關部分
在Python腳本中定義matmul運算:
根據Ops名稱MatMul從Ops庫中找出對應Ops類型
創建ops節點
創建ops節點并指定相關屬性和設備分配
4.3.2 C++相關部分
Python腳本通過swig調用進入C接口API文件core/client/tensor_c_api.cc,調用TF_NewNode函數生成節點,同時還需要指定輸入變量,TF_AddInput函數設置first輸入變量,TF_AddInputList函數設置other輸入變量。這里op_type為MatMul,first輸入變量為a,other輸入變量為b。
創建節點根據節點類型從注冊的Ops工廠中生成,即TF通過工廠模式把一系列Ops注冊到Ops工廠中。其中MatMul的注冊函數為如下
4.3.3 MatMul正向計算
MatMul的實現部分在core/kernels/matmul_op.cc文件中,類MatMulOp繼承于OpKernel,成員函數Compute完成計算操作。
MatMul的測試用例core/kernels/matmul_op_test.cc文件,要調試這個測試用例,可通過如下方式:
在TF中MatMul實現了CPU和GPU兩個版本,其中CPU版本使用Eigen庫,GPU版本使用cuBLAS庫。
CPU版的MatMul使用Eigen庫,調用方式如下:
簡而言之就是調用eigen的constract函數。
GPU版的MatMul使用cuBLAS庫,準確而言是基于cuBLAS的stream_executor庫。Stream executor是google開發的開源并行計算庫,調用方式如下:
其中stream類似于設備句柄,可以調用stream executor中的cuda模塊完成運算。
4.3.4 MatMul梯度計算
MatMul的梯度計算本質上也是一種kernel ops,描述為MatMulGrad。MatMulgrad操作是定義在grad_ops工廠中,類似于ops工廠。定義方式如下:
MatmulGrad由FDH(Function Define Helper)完成定義,
其中attr_adj_x="transpose_a" ax0=false, ax1=true, attr_adj_y= "transpose_b", ay0=true, ay1=false, *g屬于FunctionDef類,包含MatMul的梯度定義。
從FDH定義中可以看出MatMulGrad本質上還是MatMul操作。在矩陣求導運算中:
MatMulGrad的測試用例core/ops/math_grad_test.cc文件,要調試這個測試用例,可通過如下方式:
4.4 Conv2d
關于conv2d的python調用部分和C++創建部分可參考MatMul中的描述。
4.4.1 Conv2d正向計算部分
TF中conv2d接口如下所示,簡單易用:
實現部分在core/kernels/conv_ops.cc文件中,類Conv2DOp繼承于抽象基類OpKernel。
Conv2DOp的測試用例core/kernels/eigen_spatial_convolutions_test.cc文件,要調試這個測試用例,可通過如下方式:
Conv2DOp的成員函數Compute完成計算操作。
為方便描述,假設tf.nn.conv2d中input參數的shape為[batch, in_rows, in_cols, in_depth],filter參數的shape為[filter_rows, filter_cols, in_depth, out_depth]。
首先,計算卷積運算后輸出tensor的shape。
? ?若padding=VALID,output_size = (input_size - filter_size + stride) / stride;
? ?若padding=SAME,output_size = (input_size + stride - 1) / stride;
其次,根據計算結果給輸出tensor分配內存。
然后,開始卷積計算。Conv2DOp實現了CPU和GPU兩種模式下的卷積運算。同時,還需要注意input tensor的輸入格式,通常有NHWC和NCHW兩種格式。在TF中,Conv2d-CPU模式下目前僅支持NHWC格式,即[Number, Height, Weight, Channel]格式。Conv2d-GPU模式下以NCHW為主,但支持將NHWC轉換為NCHW求解。C++中多維數組是row-major順序存儲的,而Eigen默認是col-major順序的,則C++中[N, H, W, C]相當于Eigen中的[C, W, H, N],即dimention order是相反的,需要特別注意。
Conv2d-CPU模式下調用Eigen庫函數。
Eigen庫中卷積函數的詳細代碼參見圖 4 2。
圖 4 2 Eigen卷積運算的定義
? ?Tensor:: extract_image_patches () 為卷積或池化操作抽取與kernel size一致的image patches。該函數的定義在eigen3/unsupported/Eigen/CXX11/src/Tensor/ TensorBase.h中,參考該目錄下ReadME.md。
? ?Tensor:: extract_image_patches () 的輸出與input tensor的data layout有關。設input tensor為ColMajor格式[NHWC],則image patches輸出為[batch, filter_index, filter_rows, filter_cols, in_depth],并reshape為[batch * filter_index, filter_rows * filter_cols * in_depth],而kernels維度為[filter_rows * filter_cols * in_depth, out_depth],然后kernels矩陣乘image patches得到輸出矩陣[batch * filter_index, out_depth],并reshape為[batch, out_rows, out_cols, out_depth]。
Conv2d-GPU模式下調用基于cuDNN的stream_executor庫。若input tensor為NHWC格式的,則先轉換為NCHW格式
調用cudnn庫實現卷積運算:
計算完成后再轉換成HHWC格式的
4.4.2 Conv2d梯度計算部分
Conv2D梯度計算公式,假設output=Conv2d(input, filter),則
Conv2D梯度計算的測試用例core/kernels/eigen_backward_spatial_convolutions_test.cc文件,要調試這個測試用例,可通過如下方式:
Conv2d的梯度計算函數描述為Conv2DGrad。Conv2DGrad操作定義在grad_ops工廠中。注冊方式如下:
Conv2DGrad由FDH(Function Define Helper)完成定義,參見圖 4 3。
圖 4 3 Conv2DGrad的函數定義
Conv2DGrad梯度函數定義中依賴Conv2DBackpropInput和Conv2DBackpropFilter兩種Ops,二者均定義在kernels/conv_grad_ops.cc文件中。
Conv2DBackpropInputOp和Conv2DBackpropFilterOp的實現分為GPU和CPU版本。
Conv2D運算的GPU版實現定義在類Conv2DSlowBackpropInputOp
Conv2D運算的CPU版有兩種實現形式,分別為custom模式和fast模式。Custom模式基于賈揚清在caffe中的思路實現,相關類是Conv2DCustomBackpropInputOp
根據Conv2DGrad的函數定義,從代碼分析Conv2D-GPU版的實現代碼,即分析Conv2DBackpropInput和Conv2DBackpropFilter的實現方式。
Conv2DSlowBackpropInputOp的成員函數Compute完成計算操作。
Compute實現部分調用stream executor的相關函數,需要先獲取庫的stream句柄,再調用卷積梯度函數。
stream executor在卷積梯度運算部分仍然是借助cudnn庫實現的。
4.4.3 MaxPooling計算部分
在很多圖像分類和識別問題中都用到了池化運算,池化操作主要有較大池化(max pooling)和均值池化(avg pooling),本章節主要介紹較大池化的實現方法。調用TF接口可以很容易實現池化操作。
Eigen庫中較大池化的詳細描述如下:
其中較大池化運算主要分為兩步,第一步中extract_image_patch為池化操作抽取與kernel size一致的image patches,第二步計算每個image patch的較大值。
4.5 SendOp & RecvOp
TF所有操作都是節點形式表示的,包括計算節點和非計算節點。在跨設備通信中,發送節點(SendOp)和接收節點(RecvOp)為不同設備的兩個相鄰節點完成完成數據通信操作。Send和Recv通過TCP或RDMA來傳輸數據。
TF采用Rendezvous(回合)通信機制,Rendezvous類似生產者/消費者的消息信箱。引用TF描述如下:
TF的消息傳遞屬于采用“發送不阻塞/接收阻塞”機制,實現場景有LocalRendezvous
(本地消息傳遞)、RpcRemoteRendezvous (分布式消息傳遞)。除此之外還有IntraProcessRendezvous用于本地不同設備間通信。
TF會在不同設備的兩個相鄰節點之間添加Send和Recv節點,通過Send和Recv之間進行通信來達到op之間通信的效果,如圖 4 4右子圖所示。圖中還涉及到一個優化問題,即a->b和a->c需要建立兩組send/recv連接的,但兩組連接是可以共用的,所以合并成一組連接。
圖 4 4 Graph跨設備通信
Send和Recv分別對應OpKernel中的SendOp和RecvOp兩個類(kernels/sendrecv_ops.h)。
SendOp的計算函數。
SendOp作為發送方需要先獲取封裝ctx消息,然后借助Rendezvous模塊發送給接收方。
RecvOp的計算函數如下。
RecvOp作為接收方借助Rendezvous模塊獲取ctx消息。
其中parsed變量是類ParsedKey的實例。圖 5?5是Rendezvous封裝的ParsedKey消息實體示例。
4.6 ReaderOp & QueueOp
4.6.1 TF數據讀取
TF系統定義了三種數據讀取方式[13]:
? ?供給數據(Feeding): 在TensorFlow程序運行的每一步, 通過feed_dict來供給數據。
? ?從文件讀取數據: 在TensorFlow圖的起始, 讓一個輸入管線(piplines)從文件中讀取數據放入隊列,通過QueueRunner供給數據,其中隊列可以實現多線程異步計算。
? ?預加載數據: 在TensorFlow圖中定義常量或變量來保存所有數據,如Mnist數據集(僅適用于數據量比較小的情況)。
除了以上三種數據讀取方式外,TF還支持用戶自定義數據讀取方式,即繼承ReaderOpKernel類創建新的輸入讀取類[14]。本章節主要講述通過piplines方式讀取數據的方法。
Piplines利用隊列實現異步計算
從piplines讀取數據也有兩種方式:一種是讀取所有樣本文件路徑名轉換成string tensor,使用input_producer將tensor亂序(shuffle)或slice(切片)處理放入隊列中;另一種是將數據轉化為TF標準輸入格式,即使用TFRecordWriter將樣本數據寫入tfrecords文件中,再使用TFRecordReader將tfrecords文件讀取到隊列中。
圖 4 6描述了piplines讀取數據的第一種方式,這些流程通過節點和邊串聯起來,成為graph數據流的一部分。
從左向右, 第一步 是載入文件列表,使用convert_to_tensor函數將文件列表轉化為tensor,如cifar10數據集中的image_files_tensor和label_tensor。
第二步是使用input_producer將image_files_tensor和label_tensor放入圖中的文件隊列中,這里的input_producer作用就是將樣本放入隊列節點中,有string_input_producer、range_input_producer和slice_input_producer三種,其中slice_input_producer的切片功能支持亂序,其他兩種需要借助tf.train.shuffle_batch函數作亂序處理,有關三種方式的具體描述可參考tensorflow/python/training/input.py注釋說明。
第三步是使用tf.read_file()讀取隊列中的文件數據到內存中,使用解碼器如tf.image.decode_jpeg()解碼成[height, width, channels]格式的數據。
最后就是使用batch函數將樣本數據處理成一批批的樣本,然后使用session執行訓練。
圖 4 6 使用piplines讀取數據
4.6.2 TFRecords使用
TFRecords是TF支持的標準文件格式,這種格式允許將任意的數據轉換為TFRecords支持的文件格式。TFRecords方法需要兩步:第一步是使用TFRecordWriter將樣本數據寫入tfrecords文件中,第二步是使用TFRecordReader將tfrecords文件讀取到隊列中。
圖 4 7是TFRecords文件寫入的簡單示例。tf.train.Example將數據填入到Example協議內存塊(protocol buffer),將協議內存塊序列化為一個字符串,通過TFRecordWriter寫入到TFRecords文件,圖中定義了label和image_raw兩個feature。Example協議內存塊的定義請參考文件core/example/example.proto。
圖 4 7 TFRecordWriter寫入數據示例
圖 4 8是TFRecords文件讀取的簡單示例。tf.parse_single_example解析器將Example協議內存塊解析為張量,放入example隊列中,其中features命名和類型要與Example寫入的一致。
圖 4 8 TFRecrodReader讀取數據示例
4.6.3 ReaderOps分析
ReaderOpsKernel類封裝了數據讀取的入口函數Compute,通過繼承ReaderOpsKernel類可實現各種自定義的數據讀取方法。圖 4 9是ReaderOp相關的UML視圖。
圖 4 9 ReaderOp相關的UML視圖
ReaderOpKernel子類必須重新定義成員函數SetReaderFactory實現對應的數據讀取邏輯。TFRecordReaderOp的讀取方法定義在TFRecordReader類中。
其中offset的計算方式。
作者簡介:
姚健,畢業于中科院計算所網絡數據實驗室,畢業后就職于360天眼實驗室,主要從事深度學習和增強學習相關研究工作。目前就職于騰訊MIG事業部,從事神經機器翻譯工作。聯系方式: yao_62995@163.com
歡迎加入本站公開興趣群商業智能與數據分析群
興趣范圍包括各種讓數據產生價值的辦法,實際應用案例分享與討論,分析工具,ETL工具,數據倉庫,數據挖掘工具,報表系統等全方位知識
QQ群:81035754
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/4498.html
摘要:為了進一步了解的邏輯,圖對和進行了展開分析。另外,在命名空間中還隱式聲明了控制依賴操作,這在章節控制流中相關說明。簡述是高效易用的開源庫,有效支持線性代數,矩陣和矢量運算,數值分析及其相關的算法。返回其中一塊給用戶,并將該內存塊標識為占用。 3. TF 代碼分析初步3.1 TF總體概述為了對TF有整體描述,本章節將選取TF白皮書[1]中的示例展開說明,如圖 3 1所示是一個簡單線性模型的TF...
摘要:簡介讀取數據共有三種方法當運行每步計算的時候,從獲取數據。數據直接預加載到的中,再把傳入運行。在中定義好文件讀取的運算節點,把傳入運行時,執行讀取文件的運算,這樣可以避免在和執行環境之間反復傳遞數據。本文講解的代碼。 簡介 TensorFlow讀取數據共有三種方法: Feeding:當TensorFlow運行每步計算的時候,從Python獲取數據。在Graph的設計階段,用place...
摘要:據公告稱,和的包裝庫使用了不安全的函數來反序列化編碼的機器學習模型。簡單來看,序列化將對象轉換為字節流。據悉,本次漏洞影響與版本,的到版本均受影響。作為解決方案,在宣布棄用之后,團隊建議開發者以替代序列化,或使用序列化作為替代。 ...
摘要:近日它們交鋒的戰場就是動態計算圖,誰能在這場戰爭中取得優勢,誰就把握住了未來用戶的流向。所以動態框架對虛擬計算圖的構建速度有較高的要求。動態計算圖問題之一的多結構輸入問題的高效計 隨著深度學習的發展,深度學習框架之間競爭也日益激烈,新老框架紛紛各顯神通,想要在廣大DeepLearner的服務器上占據一席之地。近日它們交鋒的戰場就是動態計算圖,誰能在這場戰爭中取得優勢,誰就把握住了未來用戶的流...
閱讀 2123·2019-08-29 16:53
閱讀 2698·2019-08-29 16:07
閱讀 2041·2019-08-29 13:13
閱讀 3266·2019-08-26 13:57
閱讀 1331·2019-08-26 13:31
閱讀 2432·2019-08-26 13:22
閱讀 1220·2019-08-26 11:43
閱讀 2084·2019-08-23 17:14