<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          TorchScript 快速入門(mén)之初識(shí)一場(chǎng)

          共 6010字,需瀏覽 13分鐘

           ·

          2022-04-18 13:43




          今天,我們又將開(kāi)啟新的 TorchScript 解讀系列教程,帶領(lǐng)大家玩轉(zhuǎn) PyTorch 模型部署。感興趣的小伙伴一起往下看吧~


          什么是 TorchScript?


          PyTorch 無(wú)疑是現(xiàn)在最成功的深度學(xué)習(xí)訓(xùn)練框架之一,是各種頂會(huì)頂刊論文實(shí)驗(yàn)的大熱門(mén)。比起其他的框架,PyTorch 最大的賣(mài)點(diǎn)是它對(duì)動(dòng)態(tài)網(wǎng)絡(luò)的支持,比其他需要構(gòu)建靜態(tài)網(wǎng)絡(luò)的框架擁有更低的學(xué)習(xí)成本。PyTorch 源碼 Readme 中還專(zhuān)門(mén)為此做了一張動(dòng)態(tài)圖:



          對(duì)研究員而言,PyTorch 能極大地提高想 idea、做實(shí)驗(yàn)、發(fā)論文的效率,是訓(xùn)練框架中的豪杰,但是它不適合部署。動(dòng)態(tài)建圖帶來(lái)的優(yōu)勢(shì)對(duì)于性能要求更高的應(yīng)用場(chǎng)景而言更像是缺點(diǎn),非固定的網(wǎng)絡(luò)結(jié)構(gòu)給網(wǎng)絡(luò)結(jié)構(gòu)分析并進(jìn)行優(yōu)化帶來(lái)了困難,多數(shù)參數(shù)都能以 Tensor 形式傳輸也讓資源分配變成一件鬧心的事。另外由于圖是由 python 代碼來(lái)構(gòu)建的,一方面部署要依賴(lài) python 環(huán)境,另一方面模型也毫無(wú)保密性可言。


          而 TorchScript 就是為了解決這個(gè)問(wèn)題而誕生的工具。包括代碼的追蹤及解析、中間表示的生成、模型優(yōu)化、序列化等各種功能,可以說(shuō)是覆蓋了模型部署的方方面面。今天我們先簡(jiǎn)要地介紹一些 TorchScript 的功能,讓大家有一個(gè)初步的認(rèn)識(shí),進(jìn)階的解讀會(huì)陸續(xù)推出~


          模型轉(zhuǎn)換



          作為模型部署的一個(gè)范式,通常我們都需要生成一個(gè)模型的中間表示(IR),這個(gè) IR 擁有相對(duì)固定的圖結(jié)構(gòu),所以更容易優(yōu)化,讓我們看一個(gè)例子:


          import torchfrom torchvision.models import resnet18
          # 使用PyTorch model zoo中的resnet18作為例子model = resnet18()model.eval()
          # 通過(guò)trace的方法生成IR需要一個(gè)輸入樣例dummy_input = torch.rand(1, 3, 224, 224)
          # IR生成with torch.no_grad(): ? ?jit_model = torch.jit.trace(model, dummy_input)


          到這里就將 PyTorch 的模型轉(zhuǎn)換成了 TorchScript 的 IR。這里我們使用了 trace 模式來(lái)生成 IR,所謂 trace 指的是進(jìn)行一次模型推理,在推理的過(guò)程中記錄所有經(jīng)過(guò)的計(jì)算,將這些記錄整合成計(jì)算圖。關(guān)于 trace 的過(guò)程我們會(huì)在未來(lái)的分享中進(jìn)行解讀。


          那么這個(gè) IR 中到底都有些什么呢?我們可以可視化一下其中的 layer1 看看:


          jit_layer1 = jit_model.layer1print(jit_layer1.graph)
          # graph(%self.6 : __torch__.torch.nn.modules.container.Sequential,# ? ? ? %4 : Float(1, 64, 56, 56, strides=[200704, 3136, 56, 1], requires_grad=0, device=cpu)):# ? %1 : __torch__.torchvision.models.resnet.___torch_mangle_10.BasicBlock = prim::GetAttr[name="1"](%self.6)# ? %2 : __torch__.torchvision.models.resnet.BasicBlock = prim::GetAttr[name="0"](%self.6)# ? %6 : Tensor = prim::CallMethod[name="forward"](%2, %4)# ? %7 : Tensor = prim::CallMethod[name="forward"](%1, %6)# ? return (%7)


          是不是有點(diǎn)摸不著頭腦?TorchScript 有它自己對(duì)于 Graph 以及其中元素的定義,對(duì)于第一次接觸的人來(lái)說(shuō)可能比較陌生,但是沒(méi)關(guān)系,我們還有另一種可視化方式:


          print(jit_layer1.code)
          # def forward(self,# ? ? argument_1: Tensor) -> Tensor:# ? _0 = getattr(self, "1")# ? _1 = (getattr(self, "0")).forward(argument_1, )# ? return (_0).forward(_1, )


          沒(méi)錯(cuò),就是代碼!TorchScript 的 IR 是可以還原成 python 代碼的,如果你生成了一個(gè) TorchScript 模型并且想知道它的內(nèi)容對(duì)不對(duì),那么可以通過(guò)這樣的方式來(lái)做一些簡(jiǎn)單的檢查。


          剛才的例子中我們使用 trace 的方法生成 IR。除了 trace 之外,PyTorch 還提供了另一種生成 TorchScript 模型的方法:script。這種方式會(huì)直接解析網(wǎng)絡(luò)定義的 python 代碼,生成抽象語(yǔ)法樹(shù) AST,因此這種方法可以解決一些 trace 無(wú)法解決的問(wèn)題,比如對(duì) branch/loop 等數(shù)據(jù)流控制語(yǔ)句的建圖。script 方式的建圖有很多有趣的特性,會(huì)在未來(lái)的分享中做專(zhuān)題分析,敬請(qǐng)期待。


          模型優(yōu)化



          聰明的同學(xué)可能發(fā)現(xiàn)了,上面的可視化中只有 resnet18 里 forward 的部分,其中的子模塊信息是不是丟失了呢?如果沒(méi)有丟失,那么怎么樣才能確定子模塊的內(nèi)容是否正確呢?別擔(dān)心,還記得我們說(shuō)過(guò) TorchScript 支持對(duì)網(wǎng)絡(luò)的優(yōu)化嗎,這里我們就可以用一個(gè) pass 解決這個(gè)問(wèn)題:


          # 調(diào)用inline pass,對(duì)graph做變換torch._C._jit_pass_inline(jit_layer1.graph)print(jit_layer1.code)
          # def forward(self,# ? ? argument_1: Tensor) -> Tensor:# ? _0 = getattr(self, "1")# ? _1 = getattr(self, "0")# ? _2 = _1.bn2# ? _3 = _1.conv2# ? _4 = _1.bn1# ? input = torch._convolution(argument_1, _1.conv1.weight, None, [1, 1], [1, 1], [1, 1], False, [0, 0], 1, False, False, True, True)# ? _5 = _4.running_var# ? _6 = _4.running_mean# ? _7 = _4.bias# ? input0 = torch.batch_norm(input, _4.weight, _7, _6, _5, False, 0.10000000000000001, 1.0000000000000001e-05, True)# ? input1 = torch.relu_(input0)# ? input2 = torch._convolution(input1, _3.weight, None, [1, 1], [1, 1], [1, 1], False, [0, 0], 1, False, False, True, True)# ? _8 = _2.running_var# ? _9 = _2.running_mean# ? _10 = _2.bias# ? out = torch.batch_norm(input2, _2.weight, _10, _9, _8, False, 0.10000000000000001, 1.0000000000000001e-05, True)# ? input3 = torch.add_(out, argument_1, alpha=1)# ? input4 = torch.relu_(input3)# ? _11 = _0.bn2# ? _12 = _0.conv2# ? _13 = _0.bn1# ? input5 = torch._convolution(input4, _0.conv1.weight, None, [1, 1], [1, 1], [1, 1], False, [0, 0], 1, False, False, True, True)# ? _14 = _13.running_var# ? _15 = _13.running_mean# ? _16 = _13.bias# ? input6 = torch.batch_norm(input5, _13.weight, _16, _15, _14, False, 0.10000000000000001, 1.0000000000000001e-05, True)# ? input7 = torch.relu_(input6)# ? input8 = torch._convolution(input7, _12.weight, None, [1, 1], [1, 1], [1, 1], False, [0, 0], 1, False, False, True, True)# ? _17 = _11.running_var# ? _18 = _11.running_mean# ? _19 = _11.bias# ? out0 = torch.batch_norm(input8, _11.weight, _19, _18, _17, False, 0.10000000000000001, 1.0000000000000001e-05, True)# ? input9 = torch.add_(out0, input4, alpha=1)# ? return torch.relu_(input9)


          這里我們就能看到卷積、batch_norm、relu 等熟悉的算子了。


          上面代碼中我們使用了一個(gè)名為 inline 的 pass,將所有子模塊進(jìn)行內(nèi)聯(lián),這樣我們就能看見(jiàn)更完整的推理代碼。pass 是一個(gè)來(lái)源于編譯原理的概念,一個(gè) TorchScript 的 pass 會(huì)接收一個(gè)圖,遍歷圖中所有元素進(jìn)行某種變換,生成一個(gè)新的圖。我們這里用到的 inline 起到的作用就是將模塊調(diào)用展開(kāi),盡管這樣做并不能直接影響執(zhí)行效率,但是它其實(shí)是很多其他 pass 的基礎(chǔ)。PyTorch 中定義了非常多的 pass 來(lái)解決各種優(yōu)化任務(wù),未來(lái)我們會(huì)做一些更詳細(xì)的介紹。


          序列化



          不管是哪種方法創(chuàng)建的 TorchScript 都可以進(jìn)行序列化,比如:


          # 將模型序列化jit_model.save('jit_model.pth')# 加載序列化后的模型jit_model = torch.jit.load('jit_model.pth')


          序列化后的模型不再與 python 相關(guān),可以被部署到各種平臺(tái)上。


          PyTorch 提供了可以用于 TorchScript 模型推理的 c++ API,序列化后的模型終于可以不依賴(lài) python 進(jìn)行推理了:


          // 加載生成的torchscript模型auto module = torch::jit::load('jit_model.pth');// 根據(jù)任務(wù)需求讀取數(shù)據(jù)std::vector inputs = ...;// 計(jì)算推理結(jié)果auto output = module.forward(inputs).toTensor();


          與其他組件的關(guān)系?


          與 torch.onnx 的關(guān)系




          ONNX 是業(yè)界廣泛使用的一種神經(jīng)網(wǎng)絡(luò)中間表示,PyTorch 自然也對(duì) ONNX 提供了支持。torch.onnx.export 函數(shù)可以幫助我們把 PyTorch 模型轉(zhuǎn)換成 ONNX 模型,這個(gè)函數(shù)會(huì)使用 trace 的方式記錄 PyTorch 的推理過(guò)程。聰明的同學(xué)可能已經(jīng)想到了,沒(méi)錯(cuò),ONNX 的導(dǎo)出,使用的正是 TorchScript 的 trace 工具。具體步驟如下:


          1. 使用 trace 的方式先生成一個(gè) TorchScipt 模型,如果你轉(zhuǎn)換的本身就是 TorchScript 模型,則可以跳過(guò)這一步。

          2. 使用許多 pass 對(duì) 1 中生成的模型進(jìn)行變換,其中對(duì) ONNX 導(dǎo)出最重要的一個(gè) pass 就是ToONNX,這個(gè) pass 會(huì)進(jìn)行一個(gè)映射,將 TorchScript 中 prim、aten 空間下的算子映射到onnx空間下的算子。

          3. 使用 ONNX 的 proto 格式對(duì)模型進(jìn)行序列化,完成 ONNX 的導(dǎo)出。


          關(guān)于 ONNX 導(dǎo)出的實(shí)現(xiàn)以及算子映射的方式將會(huì)在未來(lái)的分享中詳細(xì)展開(kāi)。


          與 torch.fx 的關(guān)系



          PyTorch1.9 開(kāi)始添加了 torch.fx 工具,根據(jù)官方的介紹,它由符號(hào)追蹤器 (symbolic tracer),中間表示(IR), Python 代碼生成 (Python code generation) 等組件組成,實(shí)現(xiàn)了 python->python 的翻譯。是不是和 TorchScript 看起來(lái)有點(diǎn)像?


          其實(shí)他們之間聯(lián)系不大,可以算是互相垂直的兩個(gè)工具,為解決兩個(gè)不同的任務(wù)而誕生。


          TorchScript 的主要用途是進(jìn)行模型部署,需要記錄生成一個(gè)便于推理優(yōu)化的 IR,對(duì)計(jì)算圖的編輯通常都是面向性能提升等等,不會(huì)給模型本身添加新的功能。


          FX 的主要用途是進(jìn)行 python->python 的翻譯,它的 IR 中節(jié)點(diǎn)類(lèi)型更簡(jiǎn)單,比如函數(shù)調(diào)用、屬性提取等等,這樣的 IR 學(xué)習(xí)成本更低更容易編輯。使用 FX 來(lái)編輯圖通常是為了實(shí)現(xiàn)某種特定功能,比如給模型插入量化節(jié)點(diǎn)等,避免手動(dòng)編輯網(wǎng)絡(luò)造成的重復(fù)勞動(dòng)。


          這兩個(gè)工具可以同時(shí)使用,比如使用 FX 工具編輯模型來(lái)讓訓(xùn)練更便利、功能更強(qiáng)大;然后用 TorchScript 將模型加速部署到特定平臺(tái)。



          推薦閱讀

          深入理解生成模型VAE

          DropBlock的原理和實(shí)現(xiàn)

          SOTA模型Swin Transformer是如何煉成的!

          有碼有顏!你要的生成模型VQ-VAE來(lái)了!

          集成YYDS!讓你的模型更快更準(zhǔn)!

          輔助模塊加速收斂,精度大幅提升!移動(dòng)端實(shí)時(shí)的NanoDet-Plus來(lái)了!

          SimMIM:一種更簡(jiǎn)單的MIM方法

          SSD的torchvision版本實(shí)現(xiàn)詳解


          機(jī)器學(xué)習(xí)算法工程師


          ? ??? ? ? ? ? ? ? ? ? ? ????????? ??一個(gè)用心的公眾號(hào)


          瀏覽 78
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美熟妇另类久久久久久不卡 | 影音先锋午夜性爱av | 最新国产免费地址 | 另类激情网 | 亚洲久久在线 |