Введение: зачем создавать ML-игру в браузере?
Браузерная ML-игра демонстрирует, что инференс нейросети на клиенте возможен без серверной инфраструктуры. Пользователь рисует объект, модель распознаёт его локально, результат появляется мгновенно. Это даёт три преимущества: приватность данных, низкую задержку и доступность на любом устройстве с современным браузером.
В этой статье мы разберём процесс создания игры Doodle Dash. Нейросеть в реальном времени распознаёт рисунки пользователя со скоростью более 60 предсказаний в секунду. Мы дообучим лёгкую модель MobileViT на датасете Quick, Draw!, конвертируем её в ONNX-формат через Optimum и запустим инференс в браузере с помощью Transformers.js и Web Workers. Отдельно рассмотрим геймдизайн: как адаптировать механику угадывания под высокую частоту предсказаний, отфильтровать неоднозначные классы и улучшить пользовательский опыт.
Материал ориентирован на разработчиков и ML-инженеров, которые хотят создавать клиентские AI-приложения без затрат на серверный хостинг. Практический результат - работающий прототип игры, который можно адаптировать под свои задачи.
Обзор технологий: MobileViT, Quick, Draw!, ONNX, Optimum, Transformers.js
Каждый инструмент в этом стеке решает конкретную задачу. MobileViT - лёгкая архитектура для распознавания изображений, сочетающая свёрточные слои и трансформерные блоки. Она даёт хороший баланс между точностью и скоростью на CPU, что критично для браузерного инференса. Quick, Draw! - открытый датасет Google с миллионами простых рисунков, размеченных по 345 категориям. ONNX - формат обмена моделями между фреймворками, который поддерживает оптимизации под разные устройства. Optimum - библиотека Hugging Face для конвертации и оптимизации моделей, включая экспорт в ONNX. Transformers.js - JavaScript-библиотека для запуска моделей трансформеров в браузере. Web Workers - технология фоновых потоков, которая выносит вычисления из основного потока UI.
Выбор MobileViT обусловлен размером: модель после квантования занимает несколько мегабайт и работает на мобильных устройствах. Quick, Draw! подходит для задачи распознавания набросков, поскольку рисунки в нём простые и однозначные. Transformers.js обеспечивает единый API для загрузки ONNX-моделей и выполнения предсказаний, а Web Workers предотвращают блокировку интерфейса при интенсивных вычислениях.
Шаг 1: Подготовка данных и дообучение MobileViT на датасете Quick, Draw!
Датасет - это структурированная коллекция данных для обучения, тестирования и валидации моделей машинного обучения. Он обычно делится на тренировочный, валидационный и тестовый наборы. Качество и разнообразие датасета напрямую влияют на точность, обобщающую способность и надёжность модели. В случае Quick, Draw! мы получаем готовые векторные штрихи, которые нужно преобразовать в растровые изображения.
Загрузка и предобработка датасета Quick, Draw!
Для загрузки данных используйте библиотеку quickdraw. Она скачивает файлы в формате .npy с координатами штрихов. Преобразование в растровые изображения выполняется через отрисовку штрихов на холсте заданного размера. Для MobileViT используйте разрешение 224x224 пикселя, хотя для быстрого прототипа можно начать с 28x28. После отрисовки нормализуйте значения пикселей в диапазон [0, 1] и разделите данные на обучающую и валидационную выборки в соотношении 80/20.
Пример преобразования штрихов в изображение:
import numpy as np
from PIL import Image, ImageDraw
def strokes_to_image(strokes, size=224):
img = Image.new('L', (size, size), 0)
draw = ImageDraw.Draw(img)
for stroke in strokes:
points = [(x * size / 256, y * size / 256) for x, y in zip(stroke[0], stroke[1])]
if len(points) > 1:
draw.line(points, fill=255, width=3)
return np.array(img) / 255.0Нормализация пикселей ускоряет сходимость обучения. Для валидации отложите по 1000 примеров на каждый класс, чтобы получить объективную оценку точности.
Дообучение MobileViT: код и гиперпараметры
Загрузите предобученную MobileViT из Hugging Face и замените классификационный слой на нужное количество классов. Для Quick, Draw! это 345 категорий, но после фильтрации неоднозначных классов останется меньше. Используйте Trainer API для обучения.
from transformers import MobileViTForImageClassification, MobileViTImageProcessor, Trainer, TrainingArguments
model = MobileViTForImageClassification.from_pretrained(
'apple/mobilevit-small',
num_labels=345,
ignore_mismatched_sizes=True
)
training_args = TrainingArguments(
output_dir='./mobilevit_quickdraw',
learning_rate=2e-5,
per_device_train_batch_size=32,
num_train_epochs=5,
evaluation_strategy='epoch',
save_strategy='epoch',
logging_steps=50,
remove_unused_columns=False,
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset=val_dataset,
)
trainer.train()Рекомендуемые гиперпараметры: learning rate 2e-5, batch size 32, 3-5 эпох. Точность на валидации после 3 эпох обычно достигает 85-90% для хорошо различимых классов. Для ускорения обучения используйте смешанную точность FP16, если доступен GPU.
Шаг 2: Конвертация модели в ONNX с помощью Optimum
Конвертация PyTorch-модели в ONNX выполняется одной командой через Optimum CLI:
optimum-cli export onnx --model ./mobilevit_quickdraw --task image-classification ./mobilevit_onnxВозможные проблемы: несовместимость отдельных операций с ONNX Runtime, ошибки при экспорте кастомных слоёв. Решение - обновить Optimum и Transformers до последних версий, а также проверить, что модель не содержит динамических операций, не поддерживаемых ONNX. Преимущества ONNX: кросс-платформенность, оптимизация под CPU и GPU, возможность квантования.
После конвертации проверьте размер файла model.onnx. Для MobileViT Small он составляет около 20-25 МБ в FP32. Этого достаточно для браузерной загрузки, но после квантования размер уменьшится в 4 раза.
Шаг 3: Запуск инференса в браузере с Transformers.js и Web Workers
Transformers.js позволяет запускать модели трансформеров в браузере. Установите библиотеку через npm: npm install @xenova/transformers. Загрузите ONNX-модель и создайте пайплайн классификации изображений.
Интеграция Transformers.js: загрузка модели и инференс
import { pipeline } from '@xenova/transformers';
const classifier = await pipeline('image-classification', './mobilevit_onnx');
const canvas = document.getElementById('drawing-canvas');
const imageData = canvas.toDataURL('image/png');
const predictions = await classifier(imageData);
console.log(predictions);Передавайте изображение с canvas в виде data URL или тензора. Модель возвращает массив предсказаний с вероятностями. Для ускорения загрузки разместите модель на CDN и используйте кэширование браузера.
Использование Web Workers для неблокирующего UI
Инференс в основном потоке блокирует рендеринг интерфейса. Вынесите вычисления в Web Worker. Создайте файл worker.js:
import { pipeline } from '@xenova/transformers';
let classifier;
self.onmessage = async (event) => {
if (event.data.type === 'init') {
classifier = await pipeline('image-classification', event.data.modelPath);
self.postMessage({ type: 'ready' });
} else if (event.data.type === 'predict') {
const predictions = await classifier(event.data.image);
self.postMessage({ type: 'result', predictions });
}
};В основном потоке создайте worker и обменивайтесь сообщениями через postMessage. Это обеспечивает плавность анимаций и отзывчивость интерфейса при частоте предсказаний более 60 раз в секунду.
Геймдизайн Doodle Dash: адаптация под скорость предсказаний
Doodle Dash - игра, в которой игрок рисует заданный объект, а модель распознаёт его в реальном времени. Высокая частота предсказаний позволяет создать динамику: модель оценивает рисунок каждые 16 миллисекунд, и игра мгновенно реагирует на изменения. Это принципиально отличается от серверных решений, где задержка составляет сотни миллисекунд.
Фильтрация классов и обработка неоднозначностей
Проанализируйте матрицу ошибок на валидационной выборке. Удалите классы с точностью ниже 70% или высокой путаницей между похожими категориями, например «круг» и «овал». Оставьте 100-200 хорошо различимых классов. Это повысит субъективное качество игры и уменьшит фрустрацию игрока.
Для анализа матрицы ошибок используйте следующий подход: соберите предсказания модели на валидации, постройте confusion matrix, найдите пары классов с взаимной путаницей выше 15%. Удалите один класс из каждой такой пары.
Оптимизация пользовательского опыта
Показывайте топ-3 предсказания модели с вероятностями. Используйте цветовую индикацию уверенности: зелёный при вероятности выше 80%, жёлтый при 50-80%, красный ниже 50%. Добавьте звуковые эффекты при правильном распознавании. Обеспечьте быстрый отклик: предсказание должно обновляться не реже 60 раз в секунду.
Таймер на 20 секунд на раунд создаёт напряжение. Подсказки в виде текстового описания объекта помогают игроку. Счёт увеличивается за каждый угаданный объект, бонус за скорость добавляет азарт.
Оптимизация модели для клиентского инференса
Квантование - это процесс преобразования чисел с плавающей точкой в более компактные форматы, например INT8. Квантование до INT8 может уменьшить размер модели в 4 раза и увеличить производительность в 2-4 раза. Существуют методы: пост-тренировочное квантование (PTQ), квантование с дообучением (QAT) и динамическое квантование.
Квантование модели до INT8
Используйте Optimum CLI для квантования ONNX-модели:
optimum-cli onnxruntime quantize --avx2 --onnx_model ./mobilevit_onnx/model.onnx --output ./mobilevit_quantizedСравните размер и скорость до и после. Для MobileViT Small квантование уменьшает размер с 22 МБ до 5-6 МБ. Скорость инференса на CPU увеличивается в 2-3 раза. Точность падает на 1-2%, что приемлемо для игрового сценария. Квантование может привести к потере точности, особенно для сложных моделей, поэтому проверьте метрики на валидации после квантования.
Другие техники оптимизации: pruning удаляет наименее значимые веса, distillation переносит знания большой модели в меньшую. Для браузерных игр квантование даёт наибольший эффект при минимальных усилиях. Не все фреймворки и железо поддерживают квантованные модели, но ONNX Runtime Web поддерживает INT8 на большинстве современных браузеров.
Заключение: возможности и ограничения браузерных ML-игр
Мы прошли полный цикл: дообучение MobileViT на Quick, Draw!, конвертация в ONNX через Optimum, запуск инференса в браузере с Transformers.js и Web Workers, адаптация геймдизайна под высокую скорость предсказаний. Результат - работающая игра Doodle Dash, которая распознаёт рисунки локально на устройстве пользователя.
Браузерные ML-игры демонстрируют возможности клиентского инференса, но имеют ограничения. Размер модели ограничен памятью устройства, производительность зависит от CPU или GPU пользователя, а сложные архитектуры не помещаются в браузерный контекст. Для простых задач классификации изображений и текста ограничения не критичны.
Экспериментируйте с другими датасетами и архитектурами. Попробуйте заменить MobileViT на более лёгкую модель или добавить распознавание жестов. Делитесь результатами и метриками производительности: это помогает сообществу оценивать реальные возможности браузерного ML.