Vor etwa einem Jahr stellten PyTorch-Entwickler die TorchScript- Community vor, ein Tool, mit dem Sie aus einer Python-Pipline eine veräußerliche Lösung erstellen können, die sich mit ein paar Codezeilen und wenigen Mausklicks in ein C ++ - System integrieren lässt. Im Folgenden teile ich die Erfahrungen mit seiner Verwendung und versuche, die Fallstricke auf diesem Weg zu beschreiben. Ich werde ein besonderes Augenmerk auf die Implementierung des Projekts unter Windows legen, denn obwohl in der Regel unter Ubuntu in ML geforscht wird, ist die endgültige Lösung häufig (plötzlich!) Unter den „Fenstern“ erforderlich.
Beispielcode zum Exportieren eines Modells und eines C ++ - Projekts unter Verwendung des Modells finden Sie im Repository auf GitHub .

PyTorch-Entwickler lassen sich nicht täuschen. Mit dem neuen Tool können Sie ein Forschungsprojekt in PyTorch in wenigen Arbeitstagen und mit etwas Geschick schneller in Code verwandeln, der in ein C ++ - System eingebettet ist.
TorchScript wurde in PyTorch Version 1.0 veröffentlicht und wird ständig weiterentwickelt und geändert. Wenn die erste Version vor einem Jahr voller Fehler und experimenteller war, dann unterscheidet sich die aktuelle Version 1.3 zumindest in Bezug auf den zweiten Punkt merklich: Sie können sie nicht mehr als experimentell bezeichnen, sie ist für den praktischen Gebrauch durchaus geeignet. Ich werde mich auf sie konzentrieren.
Das Herzstück von TorchScript ist ein eigenständiger (Python-freier) Compiler einer Python-ähnlichen Sprache sowie Tools zum Konvertieren eines in Python und PyTorch geschriebenen Programms, Methoden zum Speichern und Laden der resultierenden Module und eine Bibliothek für deren Verwendung in C ++. Um zu funktionieren, müssen Sie dem Projekt mehrere DLLs mit einem Gesamtgewicht von ca. 70 MB (für Windows) hinzufügen, um mit der CPU und 300 MB für die GPU-Version arbeiten zu können. TorchScript unterstützt die meisten Funktionen von PyTorch und die Hauptfunktionen der Python-Sprache. Bibliotheken von Drittanbietern wie OpenCV oder NumPy müssen jedoch vergessen werden. Glücklicherweise haben viele Funktionen von NumPy ein Analogon in PyTorch.
Konvertieren Sie die Pipeline in das PyTorch-Modell in TorchScript
TorchScript bietet zwei Möglichkeiten, Python-Code in sein internes Format zu konvertieren: Tracing und Scripting (Tracing und Scripting). Warum zwei? Nein, es ist natürlich klar, dass zwei besser sind als einer ...

