Ускорьте работу Word2vec от Gensim для получения большого набора данных

Я пытаюсь построить модель Word2vec (или FastText) с помощью Gensim на массивном наборе данных, который состоит из 1000 файлов, каждый из которых содержит ~ 210 000 предложений, и каждое предложение содержит ~ 1000 слов. Обучение проводилось на 36-ядерной машине с ОЗУ 185 ГБ. Я подтвердил, что

gensim.models.word2vec.FAST_VERSION == 1

Во-первых, я пробовал следующее:

files = gensim.models.word2vec.PathLineSentences('path/to/files')
model = gensim.models.word2vec.Word2Vec(files, workers=-1)

Но через 13 часов я решил, что он работает слишком долго, и остановил его.

Затем я попытался создать словарь на основе одного файла и обучить на основе всех 1000 файлов следующим образом:

files = os.listdir['path/to/files']
model = gensim.models.word2vec.Word2Vec(min_count=1, workers=-1)
model.build_vocab(corpus_file=files[0])
for file in files:
    model.train(corpus_file=file, total_words=model.corpus_total_words, epochs=1)

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

Могу посоветовать, как быстро и успешно запустить его. Спасибо!

Обновление №1:

Вот код для проверки обновлений векторов:

file = 'path/to/single/gziped/file'
total_words = 197264406 # number of words in 'file'
total_examples = 209718 # number of records in 'file'
model = gensim.models.word2vec.Word2Vec(iter=5, workers=12)
model.build_vocab(corpus_file=file)
wv_before = model.wv['9995']
model.train(corpus_file=file, total_words=total_words, total_examples=total_examples, epochs=5)
wv_after = model.wv['9995']

поэтому векторы: wv_before и wv_after точно такие же


person Kamaney    schedule 26.01.2020    source источник


Ответы (1)


В gensim Word2Vec нет возможности принимать отрицательное значение для workers. (Откуда вы взяли такую ​​идею?)

Так что вполне возможно, что что-то ломается, возможно, даже не предпринимаются попытки тренировок.

Был ли разумный вывод журнала (на уровне INFO), свидетельствующий о том, что обучение прогрессировало в ваших пробных запусках, либо против PathLineSentences, либо во второй попытке? Показывали ли загруженные потоки такие утилиты, как top? Предполагал ли результат конкретную скорость прогресса и позволил ли вам спрогнозировать вероятное время окончания?

Я бы посоветовал использовать положительное значение workers и наблюдать за INFO-уровневым ведением журнала, чтобы лучше понять, что происходит.

К сожалению, даже с 36 ядрами использование итерируемой последовательности корпуса (например, PathLineSentences) помещает gensim Word2Vec в модель, где вы, вероятно, получите максимальную пропускную способность со значением workers в диапазоне 8–16, используя гораздо меньше, чем все ваши потоки. Но он будет делать правильные вещи с корпусом любого размера, даже если он собирается с помощью повторяющейся последовательности на лету.

Использование режима corpus_file может привести к насыщению гораздо большего числа ядер, но вы все равно должны указать фактическое количество используемых рабочих потоков - в вашем случае workers=36 - и он предназначен для работы со всеми данными из одного файла.

