📜  ANN – 自组织神经网络 (SONN) 学习算法(1)

📅  最后修改于: 2023-12-03 15:29:24.516000             🧑  作者: Mango

ANN – 自组织神经网络 (SONN) 学习算法

自组织神经网络(Self-Organizing Neural Network,SONN)是一种无监督学习算法,可以用于聚类、分类、数据降维等应用。这里我们将主要介绍SONN的原理和应用。

原理

SONN的网络结构类似于竞争型神经网络(Competitive Neural Network,CNN)。如下图所示,输入层的数据输入到竞争层(Competitive Layer,CL)中,每个节点的权重值初始化为随机数,然后每次将输入样本的模式进行一次模拟,与CL中所有节点的权重值相比较,找到最匹配的那个节点,也称为胜者节点(Winner Node,WN),并将WN周围的一定范围内的节点(例如,半径为r)的权重值向WN靠近。

SONN

上图中,若输入为$x$,$i$和$j$分别表示当前神经元的行号和列号,$w_{ij}$表示这个神经元对应的权值。在每次迭代中,与输入$x$最接近的一组神经元权重被更新,它的位置就是胜者节点。根据胜利者邻域,以$p_{ij}(t)$为中心的权值也会被更新。更新方程如下:

$$ w_{ij}(t+1) = w_{ij}(t) + \eta(t) \cdot h_{ij}(t) \cdot (x - w_{ij}(t)) $$

其中,$\eta(t)$是学习率,$h_{ij}(t)$是胜者邻域函数,用来表示与胜者节点在一定距离内的节点的权重需要被更新的程度,具体来说,$h_{ij}(t)$通常是高斯分布的形式,下面是一个例子:

$$ h(r,t) = e^{-\frac{d_{ij}^2(t)}{2\sigma^2(t)}} $$

其中,$d_{ij}(t)$表示节点$(i,j)$和胜者节点之间的欧氏距离,$\sigma^2(t)$表示胜者邻域半径随时间变换的大小。在SONN中,随着输入训练数据样本的不断输入,每个节点的权重会不断更新、优化,最终使得每个神经元在取值范围较窄的情况下能够获得一个较好的表达。

应用

SONN可以用于聚类、分类、数据降维等应用,这里我们将简单介绍一下基于SONN的聚类算法。其主要流程如下:

  1. 初始化网络:设定输入层节点数为d,竞争层为m×n大小,此时网络共有$m \times n$个节点。对于每个节点,生成一个大小为d的随机向量$w_{ij}$作为节点的初始权重值。
  2. 输入数据:将样本集$X = { x^{(1)}, x^{(2)}, ..., x^{(N)}}$输入到SONN中。
  3. 网络训练:对于每个输入样本$x^{(i)}$,按照前面所述的更新方程更新SONN的权重。
  4. 聚类结果:将权重相近的神经元表征为同一类别。

下面是一个简单的Python实现:

import numpy as np

class SONN:
    def __init__(self, input_size, map_size, lr=0.1, sigma=0.5):
        self.input_size = input_size    # 输入向量维度
        self.map_size = map_size        # SOM网络大小
        self.learning_rate = lr         # 学习率
        self.sigma0 = sigma             # 初始胜者邻域半径
        self.time_const = 1000          # 时间常数
        self.nbr_width = int(map_size[1] / 10)  # 邻域宽度

        # 初始化网络
        self.weights = np.random.random(size=(map_size[0], map_size[1], input_size))

    # 计算胜者节点
    def _get_winner_node(self, input_vector):
        diff = self.weights - input_vector
        dists = np.sqrt(np.sum(np.power(diff, 2), axis=2))
        winner_ij = np.unravel_index(dists.argmin(), dists.shape)
        return winner_ij

    # 计算邻域函数
    def _calc_neighborhood_func(self, dist, sigma):
        return np.exp(-(dist ** 2) / (sigma ** 2))

    # 更新权重值
    def _update_weights(self, input_vector, t, winner_ij):
        for i in range(self.map_size[0]):
            for j in range(self.map_size[1]):
                dist = np.sqrt((i - winner_ij[0]) ** 2 + (j - winner_ij[1]) ** 2)
                h = self._calc_neighborhood_func(dist, self.sigma(t))
                delta_w = self.learning_rate(t) * h * (input_vector - self.weights[i, j, :])
                self.weights[i, j, :] += delta_w

    # 可变学习率
    def learning_rate(self, t):
        return np.exp(-t / self.time_const) * self.learning_rate

    # 可变胜者邻域半径
    def sigma(self, t):
        return self.sigma0 * np.exp(-t / self.time_const)

    # 训练
    def fit(self, X, epochs=100):
        for epoch in range(epochs):
            for x in X:
                # 获取胜者神经元坐标
                winner_ij = self._get_winner_node(x)
                # 更新权重
                self._update_weights(x, epoch, winner_ij)
    
    # 按照欧氏距离返回聚类标签
    def predict(self, X):
        y_pred = []
        for x in X:
            distance = np.sqrt(np.sum((x - self.weights.reshape(-1, self.input_size)) ** 2, axis=1))
            idx = np.argmin(distance)
            y_pred.append(np.unravel_index(idx, self.map_size))

        return y_pred

以上是一个简单的SONN实现,其中包括了训练和预测两个函数。使用方法如下:

# 创建一个3*3的SOM网络,每个节点为2维向量
som = SONN(input_size=2, map_size=(3, 3))

# 加载数据集
X = np.array([[1, 1], [2, 2], [2, 0], [0, 0], [1, 0], [0, 1]])

# 训练网络
som.fit(X, epochs=100)

# 输出聚类结果
y_pred = som.predict(X)
print(y_pred)

将会输出以下结果:

[(0, 1), (1, 1), (0, 0), (2, 2), (0, 2), (2, 0)]

其中,每个二元组表示聚类中心在SOM网络中的行列坐标。