Bei diesen Methoden zeigt sich jedoch, wie beim bekannten Aphorismus, eine Abweichung von links und rechts: Beide sind schlimmer. Nun, die Welt ist nicht perfekt. Nur in einer bestimmten Situation müssen Sie diejenige auswählen, die besser geeignet ist.
Die Rückverfolgungsmethode ist sehr einfach. Eine Stichprobe von Daten wird entnommen (normalerweise durch Zufallszahlen initialisiert), an die Funktion oder Methode der Klasse gesendet, die uns interessiert, und PyTorch erstellt und speichert den Berechnungsgraphen auf die gleiche Weise, wie dies normalerweise beim Trainieren eines neuronalen Netzwerks der Fall ist. Voila - das Skript ist fertig:
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)
Das obige Beispiel erzeugt ein Objekt der ScriptModule-Klasse. Es kann gespeichert werden
scripted_model.save('my_script.pth')
und lade es dann in ein C ++ - Programm (mehr dazu weiter unten ) oder in Python-Code anstelle des ursprünglichen Objekts:
Beispiel-Python-Code unter Verwendung eines gespeicherten Modells 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>)
Das resultierende ScriptModule
kann überall nn.Module
erscheinen, wo nn.Module
häufig verwendet wird.
Auf die beschriebene Weise können Sie Instanzen der Klasse und Funktionen nn.Module
verfolgen (im letzteren Fall wird eine Instanz der Klasse torch._C.Function
).
Diese Methode (Ablaufverfolgung) hat einen wichtigen Vorteil: Auf diese Weise können Sie fast jeden Python-Code konvertieren, der keine externen Bibliotheken verwendet. Es gibt jedoch einen ebenso wichtigen Nachteil: Für alle Verzweigungen wird nur die Verzweigung gespeichert, die für die Testdaten ausgeführt wurde:
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)
Hoppla! Dies scheint nicht das zu sein, was wir möchten, oder? Es ist gut, dass mindestens eine Warnmeldung (TracerWarning) ausgegeben wird. Es lohnt sich, auf solche Botschaften zu achten.
Hier kommt die zweite Methode zu unserer Hilfe - Scripting:
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)
Hurra, das erwartete Ergebnis ist eingetroffen! Das Skript analysiert rekursiv Python-Code und konvertiert ihn in Code in seiner eigenen Sprache. Am Ausgang erhalten wir auch die ScriptModule
Klasse (für Module) oder torch._C.Function
(für Funktionen). Es scheint, hier ist es Glück! Es entsteht jedoch ein weiteres Problem: Die interne Sprache von TorchScript ist im Gegensatz zu Python stark typisiert. Der Typ jeder Variablen wird durch die erste Zuweisung bestimmt, der Typ der Funktionsargumente ist standardmäßig Tensor
. Daher zum Beispiel ein bekanntes Muster
def my_func(x): y = None if x.max() > 0: y = x return y my_func = torch.jit.script(my_func)
Die Ablaufverfolgung schlägt fehl.
Ein Trace-Fehler sieht so aus 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
Es ist bemerkenswert, dass, obwohl beim torch.jit.script
ein Fehler auftritt, auch die Stelle angegeben wird, die ihn im torch.jit.script
verursacht hat.
Auch Punkte nach Konstanten spielen eine Rolle:
def my_func(x): if x.max() > 0: y = 1.25 else: y = 0 return y my_func = torch.jit.script(my_func)
wird einen Fehler geben 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
Weil es notwendig ist, nicht 0
, sondern 0.
zu schreiben, damit der Typ in beiden Zweigen gleich ist! Weißt du, mit deiner Python verwöhnt!
Dies ist nur der Anfang der Liste der Änderungen, die Sie am Python-Code vornehmen müssen, damit er erfolgreich in ein TorchScript-Modul umgewandelt werden kann. Ich werde die typischsten Fälle später detaillierter auflisten . Grundsätzlich gibt es hier keine Raketenwissenschaft und Ihr Code kann entsprechend korrigiert werden. In den meisten torchvision
möchte ich jedoch keine Module von torchvision
, auch keine Standardmodule von torchvision
. Sie eignen sich normalerweise nicht für Skripterstellung.
Glücklicherweise können beide Technologien kombiniert werden: Was gescriptet wird, wird gescriptet, und was nicht gescriptet wird, wird verfolgt:
class MyModule(torch.nn.Module): def __init__(self): super(MyModule, self).__init__() self.resnet = torchvision.models.resnet34(pretrained = True)
Im obigen Beispiel wird die Ablaufverfolgung verwendet, um ein Modul einzuschließen, das in einem Modul, in dem nicht genügend Ablaufverfolgung vorhanden ist und das Skripting erforderlich ist, nicht skriptfähig ist. Es gibt eine umgekehrte Situation. Wenn wir beispielsweise ein Modell zu ONNX hochladen müssen, wird die Ablaufverfolgung verwendet. Das verfolgte Modell kann jedoch TorchScript-Funktionen enthalten, sodass dort Logik implementiert werden kann, die Verzweigungen und Schleifen erfordert! Ein Beispiel finden Sie in der offiziellen Dokumentation zu torch.onnx .
Die von PyTorch bereitgestellten Funktionen zum Erstellen von TorchScript-Modulen werden in der offiziellen Dokumentation und torch.jit
ausführlicher beschrieben. Insbesondere erwähnte ich keine bequeme Möglichkeit, torch.jit.trace
und torch.jit.script
in Form von Dekoratoren zu verwenden, was die Besonderheiten des Debuggens von torch.jit.trace
torch.jit.script
. Dies und vieles mehr finden Sie in der Dokumentation.
Wir fügen das Modell in ein C ++ - Projekt ein
Leider beschränkt sich die offizielle Dokumentation auf Beispiele der Form " torch.ones
2 Tensoren, die mit torch.ones
erzeugt torch.ones
". Ich habe ein realitätsnahes Beispiel für ein Projekt vorbereitet, das ein Bild von OpenCV an das neuronale Netzwerk sendet und die Ergebnisse in Form eines Antworttensors, eines Tupels von Variablen und eines Bildes mit Segmentierungsergebnissen empfängt.
Damit das Beispiel funktioniert, benötigen Sie gespeicherte Klassifizierungsskripte mit ResNet34 und eine Segmentierung mit DeepLabV3. Um diese Skripte vorzubereiten, müssen Sie diesen Jupyter-Editor ausführen .
Wir brauchen die torchlib
. Sie können es auf verschiedene Arten bekommen:
- Wenn Sie PyTorch bereits mit
pip install
, finden Sie es im Python-Verzeichnis: <Miniconda3>\Lib\site-packages\torch
; - Wenn Sie PyTorch aus dem Quellcode kompiliert haben, ist es dort:
<My Pytorch repo>\build\lib.win-amd64-3.6\torch
; - Schließlich können Sie die Bibliothek separat von pytorch.org herunterladen, indem Sie Language = C ++ wählen und das Archiv entpacken.
C ++ Code ist recht einfach. Es ist notwendig:
- Header-Datei einschließen
#include <torch/script.h>
- Modell herunterladen
torch::jit::script::Module module = torch::jit::load("../resnet34_infer.pth");
- Daten vorbereiten
torch::Tensor tensor = torch::from_blob(img.data, { img.rows, img.cols, 3 }, torch::kByte);
- Rufumleitungsfunktion und Ergebnis abrufen
auto output = module.forward( { tensor } )
- Holen Sie sich Daten aus dem Ergebnis. Wie das geht, hängt davon ab, was das neuronale Netzwerk zurückgibt. Übrigens kann es im allgemeinen Fall auch nicht nur ein Bild aufnehmen, daher ist es besser, sich den Quellcode des gesamten Beispiels anzuschauen, es gibt verschiedene Möglichkeiten. So erhalten Sie beispielsweise Daten von einem eindimensionalen Tensor vom Typ float:
float* data = static_cast<float*>(output.toTensor().data_ptr());
- Es gibt noch eine weitere Feinheit. Vergessen Sie nicht, das Analogon
with torch.no_grad()
in den Code einzufügen, um keine Ressourcen für die Berechnung und Speicherung der Farbverläufe zu verschwenden, die wir nicht benötigen. Leider kann dieser Befehl nicht in das Skript aufgenommen werden, sodass Sie ihn zum C ++ - Code hinzufügen müssen:
torch::NoGradGuard no_grad;
Wie Sie mit CMake ein Projekt erstellen, wird im offiziellen Handbuch beschrieben . Das Thema des Projekts in Visual Studio wird dort jedoch nicht veröffentlicht, daher werde ich es genauer beschreiben. Sie müssen die Projekteinstellungen manuell anpassen:
- Ich habe es auf Visual Studio 2017 getestet. Über andere Versionen kann ich nichts sagen.
- Das Toolset v14.11 v141 muss installiert sein (markieren Sie
"VC++ 2017 version 15.4 v14.11 toolset"
im VS-Installationsprogramm). - Die Plattform muss
x64
. v141(Visual Studio 2017)
General → Platform Toolset
die v141(Visual Studio 2017)
<libtorch dir>\include
in C/C++ → General → Additional Include Directories
<libtorch dir>\include
<libtorch dir>\lib
Linker → General → Additional Library Directories
<libtorch dir>\lib
torch.lib; c10.lib
Linker → Input → Additional Dependencies
die torch.lib; c10.lib
torch.lib; c10.lib
. Im Internet schreiben sie, dass caffe2.lib
möglicherweise noch benötigt wird, und für die GPU und etwas anderes aus <libtorch dir>\lib
, aber in der aktuellen Version reichte es mir, diese beiden Bibliotheken hinzuzufügen. Möglicherweise handelt es sich hierbei um veraltete Informationen.- Sie schreiben auch, dass Sie
C/C++ → Language → Conformance Mode
= No
einstellen müssen, aber ich habe den Unterschied nicht gesehen.
Außerdem sollte die Variable __cplusplus
NICHT im Projekt deklariert werden. Der Versuch, die /Zc:__cplusplus
hinzuzufügen /Zc:__cplusplus
führt zu Kompilierungsfehlern in der Datei ivalue.h
.
Im angehängten Projekt werden die Pfadeinstellungen (nicht nur zu TorchLib, sondern auch zu OpenCV und CUDA) in die Requisitendatei übernommen . Vor dem Zusammenbau müssen Sie sie dort entsprechend Ihrer lokalen Konfiguration registrieren. Das ist in der Tat alles.
Was Sie sonst noch beachten sollten
Wenn Ihnen der beschriebene Vorgang zu einfach erschien, hat Sie Ihre Intuition nicht getäuscht. Es gibt eine Reihe von Nuancen, die berücksichtigt werden müssen, um ein in Python geschriebenes PyTorch-Modell in TorchScript zu konvertieren. Ich werde unten diejenigen auflisten, denen ich begegnen musste. Ich habe bereits einige erwähnt, aber ich wiederhole, um alles an einem Ort zu sammeln.

