Наука о данных в реальном мире
Deep Java Library (DJL) - набор инструментов для глубокого обучения для разработчиков Java
Amazon выпускает библиотеку Deep Java Library (DJL), которая дает разработчикам Java возможность начать глубокое обучение.
Deep Java Library (DJL) - это библиотека с открытым исходным кодом, созданная Amazon для разработки моделей машинного обучения (ML) и глубокого обучения (DL) непосредственно на Java, упрощая использование фреймворков глубокого обучения.
Недавно я использовал DJL для разработки модели классификации обуви и нашел этот инструментарий интуитивно понятным и простым в использовании; очевидно, что много внимания было уделено дизайну и тому, как Java-разработчики будут его использовать. API DJL абстрагируют часто используемые функции для разработки моделей и управления инфраструктурой. Я обнаружил, что высокоуровневые API, используемые для обучения, тестирования и выполнения логического вывода, позволили мне использовать свои знания Java и жизненного цикла машинного обучения для разработки модели менее чем за час с минимальным кодом.
Модель классификации обуви
Модель классификации обуви - это мультиклассовая модель компьютерного зрения (CV), обученная с использованием обучения с учителем, которая классифицирует обувь по одному из четырех классов: ботинки, сандалии, туфли или тапочки.
О данных
Самая важная часть разработки точной модели машинного обучения - использование данных из авторитетного источника. Источником данных для модели классификации обуви является набор данных UTZappos50k, предоставленный Техасским университетом в Остине и свободно доступный для академического некоммерческого использования. Набор данных обуви состоит из 50 025 изображений каталога с пометками, собранных с сайта Zappos.com.
Обучите модель классификации обуви
Обучение - это процесс создания модели машинного обучения путем предоставления обучающих данных алгоритму обучения для изучения. Термин модель относится к артефакту, создаваемому в процессе обучения; модель содержит шаблоны, обнаруженные в обучающих данных, и может использоваться для прогнозирования (или вывода). Перед тем, как начать тренировочный процесс, я настроил свою локальную среду для развития. Вам понадобится JDK 8 (или новее), IntelliJ, механизм машинного обучения для обучения (например, Apache MXNet), переменная среды, указывающая путь к вашему движку, и зависимости сборки для DJL.
dependencies { compile "org.apache.logging.log4j:log4j-slf4j-impl:2.12.1" compile "ai.djl:api:0.2.0" compile "ai.djl:basicdataset:0.2.0" compile "ai.djl:examples:0.2.0" compile "ai.djl:model-zoo:0.2.0" compile "ai.djl.mxnet:mxnet-model-zoo:0.2.0" runtimeOnly "ai.djl.mxnet:mxnet-native-mkl:1.6.0-a:osx-x86_64" }
DJL остается верным девизу Java: напиши один раз, запусти где угодно (WORA), будучи независимым от движка и фреймворка глубокого обучения. Разработчики могут написать код, работающий на любом движке. DJL в настоящее время предоставляет реализацию Apache MXNet, механизма машинного обучения, который упрощает разработку глубоких нейронных сетей. API DJL используют JNA, Java Native Access, для вызова соответствующих операций Apache MXNet. С точки зрения оборудования, обучение происходило локально на моем ноутбуке с использованием центрального процессора. Однако для лучшей производительности команда DJL рекомендует использовать машину как минимум с одним графическим процессором. Если у вас нет доступного графического процессора, всегда есть возможность использовать Apache MXNet на Amazon EC2. Приятной особенностью DJL является то, что он обеспечивает автоматическое определение CPU / GPU на основе конфигурации оборудования, чтобы всегда обеспечивать наилучшую производительность.
Загрузить набор данных из источника
Данные обуви были сохранены локально и загружены с использованием набора данных DJL ImageFolder
, который представляет собой набор данных, который может извлекать изображения из локальной папки. В терминах DJL Dataset
просто содержит обучающие данные. Существуют реализации наборов данных, которые можно использовать для загрузки данных (на основе предоставленного вами URL-адреса), извлечения данных и автоматического разделения данных на наборы для обучения и проверки. Автоматическое разделение - полезная функция, поскольку важно никогда не использовать одни и те же данные, на которых была обучена модель, для проверки ее производительности. Набор данных проверки обучения используется для поиска закономерностей в данных; набор данных проверки используется для оценки точности модели обуви в процессе обучения.
//identify the location of the training dataString trainingDatasetRoot = "src/test/resources/imagefolder/train"; //identify the location of the validation dataString validateDatasetRoot = "src/test/resources/imagefolder/validate"; //create training ImageFolder dataset ImageFolder trainingDataset = initDataset(trainingDatasetRoot); //create validation ImageFolder dataset ImageFolder validateDataset = initDataset(validateDatasetRoot); private ImageFolder initDataset(String datasetRoot) throws IOException { ImageFolder dataset = new ImageFolder .Builder() .setRepository(new SimpleRepository(Paths.get(datasetRoot))).optPipeline( // create preprocess pipeline new Pipeline() .add(new Resize(NEW_WIDTH, NEW_HEIGHT)) .add(new ToTensor())) .setSampling(BATCH_SIZE,true) .build(); dataset.prepare(); return dataset; }
При локальном структурировании данных я не опускался до наиболее детализированного уровня, определенного набором данных UTZappos50k, такого как классификационные метки для ботинок до щиколотки, выше колена, середины икры, выше колена и т. Д. Мои местные данные хранятся на самом высоком уровне классификации, который включает только ботинки, сандалии, туфли и тапочки.
Обучите модель
Теперь, когда у меня есть данные об обуви, разделенные на наборы для обучения и проверки, я буду использовать нейронную сеть для обучения модели.
public final class Training extends AbstractTraining { . . . @Override protected void train(Arguments arguments) throws IOException { . . . try (Model model = Models.getModel(NUM_OF_OUTPUT, NEW_HEIGHT, NEW_WIDTH)) { TrainingConfig config = setupTrainingConfig(loss); try (Trainer trainer = model.newTrainer(config)) { trainer.setMetrics(metrics); trainer.setTrainingListener(this); Shape inputShape = new Shape(1, 3, NEW_HEIGHT, NEW_WIDTH); // initialize trainer with proper input shape trainer.initialize(inputShape); //find the patterns in data fit(trainer, trainingDataset, validateDataset, "build/logs/training"); //set model properties model.setProperty("Epoch", String.valueOf(EPOCHS)); model.setProperty("Accuracy", String.format("%.2f", getValidationAccuracy())); //save the model after done training for //inference later model saved //as shoeclassifier-0000.params model.save(Paths.get(modelParamsPath), modelParamsName); } } }
Обучение начинается с подачи обучающих данных в качестве входных данных в Блок. В терминах DJL Block
- это составной блок, который формирует нейронную сеть. Вы можете комбинировать блоки (как блоки Lego), чтобы образовать сложную сеть. В конце процесса обучения Block
представляет полностью обученную модель. Первый шаг - получить экземпляр модели, вызвав Models.getModel(NUM_OF_OUTPUT, NEW_HEIGHT, NEW_WIDTH)
. Метод getModel()
создает пустую модель, строит нейронную сеть и устанавливает нейронную сеть в модель.
/* Use a neural network (ResNet-50) to train the model ResNet-50 is a deep residual network with 50 layers; good for image classification */ public class Models { public static ai.djl.Model getModel(int numOfOutput, int height, int width) { //create new instance of an empty model ai.djl.Model model = ai.djl.Model.newInstance(); //Block is a composable unit that forms a neural network; //combine them like Lego blocks to form a complex network Block resNet50 = new ResNetV1.Builder() .setImageShape(new Shape(3, height, width)) .setNumLayers(50) .setOutSize(numOfOutput) .build(); //set the neural network to the model model.setBlock(resNet50); return model; } }
Следующим шагом является установка и настройка Trainer
путем вызова метода model.newTrainer(config)
. Объект конфигурации был инициализирован путем вызова метода setupTrainingConfig(loss)
, который устанавливает конфигурацию обучения (или гиперпараметры) для определения того, как обучается сеть.
private static TrainingConfig setupTrainingConfig(Loss loss) { // epoch number to change learning rate int[] epoch = {3, 5, 8}; int[] steps = Arrays .stream(epoch) .map(k -> k * 60000 / BATCH_SIZE).toArray(); //initialize neural network weights using Xavier initializer Initializer initializer = new XavierInitializer( XavierInitializer.RandomType.UNIFORM, XavierInitializer.FactorType.AVG, 2); //set the learning rate //adjusts weights of network based on loss MultiFactorTracker learningRateTracker = LearningRateTracker .multiFactorTracker() .setSteps(steps) .optBaseLearningRate(0.01f) .optFactor(0.1f) .optWarmUpBeginLearningRate(1e-3f) .optWarmUpSteps(500) .build(); //set optimization technique //minimizes loss to produce better and faster results //Stochastic gradient descent Optimizer optimizer = Optimizer .sgd() .setRescaleGrad(1.0f / BATCH_SIZE) .setLearningRateTracker(learningRateTracker) .optMomentum(0.9f) .optWeightDecays(0.001f) .optClipGrad(1f) .build(); return new DefaultTrainingConfig(initializer, loss) .setOptimizer(optimizer) .addTrainingMetric(new Accuracy()) .setBatchSize(BATCH_SIZE); }
Для обучения установлено несколько гиперпараметров:
newHeight
иnewWidth
- форма изображения.batchSize
- размер партии, используемой для обучения; выберите подходящий размер в зависимости от вашей модели.numOfOutput
- количество этикеток; Есть 4 ярлыка для классификации обуви.loss
- функции потерь сравнивают предсказания модели с истинными метками, измеряющими, насколько хороша (или плоха) модель.Initializer
- определяет метод инициализации; в данном случае инициализация Ксавьера.MultiFactorTracker
- настраивает параметры скорости обучения.Optimizer
: метод оптимизации для минимизации значения функции потерь; в данном случае - стохастический градиентный спуск (SGD).
Следующим шагом является установка Metrics
, обучающего слушателя, и инициализация Trainer
с правильной формой ввода. Metrics
собирать и составлять отчеты о ключевых показателях эффективности (KPI) во время обучения, которые можно использовать для анализа и мониторинга эффективности и стабильности тренировки. Затем я начинаю процесс обучения с вызова fit(trainer, trainingDataset, validateDataset, “build/logs/training”)
method, который выполняет итерацию по обучающим данным и сохраняет шаблоны, найденные в модели.
public void fit(Trainer trainer, Dataset trainingDataset, Dataset validateDataset,String outputDir) throws IOException { // find patterns in data for (int epoch = 0; epoch < EPOCHS; epoch++) { for (Batch batch : trainer.iterateDataset(trainingDataset)) { trainer.trainBatch(batch); trainer.step(); batch.close(); } //validate patterns found if (validateDataset != null) { for (Batch batch: trainer.iterateDataset(validateDataset)){ trainer.validateBatch(batch); batch.close(); } } //reset training and validation metric at end of epoch trainer.resetTrainingMetrics(); //save model at end of each epoch if (outputDir != null) { Model model = trainer.getModel(); model.setProperty("Epoch", String.valueOf(epoch)); model.save(Paths.get(outputDir), "resnetv1"); } } }
В конце обучения хорошо работающий артефакт проверенной модели сохраняется локально вместе со своими свойствами с помощью model.save(Paths.get(modelParamsPath), modelParamsName)
method. Метрики, полученные во время тренировочного процесса, показаны ниже.
Выполнить вывод
Теперь, когда у меня есть модель, я могу использовать ее для вывода (или прогнозирования) новых данных, для которых мне неизвестна классификация (или цель). После установки необходимых путей к модели и классифицируемому изображению я получаю пустой экземпляр модели с помощью метода Models.getModel(NUM_OF_OUTPUT, NEW_HEIGHT, NEW_WIDTH)
и инициализирую его с помощью метода model.load(Paths.get(modelParamsPath), modelParamsName)
. Это загружает модель, которую я обучил на предыдущем шаге. Затем я инициализирую Predictor
с указанным Translator
, используя model.newPredictor(translator)
method. Вы заметите, что я передаю Translator
Predictor
. С точки зрения DJL, Translator
обеспечивает функции предварительной и постобработки модели. Например, в моделях CV изображения необходимо преобразовать в оттенки серого; Translator
может сделать это за вас. Predictor
позволяет мне выполнять логический вывод для загруженного Model
с помощью метода predictor.predict(img)
, передавая изображение для классификации. Я делаю единичный прогноз, но DJL также поддерживает пакетные прогнозы. Вывод хранится в predictResult
, который содержит оценку вероятности для каждой метки. Модель автоматически закрывается после завершения вывода, что делает память DJL эффективной.
private Classifications predict() throws IOException, ModelException, TranslateException { //the location to the model saved during training String modelParamsPath = "build/logs"; //the name of the model set during training String modelParamsName = "shoeclassifier"; //the path of image to classify String imageFilePath = "src/test/resources/slippers.jpg"; //Load the image file from the path BufferedImage img = BufferedImageUtils.fromFile(Paths.get(imageFilePath)); //holds the probability score per label Classifications predictResult; try (Model model = Models.getModel(NUM_OF_OUTPUT, NEW_HEIGHT, NEW_WIDTH)) { //load the model model.load(Paths.get(modelParamsPath), modelParamsName); //define a translator for pre and post processing Translator<BufferedImage, Classifications> translator = new MyTranslator(); //run the inference using a Predictor try (Predictor<BufferedImage, Classifications> predictor = model.newPredictor(translator)) { predictResult = predictor.predict(img); } } return predictResult; }
Выводы (для каждого изображения) показаны ниже с соответствующими оценками вероятности.
Выводы и следующие шаги
Я занимаюсь разработкой приложений на основе Java с конца 90-х и начал свое путешествие по машинному обучению в 2017 году. Мой путь был бы намного проще, если бы DJL существовал тогда. Я настоятельно рекомендую Java-разработчикам, которые хотят перейти на машинное обучение, попробовать DJL. В моем примере я разработал модель классификации обуви с нуля; однако DJL также позволяет разработчикам развертывать предварительно обученные модели с минимальными усилиями. DJL также поставляется с популярными наборами данных из коробки, что позволяет разработчикам мгновенно приступить к работе с машинным обучением. Прежде чем начать работу с DJL, я бы порекомендовал вам иметь четкое представление о жизненном цикле машинного обучения и знать общие термины машинного обучения. Как только у вас будет базовый уровень понимания ML, вы сможете быстро освоить API DJL.
Amazon имеет открытый исходный код DJL, где более подробную информацию об инструментарии можно найти на веб-сайте DJL и на странице Спецификация API библиотеки Java. Код модели классификации обуви можно найти на GitLab. Удачи в вашем пути к машинному обучению. Если у вас возникнут вопросы, свяжитесь со мной.