DirectML是一個高效能、硬體加速的機器學習函式庫
眼見機器學習影響越來越大,我現在上車也不遲
以前的DX12學習對現在的我幫助甚大,那這次也開始為新的未來做鋪墊了
而這次的挑戰其實更巨大了,以前在研究所上的機器學習課教得不深
教授給的期末專案就只是臉部辨識而已,感覺連個邊都沒摸到,反而像是在修影像處理
所以接著我這機器學習小白寫的內容,如果有誤還請高手不吝糾正/補充
硬體需求
根據
官網來看,AMD、Intel、NVIDIA其實都支援 (甚至還有高通)
- Win10 版本1903 (build 18362)以上
- AMD GCN 1st Gen (Radeon HD 7000 series) and above
- Intel Haswell (4th-gen core) HD Integrated Graphics and above
- NVIDIA Kepler (GTX 600 series) and above
只要有DirectX 12支援的裝置基本上都能跑,但我想那些有著機器學習單元的GPU一定更快
軟體需求
- Win10 SDK,一樣版本1903以上
- Microsoft.AI.DirectML,從NuGet安裝即可
另外還可以根據需求決定要不要結合Windows ML、ONNX、TensorFlow或者PyTorch等等
Code Study
初始化基本上是這幾個步驟:
- 初始化D3D12相關物件
- 初始化IDMLDevice,機器學習的Device是獨立的而非原本的ID3D12Device
- 設定張量(Tensor)資訊,這是機器學習最基本最重要的單元,0階代表純量、1階代表向量、2階代表矩陣,3階以上則是可以更自由的根據應用組合這些數據
在DirectML裡面主要是設定DML_BUFFER_TENSOR_DESC,而DirectML一定要定義4D或者5D張量,4D的資料為NCHW順序 (圖片數量、圖片通道數量RGB之類的、高度與寬度),5D資料為NCDHW (多了個Depth,用來處理3D資料結構)
這個範例準備了Float 32-bit資料類型,然後定義NCHW為(1,2,3,4),總共24個數值
- 設定機器學習的運算子,種類超多,從基本的算術運算到複雜的卷積、GEMM都有
要設定的資料結構更多了,包含以下:
- DML_TENSOR_DESC,這個描述指向剛剛設定的DML_BUFFER_TENSOR_DESC,這個結構可以用於後續的設定
- DML_OPERATOR_DESC,這個結構用以描述運算子要怎麼做,Type的部分丟個enum即可,範例是丟DML_OPERATOR_ELEMENT_WISE_IDENTITY,來做f(x)=x單純的數值傳遞,然後Desc指標指向了另一個DML_ELEMENT_WISE_IDENTITY_OPERATOR_DESC
- DML_ELEMENT_WISE_IDENTITY_OPERATOR_DESC,這個結構包含了InputTensor、OutputTensor以及ScaleBias三個成員,IO部分直接餵入第一點的TENSOR_DESC即可,畢竟單進單出,而ScaleBias成員根據文件說明,會做g(x) = x * scale + bias這種調整 (感覺一看就是給UV mapping用的啊!) 不過這邊沒用到維持null即可
- 當然單純傳遞參數略顯無聊,我個人測試了其他運算子像是_ABS,_ADD等等,每一種運算都有相對應的OPERATOR_DESC,一定要使用正確的!
- 編譯Operator
- 設定IDMLOperatorInitializer,也是相當重要的物件,主要是會回傳DML_BINDING_PROPERTIES這個資訊,讓開發者知道資源所需要的記憶體大小以及需要的Descriptor數量,這樣才有辦法建立需要的ID3D12DescriptorHeap
- 建立DML_BINDING_TABLE_DESC,這個是要用來綁定到GPU的結構,沒它的話GPU認不出計算上要使用哪一塊記憶體
- 建立暫存/永久Buffer,主要依據前述的BINDING_PROPERTIES有沒有回傳資源大小,簡單的運算是不需要額外建立這些Buffer的
- 建立IDMLCommandRecorder,這相當於建立D3D12 commandlist,主要是為了呼叫RecordDispatch()這個函式,它跟commandlist的函式一樣,呼叫後不會馬上執行,而是"錄製"機器學習指令
十分建議實作一個high-level類別來打包以上動作,不然真的很冗長...
而Runtime部分,這個範例馬上根據初始化的資料建立資源,建了96 bytes的IO buffer
並且使用dmlBindingTable->BindInputs(1, &inputBindingDesc);
以及dmlBindingTable->BindOutputs(1, &outputBindingDesc);
來綁定資源,這都是一本道
然後呼叫RecordDispatch,傳入編譯過的Operator,完成計算,後續它只是把數值讀回CPU印出,很簡單就不說了
這邊的重點只有一個: IO與Operator的要求要一致
後來我自己玩玩把運算類型改成DML_OPERATOR_ELEMENT_WISE_ADD,那很明顯需要2個輸入跟1個輸出,所以小改了一下code:
inputBindingDescs.push_back(inputBindingDesc);
inputBindingDescs.push_back(inputBindingDesc);
dmlBindingTable->BindInputs(2, inputBindingDescs.data());
只要有個要求沒達到,就是賞你一個runtime exception,所以很難不注意到
=========================================================
實務當中,大概就是把各種影像讀取進來
建立4D張量,再透過一番計算操作,輸出想要的結果,或把這個結果當作其它應用的輸入
另一個範例DirectMLSuperResolution今天應該是看不完了,今天還有一些機器學習的基本要看
留到下個學習日再說~