DM&ML_note.7-神经网络聚类算法:SOM
2016-11-10 11:52
260 查看
这个学期要学DM&ML,用的是《数据挖掘算法原理与实现》王振武 本着造福同学的思想,开一个DM&ML的笔记系列,打算给书上的源代码添加一点注释,方便阅读和理解
前置知识要求
SOM网络设计
具体实现
感想
1.输入层结点数:样本维度=7*5
2.输出层结点数:取96个神经元构成8*12的二维平面阵。
3.权值初始化:随机归一化小数
4.领域半径:
5.学习率:
2.算法本身是模拟人体的神经元工作,某一类信息能够使得一定区域的神经元“兴奋”,在数据上表现为权值高,并且并且该区域内的兴奋度随着半径减小而提高。通过不断的学习输入样例,最终训练出用于聚类的网络。算法本身是不保证收敛的,但是在这份样例输入当中,收敛次数只有8次,这一点有点让我惊讶。迭代一次的复杂度为O(神经元个数*输入样例长度)。并且该算法有个特点,就是训练出来的结果可视化比较好。相似的元素的获胜节点【即最能反映出该元素类别的神经元】在神经元网络中是相近的,以样例数据为例,A,O,D,Q,C这几个数据。往往O,D,Q,C的坐标相对集中,因为他们更“相似”,而往往A的获胜节点的坐标和这四个数据的坐标相距甚远,因为他们“不太相似”。因为在学习的时候获胜节点的会影响到领域半径内结点的权值,所以相似的元素总能在空间上聚集。这样可以可视化的显示出数据的相关性和聚类特性。
3.到此为止基本上这本书上的源码就学习完毕了,当然他还提供了一份C#的SVM支持向量机的源码我还没有看,我会再写个总结帖把这个学期的数据挖掘学习笔记,链接,资源总结到一块去,顺便考虑把并行也做了。先这样。【吃饭】
前置知识要求
SOM网络设计
具体实现
感想
前置知识要求
C++SOM网络设计
注意,请仔细看这一部分的设计,这里写的是源码的相关参数,书上P191写的是例子的参数,有所不同。1.输入层结点数:样本维度=7*5
2.输出层结点数:取96个神经元构成8*12的二维平面阵。
3.权值初始化:随机归一化小数
4.领域半径:
r(t+1)=r(t)*(1-当前迭代数n/总迭代数N),t>1 r(t)=Max_size of outputLayer,t=1
5.学习率:
a(t+1)=a(t)*(1-当前迭代数n/总迭代数N),t>1 a(t)=default efficiency,t=1
具体实现
#include <fstream> #include <string> #include <iomanip> #include <iostream> #include <stdio.h> #include <stdlib.h> #include <math.h> using namespace std;/*hiro:忘记声明命名空间*/ #define InputLayerNum 35 #define OutputLayerRow 8 #define OutputLayerColumn 12 #define total_iteration_Num 1000000//10000//80//100//1000 #define error_limit 0.0000000000008//0.1//0.0000000000008//0.000000000000008//0.0001 #define efficiency 0.9//0.3//0.9//0.3//0.9 #define is_win true /*hiro:添加全局的字符-下标转换数组,真是的,该全局的不全局,,,写函数也好啊。。。*/ const string character = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; /*hiro:写在前面,我真的想不懂为何一堆应该局部的变量写全局,,偷懒也不是这么偷的啊。。。 而且该全局可以偷懒的又不写,,*/ int i,j,k,l,m,n; int inputMode[26][7][5]; /*hiro:↓↓修改了原本不友好的写法*/ double weight[OutputLayerRow][OutputLayerColumn][InputLayerNum]; /*hiro:记录迭代次数*/ int current_iteration_num=0; /*hiro:当前学习效率*/ double study_efficiency=efficiency; /*hiro:↓↓修改了原本不友好的写法*/ /*hiro:这个变量的名字应该和一些库文件里的声明冲突了,遂更改为my_distance*/ long double my_distance[OutputLayerRow][OutputLayerColumn]; /*hiro:保存了当前时刻的结点影响范围*/ int neighbor_width=OutputLayerColumn; int neighbor_height=OutputLayerRow; /*hiro:姑且是保存了所有获胜节点的下标,但是并没有用上 后面它又通过遍历的方法来获取获胜结点的下标。。*/ int row[OutputLayerRow], column[OutputLayerColumn]; /*hiro:改为bool 型更符合语意,保存了该下标是否为获胜节点*/ bool flag[OutputLayerRow][OutputLayerColumn]; int temp_row,temp_column; int winner_row,winner_column; long double min_distance=1000.0; /****************************************************************/ //该函数初始化距离变量为0,初始化保存胜出节点的位置的变量 /****************************************************************/ void init_distance() { for(i=0;i<OutputLayerRow;i++) for(j=0;j<OutputLayerColumn;j++) my_distance[i][j]=0.0; } /*hiro:增加函数用于处理范围参数 for_weight字段用来判断在weight_change函数里需要额外进行的操作*/ void legalizeInput(int &ttLow, int & ttUp, int &ppLow, int &ppUp,bool for_weight){ if (for_weight){ winner_row = temp_row; winner_column = temp_column; } ttLow = winner_column - neighbor_width / 2; ttUp = winner_column + neighbor_width / 2; ppLow = winner_row - neighbor_height / 2; ppUp = winner_row + neighbor_height / 2; if (ttLow<0) ttLow = 0; if (ttUp >= OutputLayerColumn) ttUp = OutputLayerColumn - 1; if (ppLow<0) ppLow = 0; if (ppUp >= OutputLayerRow) ppUp = OutputLayerRow - 1; } /****************************************************************/ //该函数用于计算欧氏距离,并找到获胜神经元 /****************************************************************/ void eula_distance() { int ttLow,ttUp,ppLow,ppUp; /*hiro:替换为用函数处理参数合法性*/ legalizeInput(ttLow, ttUp, ppLow, ppUp,false); for(i=ppLow;i<=ppUp;i++) for(j=ttLow;j<=ttUp;j++) { if (flag[i][j] != is_win) { for(m=0;m<7;m++) for(n=0;n<5;n++) my_distance[i][j]+=pow((inputMode[l][m] -weight[i][j][m*5+n]),2); if(my_distance[i][j]<min_distance) { min_distance=my_distance[i][j]; temp_row=i; temp_column=j; } } } if(current_iteration_num>0) { if(min_distance<=error_limit) { row[temp_row]=temp_row; column[temp_column]=temp_column; flag[temp_row][temp_column]=is_win; } } } /****************************************************************/ //调整权值 /****************************************************************/ void weight_change() { int ttLow,ttUp,ppLow,ppUp; /*hiro:替换为用函数处理参数合法性*/ legalizeInput(ttLow, ttUp, ppLow, ppUp,true); for(i=ppLow;i<=ppUp;i++) for(j=ttLow;j<=ttUp;j++) { if(!(flag[i][j]==is_win)) { for(m=0;m<7;m++) for(n=0;n<5;n++) weight[i][j][m*5+n]= weight[i][j][m*5+n]+ study_efficiency*(inputMode[l][m] -weight[i][j][m*5+n]); } } } /****************************************************************/ //调整学习效率以及获胜节点的邻域大小 /****************************************************************/ void paraChange() { /*hiro:省略了一些类型强制转换,稍微改了一下写法*/ double temp_rate = (double)current_iteration_num / total_iteration_Num; study_efficiency*=(1.0-temp_rate); neighbor_width*=(1.0 - temp_rate); neighbor_height*=(1.0 - temp_rate); } /*****************************************************************/ //该函数用于将所有输入模式从文件中读入,并存放到数组inputMode中; //同时进行权值的初始化,采用随机赋值的方法; /*****************************************************************/ void initialize() { for(i=0;i<OutputLayerRow;i++) row[i] = 100;/*hiro:这个100只是个初始值,没有什么意义*/ for(j=0;j<OutputLayerColumn;j++) column[j]=100; for(i=0;i<OutputLayerRow;i++) for(j=0;j<OutputLayerColumn;j++) flag[i][j]=false; //从文件中将所有输入模式读入,并存放到数组inputMode中 FILE *pf=fopen("相关数据\\输入数据\\input.txt","a+"); if(pf==NULL) { cout<<"Can not open input file!\n"; exit(0); } for(i=0;i<26;i++) for(j=0;j<7;j++) for(k=0;k<5;k++) fscanf(pf,"%d",&inputMode[i][j][k]); fclose(pf);/*hiro:忘记关闭文件*/ ///////////////////////////////////////////////////// //用于测试是否能够正确读入输入模式 ofstream mode ("相关数据\\输出数据\\向量模式.txt",ios::out) ; for(i=0;i<26;i++) { mode<<character[i]<<"\n"<<endl; for(j=0;j<7;j++) { for(k=0;k<5;k++) mode<<inputMode[i][j][k]<<" "; mode<<endl; } mode << "\n\n"<<endl;; } mode.close(); /*hiro:没有关闭文件,没有刷新缓冲*/ //////////////////////////////////////////////////// //权值初始化,采用随机赋值的方法 for(i=0;i<OutputLayerRow;i++) for(j=0;j<OutputLayerColumn;j++) for(k=0;k<InputLayerNum;k++) weight[i][j][k]=(double(rand()%101))/100.0; ///////////////////////////////////////////////////// //用于测试是否能够正确初始化权值 ofstream quan ("相关数据\\输出数据\\初始的权值.txt",ios::out) ; for(i=0;i<OutputLayerRow;i++) for(j=0;j<OutputLayerColumn;j++) { quan<<"\n\n\n"<<"Node["<<i+1<<"]["<<j+1<<"]"<<endl; for(k=0;k<InputLayerNum;k++) { if(k%5==0) quan<<endl; /*hiro:设置显示浮点数后6位*/ quan<<setprecision(6)<<setiosflags(ios::fixed)<<weight[i][j][k]<<" "; } quan<<"\n\n"<<endl; } quan.close(); /*hiro:忘记关闭文件和刷新缓冲区*/ //////////////////////////////////////////////////// } /************************************************************/ //利用数据测试训练后的网络 //hiro:由于原函数过于冗余,对两个函数进行整合, //使用val来标记输入输出的区别, //val==true使用标准测试数据 //val==false使用非标准测试数据 /************************************************************/ void test_netWork(bool val) { string s_out,s_in; if (val) s_out = "相关数据\\输出数据\\标准测试.txt"; else{ s_out = "相关数据\\输出数据\\非标准测试.txt"; s_in = "相关数据\\输入数据\\非标准数据测试.txt"; ifstream fin(s_in); if (!fin.is_open()){ cout << "Can not open input file!\n"; exit(0); } for (i = 0; i<26; i++) for (j = 0; j<7; j++) for (k = 0; k<5; k++) fin >> inputMode[i][j][k]; fin.close(); } ofstream fout(s_out); for (l = 0; l<26; l++) { init_distance(); min_distance = 1000; for (i = 0; i<OutputLayerRow; i++) for (j = 0; j<OutputLayerColumn; j++) { for (m = 0; m<7; m++) for (n = 0; n<5; n++) /*hiro:稍微去掉一些冗余的(long double)*/ my_distance[i][j] += (long double)pow((inputMode[l][m] - (long double)weight[i][j][m * 5 + n]), 2); if (my_distance[i][j]<min_distance) { min_distance = my_distance[i][j]; temp_row = i; temp_column = j; } } fout<< character[l] << "'s winner is Node[" << temp_row + 1 << "][" << temp_column + 1 << "]\n" << endl; } fout.close(); } void main(void) { int iteration_numbers[26] = { 0 };/*hiro:简化了初始化*/ int total_num=0; initialize(); for(l=0;l<26;l++) { winner_row=OutputLayerRow/2; winner_column=OutputLayerColumn/2; while(current_iteration_num<total_iteration_Num) { init_distance(); eula_distance(); weight_change(); if(min_distance<=error_limit) break; ++current_iteration_num; paraChange(); }; iteration_numbers[l]=current_iteration_num+1; neighbor_width=OutputLayerColumn; neighbor_height=OutputLayerRow; study_efficiency = efficiency; current_iteration_num=0; min_distance=1000.0; } for(l=0;l<26;l++) total_num+=iteration_numbers[l]; ofstream iteration_num("相关数据\\输出数据\\迭代次数.txt",ios::out); for(l=0;l<26;l++) { iteration_num<<character[l]<<" 迭代"<<iteration_numbers[l]<<"次!\n"<<endl; } iteration_num << "整个训练过程共迭代" << total_num << "次!\n" << endl; iteration_num.close(); /*hiro:去掉DOS显示,缩短严重冗余的代码*/ ofstream all_weight("相关数据\\输出数据\\训练后所有权值.txt",ios::out ) ; ofstream winner_weight("相关数据\\输出数据\\训练后胜出权值.txt", ios::out); ofstream winner_node("相关数据\\输出数据\\获胜节点.txt", ios::out); for (i = 0; i<OutputLayerRow; i++) for(j=0;j<OutputLayerColumn;j++) { all_weight<<"\n\n\n"<<"Node["<<i+1<<"]["<<j+1<<"]"<<endl; if (flag[i][j] == is_win) winner_weight << "\n\n\n" << "Node[" << i + 1 << "][" << j + 1 << "]" << endl; for(k=0;k<InputLayerNum;k++) { if(k%5==0) all_weight<<endl; /* ///////////////////////////////////////////////// if(weight[i][j][k]>0.9999999) weight[i][j][k]=1.0; if(weight[i][j][k]<0.0000001) weight[i][j][k]=0.0; */ ///////////////////////////////////////////////// /*hiro:显示浮点数小数点后8位*/ all_weight<<setprecision(8)<<setiosflags(ios::fixed)<<weight[i][j][k]<<" "; if (flag[i][j] == is_win) winner_weight << setprecision(8) << setiosflags(ios::fixed) << weight[i][j][k] << " "; } if (flag[i][j] == is_win) winner_node << "Node[" << i + 1 << "][" << j + 1 << "]" << endl; } /*hiro:没有刷缓冲区和关文件*/ printf("\n"); all_weight.close(); winner_weight.close(); winner_node.close(); /////////////////////////////////////////////////// //网络测试 test_netWork(true); test_netWork(false); }
感想
1.这份代码应该是出自写C4.5算法的那位兄台之手,看着代码还不算长,于是对代码的可读性进行了一定优化,并且减少大量冗余代码。2.算法本身是模拟人体的神经元工作,某一类信息能够使得一定区域的神经元“兴奋”,在数据上表现为权值高,并且并且该区域内的兴奋度随着半径减小而提高。通过不断的学习输入样例,最终训练出用于聚类的网络。算法本身是不保证收敛的,但是在这份样例输入当中,收敛次数只有8次,这一点有点让我惊讶。迭代一次的复杂度为O(神经元个数*输入样例长度)。并且该算法有个特点,就是训练出来的结果可视化比较好。相似的元素的获胜节点【即最能反映出该元素类别的神经元】在神经元网络中是相近的,以样例数据为例,A,O,D,Q,C这几个数据。往往O,D,Q,C的坐标相对集中,因为他们更“相似”,而往往A的获胜节点的坐标和这四个数据的坐标相距甚远,因为他们“不太相似”。因为在学习的时候获胜节点的会影响到领域半径内结点的权值,所以相似的元素总能在空间上聚集。这样可以可视化的显示出数据的相关性和聚类特性。
3.到此为止基本上这本书上的源码就学习完毕了,当然他还提供了一份C#的SVM支持向量机的源码我还没有看,我会再写个总结帖把这个学期的数据挖掘学习笔记,链接,资源总结到一块去,顺便考虑把并行也做了。先这样。【吃饭】
相关文章推荐
- hellow ML(TensorFlow&神经网络模式分类)
- 【HowTo ML】分类问题->神经网络入门
- DM&ML_note.6-K-中心点聚类算法
- Self Organizing Maps (SOM): 一种基于神经网络的聚类算法
- Self Organizing Maps (SOM): 一种基于神经网络的聚类算法
- 自组织特征映射神经网络(SOM)
- Micheal Nielsen's神经网络学习之三:过拟合与规范化
- 算法讲解—【1】—决策树&神经网络
- ML 神经网络 NeuralNetworks
- 机器学习之&&Andrew Ng课程复习--- 神经网络的表层结构1
- 深度学习_caffe (4) 基于mnist实例搭建新的神经网络&在caffe中添加层
- 自组织神经网络介绍:自组织特征映射SOM(Self-organizing feature Map),第一部分
- 神经网络指南Hacker's guide to Neural Networks
- SOM神经网络的数据分类--柴油机故障诊断
- 自组织神经网络介绍:自组织特征映射SOM(Self-organizing feature Map),第一部分
- Micheal Nielsen's神经网络学习之二
- 开始写ML&DM方面的博客了
- SOM神经网络小记
- Coursera_Stanford_ML_ex4_神经网络训练 作业记录
- 自组织神经网络介绍:自组织特征映射SOM(Self-organizing feature Map),第一部分