Интеграция предобученных нейросетей в Java-проектах: практический пример

b23da6af3633a9f87994e559f49709c2

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

В данной статье приведен практический пример интеграции предобученной модели NER (Named Entity Recognition) для русского языка с использованием DeepPavlov и библиотеки ONNX Runtime в Java-проекте.

Что такое NER?

NER — это задача извлечения именованных сущностей из текста. Модель автоматически определяет имена людей, географические названия, даты и организации.

Пример текста:
»Иван Иванов работает в Название_Компании и живет в Москве.»

Результат модели:

  • Иван Иванов → B-PER (имя человека)

  • Москве → B-LOC (место)

  • Название_Компании → B-ORG (организация)

Вообще, существует множество предобученных моделей для извлечения именованных сущностей (NER) и других задач NLP. Вот некоторые из них:

Эти модели значительно сокращают время разработки, обеспечивая высокую точность распознавания сущностей. Например, в анализе пользовательских отзывов их можно использовать для извлечения ключевых слов, автоматизации ответов или построения рекомендаций.

Пример применения

Допустим, у вас есть база данных статей, и вы хотите извлечь из них информацию об упомянутых людях, местах и организациях. Автоматическое извлечение через модель NER позволяет:

  • Быстро анализировать тексты.

  • Строить базу знаний.

  • Улучшать поиск по ключевым именам или локациям.

Настройка проекта

Добавьте в ваш pom.xml зависимость для ONNX Runtime:


    com.microsoft.onnxruntime
    onnxruntime
    1.16.3

Скачайте предобученную модель NER для русского языка из DeepPavlov.

Пример модели: deeppavlov_ner_onnx_model.onnx

Пример кода

import ai.onnxruntime.*;
import java.util.*;

public class RussianNER {
    public static void main(String[] args) throws OrtException {
        // Путь к модели DeepPavlov в формате ONNX
        String modelPath = "models/deeppavlov_ner_onnx_model.onnx";

        // Создаем окружение ONNX Runtime
        OrtEnvironment env = OrtEnvironment.getEnvironment();
        OrtSession.SessionOptions options = new OrtSession.SessionOptions();
        OrtSession session = env.createSession(modelPath, options);

        // Ввод текста на русском языке
        String text = "Иван Иванов работает в Название_Компании и живет в Москве.";

        // Токенизация текста
        String[] tokens = {"[CLS]", "Иван", "Иванов", "работает", "в", "Название_Компании", "и", "живет", "в", "Москве", "[SEP]"};
        long[][] inputIds = {{101, 3621, 3623, 3180, 1051, 27654, 2309, 1051, 11292, 102}};
        long[][] attentionMask = {{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}};

        // Преобразование входных данных в тензоры
        OnnxTensor inputTensor = OnnxTensor.createTensor(env, inputIds);
        OnnxTensor attentionTensor = OnnxTensor.createTensor(env, attentionMask);

        // Запуск модели
        OrtSession.Result result = session.run(
            Map.of("input_ids", inputTensor, "attention_mask", attentionTensor)
        );

        // Извлечение результатов (logits)
        float[][][] logits = (float[][][]) result.get(0).getValue();

        // Поиск максимального значения (argMax) для каждой позиции токена
        int[] predictions = new int[logits[0].length];
        for (int i = 0; i < logits[0].length; i++) {
            predictions[i] = argMax(logits[0][i]);
        }

        // Интерпретация результатов
        System.out.println("Токены: " + Arrays.toString(tokens));
        System.out.println("Метки NER: " + Arrays.toString(predictions));
    }

    // Метод для нахождения индекса максимального значения
    private static int argMax(float[] values) {
        int maxIndex = 0;
        for (int i = 1; i < values.length; i++) {
            if (values[i] > values[maxIndex]) {
                maxIndex = i;
            }
        }
        return maxIndex;
    }
}

