29 KiB
ЛАБОРАТОРНАЯ РАБОТА №3
Применение многослойного персептрона. Автоассоциативная ИНС
Цель работы: знакомство с применением многослойного персептрона для решения задач сжатия данных, прогнозирования временных рядов и распознавания образов.
Задание
- Открыть файл с данными по минеральной воде, который использовался при решении задач классификации в предыдущей лабораторной работе. Построить и обучить автоассоциативные нейронные сети с 2-мя и 3-мя нейронами в скрытом слое:
а) для исходных данных из 5-ти классов;
б) для исходных данных из 4-х классов.
Провести визуализацию данных в скрытом слое каждой сети на плоскость и в 3-х мерное пространство. Проанализировать полученные результаты. Выбрать и сохранить автоассоциативные ИНС, обеспечивающие наилучшее сжатие исходных данных.
- …
- Решить задачу распознавания 9-ти изображений самолетов. Исходные данные (файлы avia1.bmp, …, avia9.bmp) необходимо предварительно преобразовать в набор векторов со значениями признаков 0 или 1. Обученная нейронная сеть должна правильно определять модель самолета и его класс (истребитель/бомбардировщик). Принадлежность модели к определенному классу выбирается студентом самостоятельно.
Импорт библиотек:
import numpy as np
import pandas as pd
import torch
import matplotlib.pyplot as plt
from IPython.display import clear_output
from mpl_toolkits.mplot3d import Axes3D
from sklearn.model_selection import train_test_split
from torch import nn
%matplotlib inlineСодержание:
1. Подготовка данных
2. Автоассоциативная нейронная сеть на полных данных
3. Автоассоциативная нейронная сеть на неполных данных
1. Подготовка данных
Загрузим в датафрейм data данные о сорока образцах минеральной воды, хранящиеся в файле min_water.txt.
data = pd.read_csv('min_water.csv')
data.head(n=5)Вынесем в отдельные переменные:
y_binary— выходной признак для задачи бинарной классификации (первый столбец датафрейма);y_multiclass— выходной признак для задачи многоклассовой классификации (второй столбец датафрейма);X_data— входные признаки (оставшиеся столбцы).
y_binary = data.iloc[:, 0]
y_multiclass = data.iloc[:, 1]
X_data = data.iloc[:, 2:]Выпишите в список features отобранные в прошлой лабораторной работе признаки (формат: features = ['VAR1', 'VAR2']):
features = # Ваш код здесьДатафрейм с отобранными входными признаками X_data_filtered:
X_data_filtered = X_data.loc[:, features]
X_data_filtered.head(n=5)C помощью функции train_test_split разбейте данные (X_data_filtered, y_multiclass) на обучающую (X_multiclass_train, y_multiclass_train), валидационную (X_multiclass_valid, y_multiclass_valid) и тестовую выборки (X_multiclass_test, y_multiclass_test) с сохранением соотншений классов (сортов минеральной воды):
X_multiclass_train, X_multiclass_test, y_multiclass_train, y_multiclass_test = # Ваш код здесь
X_multiclass_train, X_multiclass_valid, y_multiclass_train, y_multiclass_valid = # Ваш код здесьПроизведите нормализацию или стандартизацию (на выбор) входных данных. Результат сохраните в переменные (X_multiclass_train, X_multiclass_valid, X_multiclass_test), которую затем представьте в виде тензоров:
X_means = # Ваш код здесь
X_stds = # Ваш код здесь
X_multiclass_train = # Ваш код здесь
X_multiclass_valid = # Ваш код здесь
X_multiclass_test = # Ваш код здесь2. Автоассоциативная нейронная сеть на полных данных
Автоассоциативная сеть (или автоассоциативная память) — тип нейронной сети, способный восстанавливать полный шаблон данных по его частичному или зашумлённому представлению.
Типичная автоассоциативная сеть содержит как минимум три скрытых слоя:
- первый скрытый слой выполняет нелинейное кодирование входных данных (энкодер);
- средний слой («узкое горло» или «бутылочное горло») формирует сжатое представление данных — в результате обучения выдаёт компактное кодирование;
- последний скрытый слой служит декодером: восстанавливает исходные данные из сжатого представления.
Цель обучения: в процессе минимизации ошибки воспроизведения сеть стремится сделать выходной сигнал максимально близким к входному. Это эквивалентно оптимальному кодированию в «узком горле» сети.
Допишите класс Autoencoder структурами энкодера и декодера на основе полносвязных слоёв nn.Linear. В качестве функций активации используйте nn.ReLU(). При этом на выходе энкодера функцию активации можно не применять — это позволит сохранить отрицательные значения в кодированном представлении.
class Autoencoder(nn.Module):
def __init__(self, n_inputs, n_hiddens, bottleneck_size):
super().__init__()
self.encoder = nn.Sequential(
# Ваш код здесь
)
self.decoder = nn.Sequential(
# Ваш код здесь
)
def forward(self, x):
encoded = self.encoder(x)
decoded = self.decoder(encoded)
return decodedСоздайте экземпляр модели с двумя нейронами в «узком горле»:
model_2 = Autoencoder(# Ващ код здесь
print(model_2)Пропустим данные через эту модель для её проверки:
model_2(X_multiclass_train[:3])Удостоверимся, что размерность её выхода совпадает с размерностью её входа:
assert X_multiclass_train[:3].shape == model_2(X_multiclass_train[:3]).shapeПроверим, как модель обучается. Зададим оптимизатор и среднеквадратическую функцию потерь:
optimizer = torch.optim.SGD(model_2.parameters(), lr=1.5)
criterion = nn.MSELoss()Рассчитаем значение функции потерь:
decoded = model_2(X_multiclass_train)
loss = criterion(decoded, X_multiclass_train)
lossВыполните несколько раз эту и предыдущую ячейку, чтобы убедиться в уменьшении ошибки:
loss.backward()
optimizer.step()
optimizer.zero_grad()Задайте параметры для обучения автоассоциативной сети с двумя нейронами в «узком горле»:
torch.manual_seed(seed=42)
model_2 = # Ваш код здесь
epochs = # Ваш код здесь
learning_rate = # Ваш код здесь
momentum = # Ваш код здесь
optimizer = # Ваш код здесь
criterion = # Ваш код здесьОбучение нейронной сети:
loss_train_history, loss_valid_history = [], []
for epoch in range(epochs):
# Ваш код здесь
# Отключаем градиенты для этапа валидации
with torch.no_grad():
# Ваш код здесь
if (epoch + 1) % 5 == 0:
clear_output(True)
plt.plot(range(1, epoch+2), loss_train_history, label='Train', color='green')
plt.plot(range(1, epoch+2), loss_valid_history, label='Valid', color='red')
plt.title(f'Epoch: {epoch + 1}, Loss Train: {loss_train_history[-1]:.6f}, Loss Valid: {loss_valid_history[-1]:.6f}')
plt.grid(True, alpha=0.3)
plt.legend(loc='best')
plt.show()Проверим качество обученной сети на тестовой выборке:
with torch.no_grad():
decoded_test = model_2(X_multiclass_test)
loss_test = criterion(decoded_test, X_multiclass_test)
print(f'Loss Test: {loss_test.item():.6f}')Далее необходимо через обученную сети пропустить все имеющиеся входные данные. Здесь можно сконкатенировать обучающую, валидационную и тестовую выборки, но можно и заново стандартизировать исходные данные X_data_filtered с помощью переменных X_means и X_stds, которые обязательно должны быть ранее рассчитаны на обучающей выборке X_multiclass_train (аналогично будет и для нормировки данных). Результат будет прелставлен в виде тензора.
Такой вариант сохранит исходный порядок записей в данных и позволит нам использовать исходный вектор y_multiclass для разметки классов ниже.
X_data_tensor = torch.tensor(((X_data_filtered - X_means) / X_stds).values).float()После обучения сети получим двумерные данные с выхода энкодера:
encoded_2d = model_2.encoder(X_data_tensor).detach().numpy()
print(encoded_2d[:3])Построим двумерную диаграмму рассеяния и отметим классы с помощью y_multiclass:
scatter = plt.scatter(x=encoded_2d[:, 0], y=encoded_2d[:, 1], c=y_multiclass, cmap='viridis')
plt.grid(True, alpha=0.3)
# Код для легенды
handles, labels = scatter.legend_elements(prop='colors')
plt.legend(handles, labels, loc='best', title='Classes')
plt.show()По аналогии обучите автоассоциативную сеть с тремя нейронами в «узком горле»:
torch.manual_seed(seed=42)
model_3 = # Ваш код здесь
# Ваш код здесь# Ваш код здесьПосле обучения сети получите трёхмерные данные с выхода энкодера (снова по всем данным X_data_tensor, которые уже были подготовлены выше):
encoded_3d = # Ваш код здесьПостроим трёхмерную диаграмму рассеяния:
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')
scatter = ax.scatter(
xs=encoded_3d[:, 0],
ys=encoded_3d[:, 1],
zs=encoded_3d[:, 2],
c=y_multiclass,
cmap='viridis',
s=50
)
ax.grid(True, alpha=0.3)
handles, labels = scatter.legend_elements(prop='colors', alpha=0.8)
ax.legend(handles, labels, loc='best', title='Classes')
# Настраиваем угол обзора:
# elev — высота, azim — азимут
ax.view_init(elev=20, azim=45)
plt.show()Сохраним в бинарные файлы .npy выходы энкодеров обеих моделей — для следующей лабораторной работы:
np.save('encoded_2d.npy', encoded_2d)
np.save('encoded_3d.npy', encoded_3d)3. Автоассоциативная нейронная сеть на неполных данных
Выберите класс, который нужно исключить:
label_to_exclude = # Ваш код здесьСоздадим маску для исключения данных этого класса:
mask_to_exclude = y_multiclass != label_to_excludeДанные исключены:
X_data_include = X_data_filtered.loc[mask_to_exclude, :]
y_include = y_multiclass[mask_to_exclude]По аналогии с помощью функции train_test_split разбейте данные (X_data_include, y_include) на обучающую (X_include_train, y_include_train), валидационную (X_include_valid, y_include_valid) и тестовую выборки (X_include_test, y_include_test) с сохранением соотншений классов:
X_include_train, X_include_test, y_include_train, y_include_test = # Ваш код здесь
X_include_train, X_include_valid, y_include_train, y_include_valid = # Ваш код здесьОбратите внимание, что X_means и X_stds перерасчитываются уже на другой по составу обучающей выборке.
X_means = # Ваш код здесь
X_stds = # Ваш код здесь
X_include_train = # Ваш код здесь
X_include_valid = # Ваш код здесь
X_include_test = # Ваш код здесьПо аналогии с предыдущим пунктом реализуйте обучение автоассоциативных сетей с двумя и тремя нейронами в «узком горле».
Результаты выходов энкодеров в обоих случаях также сохраните в отдельные бинарные файлы.
# Ваш код здесь# Ваш код здесьОбратите внимание, что тензор X_data_tensor снова получен из исходных данных X_data_filtered, но уже с пересчитанными X_means и X_stds (без одного класса):
X_data_tensor = torch.tensor(((X_data_filtered - X_means) / X_stds).values).float()encoded_2d_include = # Ваш код здесь# Ваш код здесь# Ваш код здесьencoded_3d_include = # Ваш код здесьnp.save('encoded_2d_include.npy', encoded_2d_include)
np.save('encoded_3d_include.npy', encoded_3d_include)Литература:
- Бородкин А.А., Елисеев В.Л. Основы и применение искусственных нейронных сетей. Сборник лабораторных работ: методическое пособие. – М.: Издательский дом МЭИ, 2017.
- MachineLearning.ru — профессиональный информационно-аналитический ресурс, посвященный машинному обучению, распознаванию образов и интеллектуальному анализу данных: http://www.machinelearning.ru
- Modern State of Artificial Intelligence — Online Masters program at MIPT: https://girafe.ai/