- Der Typ der Variablen, die an die Funktion übergeben werden, ist standardmäßig Tensor. Wenn dies in einigen (sehr häufigen) Fällen nicht akzeptabel ist, müssen Sie die Typen manuell mithilfe von Typanmerkungen im MyPy-Stil deklarieren.
def calc_letter_statistics(self, cls_preds: List[Tensor], cls_thresh: float)->Tuple[int, Tuple[Tensor, Tensor, Tensor]]
oder so:
def calc_letter_statistics(self, cls_preds, cls_thresh):
- Variablen sind stark typisiert und der Typ wird, falls nicht explizit angegeben, durch die erste Zuweisung bestimmt. Bekannte Konstruktionen der Form
x=[]; for ...: x.append(y)
x=[]; for ...: x.append(y)
muss editiert werden, weil Zum Zeitpunkt der Zuweisung von []
Compiler nicht herausfinden, welcher Typ in der Liste enthalten sein wird. Daher müssen Sie den Typ explizit angeben, zum Beispiel:
from typing import List x: List[float] = []
oder (ein anderes "zum Beispiel")
from torch import Tensor from typing import Dict, Tuple, List x: Dict[int: Tuple[float, List[Tensor], List[List[int]]]] = {}
- Im obigen Beispiel müssen die Namen importiert werden, da diese Namen in TorchScript-Code eingenäht sind. Alternativer, scheinbar legaler Ansatz
import torch import typing x: typing.List[torch.Tensor] = []
führt dazu, dass ein unbekannter Typkonstruktor eingibt. Listenfehler beim Skripten
- Ein weiteres bekanntes Design, von dem Sie sich trennen müssen:
x = None if smth: x = torch.tensor([1,2,3])
Es gibt zwei Möglichkeiten. Oder weisen Sie Tensor beide Male zu (die Tatsache, dass es unterschiedliche Dimensionen hat, ist nicht beängstigend):
x = torch.tensor(0) if smth: x = torch.tensor([1,2,3])
und vergessen Sie nicht zu suchen, was nach einem solchen Austausch brechen wird. Oder versuchen Sie ehrlich zu schreiben:
x: Optional[Tensor] = None if smth: x = torch.tensor([1,2,3])
Bei weiterer Verwendung von x
wo der Tensor erwartet wird, wird höchstwahrscheinlich ein Fehler angezeigt : Es wurde ein Wert vom Typ 'Tensor' für das Argument 'x' erwartet, stattdessen wurde der Typ 'Optional [Tensor]' gefunden.
Vergessen Sie nicht, beispielsweise bei der ersten Zuweisung x=0.
zu schreiben x=0.
anstelle des üblichen x=0
usw., wenn die Variable x
vom Typ float
.
Wenn wir irgendwo die altmodische Initialisierung des Tensors über x = torch.Tensor(...)
, müssen Sie sich davon x = torch.Tensor(...)
und diese durch eine jüngere Version mit einem kleinen Buchstaben x = torch.tensor(...)
ersetzen. Andernfalls wird es während der Skripterstellung fliegen: Unknown builtin op: aten :: Tensor. Hier einige Vorschläge: aten :: tensor . Es scheint, dass sie sogar erklären, was das Problem ist, und es ist klar, was getan werden muss. Es ist jedoch klar, ob Sie bereits die richtige Antwort kennen.
Der Code wird im Kontext des Moduls geschrieben, in dem torch.jit.script
aufgerufen wird. Wenn also irgendwo in den Eingeweiden der math.pow
oder -funktion beispielsweise math.pow
wird, müssen Sie import math
zum Kompilierungsmodul hinzufügen. Und es ist besser, die Klasse dort zu @torch.jit.script
, wo sie deklariert ist: entweder mit dem Dekorator @torch.jit.script
oder durch Deklaration einer zusätzlichen Funktion daneben, die ScriptModule daraus macht. Andernfalls erhalten wir eine undefinierte mathematische Fehlermeldung, wenn wir versuchen, eine Klasse aus einem Modul zu kompilieren, in dem anscheinend ein math
Import durchgeführt wurde.
Wenn Sie irgendwo eine Konstruktion der Form my_tensor[my_tensor < 10] = 0
oder ähnlich haben, erhalten Sie einen kryptischen Fehler bei der Skripterstellung:
*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]]'.*
Sie müssen die Zahl durch den Tensor ersetzen: my_tensor[my_tensor < 10] = torch.tensor(0.).to(my_tensor.device)
. Und vergessen Sie nicht a) die Entsprechung der Typen my_tensor
und created tensor (in diesem Fall float) und b) about .to(my_tensor.device)
. Wenn Sie die Sekunde vergessen, wird alles gescriptet, aber bereits während der Arbeit mit der GPU werden Sie verärgert sein, was wie der CUDA-Fehler mit den kryptischen Worten aussieht : Es wurde ein unzulässiger Speicherzugriff festgestellt , ohne anzugeben, wo der Fehler aufgetreten ist!
Vergessen Sie nicht, dass standardmäßig nn.Module
und dementsprechend auch Modelle von torchvision im "Zugmodus" erstellt werden (Sie werden es nicht glauben, aber es stellt sich heraus, dass es einen solchen Modus gibt ). In diesem Fall werden Dropout- und andere Tricks aus dem Zugmodus verwendet, die entweder die Spur brechen oder bei der Ausführung zu unzureichenden Ergebnissen führen. Denken Sie daran, model.eval()
bevor Sie model.eval()
oder model.eval()
.
Für Funktionen und gewöhnliche Klassen müssen Sie den Typ für nn.Module - eine Instanz - skripten
Versuchen Sie in einer Skriptmethode, auf eine globale Variable zuzugreifen
cls_thresh = 0.3 class MyModule(torch.nn.Module): ... x = r < cls_thresh ...
führt zu einem Skriptfehler in der Form, dass der Python-Wert vom Typ 'float' nicht als Wert verwendet werden kann . Es ist notwendig, die Variable im Konstruktor zu einem Attribut zu machen:
cls_thresh = 0.3 class MyModule(torch.nn.Module): def __init__(self): ... self.cls_thresh = cls_thresh ... x = r < self.cls_thresh ...
- Eine weitere Subtilität ergibt sich, wenn das Klassenattribut als Slice-Parameter verwendet wird:
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]
verursacht Skriptfehler Tupel-Slice-Indizes müssen Integer-Konstanten sein . Es muss angegeben werden, dass das Attribut num_layers konstant ist und sich nicht ändert:
class FPN(nn.Module): num_layers: torch.jit.Final[int] def __init__(self, block, num_blocks, num_layers =5): ...
- In einigen Fällen, in denen der Tensor normalerweise passte, müssen Sie die Zahl explizit übergeben:
xx1 = x1.clamp(min=x1[i])
Löst einen Fehler aus, wenn ein Skript erstellt wird. Es wurde Expected a value of type 'Optional[number]' for argument 'min' but instead found type 'Tensor'.
. Aus der Fehlermeldung geht hervor, was zu tun ist:
xx1 = x1.clamp(min=x1[i].item())
Die obigen Probleme treten bei der Ablaufverfolgung auf. Aus diesem Grund ist es normalerweise nicht möglich, fertige Lösungen in TorchScript zu kompilieren, und Sie müssen entweder den Quellcode für längere Zeit massieren (wenn der Quellcode zum Bearbeiten geeignet ist) oder die Ablaufverfolgung verwenden. Aber die Spur hat ihre eigenen Nuancen:
- Konstrukte des Formulars funktionieren im Trace nicht
tensor_a.to(tensor_b.device)
Das Gerät, auf das der Tensor geladen ist, ist zum Zeitpunkt der Verfolgung festgelegt und ändert sich während der Ausführung nicht. Dieses Problem kann teilweise nn.Module
werden, indem der Tensor als Mitglied von nn.Module
Typ Parameter
deklariert wird. Beim Laden des Modells wird dann das in der Funktion torch.jit.load
angegebene Gerät torch.jit.load
.
Nachwort
All das schafft natürlich Probleme. Mit TorchScript können Sie jedoch das Modell selbst und den Python-Code, der die Vor- und Nachbearbeitung ermöglicht, kombinieren und als Ganzes an die Lösung senden. Ja, und die Zeit, um die Lösung für die Kompilierung vorzubereiten, ist trotz der oben genannten Schwierigkeiten unvergleichlich geringer als die Kosten für die Erstellung einer Lösung, aber hier bietet PyTorch große Vorteile, sodass das Spiel die Kerze wert ist.
