K-Means算法

作者: 任嘉平生愿 | 来源:发表于2019-02-18 12:32 被阅读51次

    定义:K-Means 是一种基于距离的排他的聚类划分方法。

    例如:身高体重划分

    image

    由于 K-Means 算法值针对给定的完整数据集进行操作,不需要任何特殊的训练数据

    所以 K-Means 是一种无监督的机器学习

    K-Means 实现

    image

    以身高和体重为例子,开始确定3个K点(大圆点),然后计算与K 的距离然后将多个数据集划分。

    k 的选择一般基于经验值和多次实验结果。例如1.5米50kg算矮标准,1.5米60kg算矮胖。

    算法实现

    具体的算法步骤如下:

    1.随机选择K个中心点
    2.把每个数据点分配到离它最近的中心点;
    3.重新计算每类中的点到该类中心点距离的平均值
    4.分配每个数据到它最近的中心点;
    5.重复步骤3和4,直到所有的观测值不再被分配或是达到最大的迭代次数(R把10次作为默认迭代次数)。

    java代码实现
    package KMeans;
    
    import java.io.*;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Vector;
    
    
    public class MyKmeans {
    
        static Vector<Point>  li=new Vector<Point>();
        //static List<Point>  li=new ArrayList<Point>();
        static List<Vector<Point>> list=new ArrayList<Vector<Point>>(); //每次迭代保存结果,一个vector代表一个簇
        private final static Integer K=2; //选K=2,也就是估算有两个簇。
        private final static Double converge=0.001; //当距离小于某个值的时候,就认为聚类已经聚类了,不需要再迭代,这里的值选0.001
    
        //读取数据
        public static final void readF1() throws IOException {
            String filePath="simple_k-means.txt";
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            InputStream inputStream = classLoader.getResourceAsStream(filePath);
            BufferedReader br = new BufferedReader(new InputStreamReader(inputStream));
            for (String line = br.readLine(); line != null; line = br.readLine()) {
                if(line.length()==0||"".equals(line))continue;
                String[] str=line.split(" ");
                Point p0=new Point();
                p0.setX(Double.valueOf(str[0]));
                p0.setY(Double.valueOf(str[1]));
                li.add(p0);
                //System.out.println(line);
            }
            br.close();
        }
        //math.sqrt(double n)
        //扩展下,如果要给m开n次方就用java.lang.StrictMath.pow(m,1.0/n);
        //采用欧氏距离
        public static  Double DistanceMeasure(Point p1,Point p2){
    
            Double tmp=StrictMath.pow(p2.getX()-p1.getX(), 2)+StrictMath.pow(p2.getY()-p1.getY(), 2);
            return Math.sqrt(tmp);
        }
    
        //计算新的簇心
        public static Double CalCentroid(){
            System.out.println("------------------------------------------------");
            Double movedist=Double.MAX_VALUE;
            for(int i=0;i<list.size();i++){
                Vector<Point> subli=list.get(i);
                Point po=new Point();
                Double sumX=0.0;
                Double sumY=0.0;
                Double Clusterlen=Double.valueOf(subli.size());
                for(int j=0;j<Clusterlen;j++){
                    Point nextp=subli.get(j);
                    sumX=sumX+nextp.getX();
                    sumY=sumY+nextp.getY();
                }
                po.setX(sumX/Clusterlen);
                po.setY(sumY/Clusterlen);
                //新的点与旧点之间的距离
                Double dist=DistanceMeasure(subli.get(0),po);
                //在多个簇心移动的过程中,返回移动距离最小的值
                if(dist<movedist)movedist=dist;
                list.get(i).clear();
                list.get(i).add(po);
                System.out.println("C"+i+"的簇心为:"+po.getX()+","+po.getY());
            }
            String test="ll";
            return movedist;
        }
        //本次的簇心
        //下一次移动的簇心
    
        private static Double move=Double.MAX_VALUE;//移动距离
        //不断地迭代,直到收敛
        public static void RecursionKluster(){
            for(int times=2;move>converge;times++){
                System.out.println("第"+times+"次迭代");
                //默认每一个list里的Vector第0个元素是质心
                for(int i=0;i<li.size();i++){
                    Point p=new Point();
                    p=li.get(i);
                    int index = -1;
    
                    double neardist = Double.MAX_VALUE;
                    for(int k=0;k<K;k++){
                        Point centre=list.get(k).get(0);
                        double currentdist=DistanceMeasure(p,centre);
                        if(currentdist<neardist){
                            neardist=currentdist;
                            index=k;
                        }
                    }
    
                    System.out.println("C"+index+":的点为:"+p.getX()+","+p.getY());
                    list.get(index).add(p);
    
                }
                //重新计算簇心,并返回移动的距离,最小的那个距离
    
                move=CalCentroid();
                System.out.println("各个簇心移动中最小的距离为,move="+move);
            }
        }
    
        public static void Kluster(){
    
            for(int k=0;k<K;k++){
                Vector<Point> vect=new Vector<Point>();
                Point p=new Point();
                p=li.get(k);
                vect.add(p);
                list.add(vect);
            }
            System.out.println("第1次迭代");
            //默认每一个list里的Vector第0个元素是质心
            for(int i=K;i<li.size();i++){
                Point p=new Point();
                p=li.get(i);
                int index = -1;
    
                double neardist = Double.MAX_VALUE;
                for(int k=0;k<K;k++){
                    Point centre=list.get(k).get(0);
                    double currentdist=DistanceMeasure(p,centre);
                    if(currentdist<neardist){
                        neardist=currentdist;
                        index=k;
                    }
                }
    
                System.out.println("C"+index+":的点为:"+p.getX()+","+p.getY());
                list.get(index).add(p);
    
            }
    
        }
    
        public static void main(String[] args) throws IOException {
            // TODO Auto-generated method stub
            //读取数据
            readF1();
            //第一次迭代
            Kluster();
            //第一次迭代后计算簇心
            CalCentroid();
            //不断迭代,直到收敛
            RecursionKluster();
        }
    
    }
    
    
    package KMeans;
    
    
    import lombok.Data;
    
    @Data
    public class Point {
        double X;
        double Y;
    }
    
    

    simple_k-means.txt

    1 1  
    2 1  
    1 2  
    2 2  
    3 3  
    8 8  
    8 9  
    9 8  
    9 9 
    
    

    运行结果

    第1次迭代
    C0:的点为:1.0,2.0
    C1:的点为:2.0,2.0
    C1:的点为:3.0,3.0
    C1:的点为:8.0,8.0
    C1:的点为:8.0,9.0
    C1:的点为:9.0,8.0
    C1:的点为:9.0,9.0
    ------------------------------------------------
    C0的簇心为:1.0,1.5
    C1的簇心为:5.857142857142857,5.714285714285714
    第2次迭代
    C0:的点为:1.0,1.0
    C0:的点为:2.0,1.0
    C0:的点为:1.0,2.0
    C0:的点为:2.0,2.0
    C0:的点为:3.0,3.0
    C1:的点为:8.0,8.0
    C1:的点为:8.0,9.0
    C1:的点为:9.0,8.0
    C1:的点为:9.0,9.0
    ------------------------------------------------
    C0的簇心为:1.6666666666666667,1.75
    C1的簇心为:7.971428571428572,7.942857142857143
    各个簇心移动中最小的距离为,move=0.7120003121097943
    第3次迭代
    C0:的点为:1.0,1.0
    C0:的点为:2.0,1.0
    C0:的点为:1.0,2.0
    C0:的点为:2.0,2.0
    C0:的点为:3.0,3.0
    C1:的点为:8.0,8.0
    C1:的点为:8.0,9.0
    C1:的点为:9.0,8.0
    C1:的点为:9.0,9.0
    ------------------------------------------------
    C0的簇心为:1.777777777777778,1.7916666666666667
    C1的簇心为:8.394285714285715,8.388571428571428
    各个簇心移动中最小的距离为,move=0.11866671868496578
    第4次迭代
    C0:的点为:1.0,1.0
    C0:的点为:2.0,1.0
    C0:的点为:1.0,2.0
    C0:的点为:2.0,2.0
    C0:的点为:3.0,3.0
    C1:的点为:8.0,8.0
    C1:的点为:8.0,9.0
    C1:的点为:9.0,8.0
    C1:的点为:9.0,9.0
    ------------------------------------------------
    C0的簇心为:1.7962962962962965,1.7986111111111114
    C1的簇心为:8.478857142857143,8.477714285714285
    各个簇心移动中最小的距离为,move=0.019777786447494432
    第5次迭代
    C0:的点为:1.0,1.0
    C0:的点为:2.0,1.0
    C0:的点为:1.0,2.0
    C0:的点为:2.0,2.0
    C0:的点为:3.0,3.0
    C1:的点为:8.0,8.0
    C1:的点为:8.0,9.0
    C1:的点为:9.0,8.0
    C1:的点为:9.0,9.0
    ------------------------------------------------
    C0的簇心为:1.799382716049383,1.7997685185185184
    C1的簇心为:8.495771428571429,8.495542857142857
    各个簇心移动中最小的距离为,move=0.003296297741248916
    第6次迭代
    C0:的点为:1.0,1.0
    C0:的点为:2.0,1.0
    C0:的点为:1.0,2.0
    C0:的点为:2.0,2.0
    C0:的点为:3.0,3.0
    C1:的点为:8.0,8.0
    C1:的点为:8.0,9.0
    C1:的点为:9.0,8.0
    C1:的点为:9.0,9.0
    ------------------------------------------------
    C0的簇心为:1.7998971193415638,1.7999614197530864
    C1的簇心为:8.499154285714287,8.499108571428572
    各个簇心移动中最小的距离为,move=5.49382956874724E-4
    
    Process finished with exit code 0
    

    K-Means 聚类算法 - 匠心十年 - 博客园

    相关文章

      网友评论

        本文标题:K-Means算法

        本文链接:https://www.haomeiwen.com/subject/wjcaeqtx.html