diff --git a/labworks/LW2/LW2_variant2.md b/labworks/LW2/LW2_variant2.md index e69de29..2f9bb77 100644 --- a/labworks/LW2/LW2_variant2.md +++ b/labworks/LW2/LW2_variant2.md @@ -0,0 +1,577 @@ +# Лабораторная работа №2: Обнаружение аномалий с помощью автокодировщиков +**Аникеев А.А; Чагин С.А. — А-02-22** +## Вариант 2 (номер бригады k=5) - данные WBC + +### Цель работы + +Получить практические навыки создания, обучения и применения искусственных нейронных сетей типа автокодировщик. +Исследовать влияние архитектуры автокодировщика и количества эпох обучения на области в пространстве признаков, +распознаваемые автокодировщиком после обучения. Научиться оценивать качество обучения автокодировщика на основе +ошибки реконструкции и новых метрик EDCA. Научиться решать актуальную задачу обнаружения аномалий в данных с +помощью автокодировщика как одноклассового классификатора. + +### Определение варианта + +- Номер бригады: k = 5 +- N = k mod 3 = 5 mod 3 = 2 +- Вариант 2 => данные **WBC** + +### Подготовка среды + +```python +import os +os.chdir('/content/drive/MyDrive/Colab Notebooks') +``` + +```python +from google.colab import drive +drive.mount('/content/drive') +import os +work_dir = '/content/drive/MyDrive/Colab Notebooks/is_lab2' +os.makedirs(work_dir, exist_ok=True) +os.chdir(work_dir) +os.makedirs('out', exist_ok=True) +dataset_name = 'WBC' +base_url = "http://uit.mpei.ru/git/main/is_dnn/raw/branch/main/labworks/LW2/" +!wget -N {base_url}lab02_lib.py +!wget -N {base_url}data/{dataset_name}_train.txt +!wget -N {base_url}data/{dataset_name}_test.txt +!cp {dataset_name}_train.txt train.txt +!cp {dataset_name}_test.txt test.txt +print("Файлы успешно скачаны!") +print("Содержимое рабочей директории:") +!ls -la +``` + +--- + +## ЗАДАНИЕ 1 + +### Пункт №1. Импорт необходимых для работы библиотек и модулей. +```python +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 + +# Параметры для варианта 5 +k = 5 # номер бригады +center_coords = (k, k) # координаты центра (5, 5) +``` + +**Описание:** Импортируем необходимые библиотеки, модули, устанавливаются параметры для варианта 2. + +### Пункт №2. Генерация индивидуального набора двумерных данных в пространстве признаков. +```python + +print("Генерация синтетических данных с центром в (5, 5)...") + +data = lib.datagen(k, k, 1000, 2) + +print(f"Сгенерировано {len(data)} точек") +print(f"Центр данных: {center_coords}") +print(f"Размерность данных: {data.shape}") +``` + +**Результат выполнения:** +``` +Сгенерировано 1000 точек +Центр данных: (5, 5) +Размерность данных: (1000, 2) +``` + +![Результаты](1.png) + +### Пункт №3. Создание и обучение автокодировщика AE1 простой архитектуры. +```python +print("="*50) +print("Обучение AE1") +print("="*50) + +def create_simple_ae(): + model = Sequential() + + model.add(Dense(2, input_shape=(2,), activation='tanh')) + + model.add(Dense(1, activation='tanh')) + + model.add(Dense(2, activation='linear')) + return model + +ae1 = create_simple_ae() +ae1.compile(optimizer=Adam(learning_rate=0.001), loss='mse') + +print("Архитектура AE1:") +ae1.summary() + +print("\nНачало обучения AE1...") +history_ae1 = ae1.fit(data, data, + epochs=1000, + batch_size=32, + validation_split=0.2, + verbose=1, + callbacks=[EarlyStopping(patience=300, restore_best_weights=True)]) + +ae1.save('out/AE1.h5') + +X_pred_ae1 = ae1.predict(data) +reconstruction_errors_ae1 = np.mean(np.square(data - X_pred_ae1), axis=1) +threshold_ae1 = np.max(reconstruction_errors_ae1) + +print("\nАнализ результатов AE1") +mse_ae1 = history_ae1.history['loss'][-1] +print(f"Финальная ошибка MSE AE1: {mse_ae1:.6f}") +print(f"Порог ошибки реконструкции AE1: {threshold_ae1:.6f}") + +plt.figure(figsize=(15, 4)) + +plt.subplot(1, 3, 1) +plt.plot(history_ae1.history['loss'], label='Training Loss', color='blue') +plt.plot(history_ae1.history['val_loss'], label='Validation Loss', color='red') +plt.title('AE1: Ошибка обучения (MSE)') +plt.xlabel('Эпоха') +plt.ylabel('MSE') +plt.legend() +plt.grid(True, alpha=0.3) + +plt.subplot(1, 3, 2) +plt.plot(reconstruction_errors_ae1, 'b-', alpha=0.7, linewidth=0.8) +plt.axhline(y=threshold_ae1, color='red', linestyle='--', linewidth=2, + label=f'Порог: {threshold_ae1:.2f}') +plt.title('AE1: Ошибки реконструкции') +plt.xlabel('Номер точки') +plt.ylabel('Ошибка реконструкции') +plt.legend() +plt.grid(True, alpha=0.3) + +plt.subplot(1, 3, 3) +plt.hist(reconstruction_errors_ae1, bins=20, alpha=0.7, color='blue', edgecolor='black') +plt.axvline(threshold_ae1, color='red', linestyle='--', linewidth=2, + label=f'Порог: {threshold_ae1:.2f}') +plt.title('AE1: Распределение ошибок') +plt.xlabel('Ошибка реконструкции') +plt.ylabel('Частота') +plt.legend() +plt.grid(True, alpha=0.3) + +plt.tight_layout() +plt.savefig('out/ae1_detailed_results.png', dpi=300, bbox_inches='tight') +plt.show() + +with open('out/AE1_ire_th.txt', 'w') as f: + f.write(str(threshold_ae1)) + +ae1_trained = ae1 +IRE1 = reconstruction_errors_ae1 +IREth1 = threshold_ae1 + +print(f"Обучение AE1 завершено!") +print(f"Минимальная ошибка: {np.min(IRE1):.6f}") +print(f"Максимальная ошибка: {np.max(IRE1):.6f}") +print(f"Средняя ошибка: {np.mean(IRE1):.6f}") +``` + +**Описание:** Создается автокодировщик AE1 с простой архитектурой, одним скрытым слоем с одним нейроном. + +**Результаты обучения AE1:** + +Финальная ошибка MSE AE1: 0.009326 + +Порог ошибки реконструкции AE1: 0.067896 + +Минимальная ошибка: 0.000035 + +Максимальная ошибка: 0.067896 + +![Результаты AE1](2.png) + +### Пункт №4. Создание и обучение второго автокодировщика AE2 усложненной архитектуры. + +```python +print("="*50) +print("Обучение AE2") +print("="*50) + +print("Используется архитектура по умолчанию: [2-3-2-1-2-3-2]") +print("Количество скрытых слоев: 5") +print("Нейроны в скрытых слоях: 3-2-1-2-3") + +def create_ae2_default(): + model = Sequential() + + model.add(Dense(2, input_shape=(2,), activation='tanh')) + + model.add(Dense(3, activation='tanh')) + model.add(Dense(2, activation='tanh')) + model.add(Dense(1, activation='tanh')) + model.add(Dense(2, activation='tanh')) + model.add(Dense(3, activation='tanh')) + + model.add(Dense(2, activation='linear')) + + return model + +ae2 = create_ae2_default() +ae2.compile(optimizer=Adam(learning_rate=0.001), loss='mse') + +print("\nАрхитектура AE2:") +ae2.summary() + +print(f"\nНачало обучения AE2 (patience=300)...") +history_ae2 = ae2.fit(data, data, + epochs=3000, + batch_size=32, + validation_split=0.2, + verbose=1, + callbacks=[EarlyStopping(patience=300, restore_best_weights=True)]) + +ae2.save('out/AE2.h5') + +X_pred_ae2 = ae2.predict(data) +reconstruction_errors_ae2 = np.mean(np.square(data - X_pred_ae2), axis=1) +threshold_ae2 = np.max(reconstruction_errors_ae2) + +print("\n" + "="*50) +print("АНАЛИЗ РЕЗУЛЬТАТОВ AE2") +print("="*50) +mse_ae2 = history_ae2.history['loss'][-1] +print(f"Финальная ошибка MSE AE2: {mse_ae2:.6f}") +print(f"Порог ошибки реконструкции AE2: {threshold_ae2:.6f}") + +print("\nПРОВЕРКА РЕКОМЕНДАЦИЙ:") +if mse_ae2 >= 0.01: + print("✓ MSE_stop для AE2 соответствует рекомендации (≥ 0.01)") +else: + print("✗ MSE_stop для AE2 слишком низкая, возможно переобучение") + +plt.figure(figsize=(15, 4)) + +plt.subplot(1, 3, 1) +plt.plot(history_ae2.history['loss'], label='Training Loss', color='green', linewidth=2) +plt.plot(history_ae2.history['val_loss'], label='Validation Loss', color='red', linewidth=2) +plt.title('AE2: Динамика обучения (MSE)', fontsize=12, fontweight='bold') +plt.xlabel('Эпоха') +plt.ylabel('MSE') +plt.legend() +plt.grid(True, alpha=0.3) + +plt.subplot(1, 3, 2) +plt.plot(reconstruction_errors_ae2, 'g-', alpha=0.7, linewidth=0.8) +plt.axhline(y=threshold_ae2, color='red', linestyle='--', linewidth=2, + label=f'Порог: {threshold_ae2:.4f}') +plt.title('AE2: Ошибки реконструкции по точкам', fontsize=12, fontweight='bold') +plt.xlabel('Номер точки') +plt.ylabel('Ошибка реконструкции') +plt.legend() +plt.grid(True, alpha=0.3) + +plt.subplot(1, 3, 3) +plt.hist(reconstruction_errors_ae2, bins=20, alpha=0.7, color='green', edgecolor='black') +plt.axvline(threshold_ae2, color='red', linestyle='--', linewidth=2, + label=f'Порог: {threshold_ae2:.4f}') +plt.title('AE2: Распределение ошибок', fontsize=12, fontweight='bold') +plt.xlabel('Ошибка реконструкции') +plt.ylabel('Частота') +plt.legend() +plt.grid(True, alpha=0.3) + +plt.tight_layout() +plt.savefig('out/ae2_detailed_results.png', dpi=300, bbox_inches='tight') +plt.show() + +with open('out/AE2_ire_th.txt', 'w') as f: + f.write(str(threshold_ae2)) + +ae2_trained = ae2 +IRE2 = reconstruction_errors_ae2 +IREth2 = threshold_ae2 + +print("\nДЕТАЛЬНАЯ СТАТИСТИКА AE2:") +print(f"Минимальная ошибка: {np.min(IRE2):.6f}") +print(f"Максимальная ошибка: {np.max(IRE2):.6f}") +print(f"Средняя ошибка: {np.mean(IRE2):.6f}") +print(f"Медианная ошибка: {np.median(IRE2):.6f}") +print(f"Стандартное отклонение: {np.std(IRE2):.6f}") +print(f"Количество точек с ошибкой выше порога: {np.sum(IRE2 > IREth2)}") +print(f"Процент точек выше порога: {np.sum(IRE2 > IREth2) / len(IRE2) * 100:.2f}%") + +print(f"\nОбучение AE2 завершено!") +print(f"Архитектура: [2-3-2-1-2-3-2]") +print(f"Количество скрытых слоев: 5") +print(f"Нейроны в скрытых слоях: 3-2-1-2-3") +``` + +**Описание:** Создается автокодировщик AE2 с усложненной архитектурой, 5 скрытых слоев с нейронами: 3, 2, 1, 2, 3. + +**Результаты обучения AE2:** + +Финальная ошибка MSE AE2: 0.004915 + +Порог ошибки реконструкции AE2: 0.044551 + +Минимальная ошибка: 0.000000 + +Максимальная ошибка: 0.044551 + +Средняя ошибка: 0.004790 + +Медианная ошибка: 0.002186 + +Стандартное отклонение: 0.006661 + +Количество точек с ошибкой выше порога: 0 + +Процент точек выше порога: 0.00% + +![Результаты AE2](3.png) + + +### Пункт №5. Характеристики качества обучения EDCA. +```python +print("="*70) +print("РАСЧЕТ ХАРАКТЕРИСТИК КАЧЕСТВА ОБУЧЕНИЯ EDCA") +print("="*70) + +numb_square = 20 + +print("\n" + "="*30) +print("РАСЧЕТ ДЛЯ AE1") +print("="*30) +xx, yy, Z1 = lib.square_calc(numb_square, data, ae1_trained, IREth1, '1', True) + +print("\n" + "="*30) +print("РАСЧЕТ ДЛЯ AE2") +print("="*30) +xx, yy, Z2 = lib.square_calc(numb_square, data, ae2_trained, IREth2, '2', True) + +print("\n" + "="*50) +print("СРАВНЕНИЕ ОБЛАСТЕЙ АППРОКСИМАЦИИ AE1 И AE2") +print("="*50) +lib.plot2in1(data, xx, yy, Z1, Z2) +``` + +**Оценка качества AE1:** + +IDEAL = 0. Excess: 0.0 + +IDEAL = 0. Deficit: 0.7777777777777778 + +IDEAL = 1. Coating: 0.2222222222222222 + +summa: 1.0 + +IDEAL = 1. Extrapolation precision (Approx): 4.5 + +**Оценка качества AE2:** + +IDEAL = 0. Excess: 0.2222222222222222 + +IDEAL = 0. Deficit: 0.6111111111111112 + +IDEAL = 1. Coating: 0.3888888888888889 + +summa: 1.0 + +IDEAL = 1. Extrapolation precision (Approx): 1.6363636363636365 + +![график](4.png) + +![график](5.png) + +![график](6.png) + +![график](7.png) + +![график](8.png) + +**ВЫВОД О ПРИГОДНОСТИ AE1 И AE2 ДЛЯ ОБНАРУЖЕНИЯ АНОМАЛИЙ** + +На основе анализа характеристик EDCA можно сделать следующие выводы: +AE1 (простая архитектура [2-1-2]): + +НЕ ПРИГОДЕН для качественного обнаружения аномалий по следующим причинам: + + Deficit = 0.78 - критически высокое значение, что означает, что автокодировщик пропускает 78% обучающих данных и не распознает их как нормальные + + Coating = 0.22 - чрезвычайно низкое значение, автокодировщик охватывает только 22% области обучающих данных + + Approx = 4.5 - значительно превышает идеальное значение 1, что свидетельствует о очень плохой аппроксимации данных + +Положительный аспект: Excess = 0.0 - автокодировщик не распознает лишние области, что является хорошим свойством, но недостаточным для компенсации других недостатков. +AE2 (архитектура [2-3-2-1-2-3-2]): + +ТРЕБУЕТ СУЩЕСТВЕННОГО УЛУЧШЕНИЯ и в текущем состоянии не пригоден для качественного обнаружения аномалий: + + Excess = 0.22 - автокодировщик распознает 22% лишних областей, что может приводить к пропуску аномалий + + Deficit = 0.61 - все еще высокое значение, 61% данных не распознается как нормальные + + Coating = 0.39 - низкое покрытие, только 39% обучающих данных охватывается областью распознавания + + Approx = 1.64 - лучше чем у AE1, но все еще далеко от идеального значения 1 + +ОБЩИЙ ВЫВОД: + +Оба автокодировщика демонстрируют недостаточное качество аппроксимации обучающих данных. AE1 слишком прост и не способен adequately выучить распределение данных, в то время как AE2, хотя и показывает улучшение, все еще требует значительной доработки архитектуры и параметров обучения для достижения приемлемого качества обнаружения аномалий. + +Рекомендация: Необходимо создать улучшенный автокодировщик AE3 с более сложной архитектурой, увеличить количество эпох обучения и оптимизировать гиперпараметры для достижения значений EDCA, близких к идеальным (Excess ≈ 0, Deficit ≈ 0, Coating ≈ 1, Approx ≈ 1). + +### Пункт №6. Улучшение автокодировщика АЕ2. + +**Анализ проблем текущего AE2:** + +• Excess = 0.22 - слишком много лишних областей + +• Deficit = 0.61 - пропускает много нормальных данных + +• Coating = 0.39 - плохое покрытие обучающих данных + +• Approx = 1.64 - требует улучшения аппроксимации + +**Стратегия улучшения AE2:** + +• Увеличение количества эпох обучения с 3000 до 5000 + +• Увеличение patience с 300 до 400 + +• Уменьшение learning rate с 0.001 до 0.0005 + +• Уменьшение batch size с 32 до 16 + +```python +print("="*70) +print("УЛУЧШЕНИЕ АВТОКОДИРОВЩИКА AE2 - ПОВТОРНОЕ ОБУЧЕНИЕ") +print("="*70) + +def create_ae2_improved(): + model = Sequential() + + model.add(Dense(2, input_shape=(2,), activation='tanh')) + + model.add(Dense(3, activation='tanh')) + model.add(Dense(2, activation='tanh')) + model.add(Dense(1, activation='tanh')) + model.add(Dense(2, activation='tanh')) + model.add(Dense(3, activation='tanh')) + + model.add(Dense(2, activation='linear')) + + return model + +ae2_improved = create_ae2_improved() +ae2_improved.compile(optimizer=Adam(learning_rate=0.0005), loss='mse') + +print("\nАрхитектура AE2 (улучшенный): [2-3-2-1-2-3-2]") +print("Количество скрытых слоев: 5") +ae2_improved.summary() + +print(f"\nНачало обучения улучшенного AE2 (5000 эпох, patience=400)...") +history_ae2_improved = ae2_improved.fit(data, data, + epochs=5000, + batch_size=16, + validation_split=0.2, + verbose=1, + callbacks=[EarlyStopping(patience=400, restore_best_weights=True)]) + +ae2_improved.save('out/AE2_improved.h5') + +X_pred_ae2_improved = ae2_improved.predict(data) +reconstruction_errors_ae2_improved = np.mean(np.square(data - X_pred_ae2_improved), axis=1) +threshold_ae2_improved = np.max(reconstruction_errors_ae2_improved) + +print("\n" + "="*50) +print("АНАЛИЗ РЕЗУЛЬТАТОВ УЛУЧШЕННОГО AE2") +print("="*50) +mse_ae2_improved = history_ae2_improved.history['loss'][-1] +print(f"Финальная ошибка MSE улучшенного AE2: {mse_ae2_improved:.6f}") +print(f"Порог ошибки реконструкции улучшенного AE2: {threshold_ae2_improved:.6f}") + +plt.figure(figsize=(15, 4)) + +plt.subplot(1, 3, 1) +plt.plot(history_ae2_improved.history['loss'], label='Training Loss', color='green', linewidth=2) +plt.plot(history_ae2_improved.history['val_loss'], label='Validation Loss', color='red', linewidth=2) +plt.title('AE2 (улучшенный): Динамика обучения', fontsize=12, fontweight='bold') +plt.xlabel('Эпоха') +plt.ylabel('MSE') +plt.legend() +plt.grid(True, alpha=0.3) + +plt.subplot(1, 3, 2) +plt.plot(reconstruction_errors_ae2_improved, 'green', alpha=0.7, linewidth=0.8) +plt.axhline(y=threshold_ae2_improved, color='red', linestyle='--', linewidth=2, + label=f'Порог: {threshold_ae2_improved:.4f}') +plt.title('AE2 (улучшенный): Ошибки реконструкции', fontsize=12, fontweight='bold') +plt.xlabel('Номер точки') +plt.ylabel('Ошибка реконструкции') +plt.legend() +plt.grid(True, alpha=0.3) + +plt.subplot(1, 3, 3) +plt.hist(reconstruction_errors_ae2_improved, bins=20, alpha=0.7, color='green', edgecolor='black') +plt.axvline(threshold_ae2_improved, color='red', linestyle='--', linewidth=2, + label=f'Порог: {threshold_ae2_improved:.4f}') +plt.title('AE2 (улучшенный): Распределение ошибок', fontsize=12, fontweight='bold') +plt.xlabel('Ошибка реконструкции') +plt.ylabel('Частота') +plt.legend() +plt.grid(True, alpha=0.3) + +plt.tight_layout() +plt.savefig('out/ae2_improved_results.png', dpi=300, bbox_inches='tight') +plt.show() + +ae2_trained = ae2_improved +IRE2 = reconstruction_errors_ae2_improved +IREth2 = threshold_ae2_improved + +print(f"\nОбучение улучшенного AE2 завершено!") +print(f"Количество фактических эпох: {len(history_ae2_improved.history['loss'])}") +``` + +**Описание:** Создается улучшенный автокодировщик AE2. + +**АНАЛИЗ РЕЗУЛЬТАТОВ УЛУЧШЕННОГО AE2:** + +Финальная ошибка MSE улучшенного AE2: 0.005074 + +Порог ошибки реконструкции улучшенного AE2: 0.060338 + +Количество фактических эпох: 5000 + +![Результаты улучшенного AE2](9.png) + +### Пункт №7. Подобрали подходящие параметры автокодировщика. + +**Оценка качества AE2** + +IDEAL = 0. Excess: 0.4111111111111112 + +IDEAL = 0. Deficit: 0.5 + +IDEAL = 1. Coating: 0.5 + +summa: 1.0 + +IDEAL = 1. Extrapolation precision (Approx): 0.9 + +AE2 (улучшенная архитектура [2-3-2-1-2-3-2]): + + Excess = 0.41 - УХУДШЕНИЕ: распознает 41% лишних областей, но это не так много + + Deficit = 0.50 - УЛУЧШЕНИЕ: пропускает 50% данных (было 41%) + + Coating = 0.50 - УЛУЧШЕНИЕ: охватывает 50% данных (было 39%) + + Approx = 0.90 - ЗНАЧИТЕЛЬНОЕ УЛУЧШЕНИЕ: близко к идеалу 1.0 + +**Описание:** AE2 после улучшения стал значительно лучше по основным метрикам аппроксимации. + +### Пункт №7. Создание тестовой выборки. +