منذ عام تقريبًا ، قدم مطورو PyTorch مجتمع TorchScript ، وهي أداة تسمح لك بإيجاد حل قابل للاستبدال من خط أنابيب في بيثون مع بضع نقرات بالماوس يمكن تضمينها في نظام C ++. أشارك أدناه تجربة استخدامه وأحاول وصف المزالق التي صودفت في هذا المسار. سأولي اهتمامًا خاصًا لتنفيذ المشروع على Windows ، لأنه على الرغم من إجراء الأبحاث في ML عادةً على Ubuntu ، غالبًا ما يكون الحل النهائي (فجأة!) مطلوبًا بموجب "windows".
يمكن العثور على نموذج التعليمة البرمجية لتصدير نموذج ومشروع C ++ باستخدام النموذج في مستودع التخزين على GitHub .

مطورو PyTorch لا ينخدعون. تتيح لك الأداة الجديدة بالفعل تحويل مشروع بحثي في PyTorch إلى كود مضمن في نظام C ++ في بضعة أيام عمل ، وببعض المهارات بشكل أسرع.
ظهر TorchScript في PyTorch الإصدار 1.0 ويستمر في التطور والتغيير. إذا كان الإصدار الأول قبل عام ممتلئًا بالأخطاء وكان أكثر تجريبية ، فإن الإصدار الحالي 1.3 على الأقل في النقطة الثانية أصبح مختلفًا بشكل ملحوظ: لم يعد بالإمكان تسميته تجريبيًا ، فهو مناسب تمامًا للاستخدام العملي. سوف أركز عليها.
يوجد في قلب TorchScript مترجم مستقل (خالٍ من بيثون) بلغة تشبه الثعبان ، فضلاً عن أدوات لتحويل برنامج مكتوب ببيثون و PyTorch إليه ، وطرق لحفظ وتحميل الوحدات النمطية الناتجة ، ومكتبة لاستخدامها في C ++. للعمل ، سوف تضطر إلى إضافة عدة مكتبات الارتباط الحيوي (DLL) إلى المشروع بوزن إجمالي يبلغ حوالي 70 ميغابايت (لنظام Windows) للعمل على وحدة المعالجة المركزية و 300 ميغابايت لإصدار GPU. يدعم TorchScript معظم ميزات PyTorch والميزات الرئيسية للغة بيثون. لكن مكتبات الأطراف الثالثة ، مثل OpenCV أو NumPy ، يجب أن تنسى. لحسن الحظ ، فإن العديد من الوظائف من NumPy لها تناظرية في PyTorch.
تحويل خط الأنابيب إلى نموذج PyTorch على TorchScript
يوفر TorchScript طريقتين لتحويل شفرة Python إلى تنسيقها الداخلي: التتبع والبرمجة النصية (التتبع والبرمجة النصية). لماذا اثنين؟ لا ، من الواضح ، بالطبع ، أن الاثنين أفضل من واحد ...

