Сравнить коммиты

...

2 Коммитов

Двоичные данные
.DS_Store поставляемый

Двоичный файл не отображается.

Двоичные данные
labworks/.DS_Store поставляемый

Двоичный файл не отображается.

Двоичные данные
labworks/LW2/.DS_Store поставляемый

Двоичный файл не отображается.

Двоичный файл не отображается.

Разница между файлами не показана из-за своего большого размера Загрузить разницу

@ -0,0 +1,742 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Лабораторная работа №2\n",
"## Обнаружение аномалий с помощью автокодировщиков\n",
"\n",
"**Выполнили:** Троянов Даниил Сергеевич, Чернов Данила Евгеньевич \n",
"**Группа:** А-01-22 \n",
"**Бригада:** 1\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Задание 1\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 1) Импорт необходимых библиотек и модулей\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import lab02_lib as lib\n",
"from unittest.mock import patch\n",
"import builtins\n",
"\n",
"# Создаем директорию для выходных файлов\n",
"os.makedirs('out', exist_ok=True)\n",
"\n",
"print('Библиотеки успешно импортированы')\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 2) Генерация индивидуального набора двумерных данных\n",
"\n",
"Сгенерируем набор данных с координатами центра (1, 1), где 1 – номер бригады.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Генерация датасета для бригады 1\n",
"brigade_num = 1\n",
"data = lib.datagen(brigade_num, brigade_num, 1000, 2)\n",
"\n",
"# Вывод данных и размерности\n",
"print('Исходные данные (первые 10 строк):')\n",
"print(data[:10])\n",
"print('\\nРазмерность данных:')\n",
"print(data.shape)\n",
"print(f'\\nЦентр кластера: ({brigade_num}, {brigade_num})')\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 3) Создание и обучение автокодировщика AE1 простой архитектуры\n",
"\n",
"Создадим простой автокодировщик с минимальной архитектурой и небольшим количеством эпох обучения.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Обучение AE1 с использованием функции из lab02_lib\n",
"# Для простой архитектуры используем вариант с пользовательской архитектурой\n",
"# Архитектура: 2 -> 1 -> 2 (1 скрытый слой с 1 нейроном)\n",
"\n",
"print('Обучение автокодировщика AE1...')\n",
"print('Архитектура: 2 -> 1 -> 2 (простая)')\n",
"\n",
"# Автоматизируем ввод для выбора пользовательской архитектуры\n",
"with patch('builtins.input', side_effect=['1', '1', '1']):\n",
" ae1_trained, IRE1, IREth1 = lib.create_fit_save_ae(\n",
" data, \n",
" 'out/AE1.h5', \n",
" 'out/AE1_ire_th.txt',\n",
" 1000, # epochs\n",
" True, # verbose_show\n",
" 300, # patience\n",
" early_stopping_delta=0.001\n",
" )\n",
"\n",
"# Вычисление MSE из истории обучения (приблизительно)\n",
"# Для точного значения нужно сохранить историю, но функция не возвращает её\n",
"# Используем предсказание для оценки\n",
"X_pred_ae1 = ae1_trained.predict(data, verbose=0)\n",
"mse_ae1 = np.mean((data - X_pred_ae1) ** 2)\n",
"\n",
"print(f'\\nОбучение завершено!')\n",
"print(f'MSE_stop (приблизительно): {mse_ae1:.6f}')\n",
"print(f'Порог IRE: {IREth1:.6f}')\n",
"print(f'Количество скрытых слоев: 1')\n",
"print(f'Количество нейронов в скрытых слоях: 1')\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 4) Построение графика ошибки реконструкции для AE1\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Построение графика ошибки реконструкции\n",
"lib.ire_plot('training', IRE1, IREth1, 'AE1')\n",
"print(f'Порог ошибки реконструкции (IREth1): {IREth1:.6f}')\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 5) Создание и обучение автокодировщика AE2 с усложненной архитектурой\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Обучение AE2 с использованием функции из lab02_lib\n",
"# Для усложненной архитектуры используем вариант с пользовательской архитектурой\n",
"# Архитектура: 2 -> 8 -> 4 -> 2 -> 1 -> 2 -> 4 -> 8 -> 2 (6 скрытых слоев)\n",
"\n",
"print('Обучение автокодировщика AE2...')\n",
"print('Архитектура: 2 -> 8 -> 4 -> 2 -> 1 -> 2 -> 4 -> 8 -> 2 (усложненная)')\n",
"\n",
"# Автоматизируем ввод для выбора пользовательской архитектуры\n",
"# 7 скрытых слоев: 8 4 2 1 2 4 8\n",
"with patch('builtins.input', side_effect=['1', '7', '8 4 2 1 2 4 8']):\n",
" ae2_trained, IRE2, IREth2 = lib.create_fit_save_ae(\n",
" data, \n",
" 'out/AE2.h5', \n",
" 'out/AE2_ire_th.txt',\n",
" 3000, # epochs\n",
" True, # verbose_show\n",
" 300, # patience\n",
" early_stopping_delta=0.001\n",
" )\n",
"\n",
"# Вычисление MSE из предсказания\n",
"X_pred_ae2 = ae2_trained.predict(data, verbose=0)\n",
"mse_ae2 = np.mean((data - X_pred_ae2) ** 2)\n",
"\n",
"print(f'\\nОбучение завершено!')\n",
"print(f'MSE_stop (приблизительно): {mse_ae2:.6f}')\n",
"print(f'Порог IRE: {IREth2:.6f}')\n",
"print(f'Количество скрытых слоев: 6')\n",
"print(f'Количество нейронов в скрытых слоях: 8-4-2-1-2-4-8')\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 6) Построение графика ошибки реконструкции для AE2\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Построение графика ошибки реконструкции\n",
"lib.ire_plot('training', IRE2, IREth2, 'AE2')\n",
"print(f'Порог ошибки реконструкции (IREth2): {IREth2:.6f}')\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 7) Расчет характеристик качества обучения EDCA для AE1 и AE2\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Расчет EDCA для AE1\n",
"numb_square = 20\n",
"xx, yy, Z1 = lib.square_calc(numb_square, data, ae1_trained, IREth1, '1', True)\n",
"\n",
"# Сохраняем результаты EDCA для AE1 сразу после расчета\n",
"excess_ae1 = None\n",
"approx_ae1 = None\n",
"try:\n",
" with open('out/result.txt', 'r') as f:\n",
" content = f.read()\n",
" if 'AE1' in content:\n",
" lines = content.split('\\n')\n",
" for line in lines:\n",
" if 'Excess' in line and excess_ae1 is None:\n",
" excess_ae1 = float(line.split('=')[1].strip())\n",
" elif 'Approx' in line and approx_ae1 is None:\n",
" approx_ae1 = float(line.split('=')[1].strip())\n",
"except:\n",
" pass\n",
"\n",
"print(f'\\nСохраненные результаты для AE1:')\n",
"print(f'Excess: {excess_ae1}')\n",
"print(f'Approx: {approx_ae1}')\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Расчет EDCA для AE2\n",
"xx, yy, Z2 = lib.square_calc(numb_square, data, ae2_trained, IREth2, '2', True)\n",
"\n",
"# Сохраняем результаты EDCA для AE2 сразу после расчета\n",
"excess_ae2 = None\n",
"approx_ae2 = None\n",
"try:\n",
" with open('out/result.txt', 'r') as f:\n",
" content = f.read()\n",
" if 'AE2' in content:\n",
" lines = content.split('\\n')\n",
" for line in lines:\n",
" if 'Excess' in line and excess_ae2 is None:\n",
" excess_ae2 = float(line.split('=')[1].strip())\n",
" elif 'Approx' in line and approx_ae2 is None:\n",
" approx_ae2 = float(line.split('=')[1].strip())\n",
"except:\n",
" pass\n",
"\n",
"print(f'\\nСохраненные результаты для AE2:')\n",
"print(f'Excess: {excess_ae2}')\n",
"print(f'Approx: {approx_ae2}')\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Сравнение характеристик качества обучения и областей аппроксимации\n",
"lib.plot2in1(data, xx, yy, Z1, Z2)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 8) Создание тестовой выборки\n",
"\n",
"Создадим тестовую выборку из элементов, которые AE1 распознает как норму, а AE2 детектирует как аномалии.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Создание тестовой выборки\n",
"# Точки, которые находятся на среднем расстоянии от центра (1, 1)\n",
"# чтобы AE1 их не распознал как аномалии (IRE < порог AE1 ~2.06),\n",
"# но AE2 распознал как аномалии (IRE > порог AE2 ~0.41)\n",
"# Выбираем точки на расстоянии примерно 1-1.5 от центра\n",
"data_test = np.array([\n",
" [-0.5, 0.5], \n",
" [1, 0.5], \n",
" [0.2, 1.2], \n",
" [0, 0.1] \n",
"])\n",
"\n",
"print('Тестовая выборка:')\n",
"print(data_test)\n",
"print(f'\\nРазмерность: {data_test.shape}')\n",
"print(f'\\nЦентр обучающих данных: (1, 1)')\n",
"print(f'Расстояния от центра:')\n",
"for i, point in enumerate(data_test):\n",
" dist = np.sqrt((point[0] - 1)**2 + (point[1] - 1)**2)\n",
" print(f' Точка {i+1} {point}: расстояние = {dist:.3f}')\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 9) Применение автокодировщиков к тестовым данным\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Тестирование AE1\n",
"predicted_labels1, ire1_test = lib.predict_ae(ae1_trained, data_test, IREth1)\n",
"lib.anomaly_detection_ae(predicted_labels1, ire1_test, IREth1)\n",
"lib.ire_plot('test', ire1_test, IREth1, 'AE1')\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Тестирование AE2\n",
"predicted_labels2, ire2_test = lib.predict_ae(ae2_trained, data_test, IREth2)\n",
"lib.anomaly_detection_ae(predicted_labels2, ire2_test, IREth2)\n",
"lib.ire_plot('test', ire2_test, IREth2, 'AE2')\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Визуализация элементов обучающей и тестовой выборки\n",
"lib.plot2in1_anomaly(data, xx, yy, Z1, Z2, data_test)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Подсчет обнаруженных аномалий\n",
"anomalies_ae1 = int(predicted_labels1.sum())\n",
"anomalies_ae2 = int(predicted_labels2.sum())\n",
"\n",
"# Используем сохраненные ранее значения EDCA метрик\n",
"# (они были сохранены в ячейках после вызова square_calc)\n",
"# Если переменные не определены, пытаемся прочитать из файла\n",
"try:\n",
" excess_ae1_val = excess_ae1 if excess_ae1 is not None else None\n",
"except NameError:\n",
" excess_ae1_val = None\n",
"\n",
"try:\n",
" approx_ae1_val = approx_ae1 if approx_ae1 is not None else None\n",
"except NameError:\n",
" approx_ae1_val = None\n",
"\n",
"try:\n",
" excess_ae2_val = excess_ae2 if excess_ae2 is not None else None\n",
"except NameError:\n",
" excess_ae2_val = None\n",
"\n",
"try:\n",
" approx_ae2_val = approx_ae2 if approx_ae2 is not None else None\n",
"except NameError:\n",
" approx_ae2_val = None\n",
"\n",
"print('Таблица 1 - Результаты задания №1')\n",
"print('=' * 120)\n",
"print(f'{\"Модель\":<10} {\"Скрытых слоев\":<15} {\"Нейроны\":<25} {\"Эпох\":<10} {\"MSE_stop\":<12} {\"Порог IRE\":<12} {\"Excess\":<10} {\"Approx\":<10} {\"Аномалий\":<10}')\n",
"print('-' * 120)\n",
"print(f'AE1 {1:<15} {1:<25} {1000:<10} {mse_ae1:<12.6f} {IREth1:<12.6f} {excess_ae1_val if excess_ae1_val is not None else \"N/A\":<10} {approx_ae1_val if approx_ae1_val is not None else \"N/A\":<10} {anomalies_ae1}/{len(data_test):<10}')\n",
"print(f'AE2 {6:<15} {\"8-4-2-1-2-4-8\":<25} {3000:<10} {mse_ae2:<12.6f} {IREth2:<12.6f} {excess_ae2_val if excess_ae2_val is not None else \"N/A\":<10} {approx_ae2_val if approx_ae2_val is not None else \"N/A\":<10} {anomalies_ae2}/{len(data_test):<10}')\n",
"print('=' * 120)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Задание 2\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 1) Изучение набора реальных данных Letter\n",
"\n",
"Набор данных Letter представляет собой характеристики букв английского алфавита. Для обнаружения аномалий нормальные примеры используются для обучения, а аномальные - для тестирования.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Загрузка обучающей и тестовой выборки Letter\n",
"train_letter = np.loadtxt('data/letter_train.txt', dtype=float)\n",
"test_letter = np.loadtxt('data/letter_test.txt', dtype=float)\n",
"\n",
"print('Обучающая выборка Letter:')\n",
"print(f'Размерность: {train_letter.shape}')\n",
"print(f'Количество признаков: {train_letter.shape[1]}')\n",
"print(f'Количество примеров: {train_letter.shape[0]}')\n",
"print(f'\\nПервые 5 строк:')\n",
"print(train_letter[:5])\n",
"\n",
"print(f'\\nТестовая выборка Letter:')\n",
"print(f'Размерность: {test_letter.shape}')\n",
"print(f'Количество примеров: {test_letter.shape[0]}')\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 2.1) Нормализация данных (дополнительное исследование, для сравнения)\n",
"\n",
"Создадим нормализованную версию данных для сравнения результатов обучения.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Нормализация данных для сравнения результатов\n",
"from sklearn.preprocessing import StandardScaler\n",
"\n",
"# Создаем нормализованные версии данных\n",
"scaler = StandardScaler()\n",
"train_letter_normalized = scaler.fit_transform(train_letter)\n",
"test_letter_normalized = scaler.transform(test_letter)\n",
"\n",
"print('Нормализация данных выполнена')\n",
"print(f'\\nИсходные данные (первые 3 признака первого примера):')\n",
"print(f' train_letter[0, :3] = {train_letter[0, :3]}')\n",
"print(f'\\nНормализованные данные (первые 3 признака первого примера):')\n",
"print(f' train_letter_normalized[0, :3] = {train_letter_normalized[0, :3]}')\n",
"print(f'\\nСтатистика нормализованных данных:')\n",
"print(f' Среднее: {train_letter_normalized.mean(axis=0)[:5]}...')\n",
"print(f' Стд. отклонение: {train_letter_normalized.std(axis=0)[:5]}...')\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 2.2) Обучение автокодировщика на ненормализованных данных\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Обучение автокодировщика для Letter на НЕнормализованных данных\n",
"# Входной и выходной слои создаются автоматически по размеру данных (32 признака)\n",
"# Мы задаем только скрытые слои\n",
"# Полная архитектура: 32(вход) -> 100 -> 68 -> 48 -> 32 -> 24 -> 16 -> 8 -> 16 -> 24 -> 32 -> 48 -> 64 -> 100 -> 32(выход)\n",
"# 11 скрытых слоев: 100 64 48 32 24 16 8 16 24 32 48 64 100 \n",
"\n",
"import warnings\n",
"# Подавляем предупреждение о формате сохранения HDF5\n",
"warnings.filterwarnings('ignore', category=UserWarning, module='absl')\n",
"\n",
"print('Обучение автокодировщика для данных Letter (НЕнормализованные данные)...')\n",
"print('Архитектура: 32(вход) -> 100 -> 68 -> 48 -> 32 -> 24 -> 16 -> 8 -> 16 -> 24 -> 32 -> 48 -> 64 -> 100 -> 32(выход)')\n",
"\n",
"# Автоматизируем ввод для выбора пользовательской архитектуры\n",
"with patch('builtins.input', side_effect=['1', '17', ' 100 86 72 64 48 32 24 16 8 16 24 32 48 64 72 86 100']):\n",
" ae_letter_raw, IRE_letter_raw, IREth_letter_raw = lib.create_fit_save_ae(\n",
" train_letter, \n",
" 'out/Letter_AE_raw.h5', \n",
" 'out/Letter_AE_raw_ire_th.txt',\n",
" 100000, # epochs\n",
" False, # verbose_show\n",
" 10000, # patience\n",
" verbose_every_n_epochs=500\n",
" )\n",
"\n",
"# Вычисление MSE из предсказания\n",
"X_pred_letter_raw = ae_letter_raw.predict(train_letter, verbose=0)\n",
"mse_letter_raw = np.mean((train_letter - X_pred_letter_raw) ** 2)\n",
"\n",
"print(f'\\nОбучение завершено (НЕнормализованные данные)!')\n",
"print(f'MSE_stop (приблизительно): {mse_letter_raw:.6f}')\n",
"print(f'Порог IRE: {IREth_letter_raw:.6f}')\n",
"print(f'Количество скрытых слоев: 11')\n",
"print(f'Количество нейронов в скрытых слоях: 100 -> 68 -> 48 -> 32 -> 24 -> 16 -> 8 -> 16 -> 24 -> 32 -> 48 -> 64 -> 100')\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 2.3) Обучение автокодировщика на нормализованных данных\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Обучение автокодировщика для Letter на нормализованных данных\n",
"# Та же архитектура для сравнения: 32(вход) -> 100 -> 68 -> 48 -> 32 -> 24 -> 16 -> 8 -> 16 -> 24 -> 32 -> 48 -> 64 -> 100 -> 32(выход)\n",
"# 11 скрытых слоев: 100 86 72 64 48 32 24 16 8 16 24 32 48 64 72 86 100\n",
"\n",
"print('Обучение автокодировщика для данных Letter (нормализованные данные)...')\n",
"print('Архитектура: 32(вход) -> 100 -> 68 -> 48 -> 32 -> 24 -> 16 -> 8 -> 16 -> 24 -> 32 -> 48 -> 64 -> 100 -> 32(выход)')\n",
"\n",
"# Автоматизируем ввод для выбора пользовательской архитектуры\n",
"with patch('builtins.input', side_effect=['1', '17', ' 100 86 72 64 48 32 24 16 8 16 24 32 48 64 72 86 100']):\n",
" ae_letter_norm, IRE_letter_norm, IREth_letter_norm = lib.create_fit_save_ae(\n",
" train_letter_normalized, \n",
" 'out/Letter_AE_norm.h5', \n",
" 'out/Letter_AE_norm_ire_th.txt',\n",
" 20000, # epochs\n",
" True, # verbose_show\n",
" 200, # patience\n",
" verbose_every_n_epochs=500\n",
" )\n",
"\n",
"# Вычисление MSE из предсказания\n",
"X_pred_letter_norm = ae_letter_norm.predict(train_letter_normalized, verbose=0)\n",
"mse_letter_norm = np.mean((train_letter_normalized - X_pred_letter_norm) ** 2)\n",
"\n",
"print(f'\\nОбучение завершено (нормализованные данные)!')\n",
"print(f'MSE_stop (приблизительно): {mse_letter_norm:.6f}')\n",
"print(f'Порог IRE: {IREth_letter_norm:.6f}')\n",
"print(f'Количество скрытых слоев: 11')\n",
"print(f'Количество нейронов в скрытых слоях: 100 -> 68 -> 48 -> 32 -> 24 -> 16 -> 8 -> 16 -> 24 -> 32 -> 48 -> 64 -> 100')\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 2.4) Сравнение результатов обучения на нормализованных и ненормализованных данных\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Сравнение результатов\n",
"print('=' * 100)\n",
"print('СРАВНЕНИЕ РЕЗУЛЬТАТОВ ОБУЧЕНИЯ')\n",
"print('=' * 100)\n",
"print(f'{\"Параметр\"} {\"НЕнормализованные\"} {\"Нормализованные\"}')\n",
"print('-' * 100)\n",
"print(f'{\"MSE_stop\"} {mse_letter_raw} {mse_letter_norm}')\n",
"print(f'{\"Порог IRE\"} {IREth_letter_raw} {IREth_letter_norm:}')\n",
"print(f'{\"Архитектура\":<30} {\"17 слоев\"} {\"17 слоев\"}')\n",
"print(f'{\"Нейроны\"} {\"32(вход) -> 100 -> 68 -> 48 -> 32 -> 24 -> 16 -> 8 -> 16 -> 24 -> 32 -> 48 -> 64 -> 100 -> 32(выход)\"} {\"32(вход) -> 100 -> 68 -> 48 -> 32 -> 24 -> 16 -> 8 -> 16 -> 24 -> 32 -> 48 -> 64 -> 100 -> 32(выход)\"}')\n",
"print('=' * 100)\n",
"\n",
"# Выбираем лучшую модель для дальнейшей работы\n",
"if mse_letter_norm < mse_letter_raw:\n",
" print('\\nЛучшая модель: НОРМАЛИЗОВАННЫЕ данные')\n",
" ae_letter = ae_letter_norm\n",
" IRE_letter = IRE_letter_norm\n",
" IREth_letter = IREth_letter_norm\n",
" mse_letter = mse_letter_norm\n",
" test_letter_used = test_letter_normalized\n",
" print(f'Используем эту модель для дальнейших экспериментов')\n",
"else:\n",
" print('\\nЛучшая модель: НЕнормализованные данные')\n",
" ae_letter = ae_letter_raw\n",
" IRE_letter = IRE_letter_raw\n",
" IREth_letter = IREth_letter_raw\n",
" mse_letter = mse_letter_raw\n",
" test_letter_used = test_letter\n",
" print(f'Используем эту модель для дальнейших экспериментов')\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 3) Построение графика ошибки реконструкции для Letter\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Построение графика ошибки реконструкции для выбранной модели\n",
"lib.ire_plot('training', IRE_letter, IREth_letter, 'Letter_AE')\n",
"print(f'Порог ошибки реконструкции: {IREth_letter:.6f}')\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 4) Применение автокодировщика к тестовой выборке Letter\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Тестирование автокодировщика на тестовой выборке\n",
"# Используем выбранную модель (лучшую из нормализованных/ненормализованных)\n",
"predicted_labels_letter, ire_letter_test = lib.predict_ae(ae_letter, test_letter_used, IREth_letter)\n",
"\n",
"# Подсчет обнаруженных аномалий\n",
"num_anomalies = int(predicted_labels_letter.sum())\n",
"total_test = len(test_letter_used)\n",
"anomaly_percentage = (num_anomalies / total_test) * 100\n",
"\n",
"print(f'Обнаружено аномалий: {num_anomalies} из {total_test}')\n",
"print(f'Процент обнаруженных аномалий: {anomaly_percentage:.1f}%')\n",
"\n",
"lib.anomaly_detection_ae(predicted_labels_letter, ire_letter_test, IREth_letter)\n",
"lib.ire_plot('test', ire_letter_test, IREth_letter, 'Letter_AE')\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 5) Результаты задания №2\n",
"\n",
"Результаты исследования занесены в таблицу:\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"print('Таблица 2 - Результаты задания №2')\n",
"print('=' * 100)\n",
"print(f'{\"Dataset\":<15} {\"Скрытых слоев\":<15} {\"Нейроны\":<40} {\"Эпох\":<10} {\"MSE_stop\":<12} {\"Порог IRE\":<12} {\"% аномалий\":<12}')\n",
"print('-' * 100)\n",
"print(f'Letter {11:<15} {\"32(вход) -> 100 -> 68 -> 48 -> 32 -> 24 -> 16 -> 8 -> 16 -> 24 -> 32 -> 48 -> 64 -> 100 -> 32(выход)\":<40} {20000:<10} {mse_letter:<12.6f} {IREth_letter:<12.6f} {anomaly_percentage:.1f}%')\n",
"print('=' * 100)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Выводы\n",
"\n",
"### Требования к данным для обучения:\n",
"- Данные для обучения должны быть без аномалий, чтобы автокодировщик смог рассчитать верное пороговое значение\n",
"\n",
"### Требования к архитектуре автокодировщика:\n",
"- Архитектура должна постепенно сужаться к бутылочному горлышку, а затем постепенно возвращаться к исходным выходным размерам\n",
"- Для двумерных данных оптимальное количество скрытых слоев: 3-6\n",
"- Для многомерных данных (32 признака) оптимальное количество скрытых слоев: от 7-ми\n",
"\n",
"### Требования к количеству эпох обучения:\n",
"- Для простых данных (2D): 1000-3000 эпох\n",
"- Для сложных данных (многомерных): до 100000 эпох\n",
"\n",
"### Требования к ошибке MSE_stop:\n",
"- Для двумерных данных: оптимальная ошибка MSE-stop в районе 0.01\n",
"- Для многомерных данных: оптимальная ошибка MSE-stop в районе 0.1-0.01\n",
"\n",
"### Требования к порогу обнаружения аномалий:\n",
"- Для двумерных данных: значение порога в районе 0.4-2.5\n",
"- Для многомерных данных: значение порога не больше 1.6\n",
"\n",
"### Требования к характеристикам качества обучения EDCA:\n",
"- Значение Excess должно быть как можно ближе к 0\n",
"- Значение Deficit должно быть как можно ближе к 0 \n",
"- Значение Coating должно быть как можно ближе к 1 \n",
"- Значение Approx должно быть как можно ближе к 1\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"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.11.9"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

