Как ускорить время загрузки модели 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. Что, безусловно, ускорит дело, но интуиция подсказывает мне, что это не очень хорошая идея.

3 ответов


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

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

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

обратите внимание, что после загрузки таких векторов в исходном word2vec.формат c-origined, вы можете повторно сохранить их, используя родной save(). Если вы сохраните их несжатыми, а резервный массив достаточно велик (и набор GoogleNews определенно достаточно велик), то резервный массив сбрасывается в отдельный файл в необработанном двоичном формате. Этот файл позже может быть сопоставлен с памяти с диска, используя родной .

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

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

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

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

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

теперь мы сделаем короткую программу Python, которая служит для загрузки векторов карты памяти,и force полный массив в память. Мы также хотим, чтобы эта программа зависала до внешнего завершения (сохраняя карту живой),и будьте осторожны, чтобы не пересчитать уже нормированные векторы. Это требует другого трюка, потому что загруженные 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(), и любые последующие обращения, могут все повторно использовать данные, которые предыдущий процесс уже внес в адресное пространство и активную память. Операции, требующие подобия-calcs против каждого вектора, по-прежнему потребуют времени для доступа к нескольким ГБ ОЗУ и выполнения вычислений / сортировки, но больше не потребуют дополнительного диска-IO и избыточного повторная нормализация.

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

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


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

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

другие опции-всегда поддерживать работу Gensim. Ты можешь!--8-->создать FIFO для сценария под управлением Gensim. Скрипт действует как "сервер", который может читать файл, в который пишет" клиент", наблюдая за векторными запросами.

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

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

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

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