[Перевод] Deep Learning теперь на Java

Не любите Java? Да вы не умеете ее готовить! Mani Sarkar предлагает нам познакомиться с инструментом Valohai, позволяющим проводить исследования модели на Java.

etp-sezekre2qlznmvgdzlgds0a.png

Дисклеймер от переводчика

Я надеюсь, это не является рекламной публикацией. Я не связан с Valohai. Я лишь перевел статью, на которую указываю ссылку. Если коряво перевел — пинайте в личку. Если будет нужно — могу удалить ссылки и упоминание других внешних ресурсов. Спасибо за понимание.


Введение


Некоторое время назад я столкнулся с облачным сервисом под названием Valohai, и меня порадовал его пользовательский интерфейс и простота дизайна и компоновки. Я попросил об услуге одного из членов Valohai и получил демо-версию. До этого я написал простой конвейер с использованием GNU Parallel, JavaScript, Python и Bash — и еще один, использующий только GNU Parallel и Bash.

Я также думал об использовании готовых к использованию инструментов управления задачами / рабочими процессами, таких как Jenkins X, Jenkins Pipeline, Concourse или Airflow, но по разным причинам я решил этого не делать.

Я заметил, что многие примеры и документация по Valohai основаны на Python и R и соответствующих фреймворках и библиотеках. Я решил не упускать возможность и хочу исправить нехватку примеров и документации.

Valohai подтолкнул меня к тому, чтобы реализовать что-то, используя известную библиотеку Java под названием DL4J — Deep Learning for Java.

Мой первый опыт работы с Valohai уже произвел на меня хорошее впечатление после прощупывания его дизайна, компоновки и рабочего процесса. Создатели уже приняли во внимание различные аспекты как рабочих процессов разработчика, так и инфраструктуры. В нашем мире процесс разработки инфраструктуры в основном управляется командами DevOps или SysOps, и мы знаем нюансы и болевые точки, связанные с ним.

Что нам нужно и как?


В любом проекте по машинному обучению есть два важных компонента (с точки зрения высокого уровня) — это код, который будет работать с моделью, и код, который будет работать с инфраструктурой, в которой будет выполняться весь жизненный цикл проекта.

Конечно, будут шаги и компоненты, необходимые до, во время и после, но для простоты, скажем, нам нужен код и инфраструктура.

Код


Для кода я выбрал сложный пример с использованием DL4J, это проект MNist с обучающим набором из 60000 изображений и тестовым набором из 10000 изображений рукописных цифр. Этот набор данных доступен через библиотеку DL4J (точно так же, как и Keras).

Прежде чем начать, рекомендуется взглянуть на исходный код, который мы будем использовать. Основной класс Java называется org.deeplearning4j.feedforward.mnist.MLPMnistSingleLayerRunner.

Инфраструктура


Мы решили попробовать пример Java с использованием Valohai в качестве нашей инфраструктуры для проведения экспериментов (обучение и оценка модели). Valohai распознает git-репозитории и напрямую подключается к ним, позволяя выполнять наш код независимо от платформы или языка — так что мы увидим, как это работает. Это также означает, что если вы используете GitOps или Infrastructure-As-Code, у вас также все будет работать.

Для этого нам просто нужна учетная запись в Valohai. После создания бесплатной учетной записи получаем доступ к нескольким экземплярам различных конфигураций. Для того, что мы хотели бы сделать, Free-Tier более чем достаточно.

Deep Learning для Java и Valohai


Мы предоставим все зависимости в образ Docker и будем использовать его для компиляции нашего Java-приложения, обучения модели и оценки ее на платформе Valohai с помощью простого файла valohai.yaml, который находится в корневой папке репозитория проекта.

Deep Learning для Java: DL4J


Самая простая часть. Нам не нужно много делать, просто соберите jar и загрузите набор данных в контейнер Docker. У нас есть предварительно созданный образ Docker, который содержит все зависимости, необходимые для создания приложения Java. Мы поместили этот образ в Docker Hub, и вы можете найти его, выполнив поиск dl4j-mnist-single-layer (мы будем использовать специальный тег, как определено в файле YAML). Мы решили использовать GraalVM 19.1.1 в качестве нашей сборочной и выполняемой среды Java для этого проекта, и она встроена в образ Docker.

Когда uber jar вызывается из командной строки, мы создаем класс MLPMnistSingleLayerRunner, который указывает нам предполагаемое действие в зависимости от параметров, передаваемых в:

