186 KiB
186 KiB
Пример предобработки и векторизации текстовых данных с помощью word2vec¶
Инициализация spacy и загрузка модели для русского языка¶
In [2]:
import spacy
sp = spacy.load("ru_core_news_lg")
Загрузка содержимого текстовых документов¶
In [3]:
import pandas as pd
from docx import Document
import os
def read_docx(file_path):
doc = Document(file_path)
full_text = []
for paragraph in doc.paragraphs:
full_text.append(paragraph.text)
return "\n".join(full_text)
def load_docs(dataset_path):
df = pd.DataFrame(columns=["doc", "text"])
for file_path in os.listdir(dataset_path):
if file_path.startswith("~$"):
continue
text = read_docx(dataset_path + file_path)
df.loc[len(df.index)] = [file_path, text]
return df
df = load_docs("data/text/")
df["type"] = df.apply(lambda row: 0 if str(row["doc"]).startswith("tz_") else 1, axis=1)
df.info()
df.sort_values(by=["doc"], inplace=True)
display(df.head(), df.tail())
Предобработка текстовых данных¶
- приведение к нижнему регистру;
- удаление стоп-слов;
- удаление знаков препинания и спец-символов;
- лемматизация;
- формирование би-грамм с помощью Phraser, Phrases.
У Phrases можно изменять параметры: min_count и threshold (минимальное количество вхождений и порог оценки устойчивости) для повышения качества полученных би-грамм.
In [4]:
from gensim.models.phrases import Phraser, Phrases
def prep_text(text):
doc = sp(text)
lower_sents = []
for sent in doc.sents:
lower_sents.append([word.lemma_.lower() for word in sent if not word.is_punct and not word.is_stop and not word.is_space])
lower_bigram = Phraser(Phrases(lower_sents))
clean_sents = []
for sent in lower_sents:
clean_sents.append(lower_bigram[sent])
return clean_sents
df["prep_text"] = df.apply(lambda row: prep_text(row["text"]), axis=1)
df
Out[4]:
Векторизация текстовых данных с помощью word2vec¶
- функция explode объединяет все массивы столбца prep_text в один;
- будет получено 64-мерное пространство (векторы длиной 64 элемента);
- используется метод skip-gram (sg=1). Данный метод предпочтительно использовать на небольшом корпусе, а CBOW на большом (sg=0);
- размер окна 10 (по 10 слов слева и справа от текущего для вычисления контекста);
- min_count - минимальное количество вхождений слова в корпус для включения в n-мерное пространство;
- workers - количество потоков для вычисления;
- seed - задание начального состояния для повторяемости (random_state).
In [5]:
from gensim.models.word2vec import Word2Vec
word2vec = Word2Vec(
sentences=df["prep_text"].explode().tolist(),
vector_size=64,
sg=1,
window=10,
epochs=5,
min_count=10,
workers=4,
seed=9,
)
Вывод уникальных идентификаторов для каждого слова¶
Слова в пространстве хранятся в виде числового идентификатора для сокращения потребления памяти
In [6]:
word2vec.wv.key_to_index
Out[6]:
Вывод трех наиболее похожих по смыслу слова для слова 1С¶
In [7]:
word2vec.wv.most_similar("1с", topn=3)
Out[7]:
Координаты слова 1С в 64-мерном пространстве¶
In [8]:
word2vec.wv["1с"]
Out[8]:
Поиск лишнего по смыслу слова (самое отдаленное по смыслу слово)¶
In [9]:
word2vec.wv.doesnt_match("java php 1с oracle заказчик".split())
Out[9]:
Смысловое расстояние между словами (расстояние в пространстве)¶
In [10]:
word2vec.wv.similarity("java", "javascript")
Out[10]:
In [11]:
word2vec.wv.similarity("java", "1с")
Out[11]:
Векторная арифметика: 1c - бухгалтерия + машина =¶
In [12]:
word2vec.wv.most_similar(positive=["1с", "машина"], negative=["бухгалтерия"])
Out[12]:
Векторная арифметика: субд - база + язык =¶
In [13]:
word2vec.wv.most_similar(positive=["субд", "язык"], negative=["база"])
Out[13]:
Векторная арифметика: java - машина + бухгалтерия =¶
In [14]:
word2vec.wv.most_similar(positive=["java", "бухгалтерия"], negative=["машина"])
Out[14]:
Векторная арифметика: 866 - мгц + мбайт =¶
In [15]:
word2vec.wv.most_similar(positive=["866", "мбайт"], negative=["мгц"])
Out[15]:
Векторная арифметика: 866 - процессор + память =¶
In [18]:
word2vec.wv.most_similar(positive=["866", "память"], negative=["процессор"])
Out[18]:
Понижение размерности векторного пространства с 64 до 2 измерений¶
In [19]:
from sklearn.manifold import TSNE
tsne = TSNE(n_components=2, max_iter=1000, random_state=9)
coords_df = pd.DataFrame(
tsne.fit_transform(word2vec.wv[word2vec.wv.key_to_index]), columns=["x", "y"]
)
coords_df["token"] = word2vec.wv.key_to_index.keys()
coords_df
Out[19]:
Визуализация векторного пространства¶
Диаграмма интерактивная и позволяет изменять масштаб и перемещаться
In [20]:
from bokeh.io import output_notebook
from bokeh.plotting import show, figure
output_notebook()
p = figure(width=800, height=800)
p.text(x=coords_df.x, y=coords_df.y, text=coords_df.token) # type: ignore
show(p)