كيفية تكوين صداقات PyTorch و C ++. باستخدام TorchScript

منذ عام تقريبًا ، قدم مطورو 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) # add batch dimension scripted_model = torch.jit.load('my_script.pth') y = scripted_model(x) print(y[0].argmax(), y[0][y[0].argmax()]) 

 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) #       torch.jit.script(my_module) #    -   resnet34. #     self.resnet  ScriptModule. self.resnet.eval() # NB:     !  -  ! self.resnet = torch.jit.trace(self.resnet, torch.rand((1,3,224,224), dtype=torch.float)) def forward(self, x): if x.shape[2] < 224 or x.shape[3] < 224: return torch.tensor(0) else: return self.resnet(x) my_module = MyModule() my_module = torch.jit.script(my_module) 

في المثال أعلاه ، يتم استخدام التتبع لتضمين وحدة غير قابلة للبرمجة في وحدة نمطية حيث لا يوجد ما يكفي من التتبع والنص البرمجي ضروري. هناك موقف عكسي. على سبيل المثال ، إذا كنا بحاجة إلى تحميل نموذج إلى 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 . يمكنك الحصول عليه بعدة طرق:


  1. إذا كان PyTorch مثبتًا لديك بالفعل باستخدام pip install ، فيمكنك العثور عليه في دليل Python: <Miniconda3>\Lib\site-packages\torch ؛
  2. إذا كان لديك PyTorch مترجم من المصدر ، فهناك: <My Pytorch repo>\build\lib.win-amd64-3.6\torch ؛
  3. أخيرًا ، يمكنك تنزيل المكتبة بشكل منفصل من pytorch.org باختيار Language = C ++ ، وفك ضغط الأرشيف.

رمز C ++ بسيط للغاية. من الضروري:


  1. تضمين ملف الرأس
     #include <torch/script.h> 
  2. تحميل نموذج
     torch::jit::script::Module module = torch::jit::load("../resnet34_infer.pth"); 
  3. إعداد البيانات
     torch::Tensor tensor = torch::from_blob(img.data, { img.rows, img.cols, 3 }, torch::kByte); 
  4. استدعاء وظيفة forward والحصول على نتيجة
     auto output = module.forward( { tensor } ) 
  5. الحصول على البيانات من النتيجة. تعتمد كيفية القيام بذلك على ما تقوم به الشبكة العصبية. بالمناسبة ، في الحالة العامة ، يمكنه أيضًا قبول صورة واحدة فقط ، لذلك من الأفضل أن ننظر إلى الكود المصدري للمثال بالكامل ، هناك خيارات مختلفة. على سبيل المثال ، للحصول على بيانات من الموتر أحادي البعد من النوع float:
     float* data = static_cast<float*>(output.toTensor().data_ptr()); 
  6. هناك واحد أكثر دقة. لا تنسَ إدراج التماثلية with torch.no_grad() في الكود حتى لا تهدر الموارد عند حساب التدرجات التي لا نحتاج إليها وتخزينها. لسوء الحظ ، لا يمكن تضمين هذا الأمر في البرنامج النصي ، لذلك يجب عليك إضافته إلى رمز C ++:
     torch::NoGradGuard no_grad; 

تم شرح كيفية بناء مشروع باستخدام CMake في الدليل الرسمي . لكن لم يتم الكشف عن موضوع المشروع في Visual Studio ، لذلك سأصفه بمزيد من التفاصيل. سيتعين عليك تعديل إعدادات المشروع يدويًا:


  1. اختبرت في Visual Studio 2017. لا أستطيع أن أقول عن الإصدارات الأخرى.
  2. يجب تثبيت مجموعة أدوات v14.11 v141 (علامة اختيار "VC++ 2017 version 15.4 v14.11 toolset" في المثبت VS).
  3. يجب أن تكون المنصة x64 .
  4. في General → Platform Toolset حدد v141(Visual Studio 2017)
  5. في C/C++ → General → Additional Include Directories إضافة C/C++ → General → Additional Include Directories <libtorch dir>\include
  6. في Linker → General → Additional Library Directories أضف <libtorch dir>\lib
  7. في Linker → Input → Additional Dependencies إضافة torch.lib; c10.lib torch.lib; c10.lib . على الإنترنت ، يكتبون أن caffe2.lib قد لا تزال هناك حاجة ، وبالنسبة إلى وحدة معالجة الرسومات وشيء آخر من <libtorch dir>\lib ، ولكن في الإصدار الحالي ، كانت إضافة هاتين المكتبتين كافية لي. ربما هذه معلومات قديمة.
  8. يكتبون أيضًا أنك بحاجة إلى ضبط 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): # type: (List[Tensor], float)->Tuple[int, Tuple[Tensor, Tensor, Tensor]] 

  • تتم كتابة المتغيرات بشدة ويتم تحديد النوع ، إذا لم يتم تحديده صراحة ، بواسطة الواجب الأول. الإنشاءات المألوفة للنموذج 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 تقدم مزايا رائعة ، وبالتالي فإن اللعبة تستحق كل هذا العناء.


Source: https://habr.com/ru/post/ar480328/


All Articles