用Spark实现K-means(scala:面向函数式编程风格)
来源:互联网 发布:hp1010墨盒清零软件 编辑:程序博客网 时间:2024/05/01 14:13
免责声明:本文仅代表个人观点,如有错误,请读者自己鉴别;如果本文不小心含有别人的原创内容,请联系我删除;本人心血制作,若转载请注明出处
K-means算法很多人在其他平台实现过(C语言、MATLAB、JAVA、C++),但是在spark的实现是最让我印象深刻的一次,我们知道spark的所有操作都是基于RDD实现的,而关于RDD的操作可以分为两种:transformation、action,而这些转换怎么通过面向函数式编程scala实现,是我这次所要着重讲的。
主要分为模式设置、数据集导入、代码框架、试验结果保存(k-means效果图保存)、总结
1、设置模式(我这次是在单击上上实现的,从单机到集群转换一步之遥而已)
2、数据集导入:calteck-256, iamgeSize : 256 * 256
这次我要导入的是calteck-256数据集,这个数据集有很多类,每一类有很多图片,我破门首先要做的是找到一种合适的数据结构(数组、容器、链表、、、)来储存数据,导入spark,建立spark的数据库DataFrame,是我们这一步要做的
1)、读入数据(二维数组方式Data[imageNum][imageSize])
首先要取读取每个路径,如果里面是文件夹,就继续往里面挖,如果里面是图片,就读取
读取图片:
将图片信息转成Vector形式(24位,每次读八位,正好是RGB图,3个通道),并生成DataFrame
3、代码框架
1)、生成种子点
随机生成种子点:
或者,也可以在整幅图上均匀生成种子点(行、列、R、G、B)
9
2)、计算每个像素点与种子点的距离,并得到标签
3)、更新聚类中心
4、实验结果保存
1)、将聚类结果得到的标签+原图像 生成聚类效果图
得到标签
得到聚类效果图
实际上也就是判断这个像素点与邻域像素点的标签的关系,不一样的多了,就判断是类之间的分界线,标为蓝色
2)、保存效果图,之前的读取是从24位读成了3个8位数据(RGB),现在是将3个8位数据(RGB)合成之前的24位
效果图
3)、后处理,主要是根据连通区域去处理
这个主要是在聚类里面的内容(SLIC),因为slic算法会初始化很多种子点,最后要进行合并,在这里我们可以用四连通,也可以用八连通,常用四联通就好,具体做法是:如果两个类在四连通区域内有连接,就要进行相应的合并,去坏点,关于这个最好定义相应的数据结构,小弟不才,所实现的方式确实效率一般,就不多讲了,贴出代码,仅供参考吧
// 找到位置为 num 的位置,对新的数组对应位置赋为 1 public int[][] getnumPos(int[][] belong,int num) { int row = belong.length; int col = belong[0].length; int[][] numPos = new int[row][col]; for(int i=0;i<row;i++) { for(int j=0;j<col;j++) { numPos[i][j] = (belong[i][j] == num)? 1:0; } } return numPos; } // 得到4连通区域,分别给连通区域赋值给1,2,3,,, public int[][] geteLiantong(int[][] numPos) { int row = numPos.length; int col = numPos[0].length; int[][] liantong = new int[row][col]; // 用来记录每一行连通区域的初始位置,结束位置,以及所属连通区域编号 int Num = 0; //用来记录连通区域的编号 // 寻找第 1 行的连通区域 int flag=0; for(int m=0; m<col; m++) { int num =0; //用来表示这一行连通区域的个数 // 如果这个点为1,上个点为0 if(numPos[0][m] == 1 && flag==0) { flag=1; Num++; } if(numPos[0][m] == 0 && flag == 1) { flag=0; } //如果 flag 为 1 ,就给 liantong 数组的对应位置赋值 num liantong[0][m] = (flag == 1) ? Num :0 ; } //接下来从第 2 行开始查找,每一行的连通区域 for(int i=1;i<row;i++) { flag=0; //代表当前位置是否为1,如果为1则flag赋1 for(int j=0;j<col;j++) { //如果这个点为 0 if(numPos[i][j] == 0) flag=0; // 如果这个点为1,左面点为0,上面点为0 ,这个点赋值为 ++NUm else if(numPos[i][j] == 1 && flag == 0 && liantong[i-1][j] == 0) { flag=1; liantong[i][j] = ++Num; } // 如果这个点为1,左面点不为0,上面点为0 ,这个点赋值为 liantong[i][j-1] else if(numPos[i][j] == 1 && flag == 1 && liantong[i-1][j] == 0) { liantong[i][j] =liantong[i][j-1]; } // 如果这个点为1,左面点为0,上面点不为0 ,这个点赋值为 liantong[i-1][j] else if(numPos[i][j] == 1 && flag == 0 && liantong[i-1][j] != 0) { flag=1; liantong[i][j] =liantong[i-1][j]; } // 如果这个点为1,左面点不为0,上面点不为0 ,这个点赋值为 上面和左面点中较小的,并把较大的点全部赋值给较小的点 else if(numPos[i][j] == 1 && flag == 1 && liantong[i-1][j] != 0) { flag=1; liantong[i][j] = (numPos[i-1][j] < liantong[i][j-1])?numPos[i-1][j]:liantong[i][j-1]; int bigger = liantong[i][j-1]; if(liantong[i-1][j]>liantong[i][j-1]) bigger= liantong[i-1][j]; for(int m=i;m>=0;m--) { int numbigger = 0; for(int n=0;n<col;n++) { if(liantong[m][n] == bigger) { liantong[m][n] = liantong[i][j]; numbigger++; } } if(numbigger == 0) m=-1; } } } } return liantong; } /* 处理4连通区域,如果连通区域小于100个点,赋值给邻域 * 输入: 得到的连通区域矩阵 * 输出: 对对应位置进行处理 * */ public int[][] manageLiantong(int[][] liantong,int[][] Label) { int row = liantong.length; int col = liantong[0].length; int[] numPos = new int[row*col]; // 记下出现过的连通区域的标号 ,取一个足够大的矩阵来存储 int num = 0; //记下出现过得连通区域的个数 int Flag = 0; for(int i=0;i<row;i++) { for(int j=0;j<col;j++) { if(liantong[i][j] !=0) { Flag = 0; for(int n=0;n<num+1;n++) { if(liantong[i][j] == numPos[n]) { Flag =1; n=num+1; } } if(Flag == 0) { numPos[num] = liantong[i][j]; num = num+1; } } } } for(int n=0;n<num+1;n++) { int numNum = 0; //表示标号为 numPos[n] 的连通区域的像素个数 for(int i=0;i<row;i++) { for(int j=0;j<col;j++) { if(liantong[i][j] ==numPos[n]) numNum++; } } if(numNum>0 && numNum<50) { int label_n = 0; for(int x=0;x<row;x++) { for(int y=0;y<col;y++) { if(Label[x][y] == n) { if(x-1>=0) label_n=Label[x-1][y]; else if(y-1>=0) label_n=Label[x][y-1]; else if(y+1<=col-1) label_n=Label[x][y+1]; else if(x+1<=row-1) label_n=Label[x+1][y]; y=col; x=row; } } } for(int x=0;x<row;x++) { for(int y=0;y<col;y++) { if(Label[x][y] == n) Label[x][y] = label_n; } } } } return Label; }}
总结
面向函数式编程,非常灵活,确实不是我们能一朝一夕掌握的,所以大家应该逐渐时应这个风格,在用spark做工程时,可以尽量用scala去写,但是如果实在难以实现,可以用java去定义函数,用scala调用,在这里会有:数据导入问题、算法实现问题、数据处理问题、图片读取及保存问题,等等
在做工程期间特别感谢启功同志对我的大力支持与帮助。。。
- 用Spark实现K-means(scala:面向函数式编程风格)
- k-means/k-means++算法的笔记及scala实现
- scala面向函数式编程
- spark mllib k-means算法实现
- K-Means及Spark实现Kmeans算法
- scala编程系列(6)-scala函数式风格
- scala学习笔记三----scala函数式编程风格学习
- Spark基础-Scala函数式编程
- Spark基础-Scala集合函数式编程
- Spark MLlib(一)K-Means
- Spark K-Means
- Spark-K-Means算法
- k-means算法原理以及Scala调用MLlib实现
- K均值(K-means)算法原理及Spark MLlib调用实例(Scala/Java/python)
- 【Scala编程】函数式风格编写排序算法
- 初试Spark之K-Means聚类算法实现
- 初试Spark之K-Means聚类算法实现
- spark平台 mllib K-Means聚类算法 实现
- 系统学习机器学习之模型选择
- Maven 整合 SpringMvc Spring Hibernate +oracle
- java List系(ArrayList,LinkedList,Vector)的比较
- 一个简易TCP服务器的多种实现
- vs2010 如何快速查看 别人工程代码
- 用Spark实现K-means(scala:面向函数式编程风格)
- java上传文件跟批量下载文件
- Android 基于Message的进程间通信
- C++入门(20):字符串
- AutoCompleteTextView的简单使用
- nodejs-post文件上传原理详解
- WebDriverWait
- ajax传递给后台数组参数方式
- 安卓修改过源码之后如何给生成的app进行系统签名