@ -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()
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
# Были выведены цифры 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()
|
||||
```
|
||||
|
||||

|
||||
|
||||
```
|
||||
# Обучение модели
|
||||
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()
|
||||
```
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 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()
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
**По результатам проведнного эксперимента наилучший показатель точности продемонстрировала нейронная сеть со 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)))
|
||||
```
|
||||
|
||||

|
||||
|
||||
```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)))
|
||||
```
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 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))
|
||||
```
|
||||
|
||||

|
||||
|
||||
```
|
||||
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))
|
||||
```
|
||||
|
||||
```
|
||||

|
||||
```
|
||||
|
||||
```
|
||||
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))
|
||||
```
|
||||
|
||||

|
||||
|
||||
```
|
||||
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))
|
||||
```
|
||||
|
||||

|
||||
|
||||
```
|
||||
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 48ms/step
|
||||
I think it's 7
|
||||
```
|
||||
|
||||
|
||||
**При повороте рисунков цифр НС не смогла распознать одну из цифр. Так получилось, во-первых, по той же причине, почему НС не распознала одну из цифр в пункте 13, во-вторых - наша НС не обучалась на перевернутых цифрах**
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
После Ширина: | Высота: | Размер: 315 B |
|
После Ширина: | Высота: | Размер: 948 B |
|
После Ширина: | Высота: | Размер: 273 B |
|
После Ширина: | Высота: | Размер: 920 B |
|
После Ширина: | Высота: | Размер: 24 KiB |
@ -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()
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
# Были выведены цифры 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()
|
||||
```
|
||||
|
||||

|
||||
|
||||
```
|
||||
# Обучение модели
|
||||
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()
|
||||
```
|
||||
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 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()
|
||||
```
|
||||
|
||||

|
||||
|
||||
|
||||
**По результатам проведнного эксперимента наилучший показатель точности продемонстрировала нейронная сеть со 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)))
|
||||
```
|
||||
|
||||

|
||||
|
||||
```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)))
|
||||
```
|
||||

|
||||
|
||||
---
|
||||
|
||||
## 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))
|
||||
```
|
||||
|
||||

|
||||
|
||||
```
|
||||
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))
|
||||
```
|
||||
|
||||
```
|
||||

|
||||
```
|
||||
|
||||
```
|
||||
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))
|
||||
```
|
||||
|
||||

|
||||
|
||||
```
|
||||
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))
|
||||
```
|
||||
|
||||

|
||||
|
||||
```
|
||||
1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 48ms/step
|
||||
I think it's 7
|
||||
```
|
||||
|
||||
|
||||
**При повороте рисунков цифр НС не смогла распознать одну из цифр. Так получилось, во-первых, по той же причине, почему НС не распознала одну из цифр в пункте 13, во-вторых - наша НС не обучалась на перевернутых цифрах**
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
После Ширина: | Высота: | Размер: 42 KiB |
|
После Ширина: | Высота: | Размер: 80 KiB |
|
После Ширина: | Высота: | Размер: 85 KiB |
|
После Ширина: | Высота: | Размер: 8.2 KiB |
|
После Ширина: | Высота: | Размер: 38 KiB |
|
После Ширина: | Высота: | Размер: 58 KiB |
@ -0,0 +1 @@
|
||||
Subproject commit 3f51d49924a6a14ad44f2a5e70c707fb576ac786
|
||||
@ -0,0 +1 @@
|
||||
Subproject commit 3f51d49924a6a14ad44f2a5e70c707fb576ac786
|
||||