Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
697775ec61 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -17,6 +17,3 @@ ipython_config.py
|
||||
|
||||
# others
|
||||
/Lections
|
||||
|
||||
# data
|
||||
/static/pmu
|
@ -231,7 +231,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.8"
|
||||
"version": "3.12.5"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
@ -2923,7 +2923,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.8"
|
||||
"version": "3.12.5"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
@ -2429,7 +2429,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.8"
|
||||
"version": "3.12.5"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
@ -2323,7 +2323,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.8"
|
||||
"version": "3.12.5"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
@ -1218,7 +1218,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.8"
|
||||
"version": "3.12.5"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
1192
lab_6/lab6.ipynb
1192
lab_6/lab6.ipynb
File diff suppressed because one or more lines are too long
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
622
lab_8/lab8.ipynb
622
lab_8/lab8.ipynb
@ -1,622 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"## Обработка текстов на естественном языке\n",
|
||||
"\n",
|
||||
"### Определение задачи анализа текстов\n",
|
||||
"\n",
|
||||
"**Задача:** Необходимо проанализировать набор пояснительных записок к курсовой работе, представленных в формате Word-документов, и определить степень схожести их содержания. Это позволит выявить возможные случаи плагиата или схожести в описании проектов.\n",
|
||||
"\n",
|
||||
"**Цель:** Разработать модель, которая сможет автоматически анализировать текстовые данные, выделять ключевые особенности и сравнивать документы на предмет схожести содержания.\n",
|
||||
"\n",
|
||||
"**Методы:** Для решения задачи будут использованы методы обработки естественного языка (NLP), включая предобработку текста, нормализацию, фильтрацию, векторизацию (например, мешок слов, TF-IDF), а также методы машинного обучения для кластеризации документов.\n",
|
||||
"\n",
|
||||
"---"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Выгрузка данных из файлов"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"import pandas as pd\n",
|
||||
"from docx import Document\n",
|
||||
"from docx.document import Document as DocumentType\n",
|
||||
"from pandas import DataFrame\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def read_docx(file_path: str) -> str:\n",
|
||||
" try:\n",
|
||||
" doc: DocumentType = Document(file_path)\n",
|
||||
" full_text: list[str] = [paragraph.text for paragraph in doc.paragraphs]\n",
|
||||
" return \"\\n\".join(full_text)\n",
|
||||
" except Exception as e:\n",
|
||||
" print(f\"Error reading {file_path}: {e}\")\n",
|
||||
" return \"\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def load_docs(dataset_path: str) -> DataFrame:\n",
|
||||
" df = pd.DataFrame(columns=[\"doc\", \"text\"])\n",
|
||||
" for file_name in os.listdir(dataset_path):\n",
|
||||
" if file_name.startswith(\"~$\") or not file_name.lower().endswith(\".docx\"):\n",
|
||||
" continue\n",
|
||||
" file_path: str = os.path.join(dataset_path, file_name)\n",
|
||||
" text: str = read_docx(file_path)\n",
|
||||
" df.loc[len(df.index)] = [file_name, text]\n",
|
||||
" return df\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Загрузка данных\n",
|
||||
"project_root: str = os.path.abspath(os.path.join(os.path.dirname(\"lab8.ipynb\"), \"..\"))\n",
|
||||
"dataset_path: str = os.path.join(project_root, \"static\", \"pmu\")\n",
|
||||
"\n",
|
||||
"df: DataFrame = load_docs(dataset_path)\n",
|
||||
"df.info()\n",
|
||||
"df.sort_values(by=[\"doc\"], inplace=True)\n",
|
||||
"\n",
|
||||
"# Вывод первых строк данных\n",
|
||||
"display(df.head())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Предобработка текста\n",
|
||||
"\n",
|
||||
"Для выполнения предобработки текста необходимо выполнять следующие шаги:\n",
|
||||
"- **Изменение регистра:** приведение текста к нижнему регистру.\n",
|
||||
"- **Применение регулярного выражения:** удаление всех символов, кроме букв и пробелов.\n",
|
||||
"- **Удаление диакритических знаков:** нормализация текста с использованием unicodedata.\n",
|
||||
"- **Преобразование чисел в строку:** использование библиотеки num2words.\n",
|
||||
"- **Токенизация:** разбиение текста на отдельные слова.\n",
|
||||
"- **Удаление стоп-слов:** удаление слов, которые не несут смысловой нагрузки."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import re\n",
|
||||
"import unicodedata\n",
|
||||
"from typing import Set\n",
|
||||
"\n",
|
||||
"import spacy\n",
|
||||
"from num2words import num2words\n",
|
||||
"from spacy.tokens.doc import Doc\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Загрузка модели для русского языка\n",
|
||||
"# Чтобы скачать модель\n",
|
||||
"# python -m spacy download ru_core_news_lg\n",
|
||||
"nlp = spacy.load(\"ru_core_news_lg\")\n",
|
||||
"\n",
|
||||
"# Список стоп-слов\n",
|
||||
"stop_words: Set[str] = nlp.Defaults.stop_words\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Метод для предобработки текста\n",
|
||||
"def preprocess_text(text: str) -> str:\n",
|
||||
" # Приведение текста к нижнему регистру\n",
|
||||
" text = text.lower()\n",
|
||||
" \n",
|
||||
" # Замена переносов на пробел\n",
|
||||
" text = text.replace(\"\\n\", \" \")\n",
|
||||
" \n",
|
||||
" # Удаление всех символов, кроме букв и пробелов\n",
|
||||
" text = re.sub(r\"[^a-zA-Zа-яА-Я ]\", \"\", text)\n",
|
||||
" \n",
|
||||
" # Удаление диакритических знаков\n",
|
||||
" text = unicodedata.normalize(\"NFKD\", text)\n",
|
||||
" text = \"\".join([char for char in text if not unicodedata.combining(char)])\n",
|
||||
" \n",
|
||||
" # Преобразование чисел в слова\n",
|
||||
" words: list[str] = text.split()\n",
|
||||
" words = [num2words(word, lang=\"ru\") if word.isdigit() else word for word in words]\n",
|
||||
" text = \" \".join(words)\n",
|
||||
" \n",
|
||||
" # Токенизация с использованием spaCy\n",
|
||||
" doc: Doc = nlp(text)\n",
|
||||
" \n",
|
||||
" # Удаление стоп-слов и пунктуации\n",
|
||||
" tokens: list[str] = [token.text for token in doc if not token.is_stop and not token.is_punct]\n",
|
||||
" \n",
|
||||
" return \" \".join(tokens)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Применение метода предобработки к каждому тексту в DataFrame\n",
|
||||
"df[\"preprocessed_text\"] = df[\"text\"].apply(preprocess_text)\n",
|
||||
"\n",
|
||||
"# Вывод первых строк DataFrame с предобработанными текстами\n",
|
||||
"display(df.head())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Нормализация текста (Лемматизация)\n",
|
||||
"\n",
|
||||
"**Нормализация текста** — это процесс приведения текста к единому стандартному виду (форме). В данном случае используется метод лемматизации.\n",
|
||||
"\n",
|
||||
"**Лемматизация** — это процесс приведения слова к его начальной форме (лемме)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Функция для лемматизации текста\n",
|
||||
"def lemmatize_text(text: str) -> str:\n",
|
||||
" doc: Doc = nlp(text)\n",
|
||||
" lemmas: list[str] = [token.lemma_ for token in doc]\n",
|
||||
" return \" \".join(lemmas)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Применение лемматизации к предобработанным текстам\n",
|
||||
"df[\"lemmatized_text\"] = df[\"preprocessed_text\"].apply(lemmatize_text)\n",
|
||||
"\n",
|
||||
"# Вывод первых строк DataFrame с лемматизированными текстами\n",
|
||||
"display(df.head())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Фильтрация текста\n",
|
||||
"\n",
|
||||
"**Фильтрация текста** – сокращение пространства признаков за счет удаления слов по определённым условиям. В данном случае удаляются слова с длинной меньше 4 символов и больше 25."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Функция для фильтрации текста\n",
|
||||
"def filter_text(text: str, min_length: int = 3, max_length: int = 25) -> str:\n",
|
||||
" words: list[str] = text.split()\n",
|
||||
" # Удаление слов, длина которых меньше min_length или больше max_length\n",
|
||||
" filtered_words: list[str] = [\n",
|
||||
" word for word in words \n",
|
||||
" if min_length <= len(word) <= max_length\n",
|
||||
" ]\n",
|
||||
" return \" \".join(filtered_words)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Применение фильтрации к лемматизированным текстам\n",
|
||||
"df[\"filtered_text\"] = df[\"lemmatized_text\"].apply(filter_text)\n",
|
||||
"\n",
|
||||
"# Вывод первых строк DataFrame с отфильтрованными текстами\n",
|
||||
"display(df.head())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Морфологический анализ (POS tagging)\n",
|
||||
"\n",
|
||||
"**Морфологический анализ** – процесс выделения частей речи.\n",
|
||||
"\n",
|
||||
"На данном этапе каждому слову текста ставится в соответствие множество морфологических признаков, которые зависят от роли слова в предложении:\n",
|
||||
"- Часть речи;\n",
|
||||
"- Падеж;\n",
|
||||
"- Род;\n",
|
||||
"- Число;\n",
|
||||
"- Одушевленность;\n",
|
||||
"- и другие."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Функция для морфологического анализа\n",
|
||||
"def morphological_analysis(text: str) -> list[dict]:\n",
|
||||
" doc: Doc = nlp(text)\n",
|
||||
" # Сбор информации о каждом токене: текст, лемма, часть речи, морфологические признаки\n",
|
||||
" tokens_info: list[dict] = [\n",
|
||||
" {\n",
|
||||
" \"text\": token.text,\n",
|
||||
" \"lemma\": token.lemma_,\n",
|
||||
" \"pos\": token.pos_,\n",
|
||||
" \"morph\": token.morph,\n",
|
||||
" }\n",
|
||||
" for token in doc\n",
|
||||
" ]\n",
|
||||
" return tokens_info\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Применение морфологического анализа к отфильтрованным текстам\n",
|
||||
"df[\"morphological_info\"] = df[\"filtered_text\"].apply(morphological_analysis)\n",
|
||||
"\n",
|
||||
"# Вывод первых строк DataFrame с морфологическим анализом\n",
|
||||
"display(df.head())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Формирование N-грамм\n",
|
||||
"\n",
|
||||
"**N-грамма** – это последовательность из N элементов (слов, букв или других символов) в тексте.\n",
|
||||
"\n",
|
||||
"**Виды N-грамм:**\n",
|
||||
"- **Униграммы (Unigrams, N=1)** – последовательности из одного элемента (слова).\n",
|
||||
"- **Биграммы (Bigrams, N=2)** – последовательности из двух элементов.\n",
|
||||
"- **Триграммы (Trigrams, N=3)** – последовательности из трех элементов.\n",
|
||||
"- **N-граммы (для N > 3)** – последовательности из N элементов."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import nltk\n",
|
||||
"from nltk.util import ngrams\n",
|
||||
"from nltk.tokenize import word_tokenize\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Загрузка ресурсов для токенизации\n",
|
||||
"nltk.download(\"punkt\")\n",
|
||||
"nltk.download(\"punkt_tab\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def generate_ngrams(text: str, n: int = 2) -> list[tuple]:\n",
|
||||
" # Токенизация текста\n",
|
||||
" tokens: list[str] = word_tokenize(text, language=\"russian\")\n",
|
||||
" # Формирование N-грамм\n",
|
||||
" n_grams: list[tuple] = list(ngrams(tokens, n))\n",
|
||||
" return n_grams\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Пример для биграмм (N=2)\n",
|
||||
"df[\"bigrams\"] = df[\"filtered_text\"].apply(lambda x: generate_ngrams(x, n=2))\n",
|
||||
"\n",
|
||||
"# Пример для триграмм (N=3)\n",
|
||||
"df[\"trigrams\"] = df[\"filtered_text\"].apply(lambda x: generate_ngrams(x, n=3))\n",
|
||||
"\n",
|
||||
"# Вывод первых строк DataFrame с N-граммами\n",
|
||||
"display(df.head())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Векторизация с использованием мешка слов (BoW)\n",
|
||||
"\n",
|
||||
"**Векторизация текста** – это процесс преобразования текстовых данных в числовые векторы, которые могут быть использованы для анализа и обработки в моделях машинного обучения.\n",
|
||||
"\n",
|
||||
"**Метод мешка слов (Bag of Words, BoW)** – это простой и популярный способ векторизации текста. Основная идея заключается в том, что текст представляется как \"мешок\" (набор) слов без учета их порядка и грамматики. Каждое слово в тексте рассматривается как отдельная характеристика, а текст представляется в виде вектора, где каждый элемент соответствует частоте (или наличию) определенного слова."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from typing import Any, Union\n",
|
||||
"\n",
|
||||
"from numpy import ndarray\n",
|
||||
"from scipy.sparse._matrix import spmatrix\n",
|
||||
"from sklearn.feature_extraction.text import CountVectorizer\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Создаем объект CountVectorizer\n",
|
||||
"bow_vectorizer = CountVectorizer()\n",
|
||||
"\n",
|
||||
"# Применяем векторизацию к отфильтрованным текстам\n",
|
||||
"bow_matrix: Union[ndarray[Any, Any], spmatrix] = bow_vectorizer.fit_transform(df[\"filtered_text\"])\n",
|
||||
"\n",
|
||||
"# Преобразуем результат в DataFrame для удобства\n",
|
||||
"bow_df = pd.DataFrame(bow_matrix.toarray(), columns=bow_vectorizer.get_feature_names_out()) # type: ignore\n",
|
||||
"\n",
|
||||
"# Выводим первые строки результата\n",
|
||||
"display(bow_df.head())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Векторизация с использованием частотного портрета (TF-IDF)\n",
|
||||
"\n",
|
||||
"**TF-IDF** – это более продвинутый метод векторизации, который учитывает не только частоту слова в тексте, но и его важность в корпусе. Он состоит из двух компонентов:\n",
|
||||
"- **TF (Term Frequency)** – частота слова в тексте.\n",
|
||||
"- **IDF (Inverse Document Frequency)** – обратная частота документа, которая уменьшает вес слов, часто встречающихся в корпусе (например, стоп-слов)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from sklearn.feature_extraction.text import TfidfVectorizer\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Создаем объект TfidfVectorizer\n",
|
||||
"tfidf_vectorizer = TfidfVectorizer(max_df=0.95)\n",
|
||||
"\n",
|
||||
"# Применяем векторизацию к отфильтрованным текстам\n",
|
||||
"tfidf_matrix: spmatrix = tfidf_vectorizer.fit_transform(df[\"filtered_text\"])\n",
|
||||
"\n",
|
||||
"# Преобразуем результат в DataFrame для удобства\n",
|
||||
"tfidf_df = pd.DataFrame(tfidf_matrix.toarray(), columns=tfidf_vectorizer.get_feature_names_out()) # type: ignore\n",
|
||||
"\n",
|
||||
"# Выводим первые строки результата\n",
|
||||
"display(tfidf_df.head())"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"### Четкая неиерархическая кластеризация (K-Means)\n",
|
||||
"\n",
|
||||
"**K-Means** — это метод неиерархической кластеризации, который разделяет данные на \n",
|
||||
"k кластеров (групп) на основе их близости к центроидам (центрам кластеров). Основная идея заключается в том, чтобы минимизировать суммарное квадратичное отклонение точек внутри кластера от его центра.\n",
|
||||
"\n",
|
||||
"---"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Метод локтя\n",
|
||||
"\n",
|
||||
"**Метод локтя** – это способ определения оптимального числа кластеров k в алгоритме K-Means. Он основывается на анализе инерции (суммы квадратов расстояний внутри кластеров).\n",
|
||||
"\n",
|
||||
"**Принцип работы алгоритма:**\n",
|
||||
"1. Вычисление инерции: Для различных значений k вычисляется инерция W(k).\n",
|
||||
"2. Построение графика: Строится график зависимости инерции от числа кластеров k.\n",
|
||||
"3. Поиск \"локтя\": На графике ищется точка, где снижение инерции начинает замедляться. Эта точка указывает на оптимальное количество кластеров.\n",
|
||||
"\n",
|
||||
"Метод помогает избежать переобучения и обеспечивает баланс между сложностью модели и качеством кластеризации."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from sklearn.cluster import KMeans\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Вычисляем SSE для разного количества кластеров\n",
|
||||
"sse = []\n",
|
||||
"max_clusters = 50 # Максимальное количество кластеров для проверки\n",
|
||||
"for k in range(1, max_clusters + 1):\n",
|
||||
" kmeans = KMeans(n_clusters=k, random_state=42)\n",
|
||||
" kmeans.fit(tfidf_matrix)\n",
|
||||
" sse.append(kmeans.inertia_) # inertia_ содержит SSE\n",
|
||||
"\n",
|
||||
"# Визуализация метода локтя\n",
|
||||
"plt.figure(figsize=(10, 6))\n",
|
||||
"plt.plot(range(1, max_clusters + 1), sse, marker=\"o\")\n",
|
||||
"plt.xlabel(\"Количество кластеров\")\n",
|
||||
"plt.ylabel(\"SSE (Сумма квадратов ошибок)\")\n",
|
||||
"plt.title(\"Метод локтя для подбора количества кластеров\")\n",
|
||||
"plt.xticks(range(1, max_clusters + 1))\n",
|
||||
"plt.grid()\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Коэффициент силуэта\n",
|
||||
"\n",
|
||||
"**Коэффициент силуэта** – это метрика для оценки качества кластеризации, варьирующаяся от -1 до 1:\n",
|
||||
"- 1: Объект хорошо кластеризован, далеко от других кластеров.\n",
|
||||
"- 0: Объект на границе между кластерами.\n",
|
||||
"- -1: Объект, вероятно, неправильно классифицирован.\n",
|
||||
"\n",
|
||||
"Он рассчитывается по формуле: `s(i) = b(i) - a(i) / max(a(i), b(i))`, где `a(i)` – среднее расстояние до объектов в том же кластере, а `b(i)` – минимальное среднее расстояние до объектов в ближайшем кластере. Высокие значения указывают на хорошую кластеризацию, низкие – на проблемы с классификацией."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from sklearn.metrics import silhouette_score\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Вычисляем коэффициент силуэта для разного количества кластеров\n",
|
||||
"silhouette_scores = []\n",
|
||||
"for k in range(2, max_clusters + 1): # Минимум 2 кластера для силуэта\n",
|
||||
" kmeans = KMeans(n_clusters=k, random_state=42)\n",
|
||||
" kmeans.fit(tfidf_matrix)\n",
|
||||
" score = silhouette_score(tfidf_matrix, kmeans.labels_, random_state=42)\n",
|
||||
" silhouette_scores.append(score)\n",
|
||||
"\n",
|
||||
"# Визуализация коэффициента силуэта\n",
|
||||
"plt.figure(figsize=(10, 6))\n",
|
||||
"plt.plot(range(2, max_clusters + 1), silhouette_scores, marker=\"o\")\n",
|
||||
"plt.xlabel(\"Количество кластеров\")\n",
|
||||
"plt.ylabel(\"Коэффициент силуэта\")\n",
|
||||
"plt.title(\"Коэффициент силуэта для подбора количества кластеров\")\n",
|
||||
"plt.xticks(range(2, max_clusters + 1))\n",
|
||||
"plt.grid()\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Кластеризация с использованием K-Means"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from sklearn.cluster import KMeans\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Задаем количество кластеров\n",
|
||||
"n_clusters = 23\n",
|
||||
"kmeans = KMeans(n_clusters=n_clusters, random_state=42)\n",
|
||||
"\n",
|
||||
"# Обучаем модель на TF-IDF векторах\n",
|
||||
"kmeans.fit(tfidf_matrix)\n",
|
||||
"\n",
|
||||
"# Добавляем метки кластеров в DataFrame\n",
|
||||
"df[\"cluster\"] = kmeans.labels_\n",
|
||||
"\n",
|
||||
"# Выводим документы с их кластерами\n",
|
||||
"display(df[[\"doc\", \"cluster\"]])"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Визуализация кластеров текстов с использованием PCA"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from sklearn.decomposition import PCA\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Задаем количество кластеров\n",
|
||||
"n_clusters = 23\n",
|
||||
"\n",
|
||||
"# Создаем модель K-Means\n",
|
||||
"kmeans = KMeans(n_clusters=n_clusters, random_state=42)\n",
|
||||
"\n",
|
||||
"# Обучаем модель и предсказываем кластеры\n",
|
||||
"clusters = kmeans.fit_predict(tfidf_matrix)\n",
|
||||
"\n",
|
||||
"# Уменьшаем размерность данных до 2 компонент с помощью PCA\n",
|
||||
"pca = PCA(n_components=2)\n",
|
||||
"X_pca = pca.fit_transform(tfidf_matrix.toarray())\n",
|
||||
"\n",
|
||||
"# Визуализируем кластеры\n",
|
||||
"plt.figure(figsize=(12, 10))\n",
|
||||
"plt.scatter(X_pca[:, 0], X_pca[:, 1], c=clusters, cmap=\"viridis\", s=50, alpha=0.6)\n",
|
||||
"\n",
|
||||
"# Добавляем подписи для центроидов кластеров\n",
|
||||
"centers = pca.transform(kmeans.cluster_centers_)\n",
|
||||
"plt.scatter(centers[:, 0], centers[:, 1], c=\"red\", s=200, alpha=0.75, marker=\"X\")\n",
|
||||
"\n",
|
||||
"# Добавляем подписи для каждого документа\n",
|
||||
"for i, doc in enumerate(df[\"doc\"]):\n",
|
||||
" plt.text(X_pca[i, 0], X_pca[i, 1], doc, fontsize=8, alpha=0.8)\n",
|
||||
"\n",
|
||||
"# Добавляем заголовок и подписи осей\n",
|
||||
"plt.title(\"Визуализация кластеров текстов с использованием PCA\")\n",
|
||||
"plt.xlabel(\"Компонента PCA 1\")\n",
|
||||
"plt.ylabel(\"Компонента PCA 2\")\n",
|
||||
"plt.colorbar(label=\"Кластер\")\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"#### Интерактивное отображение результата кластеризации"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import plotly.express as px\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Создаем DataFrame для визуализации\n",
|
||||
"df_pca = pd.DataFrame(X_pca, columns=[\"PCA 1\", \"PCA 2\"])\n",
|
||||
"df_pca[\"Cluster\"] = clusters\n",
|
||||
"df_pca[\"Text\"] = df[\"doc\"]\n",
|
||||
"\n",
|
||||
"# Создаем интерактивный scatter plot\n",
|
||||
"fig = px.scatter(\n",
|
||||
" df_pca, x=\"PCA 1\", y=\"PCA 2\", color=\"Cluster\", hover_data=[\"Text\"],\n",
|
||||
" title=\"Визуализация кластеров текстов с использованием PCA\",\n",
|
||||
" labels={\"PCA 1\": \"Компонента PCA 1\", \"PCA 2\": \"Компонента PCA 2\"}\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Добавляем центроиды кластеров\n",
|
||||
"centers = pca.transform(kmeans.cluster_centers_)\n",
|
||||
"centers_df = pd.DataFrame(centers, columns=[\"PCA 1\", \"PCA 2\"])\n",
|
||||
"centers_df[\"Cluster\"] = range(n_clusters)\n",
|
||||
"fig.add_scatter(\n",
|
||||
" x=centers_df[\"PCA 1\"], y=centers_df[\"PCA 2\"], mode=\"markers\",\n",
|
||||
" marker=dict(color=\"red\", size=10, symbol=\"x\"), name=\"Centroids\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Показываем график\n",
|
||||
"# Уберём вывод отображение графика, \n",
|
||||
"# чтобы Jupiter-notebook корректно отображался в репозитории\n",
|
||||
"fig.show()"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "aimenv",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.8"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 2
|
||||
}
|
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user