老饼讲解-神经网络
自实现-竞争网络
SOM神经网络
SOM-代码重写(批量训练)
作者 : 老饼 日期 : 2022-06-09 04:42:24 更新 : 2022-06-29 01:26:24
本站原创文章,转载请说明来自《老饼讲解-BP神经网络》bp.bbbdata.com


本文是笔者细扒matlab2014b神经网络工具箱newsom的源码后,

去除冗余代码,重现的简版newsom代码,代码与newsom的结果完全一致。

通过本代码的学习,可以完全细节的了解SOM神经网络的实现逻辑。





  01.  代码结构说明  


代码主要包含了4个函数:   testSomNet      trainSomNet      init_wd      predictSomNet  


testSomNet:  测试用例主函数,直接运行时就是执行该函数。


1、数据生成:随机生成样本点,
2、用自写的函数训练一个SOM网络,并预测测试样本的输出。
3、使用工具箱训练一个SOM网络,并预测测试样本的输出。
4、比较自写函数与工具箱训练结果是否一致(权重、预测结果的比较)


trainSomNet:训练一个SOM神经网络。


使用批量训练方式,训练SOM神经网络的权重



init_wd:初始化权重和距离矩阵。


生成隐层拓扑结构,并计算隐节点之间的距离矩阵d,
然后根据隐层拓扑结构和训练样本,初始化权重矩阵w.



predictSomNet:用训练好的网络进行预测。


传入需要预测的X,与网络的权重矩阵,即可得到预测结果。







  02.  代码运行解说  


运行代码,得到预测结果与对比结果,如下:



从结果可以看到, 自写的SOM神经网络与 工具箱newsom得到的结果完全一致。







  03. 算法主要流程   


整体流程如下:


1、生成拓扑结构,并由拓扑结构算得矩阵距离                            
2、初始化聚类点中心                                                                 
3、训练m次:                                                                              
-----用收缩法计算本次邻域范围dn                                             
-----抽取部分样本,计算每个输出节点权重的kohonen加权更新量
-----对w进行更新                                                                     
4、输出训练好的w(聚类中心点的位置)                                  





  04. 具体代码如下  


matlab2014b亲测已跑通:



function testSomNet()
%本代码来自bp.bbbdata.com
%本代码模仿matlab神经网络工具箱的newsom神经网络,用于训练《SOM神经网络》,
%本代码扒自matlab2014b,使用的是批量训练法。
%代码主旨用于教学,供大家学习理解newsom神经网络原理
% ---------数据生成与参数预设-------------
% 数据生成
rand('seed',70);
X      = [rand(1,40)*3;rand(1,40)*2]; % 生成样本
test_x = [0.5;0.5];                  % 测试样本

% 网络参数
epochs = 150;                        % 训练步数
dimensions = [8 5];                  % 输出节点拓扑维度
%---------调用自写函数进行训练--------------
rand('seed',70);
w = trainSomNet(X,dimensions,epochs);
py = find(predictSomNet(w,test_x))

% -----调用工具箱,与工具箱的结果比较------
% 调用工具箱进行训练
rand('seed',70);
net = newsom(X,dimensions);
net.trainParam.epochs = epochs;
net = train(net,X);

% 工具箱的结果
pyByTool = find(sim(net,test_x))
w_tools  = net.IW{1};
% 与工具箱的差异
maxECompareNet = max([max(abs(w(:)-w_tools(:))),max(abs(pyByTool(:)-py(:)))])
end

% 训练函数
function w = trainSomNet(X,dimensions,epochs)
[xn,sn] = size(X);
[w,d]   = init_wd(dimensions,X);% 初始化权重与距离矩阵
init_nd = 3;   % 初始邻域距离
step    = 0;   % 初始迭代步数
steps   = 100; % 该步数之后,邻域距离不再收缩
for epoch=1:epochs
    % 本轮样本的竞争成功的神经元
    py = predictSomNet(w,X);
    py = py .* double(rand(size(py))<0.9); % 随机屏蔽掉一些样本
    
    % 本轮的邻域矩阵
    nd = 1 + (init_nd-1) * (1-step/steps);    % 本轮邻域距离
    neighborhood = (d <= nd);                 % 计算邻域矩阵
    
    % 计算学习率矩阵
    LR  = 0.5*( neighborhood * py + py); % 竞争成功节点标为1,竞争成功节点的近邻节点标为0.5,
    LRS = sum(LR,2);
    LR  = LR ./ LRS(:,ones(1,sn));   % 归一化
    
    % 计算dw
    dw =  LR*X' - w;
    
    %所有样本中,既不竞争成功又不在成功节点的邻域的节点,则不需更新。
    loserIndex = (LRS == 0);
    dw(loserIndex,:) = 0;
    
    % 更新w
    w = w+dw;
    step = step + 1;
end
end

% 初始化函数
function [w,d] = init_wd(dimensions,X)
% 源代码在 initsompc
[xn,sn] = size(X);             % 输入个数与样本个数
ndim    = length(dimensions);  % 拓扑维度  
pos     = hextop(dimensions);  % 初始化拓扑点位置
d       = linkdist(pos);       % 计算拓扑点的距离矩阵

%初始化w:看起来像是想把pos旋转到X的主成分方向 ,但看起来糊里糊涂
posMean  = mean(X,2);
Xm       = X - repmat(posMean,1,sn); % 将X移到中心
[U,S,V]  = svd(Xm);                  % U*S*V' = Xm
basis    = U*S;
stdev    = std(V,1,1)';
posBasis = basis * 2.5 * diag(stdev);

% 这段比较迷糊,猜测原本的意思是打算让拓扑点最多的维度对应最大主成份方向 
[~,dimOrder] = sort(dimensions,2,'descend');
restoreOrder = [sort(dimOrder) (ndim+1):min(xn,sn)]; %应该不是sort(dimOrder),

% 当拓扑维度>输入维度,进行矩阵补齐
if ndim > xn
    posBasis = [posBasis rands(xn,ndim-xn)*0.001];
end
posBasis = posBasis(:,restoreOrder);

% 当样本维度<输入维度,进行矩阵补齐
if (sn < xn)
    posBasis = [posBasis zeros(xn,xn-sn)];
end
% 当输入维度>拓扑维度,进行矩阵补齐
if xn > ndim
    pos = [pos; zeros(xn-ndim,size(pos,2))];
end
% 将 pos进行归一化
norm_deno = max(pos,[],2)-min(pos,[],2);
norm_deno(norm_deno==0) = 1;
norm_pos = 2*(pos-repmat(min(pos,[],2),1,size(pos,2)))./(repmat(norm_deno,1,size(pos,2)))-1;

% 将归一化后的 pos进行旋转并拉伸,移到X的中心位置
w = (repmat(posMean,1,size(norm_pos,2)) + (posBasis * norm_pos))';

end

%SOM神经网络的预测函数
function y = predictSomNet(w,X)
%----初始化----
z = zeros(size(w,1),size(X,2));  % 初始化输出神经元值
y = z;                           % 初始化输出神经元激活值(即最终的输出)

%----计算神经元值----
for i= 1: size(X,2)
    z(:,i) = -sum((repmat( X(:,i)',size(w,1),1)-w).^ 2,2) .^ 0.5;  % 计算每个x的神经元值
end
%----将神经元值进行竞争,得到最终输出----
[~,idx] = max(z);                       % 找出竞争成功的神经元
y(idx+ size(y,1)*(0:size(y,2)-1)) = 1;  % 将竞争成功的神经元标为1
end


PCA初始化的方法,暂时未找到相关参考文献,也十分希望有相关文献的读者,能提供给老饼。



 End 









联系小饼