聚类算法
聚类和分类有什么区别?
分类数据样本是带有标签的,是知道它是哪一个类别的,是有监督的。
但聚类没有标签,是无监督的。
寻找优质用户
在银行客户的数据集之中寻找20%的优质客户,将他们归为一个类别。
社区发现
将联系比较多的几个人聚成一个类别,就可以认为这几个人是一个社群。说明这几个人关系会比较好一点。
异常点监控
将银行后台数据归类为两个类别,比如正常一个类别,不正常一个类别。大多数情况下,数据都会归为正常类别的那一个类,但是党羽带异常点的时候,就会归为另一个类别,所以这个点可能是信用卡诈骗或者是黑客攻击。
K-MEANS
选取两个点作为聚类的中心点(随机)
计算个个点之间的距离。G0代表类别规划,在一个类中,为0表示不是这个类,为1表示是这个类。
将第二类的所有x,y求平均值,找中心点。
中心带确定之后再次计算距离,重新聚类,如此迭代,直到聚类不再发生变化,迭代停止。
类别迭代过程图:相同类别之间数据特征相隔比较近,不同的相隔较远。
python实现k-means算法实战
import numpy as np
import matplotlib.pyplot as plt
data = np.genfromtxt("kmeans.txt",delimiter=" ")
plt.scatter(data[:,0],data[:,1])
# plt.show()
# print(data.shape)#(80,2)
#计算距离
def euclDistance(vector1,vector2):
return np.sqrt(sum((vector2-vector1)**2))
#初始化质心
def initCentroids(data,k):
numSamples,dim = data.shape
#k个质心,列数和样本列数相同
centroids = np.zeros((k,dim))
#随机选出k个质点
for i in range(k):
index = int(np.random.uniform(0,numSamples))#uniform浮点数
#作为初始化质心
centroids[i,:] = data[index,:]
return centroids
#传入数据集和k的值
def kmeans(data,k) :
#计算样本个数,0代表行,1代表列
numSamples = data.shape[0]
#样本的属性,第一列保存该样本的属于哪个簇,第二列保存该样本与它所属样本的误差值。
clusterData = np.array(np.zeros((numSamples,2)))#把它初始化为0
#决定质心是否要改变的变量
clusterChanged = True
#初始化质心
centroids = initCentroids(data,k)
while clusterChanged:
clusterChanged = False
#循环每一个样本
for i in range(numSamples):
#最小距离
minDist = 100000.0
#定义样本所属簇,默认为0
minIndex = 0
#循环计算每一个质心与该样本的距离
for j in range(k):
distance = euclDistance(centroids[j,:],data[i,:])
#如果计算距离小于最小距离,则更新最小距离
if distance < minDist :
minDist = distance
#更新最小距离
clusterData[i,1] = minDist
#更新样本所属的簇
minIndex = j
#如果样本所属的簇发生了变化
if clusterData[i,0] != minIndex:
#质心要重新计算
clusterChanged = True
#更新样本所属簇
clusterData[i,0] = minIndex
#更新质心
for j in range(k):
#获取第j个簇所有样本所在的索引
#nonzero输出矩阵中不为0的数的坐标,分两行,一行保存所有行坐标,一行保存所有列坐标,
cluster_index = np.nonzero(clusterData[:,0] == j)
#第j个簇所有的样本点
pointsInCluster = data[cluster_index]
#计算质心
#mean求取平均值
# axis 不设置值,对 m*n 个数求均值,返回一个实数
# axis = 0:压缩行,对各列求均值,返回 1* n 矩阵
# axis =1 :压缩列,对各行求均值,返回 m *1 矩阵
centroids[j,:] = np.mean(pointsInCluster,axis=0)
# showCluster(data,k,centroids,cluster_index)
return centroids,clusterData
#显示结果
def showCluster(data,k,centroids,clusterData):
numSamples,dim = data.shape
if dim != 2:
print("dimension of your data is not 2!")
return 1
# 用不同的颜色来表示各个类别
mark = ['or', 'ob', 'og', 'ok', '^r', '+r', 'sr', 'dr', '<r', 'pr']
if k > len(mark) :
print("Your k is too large!")
return 1
#画样本点
for i in range(numSamples):
markIndex = int(clusterData[i,0])
plt.plot(data[i,0],data[i,1],mark[markIndex])
# 用不同的颜色来表示各个类别
mark = ['*r', '*b', '*g', '*k', '^b', '+b', 'sb', 'db', '<b', 'pb']
#画质心点
for i in range(k) :
plt.plot(centroids[i,0],centroids[i,1],mark[i],markersize = 20)
plt.show()
#设置k值
k = 4
#centroids 簇的中心点
#cluster Data样本属性,第一列保存该样本属哪个簇,第二列保存该样本跟他所属簇的误差
centroids,clusterData = kmeans(data,k)
if np.isnan(centroids).any():#只要里面存在任何的空值,那么就为true
print('Error')
else:
print('cluster complete!')
#显示结果
showCluster(data,k,centroids,clusterData)
print(centroids)
画出簇的作用域:
#做预测
x_test = [0,1]
np.tile(x_test,(k,1))#行复制k次,列复制1次
#误差
np.tile(x_test,(k,1))-centroids
#误差平方
(np.tile(x_test,(k,1))-centroids)**2
#误差平方和
((np.tile(x_test,(k,1))-centroids)**2).sum(axis=1)
#最小值所在索引号
np.argmin(((np.tile(x_test,(k,1))-centroids)**2).sum(axis=1))
def predict(datas):
return np.array([np.argmin(((np.tile(data,(k,1))-centroids)**2).sum(axis=1)) for data in datas])
#获取数据值所在范围
x_min,x_max = data[:,0].min() - 1,data[:,0].max() + 1
y_min,y_max = data[:,1].min() - 1,data[:,1].max() + 1
#生成网格矩阵
xx,yy = np.meshgrid(np.arange(x_min,x_max,0.02),
np.arange(y_min,y_max,0.02))
z = predict(np.c_[xx.ravel(),yy.ravel()])
#扁平化,得到一个一个的点
#ravel和flatten类似,多维数据转一维,flatten不会改变原始数据,而ravel会
z = z.reshape(xx.shape)
#等高线图
#在这里,只有两个高度,0和1
cs = plt.contourf(xx,yy,z)
showCluster(data,k,centroids,clusterData)
sklearn-K-Means算法:
from sklearn.cluster import KMeans
import numpy as np
import matplotlib.pyplot as plt
#载入数据
data = np.genfromtxt('kmeans.txt',delimiter=" ")
k = 4
model = KMeans(n_clusters=k)
model.fit(data)
#分类中心点坐标
centers = model.cluster_centers_
print(centers)
#预测结果
result = model.predict(data)
print(result)
print(model.labels_)
mark = ['or','ob','og','oy']
for i,d in enumerate(data):
plt.plot(d[0],d[1],mark[result[i]])
mark = ['*r','*b','*g','*y']
for i,center in enumerate(centers):
plt.plot(center[0],center[1],mark[i],markersize=20)
plt.show()
Sklearn-mini-Batch-K-Means
KMeans和Mini的结果对比:
数据量非常大的时候可以使用Mini,一般情况下直接使用K-Means就好。
K-Means存在的四个问题
算法分析1:
算法分析2:
算法分析3:
算法分析4:
数据比较大时,收敛会比较慢。
解决K-Means的算法分析1的问题:
算法优化1:两条竖线时取模的意思,内部可以为两个向量。
import numpy as np
import matplotlib.pyplot as plt
data = np.genfromtxt("kmeans.txt",delimiter=" ")
plt.scatter(data[:,0],data[:,1])
# plt.show()
# print(data.shape)#(80,2)
#计算距离
def euclDistance(vector1,vector2):
return np.sqrt(sum((vector2-vector1)**2))
#初始化质心
def initCentroids(data,k):
numSamples,dim = data.shape
#k个质心,列数和样本列数相同
centroids = np.zeros((k,dim))
#随机选出k个质点
for i in range(k):
index = int(np.random.uniform(0,numSamples))#uniform浮点数
#作为初始化质心
centroids[i,:] = data[index,:]
return centroids
#传入数据集和k的值
def kmeans(data,k) :
#计算样本个数,0代表行,1代表列
numSamples = data.shape[0]
#样本的属性,第一列保存该样本的属于哪个簇,第二列保存该样本与它所属样本的误差值。
clusterData = np.array(np.zeros((numSamples,2)))#把它初始化为0
#决定质心是否要改变的变量
clusterChanged = True
#初始化质心
centroids = initCentroids(data,k)
while clusterChanged:
clusterChanged = False
#循环每一个样本
for i in range(numSamples):
#最小距离
minDist = 100000.0
#定义样本所属簇,默认为0
minIndex = 0
#循环计算每一个质心与该样本的距离
for j in range(k):
distance = euclDistance(centroids[j,:],data[i,:])
#如果计算距离小于最小距离,则更新最小距离
if distance < minDist :
minDist = distance
#更新最小距离
clusterData[i,1] = minDist
#更新样本所属的簇
minIndex = j
#如果样本所属的簇发生了变化
if clusterData[i,0] != minIndex:
#质心要重新计算
clusterChanged = True
#更新样本所属簇
clusterData[i,0] = minIndex
#更新质心
for j in range(k):
#获取第j个簇所有样本所在的索引
#nonzero输出矩阵中不为0的数的坐标,分两行,一行保存所有行坐标,一行保存所有列坐标,
cluster_index = np.nonzero(clusterData[:,0] == j)
#第j个簇所有的样本点
pointsInCluster = data[cluster_index]
#计算质心
#mean求取平均值
# axis 不设置值,对 m*n 个数求均值,返回一个实数
# axis = 0:压缩行,对各列求均值,返回 1* n 矩阵
# axis =1 :压缩列,对各行求均值,返回 m *1 矩阵
centroids[j,:] = np.mean(pointsInCluster,axis=0)
# showCluster(data,k,centroids,cluster_index)
return centroids,clusterData
#显示结果
def showCluster(data,k,centroids,clusterData):
numSamples,dim = data.shape
if dim != 2:
print("dimension of your data is not 2!")
return 1
# 用不同的颜色来表示各个类别
mark = ['or', 'ob', 'og', 'ok', '^r', '+r', 'sr', 'dr', '<r', 'pr']
if k > len(mark) :
print("Your k is too large!")
return 1
#画样本点
for i in range(numSamples):
markIndex = int(clusterData[i,0])
plt.plot(data[i,0],data[i,1],mark[markIndex])
# 用不同的颜色来表示各个类别
mark = ['*r', '*b', '*g', '*k', '^b', '+b', 'sb', 'db', '<b', 'pb']
#画质心点
for i in range(k) :
plt.plot(centroids[i,0],centroids[i,1],mark[i],markersize = 20)
plt.show()
#设置k值
k = 4
min_loss = 10000
min_loss_centroids = np.array([])
min_loss_clusterData = np.array([])
for i in range(50):
#centroids为簇的中心点
#clusterData为样本的属性,第一列保存样本属于哪个簇,第二列保存该样本与它所属簇的距离误差
centroids,clusterData = kmeans(data,k)
loss = sum(clusterData[:,1])/data.shape[0]
if loss<min_loss:
min_loss = loss
min_loss_centroids = centroids
min_loss_clusterData = clusterData
print('cluster complete')
showCluster(data,k,min_loss_centroids,min_loss_clusterData)
运行时可能会出现这种情况,就是我们之前讲过的两个质心在变换过程中重合了,所以在最后四个结果中,会有空值,所以会出现警告。但它选取的时最小代价的那一个,所以不会影响最后结果。
解决K-Means的算法分析2的问题:
算法优化:
肘部法则:
当出现第二种情况,他的代价函数没有很明显的肘部,那么就要根据业务需求或自己的经验来进行判断了。
画肘部法则代价函数曲线:
list_loss = []
for k in range(2,10):
min_loss = 10000
min_loss_centroids = np.array([])
min_loss_clusterData = np.array([])
for i in range(50):
#centroids为簇的中心点
#clusterData为样本的属性,第一列保存样本属于哪个簇,第二列保存该样本与它所属簇的距离误差
centroids,clusterData = kmeans(data,k)
loss = sum(clusterData[:,1])/data.shape[0]
if loss<min_loss:
min_loss = loss
min_loss_centroids = centroids
min_loss_clusterData = clusterData
list_loss.append(min_loss)
# print('cluster complete')
# showCluster(data,k,min_loss_centroids,min_loss_clusterData)
plt.plot(range(2,10),list_loss)
plt.xlabel('k')
plt.ylabel('loss')
plt.show()
可视化K-Means
https://www.naftaliharris.com/blog/visualizing-k-means-clustering/
可视化聚类网站网址。
DBSCAN算法
基于密度的聚类算法。
DBSCAN的可视化:
epsilon是聚类领域的半径。实心的圈表示样本对象是核心对象,因为圈内对象个数大于minpoints。
可能会产生孤立地点。因为它大于设置领域的半径,导致不属于任何一个类别。
可以通过设置epsilon,minpoints的值来进行降噪。
实战DBSCAN
要注意调节半径和minpoints的值。
from sklearn.cluster import DBSCAN
import numpy as np
import matplotlib.pyplot as plt
data = np.genfromtxt('kmeans.txt',delimiter=" ")
model = DBSCAN(eps=1.5,min_samples=4)
model.fit(data)
result = model.fit_predict(data)
mark = ['or','ob','og','oy','ok','om']
for i,d in enumerate(data):
plt.plot(d[0],d[1],mark[result[i]])
plt.show()
可以认为粉红色是噪音值。
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
x1, y1 = datasets.make_circles(n_samples=2000, factor=0.5, noise=0.05)
x2, y2 = datasets.make_blobs(n_samples=1000, centers=[[1.2,1.2]], cluster_std=[[.1]])#数据标准差为0.1
x = np.concatenate((x1, x2))
plt.scatter(x[:, 0], x[:, 1], marker='o')
plt.show()
from sklearn.cluster import KMeans
y_pred = KMeans(n_clusters=3).fit_predict(x)
plt.scatter(x[:, 0], x[:, 1], c=y_pred)#颜色就用解惑来表示
plt.show()
from sklearn.cluster import DBSCAN
y_pred = DBSCAN().fit_predict(x)#如果不传参,会是一个默认参数eps=0.5,min_point = 5
plt.scatter(x[:, 0], x[:, 1], c=y_pred)
plt.show()
y_pred = DBSCAN(eps = 0.2).fit_predict(x)
plt.scatter(x[:, 0], x[:, 1], c=y_pred)
plt.show()
y_pred = DBSCAN(eps = 0.2, min_samples=50).fit_predict(x)
plt.scatter(x[:, 0], x[:, 1], c=y_pred)
plt.show()