Esecuzione di più modelli ML in una catena

Windows ML supporta il carico e l'esecuzione di catene di modelli ad alte prestazioni ottimizzandone attentamente il percorso GPU. Le catene di modelli sono definite da due o più modelli eseguiti in sequenza, in cui gli output di un modello diventano gli input per il modello successivo verso il basso nella catena.

Per spiegare come concatenare in modo efficiente i modelli con Windows ML, si userà un modello ONNX di trasferimento dello stile FNS-Candy come esempio di toy. È possibile trovare questo tipo di modello nella cartella di esempio di trasferimento di stile FNS-Candy in GitHub.

Si supponga di voler eseguire una catena composta da due istanze dello stesso modello di FNS-Candy, qui denominato mosaic.onnx. Il codice dell'applicazione passa un'immagine al primo modello della catena, consente di calcolare gli output e quindi di passare l'immagine trasformata a un'altra istanza di FNS-Candy, generando un'immagine finale.

La procedura seguente illustra come eseguire questa operazione usando Windows ML.

Nota

In uno scenario reale è molto probabile che si usino due modelli diversi, ma questo dovrebbe essere sufficiente per illustrare i concetti.

  1. Prima di tutto, caricare il modello mosaic.onnx in modo da poterlo usare.
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. Creare quindi due sessioni identiche nella GPU predefinita del dispositivo usando lo stesso modello del parametro di input.
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));

Nota

Per sfruttare i vantaggi del concatenamento delle prestazioni, è necessario creare sessioni GPU identiche per tutti i modelli. Questa operazione non comporta un ulteriore spostamento dei dati dalla GPU e nella CPU, riducendo così le prestazioni.

  1. Le righe di codice seguenti creeranno associazioni per ogni sessione:
LearningModelBinding binding1(session1);
LearningModelBinding binding2(session2);
LearningModelBinding binding1 = new LearningModelBinding(session1);
LearningModelBinding binding2 = new LearningModelBinding(session2);
  1. Successivamente, verrà associato un input per il primo modello. Si passerà un'immagine che si trova nello stesso percorso del modello. In questo esempio l'immagine è denominata "fish_720.png".
//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. Affinché il modello successivo nella catena usi gli output della valutazione del primo modello, è necessario creare un tensore di output vuoto e associare l'output in modo da avere un marcatore con cui concatenare:
//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);

Nota

È necessario usare il tipo di dati TensorFloat quando si associa l'output. Ciò impedirà la de-tensorizzazione dopo il completamento della valutazione per il primo modello, evitando quindi ulteriori code GPU per le operazioni di caricamento e associazione per il secondo modello.

  1. A questo punto, viene eseguita la valutazione del primo modello e ne vengono associati gli output all'input del modello successivo:
//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. Infine, recuperare l'output finale generato dopo l'esecuzione di entrambi i modelli usando la riga di codice seguente.
auto finalOutput = session2AsyncOp.get().Outputs().First().Current().Value();
var finalOutput = results.Outputs.First().Value;

L'operazione è terminata. Entrambi i modelli ora possono essere eseguiti in sequenza rendendo la maggior parte delle risorse GPU disponibili.

Nota

Per informazioni su Windows Machine Learning, usa le risorse seguenti:

  • Per porre domande tecniche o rispondere a domande tecniche su Windows Machine Learning, usa il tag windows-machine-learning in Stack Overflow.
  • Per segnalare un bug, registra il problema in GitHub.