文章目录
- 1.理解 K-Means 聚类
- 目标
- 理论
- T 恤尺码问题
- 它是如何工作的?
- 其他资源
- 2.OpenCV 中的 K-Means 聚类
- 目标
- 理解参数
- 输入参数
- 输出参数
1.理解 K-Means 聚类
目标
在本章中,我们将理解 K-Means 聚类的概念、其工作原理等。
理论
我们将通过一个常用的例子来处理这个问题。
T 恤尺码问题
假设一家公司即将向市场推出一款新的 T 恤。显然,他们必须生产不同尺寸的款式以满足各种身材的人。因此,该公司制作了人们的身高和体重数据,并将它们绘制在图表上,如下所示:
公司无法生产所有尺寸的 T 恤。相反,他们将人们分为小号、中号和大号,并只生产适合所有人的这 3 种型号。将人们分成三组可以通过 k 均值聚类来完成,算法会为我们提供最佳的 3 种尺寸,这将满足所有人的需求。如果不能,公司可以将人们分成更多组,可能是五组,依此类推。查看下图:
它是如何工作的?
该算法是一个迭代过程。我们将借助图像逐步解释它。
考虑如下一组数据(您可以将其视为 T 恤问题)。我们需要将此数据聚类为两组。
步骤:1 - 算法随机选择两个质心, C 1 C1 C1 和 C 2 C2 C2(有时,任何两个数据都被视为质心)。
步骤:2 - 计算每个点到两个质心的距离。如果测试数据更接近 C 1 C1 C1,则该数据标记为“0”。如果它更接近 C 2 C2 C2,则标记为“1”(如果有更多质心,则标记为“2”、“3”等)。
在我们的例子中,我们将所有“0”标记为红色,将“1”标记为蓝色。因此,经过上述操作后,我们得到以下图像。
步骤:3 - 接下来,我们分别计算所有蓝点和红点的平均值,这将是我们的新质心。也就是说, C 1 C1 C1 和 C 2 C2 C2 移至新计算的质心。(请记住,显示的图像不是真实值,也不是真实比例,仅用于演示)。
再次使用新质心执行步骤 2,并将数据标记为“0”和“1”。
因此,我们得到以下结果:
现在,步骤 2 和 步骤 3 迭代,直到两个质心都收敛到固定点。
(或者,根据我们提供的标准,例如最大迭代次数,或达到特定精度等,可能会停止。) 这些点使得测试数据和其对应质心之间的距离总和最小。或者简单地说,
C
1
↔
R
e
d
_
P
o
i
n
t
s
C1 \leftrightarrow Red\_Points
C1↔Red_Points 和
C
2
↔
B
l
u
e
_
P
o
i
n
t
s
C2 \leftrightarrow Blue\_Points
C2↔Blue_Points 之间的距离总和最小。
[ m i n i m i z e [ J = ∑ A l l R e d _ P o i n t s d i s t a n c e ( C 1 , R e d _ P o i n t ) + ∑ A l l B l u e _ P o i n t s d i s t a n c e ( C 2 , B l u e _ P o i n t ) ] ] [minimize \;\bigg[J = \sum_{All\: Red\_Points}distance(C1,Red\_Point) + \sum_{All\: Blue\_Points}distance(C2,Blue\_Point)\bigg]] [minimize[J=AllRed_Points∑distance(C1,Red_Point)+AllBlue_Points∑distance(C2,Blue_Point)]]
最终结果几乎如下所示:
因此,这只是对 K-Means 聚类的直观理解。有关更多详细信息和数学解释,请阅读任何标准机器学习教科书或查看其他资源中的链接。它只是 K-Means 聚类的顶层。对该算法有很多修改,例如如何选择初始质心,如何加快迭代过程等。
其他资源
- 机器学习课程,Andrew Ng 教授的视频讲座(部分图片取自此处)
2.OpenCV 中的 K-Means 聚类
目标
- 学习使用 OpenCV 中的 cv.kmeans() 函数进行数据聚类
理解参数
输入参数
- samples :它应该是 np.float32 数据类型,并且每个特征应该放在一个列中。
- nclusters(K) :结束时所需的聚类数
- criteria :它是迭代终止标准。当满足此标准时,算法迭代停止。实际上,它应该是 3 个参数的元组。它们是 `( type, max_iter, epsilon )`:
- 终止标准类型。它有 3 个标志,如下所示:
- cv.TERM_CRITERIA_EPS - 如果达到指定的准确度 epsilon,则停止算法迭代。
- cv.TERM_CRITERIA_MAX_ITER - 在指定的迭代次数 max_iter 后停止算法。
- cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER - 当满足上述任何条件时停止迭代。
- max_iter - 指定最大迭代次数的整数。
- epsilon - 所需准确度
- 终止标准类型。它有 3 个标志,如下所示:
- attempts:标志,用于指定使用不同初始标签执行算法的次数。算法返回产生最佳紧凑度的标签。此紧凑度作为输出返回。
- flags:此标志用于指定如何获取初始中心。通常,为此使用两个标志:cv.KMEANS_PP_CENTERS 和 cv.KMEANS_RANDOM_CENTERS。
输出参数
-
紧凑性:它是每个点到其相应中心的平方距离之和。
-
标签:这是标签数组(与上一篇文章中的“代码”相同),其中每个元素标记为“0”、“1”…
-
中心:这是聚类中心的数组。
现在我们将通过三个示例了解如何应用 K-Means 算法。
- 仅具有一个特征的数据
考虑一下,您有一组仅具有一个特征的数据,即一维数据。例如,我们可以以 T 恤问题为例,您仅使用人的身高来决定 T 恤的尺寸。
因此我们首先创建数据并在 Matplotlib 中绘制它
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
x = np.random.randint(25,100,25)
y = np.random.randint(175,255,25)
z = np.hstack((x,y))
z = z.reshape((50,1))
z = np.float32(z)
plt.hist(z,256,[0,256]),plt.show()
因此,我们有“z”,它是一个大小为 50 的数组,值范围从 0 到 255。我已将“z”重塑为列向量。当存在多个特征时,它将更有用。然后我制作了 np.float32 类型的数据。
我们得到以下图像:
现在我们应用 KMeans 函数。在此之前,我们需要指定标准。我的标准是这样的:每当运行 10 次算法迭代,或达到 epsilon = 1.0 的精度时,就停止算法并返回答案。
# Define criteria = ( type, max_iter = 10 , epsilon = 1.0 )
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 10, 1.0)
# Set flags (Just to avoid line break in the code)
flags = cv.KMEANS_RANDOM_CENTERS
# Apply KMeans
compactness,labels,centers = cv.kmeans(z,2,None,criteria,10,flags)
这给了我们紧凑性、标签和中心。在本例中,我得到的中心是 60 和 207。标签的大小与测试数据的大小相同,其中每个数据将根据其质心标记为“0”、“1”、“2”等。现在我们根据数据的标签将其拆分为不同的集群。
A = z[labels==0]
B = z[labels==1]
现在我们用红色绘制 A,用蓝色绘制 B,用黄色绘制它们的质心。
# Now plot 'A' in red, 'B' in blue, 'centers' in yellow
plt.hist(A,256,[0,256],color = 'r')
plt.hist(B,256,[0,256],color = 'b')
plt.hist(centers,32,[0,256],color = 'y')
plt.show()
下面是我们得到的输出:
- 具有多个特征的数据
在前面的例子中,我们只取 T 恤问题的身高。在这里,我们将取身高和体重,即两个特征。
记住,在前面的例子中,我们将数据制成单列向量。每个特征排列在一列中,而每行对应一个输入测试样本。
例如,在本例中,我们设置了一个大小为 50x2 的测试数据,其中包含 50 个人的身高和体重。第一列对应所有 50 个人的身高,第二列对应他们的体重。第一行包含两个元素,其中第一个元素是第一个人的身高,第二个元素是他的体重。同样,其余行对应其他人的身高和体重。
查看下图:
现在我直接转到代码:
import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
X = np.random.randint(25,50,(25,2))
Y = np.random.randint(60,85,(25,2))
Z = np.vstack((X,Y))
# convert to np.float32
Z = np.float32(Z)
# define criteria and apply kmeans()
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 10, 1.0)
ret,label,center=cv.kmeans(Z,2,None,criteria,10,cv.KMEANS_RANDOM_CENTERS)
# Now separate the data, Note the flatten()
A = Z[label.ravel()==0]
B = Z[label.ravel()==1]
# Plot the data
plt.scatter(A[:,0],A[:,1])
plt.scatter(B[:,0],B[:,1],c = 'r')
plt.scatter(center[:,0],center[:,1],s = 80,c = 'y', marker = 's')
plt.xlabel('Height'),plt.ylabel('Weight')
plt.show()
以下是我们得到的输出:
- 颜色量化
颜色量化是减少图像中颜色数量的过程。这样做的原因之一是为了减少内存。有时,某些设备可能存在限制,因此只能产生有限数量的颜色。在这些情况下,也会执行颜色量化。这里我们使用 k-means 聚类进行颜色量化。
这里没有什么新东西需要解释。有 3 个特征,例如 R、G、B。因此,我们需要将图像重塑为 Mx3 大小的数组(M 是图像中的像素数)。聚类之后,我们将质心值(也是 R、G、B)应用于所有像素,这样生成的图像将具有指定数量的颜色。我们再次需要将其重塑回原始图像的形状。
以下是代码:
import numpy as np
import cv2 as cv
img = cv.imread('home.jpg')
Z = img.reshape((-1,3))
# convert to np.float32
Z = np.float32(Z)
# define criteria, number of clusters(K) and apply kmeans()
criteria = (cv.TERM_CRITERIA_EPS + cv.TERM_CRITERIA_MAX_ITER, 10, 1.0)
K = 8
ret,label,center=cv.kmeans(Z,K,None,criteria,10,cv.KMEANS_RANDOM_CENTERS)
# Now convert back into uint8, and make original image
center = np.uint8(center)
res = center[label.flatten()]
res2 = res.reshape((img.shape))
cv.imshow('res2',res2)
cv.waitKey(0)
cv.destroyAllWindows()
当 K=8 时,结果如下: