過去兩週解釋了對 Tensor Primitive Function 的實作可能。這週退一步來看,給出了如何抵達要去實作 primitive function 的 high level workflow overview。
一般在上課中對 ML 的印象比較多是在 Training 的部分,如何調用 ML operator 建構模型來提高準確率。為了方便調度,自然而然也就偏向了選擇 Python 這樣的 scripting language。而有別於 Training 時,Deploy 又是另一回事了,我們需要實作 primitive function 在環境上優化執行效率。
對 Development / Deploy 之間,課程提出一些觀察⋯⋯
在抽象化層級較高時,機器學習模型可以很直觀的,同時也是 dataflow graph(表示出 use-define 關係)。
lv_0 = linear(input, w0, b0)
lv_1 = max(lv_0, 0)
lv_2 = linear(lv_1, w1, b1)
output = softmax(lv2)
而上週所提到的 low level primitive function,是 deployment 中必要的一環。可以發現這些函數裡面只包含運算。在 deploy 時,我們傾向將記憶體配置的工作交給 framework。
lv_0 = allocate_space()
linear(input, w0, b0, lv_0) # primitive function
lv_1 = allocate_space()
max(lv_0, 0, lv_1) # primitive function
lv_2 = allocate_space()
linear(lv_1, w1, b1, lv_2) # primitive function
output = allocate_space()
softmax(lv_2, output) # primitive function
OS: 如果要緊追著週更 loading 其實也沒有想像中少(畢竟要查資料看論文之類的)
為了隱藏 activation data 的記憶體配置問題,可以用個 adaptor 來把 primitive function 包起來:
def adaptor(prim_func, inputs, shape, dtype) :
result = np.empty(shape, dtype=dtype) # allocate space
prim_func(*inputs, result)
return result
不過這只是語言表示上的不同,不如上週提到的因為 decoupling 而產生出了可以最佳化的解平面這樣的有用。個人覺得這只是提供 deployment 上的 sugar 而已。不過所謂的 framework 是不是也應該包含這樣的 sugar 來滿足大部分基本使用者的部屬需求,我覺得這是可以思考的問題。
個人覺得 TVM 現階段做出的 syntax sugar 不是一個很吸引人的賣點。Framework 的本質不應該是 sugar,而是更接近 MLIR 一樣提供的 modularity。
另一個 TQ 提到有趣的事情,是我們或許需要為了有 side effect 的 model graph 做準備。因為當有 side effect 時 dataflow graph 是不足的。而一種做法是用 dataflow block 一樣的 grouping 把部分 operator 圈起來,使得同 block 中的 operator 彼此之間是 side-effect free,而可以在異質運算中 dispatch 給 computing unit 做運算。
Operator 的 grouping / partition 是個非常有趣的問題。如果要實作這樣的機制,需要 runtime 和硬體的配合。在某種程度上我們或許可以用軟體 prefetch 來做到 out-of-order 的效果。或許這些想法該怎麼 modularize 才是 framework 設計的難處。
不過 TQ 接著就說了:「In the rest of the lecture, we are going to deal with models that compose into a single dataflow block.」看來接下來上課中只會看到比較 trivial,好 deploy 的 model?