Freigeben über


Ausführen mehrerer ML-Modelle in einer Kette

Windows ML unterstützt hohe Auslastung und Ausführung von Modellketten durch sorgfältige Optimierung des GPU-Pfads. Modellketten werden durch zwei oder mehr Modelle definiert, die sequenziell ausgeführt werden, wobei die Ausgaben eines Modells zu den Eingaben für das nächste Modell in der Kette werden.

Um zu erklären, wie Modelle mit Windows ML effizient verkettet werden können, verwenden wir ein ONNX-Modell mit FNS-Candy-Stilübertragung als Beispiel. Sie finden diesen Modelltyp im FNS-Candy Style Transfer-Beispielordner in unserem GitHub.

Angenommen, wir möchten eine Kette ausführen, die aus zwei Instanzen desselben FNS-Candy Modells besteht, hier als mosaik.onnx bezeichnet. Der Anwendungscode würde ein Bild an das erste Modell in der Kette übergeben, die Ausgabe berechnen und dann dieses transformierte Bild an eine andere Instanz von FNS-Candy übergeben, wodurch ein endgültiges Bild erzeugt wird.

Die folgenden Schritte veranschaulichen, wie Sie dies mit Windows ML erreichen.

Hinweis

In einem echten Wortszenario würden Sie wahrscheinlich zwei verschiedene Modelle verwenden, aber dies sollte ausreichen, um die Konzepte zu veranschaulichen.

  1. Zunächst laden wir das Mosaik.onnx-Modell , damit wir es verwenden können.
std::wstring filePath = L"path\\to\\mosaic.onnx"; 
LearningModel model = LearningModel::LoadFromFilePath(filePath);
string filePath = "path\\to\\mosaic.onnx";
LearningModel model = LearningModel.LoadFromFilePath(filePath);
  1. Anschließend erstellen wir zwei identische Sitzungen auf der Standard-GPU des Geräts mit demselben Modell wie den Eingabeparameter.
LearningModelSession session1(model, LearningModelDevice(LearningModelDeviceKind::DirectX));
LearningModelSession session2(model, LearningModelDevice(LearningModelDeviceKind::DirectX));
LearningModelSession session1 = 
  new LearningModelSession(model, new LearningModelDevice(LearningModelDeviceKind.DirectX));
LearningModelSession session2 = 
  new LearningModelSession(model, new LearningModelDevice(LearningModelDeviceKind.DirectX));

Hinweis

Um die Leistungsvorteile der Verkettung zu erzielen, müssen Sie für alle Ihre Modelle identische GPU-Sitzungen erstellen. Wenn dies nicht der Fall ist, führt dies zu einer zusätzlichen Datenverschiebung aus der GPU in die CPU, was die Leistung beeinträchtigen würde.

  1. Die folgenden Codezeilen erstellen Bindungen für jede Sitzung:
LearningModelBinding binding1(session1);
LearningModelBinding binding2(session2);
LearningModelBinding binding1 = new LearningModelBinding(session1);
LearningModelBinding binding2 = new LearningModelBinding(session2);
  1. Als Nächstes binden wir eine Eingabe für unser erstes Modell. Wir übergeben ein Bild, das sich im gleichen Pfad wie unser Modell befindet. In diesem Beispiel wird das Bild als "fish_720.png" bezeichnet.
//get the input descriptor
ILearningModelFeatureDescriptor input = model.InputFeatures().GetAt(0);
//load a SoftwareBitmap
hstring imagePath = L"path\\to\\fish_720.png";

