Как ускорить время загрузки модели Gensim Word2vec?

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

Я использую предварительно подготовленную модель с 3 миллионами слов от Google (GoogleNews-vectors-negative300).

Итак, я загружаю модель с помощью Gensim:

import gensim
model = gensim.models.KeyedVectors.load_word2vec_format('GoogleNews-vectors-negative300.bin', binary=True)

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

Итак, что я могу сделать, чтобы ускорить время загрузки?

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

Ответ 1

В последних версиях gensim вы можете загрузить подмножество, начиная с фронта файла, используя необязательный параметр limit до load_word2vec_format(). (Похоже, что векторы GoogleNews находятся примерно в порядке наименее частых порядков, поэтому первый N обычно является подмножеством N-размера, который вы хотите. Поэтому используйте limit=500000, чтобы получить самые частые векторы 500 000 слов - все еще довольно большой словарный запас - сохранение 5/6-й части памяти/времени загрузки.)

Так что это может немного помочь. Но если вы повторно загружаете для каждого веб-запроса, вам все равно придется болеть от загрузки скорости с привязкой к IO и избыточной избыточной памяти для хранения каждой повторной загрузки.

Есть несколько трюков, которые вы можете использовать в комбинации, чтобы помочь.

Обратите внимание, что после загрузки таких векторов в исходном формате word2vec.c-origin, вы можете повторно сохранить их, используя gensim native save(). Если вы сохраните их несжатыми, а массив поддержки достаточно велик (и набор GoogleNews определенно достаточно велик), массив поддержки будет сбрасываться в отдельный файл в формате двоичного файла. Этот файл позже может быть отображен на карту с диска, используя опцию gensim native [load(filename, mmap='r')][1].

Изначально это заставит загрузку казаться мгновенной - вместо того, чтобы читать весь массив с диска, ОС просто отобразит области виртуальных адресов на данные диска, так что через некоторое время, когда код обратится к этим ячейкам памяти, необходимые диапазоны будет считываться с диска. Пока все хорошо!

Однако, если вы выполняете типичные операции, такие как most_similar(), вы все равно столкнетесь с большими задержками, чуть позже. Это потому, что эта операция требует как первоначального сканирования и расчета по всем векторам (при первом вызове, для создания векторов с нормализованной длиной единицы для каждого слова), а затем другого сканирования и вычисления по всем нормированным векторам (по каждый вызов, чтобы найти N-наиболее похожие векторы). Те, у кого есть полный доступ к проверке, будут отображать всю информацию в целом по массиву - снова стоимость пары минут IO.

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

Сначала загрузите векторы word2vec.c в формате load_word2vec_format(). Затем используйте model.init_sims(replace=True), чтобы принудительно выполнить нормализацию единицы, разрушительно на месте (сглаживание ненормированных векторов).

Затем сохраните модель в новом префиксе файла: model.save('GoogleNews-vectors-gensim-normed.bin``. (Обратите внимание, что на самом деле это создает несколько файлов на диске, которые необходимо хранить вместе для модель, подлежащая повторной загрузке.)

Теперь мы сделаем небольшую программу Python, которая служит для загрузки и переноса данных в памяти, и принудительно включите весь массив в память. Мы также хотим, чтобы эта программа зависала до тех пор, пока ее не закончили (сохраняя отображение живым), и будьте осторожны, чтобы не переучитывать уже нормированные векторы. Это требует еще одного трюка, потому что загруженные KeyedVectors на самом деле не знают, что векторы нормированы. (Обычно сохраняются только исходные векторы, а нормированные версии пересчитываются по мере необходимости.)

Примерно должно работать следующее:

from gensim.models import KeyedVectors
from threading import Semaphore
model = KeyedVectors.load('GoogleNews-vectors-gensim-normed.bin', mmap='r')
model.syn0norm = model.syn0  # prevent recalc of normed vectors
model.most_similar('stuff')  # any word will do: just to page all in
Semaphore(0).acquire()  # just hang until process killed

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

Наконец, в вашем коде обработки веб-запроса вы можете просто сделать следующее:

model = KeyedVectors.load('GoogleNews-vectors-gensim-normed.bin', mmap='r')
model.syn0norm = model.syn0  # prevent recalc of normed vectors
# … plus whatever else you wanted to do with the model

Несколько процессов могут совместно использовать файлы с отображением только для чтения. (То есть, как только ОС узнает, что файл X находится в ОЗУ в определенной позиции, каждый другой процесс, который также хочет преобразовать версию X только для чтения, будет направлен на повторное использование этих данных в этой позиции.).

Таким образом, этот web-reqeust load() и любые последующие обращения могут повторно использовать данные, которые предыдущий процесс уже внес в адресное пространство и активную память. Операции, требующие вычисления подобия по каждому вектору, по-прежнему занимают время для доступа к нескольким ГБ ОЗУ и выполняют вычисления/сортировку, но больше не потребуют дополнительного дискового ввода-вывода и избыточной повторной нормализации.

Если система сталкивается с другим давлением в памяти, диапазоны массива могут выпасть из памяти до тех пор, пока следующая прочитанная страница не вернет их обратно. И если машине не хватает ОЗУ для полной загрузки векторов, тогда для каждого сканирования потребуется смешивание пейджинга и выхода, а производительность будет разочаровывающим, неважно, что. (В таком случае: получите больше ОЗУ или работайте с меньшим набором векторов.)

Но если у вас достаточно ОЗУ, это приводит к тому, что исходный/естественный код загрузки и использования непосредственно "работает" довольно быстро, без дополнительного интерфейса веб-сервиса, поскольку машина разделяет файлы, отображаемые функции памяти как служебный интерфейс.

Ответ 2

Мне очень нравится библиотека вложения Vzhong. https://github.com/vzhong/embeddings

Он хранит словарные векторы в SQLite, что означает, что нам не нужно загружать модель, а просто извлекать соответствующие векторы из базы данных: D

Ответ 3

У меня есть эта проблема, когда я использую набор данных новостей google. Проблема в том, что в наборе данных есть больше слов, чем вам когда-либо понадобится. Существует огромное количество опечаток, а что нет. То, что я делаю, это сканировать данные, над которыми я работаю, построить словарь, например, 50k наиболее распространенных слов, получить векторы с Gensim и сохранить словарь. Загрузка этого словаря занимает полсекунды вместо 2 минут.

Если у вас нет конкретного набора данных, вы можете использовать самые распространенные слова 50 или 100k из большого набора данных, например набор данных новостей от WMT, чтобы вы начали.

Другие варианты - всегда поддерживать Gensim. Вы можете создать FIFO для script запуска Gensim. script действует как "сервер", который может читать файл, на который "клиент" пишет, просматривая векторные запросы.

Я думаю, что самым элегантным решением является запуск веб-сервиса, обеспечивающего встраивание слов. В качестве примера рассмотрим word2vec API. После установки, внедрение в "ресторан" так же просто, как:

curl http://127.0.0.1:5000/word2vec/model?word=restaurant

Ответ 4

Метод успеха:

model = Word2Vec.load_word2vec_format('wikipedia-pubmed-and-PMC-w2v.bin',binary=True)
model.init_sims(replace=True)
model.save('bio_word')

позже загрузите модель

Word2Vec.load('bio_word',mmap='r')

для получения дополнительной информации: https://groups.google.com/forum/#!topic/gensim/OvWlxJOAsCo