用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,如果为1flag1            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调用,在这里会有:数据导入问题、算法实现问题、数据处理问题、图片读取及保存问题,等等

       在做工程期间特别感谢启功同志对我的大力支持与帮助。。。


0 0