Overview
TurboTransformers是騰訊最近開源的BERT推理模型酱固,它的特點(diǎn)就是一個(gè)字饥悴,快逗鸣。本人用BERT(huggingface/transformers)在V100上做了測試乍构,測試結(jié)果和官宣的基本一致:TurboTransformers的推理速度要比Pytorch快上1~4倍。
它之所以快柳譬,是因?yàn)樗菍S糜贐ERT的輕量級推理模型喳张。
分層
不管是計(jì)算機(jī)的硬件、軟件征绎,還是現(xiàn)在的深度學(xué)習(xí)蹲姐,它們都遵循著一個(gè)很重要的設(shè)計(jì)思想--分層:
- 用簡單的代碼(或電路)來實(shí)現(xiàn)一個(gè)基本功能組件。
- 用幾個(gè)基本組件組合成一個(gè)功能更強(qiáng)的復(fù)雜組件人柿。
- 從簡單到復(fù)雜柴墩,像搭積木一樣,一層層地搭建出擁有很強(qiáng)功能的組件凫岖。
開發(fā)者只需要基于PyTorch的幾個(gè)基本組件就能搭建出BERT模型江咳,而且這些組件本身對他們來說都是透明的。正因如此哥放,PyTorch才越來越受到研究者青睞歼指。
分層設(shè)計(jì)的優(yōu)點(diǎn)很多,例如甥雕,可以簡化問題踩身、降低創(chuàng)新門檻、加速開發(fā)等社露,但它的缺點(diǎn)也很明顯:
- 流程固定化
- 存在中間層延遲
深度神經(jīng)網(wǎng)絡(luò)里有個(gè)經(jīng)典套路:一個(gè)激活函數(shù)層后面緊跟著一個(gè)dropout層挟阻。PyTorch需要lanuch兩個(gè)GPU kernel程序來完成這兩步計(jì)算。
F.dropout(F.relu(x))
實(shí)際上峭弟,這兩項(xiàng)計(jì)算都是element-wise的附鸽,是可以合并成一個(gè)kernel的。但目前來說瞒瘸,不管是PyTorch坷备,還是其他的通用訓(xùn)練框架,它們都很少有提供這種融合計(jì)算的API情臭。
至于中間層延遲省撑,最經(jīng)典的要屬“hello world”程序。雖然只有幾行代碼俯在,但實(shí)際上要經(jīng)過的中間層數(shù)根本數(shù)不過來丁侄。
你可以閱讀深入淺出PyTorch(算子篇)來了解下矩陣相乘這個(gè)最基本的計(jì)算在PyTorch里要經(jīng)過多少個(gè)中間層。
分層展開
要想將程序的低延遲最大化朝巫,就需要把分層的代碼完全展開,并重構(gòu)代碼石景。典型例子就是嵌入式系統(tǒng)劈猿,為了實(shí)現(xiàn)某種需求拙吉,它可以打破應(yīng)用程序、程序庫揪荣、操作系統(tǒng)甚至是硬件設(shè)備的界限筷黔,打造一個(gè)軟硬件一體化產(chǎn)品。
這種分層展開的設(shè)計(jì)模式當(dāng)然也有它的局限性:專用仗颈。由于高度定制化佛舱,它通常只能用于完成某個(gè)特定功能。低延遲和專用化是呈絕對的正相關(guān)的挨决。
TurboTransformers就是采用這種設(shè)計(jì):只實(shí)現(xiàn)BERT模型前向傳播所需要的算子请祖,并融合那些可以合并的算子。
turbo.Tensor
首先脖祈,它用CUDA開發(fā)了一個(gè)輕量級的tensor計(jì)算庫肆捕,所謂的輕量級,指的是不用考慮反向傳播盖高、稀疏矩陣等操作慎陵,只實(shí)現(xiàn)BERT前向傳播所必需的operator。
雖然tensor庫是用C++寫的喻奥,但考慮到python在AI開發(fā)中的地位席纽,它用pybind11將C++ API暴露給前端的python Tensor類。
# turbo_transformers/python/pybind.cpp
72 py::class_<core::Tensor>(m, "Tensor")
73 .def_static("from_dlpack",
74 [](py::capsule capsule) -> std::unique_ptr<core::Tensor> {
75 auto tensor = (DLManagedTensor *)(capsule);
76 PyCapsule_SetName(capsule.ptr(), "used_tensor");
77 return absl::make_unique<core::Tensor>(tensor);
78 })
79 .def("to_dlpack",
80 [](core::Tensor &tensor) -> py::capsule {
81 auto *dlpack = tensor.ToDLPack();
82 return py::capsule(dlpack, "dltensor", DLPack_Capsule_Destructor);
83 })
84 .def("n_dim", &core::Tensor::n_dim)
85 .def("shape", &core::Tensor::shape)
從預(yù)訓(xùn)練模型(PyTorch)那遷移參數(shù)時(shí)撞蚕,turbo.Tensor不能直接對接torch.Tensor润梯,需要先將PyTorch的參數(shù)轉(zhuǎn)成dlpack格式, 再通過from_dlpack()將這些數(shù)據(jù)導(dǎo)入生成TurboTransformers tensor。除了dlpack之外诈豌,還支持*.npz文件格式仆救。
turbo.xxxlayer
TurboTransformers用CUDA重構(gòu)了Embedding、self-attention矫渔、intermediate彤蔽、output、LayerNorm和pooler等layer庙洼。turbo.layer不僅代碼結(jié)構(gòu)簡潔顿痪,overhead少,還合并了一部分算子油够。
這里以intermediate layer為例蚁袭,來分析這些算子的特點(diǎn)。
intermediate layer的實(shí)現(xiàn)比較簡單:一個(gè)Linear layer后面緊跟著一個(gè)gelu activation layer石咬。
PyTorch的intermediate layer的會lanuch 3個(gè)kernel來完成這部分計(jì)算:
- #1: y = input.matmul(weight)
- #2: y = y + bias
- #3: y = gelu(y)
由于#2和#3都是element-wise kernel揩悄,turbo把它們進(jìn)行了融合--AddBiasAct(),相同的計(jì)算操作鬼悠,只需要lanuch 2個(gè)kernel删性,計(jì)算速度當(dāng)然更快亏娜。
和PyTorch一樣,turbo的MatMul算子也是調(diào)用cuBLAS來進(jìn)行矩陣運(yùn)算蹬挺,而且turbo還啟用了Tensor Core來加速計(jì)算(CUBLAS_TENSOR_OP_MATH)维贺。
總結(jié)
到此,本文基本上講清了TurboTransformers的速度優(yōu)勢來源巴帮,由于篇幅所限溯泣,不能分析所有的算子。BERT的核心模塊是self-attention榕茧,如果想了解更多垃沦,可以閱讀深入淺出Transformer。
歡迎關(guān)注和點(diǎn)贊雪猪,你的鼓勵(lì)將是我創(chuàng)作的動(dòng)力
歡迎轉(zhuǎn)發(fā)至朋友圈栏尚,公眾號轉(zhuǎn)載請后臺留言申請授權(quán)~