#!/usr/bin/env python # coding: utf-8 #
# #

Методы машинного обучения

#

Введение в NLP

# In[1]: get_ipython().run_line_magic('matplotlib', 'inline') # In[2]: import pandas as pd import numpy as np import matplotlib.pyplot as plt plt.style.use('ggplot') plt.rcParams['figure.figsize'] = (12,8) # # Работа с текстом # Рассмотрим коллекцию новостных сообщений за первую половину 2017 года. Про каждое новостное сообщение известны: # * его заголовок и текст # * дата его публикации # * событие, о котором это новостное сообщение написано # * его рубрика # In[3]: df = pd.read_csv('./data/news.csv', encoding='utf8') df.head() # In[9]: print(df.text[0]) # In[4]: df.shape # In[7]: df.loc[:, 'class'].value_counts() # In[5]: df.event.value_counts() # ## Токенизация # # Используем регулярные выражения, чтобы разбить тексты на слова # In[10]: import re regex = re.compile(u"[А-Яа-я]+") def words_only(text, regex=regex): return " ".join(regex.findall(text)) df.text = df.text.str.lower() df.loc[:, 'text'] = df.text.apply(words_only) # Результат: # In[11]: print(df.text.iloc[0]) # ### Самые частые слова # In[12]: from nltk import FreqDist n_types = [] n_tokens = [] tokens = [] fd = FreqDist() for index, row in df.iterrows(): tokens = row['text'].split() fd.update(tokens) n_types.append(len(fd)) n_tokens.append(sum(fd.values())) for i in fd.most_common(10): print(u'{}: {}'.format(i[0], i[1])) # ## Обработка текстов # # # ### Удаление стоп-слов # In[13]: import nltk # In[15]: # nltk.download() # In[19]: from nltk.corpus import stopwords mystopwords = stopwords.words('russian') + ['это', 'наш' , 'тыс', 'млн', 'млрд', u'также', 'т', 'д', '-', '-'] print(mystopwords) def remove_stopwords(text, mystopwords = mystopwords): try: return u" ".join([token for token in text.split() if not token in mystopwords]) except: return u"" df.text = df.text.apply(remove_stopwords) # In[21]: df.text[0] # ### Лемматизация # In[23]: get_ipython().system('pip install pymystem3') # In[27]: get_ipython().system('pip install pymorphy2') get_ipython().system('pip install -U pymorphy2-dicts-ru') get_ipython().system('pip install -U pymorphy2-dicts-uk') # In[28]: import pymorphy2 # In[32]: m = pymorphy2.MorphAnalyzer() m.normal_forms('митинги') def lemmatize(text, mystem=m): try: return " ".join(m.normal_forms(word)[0] for word in text.split(' ')) except: return " " df.loc[:, 'text_morphy'] = df.text.apply(lemmatize) # In[33]: df.loc[0, 'text_morphy'] # In[24]: get_ipython().run_cell_magic('time', '', 'from pymystem3 import Mystem\n\nm = Mystem()\ndef lemmatize(text, mystem=m):\n try:\n return "".join(m.lemmatize(text)).strip() \n except:\n return " "\n\ndf.text = df.text.apply(lemmatize)\n') # In[26]: df.text[0] # ### Удаление стоп-лемм # In[34]: mystoplemmas = [u'который', u'прошлый', u'сей', u'свой', u'наш', u'мочь'] def remove_stoplemmas(text, mystoplemmas = mystoplemmas): try: return " ".join([token for token in text.split() if not token in mystoplemmas]) except: return "" df.text = df.text.apply(remove_stoplemmas) # In[35]: df.text.head() # Самые частые леммы: # In[36]: lemmata = [] for index, row in df.iterrows(): lemmata += row['text'].split() fd = FreqDist(lemmata) for i in fd.most_common(10): print(u'{}: {}'.format(i[0], i[1])) # ## Извлечение ключевых слов # ## N-граммы (n-grams) # * $w_1$, $w_2$ - слова # * $p(w_1)$, $p(w_2)$ - частоты слов # * $p(w_1, w_2)$ - частота биграммы # # # * $PMI(w_1, w_2) = \log\frac{p(w_1, w_2)}{p(w_1)p(w_2)}$ # * $\text{T-score}(w_1, w_2) = \frac{p(w_1,w_2) - p(w_1)p(w_2)}{p(w_1, w_2)/N}$ # * ... # Переезжаем из DataFrame в списки: # In[37]: tokens_by_topic = [] for event in df.event.unique(): tokens = [] sample = df[df.event==event] for i in range(len(sample)): tokens += sample.text.iloc[i].split() tokens_by_topic.append(tokens) # Выберем событие, из текстов про которое будем извлекать ключевые слова: # In[40]: event_id = 3 # Извлекаем биграммы по разным мерам связности: # In[41]: get_ipython().run_cell_magic('time', '', "import nltk\nfrom nltk.collocations import *\nN_best = 100 # число извлекаемых биграм\n\nbigram_measures = nltk.collocations.BigramAssocMeasures() # класс для мер ассоциации биграм\nfinder = BigramCollocationFinder.from_words(tokens_by_topic[event_id]) # класс для хранения и извлечения биграм\nfinder.apply_freq_filter(3) # избавимся от биграм, которые встречаются реже трех раз\nraw_freq_ranking = [' '.join(i) for i in finder.nbest(bigram_measures.raw_freq, N_best)] # выбираем топ-10 биграм по частоте \ntscore_ranking = [' '.join(i) for i in finder.nbest(bigram_measures.student_t, N_best)] # выбираем топ-10 биграм по каждой мере \npmi_ranking = [' '.join(i) for i in finder.nbest(bigram_measures.pmi, N_best)]\nchi2_ranking = [' '.join(i) for i in finder.nbest(bigram_measures.chi_sq, N_best)]\n") # Результаты: # In[42]: rankings = pd.DataFrame({ 'chi2': chi2_ranking, 't-score' : tscore_ranking, 'pmi': pmi_ranking, 'raw_freq':raw_freq_ranking}) rankings = rankings[['raw_freq', 'pmi', 't-score', 'chi2', ]] rankings.head(10) # In[45]: 'дмитрий_медведев' # ## Вычисление сходства # С помощью `TfidfVectorizer` и `pairwise_distances` расчитайте косинусное расстояние между всеми парами документов к корпусе # # Запишите результат в переменную `S` # In[46]: import seaborn as sns from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics import pairwise_distances # In[47]: vect = TfidfVectorizer() # In[48]: texts = df.text.values X = vect.fit_transform(texts) # In[50]: sims = pairwise_distances(X, metric='cosine') # In[52]: sims.shape # In[53]: plt.figure(figsize = (10,10)) sns.heatmap(data=sims, cmap = 'Spectral').set(xticklabels=[],yticklabels=[]) # ### FYI тоже самое в Gensim # In[ ]: get_ipython().system('pip install gensim') # In[54]: from gensim.corpora import * texts = [df.text.iloc[i].split() for i in range(len(df))] dictionary = Dictionary(texts) corpus = [dictionary.doc2bow(text) for text in texts] # In[55]: from gensim.models import * tfidf = TfidfModel(corpus) corpus_tfidf = tfidf[corpus] # In[56]: from gensim import similarities index = similarities.MatrixSimilarity(corpus_tfidf) sims = index[corpus_tfidf] # In[57]: plt.figure(figsize = (10,10)) sns.heatmap(data=sims, cmap = 'Spectral').set(xticklabels=[],yticklabels=[]) # # LSA # C помощью `TruncatedSVD` выполните LSА преобразование документов # In[58]: from sklearn.decomposition import TruncatedSVD # In[59]: lsa = TruncatedSVD(n_components=10, random_state=123) Z = lsa.fit_transform(X) # In[61]: X.shape # In[60]: Z.shape # In[63]: Z[:5] # In[65]: from sklearn.preprocessing import LabelEncoder # In[66]: enc = LabelEncoder() # In[68]: df.loc[:, 'class'].value_counts() # In[69]: label = enc.fit_transform(df.loc[:, 'class']) # In[70]: plt.scatter(Z[:,0], Z[:,1], c=label) # Так же посчитайте косинусное расстояние и визуализируйте его # In[71]: sims = pairwise_distances(Z, metric='cosine') # In[72]: sims.shape # In[73]: plt.figure(figsize = (10,10)) sns.heatmap(data=sims, cmap = 'Spectral').set(xticklabels=[],yticklabels=[]) # Составьте некоторый "поисковый запрос" и найдите наиболее подходящие документы из корпуса с помощью LSI # In[119]: q = u'премьер министр великобритании' q_norm = lemmatize(q) # In[120]: q_norm # In[121]: q_bow = vect.transform([q_norm]) # In[122]: q_lsa = lsa.transform(q_bow) # In[123]: sims = pairwise_distances(Z, q_lsa, metric='cosine') # In[124]: idx = np.argsort(sims, axis=0)[:,0] # In[125]: df.iloc[idx, 0] # ### FYI тоже самое в Gensim %%time lsi = lsimodel.LsiModel(corpus=corpus_tfidf, id2word=dictionary, num_topics=50)topics = lsi.show_topics()for t in range(5): print('=====') print(topics[t][1])corpus_lsi = lsi[corpus] index = similarities.MatrixSimilarity(lsi[corpus]) sims = index[corpus_lsi] sims = (sims + 1)/2.quert = u"поисковый запрос" vec_bow = dictionary.doc2bow(doc.lower().split()) vec_lsi = lsi[vec_bow] # convert the query to LSI spacesims = index[vec_lsi] print(list(enumerate(sims))) # print (document_number, document_similarity) 2-tuples