Изучение мер сходства для задач НЛП
Обработка естественного языка (NLP) — захватывающая область, в которой задействованы обучающие машины для понимания человеческого языка. Многие задачи в области НЛП связаны с вычислением сходства или расстояния между двумя фрагментами текста. Например, мы можем захотеть сравнить сходство между двумя речами или двумя обзорами продуктов, чтобы определить, обсуждают ли они одну и ту же тему или мнение. В области НЛП существует несколько мер подобия, и в этой статье мы рассмотрим некоторые из наиболее часто используемых мер, включая косинусное сходство, сходство Жаккара, евклидово расстояние, манхэттенское расстояние и коэффициент корреляции Пирсона.
Чтобы продемонстрировать, как эти меры сходства работают на практике, мы будем использовать набор данных выступлений Сената США. Мы извлечем текст из каждой речи и предварительно обработаем текст, удалив стоп-слова и символы, не являющиеся словами, и выполнив лемматизацию. После этого мы изучим частоту слов в речах и векторизируем речи, используя подход векторизации TF-IDF (Term Frequency-Inverse Document Frequency). Затем мы рассчитаем меры сходства и проанализируем результаты.
Получение данных
Первым шагом в нашем анализе является извлечение текста из речей Сената. У нас есть папка, содержащая несколько XML-файлов, содержащих информацию о разных выступлениях. Мы можем извлечь текстовое содержимое из каждого файла, используя библиотеку Python Beautiful Soup. В приведенном ниже коде показано, как мы можем этого добиться.
import os import pandas as pd import chardet from bs4 import BeautifulSoup # Path to the folder containing the files folder_path = "/Users/mukhamejan/Desktop/school/Winter_23/ECBS6253/ML-for-NLP-main/Inputs/105-extracted-date" # Create an empty dictionary to store the text content with senator names as keys senator_text = {} # Loop through each file in the folder for filename in os.listdir(folder_path): if filename.endswith(".txt") and filename.startswith("105-"): # Extract senator name from the filename senator_name = filename.split("-")[1] # Detect encoding and read the file content with open(os.path.join(folder_path, filename), "rb") as f: result = chardet.detect(f.read()) file_encoding = result["encoding"] with open(os.path.join(folder_path, filename), "r", encoding=file_encoding) as f: xml_content = f.read() # Remove any extra content after the XML document xml_content = xml_content[:xml_content.rfind("</DOC>") + len("</DOC>")] # Parse the file content using BeautifulSoup soup = BeautifulSoup(xml_content, "xml") # Extract the text content from the <TEXT> element text = soup.find("TEXT").text.strip() # Store the text content with senator name as the key in the dictionary senator_text[senator_name] = text # Create a Pandas DataFrame with a column for each senator's text content df = pd.DataFrame.from_dict(senator_text, orient="index", columns=["Text"])
Словарь senator_text
хранит текст речей с именами сенаторов в качестве ключей, а df
DataFrame содержит текст речей с текстом каждого сенатора в виде столбца.
Предварительная обработка текста
Предварительная обработка текста — важный шаг в НЛП, помогающий очистить текст и сделать его пригодным для анализа. В этом коде определена функция text_preprocesser
для предварительной обработки речи. Функция выполняет следующие операции:
- Заменяет все несловесные символы в тексте пробелом с помощью функции
re.sub
из модуляre
. - Разбивает текст на отдельные слова с помощью функции
word_tokenize
из библиотекиnltk
. - Преобразует все токены в нижний регистр с помощью метода
lower
. - Отфильтровывает любые стоп-слова (распространенные слова, такие как «a», «the» и т. д.), используя список стоп-слов из корпуса стоп-слов библиотеки
nltk
. - Отфильтровывает любые токены, длина которых меньше 3.
- Соединяет оставшиеся токены в одну строку, используя метод
join
. - Возвращает предварительно обработанный текст.
def text_preprocesser(text): text= re.sub(r'\W',' ', text) tokens = word_tokenize(text.lower()) tokens = [token for token in tokens if token not in stopwords.words('english')] tokens = [word for word in tokens if len(word)>=3] preprocessed_text = ' '.join(tokens) return preprocessed_text
Векторизация TF-IDF
Термин частотно-обратная частота документа (TF-IDF) представляет собой числовую статистику, которая отражает важность слова в документе. Значение TF-IDF увеличивается с частотой слова в документе и уменьшается с частотой слова в корпусе. Код реализует векторизацию TF-IDF с использованием класса TfidfVectorizer
из модуля sklearn.feature_extraction.text
. Векторизатор инициализируется функцией text_preprocessor
, определенной ранее. Метод fit_transform
векторизатора используется для преобразования предварительно обработанных текстовых данных в матрицу значений TF-IDF. Затем создается Pandas DataFrame с токенами и их значениями TF-IDF с каждым документом в виде столбцов.
# TFIDF Vectorize using the predefined preprocesser. min_df=2 here is not needed, but it does not change anything # initialise the vectorizer tfidf_vectorizer = TfidfVectorizer(preprocessor = text_preprocesser, min_df =2 ) # fit the vectorizer tfidf = tfidf_vectorizer.fit_transform([roth, murray]) # build a data frame with the tokens and their tfidf value with each document as columns tfidf_rm = pd.DataFrame(tfidf.toarray().transpose(), index=tfidf_vectorizer.get_feature_names()) tfidf_rm.columns = ['roth', 'murray'] # print to see tfidf_rm
Чтобы лучше понять тексты двух речей, я построил частотное распределение первых 10 слов в объединенных выступлениях Рота и Мюррея, используя приведенный ниже код Python:
# Apply text preprocesser to a combined string tokens = text_preprocesser(roth + " " + murray).split() # Count the tokens from collections import Counter dict_counts = Counter(tokens) dict_counts # Plot the frequency of top 10 words labels, values = zip(*dict_counts.items()) import numpy as np import matplotlib.pyplot as plt %matplotlib inline # sort your values in descending order indSort = np.argsort(values)[::-1] # rearrange your data and show top 10 words labels = np.array(labels)[indSort][0:10] values = np.array(values)[indSort][0:10] indexes = np.arange(len(labels)) plt.bar(indexes, values, color="red") # add labels plt.xticks(indexes, labels, rotation=45)
Этот код сначала предварительно обрабатывает речи, очищая их, удаляя стоп-слова и сводя слова к их корневой форме. Затем он объединяет речи Рота и Мюррея и подсчитывает частоту каждого слова. Затем на гистограмму наносятся первые 10 слов с наибольшей частотой.
Полученный график показывает частотное распределение первых 10 слов в объединенных речах Рота и Мюррея. Ось x представляет первые 10 слов, а ось y показывает частоту этих слов.
Из графика видно, что наиболее часто встречаются слова «программа», «миллион» и «федеральный», за которыми следуют «год», «налог», «бюджет», «расходы», «здравоохранение», «образование». », и «конгресс». Эти слова обычно используются в политических выступлениях, особенно при обсуждении вопросов политики и бюджета.
График частотного распределения может помочь нам получить представление о темах, обсуждаемых в выступлениях Рота и Мюррея. На нем показаны наиболее распространенные слова, используемые двумя сенаторами, что дает нам представление о направленности их выступлений. Эту информацию можно использовать в сочетании с мерами подобия, чтобы понять сходства и различия между речами Рота и Мюррея.
Меры подобия
- Косинусное сходство:
Косинусное сходство измеряет сходство между двумя ненулевыми векторами пространства внутреннего произведения. В контексте обработки естественного языка косинусное сходство обычно используется для измерения сходства между двумя документами, представленными в виде векторов частот слов или оценок TF-IDF. Мера вычисляет косинус угла между двумя векторами и возвращает значение от 0 до 1, где 1 представляет собой наибольшее сходство между двумя векторами.
Пример использования в коде:
from sklearn.metrics.pairwise import cosine_similarity # Get the tfidf values and calculate the cosine similarity value similarity_rm = cosine_similarity(tfidf_rm['roth'].values.reshape(1, -1), tfidf_rm['murray'].values.reshape(1, -1))[0][0] print("Cosine similarity between Senators Roth and Murray's speeches (TFIDF): {:.2f}%".format(similarity_rm * 100))
2. Сходство Жаккара:
Сходство Жаккара измеряет сходство между двумя наборами элементов. Он рассчитывается как отношение пересечения двух множеств к их объединению. В контексте обработки естественного языка сходство Жаккара можно использовать для сравнения сходства словарного запаса между двумя документами.
Пример использования в коде:
from sklearn.metrics import jaccard_score # Preprocess the documents and create a set of each # sets contain only the unique elements in the preprocessed text set1 = set(text_preprocesser(roth).split()) set2 = set(text_preprocesser(murray).split()) # Convert the sets to lists list1 = list(set1) list2 = list(set2) # Define Jaccard Similarity function for two sets # The measure is equal to the count of shared tokens over count of total tokens def jaccard_set(list1, list2): intersection = len(list(set(list1).intersection(list2))) union = (len(list1) + len(list2)) - intersection return float(intersection) / union # Apply function similarity = jaccard_set(list1, list2) # Print the similarity as a percentage print("Jaccard similarity: {:.2f}%".format(similarity * 100))
3. Евклидово расстояние:
Евклидово расстояние измеряет расстояние между двумя точками в пространстве. В контексте обработки естественного языка евклидово расстояние можно использовать для сравнения расстояния между двумя векторами, представляющими два документа.
Пример использования в коде:
from scipy.spatial.distance import euclidean # Calculate the Euclidean distance between the TFIDF vectors for each senator's speech distance = euclidean(tfidf_rm['roth'].values.reshape(1, -1), tfidf_rm['murray'].values.reshape(1, -1)) # Print the distance print("Euclidean distance: {:.2f}".format(distance))
4. Манхэттенское расстояние:
Манхэттенское расстояние похоже на евклидово расстояние, но вместо вычисления расстояния по прямой линии между двумя точками оно вычисляет сумму разностей между соответствующими компонентами двух точек. В контексте обработки естественного языка манхэттенское расстояние можно использовать для сравнения расстояния между двумя векторами, представляющими два документа.
Пример использования в коде:
from scipy.spatial.distance import cityblock # Calculate the Manhattan distance between the TFIDF vectors for each senator's speech distance = cityblock(tfidf_rm['roth'].values.reshape(1, -1), tfidf_rm['murray'].values.reshape(1, -1)) # Print the distance print("Manhattan distance: {:.2f}".format(distance))
5. Коэффициент корреляции Пирсона.
Коэффициент корреляции Пирсона измеряет линейную связь между двумя переменными со значением в диапазоне от -1 до 1, где -1 указывает на совершенно отрицательную корреляцию, 1 указывает на совершенно положительную корреляцию, а 0 указывает на отсутствие корреляции.
В контексте сходства текстов коэффициент корреляции Пирсона можно использовать для измерения сходства векторов TF-IDF для двух текстов. В данном коде вычисляется коэффициент корреляции Пирсона между векторами TF-IDF речей сенаторов Рота и Мюррея. Коэффициент вычисляется с помощью функции pearsonr
из модуля scipy.stats
, которая принимает на вход два массива и возвращает коэффициент корреляции и p-значение.
Коэффициент корреляции Пирсона между векторами TF-IDF выступлений сенаторов Рота и Мюррея выводится с использованием следующего кода:
from scipy.stats import pearsonr correlation, p_value = pearsonr(tfidf_rm['roth'].values.reshape(1, -1)[0], tfidf_rm['murray'].values.reshape(1, -1)[0]) print("Pearson correlation coefficient: {:.2f}".format(correlation))
Выходные данные показывают коэффициент корреляции Пирсона между векторами TF-IDF для двух речей, который представляет собой значение от -1 до 1. Значение, близкое к 1, указывает на сильную положительную корреляцию, значение, близкое к -1, указывает на сильную отрицательную корреляцию. , а значение, близкое к 0, указывает на отсутствие корреляции. В этом случае мы находим коэффициент равным 0,13.
Исходя из коэффициента корреляции Пирсона, равного 0,13, можно сказать, что существует очень слабая положительная корреляция между выступлениями сенаторов Рота и Мюррея. Это неудивительно, поскольку они принадлежат к разным политическим партиям и могут иметь разные позиции по разным вопросам.
Однако важно отметить, что корреляция Пирсона является мерой линейной корреляции, что означает, что она может не отражать нелинейные отношения между переменными. Кроме того, он измеряет только силу связи, а не направление связи, а это означает, что положительная корреляция не обязательно означает, что переменные прямо пропорциональны друг другу.
До сих пор мы исследовали несколько мер подобия, чтобы сравнить выступления двух сенаторов США, Уильяма Рота и Пэтти Мюррей. Мы использовали эти меры, чтобы оценить степень сходства между их речами и получить представление об их политических программах и стилях речи.
Наш анализ показывает, что речи двух сенаторов имеют очень низкий показатель сходства по Жаккару, составляющий 0,06%, что указывает на то, что в них не так много общих слов. Это может свидетельствовать о том, что у них очень разные политические программы и идеологии.
Евклидово расстояние между векторами TFIDF для каждого выступления сенатора составляет 0,83, что является относительно низким показателем, указывающим на некоторый уровень сходства между выступлениями. Точно так же манхэттенское расстояние 3,80 не особенно велико, что позволяет предположить, что у выступлений могут быть схожие темы и темы.
Однако коэффициент корреляции Пирсона, равный 0,13, является относительно низким, что свидетельствует об отсутствии сильной линейной зависимости между двумя выступлениями. Это может указывать на то, что у них разные стили речи и они могут расставлять приоритеты в своих речах по разным вопросам.
В целом эти результаты показывают, что, хотя между речами сенаторов Рота и Мюррея может быть некоторое сходство, у них разные политические программы и стили выступления. Важно отметить, что эти результаты основаны на одной выборке их выступлений и могут не распространяться на их общую политическую карьеру или другие образцы их речей. Потребуется дальнейший анализ, чтобы сделать более обобщающие выводы.
В поисках лучшей подруги Байдена
Мы можем провести аналогичный анализ и получить оценки сходства, сравнивая выступления всех сенаторов с речами президента Байдена.
# Find the senator whose speeches are the most similar to Biden's speeches # Combine all the senator texts into a single list all_texts = list(senator_text.values()) # Vectorize the texts using TfidfVectorizer tfidf_vectorizer = TfidfVectorizer(preprocessor=text_preprocesser, min_df =2) tfidf = tfidf_vectorizer.fit_transform(all_texts) # Calculate the cosine similarity between each pair of texts cosine_similarities = cosine_similarity(tfidf, tfidf_vectorizer.transform([senator_text['biden']])) # Create a DataFrame with the cosine similarities similarity_df = pd.DataFrame({ 'senator': list(senator_text.keys()), 'cosine_similarity': cosine_similarities.flatten() }) # Print the DataFrame similarity_df = similarity_df.sort_values(by='cosine_similarity', ascending=False) # Drop Biden similarity_df = similarity_df.drop(similarity_df[similarity_df['senator'] == 'biden'].index) # Print the DataFrame similarity_df
Первый шаг — объединить все тексты сенаторов в единый список. Затем TfidfVectorizer используется для векторизации текстов. Напоминаем, что TfidfVectorizer — это метод, который создает матрицу терминов документа с оценками tf-idf. Tfidf означает термин частотно-обратная частота документа, и это числовая статистика, которая отражает, насколько важно слово для документа в наборе документов. В этом случае TfidfVectorizer используется для преобразования текста в матрицу, где каждая строка представляет сенатора, а каждый столбец представляет слово в тексте.
После векторизации текстов вычисляется косинусное сходство между каждой парой текстов. Результатом является показатель сходства, который находится в диапазоне от 0 до 1, где 1 означает, что два текста идентичны.
Затем код создает фрейм данных с косинусным сходством и сортирует его в порядке убывания. Сенатор с наивысшей оценкой сходства с речами Байдена имеет наивысшее значение косинусного сходства. Наконец, кадр данных печатается с сенатором и оценкой косинусного сходства.
Этот код может быть полезен, чтобы понять, какие выступления сенаторов похожи на выступления Байдена. Сравнивая показатель сходства между каждым сенатором и речами Байдена, можно определить, у какого сенатора больше всего похожих речей. Результат можно использовать, чтобы понять, какой сенатор идеологически поддерживает Байдена, и может помочь в создании союзов или формировании стратегий для политических кампаний.
Вывод показывает, что Беннет является наиболее похожим на Байдена сенатором со значением косинусного подобия 0,178762, за ним следует Эшкрофт с 0,170275 и Маккейн с 0,144165. Наименее похожим на Байдена сенатором является Стивенс со значением косинусного подобия 0,0000, за ним следуют Мурковски с 0,004488 и Хатчисон с 0,006821.
Важно отметить, что значения косинусного сходства находятся в диапазоне от -1 до 1, где 1 указывает на полное сходство, а -1 указывает на полное несходство. Следовательно, значения косинусного сходства между Байденом и сенаторами в этом выводе относительно низкие, что указывает на то, что сходство между их речами не очень велико.
Вот общий график рассеяния косинусного сходства речей.
# merge the party alliance data with the initial datafram merged_df = similarity_df.merge(parties, left_on='senator', right_on='lname').drop(['lname', 'cong', 'id', "dist"], axis=1) merged_df # Create the scatter plot with hue based on party affiliation sns.scatterplot(data=merged_df, x="cosine_similarity", y="party", hue="party") plt.savefig("scatter.png")
100 кодексов демократов и 200 республиканцев. Мы видим, что схожие ценности выступлений коллег Байдена менее разбросаны, чем у республиканцев. Довольно удивительно видеть, что правый хвост «распределения» республиканских баллов указывает на более высокую величину сходства. Мне не хватает знаний в предметной области, чтобы объяснить, в чем причина этого. Тем не менее, это интересное наблюдение.
В целом, код предоставляет полезный способ определить сенаторов, чьи выступления больше всего похожи на выступления Байдена, что может быть полезно для дальнейшего анализа и понимания политического дискурса.