Files
neurocomputers-python/lab4/4_kohonen.ipynb

23 KiB

ЛАБОРАТОРНАЯ РАБОТА №4

Сеть Кохонена

Цель работы: знакомство с применением многослойного персептрона для решения задач сжатия данных, прогнозирования временных рядов и распознавания образов.

Задание

  1. Изучить разделы справки связанные с обучением сети Кохонена. Загрузить набор данных, содержащий измерения длины и ширины чашелистика и лепестка 150 экземпляров ириса (ирисы Фишера). Создать сеть Кохонена и выполнить с помощью неё кластеризацию сортов ириса. Проанализировать полученные результаты. Выполнить визуализацию исходных данных.
  2. Построить и обучить сеть Кохонена для кластеризации данных скрытого слоя автоассоциативной сети из п.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. Ирисы Фишера
2. Данные скрытого слоя

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) и также обучите на них по отдельному экземпляру сети Кохонена. Получите метки кластеров и постройте для каждого набора диаграммы рассеяния. Сравните их с соответствующими диаграммами из третьей лабораторной работы.

# Ваш код здесь
# Ваш код здесь

Литература:

  1. Бородкин А.А., Елисеев В.Л. Основы и применение искусственных нейронных сетей. Сборник лабораторных работ: методическое пособие. – М.: Издательский дом МЭИ, 2017.
  2. MachineLearning.ru — профессиональный информационно-аналитический ресурс, посвященный машинному обучению, распознаванию образов и интеллектуальному анализу данных: http://www.machinelearning.ru
  3. Modern State of Artificial Intelligence — Online Masters program at MIPT: https://girafe.ai/