لكن في حالة هذه الأساليب ، اتضح ، كما في القول المأثور المعروف ، حول الانحرافات اليمنى واليسرى: كلاهما أسوأ. حسنًا ، العالم ليس مثاليًا. فقط في حالة معينة ، تحتاج إلى اختيار الموقف الأكثر ملاءمة.
طريقة التتبع بسيطة جدا. يتم أخذ عينة من البيانات (عادة ما تتم تهيئتها بأرقام عشوائية) ، ويتم إرسالها إلى وظيفة أو طريقة الفصل التي تهمنا ، وتقوم PyTorch بإنشاء وتخزين الرسم البياني للحساب بنفس الطريقة التي تفعل بها عادة عند تدريب شبكة عصبية. فويلا - النص جاهز:
import torch import torchvision model = torchvision.models.resnet34(pretrained = True) model.eval() sample = torch.rand(1, 3, 224, 224) scripted_model = torch.jit.trace(model, sample)
المثال أعلاه ينتج كائن من فئة ScriptModule. يمكن حفظه
scripted_model.save('my_script.pth')
ثم قم بتحميله في برنامج C ++ (المزيد حول ذلك أدناه ) أو في رمز Python بدلاً من الكائن الأصلي:
نموذج شفرة بيثون باستخدام نموذج محفوظ import cv2 from torchvision.transforms import Compose, ToTensor, Normalize transforms = Compose([ToTensor(), Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])]) img = cv2.resize(cv2.imread('pics/cat.jpg'), (224,224)) img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) x = transforms(img).unsqueeze(0)
tensor(282) tensor(12.8130, grad_fn=<SelectBackward>)
يمكن أن يظهر ScriptModule
الناتج في أي مكان. يتم استخدام nn.Module
بشكل شائع.
بالطريقة الموضحة ، يمكنك تتبع مثيلات فئة ووظائف nn.Module
(في الحالة الأخيرة ، يتم torch._C.Function
على مثيل من فئة torch._C.Function
).
تتمتع هذه الطريقة (التتبع) بميزة هامة: وبهذه الطريقة يمكنك تحويل أي رمز Python لا يستخدم مكتبات خارجية تقريبًا. ولكن هناك عيبًا لا يقل أهمية: بالنسبة لأي فروع ، لن يتم تذكر سوى الفرع الذي تم تنفيذه على بيانات الاختبار:
def my_abs(x): if x.max() >= 0: return x else: return -x my_abs_traced = torch.jit.trace(my_abs, torch.tensor(0)) print(my_abs_traced(torch.tensor(1)), my_abs_traced(torch.tensor(-1)))
c:\miniconda3\lib\site-packages\ipykernel_launcher.py:2: TracerWarning: Converting a tensor to a Python boolean might cause the trace to be incorrect. We can't record the data flow of Python values, so this value will be treated as a constant in the future. This means that the trace might not generalize to other inputs! tensor(1) tensor(-1)
عفوا! يبدو أن هذا ليس ما نود ، أليس كذلك؟ من الجيد أن يتم إصدار رسالة تحذير على الأقل (TracerWarning). يجدر الانتباه إلى هذه الرسائل.
هنا الطريقة الثانية تأتي لمساعدتنا - البرمجة النصية:
my_abs_script = torch.jit.script(my_abs) print(my_abs_script(torch.tensor(1)), my_abs_script(torch.tensor(-1)))
tensor(1) tensor(1)
الصيحة ، يتم استلام النتيجة المتوقعة! تحلل البرمجة النصية بشكل متكرر شفرة Python وتحولها إلى كود بلغتها الخاصة. في الإخراج ، نحصل أيضًا على فئة ScriptModule
(للوحدات النمطية) أو torch._C.Function
(للوظائف). يبدو ، هنا هو ، السعادة! ولكن هناك مشكلة أخرى تنشأ: لغة TorchScript الداخلية مكتوبة بقوة ، على عكس بيثون. يتم تحديد نوع كل متغير بواسطة الواجب الأول ، ونوع وسيطات الدالة بشكل افتراضي هو Tensor
. لذلك ، على سبيل المثال ، نمط مألوف
def my_func(x): y = None if x.max() > 0: y = x return y my_func = torch.jit.script(my_func)
سوف تفشل تتبع.
خطأ تتبع يشبه هذا RuntimeError Traceback (most recent call last) <ipython-input-9-25414183a687> in <module>() ----> 1 my_func = torch.jit.script(my_func) d:\programming\3rd_party\pytorch\pytorch_ovod_1.3.0a0_de394b6\torch\jit\__init__.py in script(obj, optimize, _frames_up, _rcb) 1224 if _rcb is None: 1225 _rcb = _gen_rcb(obj, _frames_up) -> 1226 fn = torch._C._jit_script_compile(qualified_name, ast, _rcb, get_default_args(obj)) 1227 # Forward docstrings 1228 fn.__doc__ = obj.__doc__ RuntimeError: Variable 'y' previously has type None but is now being assigned to a value of type Tensor : at <ipython-input-8-75677614fca6>:4:8 def my_func(x): y = None if x.max() > 0: y = x ~ <--- HERE return y
تجدر الإشارة إلى أنه على الرغم من حدوث خطأ عند torch.jit.script
، تتم الإشارة أيضًا إلى المكان الذي تسبب فيه في التعليمات البرمجية النصية.
حتى النقاط التي تبدأ بعد الثوابت تلعب دورًا:
def my_func(x): if x.max() > 0: y = 1.25 else: y = 0 return y my_func = torch.jit.script(my_func)
سوف يعطي خطأ RuntimeError Traceback (most recent call last) <ipython-input-10-0a5f18586763> in <module>() 5 y = 0 6 return y ----> 7 my_func = torch.jit.script(my_func) d:\programming\3rd_party\pytorch\pytorch_ovod_1.3.0a0_de394b6\torch\jit\__init__.py in script(obj, optimize, _frames_up, _rcb) 1224 if _rcb is None: 1225 _rcb = _gen_rcb(obj, _frames_up) -> 1226 fn = torch._C._jit_script_compile(qualified_name, ast, _rcb, get_default_args(obj)) 1227 # Forward docstrings 1228 fn.__doc__ = obj.__doc__ d:\programming\3rd_party\pytorch\pytorch_ovod_1.3.0a0_de394b6\torch\jit\__init__.py in _rcb(name) 1240 # closure rcb fails 1241 result = closure_rcb(name) -> 1242 if result: 1243 return result 1244 return stack_rcb(name) RuntimeError: bool value of Tensor with more than one value is ambiguous
لأنه من الضروري عدم كتابة 0
، ولكن 0.
بحيث يكون النوع في كلا الفرعين هو نفسه! مدلل ، كما تعلمون ، مع الثعبان الخاص بك!
هذه مجرد بداية قائمة التغييرات التي تحتاج إلى إجرائها لشفرة python بحيث يمكن تحويلها بنجاح إلى وحدة TorchScript. سأذكر الحالات الأكثر نموذجية بمزيد من التفاصيل لاحقًا . من حيث المبدأ ، لا يوجد علم صواريخ هنا ، ويمكن تصحيح التعليمات البرمجية الخاصة بك وفقًا لذلك. لكن في معظم الأحيان لا أريد إصلاح وحدات الجهات الخارجية ، بما في ذلك الوحدات القياسية من torchvision
، وكالعادة لا تكون عادةً مناسبة للبرمجة النصية.
لحسن الحظ ، يمكن دمج كلتا التقنيتين: ما يجري كتابته يتم كتابته وما لا يتم كتابته هو تتبع:
class MyModule(torch.nn.Module): def __init__(self): super(MyModule, self).__init__() self.resnet = torchvision.models.resnet34(pretrained = True)
في المثال أعلاه ، يتم استخدام التتبع لتضمين وحدة غير قابلة للبرمجة في وحدة نمطية حيث لا يوجد ما يكفي من التتبع والنص البرمجي ضروري. هناك موقف عكسي. على سبيل المثال ، إذا كنا بحاجة إلى تحميل نموذج إلى ONNX ، فسيتم استخدام التتبع. لكن النموذج المتتبع قد يتضمن وظائف TorchScript ، لذلك يمكن تنفيذ المنطق الذي يتطلب وجود فروع وحلقات هناك! ويرد مثال في الوثائق الرسمية ل torch.onnx .
يتم وصف الميزات التي توفرها PyTorch لإنشاء وحدات TorchScript بمزيد من التفصيل في الوثائق الرسمية torch.jit
. على وجه الخصوص ، لم أذكر طريقة ملائمة لاستخدام torch.jit.trace
و torch.jit.script
في شكل torch.jit.script
، حول خصوصيات تصحيح الأخطاء البرمجية النصية. هذا وأكثر من ذلك بكثير في الوثائق.
نحن ندرج النموذج في مشروع C ++
لسوء الحظ ، تقتصر الوثائق الرسمية على أمثلة من النموذج "إضافة 2 torch.ones
المتولدة باستخدام torch.ones
". قمت بإعداد مثال لمشروع أقرب إلى الواقع يرسل صورة من OpenCV إلى الشبكة العصبية ويتلقى النتائج في شكل موتر الاستجابة ، مجموعة من المتغيرات ، صورة مع نتائج تجزئة.
للحصول على مثال للعمل ، تحتاج إلى برامج نصية محفوظة محفوظة باستخدام ResNet34 والتجزئة باستخدام DeepLabV3. لإعداد هذه البرامج النصية ، تحتاج إلى تشغيل هذه المفكرة jupyter .
نحن بحاجة إلى مكتبة torchlib
. يمكنك الحصول عليه بعدة طرق:
- إذا كان PyTorch مثبتًا لديك بالفعل باستخدام
pip install
، فيمكنك العثور عليه في دليل Python: <Miniconda3>\Lib\site-packages\torch
؛ - إذا كان لديك PyTorch مترجم من المصدر ، فهناك:
<My Pytorch repo>\build\lib.win-amd64-3.6\torch
؛ - أخيرًا ، يمكنك تنزيل المكتبة بشكل منفصل من pytorch.org باختيار Language = C ++ ، وفك ضغط الأرشيف.
رمز C ++ بسيط للغاية. من الضروري:
- تضمين ملف الرأس
#include <torch/script.h>
- تحميل نموذج
torch::jit::script::Module module = torch::jit::load("../resnet34_infer.pth");
- إعداد البيانات
torch::Tensor tensor = torch::from_blob(img.data, { img.rows, img.cols, 3 }, torch::kByte);
- استدعاء وظيفة
forward
والحصول على نتيجة
auto output = module.forward( { tensor } )
- الحصول على البيانات من النتيجة. تعتمد كيفية القيام بذلك على ما تقوم به الشبكة العصبية. بالمناسبة ، في الحالة العامة ، يمكنه أيضًا قبول صورة واحدة فقط ، لذلك من الأفضل أن ننظر إلى الكود المصدري للمثال بالكامل ، هناك خيارات مختلفة. على سبيل المثال ، للحصول على بيانات من الموتر أحادي البعد من النوع float:
float* data = static_cast<float*>(output.toTensor().data_ptr());
- هناك واحد أكثر دقة. لا تنسَ إدراج التماثلية
with torch.no_grad()
في الكود حتى لا تهدر الموارد عند حساب التدرجات التي لا نحتاج إليها وتخزينها. لسوء الحظ ، لا يمكن تضمين هذا الأمر في البرنامج النصي ، لذلك يجب عليك إضافته إلى رمز C ++:
torch::NoGradGuard no_grad;
تم شرح كيفية بناء مشروع باستخدام CMake في الدليل الرسمي . لكن لم يتم الكشف عن موضوع المشروع في Visual Studio ، لذلك سأصفه بمزيد من التفاصيل. سيتعين عليك تعديل إعدادات المشروع يدويًا:
- اختبرت في Visual Studio 2017. لا أستطيع أن أقول عن الإصدارات الأخرى.
- يجب تثبيت مجموعة أدوات v14.11 v141 (علامة اختيار
"VC++ 2017 version 15.4 v14.11 toolset"
في المثبت VS). - يجب أن تكون المنصة
x64
. - في
General → Platform Toolset
حدد v141(Visual Studio 2017)
- في
C/C++ → General → Additional Include Directories
إضافة C/C++ → General → Additional Include Directories
<libtorch dir>\include
- في
Linker → General → Additional Library Directories
أضف <libtorch dir>\lib
- في
Linker → Input → Additional Dependencies
إضافة torch.lib; c10.lib
torch.lib; c10.lib
. على الإنترنت ، يكتبون أن caffe2.lib
قد لا تزال هناك حاجة ، وبالنسبة إلى وحدة معالجة الرسومات وشيء آخر من <libtorch dir>\lib
، ولكن في الإصدار الحالي ، كانت إضافة هاتين المكتبتين كافية لي. ربما هذه معلومات قديمة. - يكتبون أيضًا أنك بحاجة إلى ضبط
C/C++ → Language → Conformance Mode
= No
، لكنني لم أر الفرق.
أيضًا ، يجب عدم إعلان متغير __cplusplus
في المشروع. محاولة إضافة /Zc:__cplusplus
سينتج عنه أخطاء في ivalue.h
ملف ivalue.h
.
في المشروع المرفق ، يتم إعداد إعدادات المسار (ليس فقط TorchLib ، ولكن أيضًا على OpenCV و CUDA) في ملف الدعائم ، قبل التجميع ، تحتاج إلى تسجيلهم هناك وفقًا للتهيئة المحلية. هذا ، في الواقع ، هو كل شيء.
ماذا نأخذ في الاعتبار
إذا بدا لك أن العملية الموصوفة بسيطة للغاية ، فلن يخدعك حدسك. هناك عدد من الفروق الدقيقة التي يجب مراعاتها من أجل تحويل نموذج PyTorch المكتوب في Python إلى TorchScript. سأدرج أدناه تلك التي واجهتني. لقد ذكرت بالفعل البعض ، لكنني كررت جمع كل شيء في مكان واحد.

