som可用于聚类,图像分割等,由于论文需要matlab实现了som。
%som实现
%2016-11-12
%by wangbaojia
% som原理及参数说明
% 1.竞争:匹配最佳神经元---------->计算每个样本和初始神经网络的距离,距离最近神经元作为获胜神经元
%
% 2.合作:在权值更新过程中,不仅获胜神经元的权
%值向量得到更新,而且其近邻神经元的权值向量也按照某个“近邻函数”进行更新。这样在开
%始时移动量很大,权值向量大致地可按它们的最终位置来排序;
%获胜神经元决定兴奋神经元的拓扑邻域的空间位置,从而提供了相邻神经元合作的基础
% 拓扑邻域:规则多边形一般都可以作为邻域形状,常用的主要有正方形或六边形,正方形更为普遍
%
%权重向量的调整就发生在获胜神经元的邻域内。在训练的刚开始阶段,这个邻域比较大,
%随着训练的进行,这个邻域开始不断减小
%
% 3.自适应:权值更新过程
% 算法:
% 1.初始化
% 1)迭代次数:时间步长iter
% 2)输出结点权值向量初始值,向量各元素可选区间(0,1)上的随机值,这里选择正方形邻域
% 3)学习率初始值
% 4)邻域半径的设置应尽量包含较多的邻神经元,整个输出平面的一半
% 2.求竞争获胜神经元;欧拉距离函数求解
% 3.权值更新:
% 获胜节点和邻域范围内神经元集合的m个节点更新权值,j=1:m;
% wj(t+1)=wj(t)+learnfun(t)*neighborfun(t)*(x-wj);
% 4.更新学习率,更新邻域函数
% neighborfun(t)=neighbor0*exp(-dij/t1); t1=iter/log(neighbor0)
% learnfun(t)=learn0*exp(-t/t2); t2=iter
% 5.当特征映射不再发生明显变化时或达到最大网络训练次数时退出,否则转入第2步
%载入数据,data数据每一行为一个用空格区分的多维数据样本
tic;
%样本数据的位置
file_path='C:\Users\Administrator\Desktop\testclustering\test1\';
path=strcat(file_path,'data.txt');
%path=strcat(file_path,'data_path.txt');
data=load(path);
[data_row,data_clown]=size(data);
%自组织映射网络m*n
m=5;
n=5;
%神经元节点总数som_sum
som_sum=m*n;
%权值初始化,随机初始化
w = rand(som_sum, data_clown);
%初始化学习率
learn0 = 0.6;
learn_rate = learn0;
%学习率参数
learn_para=1000;
%设置迭代次数
iter =500;
%神经元位置
[I,J] = ind2sub([m, n], 1:som_sum);
%邻域初始化
neighbor0 =2;
neighbor_redius = neighbor0;
%邻域参数
neighbor_para = 1000/log(neighbor0);
%迭代次数
for t=1:iter
% 样本点遍历
for j=1:data_row
%获取样本点值
data_x = data(j,:);
%找到获胜神经元
[win_row, win_som_index]=min(dist(data_x,w'));
%获胜神经元的拓扑位置
[win_som_row,win_som_cloumn] = ind2sub([m, n],win_som_index);
win_som=[win_som_row,win_som_cloumn];
%计算其他神经元和获胜神经元的距离,邻域函数
%distance_som = sum(( ([I( : ), J( : )] - repmat(win_som, som_sum,1)) .^2) ,2);
distance_som = exp( sum(( ([I( : ), J( : )] - repmat(win_som, som_sum,1)) .^2) ,2)/(-2*neighbor_redius*neighbor_redius)) ;
%权值更新
for i = 1:som_sum
% if distance_som(i)<neighbor_redius*neighbor_redius
w(i,:) = w(i,:) + learn_rate.*distance_som(i).*( data_x - w(i,:));
end
end
%更新学习率
learn_rate = learn0 * exp(-t/learn_para);
%更新邻域半径
neighbor_redius = neighbor0*exp(-t/neighbor_para);
end
%data数据在神经元的映射
%神经元数组som_num存储图像编号
som_num=cell(1,size(w,1));
for i=1:size(w,1)
som_num{1,i}=[];
end
%每个神经元节点对应的data样本编号
for num=1:data_row
[som_row,clown]= min(sum(( (w - repmat(data(num,:), som_sum,1)) .^2) ,2));
som_num{1,clown}= [som_num{1,clown},num];
end
%存储神经元数组,.mat格式
path1=strcat(file_path,'som_num.mat');
save(path1,'som_num');
toc;
1)针对一些问题这里统一说明一下,data中的数据没有标签的类别属性,data.txt文本数据为每一行为一个用空格区分的多维数据样本,读者的数据中如果有类别标签可取出单独用一个数组存放 ,若想要对聚类效果进行评价,如使用NMI值进行评估时,再和聚类输出结果进行比较计算,代码如下贴出,否则以上计算中不涉及类别标签
2)以上代码只是som的收敛过程,还需要进一步的聚类收敛,如使用K-means算法对som聚类中心进行聚类达到最后的聚类结果
3)各种聚类算法各有适用范围,som并不具有普适性,总体上som使用的并不多,在图像分割上较为常见,大家掌握思想就好
function score = nmi(true_labels, cluster_labels)
%NMI Compute normalized mutual information (NMI) using the true and cluster
% labels and return the value in 'score'.
%
% Input : true_labels : N-by-1 vector containing true labels
% cluster_labels : N-by-1 vector containing cluster labels
%
% Output : score : NMI value
%
% Reference: Shi Zhong, 2003.
% http://www.cse.fau.edu/~zhong/software/textclust.zip
% Compute the confusion matrix 'cmat', where
% col index is for true label (CAT),
% row index is for cluster label (CLS).
n = length(true_labels);
cat = spconvert([(1:n)' true_labels ones(n,1)]);
cls = spconvert([(1:n)' cluster_labels ones(n,1)]);
cls = cls';
cmat = full(cls * cat);
n_i = sum(cmat, 1); % Total number of data for each true label (CAT), n_i
n_j = sum(cmat, 2); % Total number of data for each cluster label (CLS), n_j
% Calculate n*n_ij / n_i*n_j
[row, col] = size(cmat);
product = repmat(n_i, [row, 1]) .* repmat(n_j, [1, col]);
index = find(product > 0);
n = sum(cmat(:));
product(index) = (n*cmat(index)) ./ product(index);
% Sum up n_ij*log()
index = find(product > 0);
product(index) = log(product(index));
product = cmat .* product;
score = sum(product(:));
% Divide by sqrt( sum(n_i*log(n_i/n)) * sum(n_j*log(n_j/n)) )
index = find(n_i > 0);
n_i(index) = n_i(index) .* log(n_i(index)/n);
index = find(n_j > 0);
n_j(index) = n_j(index) .* log(n_j(index)/n);
denominator = sqrt(sum(n_i) * sum(n_j));
%denominator = -(sum(n_i) +sum(n_j))./2;
% Check if the denominator is zero
if denominator == 0
score = 0;
else
score = score / denominator;
end