@ -1,712 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Лабораторная работа №2: Обнаружение аномалий с помощью автокодировщиков\n",
"\n",
"**Вариант 1 (номер бригады k=1) - данные Letter**\n",
"\n",
"---\n",
"\n",
"## Описание\n",
"Данная лабораторная работа посвящена изучению автокодировщиков для обнаружения аномалий. Работа включает два основных задания:\n",
"1. Работа с двумерными синтетическими данными\n",
"2. Работа с реальными данными Letter\n",
"\n",
"**Номер бригады:** k=1 \n",
"**Центр данных:** (1, 1)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Импорт необходимых библиотек\n",
"import os\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"from sklearn.preprocessing import StandardScaler\n",
"from sklearn.datasets import make_blobs\n",
"import tensorflow as tf\n",
"from tensorflow.keras.models import Sequential\n",
"from tensorflow.keras.layers import Dense, Activation\n",
"from tensorflow.keras.optimizers import Adam\n",
"from tensorflow.keras.callbacks import EarlyStopping\n",
"import lab02_lib as lib\n",
"\n",
"# Создаем папку для результатов\n",
"os.makedirs('out', exist_ok=True)\n",
"\n",
"# Параметры для варианта 1 (номер бригады k=1)\n",
"k = 1 # номер бригады\n",
"center_coords = (k, k) # координаты центра (1, 1)\n",
"\n",
"print(\"Лабораторная работа №2: Обнаружение аномалий с помощью автокодировщиков\")\n",
"print(\"Вариант 1 (номер бригады k=1) - данные Letter\")\n",
"print(\"=\" * 70)\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# ЗАДАНИЕ 1: Работа с двумерными синтетическими данными\n",
"\n",
"## 1. Генерация индивидуального набора двумерных данных\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Генерация данных с центром в точке (1, 1)\n",
"X_synthetic, _ = make_blobs(n_samples=100, centers=[center_coords], n_features=2, \n",
" cluster_std=0.5, random_state=42)\n",
"\n",
"print(f\"Сгенерировано {len(X_synthetic)} точек\")\n",
"print(f\"Центр данных: {center_coords}\")\n",
"print(f\"Размерность данных: {X_synthetic.shape}\")\n",
"\n",
"# Визуализация данных\n",
"plt.figure(figsize=(10, 8))\n",
"plt.scatter(X_synthetic[:, 0], X_synthetic[:, 1], c='blue', alpha=0.7, s=50)\n",
"plt.scatter(center_coords[0], center_coords[1], c='red', s=200, marker='x', linewidth=3, label='Центр')\n",
"plt.title(f'Синтетические данные (центр в точке {center_coords})')\n",
"plt.xlabel('X1')\n",
"plt.ylabel('X2')\n",
"plt.legend()\n",
"plt.grid(True, alpha=0.3)\n",
"plt.savefig('out/synthetic_data.png', dpi=300, bbox_inches='tight')\n",
"plt.show()\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 2. Создание и обучение автокодировщика AE1 (простая архитектура)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def create_autoencoder_ae1(input_dim):\n",
" \"\"\"Создание автокодировщика AE1 с простой архитектурой\"\"\"\n",
" model = Sequential()\n",
" \n",
" # Входной слой\n",
" model.add(Dense(input_dim, input_shape=(input_dim,)))\n",
" model.add(Activation('tanh'))\n",
" \n",
" # Скрытые слои \n",
" model.add(Dense(1)) # сжатие до 1 нейрона\n",
" model.add(Activation('tanh'))\n",
" \n",
" # Выходной слой\n",
" model.add(Dense(input_dim))\n",
" model.add(Activation('linear'))\n",
" \n",
" return model\n",
"\n",
"# Создание AE1\n",
"ae1 = create_autoencoder_ae1(2)\n",
"ae1.compile(loss='mse', optimizer=Adam(learning_rate=0.001))\n",
"\n",
"print(\"Архитектура AE1:\")\n",
"ae1.summary()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Обучение AE1\n",
"print(\"\\nОбучение AE1 (20 эпох)...\")\n",
"history_ae1 = ae1.fit(X_synthetic, X_synthetic, \n",
" epochs=20, \n",
" batch_size=32, \n",
" validation_split=0.2,\n",
" verbose=1)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Анализ результатов AE1\n",
"print(\"\\nАнализ результатов AE1\")\n",
"mse_ae1 = history_ae1.history['loss'][-1]\n",
"print(f\"Финальная ошибка MSE AE1: {mse_ae1:.6f}\")\n",
"\n",
"# Построение графика ошибки реконструкции\n",
"plt.figure(figsize=(12, 4))\n",
"plt.subplot(1, 2, 1)\n",
"plt.plot(history_ae1.history['loss'], label='Training Loss')\n",
"plt.plot(history_ae1.history['val_loss'], label='Validation Loss')\n",
"plt.title('AE1: Ошибка обучения')\n",
"plt.xlabel('Эпоха')\n",
"plt.ylabel('MSE')\n",
"plt.legend()\n",
"plt.grid(True, alpha=0.3)\n",
"\n",
"# Вычисление ошибок реконструкции для обучающих данных\n",
"X_pred_ae1 = ae1.predict(X_synthetic)\n",
"reconstruction_errors_ae1 = np.mean(np.square(X_synthetic - X_pred_ae1), axis=1)\n",
"threshold_ae1 = np.percentile(reconstruction_errors_ae1, 95)\n",
"\n",
"plt.subplot(1, 2, 2)\n",
"plt.hist(reconstruction_errors_ae1, bins=20, alpha=0.7, color='blue', edgecolor='black')\n",
"plt.axvline(threshold_ae1, color='red', linestyle='--', linewidth=2, \n",
" label=f'Порог: {threshold_ae1:.6f}')\n",
"plt.title('AE1: Распределение ошибок реконструкции')\n",
"plt.xlabel('Ошибка реконструкции')\n",
"plt.ylabel('Частота')\n",
"plt.legend()\n",
"plt.grid(True, alpha=0.3)\n",
"\n",
"plt.tight_layout()\n",
"plt.savefig('out/ae1_results.png', dpi=300, bbox_inches='tight')\n",
"plt.show()\n",
"\n",
"print(f\"Порог ошибки реконструкции AE1: {threshold_ae1:.6f}\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 3. Создание и обучение автокодировщика AE2 (усложненная архитектура)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def create_autoencoder_ae2(input_dim):\n",
" \"\"\"Создание автокодировщика AE2 с усложненной архитектурой\"\"\"\n",
" model = Sequential()\n",
" \n",
" # Входной слой\n",
" model.add(Dense(input_dim, input_shape=(input_dim,)))\n",
" model.add(Activation('tanh'))\n",
" \n",
" # Скрытые слои (усложненная архитектура)\n",
" model.add(Dense(4))\n",
" model.add(Activation('tanh'))\n",
" model.add(Dense(2))\n",
" model.add(Activation('tanh'))\n",
" model.add(Dense(1)) # сжатие до 1 нейрона\n",
" model.add(Activation('tanh'))\n",
" model.add(Dense(2))\n",
" model.add(Activation('tanh'))\n",
" model.add(Dense(4))\n",
" model.add(Activation('tanh'))\n",
" \n",
" # Выходной слой\n",
" model.add(Dense(input_dim))\n",
" model.add(Activation('linear'))\n",
" \n",
" return model\n",
"\n",
"# Создание AE2\n",
"ae2 = create_autoencoder_ae2(2)\n",
"ae2.compile(loss='mse', optimizer=Adam(learning_rate=0.001))\n",
"\n",
"print(\"Архитектура AE2:\")\n",
"ae2.summary()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Обучение AE2 \n",
"print(\"\\nОбучение AE2 (100 эпох)...\")\n",
"history_ae2 = ae2.fit(X_synthetic, X_synthetic, \n",
" epochs=100, \n",
" batch_size=32, \n",
" validation_split=0.2,\n",
" verbose=1)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Анализ результатов AE2\n",
"print(\"\\nАнализ результатов AE2\")\n",
"mse_ae2 = history_ae2.history['loss'][-1]\n",
"print(f\"Финальная ошибка MSE AE2: {mse_ae2:.6f}\")\n",
"\n",
"# Построение графика ошибки реконструкции\n",
"plt.figure(figsize=(12, 4))\n",
"plt.subplot(1, 2, 1)\n",
"plt.plot(history_ae2.history['loss'], label='Training Loss')\n",
"plt.plot(history_ae2.history['val_loss'], label='Validation Loss')\n",
"plt.title('AE2: Ошибка обучения')\n",
"plt.xlabel('Эпоха')\n",
"plt.ylabel('MSE')\n",
"plt.legend()\n",
"plt.grid(True, alpha=0.3)\n",
"\n",
"# Вычисление ошибок реконструкции для обучающих данных\n",
"X_pred_ae2 = ae2.predict(X_synthetic)\n",
"reconstruction_errors_ae2 = np.mean(np.square(X_synthetic - X_pred_ae2), axis=1)\n",
"threshold_ae2 = np.percentile(reconstruction_errors_ae2, 95)\n",
"\n",
"plt.subplot(1, 2, 2)\n",
"plt.hist(reconstruction_errors_ae2, bins=20, alpha=0.7, color='green', edgecolor='black')\n",
"plt.axvline(threshold_ae2, color='red', linestyle='--', linewidth=2, \n",
" label=f'Порог: {threshold_ae2:.6f}')\n",
"plt.title('AE2: Распределение ошибок реконструкции')\n",
"plt.xlabel('Ошибка реконструкции')\n",
"plt.ylabel('Частота')\n",
"plt.legend()\n",
"plt.grid(True, alpha=0.3)\n",
"\n",
"plt.tight_layout()\n",
"plt.savefig('out/ae2_results.png', dpi=300, bbox_inches='tight')\n",
"plt.show()\n",
"\n",
"print(f\"Порог ошибки реконструкции AE2: {threshold_ae2:.6f}\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 4. Расчет характеристик качества обучения EDCA\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Используем функции из lab02_lib для расчета EDCA\n",
"try:\n",
" # Сохраняем данные для использования с lab02_lib\n",
" np.savetxt('data.txt', X_synthetic)\n",
" \n",
" # Создаем и обучаем AE1 через lab02_lib\n",
" ae1_lib, ire_array_ae1, ire_th_ae1 = lib.create_fit_save_ae(\n",
" X_synthetic, 'out/ae1_model.h5', 'out/ire_ae1.txt', \n",
" epochs=20, verbose_show=False, patience=5\n",
" )\n",
" \n",
" # Создаем и обучаем AE2 через lab02_lib\n",
" ae2_lib, ire_array_ae2, ire_th_ae2 = lib.create_fit_save_ae(\n",
" X_synthetic, 'out/ae2_model.h5', 'out/ire_ae2.txt', \n",
" epochs=100, verbose_show=False, patience=10\n",
" )\n",
" \n",
" # Расчет характеристик EDCA\n",
" xx, yy, Z1 = lib.square_calc(20, X_synthetic, ae1_lib, ire_th_ae1, 1, visual=True)\n",
" xx, yy, Z2 = lib.square_calc(20, X_synthetic, ae2_lib, ire_th_ae2, 2, visual=True)\n",
" \n",
" print(\"Характеристики EDCA рассчитаны и визуализированы\")\n",
" \n",
"except Exception as e:\n",
" print(f\"Ошибка при расчете EDCA: {e}\")\n",
" print(\"Продолжаем без EDCA анализа\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 5. Создание тестовой выборки и применение автокодировщиков\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Создаем тестовые точки, которые AE1 распознает как норму, а AE2 как аномалии\n",
"test_points = np.array([\n",
" [1.2, 1.2], # близко к центру\n",
" [1.5, 1.5], # немного дальше\n",
" [0.8, 0.8], # с другой стороны\n",
" [1.1, 0.9] # асимметрично\n",
"])\n",
"\n",
"print(\"Тестовые точки:\")\n",
"for i, point in enumerate(test_points):\n",
" print(f\" Точка {i+1}: {point}\")\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Применение автокодировщиков к тестовым данным\n",
"print(\"\\nПрименение автокодировщиков к тестовым данным\")\n",
"\n",
"# Предсказания AE1\n",
"test_pred_ae1 = ae1.predict(test_points)\n",
"test_errors_ae1 = np.mean(np.square(test_points - test_pred_ae1), axis=1)\n",
"\n",
"# Предсказания AE2\n",
"test_pred_ae2 = ae2.predict(test_points)\n",
"test_errors_ae2 = np.mean(np.square(test_points - test_pred_ae2), axis=1)\n",
"\n",
"print(\"\\nРезультаты для тестовых точек:\")\n",
"print(\"Точка | AE1 ошибка | AE1 статус | AE2 ошибка | AE2 статус\")\n",
"print(\"-\" * 55)\n",
"for i in range(len(test_points)):\n",
" ae1_status = \"Норма\" if test_errors_ae1[i] <= threshold_ae1 else \"Аномалия\"\n",
" ae2_status = \"Норма\" if test_errors_ae2[i] <= threshold_ae2 else \"Аномалия\"\n",
" print(f\"{i+1:5d} | {test_errors_ae1[i]:10.6f} | {ae1_status:10s} | {test_errors_ae2[i]:10.6f} | {ae2_status:10s}\")\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Визуализация результатов\n",
"plt.figure(figsize=(15, 5))\n",
"\n",
"# График ошибок AE1\n",
"plt.subplot(1, 3, 1)\n",
"plt.scatter(range(len(test_errors_ae1)), test_errors_ae1, c='blue', s=100)\n",
"plt.axhline(threshold_ae1, color='red', linestyle='--', linewidth=2, \n",
" label=f'Порог AE1: {threshold_ae1:.6f}')\n",
"plt.title('AE1: Ошибки тестовых точек')\n",
"plt.xlabel('Номер точки')\n",
"plt.ylabel('Ошибка реконструкции')\n",
"plt.legend()\n",
"plt.grid(True, alpha=0.3)\n",
"\n",
"# График ошибок AE2\n",
"plt.subplot(1, 3, 2)\n",
"plt.scatter(range(len(test_errors_ae2)), test_errors_ae2, c='green', s=100)\n",
"plt.axhline(threshold_ae2, color='red', linestyle='--', linewidth=2, \n",
" label=f'Порог AE2: {threshold_ae2:.6f}')\n",
"plt.title('AE2: Ошибки тестовых точек')\n",
"plt.xlabel('Номер точки')\n",
"plt.ylabel('Ошибка реконструкции')\n",
"plt.legend()\n",
"plt.grid(True, alpha=0.3)\n",
"\n",
"# Визуализация в пространстве признаков\n",
"plt.subplot(1, 3, 3)\n",
"plt.scatter(X_synthetic[:, 0], X_synthetic[:, 1], c='lightblue', alpha=0.5, s=30, label='Обучающие данные')\n",
"plt.scatter(test_points[:, 0], test_points[:, 1], c='red', s=100, marker='s', label='Тестовые точки')\n",
"plt.scatter(center_coords[0], center_coords[1], c='black', s=200, marker='x', linewidth=3, label='Центр')\n",
"plt.title('Пространство признаков')\n",
"plt.xlabel('X1')\n",
"plt.ylabel('X2')\n",
"plt.legend()\n",
"plt.grid(True, alpha=0.3)\n",
"\n",
"plt.tight_layout()\n",
"plt.savefig('out/test_results.png', dpi=300, bbox_inches='tight')\n",
"plt.show()\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# ЗАДАНИЕ 2: Работа с реальными данными Letter\n",
"\n",
"## 1. Изучение и загрузка набора данных Letter\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Изучение набора данных Letter\n",
"print(\"Изучение набора данных Letter\")\n",
"print(\"Набор данных Letter содержит характеристики букв алфавита\")\n",
"\n",
"# Загрузка обучающей выборки\n",
"print(\"\\nЗагрузка обучающей выборки\")\n",
"X_letter_train = np.loadtxt('data/letter_train.txt')\n",
"print(f\"Размерность обучающей выборки: {X_letter_train.shape}\")\n",
"print(f\"Количество признаков: {X_letter_train.shape[1]}\")\n",
"print(f\"Количество образцов: {X_letter_train.shape[0]}\")\n",
"\n",
"# Вывод данных в консоль\n",
"print(\"\\nПервые 5 строк данных:\")\n",
"print(X_letter_train[:5])\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 2. Создание и обучение автокодировщика для Letter\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def create_letter_autoencoder(input_dim):\n",
" \"\"\"Создание автокодировщика для данных Letter\"\"\"\n",
" model = Sequential()\n",
" \n",
" # Входной слой\n",
" model.add(Dense(input_dim, input_shape=(input_dim,)))\n",
" model.add(Activation('tanh'))\n",
" \n",
" # Скрытые слои\n",
" model.add(Dense(16))\n",
" model.add(Activation('tanh'))\n",
" model.add(Dense(8))\n",
" model.add(Activation('tanh'))\n",
" model.add(Dense(4)) # сжатие до 4 нейронов\n",
" model.add(Activation('tanh'))\n",
" model.add(Dense(8))\n",
" model.add(Activation('tanh'))\n",
" model.add(Dense(16))\n",
" model.add(Activation('tanh'))\n",
" \n",
" # Выходной слой\n",
" model.add(Dense(input_dim))\n",
" model.add(Activation('linear'))\n",
" \n",
" return model\n",
"\n",
"# Нормализация данных\n",
"scaler_letter = StandardScaler()\n",
"X_letter_train_scaled = scaler_letter.fit_transform(X_letter_train)\n",
"\n",
"# Создание модели\n",
"ae_letter = create_letter_autoencoder(X_letter_train.shape[1])\n",
"ae_letter.compile(loss='mse', optimizer=Adam(learning_rate=0.001))\n",
"\n",
"print(\"Архитектура автокодировщика для Letter:\")\n",
"ae_letter.summary()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Обучение автокодировщика для Letter\n",
"print(\"\\nОбучение автокодировщика для Letter (50 эпох)...\")\n",
"history_letter = ae_letter.fit(X_letter_train_scaled, X_letter_train_scaled,\n",
" epochs=50,\n",
" batch_size=32,\n",
" validation_split=0.2,\n",
" verbose=1)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Анализ результатов обучения\n",
"print(\"\\nАнализ результатов обучения\")\n",
"mse_letter = history_letter.history['loss'][-1]\n",
"print(f\"Финальная ошибка MSE: {mse_letter:.6f}\")\n",
"\n",
"# Построение графика ошибки обучения\n",
"plt.figure(figsize=(12, 4))\n",
"plt.subplot(1, 2, 1)\n",
"plt.plot(history_letter.history['loss'], label='Training Loss')\n",
"plt.plot(history_letter.history['val_loss'], label='Validation Loss')\n",
"plt.title('Letter: Ошибка обучения')\n",
"plt.xlabel('Эпоха')\n",
"plt.ylabel('MSE')\n",
"plt.legend()\n",
"plt.grid(True, alpha=0.3)\n",
"\n",
"# Вычисление ошибок реконструкции\n",
"X_letter_pred = ae_letter.predict(X_letter_train_scaled)\n",
"reconstruction_errors_letter = np.mean(np.square(X_letter_train_scaled - X_letter_pred), axis=1)\n",
"threshold_letter = np.percentile(reconstruction_errors_letter, 95)\n",
"\n",
"plt.subplot(1, 2, 2)\n",
"plt.hist(reconstruction_errors_letter, bins=50, alpha=0.7, color='purple', edgecolor='black')\n",
"plt.axvline(threshold_letter, color='red', linestyle='--', linewidth=2, \n",
" label=f'Порог: {threshold_letter:.6f}')\n",
"plt.title('Letter: Распределение ошибок реконструкции')\n",
"plt.xlabel('Ошибка реконструкции')\n",
"plt.ylabel('Частота')\n",
"plt.legend()\n",
"plt.grid(True, alpha=0.3)\n",
"\n",
"plt.tight_layout()\n",
"plt.savefig('out/letter_training_results.png', dpi=300, bbox_inches='tight')\n",
"plt.show()\n",
"\n",
"print(f\"Порог ошибки реконструкции: {threshold_letter:.6f}\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 3. Оценка пригодности автокодировщика\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Оценка пригодности автокодировщика\n",
"print(\"\\nОценка пригодности автокодировщика\")\n",
"anomalies_train = np.sum(reconstruction_errors_letter > threshold_letter)\n",
"anomaly_rate_train = anomalies_train / len(reconstruction_errors_letter) * 100\n",
"print(f\"Обнаружено аномалий в обучающей выборке: {anomalies_train} ({anomaly_rate_train:.1f}%)\")\n",
"\n",
"if anomaly_rate_train > 10:\n",
" print(\"Порог слишком высокий, требуется корректировка параметров\")\n",
"else:\n",
" print(\"Автокодировщик подходит для обнаружения аномалий\")\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## 4. Загрузка тестовой выборки и применение к ней\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Загрузка тестовой выборки\n",
"print(\"\\nЗагрузка тестовой выборки\")\n",
"X_letter_test = np.loadtxt('data/letter_test.txt')\n",
"print(f\"Размерность тестовой выборки: {X_letter_test.shape}\")\n",
"\n",
"# Применение к тестовой выборке\n",
"print(\"\\nПрименение к тестовой выборке\")\n",
"X_letter_test_scaled = scaler_letter.transform(X_letter_test)\n",
"X_letter_test_pred = ae_letter.predict(X_letter_test_scaled)\n",
"test_errors_letter = np.mean(np.square(X_letter_test_scaled - X_letter_test_pred), axis=1)\n",
"\n",
"# Определение аномалий\n",
"test_anomalies = test_errors_letter > threshold_letter\n",
"n_anomalies = np.sum(test_anomalies)\n",
"anomaly_rate = n_anomalies / len(test_errors_letter) * 100\n",
"\n",
"print(f\"Обнаружено аномалий в тестовой выборке: {n_anomalies} из {len(test_errors_letter)} ({anomaly_rate:.1f}%)\")\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# Визуализация результатов\n",
"plt.figure(figsize=(12, 4))\n",
"plt.subplot(1, 2, 1)\n",
"plt.hist(test_errors_letter, bins=30, alpha=0.7, color='orange', edgecolor='black')\n",
"plt.axvline(threshold_letter, color='red', linestyle='--', linewidth=2, \n",
" label=f'Порог: {threshold_letter:.6f}')\n",
"plt.title('Letter: Ошибки тестовой выборки')\n",
"plt.xlabel('Ошибка реконструкции')\n",
"plt.ylabel('Частота')\n",
"plt.legend()\n",
"plt.grid(True, alpha=0.3)\n",
"\n",
"plt.subplot(1, 2, 2)\n",
"plt.scatter(range(len(test_errors_letter)), test_errors_letter, \n",
" c=test_anomalies, cmap='RdYlBu_r', alpha=0.7)\n",
"plt.axhline(threshold_letter, color='red', linestyle='--', linewidth=2, \n",
" label=f'Порог: {threshold_letter:.6f}')\n",
"plt.title('Letter: Ошибки по образцам')\n",
"plt.xlabel('Номер образца')\n",
"plt.ylabel('Ошибка реконструкции')\n",
"plt.legend()\n",
"plt.grid(True, alpha=0.3)\n",
"\n",
"plt.tight_layout()\n",
"plt.savefig('out/letter_test_results.png', dpi=300, bbox_inches='tight')\n",
"plt.show()\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# ИТОГОВЫЕ РЕЗУЛЬТАТЫ\n",
"\n",
"## Таблица результатов\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"s# Итоговые результаты\n",
"print(\"\\n\" + \"=\"*70)\n",
"print(\"ИТОГОВЫЕ РЕЗУЛЬТАТЫ\")\n",
"print(\"=\"*70)\n",
"\n",
"print(\"\\nТаблица 1 - Результаты задания №1:\")\n",
"print(\"Модель | Скрытые слои | Нейроны | Эпохи | MSE_stop | Порог | Аномалии\")\n",
"print(\"-\" * 70)\n",
"print(f\"AE1 | 1 | 1 | 20 | {mse_ae1:.6f} | {threshold_ae1:.6f} | -\")\n",
"print(f\"AE2 | 6 | 4-2-1-2-4 | 100 | {mse_ae2:.6f} | {threshold_ae2:.6f} | -\")\n",
"\n",
"print(\"\\nТаблица 2 - Результаты задания №2:\")\n",
"print(\"Dataset | Скрытые слои | Нейроны | Эпохи | MSE_stop | Порог | % аномалий\")\n",
"print(\"-\" * 70)\n",
"print(f\"Letter | 6 | 16-8-4-8-16 | 50 | {mse_letter:.6f} | {threshold_letter:.6f} | {anomaly_rate:.1f}%\")\n",
"\n",
"print(\"\\nВыводы:\")\n",
"print(\"1. AE2 показал лучшие результаты благодаря более сложной архитектуре\")\n",
"print(\"2. Для данных Letter автокодировщик успешно обнаруживает аномалии\")\n",
"print(\"3. Порог 95-го перцентиля обеспечивает разумный баланс между точностью и полнотой\")\n",
"\n",
"print(\"\\nЛабораторная работа завершена!\")\n"
]
}
],
"metadata": {
"language_info": {
"name": "python"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

Двоичные данные
labworks/LW2/out/.DS_Store поставляемый

Двоичный файл не отображается.

Двоичные данные
labworks/LW2/out/AE1.h5

Двоичный файл не отображается.

Двоичные данные
labworks/LW2/out/AE1_AE2_train_def.png

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 21 KiB

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 22 KiB

Двоичные данные
labworks/LW2/out/AE1_train_def.png

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 34 KiB

Двоичные данные
labworks/LW2/out/AE2.h5

Двоичный файл не отображается.

Двоичные данные
labworks/LW2/out/AE2_train_def.png

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 31 KiB

Двоичные данные
labworks/LW2/out/IRE_testAE1.png

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 42 KiB

Двоичные данные
labworks/LW2/out/IRE_testAE2.png

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 34 KiB

Двоичные данные
labworks/LW2/out/IRE_testLetter_AE.png

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 74 KiB

Двоичные данные
labworks/LW2/out/IRE_trainingAE1.png

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 110 KiB

Двоичные данные
labworks/LW2/out/IRE_trainingAE2.png

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 104 KiB

Двоичные данные
labworks/LW2/out/IRE_trainingLetter_AE.png

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 93 KiB

Двоичные данные
labworks/LW2/out/Letter_AE.h5

Двоичный файл не отображается.

Двоичные данные
labworks/LW2/out/Letter_AE_norm.h5

Двоичный файл не отображается.

Двоичные данные
labworks/LW2/out/Letter_AE_raw.h5

Двоичный файл не отображается.

Двоичные данные
labworks/LW2/out/XtXd_1.png

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 74 KiB

Двоичные данные
labworks/LW2/out/XtXd_1_metrics.png

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 88 KiB

Двоичные данные
labworks/LW2/out/XtXd_2.png

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 63 KiB

Двоичные данные
labworks/LW2/out/XtXd_2_metrics.png

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 76 KiB

Двоичные данные
labworks/LW2/out/ae1_results.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 194 KiB

Двоичные данные
labworks/LW2/out/ae2_results.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 180 KiB

Двоичные данные
labworks/LW2/out/letter_test_results.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 273 KiB

Двоичные данные
labworks/LW2/out/letter_training_results.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 175 KiB

@ -0,0 +1,5 @@
------------Оценка качества AE2 С ПОМОЩЬЮ НОВЫХ МЕТРИК------------
Approx = 0.7142857142857142
Excess = 0.4
Deficit = 0.0
Coating = 1.0

Двоичные данные
labworks/LW2/out/synthetic_data.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 173 KiB

Двоичные данные
labworks/LW2/out/test_results.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 273 KiB

Двоичные данные
labworks/LW2/out/train_set.png

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 44 KiB

@ -0,0 +1,435 @@
# Отчёт по лабораторной работе №2
## Обнаружение аномалий с помощью автокодировщиков
**Выполнили:** Троянов Даниил Сергеевич, Чернов Данила Евгеньевич
**Группа:** А-01-22
**Бригада:** 1
---
## Задание 1
### 1) Подготовка рабочей среды и импорт библиотек
В среде Jupyter Notebook был создан новый блокнот для выполнения лабораторной работы. Импортированы необходимые библиотеки и модули для работы с данными и нейронными сетями.
```python
import os
import numpy as np
import matplotlib.pyplot as plt
import lab02_lib as lib
from unittest.mock import patch
import builtins
# Создаем директорию для выходных файлов
os.makedirs('out', exist_ok=True)
```
### 2) Генерация синтетического набора двумерных данных
Сгенерирован индивидуальный набор двумерных данных в пространстве признаков с координатами центра (1, 1), где 1 – номер бригады. Данные представлены в виде кластера из 1000 точек.
```python
# Генерация датасета для бригады 1
brigade_num = 1
data = lib.datagen(brigade_num, brigade_num, 1000, 2)
```
Размерность данных: (1000, 2)
![Обучающая выборка](out/train_set.png)
### 3) Создание и обучение автокодировщика AE1 с простой архитектурой
Создан первый автокодировщик AE1 с минимальной архитектурой, состоящей из одного скрытого слоя с одним нейроном. Обучение проводилось в течение 1000 эпох.
```python
# Обучение AE1
with patch('builtins.input', side_effect=['1', '1', '1']):
ae1_trained, IRE1, IREth1 = lib.create_fit_save_ae(
data,
'out/AE1.h5',
'out/AE1_ire_th.txt',
1000, # epochs
True, # verbose_show
200, # patience
)
```
Архитектура: 2 → 1 → 2 (1 скрытый слой с 1 нейроном)
### 4) Анализ результатов обучения AE1
Зафиксирована ошибка MSE, на которой завершилось обучение. Построен график ошибки реконструкции обучающей выборки. Определён порог ошибки реконструкции для обнаружения аномалий.
Ошибка MSE_AE1 ≈ 0.025
```python
# Построение графика ошибки реконструкции
lib.ire_plot('training', IRE1, IREth1, 'AE1')
```
![График ошибки реконструкции для обучающей выборки, AE1](out/IRE_trainingAE1.png)
Порог ошибки реконструкции (IREth1) ≈ 0.56
### 5) Создание и обучение автокодировщика AE2 с усложнённой архитектурой
Создан второй автокодировщик AE2 с более сложной архитектурой, включающей 7 скрытых слоёв. Обучение проводилось в течение 3000 эпох для достижения лучшего качества аппроксимации области нормальных данных.
```python
# Обучение AE2
with patch('builtins.input', side_effect=['1', '7', '8 4 2 1 2 4 8']):
ae2_trained, IRE2, IREth2 = lib.create_fit_save_ae(
data,
'out/AE2.h5',
'out/AE2_ire_th.txt',
3000, # epochs
True, # verbose_show
200, # patience
)
# Вычисление MSE из предсказания
X_pred_ae2 = ae2_trained.predict(data, verbose=0)
mse_ae2 = np.mean((data - X_pred_ae2) ** 2)
print(f'\nОбучение завершено!')
print(f'MSE_stop (приблизительно): {mse_ae2:.6f}')
print(f'Порог IRE: {IREth2:.6f}')
print(f'Количество скрытых слоев: 7')
print(f'Количество нейронов в скрытых слоях: 8-4-2-1-2-4-8')
```
Архитектура: 2 → 8 → 4 → 2 → 1 → 2 → 4 → 8 → 2 (7 скрытых слоёв: 8-4-2-1-2-4-8)
### 6) Анализ результатов обучения AE2
Зафиксирована ошибка MSE, на которой завершилось обучение. Построен график ошибки реконструкции обучающей выборки. Определён порог ошибки реконструкции для обнаружения аномалий.
Ошибка MSE_AE2 ≈ 0.0098
```python
# Построение графика ошибки реконструкции
lib.ire_plot('training', IRE2, IREth2, 'AE2')
```
![График ошибки реконструкции для обучающей выборки, AE2](out/IRE_trainingAE2.png)
Порог ошибки реконструкции (IREth2) ≈ 0.38
### 7) Оценка качества обучения с помощью метрик EDCA
Рассчитаны характеристики качества обучения EDCA (Excess, Deficit, Coating, Approx) для обоих автокодировщиков. Визуализированы и сравнены области пространства признаков, распознаваемые автокодировщиками AE1 и AE2.
```python
# Расчет характеристик качества обучения для AE1
numb_square = 20
xx, yy, Z1 = lib.square_calc(numb_square, data, ae1_trained, IREth1, '1', True)
```
![Области покрытия и границы классов для AE1](out/AE1_train_def.png)
![Площади множеств Xt и Xd для AE1](out/XtXd_1.png)
![Метрики EDCA для AE1](out/XtXd_1_metrics.png)
Результаты для AE1:
- Excess ≈ 5.75
- Approx ≈ 0.148
```python
# Расчет характеристик качества обучения для AE2
xx, yy, Z2 = lib.square_calc(numb_square, data, ae2_trained, IREth2, '2', True)
```
![Области покрытия и границы классов для AE2](out/AE2_train_def.png)
![Площади множеств Xt и Xd для AE2](out/XtXd_2.png)
![Метрики EDCA для AE2](out/XtXd_2_metrics.png)
Результаты для AE2:
- Excess ≈ 0.4
- Approx ≈ 0.714
```python
# Сравнение областей аппроксимации
lib.plot2in1(data, xx, yy, Z1, Z2)
```
![Сравнение областей аппроксимации AE1 и AE2](out/AE1_AE2_train_def.png)
### 8) Оценка пригодности автокодировщиков
Полученные показатели EDCA для автокодировщика AE2 демонстрируют значительно лучшее качество аппроксимации по сравнению с AE1. Значение Approx для AE2 (0.714) существенно выше, чем для AE1 (0.148), что указывает на более точную аппроксимацию области обучающих данных. Значение Excess для AE2 (0.4) значительно ниже, чем для AE1 (5.75), что подтверждает более качественное обучение модели AE2.
### 9) Подготовка тестовой выборки
Создана тестовая выборка, состоящая из 4 элементов, не входящих в обучающую выборку. Элементы подобраны таким образом, чтобы AE1 распознавал их как норму, а AE2 обнаруживал как аномалии.
```python
# Тестовая выборка
data_test = np.array([[2.2, 1.2], [1.2, 2.2], [0.2, 1.2], [1.2, 0.2]])
```
### 10) Применение автокодировщиков к тестовым данным
Применены обученные автокодировщики AE1 и AE2 к тестовым данным. Выведены значения ошибки реконструкции для каждого элемента тестовой выборки относительно порога на график и в консоль.
```python
# Тестирование AE1
predicted_labels1, ire1 = lib.predict_ae(ae1_trained, data_test, IREth1)
lib.anomaly_detection_ae(predicted_labels1, ire1, IREth1)
lib.ire_plot('test', ire1, IREth1, 'AE1')
```
![График ошибки реконструкции для тестовой выборки, AE1](out/IRE_testAE1.png)
Результат для AE1: аномалий не обнаружено (0 из 4)
```python
# Тестирование AE2
predicted_labels2, ire2 = lib.predict_ae(ae2_trained, data_test, IREth2)
lib.anomaly_detection_ae(predicted_labels2, ire2, IREth2)
lib.ire_plot('test', ire2, IREth2, 'AE2')
```
![График ошибки реконструкции для тестовой выборки, AE2](out/IRE_testAE2.png)
Результат для AE2: обнаружено 4 аномалии из 4
### 11) Визуализация результатов тестирования
Визуализированы элементы обучающей и тестовой выборки в областях пространства признаков, распознаваемых автокодировщиками AE1 и AE2.
```python
# Построение областей аппроксимации и точек тестового набора
lib.plot2in1_anomaly(data, xx, yy, Z1, Z2, data_test)
```
![Визуализация тестовых точек в областях аппроксимации AE1 и AE2](out/AE1_AE2_train_def_anomalies.png)
### 12) Сводная таблица результатов задания №1
**Табл. 1 Результаты задания №1**
| | Количество<br>скрытых слоев | Количество<br>нейронов в скрытых слоях | Количество<br>эпох обучения | Ошибка<br>MSE_stop | Порог ошибки<br>реконструкции | Значение показателя<br>Excess | Значение показателя<br>Approx | Количество обнаруженных<br>аномалий |
|-----:|------------------------------|----------------------------------------|-----------------------------|--------------------|-------------------------------|-------------------------------|--------------------------------|-------------------------------------|
| AE1 | 1 | 1 | 1000 | 0.025 | 0.56 | 5.75 | 0.148 | 0 |
| AE2 | 7 | 8-4-2-1-2-4-8 | 3000 | 0.0098 | 0.38 | 0.4 | 0.714 | 4 |
### 13) Выводы по заданию 1
На основе проведённых экспериментов сформированы следующие выводы о требованиях к параметрам автокодировщиков для качественного обнаружения аномалий:
**Требования к данным для обучения:**
- Данные для обучения должны быть без аномалий, чтобы автокодировщик смог рассчитать верное пороговое значение ошибки реконструкции.
**Требования к архитектуре автокодировщика:**
- Архитектура должна постепенно сужаться к бутылочному горлышку (bottleneck), а затем постепенно возвращаться к исходным выходным размерам.
- Для двумерных данных оптимальное количество скрытых слоёв: 3-6.
- Простая архитектура с одним скрытым слоем показывает низкое качество аппроксимации (Excess = 5.75, Approx = 0.148), что подтверждается результатами AE1.
**Требования к количеству эпох обучения:**
- Для простых данных (2D): 1000-3000 эпох.
- При использовании ранней остановки (early stopping) обучение может завершиться раньше при достижении оптимальных значений.
**Требования к ошибке MSE_stop:**
- Для двумерных данных: оптимальная ошибка MSE-stop в районе 0.01.
- Более высокие значения (например, 0.025) указывают на недостаточное качество обучения и требуют улучшения архитектуры или увеличения количества эпох.
**Требования к порогу обнаружения аномалий:**
- Для двумерных данных: значение порога в районе 0.4-2.5.
- Более высокие пороги (например, 0.56) могут пропускать аномалии.
**Требования к характеристикам качества обучения EDCA:**
- Значение Excess должно быть как можно ближе к 0. Для AE2 получено 0.4, что является приемлемым.
- Значение Deficit должно быть как можно ближе к 0
- Значение Coating должно быть как можно ближе к 1
- Значение Approx должно быть как можно ближе к 1. Для AE2 получено 0.714, что соответствует требованиям.
---
## Задание 2
### 1) Изучение набора реальных данных Letter
Бригада 1 работает с набором данных **Letter**. Это реальный набор данных, представляющий собой характеристики букв латинского алфавита, извлечённые из изображений. Каждый пример содержит 32 признака, описывающих различные геометрические и статистические характеристики буквы.
| Параметр | Значение |
|----------|----------|
| Количество признаков | 32 |
| Количество примеров в обучающей выборке | 1500 |
| Количество примеров в тестовой выборке | 500 |
### 2) Загрузка многомерной обучающей выборки
Загружена многомерная обучающая выборка реальных данных Letter.
```python
# Загрузка обучающей и тестовой выборки Letter
train_letter = np.loadtxt('data/letter_train.txt', dtype=float)
test_letter = np.loadtxt('data/letter_test.txt', dtype=float)
```
### 3) Вывод данных и их размерности
Выведены полученные данные и их размерность в консоль.
```python
print('Обучающая выборка Letter:')
print(f'Размерность: {train_letter.shape}')
print(f'Количество признаков: {train_letter.shape[1]}')
print(f'Количество примеров: {train_letter.shape[0]}')
```
Размерность обучающей выборки: (1500, 32)
Размерность тестовой выборки: (500, 32)
### 4) Нормализация данных (для сравнения результатов)
Для сравнения результатов обучения создана нормализованная версия данных с использованием StandardScaler.
```python
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
train_letter_normalized = scaler.fit_transform(train_letter)
test_letter_normalized = scaler.transform(test_letter)
```
### 5) Обучение автокодировщика на ненормализованных данных
Создан и обучен автокодировщик с подходящей для данных архитектурой на ненормализованных данных. Выбрано необходимое количество эпох обучения.
```python
# Обучение на ненормализованных данных
with patch('builtins.input', side_effect=['1', '11', '48 36 24 16 8 4 8 16 24 36 48']):
ae_letter_raw, IRE_letter_raw, IREth_letter_raw = lib.create_fit_save_ae(
train_letter,
'out/Letter_AE_raw.h5',
'out/Letter_AE_raw_ire_th.txt',
20000, # epochs
True, # verbose_show
200, # patience
)
```
Архитектура: 32 100 -> 68 -> 48 -> 32 -> 24 -> 16 -> 8 -> 16 -> 24 -> 32 -> 48 -> 64 -> 100 → 32
Количество скрытых слоёв: 17
Нейроны в скрытых слоях: 100 -> 68 -> 48 -> 32 -> 24 -> 16 -> 8 -> 16 -> 24 -> 32 -> 48 -> 64 -> 100
### 6) Обучение автокодировщика на нормализованных данных
Создан и обучен автокодировщик с той же архитектурой на нормализованных данных для сравнения результатов.
```python
# Обучение на нормализованных данных
with patch('builtins.input', side_effect=['1', '11', '48 36 24 16 8 4 8 16 24 36 48']):
ae_letter_norm, IRE_letter_norm, IREth_letter_norm = lib.create_fit_save_ae(
train_letter_normalized,
'out/Letter_AE_norm.h5',
'out/Letter_AE_norm_ire_th.txt',
20000, # epochs
True, # verbose_show
200, # patience
)
```
### 7) Сравнение результатов обучения
Проведено сравнение результатов обучения на нормализованных и ненормализованных данных. Выбрана лучшая модель для дальнейшей работы.
**Сравнение результатов:**
| Параметр | Ненормализованные | Нормализованные |
|----------|-------------------|-----------------|
| MSE_stop | 0.340 | 0.026 |
| Порог IRE | 11.1 | 1.62 |
На основе сравнения результатов выбрана модель, обученная на нормализованных данных, так как она показала значительно лучшее качество (MSE = 0.026 против 0.340) и более низкий порог обнаружения аномалий (1.62 против 11.1).
### 8) Анализ результатов обучения
Зафиксирована ошибка MSE, на которой завершилось обучение. Построен график ошибки реконструкции обучающей выборки. Определён порог ошибки реконструкции для обнаружения аномалий.
```python
# Построение графика ошибки реконструкции
lib.ire_plot('training', IRE_letter, IREth_letter, 'Letter_AE')
```
![График ошибки реконструкции для обучающей выборки Letter](out/IRE_trainingLetter_AE.png)
### 9) Применение автокодировщика к тестовой выборке
Применён обученный автокодировщик к тестовой выборке для обнаружения аномалий. Выведен график ошибки реконструкции элементов тестовой выборки относительно порога.
```python
# Тестирование автокодировщика
predicted_labels_letter, ire_letter_test = lib.predict_ae(ae_letter, test_letter_used, IREth_letter)
lib.anomaly_detection_ae(predicted_labels_letter, ire_letter_test, IREth_letter)
lib.ire_plot('test', ire_letter_test, IREth_letter, 'Letter_AE')
```
![График ошибки реконструкции для тестовой выборки Letter](out/IRE_testLetter_AE.png)
### 10) Сводная таблица результатов задания №2
**Табл. 2 Результаты задания №2**
| Dataset | Количество<br>скрытых слоев | Количество<br>нейронов в скрытых слоях | Количество<br>эпох обучения | Ошибка<br>MSE_stop | Порог ошибки<br>реконструкции | % обнаруженных<br>аномалий |
|:--------|:----------------------------|:---------------------------------------|:----------------------------|:-------------------|:------------------------------|:---------------------------|
| Letter | 11 | 100 -> 68 -> 48 -> 32 -> 24 -> 16 -> 8 -> 16 -> 24 -> 32 -> 48 -> 64 -> 100 | 20000 | 0.026 | 1.62 | 100.0% |
*Примечание: Результаты получены для модели, обученной на нормализованных данных.*
### 11) Выводы по заданию 2
На основе проведённых экспериментов с многомерными данными (32 признака) сформированы следующие выводы:
**Требования к данным для обучения:**
- Данные для обучения должны быть без аномалий, чтобы автокодировщик смог рассчитать верное пороговое значение ошибки реконструкции.
- Для многомерных данных нормализация признаков значительно улучшает сходимость обучения и качество обнаружения аномалий. Как показали эксперименты, нормализация снизила MSE с 0.340 до 0.026 и порог IRE с 11.1 до 1.62.
**Требования к архитектуре автокодировщика:**
- Для многомерных данных требуется более сложная архитектура с большим количеством скрытых слоёв.
- Архитектура должна постепенно сужаться к бутылочному горлышку (bottleneck), а затем постепенно возвращаться к исходным выходным размерам.
- Широкая архитектура с большим количеством нейронов в начальных слоях (например, 100 -> 68 -> 48 -> 32 -> 24 -> 16 -> 8 -> 16 -> 24 -> 32 -> 48 -> 64 -> 100) позволяет лучше аппроксимировать сложные зависимости в многомерных данных.
**Требования к количеству эпох обучения:**
- Для многомерных данных требуется значительно большее количество эпох обучения (до 10000 эпох) по сравнению с двумерными данными (1000-3000 эпох).
- Использование ранней остановки (early stopping) позволяет автоматически завершить обучение при достижении оптимальных значений.
**Требования к ошибке MSE_stop:**
- Для многомерных данных оптимальная ошибка MSE-stop в районе 0.1-0.01.
- Нормализация данных может значительно улучшить качество обучения (пример: MSE снизилась с 0.340 до 0.026 при нормализации).
**Требования к порогу обнаружения аномалий:**
- Для многомерных данных значение порога не больше 1.6.
- Нормализация данных позволяет получить более низкий и стабильный порог (пример: порог снизился с 11.1 до 1.62 при нормализации).
**Требования к характеристикам качества обучения EDCA:**
- Значение Excess должно быть как можно ближе к 0
- Значение Deficit должно быть как можно ближе к 0
- Значение Coating должно быть как можно ближе к 1
- Значение Approx должно быть как можно ближе к 1
---
## Общие выводы
В ходе выполнения лабораторной работы были исследованы возможности использования автокодировщиков для обнаружения аномалий в данных различной размерности. Показано, что качество обнаружения аномалий существенно зависит от архитектуры автокодировщика, количества эпох обучения, правильного выбора порога ошибки реконструкции и предобработки данных (нормализации).
**Основные выводы:**
1. **Влияние размерности данных**: Для многомерных данных требуется более сложная архитектура и большее количество эпох обучения по сравнению с двумерными данными.
2. **Архитектура**: Архитектура должна постепенно сужаться к бутылочному горлышку и постепенно возвращаться к исходным размерам. Симметричная структура обеспечивает лучшее качество аппроксимации.
3. **Параметры обучения**: Оптимальные значения MSE-stop и порога ошибки реконструкции различаются для данных разной размерности. Для двумерных данных MSE-stop ≈ 0.01, порог 0.4-2.5. Для многомерных данных MSE-stop ≈ 0.1-0.01, порог ≤ 1.6.
4. **Метрики качества**: Характеристики EDCA (Excess, Deficit, Coating, Approx) позволяют оценить качество аппроксимации области нормальных данных и пригодность автокодировщика для обнаружения аномалий.
5. **Нормализация данных**: Для многомерных данных нормализация признаков значительно улучшает сходимость обучения и качество обнаружения аномалий. Экспериментально подтверждено, что нормализация снижает MSE с 0.340 до 0.026 и порог IRE с 11.1 до 1.62, что приводит к обнаружению 100% аномалий в тестовой выборке.

@ -1,320 +0,0 @@
# Лабораторная работа №2: Обнаружение аномалий с помощью автокодировщиков
**Троянов Д.С., Чернов Д.Е. — А-01-22**
## Вариант 1 (номер бригады k=1) - данные Letter
### Цель работы
Получить практические навыки создания, обучения и применения искусственных нейронных сетей типа автокодировщик. Исследовать влияние архитектуры автокодировщика и количества эпох обучения на области в пространстве признаков, распознаваемые автокодировщиком после обучения. Научиться оценивать качество обучения автокодировщика на основе ошибки реконструкции и новых метрик EDCA. Научиться решать актуальную задачу обнаружения аномалий в данных с помощью автокодировщика как одноклассового классификатора.
### Определение варианта
- Номер бригады: k = 1
- N = k mod 3 = 1 mod 3 = 1
- Вариант 1 => данные **Letter**
---
## ЗАДАНИЕ 1: Работа с двумерными синтетическими данными
### Блок 1: Импорт библиотек и настройка окружения
```python
import os
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import make_blobs
import tensorflow as tf
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Activation
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.callbacks import EarlyStopping
import lab02_lib as lib
# Параметры для варианта 1 (номер бригады k=1)
k = 1 # номер бригады
center_coords = (k, k) # координаты центра (1, 1)
```
**Описание:** Импортируются необходимые библиотеки и устанавливаются параметры для варианта 1.
### Блок 2: Генерация индивидуального набора двумерных данных
```python
# Генерируем данные с центром в точке (1, 1)
X_synthetic, _ = make_blobs(n_samples=100, centers=[center_coords], n_features=2,
cluster_std=0.5, random_state=42)
print(f"Сгенерировано {len(X_synthetic)} точек")
print(f"Центр данных: {center_coords}")
print(f"Размерность данных: {X_synthetic.shape}")
```
**Результат выполнения:**
```
Сгенерировано 100 точек
Центр данных: (1, 1)
Размерность данных: (100, 2)
```
![Синтетические данные](out/synthetic_data.png)
### Блок 3: Создание и обучение автокодировщика AE1
```python
def create_autoencoder_ae1(input_dim):
"""Создание автокодировщика AE1 с простой архитектурой"""
model = Sequential()
# Входной слой
model.add(Dense(input_dim, input_shape=(input_dim,)))
model.add(Activation('tanh'))
# Скрытые слои (простая архитектура)
model.add(Dense(1)) # сжатие до 1 нейрона
model.add(Activation('tanh'))
# Выходной слой
model.add(Dense(input_dim))
model.add(Activation('linear'))
return model
# Обучение AE1 (20 эпох)
history_ae1 = ae1.fit(X_synthetic, X_synthetic,
epochs=20,
batch_size=32,
validation_split=0.2,
verbose=1)
```
**Описание:** Создается автокодировщик AE1 с простой архитектурой (сжатие до 1 нейрона).
**Результаты обучения AE1:**
- Финальная ошибка MSE: 0.868448
- Порог ошибки реконструкции: 2.153999
![Результаты AE1](out/ae1_results.png)
### Блок 4: Создание и обучение автокодировщика AE2
```python
def create_autoencoder_ae2(input_dim):
"""Создание автокодировщика AE2 с усложненной архитектурой"""
model = Sequential()
# Входной слой
model.add(Dense(input_dim, input_shape=(input_dim,)))
model.add(Activation('tanh'))
# Скрытые слои
model.add(Dense(4))
model.add(Activation('tanh'))
model.add(Dense(2))
model.add(Activation('tanh'))
model.add(Dense(1)) # сжатие до 1 нейрона
model.add(Activation('tanh'))
model.add(Dense(2))
model.add(Activation('tanh'))
model.add(Dense(4))
model.add(Activation('tanh'))
# Выходной слой
model.add(Dense(input_dim))
model.add(Activation('linear'))
return model
# Обучение AE2 (больше эпох)
history_ae2 = ae2.fit(X_synthetic, X_synthetic,
epochs=100,
batch_size=32,
validation_split=0.2,
verbose=1)
```
**Описание:** Создается автокодировщик AE2 с усложненной архитектурой.
**Результаты обучения AE2:**
- Финальная ошибка MSE: 0.207574
- Порог ошибки реконструкции: 0.584772
![Результаты AE2](out/ae2_results.png)
### Блок 5: Создание тестовой выборки
```python
# Создаем тестовые точки, которые AE1 распознает как норму, а AE2 как аномалии
test_points = np.array([
[1.2, 1.2], # близко к центру
[1.5, 1.5], # немного дальше
[0.8, 0.8], # с другой стороны
[1.1, 0.9] # асимметрично
])
```
**Результат выполнения:**
```
Тестовые точки:
Точка 1: [1.2 1.2]
Точка 2: [1.5 1.5]
Точка 3: [0.8 0.8]
Точка 4: [1.1 0.9]
```
### Блок 6: Применение автокодировщиков к тестовым данным
```python
# Предсказания AE1
test_pred_ae1 = ae1.predict(test_points)
test_errors_ae1 = np.mean(np.square(test_points - test_pred_ae1), axis=1)
# Предсказания AE2
test_pred_ae2 = ae2.predict(test_points)
test_errors_ae2 = np.mean(np.square(test_points - test_pred_ae2), axis=1)
```
**Результаты для тестовых точек:**
```
Точка | AE1 ошибка | AE1 статус | AE2 ошибка | AE2 статус
-------------------------------------------------------
1 | 0.924488 | Норма | 0.086807 | Норма
2 | 1.494785 | Норма | 0.352019 | Норма
3 | 0.393357 | Норма | 0.012042 | Норма
4 | 0.556103 | Норма | 0.013652 | Норма
```
![Результаты тестирования](out/test_results.png)
---
## ЗАДАНИЕ 2: Работа с реальными данными Letter
### Блок 7: Загрузка и изучение данных Letter
```python
# Загрузка обучающей выборки
X_letter_train = np.loadtxt('data/letter_train.txt')
print(f"Размерность обучающей выборки: {X_letter_train.shape}")
print(f"Количество признаков: {X_letter_train.shape[1]}")
print(f"Количество образцов: {X_letter_train.shape[0]}")
```
**Описание:** Загружаются данные Letter, которые содержат характеристики букв алфавита.
**Результат выполнения:**
```
Размерность обучающей выборки: (1500, 32)
Количество признаков: 32
Количество образцов: 1500
```
### Блок 8: Создание и обучение автокодировщика для Letter
```python
def create_letter_autoencoder(input_dim):
"""Создание автокодировщика для данных Letter"""
model = Sequential()
# Входной слой
model.add(Dense(input_dim, input_shape=(input_dim,)))
model.add(Activation('tanh'))
# Скрытые слои
model.add(Dense(16))
model.add(Activation('tanh'))
model.add(Dense(8))
model.add(Activation('tanh'))
model.add(Dense(4)) # сжатие до 4 нейронов
model.add(Activation('tanh'))
model.add(Dense(8))
model.add(Activation('tanh'))
model.add(Dense(16))
model.add(Activation('tanh'))
# Выходной слой
model.add(Dense(input_dim))
model.add(Activation('linear'))
return model
# Обучение
history_letter = ae_letter.fit(X_letter_train_scaled, X_letter_train_scaled,
epochs=50,
batch_size=32,
validation_split=0.2,
verbose=1)
```
**Описание:** Создается автокодировщик, подходящей для 32-мерных данных Letter.
**Результаты обучения:**
- Финальная ошибка MSE: 0.371572
- Порог ошибки реконструкции: 0.782392
![Результаты обучения Letter](out/letter_training_results.png)
### Блок 9: Применение к тестовой выборке
```python
# Загрузка тестовой выборки
X_letter_test = np.loadtxt('data/letter_test.txt')
# Применение к тестовой выборке
X_letter_test_scaled = scaler_letter.transform(X_letter_test)
X_letter_test_pred = ae_letter.predict(X_letter_test_scaled)
test_errors_letter = np.mean(np.square(X_letter_test_scaled - X_letter_test_pred), axis=1)
# Определение аномалий
test_anomalies = test_errors_letter > threshold_letter
n_anomalies = np.sum(test_anomalies)
anomaly_rate = n_anomalies / len(test_errors_letter) * 100
```
**Описание:** Применяется обученный автокодировщик к тестовой выборке для обнаружения аномалий.
**Результаты обнаружения аномалий:**
```
Обнаружено аномалий в тестовой выборке: 29 из 100 (29.0%)
```
![Результаты тестирования Letter](out/letter_test_results.png)
---
## ИТОГОВЫЕ РЕЗУЛЬТАТЫ
### Таблица 1 - Результаты задания №1
| Модель | Количество скрытых слоев | Количество нейронов в скрытых слоях | Количество эпох обучения | Ошибка MSE_stop | Порог ошибки реконструкции | Значение показателя Excess | Значение показателя Approx | Количество обнаруженных аномалий |
|--------|--------------------------|-------------------------------------|--------------------------|-----------------|----------------------------|----------------------------|----------------------------|----------------------------------|
| AE1 | 1 | 1 | 20 | 0.868448 | 2.153999 | - | - | - |
| AE2 | 6 | 4-2-1-2-4 | 100 | 0.207574 | 0.584772 | - | - | - |
### Таблица 2 - Результаты задания №2
| Dataset name | Количество скрытых слоев | Количество нейронов в скрытых слоях | Количество эпох обучения | Ошибка MSE_stop | Порог ошибки реконструкции | % обнаруженных аномалий |
|--------------|--------------------------|-------------------------------------|--------------------------|-----------------|----------------------------|-------------------------|
| Letter | 6 | 16-8-4-8-16 | 50 | 0.371572 | 0.782392 | 29.0% |
---
## ВЫВОДЫ
### Требования к данным для обучения:
- Данные должны быть нормализованы для стабильного обучения
- Обучающая выборка должна содержать только нормальные (не аномальные) образцы
- Размер выборки должен быть достаточным для обучения (минимум несколько сотен образцов)
### Требования к архитектуре автокодировщика:
- **Простая архитектура (AE1)**: подходит для простых задач, но может не улавливать сложные зависимости
- **Сложная архитектура (AE2)**: лучше аппроксимирует данные, но требует больше времени на обучение
- Для многомерных данных (32 признака) необходима более глубокая архитектура с постепенным сжатием
### Требования к количеству эпох обучения:
- **AE1 (20 эпох)**: недостаточно для качественного обучения
- **AE2 (100 эпох)**: обеспечивает хорошую сходимость
- Для реальных данных (Letter) достаточно 50 эпох
### Требования к ошибке MSE_stop:
- **AE1**: 0.868448 - слишком высокая, указывает на недообучение
- **AE2**: 0.207574 - приемлемая для синтетических данных
- **Letter**: 0.371572 - хорошая для реальных данных
### Требования к порогу обнаружения аномалий:
- Порог 95-го перцентиля обеспечивает разумный баланс
- **AE1**: 2.153999 - слишком высокий, может пропускать аномалии
- **AE2**: 0.584772 - более чувствительный к аномалиям
- **Letter**: 0.782392 - подходящий для реальных данных
### Характеристики качества обучения EDCA:
- Более сложная архитектура (AE2) показывает лучшие результаты
- Увеличение количества эпох обучения улучшает качество аппроксимации
- Для качественного обнаружения аномалий необходимо тщательно подбирать параметры модели

@ -1,5 +0,0 @@
numpy>=1.21.0
matplotlib>=3.5.0
scikit-learn>=1.0.0
tensorflow>=2.8.0
pandas>=1.3.0
Загрузка…
Отмена
Сохранить