public static void main(String[] args) throws Exception {
    MLPMnistSingleLayerRunner mlpMnistRunner = new MLPMnistSingleLayerRunner();
    JCommander.newBuilder()
            .addObject(mlpMnistRunner)
            .build()
            .parse(args);
    mlpMnistRunner.execute();
}


Параметры, переданные в uber jar, принимаются этим классом и обрабатываются методом execute ().

Мы можем создать модель с помощью параметра --action train и оценить созданную модель с помощью параметра --action evaluate переданного в приложение Java.

Основные части приложения Java, которое выполняет эту работу, можно найти в двух классах Java, упомянутых в разделах ниже.

Обучение модели


Вызовем

./runMLPMnist.sh --action train --output-dir ${VH_OUTPUTS_DIR}
or
java -Djava.library.path=""             \
     -jar target/MLPMnist-1.0.0-bin.jar \
     --action train --output-dir ${VH_OUTPUTS_DIR}


Эта команда создает модель с именем mlpmnist-single-layer.pb в папке, указанной параметром --output-dir переданным в начале выполнения. С точки зрения Valohai, он должен быть помещен в $ {VH_OUTPUTS_DIR}, что мы и делаем (см. файл valohai.yaml).

Исходный код см. в классе MLPMNistSingleLayerTrain.java.

Оценка модели


Вызовем

./runMLPMnist.sh --action evaluate --input-dir ${VH_INPUTS_DIR}/model
or
java -Djava.library.path=""             \
     -jar target/MLPMnist-1.0.0-bin.jar \
     --action evaluate --input-dir ${VH_INPUTS_DIR}/model


Предполагается, что модель (созданная на этапе обучения) с именем mlpmnist-single-layer.pb будет присутствовать в папке, указанной в параметре --input-dir переданном при вызове приложения.

Исходный код см. в классе MLPMNistSingleLayerEvaluate.java.

Я надеюсь, что эта короткая иллюстрация прояснит, как работает приложение Java, которое обучает и оценивает модель.

Это все, что от нас требуется, но не стесняйтесь поиграть с остальными исходниками (вместе с README.md и скриптами bash) и удовлетворить свое любопытство и понимание того, как это делается!

Valohai


Valohai позволяет нам свободно связывать нашу среду выполнения, код и набор данных, как вы можете видеть из структуры файла YAML ниже. Таким образом, различные компоненты могут развиваться независимо друг от друга. Следовательно, в наш контейнер Docker упакованы только компоненты сборки и рантайма.

Во время выполнения мы собираем Uber JAR в контейнере Docker, загружаем его в какое-либо внутреннее или внешнее хранилище, а затем с помощью другого шага выполнения загружаем Uber JAR и набор данных из хранилища (или другого места) для запуска обучения. Таким образом, два шага выполнения разъединяются; например, мы можем скомпилировать jar один раз и выполнить сотни тренировочных шагов на одном jar. Поскольку среды сборки и среды выполнения не должны изменяться так часто, мы можем их кэшировать, а код, набор данных и модели могут быть динамически доступны во время выполнения.

valohai.yaml
Главной частью интеграции нашего Java-проекта с инфраструктурой Valohai является определение порядка шагов Execution в файле valohai.yaml, расположенном в корне папки вашего проекта. Наш valohai.yaml выглядит так:

---
- step:
    name: Build-dl4j-mnist-single-layer-java-app
    image: neomatrix369/dl4j-mnist-single-layer:v0.5
    command:
      - cd ${VH_REPOSITORY_DIR}
      - ./buildUberJar.sh
      - echo "~~~ Copying the build jar file into ${VH_OUTPUTS_DIR}"
      - cp target/MLPMnist-1.0.0-bin.jar ${VH_OUTPUTS_DIR}/MLPMnist-1.0.0.jar
      - ls -lash ${VH_OUTPUTS_DIR}
    environment: aws-eu-west-1-g2-2xlarge