- نوع المتغيرات التي تم تمريرها إلى الوظيفة هو Tensor بشكل افتراضي. إذا كان هذا غير مقبول في بعض الحالات (المتكررة جدًا) ، فسيتعين عليك الإعلان عن الأنواع يدويًا باستخدام التعليقات التوضيحية لنوع نمط MyPy ، وهذا شيء مثل هذا:
def calc_letter_statistics(self, cls_preds: List[Tensor], cls_thresh: float)->Tuple[int, Tuple[Tensor, Tensor, Tensor]]
او نحو ذلك:
def calc_letter_statistics(self, cls_preds, cls_thresh):
- تتم كتابة المتغيرات بشدة ويتم تحديد النوع ، إذا لم يتم تحديده صراحة ، بواسطة الواجب الأول. الإنشاءات المألوفة للنموذج
x=[]; for ...: x.append(y)
x=[]; for ...: x.append(y)
، لأن في وقت التعيين []
لا يمكن للمترجم معرفة أي نوع سيكون في القائمة. لذلك ، يجب عليك تحديد النوع بشكل صريح ، على سبيل المثال:
from typing import List x: List[float] = []
أو (آخر "على سبيل المثال")
from torch import Tensor from typing import Dict, Tuple, List x: Dict[int: Tuple[float, List[Tensor], List[List[int]]]] = {}
- في المثال أعلاه ، هي الأسماء التي يجب استيرادها ، حيث يتم حياكة هذه الأسماء في شفرة TorchScript. نهج بديل قانوني على ما يبدو
import torch import typing x: typing.List[torch.Tensor] = []
سينتج خطأ منشئ كتابة غير معروف منشئ الكتابة عند البرمجة النصية
- تصميم مألوف آخر يجب أن تتخلى عنه:
x = None if smth: x = torch.tensor([1,2,3])
هناك خياران. أو قم بتعيين Tensor في كل مرة (حقيقة أنه من أبعاد مختلفة ليس مخيفًا):
x = torch.tensor(0) if smth: x = torch.tensor([1,2,3])
ولا تنسى أن تبحث عن ما سوف ينكسر بعد هذا الاستبدال. أو حاول أن تكتب بأمانة:
x: Optional[Tensor] = None if smth: x = torch.tensor([1,2,3])
ولكن بعد ذلك مع الاستخدام الإضافي لـ x
حيث يتوقع حدوث الموتر ، من المحتمل أن نحصل على خطأ: توقع قيمة النوع 'Tensor' للوسيطة 'x' ولكن بدلاً من ذلك ، وجدنا النوع 'اختياري [Tensor]'.
لا تنس الكتابة ، على سبيل المثال ، x=0.
أثناء المهمة الأولى x=0.
بدلاً من المعتاد x=0
، إلخ ، إذا كان المتغير x
يجب أن يكون من النوع float
.
إذا استخدمنا في مكان ما التهيئة القديمة x = torch.Tensor(...)
عبر x = torch.Tensor(...)
، فسيتعين عليك x = torch.Tensor(...)
عنه واستبداله بإصدار أصغر بحرف صغير x = torch.tensor(...)
. خلاف ذلك ، أثناء البرمجة النصية سوف يطير: Unknown buildin op: aten :: Tensor. فيما يلي بعض الاقتراحات: aten :: tensor . يبدو أنهم حتى يفسرون ما هي المشكلة ، ومن الواضح ما يجب القيام به. ومع ذلك ، فمن الواضح إذا كنت تعرف الإجابة الصحيحة بالفعل.
تتم torch.jit.script
الشفرة في سياق الوحدة النمطية حيث torch.jit.script
استدعاء torch.jit.script
. لذلك ، إذا كان هناك مكان ما ، في أحشاء الطبقة أو الوظيفة النصية ، على سبيل المثال ، math.pow
، math.pow
إلى إضافة import math
إلى وحدة math.pow
. ومن الأفضل كتابة البرنامج النصي للفئة حيث يتم الإعلان عنها: إما باستخدام @torch.jit.script
الديكور @torch.jit.script
، أو عن طريق الإعلان عن وظيفة إضافية بجانبها تجعل ScriptModule خارجها. خلاف ذلك ، نحصل على رسالة خطأ في الرياضيات غير محددة القيمة عندما نحاول ترجمة فئة من وحدة نمطية ، على ما يبدو ، تم إجراء استيراد math
.
إذا كان لديك في مكان ما بناء النموذج my_tensor[my_tensor < 10] = 0
أو ما شابه ، فسوف تحصل على خطأ مشفر عند البرمجة النصية:
*aten::index_put_(Tensor(a!) self, Tensor?[] indices, Tensor values, bool accumulate=False) -> (Tensor(a!)):* *Expected a value of type 'Tensor' for argument 'values' but instead found type 'int'.* *aten::index_put_(Tensor(a!) self, Tensor[] indices, Tensor values, bool accumulate=False) -> (Tensor(a!)):* *Expected a value of type 'List[Tensor]' for argument 'indices' but instead found type 'List[Optional[Tensor]]'.*
ما تحتاجه هو استبدال الرقم my_tensor[my_tensor < 10] = torch.tensor(0.).to(my_tensor.device)
: my_tensor[my_tensor < 10] = torch.tensor(0.).to(my_tensor.device)
. ولا تنسى أ) مراسلات أنواع my_tensor
و tensor التي تم إنشاؤها (في هذه الحالة ، تطفو) و b) حول .to(my_tensor.device)
. إذا نسيت الثانية ، فسيتم كتابة كل شيء ، ولكن بالفعل في عملية العمل مع GPU ، سوف تشعر بالانزعاج ، والتي ستبدو وكأنها كلمة خفية CUDA error: تمت مصادفة وصول غير قانوني للذاكرة ، دون الإشارة إلى مكان حدوث الخطأ!
لا تنسَ أنه يتم افتراضيًا nn.Module
، وبالتالي يتم إنشاء نماذج من torchvision في "وضع القطار" (لن تصدق ذلك ، لكن يبدو أن هناك مثل هذا الوضع ). في هذه الحالة ، يتم استخدام Dropout والحيل الأخرى من وضع القطار ، والتي إما أن تكسر التتبع أو تؤدي إلى نتائج غير كافية عند تنفيذها. تذكر استدعاء model.eval()
قبل البرمجة النصية أو التتبع.
بالنسبة للوظائف والفئات العادية ، تحتاج إلى كتابة السيناريو ، بالنسبة إلى nn.Module - مثيل
محاولة بطريقة نصية للوصول إلى متغير عمومي
cls_thresh = 0.3 class MyModule(torch.nn.Module): ... x = r < cls_thresh ...
سيؤدي ذلك إلى حدوث خطأ في البرمجة النصية لقيمة python للنوع 'float' لا يمكن استخدامه كقيمة . من الضروري جعل المتغير سمة في المُنشئ:
cls_thresh = 0.3 class MyModule(torch.nn.Module): def __init__(self): ... self.cls_thresh = cls_thresh ... x = r < self.cls_thresh ...
- تنشأ دقة أخرى إذا تم استخدام سمة الفئة كمعلمة شريحة:
class FPN(nn.Module): def __init__(self, block, num_blocks, num_layers =5): ... self.num_layers = num_layers def forward(self, x): ... return (p3, p4, p5, p6, p7)[:self.num_layers]
يؤدي يجب أن تكون مؤشرات الشريحة tuple خطأ البرمجة النصية ثوابت عدد صحيح . من الضروري الإشارة إلى أن سمة num_layers ثابتة ولن تتغير:
class FPN(nn.Module): num_layers: torch.jit.Final[int] def __init__(self, block, num_blocks, num_layers =5): ...
- في بعض الحالات ، حيث كان الموتر يستخدم لتناسب بشكل طبيعي ، تحتاج إلى تمرير الرقم بشكل صريح:
xx1 = x1.clamp(min=x1[i])
يلقي خطأ عند البرمجة النصية Expected a value of type 'Optional[number]' for argument 'min' but instead found type 'Tensor'.
. حسنًا ، من رسالة الخطأ ، من الواضح ما يجب القيام به:
xx1 = x1.clamp(min=x1[i].item())
المشاكل المذكورة أعلاه تحدث عند البحث عن المفقودين. بسببهم ، لا يمكن عادةً ترجمة الحلول الجاهزة في TorchScript ، وعليك إما تدليك الكود المصدري لفترة طويلة (إذا كان الكود المصدري مناسبًا للتحرير) ، أو استخدام التتبع. لكن تتبع الفروق الدقيقة الخاصة به:
- لا تعمل بنيات النموذج في التتبع
tensor_a.to(tensor_b.device)
يتم تثبيت الجهاز الذي تم تحميل الموتر عليه في وقت التتبع ولا يتغير أثناء التنفيذ. يمكن التغلب على هذه المشكلة جزئيًا عن طريق إعلان الموتر عضوًا في nn.Module
النوع Parameter
. ثم ، عند تحميل النموذج ، سيتم تشغيله على الجهاز المحدد في وظيفة torch.jit.load
.
خاتمة
كل ما سبق ، بالطبع ، يخلق مشاكل. لكن TorchScript يتيح لك الجمع بين النموذج وإرساله إلى الحل ككل ورمز Python الذي يوفر المعالجة قبل وبعد المعالجة. نعم ، والوقت اللازم لإعداد الحل للتجميع ، على الرغم من الصعوبات المذكورة أعلاه ، أقل بكثير من تكلفة إنشاء حل ، ولكن هنا PyTorch تقدم مزايا رائعة ، وبالتالي فإن اللعبة تستحق كل هذا العناء.
