Partager via


Exécution de plusieurs modèles ML dans une chaîne

Windows ML assure la charge et l’exécution hautes performances des chaînes de modèles en optimisant soigneusement son chemin de GPU. Les chaînes de modèles sont définies par deux modèles ou plus qui s’exécutent séquentiellement, où les sorties d’un modèle deviennent les entrées du modèle suivant vers le bas de la chaîne.

Afin d’expliquer comment chaîner efficacement des modèles avec Windows ML, nous allons utiliser un modèle ONNX de transfert de style FNS-Candy comme exemple de jouet. Vous trouverez ce type de modèle dans le dossier exemple de transfert de style FNS-Candy dans notre GitHub.

Supposons que nous voulons exécuter une chaîne composée de deux instances du même modèle FNS-Candy, ici appelée mosaïque.onnx. Le code de l’application passerait une image au premier modèle de la chaîne, laisserait calculer les sorties, puis passerait cette image transformée à une autre instance de FNS-Candy, produisant une image finale.

Les étapes suivantes illustrent comment effectuer cette opération à l’aide de Windows ML.

Remarque

Dans un scénario réel, vous utiliseriez probablement deux modèles différents, mais cela devrait suffire pour illustrer les concepts.

  1. Tout d’abord, chargeons le modèle mosaïque.onnx pour pouvoir l’utiliser.
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. Ensuite, créons deux sessions identiques sur le GPU par défaut de l’appareil à l’aide du même modèle que le paramètre d’entrée.
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));

Remarque

Pour tirer parti des avantages en matière de performances de chaînage, vous devez créer des sessions GPU identiques pour tous vos modèles. Cela entraînerait un déplacement de données supplémentaire hors du GPU et dans le processeur, ce qui réduirait les performances.

  1. Les lignes de code suivantes créent des liaisons pour chaque session :
LearningModelBinding binding1(session1);
LearningModelBinding binding2(session2);
LearningModelBinding binding1 = new LearningModelBinding(session1);
LearningModelBinding binding2 = new LearningModelBinding(session2);
  1. Nous allons à présent lier une entrée pour notre premier modèle. Nous allons transmettre une image qui se trouve dans le même chemin que notre modèle. Dans cet exemple, l’image est appelée «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. Pour que le modèle suivant de la chaîne utilise les sorties de l’évaluation du premier modèle, nous devons créer un tensoriel de sortie vide et lier la sortie afin que nous ayons un marqueur à chaîner avec :
//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);

Remarque

Vous devez utiliser le type de données TensorFloat lors de la liaison de la sortie. Cela empêchera la dé-ensorisation de se produire une fois l’évaluation du premier modèle terminée, ce qui évite également la mise en file d’attente GPU supplémentaire pour les opérations de chargement et de liaison pour le deuxième modèle.

  1. À présent, nous exécutons l’évaluation du premier modèle et liez ses sorties à l’entrée du modèle suivant :
//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. Enfin, récupérons la sortie finale produite après l’exécution des deux modèles à l’aide de la ligne de code suivante.
auto finalOutput = session2AsyncOp.get().Outputs().First().Current().Value();
var finalOutput = results.Outputs.First().Value;

C’est tout ! Vos deux modèles peuvent désormais s’exécuter de manière séquentielle en mettant à profit les ressources GPU disponibles.

Remarque

Utilisez les ressources suivantes pour obtenir de l’aide sur Windows ML :

  • Pour poser ou répondre à des questions techniques sur Windows ML, utilisez la balise windows-machine-learning sur Stack Overflow.
  • Pour signaler un bogue, veuillez signaler un problème sur notre GitHub.