- step:
    name: Run-dl4j-mnist-single-layer-train-model
    image: neomatrix369/dl4j-mnist-single-layer:v0.5
    command:
      - echo "~~~ Unpack the MNist dataset into ${HOME} folder"
      - tar xvzf ${VH_INPUTS_DIR}/dataset/mlp-mnist-dataset.tgz -C ${HOME}
      - cd ${VH_REPOSITORY_DIR}
      - echo "~~~ Copying the build jar file from ${VH_INPUTS_DIR} to current location"
      - cp ${VH_INPUTS_DIR}/dl4j-java-app/MLPMnist-1.0.0.jar .
      - echo "~~~ Run the DL4J app to train model based on the the MNist dataset"
      - ./runMLPMnist.sh {parameters}
    inputs:
      - name: dl4j-java-app
        description: DL4J Java app file (jar) generated in the previous step 'Build-dl4j-mnist-single-layer-java-app'
      - name: dataset
        default: https://github.com/neomatrix369/awesome-ai-ml-dl/releases/download/mnist-dataset-v0.1/mlp-mnist-dataset.tgz
        description: MNist dataset needed to train the model
    parameters:
      - name: --action
        pass-as: '--action {v}'
        type: string
        default: train
        description: Action to perform i.e. train or evaluate
      - name: --output-dir
        pass-as: '--output-dir {v}'
        type: string
        default: /valohai/outputs/
        description: Output directory where the model will be created, best to pick the Valohai output directory
    environment: aws-eu-west-1-g2-2xlarge
- step:
    name: Run-dl4j-mnist-single-layer-evaluate-model
    image: neomatrix369/dl4j-mnist-single-layer:v0.5
    command:
      - cd ${VH_REPOSITORY_DIR}
      - echo "~~~ Copying the build jar file from ${VH_INPUTS_DIR} to current location"
      - cp ${VH_INPUTS_DIR}/dl4j-java-app/MLPMnist-1.0.0.jar .
      - echo "~~~ Run the DL4J app to evaluate the trained MNist model"
      - ./runMLPMnist.sh {parameters}
    inputs:
      - name: dl4j-java-app
        description: DL4J Java app file (jar) generated in the previous step 'Build-dl4j-mnist-single-layer-java-app'    
      - name: model
        description: Model file generated in the previous step 'Run-dl4j-mnist-single-layer-train-model'
    parameters:
      - name: --action
        pass-as: '--action {v}'
        type: string
        default: evaluate
        description: Action to perform i.e. train or evaluate
      - name: --input-dir
        pass-as: '--input-dir {v}'
        type: string
        default: /valohai/inputs/model
        description: Input directory where the model created by the previous step can be found created
    environment: aws-eu-west-1-g2-2xlarge


Как работает Build-dl4j-mnist-single-layer-java-app


Из файла YAML мы видим, что мы определяем этот шаг, сначала используя образ Docker, а затем запускаем скрипт для сборки Uber JAR. Наш образ докера имеет настройку зависимостей среды сборки (например, GraalVM JDK, Maven и т. д.) для создания приложения Java. Мы не указываем какие-либо входные данные или параметры, так как это этап сборки. Как только сборка будет успешной, мы копируем uber jar с именем MLPMnist-1.0.0-bin.jar (оригинальное имя) в папку /valohai/outputs (представленную как ${VH_OUTPUTS_DIR}). Все в этой папке автоматически сохраняется в хранилище вашего проекта, например, в корзине AWS S3. Наконец, мы определяем нашу работу для работы в среде AWS.

Примечание

Бесплатная учетная запись Valohai не имеет доступа к сети из контейнера Docker (по умолчанию это отключено), пожалуйста, свяжитесь со службой поддержки, чтобы включить эту опцию (я должен был сделать то же самое), иначе мы не сможем загрузить наш Maven и другие зависимости во время сборки.


Как работает Run-dl4j-mnist-single-layer-train-model


Семантика определения аналогична предыдущему шагу, за исключением того, что мы указываем два ввода: один для uber jar (MLPMnist-1.0.0.jar), а другой — для набора данных (распаковывается в папку ${HOME}/.deeplearning4j). Мы будем передавать два параметра — --action train и --output-dir /valohai/outputs. Модель, созданная на этом шаге, собирается в /valohai/outputs/model (представлена ​​в виде ${VH_OUTPUTS_DIR}/model).

Примечание

В полях ввода на вкладке «Выполнение» веб-интерфейса Valohai мы можем выбрать выходные данные из предыдущих выполнений, используя номер выполнения, т. е. #1 или #2, в дополнение к использованию URL-адресов datum: // или http: //, ввод в несколько букв имени файла также помогает искать по всему списку.


Как работает Run-dl4j-mnist-single-layer -valu-model


