模式识别的课程作业,要求实现一个k-means算法,并深入分析.不知道为什么老师讲的算法名字叫c-means.(课程报告仅供参考,避免全部一样)
kmeans算法又名k均值算法。其算法思想大致为:先从样本集中随机选取 k 个样本作为簇中心,并计算所有样本与这 k 个“簇中心”的距离,对于每一个样本,将其划分到与其距离最近的“簇中心”所在的簇中,对于新的簇计算各个簇的新的“簇中心”。
根据以上描述,可以得出算法的基本步骤:
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 = '*')
数据集: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
优点:简单,对簇型数据分类较好
缺点:
对离群点和孤立点敏感,可以通过离群点检测的LOF算法,通过去除离群点后再聚类,可以减少离群点和孤立点对于聚类效果的影响。
k值选取不确定,可以通过ISODATA算法改进.
初始聚类中心会影响最后的分类结果,可能会收敛于局部极小值,改进方法有k-means++ 或 二分K-means.
使用于球形簇类数据的聚类.根本原因在于距离度量的方式,原方法求距离的时候采用欧氏距离,因此是一个超球体.对于不规则的数据,往往基于密度的聚类算法更合适,比如DBSCAN算法.
k-means适用于连续数据.采用k-modes算法,可实现对离散数据的快速聚类.
数据量大的话,时间会很长.时间复杂度是O(nkl),l为迭代次数,n为数据集容量.在传统的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()