深度学习--SOM(Self-Organizing Maps,自组织映射)算法--90


参考链接扩展:
书上看到了关于SOM的介绍,还比较全面:
https://blog.csdn.net/rc15680632552/article/details/123892549

通过距离的计算,确定获胜神经元,winner take all,获胜神经元通过加强自身以及周围临近神经元,而抑制较远的神经元,确保相同的数据(或者类似接近的数据)输入,winner 或者winner 的周围 能再次成为winner ,
读到这一步,深吸一口气,人类社会与这个机制多么接近,一人得道鸡犬升天。名门望族也不是一天两天起来的,贾史王薛,蒋宋孔陈,唉唉唉!

1. 介绍

SOM(Self-Organizing Maps,自组织映射)是一种用于数据可视化和降维的神经网络算法。它可以将高维数据映射到低维空间(通常是二维),同时保持数据的拓扑结构。SOM在模式识别、数据挖掘和神经信息处理等领域有广泛应用。
sometimes known as Kohonen networks or Winner take all units (WTU).

注意神经元之间并不会直接相连,神经元只与输入节点相连,

通俗解释:
想象一下,你有一堆形状和颜色各异的玩具,你想把它们分类并摆放在一个二维的架子上,使得相似的玩具放在一起。SOM算法就是帮你完成这个任务的工具。它会自动学习哪些玩具相似,并将它们放在架子上相邻的位置。

2. 工作原理

  1. 初始化:首先,我们在二维平面上随机初始化一些神经元,每个神经元都有一个权重向量,这个向量的维度与输入数据的维度相同。

  2. 竞争:对于每一个输入数据,计算它与每个神经元权重向量的距离。距离最近的神经元被称为“获胜神经元”或“最佳匹配单元(BMU)”。

  3. 合作与适应:获胜神经元及其邻近的神经元会调整它们的权重向量,使其更接近输入数据。这个过程通过学习率和邻域函数来控制。


𝜎 is a time-dependent radius of influence of a neuron
d is its distance from the winning neuron
4. 迭代:重复上述步骤,直到神经元的权重向量不再显著变化或达到预定的迭代次数。

墨西哥 帽

权重的更新:

3. 代码

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
# define the winner take all units
class WTU:
    def __init__(self, m, n, dim, num_iterations, eta=0.5, sigma=None):
        """
        m x n: the dimension of 2D lattice in which nuerons are arranged
        dim: Dimension of input training data
        num_iterations: Total number of training iterations
        eta: Learning rate
        sigma: The ratius of neighbourhood function
        """
        self._m = m
        self._n = n
        self._neighbourhood = []
        self._topography = []
        self._num_iterations = int(num_iterations)
        self._learned = False
        self.dim = dim
        
        self.eta = float(eta)
        
        if sigma is None:
            sigma = max(m, n) / 2.0  # Constant radius
        else:
            sigma = float(sigma)
        self.sigma = sigma
        print("Net created with dimension:", m , n)
        
        # wight Matric and the topography of neurons
        self._W = tf.random.normal([m*n, dim], seed=0)
        #print("W:", self._W)
        self._topography = np.array(list(self._nueron_loation(m, n)))
        #print("topography:", self._topography)
        
        
    def _nueron_loation(self,m, n):
        for i in range(m):
            for j in range(n):
                yield np.array([i, j])   
                
    def training(self, x, i):
        m = self._m
        n = self._n
        
        # finding the winner and its location
        
        # 计算输入向量 x 与权重矩阵 _W 中每个权重向量的欧氏距离。
        d = tf.sqrt(tf.reduce_sum(tf.pow(self._W - tf.stack([x for i in range(m*n)]),2),1))
        # 到距离最小的权重向量,即获胜神经元
        self.WTU_idx = tf.argmin(d, 0)
        
        # 获取获胜神经元的位置
        # 使用获胜神经元的索引 WTU_idx 从神经元位置矩阵 _topography 中提取获胜神经元的位置
        
        slice_start = tf.pad(tf.reshape(self.WTU_idx, [1]),np.array([[0,1]]))
        """
        self.WTU_idx 是一个标量,表示获胜神经元的索引
        举例 _topography=[[0,0], [0,1], [0,2],    [1,0,], [1,1], [1,2],     [2,0,], [2,1], [2,2]]
        WTU_idx为其中一个: 0       1       2        3      4     5            6       7     8
        假设 WTU_idx =5
        tf.reshape(self.WTU_idx, [1]) 将这个标量转换为一个形状为 [1] 的张量。-->  [5]
        tf.pad(..., np.array([[0,1]]))  --> [5,0]
        """
        
        
        self.WTU_loc = tf.reshape(tf.slice(self._topography, slice_start,[1,2]), [2])
        """
        self._topography 是一个形状为 [m*n, 2] 的张量,其中 m 和 n 是 SOM 网络的维度,每一行表示一个神经元的位置(二维坐标)。
        tf.slice(self._topography, slice_start, [1,2]) 
        从 self._topography 中提取一个切片。
        slice_start 是开始切片的索引,[1,2] 表示切片的形状。
        这里 slice_start 的第一个元素是获胜神经元的索引,第二个元素是 0(因为我们在上一步中填充了一个 0)
        例如,如果 slice_start 是 [5, 0],
        那么 tf.slice(self._topography, [5, 0], [1, 2]) 将提取 self._topography 中第 5 行的前 2 个元素。
        tf.reshape(..., [2]) 将提取的切片重新整形为一个形状为 [2] 的张量,表示获胜神经元的位置。
        """
        # 总结一下,这两行代码的目的是将获胜神经元的索引 WTU_idx 转换为 _topography 中的位置,并将其存储在 self.WTU_loc 中。
        
        
        # Change learning rate and the radius as a function of iterations
        learning_rate = 1 - i/self._num_iterations
        _eta_new = self.eta * learning_rate
        _sigma_new = self.sigma * learning_rate
        
        # neighbourhood funxtion calculation
        # 计算每个神经元与获胜神经元之间的距离平方  --> (_topography-WTU_loc)**2
        distance_square = tf.reduce_sum(tf.pow(tf.subtract(self._topography, tf.stack([self.WTU_loc for i in range(m*n)])), 2), 1)
        # 使用高斯函数计算邻域函数,距离越远的神经元受到的影响越小
        neighbourhood_func = tf.exp(tf.negative(tf.math.divide(tf.cast(distance_square, tf.float32), tf.pow(_sigma_new, 2))))
        
        # multipy learning rate with neighbourhood func
        # 将学习率与邻域函数相乘
        eta_into_Gamma = tf.multiply(_eta_new, neighbourhood_func)
        
        # shape it so that it can be multiplied to calculate dW
        # 将 eta_into_Gamma 扩展到与权重矩阵相同的形状
        weight_multiplier = tf.stack([tf.tile(tf.slice(eta_into_Gamma, np.array([i]), np.array([1])), [self.dim]) for i in range(m*n)])
        """
        1. 
        tf.slice(eta_into_Gamma, np.array([i]), np.array([1]))
            tf.slice 用于从张量中提取一个切片。
            eta_into_Gamma 是输入张量。
            np.array([i]) 是起始索引,表示从第 i 个元素开始。
            np.array([1]) 是切片的长度,表示提取一个元素。
        因此,tf.slice(eta_into_Gamma, np.array([i]), np.array([1])) 提取 eta_into_Gamma 的第 i 个元素。
        
        2.tf.tile(..., [self.dim]) 
        tf.tile 用于将张量沿着指定维度重复。
            [self.dim] 表示将提取的单个元素重复 self.dim 次,形成一个长度为 self.dim 的向量。
        
        3.列表推导式 [... for i in range(m*n)]:
        这个列表推导式遍历 i 从 0 到 m*n-1。
        对于每个 i,执行前面的操作,提取 eta_into_Gamma 的第 i 个元素并将其扩展为长度为 self.dim 的向量。
        
        4. tf.stack(...):
        tf.stack 用于将一个列表中的张量堆叠在一起,形成一个新的张量。
        这里将所有扩展后的向量堆叠在一起,形成一个新的张量 weight_multiplier。

        总结一下,这行代码的作用是:
        从 eta_into_Gamma 中提取每个元素。
        将每个元素扩展为一个长度为 self.dim 的向量。
        将所有这些向量堆叠在一起,形成一个新的张量 eight_multiplier。
    
        """
        
        # 计算权重更新量 delta_W=eta*h*(x-m)
        delta_W = tf.multiply(weight_multiplier, tf.subtract(tf.stack([x for i in range(m*n)]), self._W))
        new_W = self._W + delta_W
        self._W = new_W
        
    
    def fit(self, X):
        for i in range(self._num_iterations):
            for x in X:
                self.training(x, i)
        # restore the centroid grid for easy retrival
        centroid_grid = [[] for i in range(self._m)]
        
        self._Wts = list(self._W)
        self._locations = list(self._topography)
        
        for i, loc in enumerate(self._topography):
            centroid_grid[loc[0]].append(self._Wts[i])
        self._centroid_grid = centroid_grid
        self._learned = True
        print("fit finish !")
    
    def winner(self, x):
        return self.WTU_idx, self.WTU_loc   # 在_topography中的索引号, 二维坐标
    
    def get_centroids(self):
        if not self._learned:
            raise ValueError("SOM not train yet")
        return self._centroid_grid
    def map_vects(self, X):
        """
        遍历输入向量 X 中的每一个向量。
        找到与每个输入向量最接近的权重向量。
        将最接近的权重向量对应的 SOM 节点位置添加到结果列表中。
        """
        if not self._learned:
            raise ValueError("SOM not train yet")
        to_return = []
        for vect in X:
            min_index = min([i for i in range(len(self._Wts))], key=lambda x: np.linalg.norm(vect - self._Wts[x]))
            to_return.append(self._locations[min_index])
        """
        使用列表推导式生成一个包含所有权重向量索引的列表 [i for i in range(len(self._Wts))]。
        使用 min 函数找到与当前向量 vect 最接近的权重向量索引 min_index。
        key=lambda x: np.linalg.norm(vect - self._Wts[x]) 是一个关键函数,
        用于计算向量 vect 和权重向量 self._Wts[x] 之间的欧几里得距离(使用 np.linalg.norm 函数)。
        """
        return to_return
              
def normalize(df):
    result = df.copy()
    
    for feature_name in df.columns:
        max_value = df[feature_name].max()
        min_value = df[feature_name].min()
        result[feature_name] = (df[feature_name]-min_value)/(max_value - min_value)
    return result.astype(np.float32)  # 注意转换成float32
import pandas as pd
df = pd.read_csv("colors.csv")
data = normalize(df[["R", "G", "B"]]).values
name = df["Color-Name"].values
n_dim = len(df.columns) - 1
colors = data
color_names = name
som = WTU(20, 20, n_dim, 100, sigma=10.0)
som.fit(colors)
Net created with dimension: 20 20
image_grid = som.get_centroids()

mapped = som.map_vects(colors)

# plot
plt.imshow(image_grid)
plt.title("Color Grid SOM")
for i, m in enumerate(mapped):
    plt.text(m[1], m[0], color_names[i], ha='center', va='center', bbox=dict(facecolor='white', alpha=0.5, lw=0))

posted @ 2024-06-28 16:37  jack-chen666  阅读(10)  评论(0编辑  收藏  举报