Учим компьютер писать как Толстой, том I
- воскресенье, 3 декабря 2017 г. в 03:11:38
— Eh bien, mon prince. Gênes et Lucques ne sont plus que des apanages, des поместья, de la famille Buonaparte. Non, je vous préviens que si vous ne me dites pas que nous avons la guerre, si vous vous permettez encore de pallier toutes les infamies, toutes les atrocités de cet Antichrist (ma parole, j'y crois) — je ne vous connais plus, vous n'êtes plus mon ami, vous n'êtes plus мой верный раб, comme vous dites 1. Ну, здравствуйте, здравствуйте. Je vois que je vous fais peur 2, садитесь и рассказывайте.
Недавно на хабре наткнулся на эту статью https://habrahabr.ru/post/342738/. И захотелось написать про word embeddings, python, gensim и word2vec. В этой части я постараюсь рассказать о обучении базовой модели w2v.
Итак, приступаем.
utf-8
.Первым делом надо скачать данные для nltk.
import nltk
nltk.dwonload()
В открывшемся окошке выбираем все, и идем пить кофе. Это займет около получаса.
По умолчанию в библиотеке русского языка нет. Но умельцы все сделали за нас. Качаем https://github.com/mhq/train_punkt и извлекаем все в папкуC:\Users\<username>\AppData\Roaming\nltk_data\tokenizers\punkt
иC:\Users\<username>\AppData\Roaming\nltk_data\tokenizers\punkt\PY3
.
Nltk мы будем использовать для разбивки текста на предложения, а предложений на слова. К моему удивлению, все это работает довольно быстро. Ну хватит настроек, пару уже написать хоть строчку нормального кода.
Создаем папку где будут скрипты и данные. Создаем enviroment.
conda create -n tolstoy-like
Активируем.
activate tolstoy
Туда же кидаем текст. Назовем файл anna.txt
Для обладателей PyCharm можно просто создать проект и в качестве интерпретатора выбрать анаконду, не создавая окружения.
Создаем скрипт train-I.py
.
Подключаем зависимости.
# -*- coding: utf-8 -*-
# imports
import gensim
import string
from nltk.tokenize import sent_tokenize
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
Считываем текст.
# load text
text = open('./anna.txt', 'r', encoding='utf-8').read()
Теперь очередь токенизатора русских предложений.
def tokenize_ru(file_text):
# firstly let's apply nltk tokenization
tokens = word_tokenize(file_text)
# let's delete punctuation symbols
tokens = [i for i in tokens if (i not in string.punctuation)]
# deleting stop_words
stop_words = stopwords.words('russian')
stop_words.extend(['что', 'это', 'так', 'вот', 'быть', 'как', 'в', '—', '–', 'к', 'на', '...'])
tokens = [i for i in tokens if (i not in stop_words)]
# cleaning words
tokens = [i.replace("«", "").replace("»", "") for i in tokens]
return tokens
На этом остановимся по подробнее. В первой строчке мы разбиваем предложение (строку) на слова (массив строк). Затем удаляем пунктуацию, которую nltk, почему-то выносит как отдельное слово. Теперь стоп-слова. Это такие слова, от которых нашей модели пользе не будет, они лишь будут сбивать ее с основного текста. К ним относят междометия, союзы и некоторые местоимения, а также любимые некоторыми слова-паразиты. Затем убираем кавычки которых в этом романе через край.
Теперь разбиваем текст на предложения, а предложения на массив слов.
sentences = [tokenize_ru(sent) for sent in sent_tokenize(text, 'russian')]
Для интереса выведем количество предложений и парочку из них.
print(len(sentences)) # 20024
print(sentences[200:209]) # [['Она', 'чувствовала', 'боится', 'боится', 'предстоящего', 'свидания'],...]
Теперь начинаем обучать модель. Не бойтесь это не займет и получасу — 20024 предложения для gensim просто расплюнуть.
# train model
model = gensim.models.Word2Vec(sentences, size=150, window=5, min_count=5, workers=4)
# save model
model.save('./w2v.model')
print('saved')
Сохраняем файл. Чтобы запустить, тем кто работает в PyCharm или Spyder достаточно нажать run. Кто пишет вручную с блокнота или другого редактора придется запустить Anaconda Promt (для этого достаточно вбить это в поиск в меню), перейти в директорию со скриптом и запустить командой
python train-I.py
Готово. Теперь вы можете с гордостью сказать, что обучали word2vec.
Как бы мы не старались, но Анны Каренины для обучении модели мало. Поэтому воспользуемся вторым произведением автора — Война и Мир.
Скачать можно отсюда, также в формате TXT. Перед использованием придется соединить два файла в один. Кидаем в директорию из первой главы, называем war.txt
. Одной из прелестью использования gensim является то, что любую загруженную модель можно доучить с новыми данными. Этим мы и займемся.
Создаем скрипт train-II.py
Думаю, что эта часть не нуждается в объяснениях, так как в ней нет ничего нового.
# -*- coding: utf-8 -*-
# imports
import gensim
import string
from nltk.tokenize import sent_tokenize
from nltk.corpus import stopwords
from nltk.tokenize import word_tokenize
# load text
text = open('./war.txt', 'r', encoding='utf-8').read()
def tokenize_ru(file_text):
# firstly let's apply nltk tokenization
tokens = word_tokenize(file_text)
# let's delete punctuation symbols
tokens = [i for i in tokens if (i not in string.punctuation)]
# deleting stop_words
stop_words = stopwords.words('russian')
stop_words.extend(['что', 'это', 'так', 'вот', 'быть', 'как', 'в', '—', '–', 'к', 'на', '...'])
tokens = [i for i in tokens if (i not in stop_words)]
# cleaning words
tokens = [i.replace("«", "").replace("»", "") for i in tokens]
return tokens
# tokenize sentences
sentences = [tokenize_ru(sent) for sent in sent_tokenize(text, 'russian')]
print(len(sentences)) # 30938
print(sentences[200:209]) # [['Он', 'нагнув', 'голову', 'расставив', 'большие', 'ноги', 'стал', 'доказывать', 'Анне', 'Павловне', 'почему', 'полагал', 'план', 'аббата', 'химера'],...]
Затем загружаем нашу модель, и скармливаем ей новые данные.
# train model part II
model = gensim.models.Word2Vec.load('./w2v.model')
model.train(sentences, total_examples=model.corpus_count, epochs=model.iter)
Здесь я немного остановлюсь. total_examples
устанавливает количество слов, в нашем случае это весь словарь модели (model.corpus_count
), включая новые. А `epochs
количество итераций. Честное слово, сам не знаю, что значит model.iter
взял из документации. Кто знает, напишите, пожалуйста, в комментариях — исправлю.
# save model
model.save('./w2v-II.model')
print('saved')
Снова все готово. Не забудьте запустить.
Их нет. И пока не будет. Модель еще не совсем совершенна, откровенно говоря, она ужасна. В следующей статье я обязательно расскажу как это исправить. Но вот вам на последок:
# -*- coding: utf-8 -*-
# imports
import gensim
model = gensim.models.Word2Vec.load('./w2v-II.model')
print(model.most_similar(positive=['княжна', 'сестра'], negative=['князь'], topn=1))
Ссылки и используемая литература.
Надеюсь моя статья вам хоть немного понравилась.