Compare commits
13 Commits
Author | SHA1 | Date | |
---|---|---|---|
b3b776860c | |||
ba0f259269 | |||
0ba5381cf4 | |||
bbd4ebb493 | |||
d64813e31e | |||
8bf7021003 | |||
fe565fc8d8 | |||
7508cf58e7 | |||
133b2b4d30 | |||
acd98448d1 | |||
5c9bbb9c0b | |||
d2ee579d28 | |||
fa420ecbdb |
5
.gitignore
vendored
5
.gitignore
vendored
@ -16,4 +16,7 @@ ipython_config.py
|
||||
/aimenv
|
||||
|
||||
# others
|
||||
/Lections
|
||||
/Lections
|
||||
|
||||
# data
|
||||
/static/pmu
|
@ -231,7 +231,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.5"
|
||||
"version": "3.12.8"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
@ -2923,7 +2923,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.5"
|
||||
"version": "3.12.8"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
@ -2429,7 +2429,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.5"
|
||||
"version": "3.12.8"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
@ -2323,7 +2323,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.5"
|
||||
"version": "3.12.8"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
@ -1218,7 +1218,7 @@
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.5"
|
||||
"version": "3.12.8"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
1192
lab_6/lab6.ipynb
Normal file
1192
lab_6/lab6.ipynb
Normal file
File diff suppressed because one or more lines are too long
BIN
lab_6/requirements.txt
Normal file
BIN
lab_6/requirements.txt
Normal file
Binary file not shown.
1108
lab_7/fuzzy_logic.ipynb
Normal file
1108
lab_7/fuzzy_logic.ipynb
Normal file
File diff suppressed because one or more lines are too long
963
lab_7/fuzzy_regression.ipynb
Normal file
963
lab_7/fuzzy_regression.ipynb
Normal file
File diff suppressed because one or more lines are too long
BIN
lab_7/requirements.txt
Normal file
BIN
lab_7/requirements.txt
Normal file
Binary file not shown.
622
lab_8/lab8.ipynb
Normal file
622
lab_8/lab8.ipynb
Normal file
@ -0,0 +1,622 @@
|
||||
{
|
||||
"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
|
||||
}
|
BIN
lab_8/requirements.txt
Normal file
BIN
lab_8/requirements.txt
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user