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

..

13 Коммитов
main ... main

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

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

@ -1,15 +1,35 @@
# Интеллектуальные системы
## <a href="https://docs.google.com/spreadsheets/d/1E1vXKs7ZLeZcsPQiLG2JUh6mjsG6mXnmtFT58aCQul0/edit?usp=sharing">Журнал</a>
## Лабораторные работы
1. [Архитектура и обучение глубоких нейронных сетей](labworks/LW1)
2. [Обнаружение аномалий](labworks/LW2)
3. [Распознавание изображений](labworks/LW3)
4. [Распознавание последовательностей](labworks/LW4)
<!--
### Лабораторная работа №2
* [Задание](labworks/LW2/IS_Lab02_2023.pdf)
* [Методические указания](labworks/LW2/IS_Lab02_Metod_2023.pdf)
* [Наборы данных](labworks/LW2/data)
* [Библиотека для автокодировщиков](labworks/LW2/lab02_lib.py)
### Лабораторная работа №3
* [Задание](labworks/LW3/IS_Lab03_2023.pdf)
* [Методические указания](labworks/LW3/IS_Lab03_Metod_2023.pdf)
* <a href="https://youtube.com/playlist?list=PLZDCDMGmelH-pHt-Ij0nImVrOmj8DYKbB" target="_blank">Плейлист с видео о сверточных сетях (крутая визуализация)</a>
### Лабораторная работа №4
* [Задание](labworks/LW4/IS_Lab04_2023.pdf)
* [Методические указания](labworks/LW4/IS_Lab04_Metod_2023.pdf)
-->
## Архив материалов курса
В 2021–2022 годах материалы выкладывались <a href="https://uii.bitbucket.io/study/courses/#:~:text=Интеллектуальные%20системы%20(глубокие%20нейронные%20сети)" target="_blank">на этой странице</a>.

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

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

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

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

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

@ -0,0 +1,533 @@
## Отчёт по лабораторной работе №1
**Троянов Д.С., Чернов Д.Е. — А-01-22**
---
## 1) В среде Google Colab создали блокнот. Импортировали необходимые библиотеки и модули.
```python
# импорт модулей
import tensorflow as tf
from tensorflow import keras
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense
from keras.utils import to_categorical
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
import os
# Укажем текущую директорию
os.chdir('/content/drive/MyDrive/Colab Notebooks/is_dnn/labworks/LW1')
```
---
## 2) Загрузили набор данных MNIST, содержащий размеченные изображения рукописных цифр.
```python
# Загрузка датасета
(X_train_orig, y_train_orig), (X_test_orig, y_test_orig) = mnist.load_data()
```
---
## 3) Разбили набор данных на обучающие и тестовые данные в соотношении 60 000:10 000 элементов.
При разбиении параметр `random_state` выбрали равным (4k – 1), где k - номер бригады, k = 6 ⇒ `random_state = 23`.
```python
# разбиваем выборку на обучающую и тестовую выборку
X = np.concatenate((X_train_orig, X_test_orig))
y = np.concatenate((y_train_orig, y_test_orig))
X_train, X_test, y_train, y_test = train_test_split(
X, y,
test_size=10000,
train_size=60000,
random_state=3,
)
# вывод размерности массивов данных
print('Shape of X train:', X_train.shape)
print('Shape of y train:', y_train.shape)
print('Shape of X test:', X_test.shape)
print('Shape of y test:', y_test.shape)
```
```
Shape of X train: (60000, 28, 28)
Shape of y train: (60000,)
Shape of X test: (10000, 28, 28)
Shape of y test: (10000,)
```
---
## 4) Вывели первые 4 элемента обучающих данных (изображения и метки цифр).
```python
# Вывод первых 4 изображений
fig, axes = plt.subplots(1, 4, figsize=(12, 3))
for i in range(4):
axes[i].imshow(X_train[i], cmap='gray')
axes[i].set_title(f'Метка: {y_train[i]}')
axes[i].axis('off')
plt.tight_layout()
plt.show()
```
![4 цифры](paragraph_4.png)
# Были выведены цифры 7, 8, 2, 2
---
## 5) Провели предобработку данных: привели обучающие и тестовые данные к формату, пригодному для обучения нейронной сети. Входные данные должны принимать значения от 0 до 1, метки цифр должны быть закодированы по принципу «one-hot encoding». Вывели размерности предобработанных обучающих и тестовых массивов данных.
```python
# развернем каждое изображение 28*28 в вектор 784
num_pixels = X_train.shape[1] * X_train.shape[2]
X_train = X_train.reshape(X_train.shape[0], num_pixels) / 255
X_test = X_test.reshape(X_test.shape[0], num_pixels) / 255
print('Shape of transformed X train:', X_train.shape)
```
```
Shape of transformed X train: (60000, 784)
```
```python
# переведем метки в one-hot
from keras.utils import to_categorical
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
print('Shape of transformed y train:', y_train.shape)
num_classes = y_train.shape[1]
```
```
Shape of transformed y train: (60000, 10)
```
---
## 6) Реализовали модель однослойной нейронной сети и обучили ее на обучающих данных с выделением части обучающих данных в качестве валидационных. Вывели информацию об архитектуре нейронной сети. Вывели график функции ошибки на обучающих и валидационных данных по эпохам.
```python
model_0 = Sequential()
model_0.add(Dense(units=num_classes, input_dim=num_pixels, activation='softmax'))
# Компиляция модели
model_0.compile(loss='categorical_crossentropy',
optimizer='sgd',
metrics=['accuracy'])
# Вывод информации об архитектуре
print("Архитектура однослойной сети:")
model_0.summary()
```
![архитектура модели](architecture_of_1_layer_NN.png)
```
# Обучение модели
history_0 = model_0.fit(X_train, y_train,
validation_split=0.1,
epochs=50)
```
```python
# График функции ошибки по эпохам
plt.figure(figsize=(10, 6))
plt.plot(history_0.history['loss'], label='Обучающая выборка')
plt.plot(history_0.history['val_loss'], label='Валидационная выборка')
plt.title('Функция ошибки по эпохам (Однослойная сеть)')
plt.xlabel('Эпохи')
plt.ylabel('Ошибка')
plt.legend()
plt.grid(True)
plt.show()
```
![график обучения](paragraph_6.png)
---
## 7) Применили обученную модель к тестовым данным. Вывели значение функции ошибки и значение метрики качества классификации на тестовых данных.
```python
# Оценка на тестовых данных
scores_0 = model_0.evaluate(X_test, y_test, verbose=0)
print("Результаты однослойной сети:")
print(f"Ошибка на тестовых данных: {scores_0[0]}")
print(f"Точность на тестовых данных: {scores_0[1]}")
```
```
Результаты однослойной сети:
Ошибка на тестовых данных: 0.28625616431236267
Точность на тестовых данных: 0.92330002784729
```
---
## 8) Добавили в модель один скрытый и провели обучение и тестирование (повторить п. 6–7) при 100, 300, 500 нейронах в скрытом слое. По метрике качества классификации на тестовых данных выбрали наилучшее количество нейронов в скрытом слое. В качестве функции активации нейронов в скрытом слое использовали функцию `sigmoid`.
```python
# Функция для создания и обучения модели
def create_and_train_model(hidden_units, model_name):
model = Sequential()
model.add(Dense(units=hidden_units, input_dim=num_pixels, activation='sigmoid'))
model.add(Dense(units=num_classes, activation='softmax'))
model.compile(loss='categorical_crossentropy',
optimizer='sgd',
metrics=['accuracy'])
history = model.fit(X_train, y_train,
validation_split=0.1,
epochs=50)
scores = model.evaluate(X_test, y_test, verbose=0)
return model, history, scores
# Эксперименты с разным количеством нейронов
hidden_units_list = [100, 300, 500]
models_1 = {}
histories_1 = {}
scores_1 = {}
# Обучение сетей с одним скрытым слоем
for units in hidden_units_list:
print(f"
Обучение модели с {units} нейронами...")
model, history, scores = create_and_train_model(units, f"model_{units}")
models_1[units] = model
histories_1[units] = history
scores_1[units] = scores
print(f"Точность: {scores[1]}")
```
# Определим лучшую модель по итогвой точности
```python
# Выбор наилучшей модели
best_units_1 = max(scores_1.items(), key=lambda x: x[1][1])[0]
print(f"
Наилучшее количество нейронов: {best_units_1}")
print(f"Точность: {scores_1[best_units_1][1]}")
```
```
Наилучшее количество нейронов: 100
Точность: 0.9422000050544739
```
# Отобразим графики ошибок для каждой из архитектур нейросети
```python
# Графики ошибок для всех моделей
plt.figure(figsize=(15, 5))
for i, units in enumerate(hidden_units_list, 1):
plt.subplot(1, 3, i)
plt.plot(histories_1[units].history['loss'], label='Обучающая')
plt.plot(histories_1[units].history['val_loss'], label='Валидационная')
plt.title(f'{units} нейронов')
plt.xlabel('Эпохи')
plt.ylabel('Ошибка')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
```
![график обучения для каждой эпохи](paragraph_8.png)
**По результатам проведнного эксперимента наилучший показатель точности продемонстрировала нейронная сеть со 100 нейронами в скрытом слое - примерно 0.9422.**
---
## 9) Добавили в архитектуру с лучшим показателем из п. 8, второй скрытый слой и провели обучение и тестирование при 50 и 100 нейронах во втором скрытом слое. В качестве функции активации нейронов в скрытом слое использовали функцию `sigmoid`.
```python
# Добавление второго скрытого слоя
second_layer_units = [50, 100]
models_2 = {}
histories_2 = {}
scores_2 = {}
for units_2 in second_layer_units:
print(f"
Обучение модели со вторым слоем {units_2} нейронов")
model = Sequential()
model.add(Dense(units=best_units_1, input_dim=num_pixels, activation='sigmoid'))
model.add(Dense(units=units_2, activation='sigmoid'))
model.add(Dense(units=num_classes, activation='softmax'))
model.compile(loss='categorical_crossentropy',
optimizer='sgd',
metrics=['accuracy'])
history = model.fit(X_train, y_train,
validation_split=0.1,
epochs=50)
scores = model.evaluate(X_test, y_test)
models_2[units_2] = model
histories_2[units_2] = history
scores_2[units_2] = scores
print(f"Точность: {scores[1]}")
```
# Результаты обучения моделей:
```
Обучение модели со вторым слоем 50 нейронов:
Точность: 0.9417999982833862
Обучение модели со вторым слоем 100 нейронов
Точность: 0.942300021648407
```
# Выбор наилучшей двухслойной модели
```python
best_units_2 = max(scores_2.items(), key=lambda x: x[1][1])[0]
print(f"
Наилучшее количество нейронов во втором слое: {best_units_2}")
print(f"Точность: {scores_2[best_units_2][1]}")
```
```
Наилучшее количество нейронов во втором слое: 100
Точность: 0.9423
```
## 10) Результаты исследования архитектуры нейронной сети занесли в таблицу:
```python
# Сбор результатов
results = {
'0 слоев': {'нейроны_1': '-', 'нейроны_2': '-', 'точность': scores_0[1]},
'1 слой_100': {'нейроны_1': 100, 'нейроны_2': '-', 'точность': scores_1[100][1]},
'1 слой_300': {'нейроны_1': 300, 'нейроны_2': '-', 'точность': scores_1[300][1]},
'1 слой_500': {'нейроны_1': 500, 'нейроны_2': '-', 'точность': scores_1[500][1]},
'2 слоя_50': {'нейроны_1': best_units_1, 'нейроны_2': 50, 'точность': scores_2[50][1]},
'2 слоя_100': {'нейроны_1': best_units_1, 'нейроны_2': 100, 'точность': scores_2[100][1]}
}
# Создаем DataFrame из результатов
df_results = pd.DataFrame([
{'Кол-во скрытых слоев': 0, 'Нейроны_1_слоя': '-', 'Нейроны_2_слоя': '-', 'Точность': results['0 слоев']['точность']},
{'Кол-во скрытых слоев': 1, 'Нейроны_1_слоя': 100, 'Нейроны_2_слоя': '-', 'Точность': results['1 слой_100']['точность']},
{'Кол-во скрытых слоев': 1, 'Нейроны_1_слоя': 300, 'Нейроны_2_слоя': '-', 'Точность': results['1 слой_300']['точность']},
{'Кол-во скрытых слоев': 1, 'Нейроны_1_слоя': 500, 'Нейроны_2_слоя': '-', 'Точность': results['1 слой_500']['точность']},
{'Кол-во скрытых слоев': 2, 'Нейроны_1_слоя': best_units_1, 'Нейроны_2_слоя': 50, 'Точность': results['2 слоя_50']['точность']},
{'Кол-во скрытых слоев': 2, 'Нейроны_1_слоя': best_units_1, 'Нейроны_2_слоя': 100, 'Точность': results['2 слоя_100']['точность']}
])
print(" " * 20 + "ТАБЛИЦА РЕЗУЛЬТАТОВ")
print("=" * 70)
# print(df_results.to_string(index=False, formatters={
# 'Точность': '{:.4f}'.format
# }))
print(df_results.reset_index(drop=True))
```
```
ТАБЛИЦА РЕЗУЛЬТАТОВ
======================================================================
Кол-во скрытых слоев Нейроны_1_слоя Нейроны_2_слоя Точность
0 0 - - 0.9233
1 1 100 - 0.9422
2 1 300 - 0.9377
3 1 500 - 0.9312
4 2 100 50 0.9418
5 2 100 100 0.9423
```
```python
# Выбор наилучшей модели
best_model_type = max(results.items(), key=lambda x: x[1]['точность'])[0]
best_accuracy = results[best_model_type]['точность']
print(f"
Наилучшая архитектура: {best_model_type}")
print(f"Точность: {best_accuracy}")
```
```
Наилучшая архитектура: 2 слоя_100
Точность: 0.9423
```
### По результатам исследования сделали выводы и выбрали наилучшую архитектуру нейронной сети с точки зрения качества классификации.
**Из таблицы следует, что лучшей архитектурой является НС с двумя скрытыми слоями по 100 и 50 нейронов, второе место занимает НС с одним скрытым слоем и 100 нейронами, на основе которой мы и строили НС с двумя скрытыми слоями. При увеличении количества нейронов в архитектуре НС с 1-м скрытым слоем в результате тестирования было вявлено, что метрики качества падают. Это связано с переобучение нашей НС с 1-м скрытым слоем (когда построенная модель хорошо объясняет примеры из обучающей выборки, но относительно плохо работает на примерах, не участвовавших в обучении) Такая тенденция вероятно возникает из-за простоты датасета MNIST, при усложнении архитектуры НС начинает переобучаться, а оценка качества на тестовых данных падает. Но также стоит отметить, что при усложнении структуры НС точнсть модели также и растет.**
---
## 11) Сохранили наилучшую нейронную сеть на диск.
```python
# Сохранение модели
best_model.save('best_mnist_model.keras')
```
---
## 12) Для нейронной сети наилучшей архитектуры вывели два тестовых изображения, истинные метки и результат распознавания изображений.
```python
# вывод тестового изображения и результата распознавания (1)
n = 123
result = best_model.predict(X_test[n:n+1])
print('NN output:', result)
plt.imshow(X_test[n].reshape(28,28), cmap=plt.get_cmap('gray'))
plt.show()
print('Real mark: ', str(np.argmax(y_test[n])))
print('NN answer: ', str(np.argmax(result)))
```
![результат 1](paragraph_12_1.png)
```python
# вывод тестового изображения и результата распознавания (3)
n = 353
result = best_model.predict(X_test[n:n+1])
print('NN output:', result)
plt.imshow(X_test[n].reshape(28,28), cmap=plt.get_cmap('gray'))
plt.show()
print('Real mark: ', str(np.argmax(y_test[n])))
print('NN answer: ', str(np.argmax(result)))
```
![результат 2](paragraph_12_3.png)
---
## 13) Создали собственные изображения рукописных цифр, подобное представленным в наборе MNIST. Цифру выбрали как остаток от деления на 10 числа своего дня рождения (12 марта → 2, 17 сентября → 7 ). Сохранили изображения. Загрузили, предобработали и подали на вход обученной нейронной сети собственные изображения. Вывели изображения и результаты распознавания.
```python
# загрузка собственного изображения
file_data_2 = Image.open('2.png')
file_data_7 = Image.open('7.png')
file_data_2 = file_data_2.convert('L') # перевод в градации серого
file_data_7 = file_data_7.convert('L') # перевод в градации серого
test_img_2 = np.array(file_data_2)
test_img_7 = np.array(file_data_7)
# вывод собственного изображения (цифра 2)
plt.imshow(test_img_2, cmap=plt.get_cmap('gray'))
plt.show()
# предобработка
test_img_2 = test_img_2 / 255
test_img_2 = test_img_2.reshape(1, num_pixels)
# распознавание
result = best_model.predict(test_img_2)
print('I think it\'s ', np.argmax(result))
```
![собственные изображения](2.png)
```
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 31ms/step
```
```python
# вывод собственного изображения (цифра 7)
plt.imshow(test_img_7, cmap=plt.get_cmap('gray'))
plt.show()
# предобработка
test_img_7 = test_img_7 / 255
test_img_7 = test_img_7.reshape(1, num_pixels)
# распознавание
result = best_model.predict(test_img_7)
print('I think it\'s ', np.argmax(result))
```
```
![собственные изображения](7.png)
```
```
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 48ms/step
I think it's 7
```
**Как видим в результате эксперимента наша НС недостаточно точно определила изображение цифры 2. Возможно это связано с малом размером используемой выборки. Для улучшения качества решения задачи классификации можно либо увеличить размерность выборки для обучения НС, либо изменить структуру НС для более точной ее работы**
---
## 14) Создать копию собственного изображения, отличающуюся от оригинала поворотом на 90 градусов в любую сторону. Сохранили изображения. Загрузили, предобработали и подали на вход обученной нейронной сети измененные изображения. Вывели изображения и результаты распознавания. Сделали выводы по результатам эксперимента.
```python
# загрузка собственного изображения
file_data_2_90 = Image.open('2_90.png')
file_data_7_90 = Image.open('7_90.png')
file_data_2_90 = file_data_2_90.convert('L') # перевод в градации серого
file_data_7_90 = file_data_7_90.convert('L') # перевод в градации серого
test_img_2_90 = np.array(file_data_2_90)
test_img_7_90= np.array(file_data_7_90)
# вывод собственного изображения (цифра 2)
plt.imshow(test_img_2_90, cmap=plt.get_cmap('gray'))
plt.show()
# предобработка
test_img_2_90 = test_img_2_90 / 255
test_img_2_90 = test_img_2_90.reshape(1, num_pixels)
# распознавание
result = best_model.predict(test_img_2_90)
print('I think it\'s ', np.argmax(result))
```
![собственные изображения повернутые на 90 градусов](2_90.png)
```
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 91ms/step
I think it's 7
```
```python
# вывод собственного изображения (цифра 7)
plt.imshow(test_img_7_90, cmap=plt.get_cmap('gray'))
plt.show()
# предобработка
test_img_7_90 = test_img_7_90 / 255
test_img_7_90 = test_img_7_90.reshape(1, num_pixels)
# распознавание
result = best_model.predict(test_img_7_90)
print('I think it\'s ', np.argmax(result))
```
![собственные изображения повернутые на 90 градусов](2_90.png)
```
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 48ms/step
I think it's 7
```
**При повороте рисунков цифр НС не смогла распознать одну из цифр. Так получилось, во-первых, по той же причине, почему НС не распознала одну из цифр в пункте 13, во-вторых - наша НС не обучалась на перевернутых цифрах**
---

