Краткий опыт использования ML.NET (Machine Learning Framework от Microsoft)

Пройдя недавно курс по машинному обучению, я отправился на поиски готовых библиотек — в первую очередь — для платформы .NET, так как я именно ее использую на своей работе. Запрос в гугл «Machine Learning + .NET» немедленно дал ссылку на фреймворк ML.NET, который сейчас имеет статус «preview». Здесь вкратце опишу, как я попробовал его использовать для классификации изображений.

Создание модели

ML.NET, насколько я успел понять, одной из своих целей имеет обеспечение очень высокого уровня абстракции для неискушенных в машинном обучении пользователей. Т. е. программист может не вникать в практическую реализацию и иметь лишь шапочное знакомство с теорией. В видеоуроках для начинающих излагается типовой сценарий применения ML.NET. Вы устанавливаете расширение для Visual Studio под названием ML .NET Model Builder (Preview). Это расширение добавляет в контекстное меню значка проекта в окне [Обозреватель Решений] пункт [Добавить] > [Machine Learning]. Вы создаете проект приложения (.NET Framework или .NET Core) — например, свой проект я назвал ImageBinaryClassification. Далее вы щелкаете правой кнопкой мыши на значке проекта в Обозревателе Решений и выбираете [Добавить] > [Machine Learning]. Открывается wizard, в котором вы выбираете одну из типовых задач машинного обучения:

  • бинарная классификация или мультиклассификация данных (данные должны быть в формате CSV или TSV)
  • линейная регрессия (данные так же — в CSV или TSV)
  • бинарная классификация или мультиклассификация изображений (jpeg, png и что-то еще)
  • recommender system

Других встроенных сценариев пока нет. Заметим, что большинство сценариев это — supervised learning, а unsupervised только один — «recommender system».

Что касается классификации изображений, то вы должны указать папку, в которой будут находиться тренировочные изображения. Изображения должны быть рассортированы по подпапкам, каждая из которых соответствует отдельному классу нашей классификации. Впрочем, все это написано в самом wizard’е. К примеру, у меня структура папок была такая:

TrainingSet
├── True
└── False

В папку True я поместил изображения, на которых есть искомый объект, а в папку False, соответственно, изображения, на которых искомого объекта нет. Далее Model Builder сам тестирует различные алгоритмы/модели машинного обучения, подходящие к случаю, и выбирает тот, который дает наибольшую точность. Точность различных алгоритмов/моделей измеряется на наборе cross validation set, который изыскивается из тех данных (в моем случае — изображений), которые выше я назвал «тренировочными».

Далее Model Builder сгенерировал в моем решении два проекта:

  • ImageBinaryClassificationML.ConsoleApp — это приложение, которое призвано делать то же, что Model Builder — обучать модель, используя ваши тренировочные данные, и сохранять ее в виде ZIP-архива MLModel.zip.
  • ImageBinaryClassificationML.Model — это простенькая библиотека, которая содержит код для использования модели (модель, напоминаю, находится в ZIP-архиве, созданном Model Builder’ом). В этой библиотеке имеется три очень простых класса: ConsumeModel, ModelInput и ModelOutput.

Помимо двух указанных проектов Model Builder сгенерировал ZIP-архив MLModel.zip, в котором, очевидно, сохраняются параметры нашей уже обученной модели. Этот файл затем понадобится непосредственно при использовании модели, в моем случае — для классификации изображений. Model Builder поместил этот ZIP-архив в проект ImageBinaryClassificationML.Model.

Стоит обратить внимание на класс ModelInput. Он содержит то, что называется data sample:

public class ModelInput
{
    [ColumnName("Label"), LoadColumn(0)]
    public bool Label { get; set; }

    [ColumnName("ImageSource"), LoadColumn(1)]
    public string ImageSource { get; set; }
}

Свойства класса помечены атрибутами, которые между прочим, имеют непосредственное отношение к TSV-файлу, содержащему data set (см. ниже)

Использование модели в своем приложении.

Итак, коль скоро есть обученная модель (параметры которой находятся в ModelML.zip) и сгенерирован API для ее использования (класс ConsumeModel в проекте ImageBinaryClassificationML.Model) я решил создать консольное приложение и попробовать классифицировать какое-нибудь изображение. Выше я уже создал проект ImageBinaryClassification. Теперь я добавил в него ссылку на проект ImageBinaryClassificationML.Model и написал простейший код вроде следующего:

ModelInput input = new ModelInput() { ImageSource = @"..\TrainingSet\True\image.jpg" };
ModelOutput prediction = ConsumeModel.Predict(input);
Console.WriteLine(prediction.Prediction);

Подводный камень 1. Зависимости. Пакеты Nuget

Проект не компилировался и требовал сослаться на сборку Microsoft.ML.Data. Я пошел в меню [Ссылки] > [Управление пакетами Nuget] и стал искать там одноименный пакет. Нашел в итоге пакет Microsoft.ML, который и установил, и стал пытаться строить проект дальше.

Подводный камень 2. Допустима только конфигурация сборки x64

Однако при очередной попытке построения проекта, я получил ошибку:

Microsoft.ML currently supports 'x64' and 'x86' processor architectures.
Please ensure your application is targeting 'x64' or 'x86'

Пришлось сделать [Свойства Проекта] > [Сборка] > [Целевая платформа]: x86. Программы для x86 я предпочитаю при прочих равных потому, что они работают на большем числе компьютеров и операционных систем. Проект успешно скомпилировался и я его запустил.

Однако при запуске вылезло исключение FileNotFoundException: Не удалось загрузить файл или сборку "Microsoft.ML.ImageAnalytics"
Установил Nuget package Microsoft.ML.ImageAnalytics.

Далее при запуске возникла ошибка FileNotFoundException: Не удалось загрузить файл или сборку "Microsoft.ML.Vision"
Установил пакет Microsoft.ML.Vision.

Теперь при запуске возникла ошибка другого сорта: DllNotFoundException: Не удается загрузить DLL "tensorflow": Не найден указанный модуль. Это уже было похоже на то, что не удается найти нативную dll-библиотеку, а не .NET’овскую сборку. Эту библиотеку я нашел в Nuget-пакете SciSharp.TensorFlow.Redist, но копировать её в папку с программой пришлось вручную. А кроме того, оказалось что библиотека tensorflow.dll существует только в 64-разрядной версии (я нашел ее в папке packages\SciSharp.TensorFlow.Redist.2.1.0\runtimes\win-x64\native). Пришлось поменять целевую платформу проекта на x64 (иначе ошибка DllNotFoundException, понятное дело, не исчезала бы).

И наконец программа заработала! И успешно классифицировала мое тестовое изображение.

Подводный камень 3. Model Builder генерирует и использует временные файлы

Далее передо мной встала задача научиться пользоваться программой ImageBinaryClassificationML.ConsoleApp (которую сгенерировал Model Builder). В исходном коде этой программы был по сути только один класс под названием ModelBuilder. Класс был большой (порядка двухсот строк кода) — я даже не стал вникать в его код. Но обратил внимание на то, что в нем было два статических поля:

public static class ModelBuilder
{
    private static string TRAIN_DATA_FILEPATH
        = @"C:\Users\Дмитрий\AppData\Local\Temp\dcf6abce-ed1c-4bda-a236-4fc9d770099b.tsv";
    private static string MODEL_FILEPATH
        = @"C:\Users\Дмитрий\AppData\Local\Temp\MLVSTools\ImageBinaryClassificationML\ImageBinaryClassificationML.Model\MLModel.zip";

    public static void CreateModel() { ... }
    . . .
}

Поля содержали пути к временным файлам, сгенерированным в папке Temp на моем компьютере. Один из них, TSV — содержал данные следующего вида:

Label   ImageSource
False   \TrainingSet\False\image1.jpg
False   \TrainingSet\False\image2.jpg
False   \TrainingSet\False\image3.jpg
True    \TrainingSet\True\image4.jpg
True    \TrainingSet\True\image5.jpg
True    \TrainingSet\True\image6.jpg
. . .

Второй файл — MLModel.zip — это параметры нашей модели, т. е. точно такой же архив, что и тот, который, как я уже говорил находился непосредственно в проекте ImageBinaryClassificationML.Model.

Итак, поскольку в полях были прописаны пути к временным файлам, то если бы я стал запускать данную программу на другом компьютере, то она бы просто не нашла нужных файлов. Понял я, что придется в этот сгенерированный код влезть и кое-что там подправить. Что я и сделал, и поместил весь код в открытый репозиторий. Я также написал утилиту ImagePreprocessing, которая подготавливает изображения для последующего обучения: конвертирует bmp в jpg (ML.NET не переваривает bmp-файлы), понижает разрешение изображений, делает их монохромными а также создает TSV-файл. Обо всем этом я надеюсь написать следующую заметку, так что продолжение следует.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *