k-means算法理解与图像分割

模式识别的课程作业,要求实现一个k-means算法,并深入分析.不知道为什么老师讲的算法名字叫c-means.(课程报告仅供参考,避免全部一样)

k-means算法介绍

kmeans算法又名k均值算法。其算法思想大致为:先从样本集中随机选取 k 个样本作为簇中心,并计算所有样本与这 k 个“簇中心”的距离,对于每一个样本,将其划分到与其距离最近的“簇中心”所在的簇中,对于新的簇计算各个簇的新的“簇中心”。

根据以上描述,可以得出算法的基本步骤:

  • 初始化常数K,随机选取初始点为质心
  • while : 直到质心不再改变
    • 计算样本与每个质心之间的相似度,
    • 将样本归类到最相似的类中
    • 重新计算质心
  • 输出最终的质心以及每个类
import numpy as np
import matplotlib.pyplot as plt
import random

#加载数据
def loadData(filename):
    dataSet = []
    with open(filename) as fr:
        for l in fr.readlines():
            indata = l.strip().split('\t')
            dataSet.append([float(x) for x in indata])
    dataSet = np.array(dataSet)
    return dataSet

#欧式距离
def disEclud(vec1, vec2):
    return ((vec1 - vec2) ** 2).sum(axis = 1) ** 0.5

#初始化类心
def initCent(dataSet, k):
    m, n = np.shape(dataSet)
    centroids = dataSet[random.sample(range(m), k)]
    return centroids

def kMeans(dataSet, k):
    m = dataSet.shape[0]
    #类心
    centroids = initCent(dataSet, k)
    #分类
    clusterAssment = np.zeros(m)
    clusterChange = np.array(centroids)
    while True:
        for i in range(m):
            dis = disEclud(dataSet[i], centroids)
            clusterAssment[i] = dis.argmin()
        for cent in range(k):
            centroids[cent] = dataSet[np.where(clusterAssment == cent)].mean(axis = 0)
        if (centroids == clusterChange).all():
            break
        else:
            clusterChange = np.array(centroids)
            continue
    return centroids, clusterAssment
    
train_x, label = loadData('test.txt')
center, classSet = kMeans(train_x, 3)

plt.scatter(train_x[:, 0], train_x[:, 1], c = p, marker = ">")
plt.scatter(a[:, 0], a[:, 1], s = 200, c = 'r', marker = '*')

在这里插入图片描述

作业内容:对wine数据集进行聚类

数据集:http://archive.ics.uci.edu/ml/index.html
对这个数据集进行聚类,一共178个样本,分成3类,13个数据集,但是有14列,第一列是分类结果.在聚类的时候不用第一列的数据.

import numpy as np
import matplotlib.pyplot as plt
import random

#加载数据
def loadData(filename):
    dataSet = []
    with open(filename) as fr:
        for l in fr.readlines():
            indata = l.strip().split(',')
            dataSet.append([float(x) for x in indata])
    dataSet = np.array(dataSet)
    train_x = np.array(dataSet[:,1::])
    train_y = np.array(dataSet[:,0])
    return train_x, train_y
     
#数据归一化处理
def dataNormalized(dataSet):
    dataSetMin = dataSet.min(axis = 0)
    print(dataSetMin.shape)
    dataSetMax = dataSet.max(axis = 0)
    dataSet = (dataSet - dataSetMin) / (dataSetMax - dataSetMin)
    return dataSet

#欧式距离
def disEclud(vec1, vec2):
    return ((vec1 - vec2) ** 2).sum(axis = 1) ** 0.5

#初始化类心
def initCent(dataSet, k, initmode=0, initcenter):
	#分为3种方法,通过initmode改变
    m, n = np.shape(dataSet)
    if initmode == 0:        #随机选取中心
        centroids = dataSet[random.sample(range(m), k)]
    elif initmode == 1:        #自定义类心
        centroids = np.array(initcenter)
    else:
    #随机选取一些数据,求出他们的平均数,作为类心
        centroids = np.zeros((k, n))
        for i in range(k):
            centroids[i] = dataSet[random.sample(range(m), m // k)].mean()
    return centroids

def kMeans(dataSet, k, mindif = 0, initmode = 0, initcenter = []):
	'''
	mindif:前后类心改变的最小阈值
	initmode:初始化类心方式
	initcenter:自定义初始化类心
	'''
    m, n = dataSet.shape
    minLimit = np.zeros((k, n))
    minLimit.fill(mindif)
    #初始类心
    centroids = initCent(dataSet, k, initmode, initcenter)
    #分类结果
    clusterAssment = np.zeros(m)
    #用于判断类心是否改变
    lastcent = np.zeros(centroids.shape)
    while True:
        #重新聚类
        for i in range(m):
            dis = disEclud(dataSet[i], centroids)
            clusterAssment[i] = dis.argmin()
        #更新类心
        for cent in range(k):
            centroids[cent] = dataSet[np.where(clusterAssment == cent)].mean(axis = 0)
        #判断是否停止迭代
        if (abs(centroids - lastcent) <= minLimit).all():
            break
        else:
            lastcent = np.array(centroids)
            continue
        
    return centroids, clusterAssment

#求众数   
def mode(train_x):
    train_x = train_x.astype(np.int64)
    counts = np.bincount(train_x)
    return np.argmax(counts)
    
#测试数据
def test(classSet, train_y, k):
    label = np.array(train_y)
    for i in range(1, k+1):
        categorySet = classSet[np.where(train_y == i)]
        category = mode(categorySet)
        label[np.where(train_y == i)] = category
    return (classSet == label).sum() / classSet.shape
    
train_x, label = loadData('wine.data.txt')
train_x = dataNormalized(train_x)
center, classSet = kMeans(train_x, 3, initmode=2)
print(test(classSet, label, 3))

输出:

[0.9494382] 
#采用第3中初始类心的方法,可以稳定在这个数据左右.
#如果是随机初始类心的话,最高的准确率可达到.0.96

k-means算法的优缺点

优点:简单,对簇型数据分类较好
缺点:

  • 对离群点和孤立点敏感,可以通过离群点检测的LOF算法,通过去除离群点后再聚类,可以减少离群点和孤立点对于聚类效果的影响。

  • k值选取不确定,可以通过ISODATA算法改进.

  • 初始聚类中心会影响最后的分类结果,可能会收敛于局部极小值,改进方法有k-means++ 或 二分K-means.

  • 使用于球形簇类数据的聚类.根本原因在于距离度量的方式,原方法求距离的时候采用欧氏距离,因此是一个超球体.对于不规则的数据,往往基于密度的聚类算法更合适,比如DBSCAN算法.

  • k-means适用于连续数据.采用k-modes算法,可实现对离散数据的快速聚类.

  • 数据量大的话,时间会很长.时间复杂度是O(nkl),l为迭代次数,n为数据集容量.在传统的K-Means算法中,我们在每轮迭代时,要计算所有的样本点到所有的质心的距离,这样会比较的耗时。

    • (1)elkan K-Means算法通过对距离计算进行优化,减少不必要的距离计算,提高计算效率,但是如果样本是稀疏的,该方法不适应.
    • (2)Mini Batch K-Means法算法是通过选取样本中一部分的数据量进行计算,算法收敛速度大大加快,但是聚类的精确度也会有一些降低,一般来说这个降低的幅度在可以接受的范围之内。为了增加算法的准确性,我们一般会多跑几次Mini Batch K-Means算法,用得到不同的随机采样集来得到聚类簇,选择其中最优的聚类簇。

k-means算法与图像分割

图像分割是图像处理中的一种方法,图像分割是指将一幅图像分解成若干互不相交区域的集合,其实质可以看成是一种像素的聚类过程。通常使用到的图像分割的方法可以分为:

  • 基于边缘的技术
  • 基于区域的技术

基于聚类算法的图像分割属于基于区域的技术。

具体的原理是:对图像的像素值进行聚类,颜色相近的像素会形成一类.

主要用到PIL和sklearn.(自己写的kmeans,由于初始类心选取的不好,经常不能很好的分离,因此用sklearn中的kMeans)

PIL主要用到以下几个函数:

#打开图片
q = Image.open('test2.jpg')
#图片显示
q.show()
#转换图片格式,灰度图
q = q.convert('L')
#图片的属性
im.format, im.size, im.mode
#rgb通道分离
r,g,b = q.split()
#取得像素点的值
q.getpixel((4,4))
#改变像素点的值
q.putpixel(xy, color)
#创建新图
Image.new(mode, size)
#保持图
im.save("save.gif","GIF")

以下是图像分离全部代码

from PIL import Image
from sklearn.cluster import KMeans

q = Image.open('test2.jpg')
q.show()
m, n = q.size
q1 = np.array(q)
#这里有坑,注意q.size和q1.shape的值不一样,横竖跌倒

q1 = q1.reshape((m*n, 3))

k = 8
y = KMeans(n_clusters = k).fit_predict(q1)

centroids = np.zeros((k, 3))
#计算类心值,之后填充图像
for cent in range(k):
    centroids[cent] = q1[np.where(y == cent)].mean(axis = 0)
    
ynew = y.reshape((n, m))
pic_new = Image.new("RGB", (m, n))
for i in range(m):
    for j in range(n):
        pic_new.putpixel((i, j), tuple([int(x) for x in centroids[ynew[j][i]]]))
pic_new.show()

原图是:
k-means算法理解与图像分割_第1张图片
聚类之后的图:k = 8
k-means算法理解与图像分割_第2张图片
分离出来的苹果:
k-means算法理解与图像分割_第3张图片

你可能感兴趣的:(模式识别)