您的位置:首页 > 编程语言 > Python开发

Python——KMeans(k均值聚类)实战(附详细代码与注解)

2020-01-13 03:09 405 查看

开始之前

各位朋友周末好,今天博主小码将开车≥Ö‿Ö≤为大家用代码实战讲解KMeans聚类,请大家坐稳了≡(▔﹏▔)≡。作为机器学习的十大经典算法之一,聚类的相关现实应用非常之广,如图像分割,文本分类、市场分割等,相关的干货知识请查阅相关资料,接下来小码直接带大家撸代码了。

前提准备

为了让各位乘客有良好的乘车体验,请准备如下:
Jupyter notebook
火狐浏览器或谷歌浏览器
win7或win10电脑一台
具备图像处理的知识如numpy,opencv等

啦啦啦!当然还有不怕吃苦的钻研精神。≧◠◡◠≦✌

需求分析

对我国某视频APP的小镇中国区潜在会员数分布用KMeans进行聚类,通过聚类结果知道会员的分布情况。先给大家看下最后的效果图:

本代码所需的图片我将通过网盘给大家
一、读取中国地图

import numpy as np
import cv2
import matplotlib.pyplot as plt
img_china = cv2.imread('../china.png')  #读取中国地图图片
plt.imshow(img_china,cmap="gray")   #对图像进行处理,并显示其格式
plt.xticks([]),plt.yticks([])      #去除x、y轴
plt.show()

【注】plt.imshow()函数负责对图像进行处理,并显示其格式,但是不能显示,其后跟着plt.show()才能显示出来。
效果:

二、特征提取过程
【注】用画图软件加入黑色散点(潜在会员数)(这个不需要大家做,已经用ps做好,保存在china_people.png),另存为china_people.png。

import numpy as np
import cv2
import matplotlib.pyplot as plt
img_china = cv2.imread('../china2thresh.png')    #读取上一步已经处理好的图片,这里无需看第一步的代码,第一步只是告诉如何处理图片,这里china2thresh.png就是第一步处理完成用于第二步的图片
img_china_people = cv2.imread('../china_people.png')  #读取用ps处理的会员分布散点图
img_people = img_china - img_china_people  #此处用到图像相减,两幅图像间对应像素的灰度值相减,用于目标检测
r_img_china = cv2.split(img_china)[0]     #图像颜色通道的分离,看不懂没关系,毕竟这不是重点
r_img_people = cv2.split(img_people)[2]
cv2.imwrite('../people.png',r_img_people)  #把处理后的img_people,存入people.png
print("中国大陆面积(像素数):",np.unique(r_img_china,return_counts = True)[1][1])   #获取中国地图像素大小
print("小镇中国区潜在会员数(像素数):",np.unique(r_img_people,return_counts = True)[1][1])  #获取小镇中国区潜在会员数像素大小
psw = []           #把三张图片img_china、img_china_people、img_people放入数组句柄中,当然我们最后需要的是img_people
psw.append(("china",img_china))
psw.append(("china + people",img_china_people))
psw.append(("people",img_people))
plot_number = len(psw)
cols = 3
rows = plot_number/cols + 1
for index in range(plot_number):
plt.subplot(rows,cols,index + 1)
plt.imshow(psw[index][1],cmap = "gray")
plt.title(psw[index][0],fontsize =15 )
plt.xticks([]),plt.yticks([])
plt.show()        #用for循环按要求打印出如下效果

【注】subplot(n,m,x)解释:建立一个mn的绘图区域,然后分别在其x=1,2,3,4,…区域绘制图像*

三、从people图中提取样本作为KMeans的训练集

print(r_img_people.shape)     #打印出people图的像素范围
X = np.argwhere(r_img_people)
print(X.shape)     #小镇中国区潜在会员数分布,样本数为78739,为二维数组
X[0:10]     #打印出前10个样本所在像素点位置

效果:

四、训练KMeans模型(重点)

from sklearn.cluster import KMeans    #从sklearn导入KMeans算法
import copy
k = 72                      #指定簇的数目,KMeans聚类需要预先指定要聚成多少类
y_KMeans = KMeans(n_clusters=k, random_state=9)    #KMeans函数调用、传参
y_pred = y_KMeans.fit_predict(X)                   #预测样本分别属于哪些类
print(y_pred)

src_img_china = copy.deepcopy(img_china)
r_src_img_china = cv2.split(src_img_china)[0]
#根据索引赋值
for i in range(len(X)):
index = X[i]
src_img_china[index[0]][index[1]] = [y_pred[i]*(255/k), (255/3 + y_pred[i]*(255/k))%255, 255*2/3+(y_pred[i]*(255/k))]

【注】random_state的作用是控制随机状态,固定random_state为某个固定的值后,每次构建的模型都是相同的、生成的数据是相同的、每次拆分的结果也是相同的。可以把它理解为控制变量。
效果:

五、可视化结果

cv2.imwrite("../result.png",cv2.cvtColor(src_img_china , cv2.COLOR_RGB2BGR)) #颜色转换,了解即可
plt.imshow(src_img_china)
plt.xticks([]),plt.yticks([])
plt.show()
plt.scatter(X[:,1],X[:,0],c=y_pred)   #把预测的结果用散点图画出
plt.xticks([]),plt.yticks([])
ax = plt.gca()
ax.invert_yaxis()
plt.show()

效果:

六、输出结果

num_pix_china = np.unique(r_img_china, return_counts=True)[1][1]
print('中国大陆面积(像素数):', num_pix_china)
num_all_people = np.unique(r_img_people, return_counts=True)[1][1]
print('小镇中国区潜在会员数(像素数):', num_all_people)
print('每个簇的中心点坐标(像素坐标):', y_KMeans.cluster_centers_)
print('每个簇的潜在会员数(簇内像素数):',np.unique(y_pred, return_counts=True))

#每个簇内到簇中心距离列表:
# print(X.shape)
# print(y_pred.shape)
dic_result_xy = {}
dic_result = {}
for i in range(y_pred.shape[0]):
if y_pred[i] not in dic_result:
dic_result_xy[y_pred[i]]=[]
dic_result[y_pred[i]]=[]
dic_result_xy[y_pred[i]].append(X[i])
#     print('jjjjjjjjj',i,X[i][0]-y_KMeans.cluster_centers_[y_pred[i]][0])
#     print('kkkkkkkkk',i,X[i][1]-y_KMeans.cluster_centers_[y_pred[i]][1])
dic_result[y_pred[i]].append( np.sqrt( np.square(X[i][0]-y_KMeans.cluster_centers_[y_pred[i]][0])\
+ np.square(X[i][1]-y_KMeans.cluster_centers_[y_pred[i]][1])))
if i%5000 == 0:
print('计算到第{}个循环'.format(i))
# print(dic_result_xy[0][:10])
# print(dic_result[0])

#每个簇内到簇中心平均距离:
tmp = []
for i in range(k):
tmp.append(np.mean(dic_result[i]))
print('每个簇簇内所有点到簇中心平均距离(像素距离):')
print(tmp)

tmp = []
for i in range(k):
tmp.append(np.median(dic_result[i]))
print('每个簇簇内所有点到簇中心中位数距离(像素距离):')
print(tmp)

tmp = []
for i in range(k):
tmp.append(max(dic_result[i]))
print('每个簇簇内所有点到簇中心最远距离(像素距离):')
print(tmp)
print(np.mean(tmp))

actual_china = 9083674#纯陆地9,083,674平方千米
pix2km = np.sqrt(actual_china/num_pix_china)
print('最终:', np.mean(tmp)*pix2km,'km')

由于打印结果比较多,小码就不把打印结果截图贴上来了,还请大家动手实践,有问题可找我交流,谢谢大家!
【附】:百度网盘图片链接
提取码:4wc4

  • 点赞 16
  • 收藏
  • 分享
  • 文章举报
Mr. Luoj 发布了33 篇原创文章 · 获赞 192 · 访问量 4381 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: