# Лабораторная работа 2. «Предобработка и классификация данных социальных сетей» [назад](README.md) ## 1. Предварительная обработка данных Лабораторная работа выполняется с использованием языка программирования Python. В случае отсутствия данного программного обеспечения на компьютере необходимо перейти на официальный сайт и скачать последнюю версию Python. Далее установить Python на компьютер следуя указаниям мастера установки. Первый этапом данной лабораторной работы является удаление всех сторонних полей с оставлением лишь полезной информации. Для этого необходимо загрузить скаченный в предыдущей лабораторной работе файл данных из социальной сети VK и извлечь необходимое поле text. ```python all_wall = [] file = open(r" wall_asp.txt") for line in file.readlines(): string = line wall = json.loads(string) json_decode = json.JSONDecoder() parsed_response = json_decode.decode(json.dumps(wall)) nodes = parsed_response.get('items') for node in nodes: all_wall.append(node.get("text")) ``` ## 2. Подсчет слов в собранных текстовых данных Задание включает в себя реализацию алгоритма WordCount. Это одна из самых простых и тривиальных задач машинного обучения. Она заключается в разбиении текста на слова, а также подсчёт их количества. Добавим в имеющийся код следующие строки ```python wordcount={} for wall in all_wall: for word in wall.split(): if word not in wordcount: wordcount[word] = 1 else: wordcount[word] += 1 try: for wc in wordcount: print(wc, wordcount[wc]) except Exception: k=1 ``` В результате перезапуска программы можно увидеть в консоли множество сочетаний вида «значение, ключ». ## 3. Кластеризация текстовых данных Следующей задачей является кластеризация текстовых данных. В области анализа данных широко распространена задача разделения множества объектов на подмножества таким образом, чтобы все объекты каждого подмножества имели больше сходства друг с другом, чем с объектами других подмножеств. Данная задача находит широкое практическое применение. Например, в области медицины алгоритм кластеризации может помочь идентифицировать центры клеток на изображении группы клеток. Используя GPS-данные мобильного устройства, можно определить наиболее посещаемые пользователем места в пределах определенной территории. В рамках текущей задачи создадим новый файл и импортируем необходимые библиотеки. ```python import numpy as np import pandas as pd import nltk import re import os import codecs import matplotlib.pyplot as plt import matplotlib as mpl ``` Необходимо считать данные в массив, а далее приступить к нормализации – приведению слова к начальной форме. Это можно сделать несколькими способами, используя стеммер Портера, стеммер `MyStem` и `PyMorphy2`. Стоит отметить – `MyStem` работает через wrapper, поэтому скорость выполнения операций очень медленная. Предлагается использовать стеммер Портера, хотя можно использовать другие и комбинировать их с друг другом (например, сначала пройтись `PyMorphy2`, а после стеммером Портера). ```python print(str(len(all_wall)) + ' запросов считано') from nltk.stem.snowball import SnowballStemmer stemmer = SnowballStemmer("russian") #nltk.download() def token_and_stem(text): tokens = [word for sent in nltk.sent_tokenize(text) for word in nltk.word_tokenize(sent)] filtered_tokens = [] for token in tokens: if re.search('[а-яА-Я]', token): filtered_tokens.append(token) stems = [stemmer.stem(t) for t in filtered_tokens] return stems def token_only(text): tokens = [word.lower() for sent in nltk.sent_tokenize(text) for word in nltk.word_tokenize(sent)] filtered_tokens = [] for token in tokens: if re.search('[а-яА-Я]', token): filtered_tokens.append(token) return filtered_tokens #Создаем словари (массивы) из полученных основ totalvocab_stem = [] totalvocab_token = [] for i in all_wall: allwords_stemmed = token_and_stem(i) #print(allwords_stemmed) totalvocab_stem.extend(allwords_stemmed) allwords_tokenized = token_only(i) totalvocab_token.extend(allwords_tokenized) ``` Создадим матрицу весов TF-IDF. Будем считать каждый поисковой запрос за документ (так делают при анализе постов в Twitter, где каждый твит – это документ). `tfidf_vectorizer` возьмем из пакета `sklearn`, а стоп-слова выберем из корпуса `ntlk` (изначально придется скачать через `nltk.download()`). Параметры можно подстроить от верхней и нижней границы до количества `n-gram` (в данном случае возьмем 3). ```python stopwords = nltk.corpus.stopwords.words('russian') #можно расширить список стоп-слов stopwords.extend(['что', 'это', 'так', 'вот', 'быть', 'как', 'в', 'к', 'на']) from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer n_featur=200000 tfidf_vectorizer = TfidfVectorizer(max_df=0.8, max_features=10000, stop_words=stopwords, min_df=0.01, use_idf=True, tokenizer=token_and_stem, ngram_range=(1,3) ) tfidf_matrix = tfidf_vectorizer.fit_transform(all_wall) print(tfidf_matrix.shape) ``` Над полученной матрицей начинаем применять различные методы кластеризации. ```python num_clusters = 5 # Метод к-средних - KMeans from sklearn.cluster import KMeans km = KMeans(n_clusters=num_clusters) km.fit(tfidf_matrix) idx = km.fit(tfidf_matrix) clusters = km.labels_.tolist() print(clusters) print (km.labels_) # MiniBatchKMeans from sklearn.cluster import MiniBatchKMeans mbk = MiniBatchKMeans(init='random', n_clusters=num_clusters) #(init='k-means++', ‘random’ or an ndarray) mbk.fit_transform(tfidf_matrix) mbk.fit(tfidf_matrix) miniclusters = mbk.labels_.tolist() print (mbk.labels_) # DBSCAN from sklearn.cluster import DBSCAN db = DBSCAN(eps=0.3, min_samples=10).fit(tfidf_matrix) labels = db.labels_ labels.shape print(labels) # Аггломеративная класстеризация from sklearn.cluster import AgglomerativeClustering agglo1 = AgglomerativeClustering( n_clusters=num_clusters, affinity='euclidean') #affinity можно выбрать любое или попробовать все по очереди: cosine, l1, l2, manhattan answer = agglo1.fit_predict(tfidf_matrix.toarray()) answer.shape ``` Полученные данные можно сгруппировать в `dataframe` и посчитать количество запросов, попавших в каждый кластер. ```python #k-means clusterkm = km.labels_.tolist() #minikmeans clustermbk = mbk.labels_.tolist() #dbscan clusters3 = labels #agglo #clusters4 = answer.tolist() frame = pd.DataFrame(all_wall, index = [clusterkm]) #k-means out = { 'title': all_wall, 'cluster': clusterkm } frame1 = pd.DataFrame(out, index = [clusterkm], columns = ['title', 'cluster']) #mini out = { 'title': all_wall, 'cluster': clustermbk } frame_minik = pd.DataFrame(out, index = [clustermbk], columns = ['title', 'cluster']) frame1['cluster'].value_counts() frame_minik['cluster'].value_counts() ``` Из-за большого количества запросов не совсем удобно смотреть таблицы и хотелось бы больше интерактивности для понимания. Поэтому сделаем графики взаимного расположения запросов друг относительного друга. Сначала необходимо вычислить расстояние между векторами. Для этого можно применить косинусное расстояние. При этом можно использовать вычитание из единицы, чтобы не было отрицательных значений и находиться в пределах от $0$ до $1$. Так как графики будут двух- и трехмерные, а исходная матрица расстояний n-мерная, то придется применять алгоритмы снижения размерности. На выбор есть много алгоритмов (MDS, PCA, t-SNE), но остановим выбор на Incremental PCA. Этот выбор сделан вследствие выигрыша данного алгоритма в ресурсоемкости. Алгоритм Incremental PCA используется в качестве замены метода главных компонентов (PCA), когда набор данных, подлежащий разложению, слишком велик, чтобы разместиться в оперативной памяти. IPCA создает низкоуровневое приближение для входных данных, используя объем памяти, который не зависит от количества входных выборок данных. ```python from sklearn.metrics.pairwise import cosine_similarity dist = 1 - cosine_similarity(tfidf_matrix) dist.shape # Метод главных компонент - PCA from sklearn.decomposition import IncrementalPCA icpa = IncrementalPCA(n_components=2, batch_size=16) icpa.fit(dist) demo2 = icpa.transform(dist) xs, ys = demo2[:, 0], demo2[:, 1] # PCA 3D from sklearn.decomposition import IncrementalPCA icpa = IncrementalPCA(n_components=3, batch_size=16) icpa.fit(dist) ddd = icpa.transform(dist) xs, ys, zs = ddd[:, 0], ddd[:, 1], ddd[:, 2] #Можно сразу примерно посмотреть, что получится в итоге from mpl_toolkits.mplot3d import Axes3D fig = plt.figure() ax = fig.add_subplot(111, projection='3d') ax.scatter(xs, ys, zs) ax.set_xlabel('X') ax.set_ylabel('Y') ax.set_zlabel('Z') plt.show() ```