23 KiB
ЛАБОРАТОРНАЯ РАБОТА №4
Сеть Кохонена
Цель работы: знакомство с применением многослойного персептрона для решения задач сжатия данных, прогнозирования временных рядов и распознавания образов.
Задание
- Изучить разделы справки связанные с обучением сети Кохонена. Загрузить набор данных, содержащий измерения длины и ширины чашелистика и лепестка 150 экземпляров ириса (ирисы Фишера). Создать сеть Кохонена и выполнить с помощью неё кластеризацию сортов ириса. Проанализировать полученные результаты. Выполнить визуализацию исходных данных.
- Построить и обучить сеть Кохонена для кластеризации данных скрытого слоя автоассоциативной сети из п.1. лабораторной работы №3. Проанализировать результаты и сравнить их с результатами классификации многослойным персептроном, полученными в лабораторной работе №2..
Импорт библиотек:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from IPython.display import clear_output
from mpl_toolkits.mplot3d import Axes3D
from sklearn.datasets import load_iris
%matplotlib inlineСодержание:
1. Ирисы Фишера
Сеть Кохонена (или самоорганизующаяся карта) — это тип нейронных сетей без учителя (unsupervised learning), разработанный финским учёным Теуво Кохоненом.
Основная цель таких сетей — визуализация и кластеризация многомерных данных путём их проецирования на пространство меньшей размерности (чаще всего двумерное).
Ключевые особенности:
- Без учителя: не требует размеченных данных для обучения.
- Топологическое упорядочивание: сохраняет пространственные отношения между точками данных — близкие входные векторы активируют соседние нейроны на карте.
- Конкурентное обучение: в каждый момент времени «побеждает» только один нейрон (ближайший к входному вектору).
Обучение сети Кохонена происходит итеративно, по одному примеру за раз.
- Шаг 1. Инициализация.
Веса всех нейронов инициализируются случайными значениями (обычно из того же распределения, что и входные данные). - Шаг 2. Выбор входного вектора.
Из набора данных случайным образом выбирается один образец \(x\) (пусть количество признаков равно \(m\)). - Шаг 3. Определение «нейрона‑победителя».
Находится нейрон, вектор весов которого наиболее близок к входному вектору. Мера близости — обычно евклидово расстояние:
\[ d(x, w_i) = \sqrt{\sum_{j=1}^{m} (x_j - w_{ij})^2} \]
- Шаг 4. Обновление весов.
Вектор весов нейрона‑победителя (и, опционально, его соседей) подтягивается ближе к входному вектору:
\[ w_i(t+1) = w_i(t) + \eta(t) \cdot (x(t) - w_i(t)) \]
где \(w_i\) — вектор весов \(i\)-го нейрона (кластера); \(\eta(t)\) — скорость обучения (learning rate) в момент времени \(t\), которая определяет, насколько сильно будут скорректированы веса; \(x(t)\) — текущий входной вектор.
- Шаг 5. Уменьшение скорости обучения.
На этом этапе скорость обучения \(\eta\) постепенно уменьшается с каждой эпохой. Это необходимо для стабилизации карты в конце обучения: на ранних этапах допускаются большие корректировки весов (чтобы быстро приблизиться к оптимальной конфигурации), а на поздних — только мелкие уточнения. Обновление скорости обучения происходит по следующему правилу:
\[ \eta(t+1) = \eta(t) \times \text{decay} \]
где \(\text{decay}\) — коэффициент затухания (гиперпараметр, обычно близкий к \(1\), например \(0{,}99\) или \(0{,}95\)).
- Шаг 6. Повторение.
Шаги 2–5 повторяются заданное число эпох или до сходимости (т.е. пока значения весов нейронов в карте практически не перестанут меняться от эпохи к эпохе — структура карты, таким образом, уже сформирована и отражает топологию входных данных).
Преимущества сетей Кохонена:
- простота реализации и интерпретации;
- наглядная визуализация сложных данных;
- устойчивость к шуму в данных;
- сохранение топологических свойств исходного пространства.
Недостатки:
- чувствительность к инициализации весов;
- необходимость ручного подбора числа кластеров;
- относительно медленное обучение на больших наборах данных.
- результат может зависеть от порядка предъявления данных (поэтому их необходимо перемешивать).
Описанная выше сеть Кохонена реализована в классе KohonenClustering:
class KohonenClustering:
def __init__(self, n_clusters, lr=0.1, decay=0.99):
self.n_clusters = n_clusters
self.lr = lr
self.decay = decay
self.cluster_weights = None
def winner(self, x):
# Вычисляем евклидово расстояние от входного вектора до каждого кластера
distances = np.zeros((x.shape[0], self.n_clusters))
for i, cluster_weight in enumerate(self.cluster_weights):
distances[:, i] = np.sqrt(np.sum((x - cluster_weight) ** 2, axis=1))
# Находим индексы нейронов с минимальным расстоянием (победителей)
winner_idx = np.argmin(distances, axis=1)
return winner_idx
def fit(self, data, epochs=100):
self.cluster_weights = np.random.randn(self.n_clusters, data.shape[1]).astype(np.float32)
lr_history = []
for epoch in range(epochs):
# Перемешиваем индексы данных для случайного выбора векторов
indices = np.random.permutation(data.shape[0])
# Проходим по всем векторам в случайном порядке
for i in indices:
# Извлекаем один вектор
x = data[i:i+1]
# Определяем нейрон‑победитель для текущего входного вектора
winner_idx = self.winner(x)
# Обновляем веса победившего нейрона: подтягиваем их ближе к входному вектору
for i, idx in enumerate(winner_idx):
self.cluster_weights[idx] += self.lr * (x[i] - self.cluster_weights[idx])
lr_history.append(self.lr)
# Постепенно уменьшаем скорость обучения для стабилизации карты
self.lr *= self.decay
if (epoch + 1) % 5 == 0:
clear_output(True)
plt.plot(range(1, epoch+2), lr_history, label='Learning Rate')
plt.title(f'Epoch: {epoch + 1}, Learning Rate: {lr_history[-1]:.6f}')
plt.grid(True, alpha=0.3)
plt.legend(loc='best')
plt.show()Загрузим данные ирисов для их анализа с помощью сети Кохонена:
iris_data = load_iris()Значения четырёх входных признаков:
X_iris_data = iris_data['data']
print(X_iris_data[:5])Названия признаков — длина и ширина чашелистика, длина и ширина лепестка:
iris_data['feature_names']Значения выходного признака — метки классов (три сорта ирисов):
y_iris_data = iris_data['target']
print(y_iris_data)Названия сортов ирисов:
iris_names = iris_data['target_names']
print(iris_names)Поскольку при обучении сети Кохонена вычисляются евклидовы расстояния между входными данными и весами нейронов, входные данные рекомендуются отнормировать или отстандартизировать:
X_means = X_iris_data.mean(axis=0)
X_stds = X_iris_data.std(axis=0, ddof=1)
X_iris_data_scaled = # Ваш код здесьОбучите модель kohonen_iris на нормированных (или стандартизированных) данных.
Для этого подберите количество кластеров n_clusters, скорость обучения lr, шаг снижения скорости обучения decay. При неоходимости измените количество эпох epochs.
kohonen_iris = KohonenClustering(
n_clusters= # Ваш код здесь
lr= # Ваш код здесь
decay= # Ваш код здесь
)
kohonen_iris.fit(X_iris_data_scaled, epochs=100)Получим метки кластеров — сопоставьте их с исходными метками классов:
clusters_iris = kohonen_iris.winner(X_iris_data_scaled)
print(clusters_iris)Построим диаграмму рассеяния, например, для пары признаков — длины и ширины лепестка. Точки разметим по трём сортам ирисов:
scatter = plt.scatter(X_iris_data[:, 2], X_iris_data[:, 3], c=y_iris_data)
plt.xlabel('petal length (cm)')
plt.ylabel('petal width (cm)')
plt.grid(True, alpha=0.3)
# Код для легенды
handles, _ = scatter.legend_elements(prop='colors')
plt.legend(handles, iris_names, loc='best', title='Classes')
plt.show()Построим такую же диаграмму, олнако точки в ней будут размечены по меткам кластеров:
scatter = plt.scatter(X_iris_data[:, 2], X_iris_data[:, 3], c=clusters_iris)
plt.xlabel('petal length (cm)')
plt.ylabel('petal width (cm)')
plt.grid(True, alpha=0.3)
# Код для легенды
handles, labels = scatter.legend_elements(prop='colors')
plt.legend(handles, labels, loc='best', title='Clusters')
plt.show()Поэкспериментируйте с количеством кластеров n_clusters. Проанализируйте варианты, когда:
- количество кластеров совпадает с количеством сортов ирисов;
- кластеров меньше сортов ирисов;
- кластеров больше сортов ирисов.
2. Данные скрытого слоя
Загрузим из файла encoded_2d.npy двумерные данные с выхода энкодера автоассоциативной сети из третьей лабораторной работы:
encoded_2d = np.load('encoded_2d.npy')
print(encoded_2d[:5])Обучите модель kohonen_2d на этих данных. Количество кластеров пусть совпадает с количеством сортов минеральной воды (т.е. 5).
kohonen_2d = KohonenClustering(
# Ваш код здесь
)
kohonen_2d.fit(encoded_2d, epochs=100)Получим метки кластеров:
clusters_2d = kohonen_2d.winner(encoded_2d)Построим двухмерную диаграмму рассеяния:
scatter = plt.scatter(x=encoded_2d[:, 0], y=encoded_2d[:, 1], c=clusters_2d, cmap='viridis')
plt.grid(True, alpha=0.3)
# Код для легенды
handles, labels = scatter.legend_elements(prop='colors')
plt.legend(handles, labels, loc='best', title='Clusters')
plt.show()Сравните диаграмму с соответствующим результатом из третьей лабораторной работы.
Загрузите данные из остальных файлов (encoded_3d.npy, encoded_2d_include.npy, encoded_3d_include.npy) и также обучите на них по отдельному экземпляру сети Кохонена. Получите метки кластеров и постройте для каждого набора диаграммы рассеяния. Сравните их с соответствующими диаграммами из третьей лабораторной работы.
# Ваш код здесь# Ваш код здесьЛитература:
- Бородкин А.А., Елисеев В.Л. Основы и применение искусственных нейронных сетей. Сборник лабораторных работ: методическое пособие. – М.: Издательский дом МЭИ, 2017.
- MachineLearning.ru — профессиональный информационно-аналитический ресурс, посвященный машинному обучению, распознаванию образов и интеллектуальному анализу данных: http://www.machinelearning.ru
- Modern State of Artificial Intelligence — Online Masters program at MIPT: https://girafe.ai/