Объяснение кода

  1. Токенизация текста:

    Мы используем простой массив токенов для демонстрации. На практике, вам нужно будет использовать токенизатор, чтобы получить правильные идентификаторы для модели (например, с помощью Hugging Face Tokenizer).

    Текст преобразуется в последовательность числовых идентификаторов (inputIds), которые понимает модель.
    101 — специальный токен [CLS] (начало), 102 — [SEP] (конец).

  2. Маска внимания:
    Указывает, какие токены нужно учитывать при обработке.

  3. Прогон модели:
    Модель возвращает метки для каждого токена, например:

    • B-PER — начало имени

    • B-LOC — начало названия места

    • B-ORG — начало названия организации

  4. Вывод результатов:
    Пример для текста «Иван Иванов работает в Название_Компаниии живет в Москве.»:

Преимущества использования DeepPavlov и ONNX Runtime

  • Оптимизация: DeepPavlov предоставляет высококачественные модели для русского языка.

  • Производительность: ONNX Runtime позволяет запускать модели эффективно на CPU и GPU.

  • Простота интеграции: Благодаря ONNX Runtime, предобученные модели легко использовать в Java-проектах.

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

        // Преобразование входных данных в тензоры
        OnnxTensor inputTensor = OnnxTensor.createTensor(env, inputIds);
        OnnxTensor attentionTensor = OnnxTensor.createTensor(env, attentionMask);

Пример структуры тензоров:

  1. Скаляр (Тензор 0-го ранга):
    Это отдельное число без направлений или дополнительных измерений. Например:

    Число: 5

    Используется для представления одной величины, например, температуры в одном месте.

  2. Вектор (Тензор 1-го ранга):
    Одномерный массив чисел. Например:

    Вектор: [1, 2, 3]

    Вектор может представлять координаты точки в пространстве или значения признаков объекта.

  3. Матрица (Тензор 2-го ранга):
    Двумерный массив чисел, с строками и столбцами. Например:

    Матрица:
    [[1, 2],
     [3, 4]]

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

  4. 3D-тензор (Тензор 3-го ранга):
    Массив, содержащий двумерные массивы (матрицы). Например:

    3D-Тензор:
    [[[1, 2], [3, 4]], 
     [[5, 6], [7, 8]]]

    Может представлять набор изображений или один цветной снимок, где каждое измерение — это слой RGB.

  5. N-мерные тензоры (Тензоры выше 3-го ранга):
    Массивы большей размерности. Например, 4D-тензоры применяются для представления видео, где измерения могут быть:

    • Батч изображений,

    • Высота и ширина,

    • Цветовые каналы.

    4D-тензор:
    [[[[1], [2]], [[3], [4]]],
     [[[5], [6]], [[7], [8]]]]

Важность тензоров:

  • Гибкость: Поддерживают любые данные, от скаляров до сложных наборов.

  • Оптимизация вычислений: Все операции в нейросетях сводятся к манипуляциям с тензорами, что эффективно реализуется на GPU/TPU.

  • Универсальность: Тензоры применимы к любым задачам — от обработки изображений до анализа временных рядов.

Тензоры важны, так как нейросети выполняют математические операции над ними, такие как умножение, сложение и применение функций активации. В Java, с ONNX Runtime или библиотекой DL4J, данные преобразуются в тензоры для обработки предобученными моделями.

2) Токен BERT

        // Токенизация текста
        String[] tokens = {"[CLS]", "Иван", "Иванов", "работает", "в", "Название_Компании", "и", "живет", "в", "Москве", "[SEP]"};
        long[][] inputIds = {{101, 3621, 3623, 3180, 1051, 27654, 2309, 1051, 11292, 102}};
        long[][] attentionMask = {{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}};

При необходимости вводе строки, например через консоль, можно осуществить токенизацию, то есть найти необходимый токен к нашему слову в библиотеке или создать собственный токенизатор.

DeepPavlov Tokenizer: Можно вызвать через REST API из Java, если модель запущена на сервере.

Итак, пример в статье показывает, как предобученные модели на Java позволяют решать реальные задачи NLP, такие как извлечение именованных сущностей. Интеграция таких решений в приложения улучшает обработку данных и автоматизирует рутинные задачи, а использование предобученных моделей экономит массу времени.

© Habrahabr.ru