Двоичные данные
labworks/LW1/2.png

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

После

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

Двоичные данные
labworks/LW1/2_90.png

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

После

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

Двоичные данные
labworks/LW1/7.png

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

После

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

Двоичные данные
labworks/LW1/7_90.png

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

После

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

@ -0,0 +1,899 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "d5jFanC8NPTN"
},
"outputs": [],
"source": [
"import os\n",
"os.chdir('/content/drive/MyDrive/Colab Notebooks/is_dnn/labworks/LW1')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "H39A4nsqNuxn"
},
"outputs": [],
"source": [
"# импорт модулей\n",
"import tensorflow as tf\n",
"from tensorflow import keras\n",
"from keras.datasets import mnist\n",
"from keras.models import Sequential\n",
"from keras.layers import Dense\n",
"from keras.utils import to_categorical\n",
"from sklearn.model_selection import train_test_split\n",
"import matplotlib.pyplot as plt\n",
"import numpy as np\n",
"from PIL import Image\n",
"import os\n",
"import pandas as pd"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"executionInfo": {
"elapsed": 462,
"status": "ok",
"timestamp": 1759127274975,
"user": {
"displayName": "Legal People",
"userId": "00818738730090246603"
},
"user_tz": -180
},
"id": "iNenKkcoRXrs",
"outputId": "041dc403-e177-4f0d-edbb-40902c8954fd"
},
"outputs": [],
"source": [
"# Загрузка датасета\n",
"(X_train_orig, y_train_orig), (X_test_orig, y_test_orig) = mnist.load_data()\n",
"\n",
"# разбиваем выборку на обучающую и тестовую выборку\n",
"X = np.concatenate((X_train_orig, X_test_orig))\n",
"y = np.concatenate((y_train_orig, y_test_orig))\n",
"\n",
"\n",
"X_train, X_test, y_train, y_test = train_test_split(\n",
" X, y,\n",
" test_size=10000,\n",
" train_size=60000,\n",
" random_state=3,\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 224
},
"executionInfo": {
"elapsed": 243,
"status": "ok",
"timestamp": 1759127328545,
"user": {
"displayName": "Legal People",
"userId": "00818738730090246603"
},
"user_tz": -180
},
"id": "bt-EmlYARsCL",
"outputId": "611d9110-39a2-46dd-94ce-b0fea7005b87"
},
"outputs": [],
"source": [
"# Вывод первых 4 изображений\n",
"fig, axes = plt.subplots(1, 4, figsize=(12, 3))\n",
"for i in range(4):\n",
" axes[i].imshow(X_train[i], cmap='gray')\n",
" axes[i].set_title(f'Метка: {y_train[i]}')\n",
" axes[i].axis('off')\n",
"plt.tight_layout()\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"executionInfo": {
"elapsed": 134,
"status": "ok",
"timestamp": 1759127329383,
"user": {
"displayName": "Legal People",
"userId": "00818738730090246603"
},
"user_tz": -180
},
"id": "5WUu3_97TjSa",
"outputId": "7a75ca25-58fe-447d-8bef-547159e6479d"
},
"outputs": [],
"source": [
"# развернем каждое изображение 28*28 в вектор 784\n",
"num_pixels = X_train.shape[1] * X_train.shape[2]\n",
"X_train = X_train.reshape(X_train.shape[0], num_pixels) / 255\n",
"X_test = X_test.reshape(X_test.shape[0], num_pixels) / 255\n",
"print('Shape of transformed X train:', X_train.shape)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"executionInfo": {
"elapsed": 7,
"status": "ok",
"timestamp": 1759127330016,
"user": {
"displayName": "Legal People",
"userId": "00818738730090246603"
},
"user_tz": -180
},
"id": "sUJfDepgUauM",
"outputId": "2a9bc3d0-8bdb-4971-f3d4-ca21cb14fca6"
},
"outputs": [],
"source": [
"# переведем метки в one-hot\n",
"y_train = to_categorical(y_train)\n",
"y_test = to_categorical(y_test)\n",
"print('Shape of transformed y train:', y_train.shape)\n",
"num_classes = y_train.shape[1]"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 1000
},
"executionInfo": {
"elapsed": 207500,
"status": "ok",
"timestamp": 1759127540337,
"user": {
"displayName": "Legal People",
"userId": "00818738730090246603"
},
"user_tz": -180
},
"id": "f3UzOyf_V2HQ",
"outputId": "9ffc63df-23bf-4947-f3c0-2a3e507034f1"
},
"outputs": [],
"source": [
"model_0 = Sequential()\n",
"model_0.add(Dense(units=num_classes, input_dim=num_pixels, activation='softmax'))\n",
"\n",
"# Компиляция модели\n",
"model_0.compile(loss='categorical_crossentropy',\n",
" optimizer='sgd',\n",
" metrics=['accuracy'])\n",
"\n",
"# Вывод информации об архитектуре\n",
"print(\"Архитектура однослойной сети:\")\n",
"model_0.summary()\n",
"\n",
"# Обучение модели\n",
"history_0 = model_0.fit(X_train, y_train,\n",
" validation_split=0.1,\n",
" epochs=50)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 533
},
"executionInfo": {
"elapsed": 260,
"status": "ok",
"timestamp": 1759127542723,
"user": {
"displayName": "Legal People",
"userId": "00818738730090246603"
},
"user_tz": -180
},
"id": "5yjxVnmFmpbV",
"outputId": "b182a139-da24-491e-9ebd-7ba1e39eec84"
},
"outputs": [],
"source": [
"# График функции ошибки по эпохам\n",
"plt.figure(figsize=(10, 6))\n",
"plt.plot(history_0.history['loss'], label='Обучающая выборка')\n",
"plt.plot(history_0.history['val_loss'], label='Валидационная выборка')\n",
"plt.title('Функция ошибки по эпохам (Однослойная сеть)')\n",
"plt.xlabel('Эпохи')\n",
"plt.ylabel('Ошибка')\n",
"plt.legend()\n",
"plt.grid(True)\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"executionInfo": {
"elapsed": 1058,
"status": "ok",
"timestamp": 1759127544719,
"user": {
"displayName": "Legal People",
"userId": "00818738730090246603"
},
"user_tz": -180
},
"id": "NF_SsO8wiEUT",
"outputId": "ffe554c2-1c94-42b7-cc63-8f407418c4ce"
},
"outputs": [],
"source": [
"# Оценка на тестовых данных\n",
"scores_0 = model_0.evaluate(X_test, y_test, verbose=0)\n",
"print(\"Результаты однослойной сети:\")\n",
"print(f\"Ошибка на тестовых данных: {scores_0[0]}\")\n",
"print(f\"Точность на тестовых данных: {scores_0[1]}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "fFcoQGdPnDFx"
},
"outputs": [],
"source": [
"# Функция для создания и обучения модели\n",
"def create_and_train_model(hidden_units, model_name):\n",
" model = Sequential()\n",
" model.add(Dense(units=hidden_units, input_dim=num_pixels, activation='sigmoid'))\n",
" model.add(Dense(units=num_classes, activation='softmax'))\n",
"\n",
" model.compile(loss='categorical_crossentropy',\n",
" optimizer='sgd',\n",
" metrics=['accuracy'])\n",
"\n",
" history = model.fit(X_train, y_train,\n",
" validation_split=0.1,\n",
" epochs=50)\n",
"\n",
" scores = model.evaluate(X_test, y_test, verbose=0)\n",
"\n",
" return model, history, scores"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "XPlFkV40joAT"
},
"outputs": [],
"source": [
"# Эксперименты с разным количеством нейронов\n",
"hidden_units_list = [100, 300, 500]\n",
"models_1 = {}\n",
"histories_1 = {}\n",
"scores_1 = {}"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"executionInfo": {
"elapsed": 638747,
"status": "ok",
"timestamp": 1759128186868,
"user": {
"displayName": "Legal People",
"userId": "00818738730090246603"
},
"user_tz": -180
},
"id": "je0i_8HxjvpB",
"outputId": "5cb19edc-9162-4c23-92d8-1671e62b2bb3"
},
"outputs": [],
"source": [
"# Обучение сетей с одним скрытым слоем\n",
"for units in hidden_units_list:\n",
" print(f\"\\nОбучение модели с {units} нейронами...\")\n",
" model, history, scores = create_and_train_model(units, f\"model_{units}\")\n",
"\n",
" models_1[units] = model\n",
" histories_1[units] = history\n",
" scores_1[units] = scores\n",
"\n",
" print(f\"Точность: {scores[1]}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"executionInfo": {
"elapsed": 20,
"status": "ok",
"timestamp": 1759128269112,
"user": {
"displayName": "Legal People",
"userId": "00818738730090246603"
},
"user_tz": -180
},
"id": "TbbBBxeMmy6c",
"outputId": "08b5a164-1e62-456c-80b9-bc7247be3fd3"
},
"outputs": [],
"source": [
"# Выбор наилучшей модели\n",
"best_units_1 = max(scores_1.items(), key=lambda x: x[1][1])[0]\n",
"print(f\"\\nНаилучшее количество нейронов: {best_units_1}\")\n",
"print(f\"Точность: {scores_1[best_units_1][1]}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 279
},
"executionInfo": {
"elapsed": 620,
"status": "ok",
"timestamp": 1759128272502,
"user": {
"displayName": "Legal People",
"userId": "00818738730090246603"
},
"user_tz": -180
},
"id": "EFEBxB2qm1fF",
"outputId": "d436ac7a-e33b-4cc2-e324-5decfc29de94"
},
"outputs": [],
"source": [
"# Графики ошибок для всех моделей\n",
"plt.figure(figsize=(15, 5))\n",
"for i, units in enumerate(hidden_units_list, 1):\n",
" plt.subplot(1, 3, i)\n",
" plt.plot(histories_1[units].history['loss'], label='Обучающая')\n",
" plt.plot(histories_1[units].history['val_loss'], label='Валидационная')\n",
" plt.title(f'{units} нейронов')\n",
" plt.xlabel('Эпохи')\n",
" plt.ylabel('Ошибка')\n",
" plt.legend()\n",
" plt.grid(True)\n",
"plt.tight_layout()\n",
"plt.show()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "EJBwT5vhnnSG"
},
"outputs": [],
"source": [
"# Добавление второго скрытого слоя\n",
"second_layer_units = [50, 100]\n",
"models_2 = {}\n",
"histories_2 = {}\n",
"scores_2 = {}"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"executionInfo": {
"elapsed": 420194,
"status": "ok",
"timestamp": 1759128698216,
"user": {
"displayName": "Legal People",
"userId": "00818738730090246603"
},
"user_tz": -180
},
"id": "K5vweMySno3t",
"outputId": "b67b155c-89ea-46e0-a590-a588433e0a38"
},
"outputs": [],
"source": [
"for units_2 in second_layer_units:\n",
" print(f\"\\nОбучение модели со вторым слоем {units_2} нейронов\")\n",
"\n",
" model = Sequential()\n",
" model.add(Dense(units=best_units_1, input_dim=num_pixels, activation='sigmoid'))\n",
" model.add(Dense(units=units_2, activation='sigmoid'))\n",
" model.add(Dense(units=num_classes, activation='softmax'))\n",
"\n",
" model.compile(loss='categorical_crossentropy',\n",
" optimizer='sgd',\n",
" metrics=['accuracy'])\n",
"\n",
" history = model.fit(X_train, y_train,\n",
" validation_split=0.1,\n",
" epochs=50)\n",
"\n",
" scores = model.evaluate(X_test, y_test)\n",
"\n",
" models_2[units_2] = model\n",
" histories_2[units_2] = history\n",
" scores_2[units_2] = scores\n",
"\n",
" print(f\"Точность: {scores[1]}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"executionInfo": {
"elapsed": 14,
"status": "ok",
"timestamp": 1759129484222,
"user": {
"displayName": "Legal People",
"userId": "00818738730090246603"
},
"user_tz": -180
},
"id": "9lJtmn_oSVkB",
"outputId": "b49d6a95-574a-4ce5-e23f-eea49ba83603"
},
"outputs": [],
"source": [
"# Выбор наилучшей двухслойной модели\n",
"best_units_2 = max(scores_2.items(), key=lambda x: x[1][1])[0]\n",
"print(f\"\\nНаилучшее количество нейронов во втором слое: {best_units_2}\")\n",
"print(f\"Точность: {scores_2[best_units_2][1]:.4f}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "p0aeriYzShJk"
},
"outputs": [],
"source": [
"# Сбор результатов\n",
"results = {\n",
" '0 слоев': {'нейроны_1': '-', 'нейроны_2': '-', 'точность': scores_0[1]},\n",
" '1 слой_100': {'нейроны_1': 100, 'нейроны_2': '-', 'точность': scores_1[100][1]},\n",
" '1 слой_300': {'нейроны_1': 300, 'нейроны_2': '-', 'точность': scores_1[300][1]},\n",
" '1 слой_500': {'нейроны_1': 500, 'нейроны_2': '-', 'точность': scores_1[500][1]},\n",
" '2 слоя_50': {'нейроны_1': best_units_1, 'нейроны_2': 50, 'точность': scores_2[50][1]},\n",
" '2 слоя_100': {'нейроны_1': best_units_1, 'нейроны_2': 100, 'точность': scores_2[100][1]}\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"executionInfo": {
"elapsed": 33,
"status": "ok",
"timestamp": 1759130442386,
"user": {
"displayName": "Legal People",
"userId": "00818738730090246603"
},
"user_tz": -180
},
"id": "SHr6z7jbSmOG",
"outputId": "2d40f526-7756-4554-f110-2242e34e58a0"
},
"outputs": [],
"source": [
"# Создаем DataFrame из результатов\n",
"df_results = pd.DataFrame([\n",
" {'Кол-во скрытых слоев': 0, 'Нейроны_1_слоя': '-', 'Нейроны_2_слоя': '-', 'Точность': results['0 слоев']['точность']},\n",
" {'Кол-во скрытых слоев': 1, 'Нейроны_1_слоя': 100, 'Нейроны_2_слоя': '-', 'Точность': results['1 слой_100']['точность']},\n",
" {'Кол-во скрытых слоев': 1, 'Нейроны_1_слоя': 300, 'Нейроны_2_слоя': '-', 'Точность': results['1 слой_300']['точность']},\n",
" {'Кол-во скрытых слоев': 1, 'Нейроны_1_слоя': 500, 'Нейроны_2_слоя': '-', 'Точность': results['1 слой_500']['точность']},\n",
" {'Кол-во скрытых слоев': 2, 'Нейроны_1_слоя': best_units_1, 'Нейроны_2_слоя': 50, 'Точность': results['2 слоя_50']['точность']},\n",
" {'Кол-во скрытых слоев': 2, 'Нейроны_1_слоя': best_units_1, 'Нейроны_2_слоя': 100, 'Точность': results['2 слоя_100']['точность']}\n",
"])\n",
"\n",
"print(\" \" * 20 + \"ТАБЛИЦА РЕЗУЛЬТАТОВ\")\n",
"print(\"=\" * 70)\n",
"# print(df_results.to_string(index=False, formatters={\n",
"# 'Точность': '{:.4f}'.format\n",
"# }))\n",
"print(df_results.reset_index(drop=True))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/"
},
"executionInfo": {
"elapsed": 41,
"status": "ok",
"timestamp": 1759130490602,
"user": {
"displayName": "Legal People",
"userId": "00818738730090246603"
},
"user_tz": -180
},
"id": "PTC5CUJeWQ_V",
"outputId": "e8009546-5876-4427-9815-9aac53db8593"
},
"outputs": [],
"source": [
"# Выбор наилучшей модели\n",
"best_model_type = max(results.items(), key=lambda x: x[1]['точность'])[0]\n",
"best_accuracy = results[best_model_type]['точность']\n",
"print(f\"\\nНаилучшая архитектура: {best_model_type}\")\n",
"print(f\"Точность: {best_accuracy:.4f}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "JRPRpppHWV8-"
},
"outputs": [],
"source": [
"# Определение наилучшей модели\n",
"if '0' in best_model_type:\n",
" best_model = model_0\n",
"elif '1' in best_model_type:\n",
" best_neurons = int(best_model_type.split('_')[1])\n",
" best_model = models_1[best_neurons]\n",
"else:\n",
" best_neurons_2 = int(best_model_type.split('_')[1])\n",
" best_model = models_2[best_neurons_2]\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "wIlYoP_HSFph"
},
"outputs": [],
"source": [
"# Сохранение модели\n",
"best_model.save('best_mnist_model.keras')"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 517
},
"executionInfo": {
"elapsed": 178,
"status": "ok",
"timestamp": 1759132236751,
"user": {
"displayName": "Legal People",
"userId": "00818738730090246603"
},
"user_tz": -180
},
"id": "Kh6-u-8OoHny",
"outputId": "7c89ce31-3967-41bb-ff79-6e77e7f5d19b"
},
"outputs": [],
"source": [
"# вывод тестового изображения и результата распознавания (1)\n",
"n = 123\n",
"result = best_model.predict(X_test[n:n+1])\n",
"print('NN output:', result)\n",
"plt.imshow(X_test[n].reshape(28,28), cmap=plt.get_cmap('gray'))\n",
"plt.show()\n",
"print('Real mark: ', str(np.argmax(y_test[n])))\n",
"print('NN answer: ', str(np.argmax(result)))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 517
},
"executionInfo": {
"elapsed": 284,
"status": "ok",
"timestamp": 1759132262259,
"user": {
"displayName": "Legal People",
"userId": "00818738730090246603"
},
"user_tz": -180
},
"id": "WVGJRtVZc8V-",
"outputId": "5d4ed790-d7ce-4569-d667-49733f75e3cb"
},
"outputs": [],
"source": [
"# вывод тестового изображения и результата распознавания (3)\n",
"n = 353\n",
"result = best_model.predict(X_test[n:n+1])\n",
"print('NN output:', result)\n",
"plt.imshow(X_test[n].reshape(28,28), cmap=plt.get_cmap('gray'))\n",
"plt.show()\n",
"print('Real mark: ', str(np.argmax(y_test[n])))\n",
"print('NN answer: ', str(np.argmax(result)))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "z5-YYw4uosUB"
},
"outputs": [],
"source": [
"# загрузка собственного изображения (Цифры 2 и 7)\n",
"from PIL import Image\n",
"file_data_2 = Image.open('2.png')\n",
"file_data_7 = Image.open('7.png')\n",
"file_data_2 = file_data_2.convert('L') # перевод в градации серого\n",
"file_data_7 = file_data_7.convert('L') # перевод в градации серого\n",
"test_img_2 = np.array(file_data_2)\n",
"test_img_7 = np.array(file_data_7)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 465
},
"executionInfo": {
"elapsed": 156,
"status": "ok",
"timestamp": 1759130765327,
"user": {
"displayName": "Legal People",
"userId": "00818738730090246603"
},
"user_tz": -180
},
"id": "dv17bJVVuslg",
"outputId": "d9b5b55c-75d9-4180-f93c-22befad0633c"
},
"outputs": [],
"source": [
"# вывод собственного изображения (цифра 2)\n",
"plt.imshow(test_img_2, cmap=plt.get_cmap('gray'))\n",
"plt.show()\n",
"# предобработка\n",
"test_img_2 = test_img_2 / 255\n",
"test_img_2 = test_img_2.reshape(1, num_pixels)\n",
"# распознавание\n",
"result = best_model.predict(test_img_2)\n",
"print('I think it\\'s ', np.argmax(result))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 465
},
"executionInfo": {
"elapsed": 651,
"status": "ok",
"timestamp": 1759130799101,
"user": {
"displayName": "Legal People",
"userId": "00818738730090246603"
},
"user_tz": -180
},
"id": "rhNzATtGuxbD",
"outputId": "11cd1569-d370-4070-c8de-02035699d8bb"
},
"outputs": [],
"source": [
"# вывод собственного изображения (цифра 7)\n",
"plt.imshow(test_img_7, cmap=plt.get_cmap('gray'))\n",
"plt.show()\n",
"# предобработка\n",
"test_img_7 = test_img_7 / 255\n",
"test_img_7 = test_img_7.reshape(1, num_pixels)\n",
"# распознавание\n",
"result = best_model.predict(test_img_7)\n",
"print('I think it\\'s ', np.argmax(result))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "4rIUmwfYXcgh"
},
"outputs": [],
"source": [
"# Тестирование на собственных повернутых изображениях\n",
"file_data_2_90 = Image.open('2_90.png')\n",
"file_data_7_90 = Image.open('7_90.png')\n",
"file_data_2_90 = file_data_2_90.convert('L') # перевод в градации серого\n",
"file_data_7_90 = file_data_7_90.convert('L') # перевод в градации серого\n",
"test_img_2_90 = np.array(file_data_2_90)\n",
"test_img_7_90= np.array(file_data_7_90)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 465
},
"executionInfo": {
"elapsed": 445,
"status": "ok",
"timestamp": 1759131775554,
"user": {
"displayName": "Legal People",
"userId": "00818738730090246603"
},
"user_tz": -180
},
"id": "xeYNkU0OZqg2",
"outputId": "b17a7b99-ce57-45fa-c069-74ea8374c3d3"
},
"outputs": [],
"source": [
"# вывод собственного изображения (цифра 2)\n",
"plt.imshow(test_img_2_90, cmap=plt.get_cmap('gray'))\n",
"plt.show()\n",
"# предобработка\n",
"test_img_2_90 = test_img_2_90 / 255\n",
"test_img_2_90 = test_img_2_90.reshape(1, num_pixels)\n",
"# распознавание\n",
"result = best_model.predict(test_img_2_90)\n",
"print('I think it\\'s ', np.argmax(result))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"colab": {
"base_uri": "https://localhost:8080/",
"height": 465
},
"executionInfo": {
"elapsed": 238,
"status": "ok",
"timestamp": 1759131800104,
"user": {
"displayName": "Legal People",
"userId": "00818738730090246603"
},
"user_tz": -180
},
"id": "8JZajicXbNSA",
"outputId": "016e8c12-472d-4a15-c420-cd955edef901"
},
"outputs": [],
"source": [
"# вывод собственного изображения (цифра 7)\n",
"plt.imshow(test_img_7_90, cmap=plt.get_cmap('gray'))\n",
"plt.show()\n",
"# предобработка\n",
"test_img_7_90 = test_img_7_90 / 255\n",
"test_img_7_90 = test_img_7_90.reshape(1, num_pixels)\n",
"# распознавание\n",
"result = best_model.predict(test_img_7_90)\n",
"print('I think it\\'s ', np.argmax(result))"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "DQJNpFTOZ7Z6"
},
"source": []
}
],
"metadata": {
"accelerator": "GPU",
"colab": {
"gpuType": "T4",
"provenance": [
{
"file_id": "1HorM0jtoJfNfcuh_uXJu8ODaEJ6xOUu-",
"timestamp": 1759209370437
}
]
},
"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": 4
}