// Get the image and bind it to the model's input
try
{
  StorageFile file = StorageFile::GetFileFromPathAsync(imagePath).get();
  IRandomAccessStream stream = file.OpenAsync(FileAccessMode::Read).get();
  BitmapDecoder decoder = BitmapDecoder::CreateAsync(stream).get();
  SoftwareBitmap softwareBitmap = decoder.GetSoftwareBitmapAsync().get();
  VideoFrame videoFrame = VideoFrame::CreateWithSoftwareBitmap(softwareBitmap);
  ImageFeatureValue image = ImageFeatureValue::CreateFromVideoFrame(videoFrame);
  binding1.Bind(input.Name(), image);
}
catch (...)
{
  printf("Failed to load/bind image\n");
}
//get the input descriptor
ILearningModelFeatureDescriptor input = model.InputFeatures[0];
//load a SoftwareBitmap
string imagePath = "path\\to\\fish_720.png";

// Get the image and bind it to the model's input
try
{
    StorageFile file = await StorageFile.GetFileFromPathAsync(imagePath);
    IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.Read);
    BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream);
    SoftwareBitmap softwareBitmap = await decoder.GetSoftwareBitmapAsync();
    VideoFrame videoFrame = VideoFrame.CreateWithSoftwareBitmap(softwareBitmap);
    ImageFeatureValue image = ImageFeatureValue.CreateFromVideoFrame(videoFrame);
    binding1.Bind(input.Name, image);
}
catch
{
    Console.WriteLine("Failed to load/bind image");
}
  1. Damit das nächste Modell in der Kette die Ergebnisse der Bewertung des ersten Modells verwenden kann, müssen wir einen leeren Ausgabe-Tensor erstellen und die Ausgabe binden, damit wir einen Marker zum Verketten haben.
//get the output descriptor
ILearningModelFeatureDescriptor output = model.OutputFeatures().GetAt(0);
//create an empty output tensor 
std::vector<int64_t> shape = {1, 3, 720, 720};
TensorFloat outputValue = TensorFloat::Create(shape); 
//bind the (empty) output
binding1.Bind(output.Name(), outputValue);
//get the output descriptor
ILearningModelFeatureDescriptor output = model.OutputFeatures[0];
//create an empty output tensor 
List<long> shape = new List<long> { 1, 3, 720, 720 };
TensorFloat outputValue = TensorFloat.Create(shape);
//bind the (empty) output
binding1.Bind(output.Name, outputValue);

Hinweis

Sie müssen den TensorFloat-Datentyp beim Binden der Ausgabe verwenden. Dadurch wird verhindert, dass die De-Tensorisierung ausgeführt wird, sobald die Auswertung für das erste Modell abgeschlossen ist. Daher wird auch verhindert, dass zusätzliche GPU-Warteschlangen für Lade- und Bindungsvorgänge für das zweite Modell ausgeführt werden.

  1. Nun führen wir die Auswertung des ersten Modells aus und binden die Ausgaben an die Eingabe des nächsten Modells:
//run session1 evaluation
session1.EvaluateAsync(binding1, L"");
//bind the output to the next model input
binding2.Bind(input.Name(), outputValue);
//run session2 evaluation
auto session2AsyncOp = session2.EvaluateAsync(binding2, L"");
//run session1 evaluation
await session1.EvaluateAsync(binding1, "");
//bind the output to the next model input
binding2.Bind(input.Name, outputValue);
//run session2 evaluation
LearningModelEvaluationResult results = await session2.EvaluateAsync(binding2, "");
  1. Abschließend rufen wir die endgültige Ausgabe ab, die nach dem Ausführen beider Modelle erstellt wurde, indem wir die folgende Codezeile verwenden.
auto finalOutput = session2AsyncOp.get().Outputs().First().Current().Value();
var finalOutput = results.Outputs.First().Value;

Das ist alles! Beide Modelle können jetzt sequenziell ausgeführt werden, indem sie die verfügbaren GPU-Ressourcen optimal nutzen.

Hinweis

Verwenden Sie die folgenden Ressourcen für Hilfe zu Windows ML:

  • Um technische Fragen zu Windows ML zu stellen oder zu beantworten, verwenden Sie bitte das Windows-Machine-Learning--Tag auf Stack Overflow-.
  • Um einen Fehler zu melden, melden Sie bitte ein Problem in unserem GitHub.