Опять же, этот шаг аналогичен предыдущему шагу, за исключением того, что мы будем передавать два параметра --action evaluate и --input-dir /valohai/inputs/model. Кроме того, мы снова указали в вводе: разделы, определенные в файле YAML с именем dl4j-java-app и model без заданного по умолчанию для них обоих. Это позволит нам выбрать uber jar и модель, которую мы хотим оценить — которая была создана с помощью шага Run-dl4j-mnist-single-layer-train-model, используя веб-интерфейс.

Я надеюсь, что это объясняет шаги в приведенном выше файле определения, но если вам требуется дополнительная помощь, пожалуйста, не стесняйтесь просматривать документацию и туториалы.

Веб-интерфейс Valohai


Получив учетную запись, мы можем войти и продолжить создание проекта с именем mlpmnist-single-layer и связать git repo github.com/valohai/mlpmnist-dl4j-example с проектом и сохранить проект.

Теперь вы можете выполнить шаг и посмотреть, как это получится!

Сборка Java-приложения DL4J


Перейдите на вкладку «Execution» в веб-интерфейсе и либо скопируйте существующее execution, либо создайте новое с помощью кнопки [Create execution]. Все необходимые параметры по умолчанию будут заполнены. Выберите Step Build-dl4j-mnist-single-layer-java-app.

Для Окружения я выбрал AWS eu-west-1 g2.2xlarge и щелкнул по кнопке [Create execution] внизу страницы, чтобы увидеть начало выполнения.

le18zo_udbscmekflaux7kqfjcw.png

Обучение модели


Перейдите на вкладку «Execution» в веб-интерфейсе и сделайте то же, что и на предыдущем шаге, и выберите Run-dl4j-mnist-single-layer-train-model. Вам нужно будет выбрать приложение Java (просто введите jar в поле), созданное на предыдущем шаге. Набор данных уже был предварительно заполнен с помощью файла valohai.yaml:

mdlsovhg3vw9zjnpoeqhxwzgk7w.png

Нажмите [Create Execution], чтобы начать.

6nmrwmveck6cgdughud1xgx7qdg.png

Вы увидите результат в консоли:

[<--- snipped --->]
11:17:05 =======================================================================
11:17:05 LayerName (LayerType) nIn,nOut TotalParams ParamsShape
11:17:05 =======================================================================
11:17:05 layer0 (DenseLayer) 784,1000 785000 W:{784,1000}, b:{1,1000}
11:17:05 layer1 (OutputLayer) 1000,10 10010 W:{1000,10}, b:{1,10}
11:17:05 -----------------------------------------------------------------------
11:17:05  Total Parameters: 795010
11:17:05  Trainable Parameters: 795010
11:17:05  Frozen Parameters: 0
11:17:05 =======================================================================
[<--- snipped --->]


Созданные модели можно найти на вкладке «Outputs» главной вкладки «Execution» во время и после выполнения:

pgg2-kmsr85x87fedfoy24lreye.png

Вы могли заметить несколько артефактов во вложенной вкладке Outputs. Это потому, что мы сохраняем контрольные точки в конце каждой эпохи. Посмотрим на это в логах:

[<--- snipped --->]
11:17:14 o.d.o.l.CheckpointListener - Model checkpoint saved: epoch 0, iteration 469, path: /valohai/outputs/checkpoint_0_MultiLayerNetwork.zip
[<--- snipped --->]


Чекпоинт содержит состояние модели в трех файлах:

configuration.json
coefficients.bin
updaterState.bin


Обучение модели. Метаданные


Возможно, вы заметили эти записи в журналах выполнения:

[<--- snipped --->]
11:17:05 {"epoch": 0, "iteration": 0, "score (loss function)": 2.410047}
11:17:07 {"epoch": 0, "iteration": 100, "score (loss function)": 0.613774}
11:17:09 {"epoch": 0, "iteration": 200, "score (loss function)": 0.528494}
11:17:11 {"epoch": 0, "iteration": 300, "score (loss function)": 0.400291}
11:17:13 {"epoch": 0, "iteration": 400, "score (loss function)": 0.357800}
11:17:14 o.d.o.l.CheckpointListener - Model checkpoint saved: epoch 0, iteration 469, path: /valohai/outputs/checkpoint_0_MultiLayerNetwork.zip
[<--- snipped --->]