@ -1,11 +1,4 @@
## Лабораторныа работа №1
## Архитектура и обучение глубоких нейронных сетей
* [Задание](IS_Lab01_2023.pdf)
* [Задание](IS_Lab01_2023.pdf)
* [Методические указания](IS_Lab01_Metod_2023.pdf)
* <a href="https://youtube.com/playlist?list=PLfdZ2TeaMzfzlpZ60rbaYU_epH5XPNbWU" target="_blank"><s>Какие нейроны, что вообще происходит?</s> Рекомендуется к просмотру для понимания (4 видео)</a>
* <a href="https://www.youtube.com/watch?v=FwFduRA_L6Q" target="_blank">Почувствуйте себя пионером нейронных сетей в области распознавания образов</a>

Двоичные данные
labworks/LW1/architecture_of_1_layer_NN.png

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

После

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

Двоичные данные
labworks/LW1/best_mnist_model.keras

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

@ -0,0 +1,534 @@
## Отчёт по лабораторной работе №1
**Троянов Д.С., Чернов Д.Е. — А-01-22**
---
## 1) В среде Google Colab создали блокнот. Импортировали необходимые библиотеки и модули.
```python
# импорт модулей
import tensorflow as tf
from tensorflow import keras
from keras.datasets import mnist
from keras.models import Sequential
from keras.layers import Dense
from keras.utils import to_categorical
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
import numpy as np
from PIL import Image
import os
import pandas as pd
# Укажем текущую директорию
os.chdir('/content/drive/MyDrive/Colab Notebooks/is_dnn/labworks/LW1')
```
---
## 2) Загрузили набор данных MNIST, содержащий размеченные изображения рукописных цифр.
```python
# Загрузка датасета
(X_train_orig, y_train_orig), (X_test_orig, y_test_orig) = mnist.load_data()
```
---
## 3) Разбили набор данных на обучающие и тестовые данные в соотношении 60 000:10 000 элементов.
При разбиении параметр `random_state` выбрали равным (4k – 1), где k - номер бригады, k = 6 ⇒ `random_state = 23`.
```python
# разбиваем выборку на обучающую и тестовую выборку
X = np.concatenate((X_train_orig, X_test_orig))
y = np.concatenate((y_train_orig, y_test_orig))
X_train, X_test, y_train, y_test = train_test_split(
X, y,
test_size=10000,
train_size=60000,
random_state=3,
)
# вывод размерности массивов данных
print('Shape of X train:', X_train.shape)
print('Shape of y train:', y_train.shape)
print('Shape of X test:', X_test.shape)
print('Shape of y test:', y_test.shape)
```
```
Shape of X train: (60000, 28, 28)
Shape of y train: (60000,)
Shape of X test: (10000, 28, 28)
Shape of y test: (10000,)
```
---
## 4) Вывели первые 4 элемента обучающих данных (изображения и метки цифр).
```python
# Вывод первых 4 изображений
fig, axes = plt.subplots(1, 4, figsize=(12, 3))
for i in range(4):
axes[i].imshow(X_train[i], cmap='gray')
axes[i].set_title(f'Метка: {y_train[i]}')
axes[i].axis('off')
plt.tight_layout()
plt.show()
```
![4 цифры](paragraph_4.png)
# Были выведены цифры 7, 8, 2, 2
---
## 5) Провели предобработку данных: привели обучающие и тестовые данные к формату, пригодному для обучения нейронной сети. Входные данные должны принимать значения от 0 до 1, метки цифр должны быть закодированы по принципу «one-hot encoding». Вывели размерности предобработанных обучающих и тестовых массивов данных.
```python
# развернем каждое изображение 28*28 в вектор 784
num_pixels = X_train.shape[1] * X_train.shape[2]
X_train = X_train.reshape(X_train.shape[0], num_pixels) / 255
X_test = X_test.reshape(X_test.shape[0], num_pixels) / 255
print('Shape of transformed X train:', X_train.shape)
```
```
Shape of transformed X train: (60000, 784)
```
```python
# переведем метки в one-hot
from keras.utils import to_categorical
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
print('Shape of transformed y train:', y_train.shape)
num_classes = y_train.shape[1]
```
```
Shape of transformed y train: (60000, 10)
```
---
## 6) Реализовали модель однослойной нейронной сети и обучили ее на обучающих данных с выделением части обучающих данных в качестве валидационных. Вывели информацию об архитектуре нейронной сети. Вывели график функции ошибки на обучающих и валидационных данных по эпохам.
```python
model_0 = Sequential()
model_0.add(Dense(units=num_classes, input_dim=num_pixels, activation='softmax'))
# Компиляция модели
model_0.compile(loss='categorical_crossentropy',
optimizer='sgd',
metrics=['accuracy'])
# Вывод информации об архитектуре
print("Архитектура однослойной сети:")
model_0.summary()
```
![архитектура модели](architecture_of_1_layer_NN.png)
```
# Обучение модели
history_0 = model_0.fit(X_train, y_train,
validation_split=0.1,
epochs=50)
```
```python
# График функции ошибки по эпохам
plt.figure(figsize=(10, 6))
plt.plot(history_0.history['loss'], label='Обучающая выборка')
plt.plot(history_0.history['val_loss'], label='Валидационная выборка')
plt.title('Функция ошибки по эпохам (Однослойная сеть)')
plt.xlabel('Эпохи')
plt.ylabel('Ошибка')
plt.legend()
plt.grid(True)
plt.show()
```
![график обучения](paragraph_6.png)
---
## 7) Применили обученную модель к тестовым данным. Вывели значение функции ошибки и значение метрики качества классификации на тестовых данных.
```python
# Оценка на тестовых данных
scores_0 = model_0.evaluate(X_test, y_test, verbose=0)
print("Результаты однослойной сети:")
print(f"Ошибка на тестовых данных: {scores_0[0]}")
print(f"Точность на тестовых данных: {scores_0[1]}")
```
```
Результаты однослойной сети:
Ошибка на тестовых данных: 0.28625616431236267
Точность на тестовых данных: 0.92330002784729
```
---
## 8) Добавили в модель один скрытый и провели обучение и тестирование (повторить п. 6–7) при 100, 300, 500 нейронах в скрытом слое. По метрике качества классификации на тестовых данных выбрали наилучшее количество нейронов в скрытом слое. В качестве функции активации нейронов в скрытом слое использовали функцию `sigmoid`.
```python
# Функция для создания и обучения модели
def create_and_train_model(hidden_units, model_name):
model = Sequential()
model.add(Dense(units=hidden_units, input_dim=num_pixels, activation='sigmoid'))
model.add(Dense(units=num_classes, activation='softmax'))
model.compile(loss='categorical_crossentropy',
optimizer='sgd',
metrics=['accuracy'])
history = model.fit(X_train, y_train,
validation_split=0.1,
epochs=50)
scores = model.evaluate(X_test, y_test, verbose=0)
return model, history, scores
# Эксперименты с разным количеством нейронов
hidden_units_list = [100, 300, 500]
models_1 = {}
histories_1 = {}
scores_1 = {}
# Обучение сетей с одним скрытым слоем
for units in hidden_units_list:
print(f"
Обучение модели с {units} нейронами...")
model, history, scores = create_and_train_model(units, f"model_{units}")
models_1[units] = model
histories_1[units] = history
scores_1[units] = scores
print(f"Точность: {scores[1]}")
```
# Определим лучшую модель по итогвой точности
```python
# Выбор наилучшей модели
best_units_1 = max(scores_1.items(), key=lambda x: x[1][1])[0]
print(f"
Наилучшее количество нейронов: {best_units_1}")
print(f"Точность: {scores_1[best_units_1][1]}")
```
```
Наилучшее количество нейронов: 100
Точность: 0.9422000050544739
```
# Отобразим графики ошибок для каждой из архитектур нейросети
```python
# Графики ошибок для всех моделей
plt.figure(figsize=(15, 5))
for i, units in enumerate(hidden_units_list, 1):
plt.subplot(1, 3, i)
plt.plot(histories_1[units].history['loss'], label='Обучающая')
plt.plot(histories_1[units].history['val_loss'], label='Валидационная')
plt.title(f'{units} нейронов')
plt.xlabel('Эпохи')
plt.ylabel('Ошибка')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
```
![график обучения для каждой эпохи](paragraph_8.png)
**По результатам проведнного эксперимента наилучший показатель точности продемонстрировала нейронная сеть со 100 нейронами в скрытом слое - примерно 0.9422.**
---
## 9) Добавили в архитектуру с лучшим показателем из п. 8, второй скрытый слой и провели обучение и тестирование при 50 и 100 нейронах во втором скрытом слое. В качестве функции активации нейронов в скрытом слое использовали функцию `sigmoid`.
```python
# Добавление второго скрытого слоя
second_layer_units = [50, 100]
models_2 = {}
histories_2 = {}
scores_2 = {}
for units_2 in second_layer_units:
print(f"
Обучение модели со вторым слоем {units_2} нейронов")
model = Sequential()
model.add(Dense(units=best_units_1, input_dim=num_pixels, activation='sigmoid'))
model.add(Dense(units=units_2, activation='sigmoid'))
model.add(Dense(units=num_classes, activation='softmax'))
model.compile(loss='categorical_crossentropy',
optimizer='sgd',
metrics=['accuracy'])
history = model.fit(X_train, y_train,
validation_split=0.1,
epochs=50)
scores = model.evaluate(X_test, y_test)
models_2[units_2] = model
histories_2[units_2] = history
scores_2[units_2] = scores
print(f"Точность: {scores[1]}")
```
# Результаты обучения моделей:
```
Обучение модели со вторым слоем 50 нейронов:
Точность: 0.9417999982833862
Обучение модели со вторым слоем 100 нейронов
Точность: 0.942300021648407
```
# Выбор наилучшей двухслойной модели
```python
best_units_2 = max(scores_2.items(), key=lambda x: x[1][1])[0]
print(f"
Наилучшее количество нейронов во втором слое: {best_units_2}")
print(f"Точность: {scores_2[best_units_2][1]}")
```
```
Наилучшее количество нейронов во втором слое: 100
Точность: 0.9423
```
## 10) Результаты исследования архитектуры нейронной сети занесли в таблицу:
```python
# Сбор результатов
results = {
'0 слоев': {'нейроны_1': '-', 'нейроны_2': '-', 'точность': scores_0[1]},
'1 слой_100': {'нейроны_1': 100, 'нейроны_2': '-', 'точность': scores_1[100][1]},
'1 слой_300': {'нейроны_1': 300, 'нейроны_2': '-', 'точность': scores_1[300][1]},
'1 слой_500': {'нейроны_1': 500, 'нейроны_2': '-', 'точность': scores_1[500][1]},
'2 слоя_50': {'нейроны_1': best_units_1, 'нейроны_2': 50, 'точность': scores_2[50][1]},
'2 слоя_100': {'нейроны_1': best_units_1, 'нейроны_2': 100, 'точность': scores_2[100][1]}
}
# Создаем DataFrame из результатов
df_results = pd.DataFrame([
{'Кол-во скрытых слоев': 0, 'Нейроны_1_слоя': '-', 'Нейроны_2_слоя': '-', 'Точность': results['0 слоев']['точность']},
{'Кол-во скрытых слоев': 1, 'Нейроны_1_слоя': 100, 'Нейроны_2_слоя': '-', 'Точность': results['1 слой_100']['точность']},
{'Кол-во скрытых слоев': 1, 'Нейроны_1_слоя': 300, 'Нейроны_2_слоя': '-', 'Точность': results['1 слой_300']['точность']},
{'Кол-во скрытых слоев': 1, 'Нейроны_1_слоя': 500, 'Нейроны_2_слоя': '-', 'Точность': results['1 слой_500']['точность']},
{'Кол-во скрытых слоев': 2, 'Нейроны_1_слоя': best_units_1, 'Нейроны_2_слоя': 50, 'Точность': results['2 слоя_50']['точность']},
{'Кол-во скрытых слоев': 2, 'Нейроны_1_слоя': best_units_1, 'Нейроны_2_слоя': 100, 'Точность': results['2 слоя_100']['точность']}
])
print(" " * 20 + "ТАБЛИЦА РЕЗУЛЬТАТОВ")
print("=" * 70)
# print(df_results.to_string(index=False, formatters={
# 'Точность': '{:.4f}'.format
# }))
print(df_results.reset_index(drop=True))
```
```
ТАБЛИЦА РЕЗУЛЬТАТОВ
======================================================================
Кол-во скрытых слоев Нейроны_1_слоя Нейроны_2_слоя Точность
0 0 - - 0.9233
1 1 100 - 0.9422
2 1 300 - 0.9377
3 1 500 - 0.9312
4 2 100 50 0.9418
5 2 100 100 0.9423
```
```python
# Выбор наилучшей модели
best_model_type = max(results.items(), key=lambda x: x[1]['точность'])[0]
best_accuracy = results[best_model_type]['точность']
print(f"
Наилучшая архитектура: {best_model_type}")
print(f"Точность: {best_accuracy}")
```
```
Наилучшая архитектура: 2 слоя_100
Точность: 0.9423
```
### По результатам исследования сделали выводы и выбрали наилучшую архитектуру нейронной сети с точки зрения качества классификации.
**Из таблицы следует, что лучшей архитектурой является НС с двумя скрытыми слоями по 100 и 50 нейронов, второе место занимает НС с одним скрытым слоем и 100 нейронами, на основе которой мы и строили НС с двумя скрытыми слоями. При увеличении количества нейронов в архитектуре НС с 1-м скрытым слоем в результате тестирования было вявлено, что метрики качества падают. Это связано с переобучение нашей НС с 1-м скрытым слоем (когда построенная модель хорошо объясняет примеры из обучающей выборки, но относительно плохо работает на примерах, не участвовавших в обучении) Такая тенденция вероятно возникает из-за простоты датасета MNIST, при усложнении архитектуры НС начинает переобучаться, а оценка качества на тестовых данных падает. Но также стоит отметить, что при усложнении структуры НС точнсть модели также и растет.**
---
## 11) Сохранили наилучшую нейронную сеть на диск.
```python
# Сохранение модели
best_model.save('best_mnist_model.keras')
```
---
## 12) Для нейронной сети наилучшей архитектуры вывели два тестовых изображения, истинные метки и результат распознавания изображений.
```python
# вывод тестового изображения и результата распознавания (1)
n = 123
result = best_model.predict(X_test[n:n+1])
print('NN output:', result)
plt.imshow(X_test[n].reshape(28,28), cmap=plt.get_cmap('gray'))
plt.show()
print('Real mark: ', str(np.argmax(y_test[n])))
print('NN answer: ', str(np.argmax(result)))
```
![результат 1](paragraph_12_1.png)
```python
# вывод тестового изображения и результата распознавания (3)
n = 353
result = best_model.predict(X_test[n:n+1])
print('NN output:', result)
plt.imshow(X_test[n].reshape(28,28), cmap=plt.get_cmap('gray'))
plt.show()
print('Real mark: ', str(np.argmax(y_test[n])))
print('NN answer: ', str(np.argmax(result)))
```
![результат 2](paragraph_12_3.png)
---
## 13) Создали собственные изображения рукописных цифр, подобное представленным в наборе MNIST. Загрузили, предобработали и подали на вход обученной нейронной сети собственные изображения. Вывели изображения и результаты распознавания.
```python
# загрузка собственного изображения (цифры 2 и 7)
file_data_2 = Image.open('2.png')
file_data_7 = Image.open('7.png')
file_data_2 = file_data_2.convert('L') # перевод в градации серого
file_data_7 = file_data_7.convert('L') # перевод в градации серого
test_img_2 = np.array(file_data_2)
test_img_7 = np.array(file_data_7)
# вывод собственного изображения (цифра 2)
plt.imshow(test_img_2, cmap=plt.get_cmap('gray'))
plt.show()
# предобработка
test_img_2 = test_img_2 / 255
test_img_2 = test_img_2.reshape(1, num_pixels)
# распознавание
result = best_model.predict(test_img_2)
print('I think it\'s ', np.argmax(result))
```
![собственные изображения](2.png)
```
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 31ms/step
```
```python
# вывод собственного изображения (цифра 7)
plt.imshow(test_img_7, cmap=plt.get_cmap('gray'))
plt.show()
# предобработка
test_img_7 = test_img_7 / 255
test_img_7 = test_img_7.reshape(1, num_pixels)
# распознавание
result = best_model.predict(test_img_7)
print('I think it\'s ', np.argmax(result))
```
![собственные изображения](7.png)
```
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 48ms/step
I think it's 7
```
**Как видим в результате эксперимента наша НС недостаточно точно определила изображение цифры 2. Возможно это связано с малом размером используемой выборки. Для улучшения качества решения задачи классификации можно либо увеличить размерность выборки для обучения НС, либо изменить структуру НС для более точной ее работы**
---
## 14) Создать копию собственного изображения, отличающуюся от оригинала поворотом на 90 градусов в любую сторону. Сохранили изображения. Загрузили, предобработали и подали на вход обученной нейронной сети измененные изображения. Вывели изображения и результаты распознавания. Сделали выводы по результатам эксперимента.
```python
# загрузка собственного изображения
file_data_2_90 = Image.open('2_90.png')
file_data_7_90 = Image.open('7_90.png')
file_data_2_90 = file_data_2_90.convert('L') # перевод в градации серого
file_data_7_90 = file_data_7_90.convert('L') # перевод в градации серого
test_img_2_90 = np.array(file_data_2_90)
test_img_7_90= np.array(file_data_7_90)
# вывод собственного изображения (цифра 2)
plt.imshow(test_img_2_90, cmap=plt.get_cmap('gray'))
plt.show()
# предобработка
test_img_2_90 = test_img_2_90 / 255
test_img_2_90 = test_img_2_90.reshape(1, num_pixels)
# распознавание
result = best_model.predict(test_img_2_90)
print('I think it\'s ', np.argmax(result))
```
![собственные изображения повернутые на 90 градусов](2_90.png)
```
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 91ms/step
I think it's 7
```
```python
# вывод собственного изображения (цифра 7)
plt.imshow(test_img_7_90, cmap=plt.get_cmap('gray'))
plt.show()
# предобработка
test_img_7_90 = test_img_7_90 / 255
test_img_7_90 = test_img_7_90.reshape(1, num_pixels)
# распознавание
result = best_model.predict(test_img_7_90)
print('I think it\'s ', np.argmax(result))
```
![собственные изображения повернутые на 90 градусов](7_90.png)
```
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 48ms/step
I think it's 7
```
**При повороте рисунков цифр НС не смогла распознать одну из цифр. Так получилось, во-первых, по той же причине, почему НС не распознала одну из цифр в пункте 13, во-вторых - наша НС не обучалась на перевернутых цифрах**
---

