Практическое применение обработки естественного языка в браузере с помощью Transformers.js
В этом руководстве рассматриваются три задачи обработки естественного языка: классификация текста, разметка данных без предварительного обучения и ответы на вопросы с использованием API pipeline() библиотеки Transformers.js.

# Введение
Долгое время запуск трансформерных моделей означал поддержание сервера на Python, оплату времени работы графического процессора и маршрутизацию каждого запроса на вывод через API. Пользователь что-то вводил, данные покидали его компьютер, обращались к вашей инфраструктуре и возвращались в виде прогноза. Такая архитектура имела смысл, когда модели были слишком большими, чтобы запускать их где-либо ещё. Но сейчас это уже не единственный вариант.
Transformers.js меняет ситуацию. Он запускает самые современные модели обработки естественного языка непосредственно в браузере, на устройстве пользователя, без участия сервера. Модели загружаются один раз, кэшируются локально и работают в автономном режиме с этого момента. Перевод с Python на JavaScript практически идентичен:
// JavaScript — практически идентичный import { pipeline } from '@huggingface/transformers'; const classifier = await pipeline('sentiment-analysis'); const result = await classifier('I love transformers!');
В этом руководстве рассматриваются три задачи обработки естественного языка: классификация текста, разметка данных без предварительного обучения и ответы на вопросы с использованием API pipeline() библиотеки Transformers.js. Для каждой задачи вы увидите, как инициализировать конвейер, как выглядит структура выходных данных и как ее интерпретировать, а также рабочий HTML-пример, который можно открыть непосредственно в браузере. В заключение руководства представлено полноценное приложение для маршрутизации заявок в службу поддержки, объединяющее все три конвейера в один практический инструмент.
Во всех примерах кода в этой статье используется путь импорта CDN, поэтому этап сборки не требуется. Откройте текстовый редактор, вставьте код и запустите его.
# Что на самом деле представляет собой Transformers.js
Библиотека разработана таким образом, чтобы быть функционально эквивалентной библиотеке трансформеров на Python от Hugging Face, то есть использовать те же предварительно обученные модели, те же названия задач и тот же API конвейера, только на JavaScript. В основе этого лежит среда выполнения ONNX Runtime.
Модели, обученные в PyTorch, TensorFlow или JAX, преобразуются в формат ONNX с помощью алгоритма Hugging Face Optimum. Затем среда выполнения ONNX Runtime выполняет эти модели в браузере. По умолчанию она работает на ЦП через WebAssembly (WASM), который функционирует во всех современных браузерах. Если вам требуется ускорение с помощью графического процессора, установка параметра device: 'webgpu' значительно ускорит вычисления через API WebGPU браузера, если это возможно, хотя в некоторых средах это пока экспериментальная функция.
- Кэширование модели . При первом запуске конвейера веса модели загружаются из Hugging Face Hub и кэшируются в браузере в IndexedDB в контексте браузера, а также в файловой системе Node.js. Тестирование разработчиками показывает, что при первой загрузке конвейер анализа настроения загружает около 111 МБ данных. Последующие запуски полностью пропускают загрузку и загружают данные из кэша. Это означает, что первая пользовательская сессия имеет затраты на пропускную способность; каждая последующая сессия выполняется быстро и в автономном режиме.
- Квантование . Параметр dtype управляет точностью модели. q8 (8-битное квантование) — это значение по умолчанию в WASM; оно обеспечивает хороший баланс между размером и точностью. q4 уменьшает размер файла примерно вдвое, что приводит к потере точности на 1–3% в большинстве задач, и это оптимальный компромисс для мобильных устройств или медленного соединения. Для использования на стороне сервера Node.js fp32 обеспечивает полную точность без ограничений по размеру.
// Выполнение WASM по умолчанию — работает везде const pipe = await pipeline('sentiment-analysis'); // WebGPU для более быстрого вывода на совместимом оборудовании const pipe = await pipeline('sentiment-analysis', null, { device: 'webgpu' }); // 4-битное квантование для загрузки меньших моделей const pipe = await pipeline('sentiment-analysis', 'Xenova/distilbert-base-uncased-finetuned-sst-2-english', { dtype: 'q4' } );
# API pipeline()
Функция конвейера представляет собой полный публичный интерфейс для большинства вариантов использования. Она объединяет три компонента: предварительно обученную модель, токенизатор и логику постобработки, в один вызываемый объект. Вы не изменяете токенизатор или веса модели напрямую. Вы вызываете конвейер с текстом и получаете структурированный результат.
Подпись состоит из трех частей:
const pipe = await pipeline(task, model?, options?); const result = await pipe(input, inferenceOptions?);
Параметр task — это строковый идентификатор, который указывает библиотеке, какой тип модели следует загрузить и как обрабатывать входные и выходные данные. Параметр model необязателен; если вы его опустите, библиотека загрузит модель по умолчанию для этой задачи. Если вы укажете идентификатор модели (например, 'Xenova/distilbert-base-uncased-finetuned-sst-2-english'), эта модель будет загружена из Hub. Параметр options позволяет задать device, dtype и progress_callback.
Оба шага асинхронны. Метод pipeline() загружает модель в память. Это медленная часть при первом запуске. Сам вызов pipe() обычно выполняется быстро после загрузки модели. Оба метода возвращают промисы, а это значит, что ваш пользовательский интерфейс должен обрабатывать состояние загрузки.
Функция progress_callback позволяет отслеживать ход загрузки и отображать его пользователю:
// Функция progress_callback срабатывает во время загрузки модели с обновлениями статуса. // Это важный UX-фактор — пользователи должны знать, что что-то происходит. const pipe = await pipeline( 'sentiment-analysis', 'Xenova/distilbert-base-uncased-finetuned-sst-2-english', { dtype: 'q8', progress_callback: (progress) => { // progress.status может быть: 'initiate', 'download', 'progress', 'done' if (progress.status === 'progress') { const pct = Math.round(progress.progress); document.getElementById('progress').textContent = `Загрузка модели: ${pct}%`; } if (progress.status === 'ready') { document.getElementById('progress').textContent = 'Model' готовый'; } } } );
Важное замечание из официальной документации: Transformers.js — это библиотека только для инференции. С её помощью нельзя выполнять тонкую настройку или обучение моделей. Если для вашей задачи требуется собственная модель, обучение происходит в другом месте (Python, облако), а полученный в результате экспорт ONNX выполняется в браузере.
# Задание 1: Классификация текста
Классификация текста присваивает входному тексту метку и оценку достоверности. Наиболее распространенной формой является анализ тональности, положительная или отрицательная, но та же архитектура конвейера обрабатывает любой фиксированный набор категорий, на которых была обучена модель.
Вот как выглядит результат:
const result = await classifier('Этот продукт полностью превзошел мои ожидания.'); // [{ label: 'POSITIVE', score: 0.9997 }]
На выходе получается массив объектов. Каждый объект содержит метку (предполагаемый класс в виде строки) и оценку (число с плавающей запятой от 0 до 1, представляющее уверенность модели). Оценка 0,9997 означает высокую уверенность модели. Оценка 0,52 означает, что она едва превышает пороговое значение для принятия решения; это следует рассматривать как неопределенность и соответствующим образом обрабатывать в логике вашего приложения.
Выходные данные всегда представляют собой массив, даже для одного входного значения, поскольку один и тот же вызов конвейера обрабатывает пакеты:
const results = await classifier([ 'Это здорово!', 'Полностью не работает, пустая трата денег.' ]); // [ // { label: 'POSITIVE', score: 0.9998 }, // { label: 'NEGATIVE', score: 0.9991 } // ]
// Полный рабочий пример
Приведённый ниже пример представляет собой полный, самодостаточный HTML-файл. Откройте его в любом современном браузере. Модель загружается при первом запуске и кэшируется при последующих загрузках, которые происходят мгновенно.
Классификатор настроения
Работает полностью в вашем браузере — без сервера, без вызовов API.
Функция loadModel вызывает pipeline() с именем задачи, идентификатором модели и параметрами. Функция progress_callback срабатывает многократно во время загрузки и обновляет текст состояния, чтобы пользователь не смотрел на зависший экран. После загрузки модели кнопка становится активной. Когда пользователь нажимает кнопку «Классифицировать», classifier(text) синхронно выполняет вывод из кэша, обычно менее чем за 200 мс на современном ноутбуке. В результате извлекаются метка и оценка из первого элемента массива, форматируется достоверность в процентах и применяется CSS-класс для цветовой кодировки.
# Задача 2: Классификация без предварительного обучения
Классификация без предварительного обучения делает то, чего не может обычная классификация текста: она классифицирует текст по категориям, которые вы определяете во время выполнения, без необходимости использования обучающих данных. Вы передаете текст и список меток на простом английском языке. Модель решает, какая метка подходит лучше всего, основываясь на своем понимании семантики языка.
Это полезно в тех случаях, когда вы не можете или не хотите обучать модель на размеченных примерах, что чаще всего встречается в реальных проектах.
// Как это работает изнутри
Модель переформулирует каждую потенциальную метку как гипотезу вывода на естественном языке (NLI). Для метки « проблема с выставлением счетов » она генерирует гипотезу « Этот текст касается проблемы с выставлением счетов » и вычисляет вероятность того, что эта гипотеза вытекает из входного текста. Побеждает метка с наивысшим показателем вывода. Именно благодаря такому подходу, основанному на NLI, вы можете использовать любую описательную английскую фразу в качестве метки и получить осмысленный результат. Модель понимает смысл ваших меток, а не только их поверхностную форму.
Вот как выглядит результат:
const classifier = await pipeline('zero-shot-classification', 'Xenova/bart-large-mnli'); const result = await classifier( 'Мой счет неверен, и с меня списали деньги дважды.', ['billing', 'technical support', 'shipping', 'returns', 'account access'] ); // { // sequence: 'Мой счет неверен, и с меня списали деньги дважды.', // labels: ['billing', 'returns', 'account access', 'technical support', 'shipping'], // scores: [0.871, 0.063, 0.031, 0.022, 0.013] // }
Результатом является объект с тремя полями. sequence — это исходный текст. labels — это массив ваших потенциальных меток, отсортированный от наивысшего к наинизшему значению. scores — это массив оценок достоверности в том же порядке. Первый элемент обоих массивов всегда является выигрышным предсказанием. Сумма оценок по всем меткам составляет приблизительно 1, когда multi_label имеет значение false (по умолчанию).
Установка параметра multi_label: true изменяет поведение: каждая метка оценивается независимо, а не конкурирует, поэтому несколько меток могут одновременно иметь высокие оценки. Используйте этот параметр, когда текст, вероятно, относится к нескольким категориям одновременно.
// Полный рабочий пример
Вот обновленный блок скрипта со всеми HTML-скобками, полностью экранированными. Вы можете вставить его непосредственно в свой блок «Пользовательский HTML» в WordPress, и он будет отлично отображаться как фрагмент кода.
Маршрутизатор заявок в службу поддержки
Вставьте заявку в службу поддержки. Модель направит ее в нужный отдел без необходимости использования обучающих данных.
Массив DEPARTMENTS содержит всю необходимую конфигурацию маршрутизации для этой системы. Никаких обучающих данных, никаких размеченных примеров. Когда поступает заявка, классификатор (текст, DEPARTMENTS, { multi_label: false }) выполняет все пять внутренних проверок на соответствие и возвращает их в ранжированном виде. Цикл обработки результатов строит горизонтальную гистограмму, показывающую оценку каждого отдела, — отсортированную визуализацию, которая сразу же показывает, куда должна быть направлена заявка и насколько уверенной была модель. Попробуйте изменить массив DEPARTMENTS на совершенно другие метки; модель правильно выполнит маршрутизацию без каких-либо изменений в коде, кроме этого массива.
# Задание 3: Ответы на вопросы
В Transformers.js ответ на вопросы осуществляется методом извлечения информации: вы предоставляете фрагмент текста в качестве контекста и задаете вопрос простым английским языком. Модель находит в фрагменте текста фрагмент, который наилучшим образом отвечает на вопрос, и возвращает его. Она не генерирует текст или рассуждения, выходящие за рамки буквального содержания контекста. Ответ всегда является подстрокой предоставленного вами входного текста.
Это делает его хорошо подходящим для анализа документов. Пользователь предоставляет документ, а модель осуществляет навигацию по нему.
Вот как выглядит результат:
const qa = await pipeline('question-answering', 'Xenova/distilbert-base-uncased-distilled-squad'); const result = await qa({ question: 'Каков срок возврата электроники?', context: `Наша политика возврата позволяет покупателям возвращать большинство товаров в течение 30 дней с момента покупки. Электроника должна быть возвращена в течение 15 дней и в оригинальной упаковке. Программное обеспечение и цифровые загрузки возврату не подлежат.` }); // { // answer: '15 days', // score: 0.9823, // start: 97, // индекс символа начала ответа в контексте // end: 104 // индекс символа конца ответа в контексте // }
Выходные данные содержат четыре поля. answer — это извлеченная подстрока. score — это степень уверенности модели в том, что данный фрагмент отвечает на вопрос. start и end — это индексы символов в исходном контексте; вы можете использовать их для выделения ответа в исходном тексте, что является ценным элементом пользовательского интерфейса для длинных документов.
Если в контексте вопроса нет однозначного ответа, оценка будет низкой, а ответ может представлять собой короткий, на первый взгляд случайный отрезок времени. Стандартная практика заключается в том, чтобы рассматривать ответы с низкой степенью достоверности (ниже 0,3 или 0,4) как «ответ не найден».
// Полный рабочий пример
Вот экранированный код для вашего блока вопросов и ответов в документации. Он идеально обрабатывает все скобки внутри скрипта и шаблонов, поэтому он будет корректно отображаться на вашем сайте.
Ответы на вопросы по документу
Вставьте любой документ, а затем задайте вопросы по нему. Ответы извлекаются непосредственно из текста.
Три конвейера загружаются параллельно с помощью Promise.all. Это быстрее, чем последовательная загрузка, поскольку загрузки перекрываются. Счетчик отслеживает, сколько из них завершилось, поэтому кнопка активируется только тогда, когда все три готовы. Когда пользователь отправляет заявку, все три вывода также выполняются параллельно. Карточка оценки настроения проверяет, является ли результат отрицательной с высокой степенью достоверности, и помечает его как срочный практический сигнал маршрутизации, не требующий дополнительной модели.
На карточке отдела отображаются три кандидата с наивысшими оценками, а не только победитель, что дает команде поддержки достаточно информации для изменения маршрутизации, если наивысшая оценка близка ко второй. Карточка контроля качества выполняет три запроса на извлечение данных из тела заявки и отображает результаты с порогом достоверности: ответы ниже 0,1 отображаются как «не найдено», а не показывают низкокачественные извлечения.
# Производительность, ограничения и когда его не следует использовать
Transformers.js устраняет сервер, но не исключает компромиссов. Знание этих компромиссов заранее избавит вас от неприятных сюрпризов в процессе эксплуатации.
- Размер загружаемого файла . Конвейер анализа настроений загружает около 111 МБ при первой загрузке, что не очень много, но и не незаметно. Модель BART с нулевым количеством примеров больше. Для приложений, ориентированных на мобильных пользователей или пользователей с лимитированным подключением, следует уменьшить размер модели примерно вдвое и рассматривать модель как поэтапное улучшение; не следует блокировать пользовательский интерфейс при загрузке модели.
- Скорость вывода . На современном ноутбуке вывод WASM для классификации коротких текстов занимает 50–200 мс. Классификация без предварительного обучения (zero-shot classification) медленнее, поскольку выполняется несколько проходов NLI, по одному для каждой потенциальной метки. Выполнение классификации без предварительного обучения для пяти меток обычно занимает 1–3 секунды на ЦП. WebGPU значительно сокращает это время там, где это поддерживается.
- Только для вывода результатов . Transformers.js не может выполнять тонкую настройку или обучение моделей. Если для вашего случая требуется пользовательская модель, например, классификатор, обученный на ваших собственных размеченных билетах, обучение происходит на сервере (Python, облачная среда), а экспорт ONNX выполняется в браузере.
- Доступность моделей . Не для всех моделей на Hugging Face Hub доступна версия ONNX. Чтобы найти совместимые модели, отфильтруйте результаты по тегу библиотеки transformers.js на Hub.
- Когда предпочтительнее использовать сервер: массовая обработка сотен текстов, где важна задержка на каждый элемент; задачи, требующие самых больших моделей, которые слишком велики для доставки через браузер; или простые приложения, где затраты на разработку браузерного вывода перевешивают его преимущества.
Краткая справочная информация по выбору типа данных в зависимости от контекста:
| Контекст | Рекомендуемый тип данных | Почему |
|---|---|---|
| Браузер, общего назначения | q8 | WASM по умолчанию, хороший баланс между размером и точностью. |
| Плохое или медленное соединение | q4 | Примерно вдвое меньший размер файла, снижение точности на 1-3%. |
| Серверная часть Node.js | fp32 | Полная точность, никаких проблем с размером загружаемых файлов. |
| WebGPU с поддержкой | fp16 | Быстрая и качественная работа на совместимом графическом процессоре. |
# Завершение
Transformers.js предоставляет высококачественную обработку естественного языка в браузере без сервера, без ключа API и без передачи пользовательских данных с устройства. Три конвейера, представленные в этом руководстве — классификация текста, разметка с нулевым обучением и ответы на вопросы — охватывают аналитическую поверхность значительной части реальных сценариев использования обработки естественного языка. Маршрутизатор заявок в службу поддержки показывает, как они объединяются в нечто действительно полезное менее чем за 200 строк HTML и JavaScript.
Начальный уровень максимально прост: один импорт CDN, один вызов функции `await pipeline()`, один вызов функции вывода. Начните с самого простого примера из этой статьи и запустите его. Измените метки в демонстрационном примере без предварительного обучения. Направьте модель QA на другой документ. Официальная документация Transformers.js и репозиторий примеров охватывают гораздо более широкий спектр задач: суммирование, перевод, распознавание именованных сущностей и многое другое, и все они следуют одному и тому же шаблону `pipeline()`.
Шитту Олумиде — инженер-программист и технический писатель, увлеченный использованием передовых технологий для создания захватывающих повествований, обладающий острым вниманием к деталям и умением упрощать сложные концепты. Шитту также можно найти в Твиттере.
Источник: www.kdnuggets.com

Добавить комментарий
Для отправки комментария вам необходимо авторизоваться.