Ваш код, который много раз пытается train() работать с corpus_file, имеет множество проблем, и я не могу придумать способ адаптировать режим corpus_file для работы с множеством ваших файлов. Некоторые из проблем включают:

  • вы создаете словарь только из 1-го файла, что означает, что любые слова, появляющиеся только в других файлах, будут неизвестны и игнорироваться, а любая из частей алгоритма Word2Vec, зависящих от частоты слов, может работать с нерепрезентативными

  • модель строит свою оценку ожидаемого размера корпуса (например: model.corpus_total_words) на build_vocab() шаге, поэтому каждый train() будет вести себя так, как если бы этот размер был общим размером корпуса, в своей отчетности о прогрессе и управлении внутренним alpha снижением скорости обучения . Таким образом, эти журналы будут неправильными, alpha будет неправильно управляться в новом разложении каждый train(), что приведет к бессмысленной мозаике вверх-вниз alpha по всем файлам.

  • вы выполняете итерацию по содержимому каждого файла только один раз, что нетипично. (Это может быть разумным в гигантском корпусе из 210 миллиардов слов, если текст каждого файла одинаково и случайным образом представляет домен. В этом случае полный корпус один раз может быть таким же хорошим, как итерация по корпусу, который составляет 1/5 размер в 5 раз. Но было бы проблемой, если бы некоторые слова / шаблоны использования все сгруппированы в определенных файлах - лучшая тренировка чередует контрастные примеры на протяжении каждой эпохи и всех эпох.)

  • min_count=1 почти всегда неразумно с этим алгоритмом, особенно в больших корпусах типичных частот слов естественного языка. Редкие слова, особенно те, которые встречаются только один или несколько раз, делают модель гигантской, но эти слова не получат хороших слов-векторов, и их содержание действует подобно шуму, мешающему улучшению других, более распространенных слов.

Я рекомендую:

Попробуйте режим итерируемой последовательности корпуса с ведением журнала и разумным workers значением, чтобы хотя бы получить точное представление о том, сколько времени это может занять. (Самым длинным шагом будет начальное сканирование словарного запаса, которое, по сути, является однопоточным и должно посещать все данные. Но вы можете .save() модель после этого шага, а затем повторно .load() ее, повозиться с настройками и попробовать разные train() подходы без повторения медленного изучения словарного запаса.)

Попробуйте агрессивно более высокие значения min_count (отказавшись от более редких слов для меньшей модели и более быстрого обучения). Возможно, также попробуйте агрессивно меньшие значения sample (например, 1e-05, 1e-06 и т. Д.), Чтобы отбросить большую часть наиболее часто используемых слов, для более быстрого обучения, которое также часто улучшает общее качество вектора слова (за счет относительно больших усилий, затрачиваемых на меньшее количество слов). частые слова).

Если он по-прежнему слишком медленный, подумайте, можно ли использовать меньшую подвыборку вашего корпуса.

Рассмотрите метод corpus_file, если вы можете скопировать большую часть или все свои данные в один требуемый файл.

person gojomo    schedule 27.01.2020
comment
Спасибо за подробный ответ. Я проверил ваши рекомендации на одном файле, прежде чем применять его к файлу uber: установив workers=8, epochs=5, min_count=5 и уровень журнала на INFO. Журнал выполнения показал, что происходит обучение, а top также показал занятые потоки. Векторы слов остаются неизменными до и после обучения ... - person Kamaney; 27.01.2020
comment
Как указано выше, если вы используете один файл (и corpus_file), вы также можете использовать workers=36. Как вы проверяете, что слова-векторы остаются неизменными? (Остерегайтесь использования чего-либо вроде most_similar() проверки, поскольку это может привести к использованию кэшированного набора векторов, нормализованных по единичной длине, который не обязательно изменится после дополнительного обучения.) - person gojomo; 27.01.2020
comment
Я проверяю образец слов сразу после вызова создания экземпляра модели и запуска model.build_vocab, а затем снова после запуска model.train. - person Kamaney; 28.01.2020
comment
Если журнал показывает, что обучение занимает много времени, значит, слова должны меняться - так что, возможно, что-то не так с тем, как вы проверяете образец слов-векторов. Если вы покажете (в своем ответе) код, который вы используете для проверки, проблема может стать ясной. - person gojomo; 28.01.2020
comment
Простой доступ, такой как wv_before = model.wv['9995'], к базовому массиву numpy, как правило, создает новое «представление» только для этой строки, а не для независимой копии. Итак, в конце, да, wv_before == wv.after будет True - но это потому, что wv_before не отставал от изменений. Если вы заметили свой образец элемента до / после, вы должны увидеть изменение - или вы можете обеспечить отдельную копию с помощью wv_before = model.wv['9995'].copy(). - person gojomo; 28.01.2020
comment
Использование отрицательного значения воркеров - очень распространенная практика для многих пакетов, что означает, что программа будет использовать максимальное количество доступных воркеров. - person hafiz031; 15.03.2021