Двоичные данные
labworks/LW1/paragraph_10.png

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

После

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

Двоичные данные
labworks/LW1/paragraph_12_1.png

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

После

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

Двоичные данные
labworks/LW1/paragraph_12_3.png

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

После

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

Двоичные данные
labworks/LW1/paragraph_4.png

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

После

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

Двоичные данные
labworks/LW1/paragraph_6.png

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

После

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

Двоичные данные
labworks/LW1/paragraph_8.png

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

После

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

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

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

@ -1,11 +0,0 @@
## Лабораторныа работа №2
## Обнаружение аномалий
* [Задание](IS_Lab02_2023.pdf)
* [Методические указания](IS_Lab02_Metod_2023.pdf)
* [Наборы данных](data)
* [Библиотека для автокодировщиков](lab02_lib.py)

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

@ -29,14 +29,12 @@ from pandas import DataFrame
from sklearn.metrics import precision_score, recall_score, f1_score, confusion_matrix
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense, Activation
from tensorflow.keras.callbacks import Callback
visual = True
verbose_show = False
# generate 2d classification dataset
def datagen(x_c, y_c, n_samples, n_features):
@ -93,27 +91,8 @@ class EarlyStoppingOnValue(tensorflow.keras.callbacks.Callback):
)
return monitor_value
class VerboseEveryNEpochs(Callback):
def __init__(self, every_n_epochs=1000, verbose=1):
super().__init__()
self.every_n_epochs = every_n_epochs
self.verbose = verbose
def on_epoch_end(self, epoch, logs=None):
if (epoch + 1) % self.every_n_epochs == 0:
if self.verbose:
print(f"\nEpoch {epoch + 1}/{self.params['epochs']}")
if logs:
log_str = ", ".join([f"{k}: {v:.4f}" for k, v in logs.items()])
print(f" - {log_str}")
#создание и обучение модели автокодировщика
def create_fit_save_ae(cl_train, ae_file, irefile, epohs, verbose_show, patience, **kwargs):
verbose_every_n_epochs = kwargs.get('verbose_every_n_epochs', 1000)
early_stopping_delta = kwargs.get('early_stopping_delta', 0.01)
early_stopping_value = kwargs.get('early_stopping_value', 0.0001)
def create_fit_save_ae(cl_train, ae_file, irefile, epohs, verbose_show, patience):
size = cl_train.shape[1]
#ans = '2'
@ -161,28 +140,22 @@ def create_fit_save_ae(cl_train, ae_file, irefile, epohs, verbose_show, patience
optimizer = tensorflow.keras.optimizers.Adam(learning_rate=0.001, beta_1=0.9, beta_2=0.999, amsgrad=False)
ae.compile(loss='mean_squared_error', optimizer=optimizer)
error_stop = 0.0001
epo = epohs
verbose = 1 if verbose_show else 0
early_stopping_callback_on_error = EarlyStoppingOnValue(monitor='loss', baseline=early_stopping_value)
early_stopping_callback_on_error = EarlyStoppingOnValue(monitor='loss', baseline=error_stop)
early_stopping_callback_on_improving = tensorflow.keras.callbacks.EarlyStopping(monitor='loss',
min_delta=early_stopping_delta, patience = patience,
verbose=verbose, mode='min',
min_delta=0.0001, patience = patience,
verbose=1, mode='auto',
baseline=None,
restore_best_weights=True)
restore_best_weights=False)
history_callback = tensorflow.keras.callbacks.History()
verbose = 1 if verbose_show else 0
history_object = ae.fit(cl_train, cl_train,
batch_size=cl_train.shape[0],
epochs=epo,
callbacks=[
early_stopping_callback_on_error,
history_callback,
early_stopping_callback_on_improving,
VerboseEveryNEpochs(every_n_epochs=verbose_every_n_epochs),
],
callbacks=[early_stopping_callback_on_error, history_callback,
early_stopping_callback_on_improving],
verbose=verbose)
ae_trainned = ae
ae_pred = ae_trainned.predict(cl_train)
@ -565,4 +538,4 @@ def ire_plot(title, IRE_test, IREth, ae_name):
plt.gcf().savefig('out/IRE_' + title + ae_name + '.png')
plt.show()
return
return

@ -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
}

Двоичные данные
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

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

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

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

После

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

@ -0,0 +1,478 @@
# Отчёт по лабораторной работе №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
# Обучение автокодировщика для Letter на НЕнормализованных данных
# Входной и выходной слои создаются автоматически по размеру данных (32 признака)
# Мы задаем только скрытые слои
# Полная архитектура: 32(вход) -> 100 -> 68 -> 48 -> 32 -> 24 -> 16 -> 8 -> 16 -> 24 -> 32 -> 48 -> 64 -> 100 -> 32(выход)
# 11 скрытых слоев: 100 64 48 32 24 16 8 16 24 32 48 64 100
import warnings
# Подавляем предупреждение о формате сохранения HDF5
warnings.filterwarnings('ignore', category=UserWarning, module='absl')
print('Обучение автокодировщика для данных Letter (НЕнормализованные данные)...')
print('Архитектура: 32(вход) -> 100 -> 68 -> 48 -> 32 -> 24 -> 16 -> 8 -> 16 -> 24 -> 32 -> 48 -> 64 -> 100 -> 32(выход)')
# Автоматизируем ввод для выбора пользовательской архитектуры
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']):
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',
100000, # epochs
False, # verbose_show
10000, # patience
verbose_every_n_epochs=500
)
# Вычисление MSE из предсказания
X_pred_letter_raw = ae_letter_raw.predict(train_letter, verbose=0)
mse_letter_raw = np.mean((train_letter - X_pred_letter_raw) ** 2)
print(f'\nОбучение завершено (НЕнормализованные данные)!')
print(f'MSE_stop (приблизительно): {mse_letter_raw:.6f}')
print(f'Порог IRE: {IREth_letter_raw:.6f}')
print(f'Количество скрытых слоев: 11')
print(f'Количество нейронов в скрытых слоях: 100 -> 68 -> 48 -> 32 -> 24 -> 16 -> 8 -> 16 -> 24 -> 32 -> 48 -> 64 -> 100')
```
Архитектура: 32 → 100 → 68 → 48 → 32 → 24 → 16 → 8 → 16 → 24 → 32 → 48 → 64 → 100 → 32
Количество скрытых слоёв: 13
Нейроны в скрытых слоях: 100 → 68 → 48 → 32 → 24 → 16 → 8 → 16 → 24 → 32 → 48 → 64 → 100
### 6) Обучение автокодировщика на нормализованных данных
Создан и обучен автокодировщик с той же архитектурой на нормализованных данных для сравнения результатов.
```python
# Обучение автокодировщика для Letter на нормализованных данных
# Та же архитектура для сравнения: 32(вход) -> 100 -> 68 -> 48 -> 32 -> 24 -> 16 -> 8 -> 16 -> 24 -> 32 -> 48 -> 64 -> 100 -> 32(выход)
# 11 скрытых слоев: 100 86 72 64 48 32 24 16 8 16 24 32 48 64 72 86 100
print('Обучение автокодировщика для данных Letter (нормализованные данные)...')
print('Архитектура: 32(вход) -> 100 -> 68 -> 48 -> 32 -> 24 -> 16 -> 8 -> 16 -> 24 -> 32 -> 48 -> 64 -> 100 -> 32(выход)')
# Автоматизируем ввод для выбора пользовательской архитектуры
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']):
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
verbose_every_n_epochs=500
)
# Вычисление MSE из предсказания
X_pred_letter_norm = ae_letter_norm.predict(train_letter_normalized, verbose=0)
mse_letter_norm = np.mean((train_letter_normalized - X_pred_letter_norm) ** 2)
print(f'\nОбучение завершено (нормализованные данные)!')
print(f'MSE_stop (приблизительно): {mse_letter_norm:.6f}')
print(f'Порог IRE: {IREth_letter_norm:.6f}')
print(f'Количество скрытых слоев: 11')
print(f'Количество нейронов в скрытых слоях: 100 -> 68 -> 48 -> 32 -> 24 -> 16 -> 8 -> 16 -> 24 -> 32 -> 48 -> 64 -> 100')
```
### 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 | 13 | 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) позволяет лучше аппроксимировать сложные зависимости в многомерных данных.
**Требования к количеству эпох обучения:**
- Для многомерных данных требуется значительно большее количество эпох обучения по сравнению с двумерными данными (1000-3000 эпох).
- Использование ранней остановки (early stopping) позволяет автоматически завершить обучение при достижении оптимальных значений.
**Требования к ошибке MSE_stop:**
- Для многомерных данных оптимальная ошибка MSE-stop в районе 0.01-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.01-0.1, порог ≤ 1.6.
4. **Метрики качества**: Характеристики EDCA (Excess, Deficit, Coating, Approx) позволяют оценить качество аппроксимации области нормальных данных и пригодность автокодировщика для обнаружения аномалий.
5. **Нормализация данных**: Для многомерных данных нормализация признаков значительно улучшает сходимость обучения и качество обнаружения аномалий. Экспериментально подтверждено, что нормализация снижает MSE с 0.340 до 0.026 и порог IRE с 11.1 до 1.62, что приводит к обнаружению 100% аномалий в тестовой выборке.

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

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

Двоичные данные
labworks/LW3/2.png

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

После

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

Двоичные данные
labworks/LW3/7.png

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

После

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

@ -1,9 +0,0 @@
## Лабораторныа работа №3
## Распознавание изображений
* [Задание](IS_Lab03_2023.pdf)
* [Методические указания](IS_Lab03_Metod_2023.pdf)
* <a href="https://youtube.com/playlist?list=PLZDCDMGmelH-pHt-Ij0nImVrOmj8DYKbB" target="_blank">Плейлист с видео о сверточных сетях</a>

Двоичные данные
labworks/LW3/best_mnist_model.keras

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

Двоичные данные
labworks/LW3/images/1.png

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

После

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

Двоичные данные
labworks/LW3/images/2.png

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

После

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

Двоичные данные
labworks/LW3/images/3.png

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

После

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

Двоичные данные
labworks/LW3/images/4.png

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

После

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

Двоичные данные
labworks/LW3/images/5.png

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

После

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

Двоичные данные
labworks/LW3/images/6.png

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

После

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

Двоичные данные
labworks/LW3/images/7.png

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

После

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

Двоичные данные
labworks/LW3/images/8.png

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

После

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

Двоичные данные
labworks/LW3/images/9.png

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

После

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

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

@ -0,0 +1,815 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "oZs0KGcz01BY"
},
"source": [
"## Задание 1"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "gz18QPRz03Ec"
},
"source": [
"### 1) Подготовка рабочей среды и импорт библиотек\n",
"\n",
"Инициализируем рабочую среду Google Colab и подключаем необходимые библиотеки для работы с нейронными сетями и обработки данных."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "mr9IszuQ1ANG"
},
"outputs": [],
"source": [
"# Подключение необходимых библиотек и модулей\n",
"import os\n",
"\n",
"\n",
"from tensorflow import keras\n",
"from tensorflow.keras import layers\n",
"from tensorflow.keras.models import Sequential\n",
"import matplotlib.pyplot as plt\n",
"import numpy as np\n",
"from sklearn.metrics import classification_report, confusion_matrix\n",
"from sklearn.metrics import ConfusionMatrixDisplay"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "FFRtE0TN1AiA"
},
"source": [
"### 2) Загрузка датасета MNIST\n",
"\n",
"Загружаем стандартный набор данных MNIST, который содержит изображения рукописных цифр от 0 до 9 с соответствующими метками."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "Ixw5Sp0_1A-w"
},
"outputs": [],
"source": [
"# Импорт и загрузка датасета MNIST\n",
"from keras.datasets import mnist\n",
"(X_train, y_train), (X_test, y_test) = mnist.load_data()"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "aCo_lUXl1BPV"
},
"source": [
"### 3) Разделение данных на обучающую и тестовую выборки\n",
"\n",
"Производим собственное разбиение датасета в соотношении 60 000:10 000. Для воспроизводимости результатов используем параметр random_state = 3 (вычисляется как 4k - 1, где k = 1 - номер нашей бригады)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "BrSjcpEe1BeV"
},
"outputs": [],
"source": [
"# Создание собственного разбиения датасета\n",
"from sklearn.model_selection import train_test_split\n",
"\n",
"# Объединение исходных обучающей и тестовой выборок в единый набор\n",
"X = np.concatenate((X_train, X_test))\n",
"y = np.concatenate((y_train, y_test))\n",
"\n",
"# Разделение на обучающую и тестовую выборки согласно заданию\n",
"X_train, X_test, y_train, y_test = train_test_split(X, y,\n",
" test_size = 10000,\n",
" train_size = 60000,\n",
" random_state = 3)\n",
"# Вывод размерностей полученных массивов\n",
"print('Shape of X train:', X_train.shape)\n",
"print('Shape of y train:', y_train.shape)\n",
"print('Shape of X test:', X_test.shape)\n",
"print('Shape of y test:', y_test.shape)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "4hclnNaD1BuB"
},
"source": [
"### 4) Предобработка данных\n",
"\n",
"Выполняем нормализацию пикселей изображений (приведение к диапазону [0, 1]) и преобразование меток в формат one-hot encoding для корректной работы с категориальной функцией потерь."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "xJH87ISq1B9h"
},
"outputs": [],
"source": [
"# Определение параметров данных и модели\n",
"num_classes = 10\n",
"input_shape = (28, 28, 1)\n",
"\n",
"# Нормализация значений пикселей: приведение к диапазону [0, 1]\n",
"X_train = X_train / 255\n",
"X_test = X_test / 255\n",
"\n",
"# Расширяем размерность входных данных, чтобы каждое изображение имело\n",
"# размерность (высота, ширина, количество каналов)\n",
"\n",
"X_train = np.expand_dims(X_train, -1)\n",
"X_test = np.expand_dims(X_test, -1)\n",
"print('Shape of transformed X train:', X_train.shape)\n",
"print('Shape of transformed X test:', X_test.shape)\n",
"\n",
"# Преобразование меток в формат one-hot encoding\n",
"y_train = keras.utils.to_categorical(y_train, num_classes)\n",
"y_test = keras.utils.to_categorical(y_test, num_classes)\n",
"print('Shape of transformed y train:', y_train.shape)\n",
"print('Shape of transformed y test:', y_test.shape)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "7x99O8ig1CLh"
},
"source": [
"### 5) Построение и обучение сверточной нейронной сети\n",
"\n",
"Создаем архитектуру сверточной нейронной сети с использованием сверточных слоев, пулинга и регуляризации. Обучаем модель на подготовленных данных с выделением части данных для валидации."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "Un561zSH1Cmv"
},
"outputs": [],
"source": [
"# Создание модели сверточной нейронной сети\n",
"model = Sequential()\n",
"model.add(layers.Conv2D(32, kernel_size=(3, 3), activation=\"relu\", input_shape=input_shape))\n",
"model.add(layers.MaxPooling2D(pool_size=(2, 2)))\n",
"model.add(layers.Conv2D(64, kernel_size=(3, 3), activation=\"relu\"))\n",
"model.add(layers.MaxPooling2D(pool_size=(2, 2)))\n",
"model.add(layers.Dropout(0.5))\n",
"model.add(layers.Flatten())\n",
"model.add(layers.Dense(num_classes, activation=\"softmax\"))\n",
"\n",
"model.summary()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "q_h8PxkN9m0v"
},
"outputs": [],
"source": [
"# Компиляция и обучение модели\n",
"batch_size = 512\n",
"epochs = 15\n",
"model.compile(loss=\"categorical_crossentropy\", optimizer=\"adam\", metrics=[\"accuracy\"])\n",
"model.fit(X_train, y_train, batch_size=batch_size, epochs=epochs, validation_split=0.1)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "HL2_LVga1C3l"
},
"source": [
"### 6) Оценка качества модели на тестовых данных\n",
"\n",
"Проводим финальную оценку обученной модели на независимой тестовой выборке, получая значения функции потерь и точности классификации."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "81Cgq8dn9uL6"
},
"outputs": [],
"source": [
"# Оценка качества работы обученной модели на тестовой выборке\n",
"scores = model.evaluate(X_test, y_test)\n",
"print('Loss on test data:', scores[0])\n",
"print('Accuracy on test data:', scores[1])"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "KzrVY1SR1DZh"
},
"source": [
"### 7) Демонстрация работы модели на отдельных примерах\n",
"\n",
"Визуализируем результаты распознавания для двух тестовых изображений, сравнивая предсказания модели с истинными метками."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "dbfkWjDI1Dp7"
},
"outputs": [],
"source": [
"# Визуализация результатов распознавания для двух тестовых изображений\n",
"\n",
"for n in [3,26]:\n",
" result = model.predict(X_test[n:n+1])\n",
" print('NN output:', result)\n",
"\n",
" plt.imshow(X_test[n].reshape(28,28), cmap=plt.get_cmap('gray'))\n",
" plt.show()\n",
" print('Real mark: ', np.argmax(y_test[n]))\n",
" print('NN answer: ', np.argmax(result))"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "YgiVGr5_1D3u"
},
"source": [
"### 8) Детальный анализ качества классификации\n",
"\n",
"Генерируем подробный отчет о качестве классификации и строим матрицу ошибок для визуального анализа работы модели по каждому классу."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "7MqcG_wl1EHI"
},
"outputs": [],
"source": [
"# Получение истинных и предсказанных меток для всех тестовых данных\n",
"true_labels = np.argmax(y_test, axis=1)\n",
"\n",
"predicted_labels = np.argmax(model.predict(X_test), axis=1)\n",
"\n",
"# Вывод подробного отчета о качестве классификации\n",
"print(classification_report(true_labels, predicted_labels))\n",
"# Построение и визуализация матрицы ошибок\n",
"conf_matrix = confusion_matrix(true_labels, predicted_labels)\n",
"\n",
"display = ConfusionMatrixDisplay(confusion_matrix=conf_matrix)\n",
"display.plot()\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "amaspXGW1EVy"
},
"source": [
"### 9) Тестирование на собственных изображениях\n",
"\n",
"Загружаем и обрабатываем собственные изображения цифр, созданные ранее, и проверяем способность модели их корректно распознавать."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "ktWEeqWd1EyF"
},
"outputs": [],
"source": [
"# Загрузка и обработка собственных изображений\n",
"from PIL import Image\n",
"\n",
"for name_image in ['2.png', '7.png']:\n",
" file_data = Image.open(name_image)\n",
" file_data = file_data.convert('L') # перевод в градации серого\n",
" test_img = np.array(file_data)\n",
"\n",
" # вывод собственного изображения\n",
" plt.imshow(test_img, cmap=plt.get_cmap('gray'))\n",
" plt.show()\n",
"\n",
" # предобработка\n",
" test_img = test_img / 255\n",
" test_img = np.reshape(test_img, (1,28,28,1))\n",
"\n",
" # распознавание\n",
" result = model.predict(test_img)\n",
" print('I think it\\'s', np.argmax(result))"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "mgrihPd61E8w"
},
"source": [
"### 10) Сравнение с моделью из предыдущей лабораторной работы\n",
"\n",
"Загружаем сохраненную полносвязную нейронную сеть из лабораторной работы №1 и оцениваем ее производительность на тех же тестовых данных для последующего сравнения."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "DblXqn3l1FL2"
},
"outputs": [],
"source": [
"model_lr1 = keras.models.load_model(\"best_mnist_model.keras\")\n",
"\n",
"model_lr1.summary()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "0ki8fhJrEyEt"
},
"outputs": [],
"source": [
"# Подготовка данных для полносвязной сети (преобразование изображений в векторы)\n",
"X_train, X_test, y_train, y_test = train_test_split(X, y,\n",
" test_size = 10000,\n",
" train_size = 60000,\n",
" random_state = 3)\n",
"num_pixels = X_train.shape[1] * X_train.shape[2]\n",
"X_train = X_train.reshape(X_train.shape[0], num_pixels) / 255\n",
"X_test = X_test.reshape(X_test.shape[0], num_pixels) / 255\n",
"print('Shape of transformed X train:', X_train.shape)\n",
"print('Shape of transformed X train:', X_test.shape)\n",
"\n",
"# Преобразование меток в формат one-hot encoding\n",
"y_train = keras.utils.to_categorical(y_train, num_classes)\n",
"y_test = keras.utils.to_categorical(y_test, num_classes)\n",
"print('Shape of transformed y train:', y_train.shape)\n",
"print('Shape of transformed y test:', y_test.shape)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "0Yj0fzLNE12k"
},
"outputs": [],
"source": [
"# Оценка качества работы обученной модели на тестовой выборке\n",
"scores = model_lr1.evaluate(X_test, y_test)\n",
"print('Loss on test data:', scores[0])\n",
"print('Accuracy on test data:', scores[1])"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "MsM3ew3d1FYq"
},
"source": [
"### 11) Сравнительный анализ моделей\n",
"\n",
"Сравниваем сверточную нейронную сеть с полносвязной сетью по ключевым показателям: количеству параметров, времени обучения и качеству классификации."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "xxFO4CXbIG88"
},
"source": [
"Таблица1:"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "xvoivjuNFlEf"
},
"source": [
"| Модель | Количество настраиваемых параметров | Количество эпох обучения | Качество классификации тестовой выборки |\n",
"|----------|-------------------------------------|---------------------------|-----------------------------------------|\n",
"| Сверточная | 34 826 | 15 | accuracy: 0.988; loss: 0.041 |\n",
"| Полносвязная | 7 852 | 50 | accuracy: 0.923; loss: 0.286 |\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "YctF8h_sIB-P"
},
"source": [
"**Выводы:**\n",
"\n",
"На основе проведенного анализа можно заключить, что сверточная нейронная сеть демонстрирует существенные преимущества перед полносвязной сетью при решении задач распознавания изображений:\n",
"\n",
"1. **Эффективность параметров**: Сверточная сеть имеет больше параметров (34 826 против 7 852), но при этом показывает значительно лучшие результаты, что говорит о более эффективном использовании параметров для извлечения пространственных признаков.\n",
"\n",
"2. **Скорость обучения**: Для достижения высокого качества сверточной сети требуется в 3.3 раза меньше эпох обучения (15 против 50), что существенно сокращает время обучения.\n",
"\n",
"3. **Точность классификации**: Сверточная сеть показывает более высокую точность (98.8% против 92.3%) и значительно меньшую функцию потерь (0.041 против 0.286). Разница в точности составляет 6.5%, что является существенным улучшением.\n",
"\n",
"4. **Обобщающая способность**: Сверточная сеть демонстрирует лучшую способность к обобщению, что видно из более низкой функции потерь на тестовых данных.\n",
"\n",
"Эти результаты подтверждают, что архитектура сверточных сетей, учитывающая пространственную структуру изображений через операции свертки и пулинга, является более подходящим выбором для задач компьютерного зрения, несмотря на большее количество параметров."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "wCLHZPGB1F1y"
},
"source": [
"## Задание 2"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "DUOYls124TT8"
},
"source": [
"### В новом блокноте выполнили п. 2–8 задания 1, изменив набор данных MNIST на CIFAR-10, содержащий размеченные цветные изображения объектов, разделенные на 10 классов. \n",
"### При этом:\n",
"### - в п. 3 разбиение данных на обучающие и тестовые произвели в соотношении 50 000:10 000\n",
"### - после разбиения данных (между п. 3 и 4) вывели 25 изображений из обучающей выборки с подписями классов\n",
"### - в п. 7 одно из тестовых изображений должно распознаваться корректно, а другое – ошибочно. "
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "XDStuSpEJa8o"
},
"source": [
"### 1) Загрузка датасета CIFAR-10\n",
"\n",
"Загружаем набор данных CIFAR-10, который содержит цветные изображения размером 32x32 пикселя, разделенные на 10 классов: самолет, автомобиль, птица, кошка, олень, собака, лягушка, лошадь, корабль, грузовик."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "y0qK7eKL4Tjy"
},
"outputs": [],
"source": [
"# Импорт и загрузка датасета MNIST\n",
"from keras.datasets import cifar10\n",
"\n",
"(X_train, y_train), (X_test, y_test) = cifar10.load_data()"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "wTHiBy-ZJ5oh"
},
"source": [
"### 2) Разделение данных на обучающую и тестовую выборки\n",
"\n",
"Создаем собственное разбиение датасета CIFAR-10 в соотношении 50 000:10 000. Используем random_state = 3 для воспроизводимости результатов (k = 1 - номер нашей бригады)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "DlnFbQogKD2v"
},
"outputs": [],
"source": [
"# Создание собственного разбиения датасета\n",
"\n",
"# Объединение исходных обучающей и тестовой выборок в единый набор\n",
"X = np.concatenate((X_train, X_test))\n",
"y = np.concatenate((y_train, y_test))\n",
"\n",
"# Разделение на обучающую и тестовую выборки согласно заданию\n",
"X_train, X_test, y_train, y_test = train_test_split(X, y,\n",
" test_size = 10000,\n",
" train_size = 50000,\n",
" random_state = 3)\n",
"# Вывод размерностей полученных массивов\n",
"print('Shape of X train:', X_train.shape)\n",
"print('Shape of y train:', y_train.shape)\n",
"print('Shape of X test:', X_test.shape)\n",
"print('Shape of y test:', y_test.shape)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "pj3bMaz1KZ3a"
},
"source": [
"### Визуализация примеров из обучающей выборки\n",
"\n",
"Отображаем сетку из 25 изображений из обучающей выборки с подписями соответствующих классов для визуального ознакомления с данными."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "TW8D67KEKhVE"
},
"outputs": [],
"source": [
"class_names = ['airplane', 'automobile', 'bird', 'cat', 'deer',\n",
" 'dog', 'frog', 'horse', 'ship', 'truck']\n",
"\n",
"plt.figure(figsize=(10,10))\n",
"for i in range(25):\n",
" plt.subplot(5,5,i+1)\n",
" plt.xticks([])\n",
" plt.yticks([])\n",
" plt.grid(False)\n",
" plt.imshow(X_train[i])\n",
" plt.xlabel(class_names[y_train[i][0]])\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "d3TPr2w1KQTK"
},
"source": [
"### 3) Предобработка данных CIFAR-10\n",
"\n",
"Нормализуем значения пикселей и преобразуем метки в формат one-hot encoding для работы с категориальной функцией потерь."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "iFDpxEauLZ8j"
},
"outputs": [],
"source": [
"# Определение параметров данных и модели\n",
"num_classes = 10\n",
"input_shape = (32, 32, 3)\n",
"\n",
"# Нормализация значений пикселей: приведение к диапазону [0, 1]\n",
"X_train = X_train / 255\n",
"X_test = X_test / 255\n",
"\n",
"print('Shape of transformed X train:', X_train.shape)\n",
"print('Shape of transformed X test:', X_test.shape)\n",
"\n",
"# Преобразование меток в формат one-hot encoding\n",
"y_train = keras.utils.to_categorical(y_train, num_classes)\n",
"y_test = keras.utils.to_categorical(y_test, num_classes)\n",
"print('Shape of transformed y train:', y_train.shape)\n",
"print('Shape of transformed y test:', y_test.shape)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "ydNITXptLeGT"
},
"source": [
"### 4) Построение и обучение сверточной сети для CIFAR-10\n",
"\n",
"Создаем более сложную архитектуру сверточной сети с использованием батч-нормализации и нескольких блоков свертки для работы с цветными изображениями."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "YhAD5CllLlv7"
},
"outputs": [],
"source": [
"# Создание модели сверточной нейронной сети\n",
"model = Sequential()\n",
"\n",
"# Блок 1\n",
"model.add(layers.Conv2D(32, (3, 3), padding=\"same\",\n",
" activation=\"relu\", input_shape=input_shape))\n",
"model.add(layers.BatchNormalization())\n",
"model.add(layers.Conv2D(32, (3, 3), padding=\"same\", activation=\"relu\"))\n",
"model.add(layers.BatchNormalization())\n",
"model.add(layers.MaxPooling2D((2, 2)))\n",
"model.add(layers.Dropout(0.25))\n",
"\n",
"# Блок 2\n",
"model.add(layers.Conv2D(64, (3, 3), padding=\"same\", activation=\"relu\"))\n",
"model.add(layers.BatchNormalization())\n",
"model.add(layers.Conv2D(64, (3, 3), padding=\"same\", activation=\"relu\"))\n",
"model.add(layers.BatchNormalization())\n",
"model.add(layers.MaxPooling2D((2, 2)))\n",
"model.add(layers.Dropout(0.25))\n",
"\n",
"# Блок 3\n",
"model.add(layers.Conv2D(128, (3, 3), padding=\"same\", activation=\"relu\"))\n",
"model.add(layers.BatchNormalization())\n",
"model.add(layers.Conv2D(128, (3, 3), padding=\"same\", activation=\"relu\"))\n",
"model.add(layers.BatchNormalization())\n",
"model.add(layers.MaxPooling2D((2, 2)))\n",
"model.add(layers.Dropout(0.4))\n",
"\n",
"model.add(layers.Flatten())\n",
"model.add(layers.Dense(128, activation='relu'))\n",
"model.add(layers.Dropout(0.5))\n",
"model.add(layers.Dense(num_classes, activation=\"softmax\"))\n",
"\n",
"\n",
"model.summary()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "3otvqMjjOdq5"
},
"outputs": [],
"source": [
"# Компиляция и обучение модели\n",
"batch_size = 64\n",
"epochs = 50\n",
"model.compile(loss=\"categorical_crossentropy\", optimizer=\"adam\", metrics=[\"accuracy\"])\n",
"model.fit(X_train, y_train, batch_size=batch_size, epochs=epochs, validation_split=0.1)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "Vv1kUHWTLl9B"
},
"source": [
"### 5) Оценка качества модели на тестовых данных\n",
"\n",
"Оцениваем финальную производительность обученной модели на тестовой выборке CIFAR-10."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "SaDxydiyLmRX"
},
"outputs": [],
"source": [
"# Оценка качества работы обученной модели на тестовой выборке\n",
"scores = model.evaluate(X_test, y_test)\n",
"print('Loss on test data:', scores[0])\n",
"print('Accuracy on test data:', scores[1])"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "OdgEiyUGLmhP"
},
"source": [
"### 6) Демонстрация работы модели на отдельных примерах\n",
"\n",
"Визуализируем результаты распознавания для двух тестовых изображений: одно должно быть распознано корректно, другое - ошибочно."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "t3yGj1MlLm9H"
},
"outputs": [],
"source": [
"# Визуализация результатов распознавания для двух тестовых изображений\n",
"\n",
"for n in [3,14]:\n",
" result = model.predict(X_test[n:n+1])\n",
" print('NN output:', result)\n",
"\n",
" plt.imshow(X_test[n].reshape(32,32,3), cmap=plt.get_cmap('gray'))\n",
" plt.show()\n",
" print('Real mark: ', np.argmax(y_test[n]))\n",
" print('NN answer: ', np.argmax(result))"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "3h6VGDRrLnNC"
},
"source": [
"### 7) Детальный анализ качества классификации CIFAR-10\n",
"\n",
"Генерируем подробный отчет о качестве классификации и строим матрицу ошибок для анализа работы модели по каждому классу."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "od56oyyzM0nw"
},
"outputs": [],
"source": [
"# Получение истинных и предсказанных меток для всех тестовых данных\n",
"true_labels = np.argmax(y_test, axis=1)\n",
"\n",
"predicted_labels = np.argmax(model.predict(X_test), axis=1)\n",
"\n",
"# Вывод подробного отчета о качестве классификации\n",
"print(classification_report(true_labels, predicted_labels, target_names=class_names))\n",
"# Построение и визуализация матрицы ошибок\n",
"conf_matrix = confusion_matrix(true_labels, predicted_labels)\n",
"\n",
"fig, ax = plt.subplots(figsize=(6, 6))\n",
"disp = ConfusionMatrixDisplay(confusion_matrix=conf_matrix,display_labels=class_names)\n",
"disp.plot(ax=ax, xticks_rotation=45) # поворот подписей по X и приятная палитра\n",
"plt.tight_layout() # чтобы всё влезло\n",
"plt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "RF4xK1cxamBc"
},
"source": [
"**Выводы по результатам классификации CIFAR-10:**\n",
"\n",
"Разработанная сверточная нейронная сеть показала хорошие результаты при классификации цветных изображений из датасета CIFAR-10. Модель достигла точности классификации около 86%, что является достойным результатом для данной задачи, учитывая сложность различения объектов в низком разрешении (32x32 пикселя) и наличие 10 различных классов.\n",
"\n",
"Использование батч-нормализации и dropout-регуляризации позволило улучшить обобщающую способность модели и предотвратить переобучение. Архитектура с тремя блоками сверточных слоев эффективно извлекает иерархические признаки из изображений, что подтверждается полученными метриками качества."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
},
{
"cell_type": "markdown",
"metadata": {},
"source": []
}
],
"metadata": {
"accelerator": "GPU",
"colab": {
"gpuType": "T4",
"provenance": []
},
"kernelspec": {
"display_name": "Python 3",
"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": 0
}

@ -0,0 +1,612 @@
# Отчёт по лабораторной работе №3
**Троянов Д.С., Чернов Д.Е. — А-01-22**
---
## Задание 1
### 1) Подготовка рабочей среды и импорт библиотек
Инициализируем рабочую среду и подключаем необходимые библиотеки для работы с нейронными сетями и обработки данных. Также настраиваем SSL для корректной загрузки датасетов.
```python
# Подключение необходимых библиотек и модулей
import os
import ssl
# Обход проблемы с SSL сертификатами на macOS
ssl._create_default_https_context = ssl._create_unverified_context
# Для работы в Google Colab раскомментируйте следующую строку:
# os.chdir('/content/drive/MyDrive/Colab Notebooks/is_lab3')
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras.models import Sequential
import matplotlib.pyplot as plt
import numpy as np
from sklearn.metrics import classification_report, confusion_matrix
from sklearn.metrics import ConfusionMatrixDisplay
```
### 2) Загрузка датасета MNIST
Загружаем стандартный набор данных MNIST, который содержит изображения рукописных цифр от 0 до 9 с соответствующими метками.
```python
# Импорт и загрузка датасета MNIST
from keras.datasets import mnist
(X_train, y_train), (X_test, y_test) = mnist.load_data()
```
### 3) Разделение данных на обучающую и тестовую выборки
Производим собственное разбиение датасета в соотношении 60 000:10 000. Для воспроизводимости результатов используем параметр random_state = 3 (вычисляется как 4k - 1, где k = 1 - номер нашей бригады). Выводим размерности полученных массивов данных.
```python
# Создание собственного разбиения датасета
from sklearn.model_selection import train_test_split
# Объединение исходных обучающей и тестовой выборок в единый набор
X = np.concatenate((X_train, X_test))
y = np.concatenate((y_train, y_test))
# Разделение на обучающую и тестовую выборки согласно заданию
X_train, X_test, y_train, y_test = train_test_split(X, y,
test_size = 10000,
train_size = 60000,
random_state = 3)
# Вывод размерностей полученных массивов
print('Shape of X train:', X_train.shape)
print('Shape of y train:', y_train.shape)
print('Shape of X test:', X_test.shape)
print('Shape of y test:', y_test.shape)
```
```
Shape of X train: (60000, 28, 28)
Shape of y train: (60000,)
Shape of X test: (10000, 28, 28)
Shape of y test: (10000,)
```
### 4) Предобработка данных
Выполняем нормализацию пикселей изображений (приведение к диапазону [0, 1]) и преобразование меток в формат one-hot encoding для корректной работы с категориальной функцией потерь. Выводим размерности предобработанных массивов данных.
```python
# Определение параметров данных и модели
num_classes = 10
input_shape = (28, 28, 1)
# Нормализация значений пикселей: приведение к диапазону [0, 1]
X_train = X_train / 255
X_test = X_test / 255
# Добавление размерности канала для корректной работы с Conv2D слоями
# Преобразование из (высота, ширина) в (высота, ширина, каналы)
X_train = np.expand_dims(X_train, -1)
X_test = np.expand_dims(X_test, -1)
print('Shape of transformed X train:', X_train.shape)
print('Shape of transformed X test:', X_test.shape)
# Преобразование меток в формат one-hot encoding
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
print('Shape of transformed y train:', y_train.shape)
print('Shape of transformed y test:', y_test.shape)
```
```
Shape of transformed X train: (60000, 28, 28, 1)
Shape of transformed X test: (10000, 28, 28, 1)
Shape of transformed y train: (60000, 10)
Shape of transformed y test: (10000, 10)
```
### 5) Построение и обучение сверточной нейронной сети
Создаем архитектуру сверточной нейронной сети с использованием сверточных слоев, пулинга и регуляризации. Обучаем модель на подготовленных данных с выделением части данных для валидации. Выводим информацию об архитектуре нейронной сети.
```python
# Создание модели сверточной нейронной сети
model = Sequential()
model.add(layers.Conv2D(32, kernel_size=(3, 3), activation="relu", input_shape=input_shape))
model.add(layers.MaxPooling2D(pool_size=(2, 2)))
model.add(layers.Conv2D(64, kernel_size=(3, 3), activation="relu"))
model.add(layers.MaxPooling2D(pool_size=(2, 2)))
model.add(layers.Dropout(0.5))
model.add(layers.Flatten())
model.add(layers.Dense(num_classes, activation="softmax"))
model.summary()
```
**Model: "sequential"**
| Layer (type) | Output Shape | Param # |
|--------------------------------|---------------------|--------:|
| conv2d (Conv2D) | (None, 26, 26, 32) | 320 |
| max_pooling2d (MaxPooling2D) | (None, 13, 13, 32) | 0 |
| conv2d_1 (Conv2D) | (None, 11, 11, 64) | 18,496 |
| max_pooling2d_1 (MaxPooling2D) | (None, 5, 5, 64) | 0 |
| dropout (Dropout) | (None, 5, 5, 64) | 0 |
| flatten (Flatten) | (None, 1600) | 0 |
| dense (Dense) | (None, 10) | 16,010 |
**Total params:** 34,826 (136.04 KB)
**Trainable params:** 34,826 (136.04 KB)
**Non-trainable params:** 0 (0.00 B)
```python
# Компиляция и обучение модели
batch_size = 512
epochs = 15
model.compile(loss="categorical_crossentropy", optimizer="adam", metrics=["accuracy"])
model.fit(X_train, y_train, batch_size=batch_size, epochs=epochs, validation_split=0.1)
```
### 6) Оценка качества модели на тестовых данных
Проводим финальную оценку обученной модели на независимой тестовой выборке, получая значения функции потерь и точности классификации.
```python
# Оценка качества работы обученной модели на тестовой выборке
scores = model.evaluate(X_test, y_test)
print('Loss on test data:', scores[0])
print('Accuracy on test data:', scores[1])
```
```
313/313 ━━━━━━━━━━━━━━━━━━━━ 1s 4ms/step - accuracy: 0.9884 - loss: 0.0409
Loss on test data: 0.04092026501893997
Accuracy on test data: 0.9883999824523926
```
### 7) Демонстрация работы модели на отдельных примерах
Визуализируем результаты распознавания для двух тестовых изображений, сравнивая предсказания модели с истинными метками.
```python
# Визуализация результатов распознавания для двух тестовых изображений
for n in [3,26]:
result = model.predict(X_test[n:n+1])
print('NN output:', result)
plt.imshow(X_test[n].reshape(28,28), cmap=plt.get_cmap('gray'))
plt.show()
print('Real mark: ', np.argmax(y_test[n]))
print('NN answer: ', np.argmax(result))
```
![MNIST тестовое изображение - цифра 6](images/1.png)
```
Real mark: 6
NN answer: 6
```
![MNIST тестовое изображение - цифра 3](images/2.png)
```
Real mark: 3
NN answer: 3
```
### 8) Детальный анализ качества классификации
Генерируем подробный отчет о качестве классификации и строим матрицу ошибок для визуального анализа работы модели по каждому классу.
```python
# Получение истинных и предсказанных меток для всех тестовых данных
true_labels = np.argmax(y_test, axis=1)
# Предсказанные метки классов
predicted_labels = np.argmax(model.predict(X_test), axis=1)
# Вывод подробного отчета о качестве классификации
print(classification_report(true_labels, predicted_labels))
# Построение и визуализация матрицы ошибок
conf_matrix = confusion_matrix(true_labels, predicted_labels)
# Отрисовка матрицы ошибок в виде "тепловой карты"
display = ConfusionMatrixDisplay(confusion_matrix=conf_matrix)
display.plot()
plt.show()
```
```
313/313 ━━━━━━━━━━━━━━━━━━━━ 2s 5ms/step
precision recall f1-score support
0 1.00 0.99 1.00 1001
1 0.99 1.00 0.99 1143
2 0.99 0.99 0.99 987
3 0.99 0.99 0.99 1023
4 0.99 0.99 0.99 974
5 1.00 0.98 0.99 907
6 0.99 0.99 0.99 974
7 0.98 0.99 0.99 1032
8 0.98 0.98 0.98 1006
9 0.98 0.99 0.98 953
accuracy 0.99 10000
macro avg 0.99 0.99 0.99 10000
weighted avg 0.99 0.99 0.99 10000
```
![Матрица ошибок для MNIST](images/3.png)
### 9) Тестирование на собственных изображениях
Загружаем и обрабатываем собственные изображения цифр, созданные ранее, и проверяем способность модели их корректно распознавать.
```python
# Загрузка и обработка собственных изображений
from PIL import Image
for name_image in ['2.png', '7.png']:
file_data = Image.open(name_image)
file_data = file_data.convert('L') # Перевод в градации серого
test_img = np.array(file_data)
# Вывод собственного изображения
plt.imshow(test_img, cmap=plt.get_cmap('gray'))
plt.show()
# Предобработка
test_img = test_img / 255
test_img = np.reshape(test_img, (1,28,28,1))
# Распознавание
result = model.predict(test_img)
print('I think it\'s', np.argmax(result))
```
![Собственное изображение - цифра 2](images/4.png)
```
I think it's 2
```
![Собственное изображение - цифра 7](images/5.png)
```
I think it's 7
```
### 10) Сравнение с моделью из предыдущей лабораторной работы
Загружаем сохраненную полносвязную нейронную сеть из лабораторной работы №1 и оцениваем ее производительность на тех же тестовых данных для последующего сравнения.
```python
model_lr1 = keras.models.load_model("best_mnist_model.keras")
model_lr1.summary()
```
**Model: "sequential"**
| Layer (type) | Output Shape | Param # |
|------------------|-------------:|--------:|
| dense (Dense) | (None, 10) | 7,850 |
**Total params:** 7,852 (30.68 KB)
**Trainable params:** 7,850 (30.66 KB)
**Non-trainable params:** 0 (0.00 B)
**Optimizer params:** 2 (12.00 B)
```python
# Подготовка данных для полносвязной сети (преобразование изображений в векторы)
X_train, X_test, y_train, y_test = train_test_split(X, y,
test_size = 10000,
train_size = 60000,
random_state = 3)
num_pixels = X_train.shape[1] * X_train.shape[2]
X_train = X_train.reshape(X_train.shape[0], num_pixels) / 255
X_test = X_test.reshape(X_test.shape[0], num_pixels) / 255
print('Shape of transformed X train:', X_train.shape)
print('Shape of transformed X train:', X_test.shape)
# Преобразование меток в формат one-hot encoding
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
print('Shape of transformed y train:', y_train.shape)
print('Shape of transformed y test:', y_test.shape)
```
```
Shape of transformed X train: (60000, 784)
Shape of transformed X train: (10000, 784)
Shape of transformed y train: (60000, 10)
Shape of transformed y test: (10000, 10)
```
```python
# Оценка качества работы модели на тестовых данных
scores = model_lr1.evaluate(X_test, y_test)
print('Loss on test data:', scores[0])
print('Accuracy on test data:', scores[1])
```
```
313/313 ━━━━━━━━━━━━━━━━━━━━ 1s 3ms/step - accuracy: 0.9233 - loss: 0.2863
Loss on test data: 0.28625616431236267
Accuracy on test data: 0.92330002784729
```
### 11) Сравнительный анализ моделей
Сравниваем сверточную нейронную сеть с полносвязной сетью по ключевым показателям: количеству параметров, времени обучения и качеству классификации. Делаем выводы по результатам применения сверточной нейронной сети для распознавания изображений.
**Таблица сравнения моделей:**
| Модель | Количество настраиваемых параметров | Количество эпох обучения | Качество классификации тестовой выборки |
|----------|-------------------------------------|---------------------------|-----------------------------------------|
| Сверточная | 34 826 | 15 | accuracy: 0.988; loss: 0.041 |
| Полносвязная | 7 852 | 50 | accuracy: 0.923; loss: 0.286 |
**Выводы:**
На основе проведенного анализа можно заключить, что сверточная нейронная сеть демонстрирует существенные преимущества перед полносвязной сетью при решении задач распознавания изображений:
1. **Эффективность параметров**: Сверточная сеть имеет больше параметров (34 826 против 7 852), но при этом показывает значительно лучшие результаты, что говорит о более эффективном использовании параметров для извлечения пространственных признаков.
2. **Скорость обучения**: Для достижения высокого качества сверточной сети требуется в 3.3 раза меньше эпох обучения (15 против 50), что существенно сокращает время обучения.
3. **Точность классификации**: Сверточная сеть показывает более высокую точность (98.8% против 92.3%) и значительно меньшую функцию потерь (0.041 против 0.286). Разница в точности составляет 6.5 процентных пункта, что является существенным улучшением.
4. **Обобщающая способность**: Сверточная сеть демонстрирует лучшую способность к обобщению, что видно из более низкой функции потерь на тестовых данных.
Эти результаты подтверждают, что архитектура сверточных сетей, учитывающая пространственную структуру изображений через операции свертки и пулинга, является более подходящим выбором для задач компьютерного зрения, несмотря на большее количество параметров.
## Задание 2
### Работа с датасетом CIFAR-10
Повторяем основные этапы задания 1, но используем датасет CIFAR-10, содержащий цветные изображения объектов 10 различных классов.
Особенности выполнения:
- Разделение данных производится в соотношении 50 000:10 000
- После разделения визуализируем 25 изображений из обучающей выборки
- При демонстрации работы модели выбираем примеры так, чтобы одно изображение распознавалось корректно, а другое - ошибочно
### 1) Загрузка датасета CIFAR-10
Загружаем набор данных CIFAR-10, который содержит цветные изображения размером 32x32 пикселя, разделенные на 10 классов: самолет, автомобиль, птица, кошка, олень, собака, лягушка, лошадь, корабль, грузовик.
```python
# Импорт и загрузка датасета CIFAR-10
from keras.datasets import cifar10
(X_train, y_train), (X_test, y_test) = cifar10.load_data()
```
### 2) Разделение данных на обучающую и тестовую выборки
Создаем собственное разбиение датасета CIFAR-10 в соотношении 50 000:10 000. Используем random_state = 3 для воспроизводимости результатов (k = 1 - номер нашей бригады). Выводим размерности полученных массивов данных.
```python
# Создание собственного разбиения датасета
# Объединение исходных выборок
X = np.concatenate((X_train, X_test))
y = np.concatenate((y_train, y_test))
# Разделение на обучающую и тестовую выборки
X_train, X_test, y_train, y_test = train_test_split(X, y,
test_size = 10000,
train_size = 50000,
random_state = 3)
# Вывод размерностей
print('Shape of X train:', X_train.shape)
print('Shape of y train:', y_train.shape)
print('Shape of X test:', X_test.shape)
print('Shape of y test:', y_test.shape)
```
```
Shape of X train: (50000, 32, 32, 3)
Shape of y train: (50000, 1)
Shape of X test: (10000, 32, 32, 3)
Shape of y test: (10000, 1)
```
### Визуализация примеров из обучающей выборки
Отображаем сетку из 25 изображений из обучающей выборки с подписями соответствующих классов для визуального ознакомления с данными.
```python
class_names = ['airplane', 'automobile', 'bird', 'cat', 'deer',
'dog', 'frog', 'horse', 'ship', 'truck']
plt.figure(figsize=(10,10))
for i in range(25):
plt.subplot(5,5,i+1)
plt.xticks([])
plt.yticks([])
plt.grid(False)
plt.imshow(X_train[i])
plt.xlabel(class_names[y_train[i][0]])
plt.show()
```
![Сетка из 25 изображений CIFAR-10](images/6.png)
### 3) Предобработка данных CIFAR-10
Нормализуем значения пикселей и преобразуем метки в формат one-hot encoding для работы с категориальной функцией потерь. Выводим размерности предобработанных массивов данных.
```python
# Определение параметров данных и модели
num_classes = 10
input_shape = (32, 32, 3)
# Нормализация значений пикселей: приведение к диапазону [0, 1]
X_train = X_train / 255
X_test = X_test / 255
print('Shape of transformed X train:', X_train.shape)
print('Shape of transformed X test:', X_test.shape)
# Преобразование меток в формат one-hot encoding
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)
print('Shape of transformed y train:', y_train.shape)
print('Shape of transformed y test:', y_test.shape)
```
```
Shape of transformed X train: (50000, 32, 32, 3)
Shape of transformed X test: (10000, 32, 32, 3)
Shape of transformed y train: (50000, 10)
Shape of transformed y test: (10000, 10)
```
### 4) Построение и обучение сверточной сети для CIFAR-10
Создаем более сложную архитектуру сверточной сети с использованием батч-нормализации и нескольких блоков свертки для работы с цветными изображениями. Обучаем модель на подготовленных данных с выделением части данных для валидации.
```python
# Создание модели сверточной нейронной сети
model = Sequential()
# Блок 1
model.add(layers.Conv2D(32, (3, 3), padding="same",
activation="relu", input_shape=input_shape))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(32, (3, 3), padding="same", activation="relu"))
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Dropout(0.25))
# Блок 2
model.add(layers.Conv2D(64, (3, 3), padding="same", activation="relu"))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(64, (3, 3), padding="same", activation="relu"))
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Dropout(0.25))
# Блок 3
model.add(layers.Conv2D(128, (3, 3), padding="same", activation="relu"))
model.add(layers.BatchNormalization())
model.add(layers.Conv2D(128, (3, 3), padding="same", activation="relu"))
model.add(layers.BatchNormalization())
model.add(layers.MaxPooling2D((2, 2)))
model.add(layers.Dropout(0.4))
model.add(layers.Flatten())
model.add(layers.Dense(128, activation='relu'))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(num_classes, activation="softmax"))
model.summary()
```
**Model: "sequential_9"**
| Layer (type) | Output Shape | Param # |
|--------------------------------------------|-------------------|---------:|
| conv2d_41 (Conv2D) | (None, 32, 32, 32) | 896 |
| batch_normalization_6 (BatchNormalization) | (None, 32, 32, 32) | 128 |
| conv2d_42 (Conv2D) | (None, 32, 32, 32) | 9,248 |
| batch_normalization_7 (BatchNormalization) | (None, 32, 32, 32) | 128 |
| max_pooling2d_26 (MaxPooling2D) | (None, 16, 16, 32) | 0 |
| dropout_24 (Dropout) | (None, 16, 16, 32) | 0 |
| conv2d_43 (Conv2D) | (None, 16, 16, 64) | 18,496 |
| batch_normalization_8 (BatchNormalization) | (None, 16, 16, 64) | 256 |
| conv2d_44 (Conv2D) | (None, 16, 16, 64) | 36,928 |
| batch_normalization_9 (BatchNormalization) | (None, 16, 16, 64) | 256 |
| max_pooling2d_27 (MaxPooling2D) | (None, 8, 8, 64) | 0 |
| dropout_25 (Dropout) | (None, 8, 8, 64) | 0 |
| conv2d_45 (Conv2D) | (None, 8, 8, 128) | 73,856 |
| batch_normalization_10 (BatchNormalization)| (None, 8, 8, 128) | 512 |
| conv2d_46 (Conv2D) | (None, 8, 8, 128) | 147,584 |
| batch_normalization_11 (BatchNormalization)| (None, 8, 8, 128) | 512 |
| max_pooling2d_28 (MaxPooling2D) | (None, 4, 4, 128) | 0 |
| dropout_26 (Dropout) | (None, 4, 4, 128) | 0 |
| flatten_9 (Flatten) | (None, 2048) | 0 |
| dense_17 (Dense) | (None, 128) | 262,272 |
| dropout_27 (Dropout) | (None, 128) | 0 |
| dense_18 (Dense) | (None, 10) | 1,290 |
**Total params:** 552,362 (2.11 MB)
**Trainable params:** 551,466 (2.10 MB)
**Non-trainable params:** 896 (3.50 KB)
```python
# Компиляция и обучение модели
batch_size = 64
epochs = 50
model.compile(loss="categorical_crossentropy", optimizer="adam", metrics=["accuracy"])
model.fit(X_train, y_train, batch_size=batch_size, epochs=epochs, validation_split=0.1)
```
### 5) Оценка качества модели на тестовых данных
Оцениваем финальную производительность обученной модели на тестовой выборке CIFAR-10.
```python
# Оценка качества работы модели на тестовых данных
scores = model.evaluate(X_test, y_test)
print('Loss on test data:', scores[0])
print('Accuracy on test data:', scores[1])
```
```
313/313 ━━━━━━━━━━━━━━━━━━━━ 7s 22ms/step - accuracy: 0.8553 - loss: 0.5210
Loss on test data: 0.5209607481956482
Accuracy on test data: 0.8553000092506409
```
### 6) Демонстрация работы модели на отдельных примерах
Визуализируем результаты распознавания для двух тестовых изображений: одно должно быть распознано корректно, другое - ошибочно.
```python
# Визуализация результатов распознавания для двух тестовых изображений
for n in [3,14]:
result = model.predict(X_test[n:n+1])
print('NN output:', result)
plt.imshow(X_test[n].reshape(32,32,3), cmap=plt.get_cmap('gray'))
plt.show()
print('Real mark: ', np.argmax(y_test[n]))
print('NN answer: ', np.argmax(result))
```
![CIFAR-10 тестовое изображение](images/7.png)
```
Real mark: 6
NN answer: 6
```
![CIFAR-10 тестовое изображение - олень (ошибочно распознано)](images/8.png)
```
Real mark: 4
NN answer: 5
```
### 7) Детальный анализ качества классификации CIFAR-10
Генерируем подробный отчет о качестве классификации и строим матрицу ошибок для анализа работы модели по каждому классу.
```python
# Получение истинных и предсказанных меток для всех тестовых данных
true_labels = np.argmax(y_test, axis=1)
# Предсказанные метки классов
predicted_labels = np.argmax(model.predict(X_test), axis=1)
# Вывод подробного отчета о качестве классификации
print(classification_report(true_labels, predicted_labels, target_names=class_names))
# Построение и визуализация матрицы ошибок
conf_matrix = confusion_matrix(true_labels, predicted_labels)
# Отрисовка матрицы ошибок в виде "тепловой карты"
fig, ax = plt.subplots(figsize=(6, 6))
disp = ConfusionMatrixDisplay(confusion_matrix=conf_matrix,display_labels=class_names)
disp.plot(ax=ax, xticks_rotation=45) # Поворот подписей по X и приятная палитра
plt.tight_layout() # Чтобы всё влезло
plt.show()
```
```
313/313 ━━━━━━━━━━━━━━━━━━━━ 1s 4ms/step
precision recall f1-score support
airplane 0.84 0.91 0.87 1007
automobile 0.95 0.91 0.93 1037
bird 0.83 0.79 0.81 1030
cat 0.77 0.65 0.70 990
deer 0.83 0.82 0.82 966
dog 0.72 0.83 0.77 1009
frog 0.90 0.89 0.89 972
horse 0.87 0.89 0.88 991
ship 0.95 0.92 0.93 990
truck 0.89 0.93 0.91 1008
accuracy 0.85 10000
macro avg 0.86 0.85 0.85 10000
weighted avg 0.86 0.85 0.85 10000
```
![Матрица ошибок для CIFAR-10](images/9.png)
**Выводы по результатам классификации CIFAR-10:**
Разработанная сверточная нейронная сеть показала хорошие результаты при классификации цветных изображений из датасета CIFAR-10. Модель достигла точности классификации 85.5% (accuracy: 0.855, loss: 0.521) на тестовой выборке, а в детальном отчете о классификации показала accuracy 0.85, что является достойным результатом для данной задачи, учитывая сложность различения объектов в низком разрешении (32x32 пикселя) и наличие 10 различных классов.
Использование батч-нормализации и dropout-регуляризации позволило улучшить обобщающую способность модели и предотвратить переобучение. Архитектура с тремя блоками сверточных слоев эффективно извлекает иерархические признаки из изображений, что подтверждается полученными метриками качества.

@ -1,7 +0,0 @@
## Лабораторныа работа №4
## Распознавание последовательностей
* [Задание](IS_Lab04_2023.pdf)
* [Методические указания](IS_Lab04_Metod_2023.pdf)

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

@ -0,0 +1,451 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "gz18QPRz03Ec"
},
"source": [
"### 1) В среде Google Colab создали новый блокнот (notebook). Импортировали необходимые для работы библиотеки и модули."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "mr9IszuQ1ANG"
},
"outputs": [],
"source": [
"# импорт модулей\n",
"import os\n",
"\n",
"from tensorflow import keras\n",
"from tensorflow.keras import layers\n",
"from tensorflow.keras.models import Sequential\n",
"import matplotlib.pyplot as plt\n",
"import numpy as np\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "FFRtE0TN1AiA"
},
"source": [
"### 2) Загрузили набор данных IMDb, содержащий оцифрованные отзывы на фильмы, размеченные на два класса: позитивные и негативные. При загрузке набора данных параметр seed выбрали равным значению (4k – 1)=3, где k=1 – номер бригады. Вывели размеры полученных обучающих и тестовых массивов данных."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "Ixw5Sp0_1A-w"
},
"outputs": [],
"source": [
"# загрузка датасета\n",
"import ssl\n",
"ssl._create_default_https_context = ssl._create_unverified_context\n",
"\n",
"from keras.datasets import imdb\n",
"\n",
"vocabulary_size = 5000\n",
"index_from = 3\n",
"\n",
"(X_train, y_train), (X_test, y_test) = imdb.load_data(\n",
" path=\"imdb.npz\",\n",
" num_words=vocabulary_size,\n",
" skip_top=0,\n",
" maxlen=None,\n",
" seed=3,\n",
" start_char=1,\n",
" oov_char=2,\n",
" index_from=index_from\n",
" )\n",
"\n",
"# вывод размерностей\n",
"print('Shape of X train:', X_train.shape)\n",
"print('Shape of y train:', y_train.shape)\n",
"print('Shape of X test:', X_test.shape)\n",
"print('Shape of y test:', y_test.shape)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "aCo_lUXl1BPV"
},
"source": [
"### 3) Вывели один отзыв из обучающего множества в виде списка индексов слов. Преобразовали список индексов в текст и вывели отзыв в виде текста. Вывели длину отзыва. Вывели метку класса данного отзыва и название класса (1 – Positive, 0 – Negative)."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "9W3RklPcZyH0"
},
"outputs": [],
"source": [
"# создание словаря для перевода индексов в слова\n",
"# загрузка словаря \"слово:индекс\"\n",
"word_to_id = imdb.get_word_index()\n",
"# уточнение словаря\n",
"word_to_id = {key:(value + index_from) for key,value in word_to_id.items()}\n",
"word_to_id[\"<PAD>\"] = 0\n",
"word_to_id[\"<START>\"] = 1\n",
"word_to_id[\"<UNK>\"] = 2\n",
"word_to_id[\"<UNUSED>\"] = 3\n",
"# создание обратного словаря \"индекс:слово\"\n",
"id_to_word = {value:key for key,value in word_to_id.items()}"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "Nu-Bs1jnaYhB"
},
"outputs": [],
"source": [
"print(X_train[26])\n",
"print('len:',len(X_train[26]))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "JhTwTurtZ6Sp"
},
"outputs": [],
"source": [
"review_as_text = ' '.join(id_to_word[id] for id in X_train[26])\n",
"print(review_as_text)\n",
"print('len:',len(review_as_text))\n",
"\n",
"# вывод метки класса и названия класса\n",
"print('Label:', y_train[26])\n",
"print('Class:', 'Positive' if y_train[26] == 1 else 'Negative')"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "4hclnNaD1BuB"
},
"source": [
"### 4) Вывели максимальную и минимальную длину отзыва в обучающем множестве."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "xJH87ISq1B9h"
},
"outputs": [],
"source": [
"print('MAX Len: ',len(max(X_train, key=len)))\n",
"print('MIN Len: ',len(min(X_train, key=len)))"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "7x99O8ig1CLh"
},
"source": [
"### 5) Провели предобработку данных. Выбрали единую длину, к которой будут приведены все отзывы. Короткие отзывы дополнили спецсимволами, а длинные обрезали до выбранной длины."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "lrF-B2aScR4t"
},
"outputs": [],
"source": [
"# предобработка данных\n",
"from tensorflow.keras.utils import pad_sequences\n",
"max_words = 500\n",
"X_train = pad_sequences(X_train, maxlen=max_words, value=0, padding='pre', truncating='post')\n",
"X_test = pad_sequences(X_test, maxlen=max_words, value=0, padding='pre', truncating='post')"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "HL2_LVga1C3l"
},
"source": [
"### 6) Повторили пункт 4."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "81Cgq8dn9uL6"
},
"outputs": [],
"source": [
"print('MAX Len: ',len(max(X_train, key=len)))\n",
"print('MIN Len: ',len(min(X_train, key=len)))"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "KzrVY1SR1DZh"
},
"source": [
"### 7) Повторили пункт 3. Сделали вывод о том, как отзыв преобразовался после предобработки."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "vudlgqoCbjU1"
},
"outputs": [],
"source": [
"print(X_train[26])\n",
"print('len:',len(X_train[26]))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "dbfkWjDI1Dp7"
},
"outputs": [],
"source": [
"review_as_text = ' '.join(id_to_word[id] for id in X_train[26])\n",
"print(review_as_text)\n",
"print('len:',len(review_as_text))"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "mJNRXo5TdPAE"
},
"source": [
"#### В результате предобработки данных все отзывы были приведены к единой длине 500 токенов. Для отзывов, исходная длина которых была меньше 500, в начало последовательности были добавлены специальные токены заполнения <PAD> (со значением 0). Это обеспечило единообразие входных данных для нейронной сети и позволило эффективно обрабатывать последовательности различной длины."
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "YgiVGr5_1D3u"
},
"source": [
"### 8) Вывели предобработанные массивы обучающих и тестовых данных и их размерности."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "7MqcG_wl1EHI"
},
"outputs": [],
"source": [
"# вывод данных\n",
"print('X train: \\n',X_train)\n",
"print('X test: \\n',X_test)\n",
"\n",
"# вывод размерностей\n",
"print('Shape of X train:', X_train.shape)\n",
"print('Shape of X test:', X_test.shape)"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "amaspXGW1EVy"
},
"source": [
"### 9) Реализовали модель рекуррентной нейронной сети, состоящей из слоев Embedding, LSTM, Dropout, Dense, и обучили ее на обучающих данных с выделением части обучающих данных в качестве валидационных. Вывели информацию об архитектуре нейронной сети. Добились качества обучения по метрике accuracy не менее 0.8."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "ktWEeqWd1EyF"
},
"outputs": [],
"source": [
"embed_dim = 32\n",
"lstm_units = 64\n",
"\n",
"model = Sequential()\n",
"model.add(layers.Embedding(input_dim=vocabulary_size, output_dim=embed_dim, input_length=max_words, input_shape=(max_words,)))\n",
"model.add(layers.LSTM(lstm_units))\n",
"model.add(layers.Dropout(0.5))\n",
"model.add(layers.Dense(1, activation='sigmoid'))\n",
"\n",
"model.summary()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "CuPqKpX0kQfP"
},
"outputs": [],
"source": [
"# компилируем и обучаем модель\n",
"batch_size = 64\n",
"epochs = 3\n",
"model.compile(loss=\"binary_crossentropy\", optimizer=\"adam\", metrics=[\"accuracy\"])\n",
"model.fit(X_train, y_train, batch_size=batch_size, epochs=epochs, validation_split=0.2)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "hJIWinxymQjb"
},
"outputs": [],
"source": [
"test_loss, test_acc = model.evaluate(X_test, y_test)\n",
"print(f\"\\nTest accuracy: {test_acc}\")"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "mgrihPd61E8w"
},
"source": [
"### 10) Оценили качество обучения на тестовых данных:\n",
"### - вывели значение метрики качества классификации на тестовых данных\n",
"### - вывели отчет о качестве классификации тестовой выборки \n",
"### - построили ROC-кривую по результату обработки тестовой выборки и вычислили площадь под ROC-кривой (AUC ROC)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "Rya5ABT8msha"
},
"outputs": [],
"source": [
"#значение метрики качества классификации на тестовых данных\n",
"print(f\"\\nTest accuracy: {test_acc}\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "2kHjcmnCmv0Y"
},
"outputs": [],
"source": [
"#отчет о качестве классификации тестовой выборки\n",
"y_score = model.predict(X_test)\n",
"y_pred = [1 if y_score[i,0]>=0.5 else 0 for i in range(len(y_score))]\n",
"\n",
"from sklearn.metrics import classification_report\n",
"print(classification_report(y_test, y_pred, labels = [0, 1], target_names=['Negative', 'Positive']))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "Kp4AQRbcmwAx"
},
"outputs": [],
"source": [
"#построение ROC-кривой и AUC ROC\n",
"from sklearn.metrics import roc_curve, auc\n",
"\n",
"fpr, tpr, thresholds = roc_curve(y_test, y_score)\n",
"plt.figure(figsize=(8, 6))\n",
"plt.plot(fpr, tpr)\n",
"plt.grid()\n",
"plt.xlabel('False Positive Rate')\n",
"plt.ylabel('True Positive Rate')\n",
"plt.title('ROC')\n",
"plt.savefig('roc_curve.png', dpi=150, bbox_inches='tight')\n",
"plt.show()\n",
"print('AUC ROC:', auc(fpr, tpr))"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "MsM3ew3d1FYq"
},
"source": [
"### 11) Сделали выводы по результатам применения рекуррентной нейронной сети для решения задачи определения тональности текста. "
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "xxFO4CXbIG88"
},
"source": [
"Таблица1:"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "xvoivjuNFlEf"
},
"source": [
"| Модель | Количество настраиваемых параметров | Количество эпох обучения | Качество классификации тестовой выборки |\n",
"|----------|-------------------------------------|---------------------------|-----------------------------------------|\n",
"| Рекуррентная | 184 897 | 3 | accuracy:0.8659 ; loss:0.3207 ; AUC ROC:0.9386 |\n"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "YctF8h_sIB-P"
},
"source": [
"#### Анализируя полученные результаты применения рекуррентной нейронной сети для классификации тональности текстовых отзывов, можно констатировать успешное выполнение поставленной задачи. Достигнутый уровень точности accuracy = 0.8659 существенно превосходит минимально необходимый порог 0.8, что свидетельствует о надежности разработанной модели. Показатель AUC ROC = 0.9386, превышающий значение 0.9, демонстрирует отличную дискриминационную способность модели в различении позитивных и негативных отзывов. Сбалансированные метрики precision и recall (0.87 для обоих классов) указывают на отсутствие значимого смещения в сторону одного из классов, что подтверждает корректность работы алгоритма классификации."
]
}
],
"metadata": {
"accelerator": "GPU",
"colab": {
"gpuType": "T4",
"provenance": []
},
"kernelspec": {
"display_name": "Python 3",
"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": 0
}

@ -0,0 +1,343 @@
# Отчёт по лабораторной работе №4
**Троянов Д.С., Чернов Д.Е. — А-01-22**
---
## Задание 1
### 1) В среде Google Colab создали новый блокнот (notebook). Импортировали необходимые для работы библиотеки и модули. Настроили блокнот для работы с аппаратным ускорителем GPU.
```python
# импорт модулей
import os
os.chdir('/content/drive/MyDrive/Colab Notebooks/is_lab4')
from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras.models import Sequential
import matplotlib.pyplot as plt
import numpy as np
```
```python
import tensorflow as tf
device_name = tf.test.gpu_device_name()
if device_name != '/device:GPU:0':
raise SystemError('GPU device not found')
print('Found GPU at: {}'.format(device_name))
```
```
Found GPU at: /device:GPU:0
```
### 2) Загрузили набор данных IMDb, содержащий оцифрованные отзывы на фильмы, размеченные на два класса: позитивные и негативные. При загрузке набора данных параметр seed выбрали равным значению (4k – 1)=3, где k=1 – номер бригады. Вывели размеры полученных обучающих и тестовых массивов данных.
```python
# загрузка датасета
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
from keras.datasets import imdb
vocabulary_size = 5000
index_from = 3
(X_train, y_train), (X_test, y_test) = imdb.load_data(
path="imdb.npz",
num_words=vocabulary_size,
skip_top=0,
maxlen=None,
seed=3,
start_char=1,
oov_char=2,
index_from=index_from
)
# вывод размерностей
print('Shape of X train:', X_train.shape)
print('Shape of y train:', y_train.shape)
print('Shape of X test:', X_test.shape)
print('Shape of y test:', y_test.shape)
```
```
Shape of X train: (25000,)
Shape of y train: (25000,)
Shape of X test: (25000,)
Shape of y test: (25000,)
```
### 3) Вывели один отзыв из обучающего множества в виде списка индексов слов. Преобразовали список индексов в текст и вывели отзыв в виде текста. Вывели длину отзыва. Вывели метку класса данного отзыва и название класса (1 – Positive, 0 – Negative).
```python
# создание словаря для перевода индексов в слова
# загрузка словаря "слово:индекс"
word_to_id = imdb.get_word_index()
# уточнение словаря
word_to_id = {key:(value + index_from) for key,value in word_to_id.items()}
word_to_id["<PAD>"] = 0
word_to_id["<START>"] = 1
word_to_id["<UNK>"] = 2
word_to_id["<UNUSED>"] = 3
# создание обратного словаря "индекс:слово"
id_to_word = {value:key for key,value in word_to_id.items()}
```
```python
print(X_train[26])
print('len:',len(X_train[26]))
```
```
[1, 37, 1388, 4, 2739, 495, 94, 96, 143, 49, 2, 875, 551, 19, 195, 2210, 5, 1698, 8, 401, 4, 65, 24, 64, 1728, 21, 400, 642, 45, 77, 6, 137, 237, 207, 258, 141, 6, 1562, 1301, 1562, 737, 22, 10, 10, 4, 22, 9, 1490, 3862, 4, 744, 19, 307, 1385, 5, 2, 2, 4, 2, 2656, 2, 1669, 19, 4, 1074, 200, 4, 55, 406, 55, 3048, 5, 246, 55, 1451, 105, 688, 8, 4, 321, 177, 32, 677, 7, 4, 678, 1850, 26, 1669, 221, 5, 3921, 10, 10, 13, 386, 37, 1388, 4, 2739, 45, 6, 66, 163, 20, 15, 304, 6, 3049, 168, 33, 4, 4352, 15, 75, 70, 2, 23, 257, 85, 5, 4, 2789, 878, 21, 1305, 2, 1773, 7, 2]
len: 130
```
```python
review_as_text = ' '.join(id_to_word[id] for id in X_train[26])
print(review_as_text)
print('len:',len(review_as_text))
# вывод метки класса и названия класса
print('Label:', y_train[26])
print('Class:', 'Positive' if y_train[26] == 1 else 'Negative')
```
```
<START> who loves the sun works its way through some <UNK> subject matter with enough wit and grace to keep the story not only engaging but often hilarious it's been a while since i've found such a thoroughly touching thoroughly enjoyable film br br the film is gorgeous drawing the eye with beautiful scenery and <UNK> <UNK> the <UNK> imagery <UNK> wonderfully with the tension between the very human very flawed and yet very likable characters due to the excellent cast all five of the major players are wonderfully interesting and dynamic br br i recommend who loves the sun it's a really funny movie that takes a poignant look at the hurts that we can <UNK> on each other and the amazingly difficult but equally <UNK> process of <UNK>
len: 738
Label: 1
Class: Positive
```
### 4) Вывели максимальную и минимальную длину отзыва в обучающем множестве.
```python
print('MAX Len: ',len(max(X_train, key=len)))
print('MIN Len: ',len(min(X_train, key=len)))
```
```
MAX Len: 2494
MIN Len: 11
```
### 5) Провели предобработку данных. Выбрали единую длину, к которой будут приведены все отзывы. Короткие отзывы дополнили спецсимволами, а длинные обрезали до выбранной длины.
```python
# предобработка данных
from tensorflow.keras.utils import pad_sequences
max_words = 500
X_train = pad_sequences(X_train, maxlen=max_words, value=0, padding='pre', truncating='post')
X_test = pad_sequences(X_test, maxlen=max_words, value=0, padding='pre', truncating='post')
```
### 6) Повторили пункт 4.
```python
print('MAX Len: ',len(max(X_train, key=len)))
print('MIN Len: ',len(min(X_train, key=len)))
```
```
MAX Len: 500
MIN Len: 500
```
### 7) Повторили пункт 3. Сделали вывод о том, как отзыв преобразовался после предобработки.
```python
print(X_train[26])
print('len:',len(X_train[26]))
```
```
[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 1 4 78
46 304 39 2 7 968 2 295 209 101 147 65 10 10
2643 2 497 8 30 6 147 284 5 996 174 10 10 11
4 130 4 2 4979 11 2 10 10 2]
len: 500
```
```python
review_as_text = ' '.join(id_to_word[id] for id in X_train[26])
print(review_as_text)
print('len:',len(review_as_text))
```
```
<PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <PAD> <START> the bad out takes from <UNK> of fire <UNK> together without any real story br br dean <UNK> tries to be a real actor and fails again br br in the end the <UNK> quit in <UNK> br br <UNK>
len: 2947
```
#### В результате предобработки данных все отзывы были приведены к единой длине 500 токенов. Для отзывов, исходная длина которых была меньше 500, в начало последовательности были добавлены специальные токены заполнения <PAD> (со значением 0). Это обеспечило единообразие входных данных для нейронной сети и позволило эффективно обрабатывать последовательности различной длины.
### 8) Вывели предобработанные массивы обучающих и тестовых данных и их размерности.
```python
# вывод данных
print('X train: \n',X_train)
print('X test: \n',X_test)
# вывод размерностей
print('Shape of X train:', X_train.shape)
print('Shape of X test:', X_test.shape)
```
```
X train:
[[ 0 0 0 ... 12 38 76]
[ 0 0 0 ... 33 4 130]
[ 0 0 0 ... 437 7 58]
...
[ 0 0 0 ... 1874 1553 422]
[ 0 0 0 ... 18 1552 234]
[ 0 0 0 ... 7 87 1090]]
X test:
[[ 0 0 0 ... 6 194 717]
[ 0 0 0 ... 30 87 292]
[ 0 0 0 ... 495 55 73]
...
[ 0 0 0 ... 7 12 908]
[ 0 0 0 ... 61 477 2302]
[ 0 0 0 ... 5 68 4580]]
Shape of X train: (25000, 500)
Shape of X test: (25000, 500)
```
### 9) Реализовали модель рекуррентной нейронной сети, состоящей из слоев Embedding, LSTM, Dropout, Dense, и обучили ее на обучающих данных с выделением части обучающих данных в качестве валидационных. Вывели информацию об архитектуре нейронной сети. Добились качества обучения по метрике accuracy не менее 0.8.
```python
embed_dim = 32
lstm_units = 64
model = Sequential()
model.add(layers.Embedding(input_dim=vocabulary_size, output_dim=embed_dim, input_length=max_words, input_shape=(max_words,)))
model.add(layers.LSTM(lstm_units))
model.add(layers.Dropout(0.5))
model.add(layers.Dense(1, activation='sigmoid'))
model.summary()
```
**Model: "sequential"**
| Layer (type) | Output Shape | Param # |
| ----------------------- | --------------- | ------: |
| embedding_4 (Embedding) | (None, 500, 32) | 160,000 |
| lstm_4 (LSTM) | (None, 64) | 24,832 |
| dropout_4 (Dropout) | (None, 64) | 0 |
| dense_4 (Dense) | (None, 1) | 65 |
**Total params:** 184,897 (722.25 KB)
**Trainable params:** 184,897 (722.25 KB)
**Non-trainable params:** 0 (0.00 B)
```python
# компилируем и обучаем модель
batch_size = 64
epochs = 3
model.compile(loss="binary_crossentropy", optimizer="adam", metrics=["accuracy"])
model.fit(X_train, y_train, batch_size=batch_size, epochs=epochs, validation_split=0.2)
```
```
Epoch 1/3
313/313 ━━━━━━━━━━━━━━━━━━━━ 108s 338ms/step - accuracy: 0.7596 - loss: 0.4853 - val_accuracy: 0.8086 - val_loss: 0.4447
Epoch 2/3
313/313 ━━━━━━━━━━━━━━━━━━━━ 85s 273ms/step - accuracy: 0.8680 - loss: 0.3281 - val_accuracy: 0.8228 - val_loss: 0.3993
Epoch 3/3
313/313 ━━━━━━━━━━━━━━━━━━━━ 84s 267ms/step - accuracy: 0.8818 - loss: 0.3008 - val_accuracy: 0.8714 - val_loss: 0.3097
```
```python
test_loss, test_acc = model.evaluate(X_test, y_test)
print(f"\nTest accuracy: {test_acc}")
```
```
782/782 ━━━━━━━━━━━━━━━━━━━━ 54s 69ms/step - accuracy: 0.8659 - loss: 0.3207
Test accuracy: 0.865880012512207
```
### 10) Оценили качество обучения на тестовых данных:
### - вывели значение метрики качества классификации на тестовых данных
### - вывели отчет о качестве классификации тестовой выборки
### - построили ROC-кривую по результату обработки тестовой выборки и вычислили площадь под ROC-кривой (AUC ROC)
```python
#значение метрики качества классификации на тестовых данных
print(f"\nTest accuracy: {test_acc}")
```
```
Test accuracy: 0.865880012512207
```
```python
#отчет о качестве классификации тестовой выборки
y_score = model.predict(X_test)
y_pred = [1 if y_score[i,0]>=0.5 else 0 for i in range(len(y_score))]
from sklearn.metrics import classification_report
print(classification_report(y_test, y_pred, labels = [0, 1], target_names=['Negative', 'Positive']))
```
```
precision recall f1-score support
Negative 0.87 0.87 0.87 12500
Positive 0.87 0.86 0.87 12500
accuracy 0.87 25000
macro avg 0.87 0.87 0.87 25000
weighted avg 0.87 0.87 0.87 25000
```
```python
#построение ROC-кривой и AUC ROC
from sklearn.metrics import roc_curve, auc
fpr, tpr, thresholds = roc_curve(y_test, y_score)
plt.figure(figsize=(8, 6))
plt.plot(fpr, tpr)
plt.grid()
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC')
plt.savefig('roc_curve.png', dpi=150, bbox_inches='tight')
plt.show()
print('AUC ROC:', auc(fpr, tpr))
```
![ROC-кривая](roc_curve.png)
```
AUC ROC: 0.9386348447999999
```
### 11) Сделали выводы по результатам применения рекуррентной нейронной сети для решения задачи определения тональности текста.
Таблица1:
| Модель | Количество настраиваемых параметров | Количество эпох обучения | Качество классификации тестовой выборки |
|----------|-------------------------------------|---------------------------|-----------------------------------------|
| Рекуррентная | 184 897 | 3 | accuracy:0.8659 ; loss:0.3207 ; AUC ROC:0.9386 |
#### Анализируя полученные результаты применения рекуррентной нейронной сети для классификации тональности текстовых отзывов, можно констатировать успешное выполнение поставленной задачи. Достигнутый уровень точности accuracy = 0.8659 существенно превосходит минимально необходимый порог 0.8, что свидетельствует о надежности разработанной модели.
#### Показатель AUC ROC = 0.9386, превышающий значение 0.9, демонстрирует отличную дискриминационную способность модели в различении позитивных и негативных отзывов. Сбалансированные метрики precision и recall (0.87 для обоих классов) указывают на отсутствие значимого смещения в сторону одного из классов, что подтверждает корректность работы алгоритма классификации.

Двоичные данные
labworks/LW4/roc_curve.png

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

После

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

Загрузка…
Отмена
Сохранить