Эти данные позволяют Valohai получить эти значения (в формате JSON), которые будут использоваться для построения метрики, которые можно увидеть во время и после выполнения на дополнительной вкладке «Metadata» на главной вкладке «Executions»:

etp-sezekre2qlznmvgdzlgds0a.png

Мы смогли сделать это, подключив класс ValohaiMetadataCreator к модели, так что во время обучения Valohai обращается к этому классу. В случае этого класса мы выводим несколько эпох, количество итераций и Score (значение функции loss). Вот фрагмент кода из класса:

public void iterationDone(Model model, int iteration, int epoch) {
        if (printIterations <= 0)
            printIterations = 1;
        if (iteration % printIterations == 0) {
            double score = model.score();
            System.out.println(String.format(
                    "{\"epoch\": %d, \"iteration\": %d, \"score (loss function)\": %f}",
                    epoch,
                    iteration,
                    score)
            );
        }
    }


Оценка модели


Как только модель была успешно создана на предыдущем этапе, следует ее оценить. Мы создаем новое Execution точно так же, как и ранее, но на этот раз выбираем шаг Run-dl4j-mnist-single-layer -valu-model. Нам нужно будет снова выбрать приложение Java (MLPMnist-1.0.0.jar) и созданную модель (mlpmnist-single-layer.pb), прежде чем начинать выполнение (как показано ниже):

e9_bamnlhownoo40utziaqazg8e.png

После выбора нужной модели в качестве входных данных нажмите кнопку [Create execution]. Оно быстрее исполнится, чем предыдущее, и мы увидим следующий результат:

gp0icomuurv7a0gt-niye5hyri4.png

Мы видим, что наш «hello world» привел к получению модели, точность которой составляет около 97% на основе тестового набора данных. Confusion matrix помогает найти случаи, когда цифра была неправильно предсказана как другая цифра.

Остается вопрос (и выходящий за рамки этого поста) — насколько хороша модель, когда сталкиваешься с реальными данными?

Чтобы клонировать репозиторий git, то вот что нужно сделать:

 $ git clone https://github.com/valohai/mlpmnist-dl4j-example 


Затем нам нужно связать наш проект Valohai, созданный через веб-интерфейс в приведенном выше разделе, с проектом, хранящимся на нашей локальной машине (тот, который мы только что клонировали). Выполните следующие команды, чтобы сделать это:

$ cd mlpmnist-dl4j-example
$ vh project --help   ### to see all the project-specific options we have for Valohai
$ vh project link


Вам будет показано что-то вроде этого:

[  1] mlpmnist-single-layer
...
Which project would you like to link with /path/to/mlpmnist-dl4j-example?
Enter [n] to create a new project.:


Выберите 1 (или подходящий вам вариант), и вы должны увидеть это сообщение:

Success! Linked /path/to/mlpmnist-dl4j-example to mlpmnist-single-layer.


Еще кое-что, прежде чем идти дальше, убедитесь, что ваш проект Valohai синхронизирован с последним git-проектом, выполнив это:

 $ vh project fetch 


h0l4vbe1eqis5kb9sd7gfslbozk.png

Теперь мы можем выполнить шаги из CLI с помощью:

 $ vh exec run Build-dl4j-mnist-single-layer-java-app


После того, как Execution выполнено, мы можем проверить его с помощью:

$ vh exec info
$ vh exec logs
$ vh exec watch


Заключение


Как мы видели, очень удобно работать с DL4J и Valohai вместе. Кроме того, мы можем разрабатывать различные компоненты, из которых состоят наши эксперименты (исследования), то есть среду сборки / выполнения, код и набор данных, и интегрировать их в наш проект.

Примеры шаблонов, использованные в этом посте, — хороший способ начать создавать более сложные проекты. И вы можете использовать веб-интерфейс или интерфейс командной строки, чтобы выполнить свою работу с Valohai. С CLI вы также можете интегрировать его с вашими установками и сценариями (или даже с заданиями CRON или CI / CD).

Кроме того, ясно, что если я работаю над проектом, связанным с AI / ML / DL, мне не нужно заботиться о создании и поддержке сквозного конвейера (что пришлось сделать многим другим и мне в прошлом).

Ссылки

  1. Проект mlpmnist-dl4j-examples на GitHub
  2. Потрясающие ресурсы AI / ML / DL
  3. Ресурсы Java AI / ML / DL
  4. Deep Learning и ресурсы DL4J


Спасибо за внимание!

© Habrahabr.ru