Архивный материал Mozilla (июнь 2022 г.). Указанные версии, результаты и планы относятся к публикации источника. Первоисточник.
Нейросетевая система машинного перевода для дополнения Firefox Translations
Firefox Translations — это дополнение для перевода веб-сайтов, которое предоставляет автоматический перевод веб‑контента. В отличие от облачных альтернатив, перевод выполняется локально на клиентской стороне на компьютере пользователя, поэтому переводимый текст не покидает устройство, что делает процесс полностью приватным. Дополнение доступно для установки в Firefox Nightly, Beta и в стабильном релизе.
Дополнение использует результаты проекта Bergamot, который является совместной работой Mozilla, Эдинбургского университета, Карлова университета в Праге, Университета Шеффилда и Тартуского университета при финансировании в рамках программы Европейского союза Horizon 2020 по исследованиям и инновациям.
В основе дополнения лежит Bergamot Translator — нейросетевая система машинного перевода, выполняющая саму задачу перевода. Этот движок также может использоваться в различных контекстах, например, на демонстрационном сайте, который позволяет пользователю выполнять произвольные переводы без использования облака.
В этой статье мы обсудим технические задачи, возникавшие при разработке движка перевода, и как мы их решили, чтобы создать работающее дополнение Firefox Translations.
Движок перевода построен поверх фреймворка marian, свободного фреймворка нейросетевого машинного перевода, написанного на чистом C++. Фреймворк имеет автономное нативное приложение, способное обеспечить простую функциональность перевода. Однако для дополнения потребовалось добавить две новые функции, которых не было в существующем нативном приложении.
Первая — перевод форм, позволяющий пользователям вводить текст на своём языке и динамически переводить его «на лету» на язык страницы. Вторая — оценка качества перевода, чтобы переводы с низкой уверенностью могли автоматически выделяться на странице и уведомлять пользователя о возможных ошибках. Это привело к разработке движка перевода как высокого уровня C++ API-слоя поверх marian.
Получившийся движок компилируется непосредственно в нативный код. Для интеграции в дополнение рассматривались три архитектурных варианта:
- Нативная интеграция в Firefox: включение всего нативного кода движка в Firefox.
- Нативные сообщения (native messaging): разворачивание движка как нативного приложения на компьютере пользователя и обмен сообщениями с дополнением.
- Wasm: портирование движка в WebAssembly и интеграция в дополнение с использованием разработанных JS-обвязок.
Мы оценивали эти решения по факторам, которые считали критически важными для производства дополнения:
- Безопасность: подход нативной интеграции в Firefox был отвергнут после внутреннего аудита безопасности Mozilla кода движка, который выявил проблемы из‑за большого числа сторонних зависимостей фреймворка marian.
- Масштабируемость и сопровождение: native messaging создал бы сложности с распространением кода проекта из‑за необходимости предоставлять сборки для всех платформ, поддерживаемых Firefox, что было бы непрактично масштабировать и поддерживать.
- Поддержка платформ: сам фреймворк marian поддерживает перевод только на процессорах с архитектурой x86/x86_64. С учётом роста числа устройств на ARM, native messaging ограничил бы доступность приватного локального перевода для широкой аудитории.
- Производительность: Wasm работает медленнее по сравнению с нативным кодом, но имеет потенциал исполнения практически на нативной скорости, используя общие аппаратные возможности, доступные на множестве платформ.
Дизайн Wasm как переносимой целевой платформы для компиляции языков программирования означал возможность разработки и распространения единого бинарного файла, работающего на всех платформах. Кроме того, Wasm безопасен по памяти и выполняется в изолированной песочнице, что делает его безопасным для парсинга и обработки веб‑контента. Все эти преимущества, вкупе с потенциалом близкой к нативной производительности, побудили нас прототипировать этот архитектурный вариант и оценить, соответствует ли он требованиям производительности дополнения.
Мы выбрали toolchain Emscripten для компиляции движка в Wasm. Движок не компилировался в Wasm «из коробки», и нам пришлось внести несколько изменений, чтобы успешно скомпилировать и выполнить перевод с помощью сгенерированного Wasm‑бинаря, среди них:
- Отключили код, связанный с обучением, специфичный для marian, так как для работы движка требовался только код, связанный с переводом.
- Отключили многопоточность, поскольку любой код с pthread, скомпилированный в Wasm, требует поддержки SharedArrayBuffer в браузерах, которая на момент разработки переходила от отключённого состояния к повторному включению.
- Движок зависит от обученных моделей, словарей и опционального файла shortlist для выполнения перевода и ожидает, что пользователь предоставит их извне. Поскольку нативный код и обычный JavaScript используют разные парадигмы доступа к файлам, мы упаковали все необходимые файлы в виртуальную файловую систему Emscripten для ускорения прототипирования.
- Заменили проприетарную библиотеку (Intel MKL) на открытую (onnxjs) для выполнения операции SGEMM в слое внимания фреймворка marian.
- Исправили проблему с некорректным использованием нативного типа данных size_t в marian, что вызывало сбои при переводе.
После получения рабочего Wasm‑бинаря переводчика мы выявили несколько ключевых проблем, которые требовалось решить, чтобы преобразовать прототип в пригодный продукт.
- Упаковка всех файлов для каждой поддерживаемой пары языков в Wasm‑бинар делала масштабирование для новых пар непрактичным. Все файлы каждой пары языков (перевод в обе стороны) в сжатом виде занимают примерно 40 МБ на диск. Например, поддержка 6 пар языков увеличивала размер бинаря примерно до 250 МБ.
- Такая упаковка заставляла пользователей загружать все поддерживаемые пары языков, даже если они планировали использовать только несколько из них, что было крайне неэффективно по сравнению с загрузкой необходимых файлов по требованию пользователя.
Мы провели бенчмарки движка по трём основным метрикам, которые считали критичными с точки зрения удобства использования:
- Время запуска: время, необходимое движку, чтобы быть готовым к переводу. На этом шаге движок загружает модели, словари и опционально файл shortlist.
- Скорость перевода: время, затраченное движком на перевод данного текста после успешного старта, измеряемое в словах в секунду (wps).
- Размер Wasm‑бинаря: объём дискового пространства генерируемого Wasm‑файла.
Размер сгенерированного Wasm‑бинаря из‑за упаковки зависел от числа поддерживаемых пар языков. Движок запускался необычно долго (~8 секунд) и очень медленно выполнял перевод, делая его непригодным для использования.
Например, перевод с английского на немецкий с соответствующими моделями давал лишь 95 wps на MacBook Pro (15‑inch, 2017), MacOS 11.6.2, 3.1 GHz Quad‑Core Intel Core i7, 16 GB RAM.
Поскольку упаковка файлов негативно влияла на удобство использования, мы решили сначала решить эту проблему. Мы внедрили новый API в движок перевода, который позволяет передавать требуемые файлы как байтовые буферы извне, вместо упаковки их во время компиляции в виртуальную ФС Emscripten.
Это позволило движку масштабироваться для новых языков без увеличения размера Wasm‑бинаря и дало дополнению возможность динамически загружать файлы только для тех пар языков, которые интересовали пользователя. Финальный размер Wasm‑бинаря (~6.5 МБ) оказался в пределах приемлемого значения.
Новый API, разработанный для решения проблемы упаковки, в сочетании с другими оптимизациями в marian, решил проблему долгого старта. Время запуска движка существенно сократилось (~1 секунда), что оказалось приемлемым.
Профилирование шага перевода в браузере показало, что наиболее вычислительно интенсивной операцией является General matrix multiply (GEMM) для 8‑бит целочисленных операндов, а код обработки исключений давал большой оверхед на скорости перевода. Мы сосредоточили усилия на оптимизации обеих частей.
Оптимизация обработки исключений: мы заменили try/catch на реализацию с if/else в часто вызываемой функции во время перевода, что привело к повышению скорости перевода примерно на 20%.
Оптимизация операции GEMM: глубокое изучение результатов профилирования показало, что отсутствие инструкции GEMM в стандарте Wasm является причиной низкой производительности на Wasm.
Экспериментальные инструкции GEMM: исключительно для оценки производительности GEMM без стандартизации в Wasm мы внедрили две экспериментальные инструкции в Firefox Nightly и Release для архитектур x86/x86_64. Эти инструкции улучшили скорость перевода примерно на 310%, и перевод веб‑страниц стал достаточно быстрым для практического использования на этих архитектурах. Эта функция была защищена флагом и открыта только привилегированным расширениям в релизе Firefox из‑за экспериментального характера. Мы по‑прежнему стремились найти стандартное решение перед выпуском в производство, но это позволило нам продолжать разработку дополнения, работая параллельно с командой Wasm Firefox над долгосрочным решением.
Нестандартное долгосрочное решение: в отсутствие конкретных сроков реализации инструкции GEMM в стандарте Wasm мы заменили экспериментальные инструкции GEMM на специфическое для Firefox нестандартное долгосрочное решение, которое обеспечивало такие же или лучшие скорости перевода, как и экспериментальные инструкции. Помимо привилегированных расширений, это решение позволило предоставлять функциональность перевода неприлигерованным расширениям и обычному контенту с такими же скоростями перевода и обеспечило работу на платформах ARM64, пусть и с низкой скоростью. Ничего этого нельзя было бы достичь с экспериментальными инструкциями GEMM.
Нативные GEMM‑интринсики: в стремлении ещё более повысить скорость перевода мы внедрили нативную реализацию GEMM в Firefox Nightly, защищённую флагом и доступную как intrinsics. Движок перевода мог напрямую вызывать эти intrinsics во время шага перевода, когда он запускался в Firefox Nightly на системах x86/x86_64. Эта работа увеличила скорость перевода на 25% и 43% для SIMD‑расширений SSSE3 и AVX2 соответственно по сравнению с ранее внедрёнными экспериментальными инструкциями.
Обновление toolchain Emscripten: недавнее обновление toolchain Emscripten до последней версии увеличило скорость перевода на всех платформах примерно на 15% в Firefox и уменьшило размер Wasm‑бинаря ещё на ~25% (финальный размер ~4.94 МБ).
В итоге нам удалось достичь скорости перевода примерно 870 wps для перевода с английского на немецкий с соответствующими моделями на Firefox Release на MacBook Pro (15‑inch, 2017), MacOS 11.6.2, 3.1 GHz Quad‑Core Intel Core i7, 16 GB RAM.
Движок перевода оптимизирован для высокой скорости перевода прежде всего на процессорах x86/x86_64, и у нас есть идеи по улучшению ситуации на ARM. Стандартизованная инструкция GEMM для Wasm может обеспечить схожие скорости на ARM, что принесёт выгоду новому классу потребительских ноутбуков и мобильных устройств. Мы также знаем, что нативный движок marian работает ещё лучше с многопоточностью, но в этой версии движка многопоточный код был отключён. Как только поддержка SharedArrayBuffer будет широко включена, мы рассчитываем повторно включить многопоточность, и возможны ещё более высокие скорости перевода.
Хочу поблагодарить партнёров консорциума Bergamot, Wasm‑команду Mozilla и моих коллег Андре Natal, Евгения Павлова за вклад в разработку зрелого движка перевода. Я признателен Лоннену, а также командам Mozilla по дополнениям, локализации, тестированию и сообществу Mozilla, которые поддерживали нас и внесли вклад в разработку дополнения Firefox Translations.
Этот проект получил финансирование от программы Европейского союза Horizon 2020 по исследованиям и инновациям по соглашению о гранте № 825303.
Спасибо! Пожалуйста, проверьте вашу почту, чтобы подтвердить подписку.
Если вы ранее не подтверждали подписку на рассылку, связанную с Mozilla, возможно, потребуется подтвердить её. Пожалуйста, проверьте ваш почтовый ящик или папку спама на наличие письма от нас.
