1. 程式人生 > >機器學習-*-K均值聚類及程式碼實現

機器學習-*-K均值聚類及程式碼實現

KMeans聚類

在聚類演算法中,最出名的應該就是k均值聚類(KMeans)了,幾乎所有的資料探勘/機器學習書籍都會介紹它,有些初學者還會將其與KNN等混淆。k均值是一種聚類演算法,屬於無監督學習的一種,而KNN是有監督學習/分類學習的一種。
聚類:顧名思義,就是講某些相似的事物聚在一起,形成一個類。這裡就涉及到幾個概念
1.如何表示一個事物?通常我們會準備好一個數據集,裡面是我們的資料,每一行代表的都是一個數據,每一列是一個數據的一種特徵。比如經典的分類資料集 iris(鳶尾花資料),每一行代表的是每一朵花,每一朵花都有花萼長度,花萼寬度,花瓣長度,花瓣寬度 4個特徵,即有4列特徵。
2.如何度量事物間的距離?我們拿到每一個數據的特徵值之後,需要根據實際情況來進行兩種資料之間的計算,常用的方法是歐氏距離、馬氏距離、餘弦距離等。
3.按照什麼樣的過程進行聚類?這裡就涉及到具體的演算法了,目前聚類大致有幾個比較流行的方法:基於劃分、基於層次、基於密度、基於網路。這裡的K均值就屬於基於劃分的方法,後續會繼續寫其餘幾種方法的代表演算法。
4.如何能判斷聚類過程結束呢?當每一種類別中的資料趨於穩定,即為完成聚類

KMeans過程

KMeans聚類過程
上圖是擷取別人blog中的圖片(參考文獻1),這裡講的其實很清楚了。
上程式碼:

#!/usr/bin/python
# -*- coding:utf-8 -*-
"""
Author LiHao
Time 2018/11/26 9:21
"""
import os
import sys
import numpy as np
import scipy as sp
from sklearn.datasets import load_iris
# 歐式距離函式
from ml_learn.algorithm.distance import eculide
import matplotlib.pyplot as plt

def load_data():
    """
    匯入iris標準資料集
    :return:
    """
    iris = load_iris()
    data = iris.data
    target = iris.target
    target_names = iris.target_names
    return data,target,target_names

class Group(object):
    """
    定義類簇
    """
    def __init__(self):
        self._name = ""
        self._no = None
        self._members = []
        self._center = None
    @property
    def no(self):
        return self._no
    @property
    def name(self):
        return self._name
    @name.setter
    def name(self,no):
        self._no = no
        self._name = "G"+str(self._no)
    @property
    def members(self):
        return self._members
    @members.setter
    def members(self,member):
        if member is None:
            raise TypeError("member is None,please set value")
        self._members.append(member)
    def clear_members(self):
        self._members = []
    @property
    def center(self):
        return self._center
    @center.setter
    def center(self,c):
        self._center = c

class KMeans(object):
    def __init__(self,k = 2):
        if (k <= 1) or (k is None):
            raise ValueError("k's num must not none and must > 1.")
        self._k = k
        # 類簇
        self._groups = self._make_groups(k)
        self._pre_mean_value = 0
        self._current_mean_value = 1

    def _make_groups(self,k):
        """
        生成類簇
        :param k:
        :return:
        """
        groups = []
        for i in range(k):
            g = Group()
            g.name = i+1
            groups.append(g)
        return groups

    def _random_x_index(self,xlen):
        indexes = np.random.randint(0,xlen,self._k).tolist()
        return indexes

    def _compute_mean_value(self):
        sum = 0
        for i in range(len(self._groups)):
            average = self._compute_members_mean(self._groups[i].members)
            self._groups[i].center = average
            sum += average
        return sum/(len(self._groups))

    def _compute_members_mean(self,members):
        np_members = np.array(members)
        average = np.average(np_members,axis=0)
        return average

    def _find_most_nearby_group(self,x):
        np_groups = np.array([group.center for group in self._groups])
        distances = eculide(x,np_groups)
        most_similarity_index = np.argmin(distances).squeeze()
        self._groups[most_similarity_index].members = x
        return most_similarity_index

    def _clear_groups_members(self):
        for group in self._groups:
            group.clear_members()

    def fit(self,X):
        rows,cols = X.shape
        # 1.首先選取k個點為初始聚類中心點
        init_indexes = self._random_x_index(rows)
        for i,index in enumerate(init_indexes):
            self._groups[i].center = X[index]
            self._groups[i].members = X[index]
        # 2.計算每個資料與聚類中心的距離,加入到最近那一個類
        while(True):
            for i in range(rows):
                #發現距離最近的group 並將資料加入至類簇中
                self._find_most_nearby_group(X[i])
            # 3.重新計算每個類簇的平均值
            # 計算各個類別的聚類中心並返回所有類簇的均值
            self._current_mean_value = self._compute_mean_value()
            epos = np.sum(self._current_mean_value-self._pre_mean_value,axis=0).squeeze()
            if epos <= 0.00001:
                break
            # 清除歷史成員 並將計算得到的均值誤差儲存
            self._clear_groups_members()
            self._pre_mean_value = self._current_mean_value
            # 4.重複2-3的運算,直到每個類簇額均值不再發生變化
    def plot_example(self):
        figure = plt.figure()
        ax = figure.add_subplot(111)
        ax.set_title("KMeans Iris Example")
        colors = ['b','r','y','k','g','c','m']
        plt.xlabel("first dim")
        plt.ylabel("third dim")
        legends = []
        for i in range(len(self._groups)):
            group = self._groups[i]
            members = group.members
            x = [member[0] for member in members]
            y = [member[2] for member in members]
            ax.scatter(x,y,c=colors[i],marker='o')
            legends.append(group.name)
        plt.legend(legends,loc="best")
        plt.show()

data,target,target_names = load_data()
kmeans = KMeans(k=3)
kmeans.fit(data)
kmeans.plot_example()

經過執行,可以得到類似下圖的結果:選取的是iris資料集,展示的是第一維和第三維的二位平面圖。iris真實資料是分為3類,每一類50個數據。
每次執行的結果可能不一樣,因為我們選取的初始中心點是隨機的,這樣就會造成結果的不穩定性。因此,很多K均值的改進方法就會從初始點選取進行優化;還有的是優化了均值計算,變成了中位數計算(K_median演算法)
在這裡插入圖片描述

參考文獻

1.各種聚類演算法(原理+程式碼+對比分析)最全總結