Udostępnij przez


Wykonywanie wielu modeli uczenia maszynowego w łańcuchu

Usługa Windows ML obsługuje wysokie obciążenie i wykonywanie łańcuchów modeli, starannie optymalizując ścieżkę procesora GPU. Łańcuchy modeli są definiowane przez co najmniej dwa modele, które są wykonywane sekwencyjnie, gdzie dane wyjściowe jednego modelu stają się danymi wejściowymi następnego modelu w dół łańcucha.

Aby wyjaśnić, jak wydajnie połączyć modele z systemem Windows ML, użyjmy modelu ONNX FNS-Candy Style Transfer ONNX jako przykładu toy. Ten typ modelu można znaleźć w folderze przykładowym FNS-Candy Style Transfer w witrynie GitHub.

Załóżmy, że chcemy wykonać łańcuch składający się z dwóch wystąpień tego samego modelu FNS-Candy, tutaj o nazwie mosaic.onnx. Kod aplikacji przekaże obraz do pierwszego modelu w łańcuchu, pozwoli mu obliczyć dane wyjściowe, a następnie przekazać ten przekształcony obraz do innego wystąpienia usługi FNS-Candy, tworząc końcowy obraz.

Poniższe kroki ilustrują, jak to osiągnąć przy użyciu uczenia maszynowego z systemem Windows.

Uwaga / Notatka

W rzeczywistym scenariuszu najprawdopodobniej użyjesz dwóch różnych modeli, ale powinno to wystarczyć, aby zilustrować koncepcje.

  1. Najpierw załadujmy model mosaic.onnx , abyśmy mogli go użyć.
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. Następnie utwórzmy dwie identyczne sesje na domyślnym procesorze GPU urządzenia przy użyciu tego samego modelu co parametr wejściowy.
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));

Uwaga / Notatka

Aby czerpać korzyści z wydajności łączenia, należy utworzyć identyczne sesje GPU dla wszystkich modeli. Nie spełniając tego warunku, nastąpi dodatkowe przenoszenie danych z procesora GPU do procesora CPU, co zmniejszy wydajność.

  1. Następujące wiersze kodu będą tworzyć powiązania dla każdej sesji:
LearningModelBinding binding1(session1);
LearningModelBinding binding2(session2);
LearningModelBinding binding1 = new LearningModelBinding(session1);
LearningModelBinding binding2 = new LearningModelBinding(session2);
  1. Następnie powiążemy dane wejściowe dla naszego pierwszego modelu. Podamy obraz znajdujący się w tej samej ścieżce co nasz model. W tym przykładzie obraz nosi nazwę "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. Aby następny model w łańcuchu używał danych wyjściowych oceny pierwszego modelu, musimy utworzyć pusty tensor wyjściowy i powiązać dane wyjściowe, aby mieć znacznik do łańcucha:
//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);

Uwaga / Notatka

Podczas powiązania danych wyjściowych należy użyć typu danych TensorFloat . Uniemożliwi to usunięcie tensoryzacji po zakończeniu oceny pierwszego modelu, co pozwoli uniknąć dodatkowego kolejkowania procesora GPU na potrzeby operacji ładowania i tworzenia powiązań dla drugiego modelu.

  1. Teraz uruchomimy ocenę pierwszego modelu i powiążemy jego dane wyjściowe z danymi wejściowymi następnego modelu:
//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. Na koniec pobierzemy końcowe dane wyjściowe wygenerowane po uruchomieniu obu modeli przy użyciu następującego wiersza kodu.
auto finalOutput = session2AsyncOp.get().Outputs().First().Current().Value();
var finalOutput = results.Outputs.First().Value;

To wszystko! Oba modele mogą teraz wykonywać operacje sekwencyjnie, wykorzystując maksymalnie dostępne zasoby procesora GPU.

Uwaga / Notatka

Skorzystaj z następujących zasobów, aby uzyskać pomoc dotyczącą uczenia maszynowego z systemem Windows:

  • Aby zadać lub odpowiedzieć na pytania techniczne dotyczące uczenia maszynowego z systemem Windows, użyj tagu windows-machine-learning w witrynie Stack Overflow.
  • Aby zgłosić usterkę, popełnij zgłoszenie na GitHubie .