K-Means 是一种使用广泛的聚类的算法,将各种聚类子集内的所有数据样本的均值作为该聚类的代表点。
算法主要思想:通过迭代把数据集划分为不同的类别,使得评价聚类性能的准则函数达到最优,从而使得生成的每个聚类类内紧凑,类间独立。
由于每次都要计算所有的样本与每一个质心之间的距离,在大规模数据集上收敛速度较慢。
选定某种距离作为数据样本间的相似性度量
k-means聚类算法不适合处理离散型属性,对连续性属性比较适合
计算样本之间的距离时,根据实际需要选择距离测度作为算法的相似性度量,如欧式距离,曼哈顿距离(各维差的绝对值之和),马氏距离(两个服从同一分布并且其协方差矩阵为Σ的随机变量之间的差异程度)
选择评价聚类性能的准则函数
k-means聚类算法使用误差平方和准则函数来评价聚类性能。
cost⋅Function=∑i=1k∑xj∈Si(xj−ui)2 c o s t · F u n c t i o n = ∑ i = 1 k ∑ x j ∈ S i ( x j − u i ) 2
相似性的计算根据一个簇中对象的平均值(均值向量)来进行
Advantages:
导入数据
def loadDataSet(fileName):
dataMat = []
with open(fileName, 'r') as fp:
for line in fp.readlines():
curLine = line.strip().split('\t')
fltLine = [float(x) for x in curLine]
dataMat.append(fltLine)
return mat(dataMat)
生成k个点
def distEclud(vecA, vecB):
return sqrt(sum(power(vecA - vecB, 2)))
def randCent(dataSet, k):
# the dimension
n = shape(dataSet)[1]
# get the random center coordinates
centroids = mat(zeros((k, n)))
for j in range(n):
minJ = min(dataSet[:, j])
rangeJ = float(max(dataSet[:, j]) - minJ)
centroids[:,j] = minJ + rangeJ * random.rand(k, 1)
return centroids
k-means:
def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
m = shape(dataSet)[0]
clusterAssment = mat(zeros((m, 2)))
centroids = createCent(dataSet, k)
clusterChanged = True
while clusterChanged:
clusterChanged = False
# calcuate the distance
for i in range(m):
minDist = inf
minIndex = -1
for j in range(k):
distJI = distMeas(centroids[j, :], dataSet[i, :])
if distJI < minDist:
minDist = distJI
minIndex = j
if clusterAssment[i, 0] != minIndex:
# unable to be stable
clusterChanged = True
clusterAssment[i, :] = minIndex, minDist**2
# print(centroids)
# update center coordinates
for cent in range(k):
ptsInClust = dataSet[nonzero(clusterAssment[:, 0].A == cent)[0]]
centroids[cent, :] = mean(ptsInClust, axis=0)
return centroids, clusterAssment
画图
def draw(dataSet, centroids, clusterAssment):
m = shape(dataSet)[0]
for i in range(m):
plt.scatter(dataSet[i, 0], dataSet[i, 1], c=color(clusterAssment[i, 0]), )
plt.show()
主程序
def main():
dataSet = loadDataSet('places.txt')
centroids, clusterAssment = kMeans(dataSet, 4)
draw(dataSet, centroids, clusterAssment)
def biKmeans(dataSet, k, distMeas=distEclud):
# number of dataSet
m = shape(dataSet)[0]
clusterAssment = mat(zeros((m, 2)))
# the first of center coordinate
centroidOne = mean(dataSet, axis=0).tolist()[0]
centList = [centroidOne]
for j in range(m):
# SSE of the first center coordiante and every one of dataset
clusterAssment[j, 1] = distMeas(mat(centroidOne), dataSet[j, :])**2
while (len (centList) < k):
lowestSSE = inf
for i in range(len(centList)):
# 第 i 类的数据集
ptsInCurrClust = dataSet[nonzero(clusterAssment[:, 0].A == i)[0], :]
centroidMat, splitClustAss = kMeans(ptsInCurrClust, 2, distMeas)
# calculate the SSE
sseSplit = sum(splitClustAss[:, 1])
sseNotSplit = sum(clusterAssment[nonzero(clusterAssment[:, 0].A != i)[0], 1])
print("SSE of split : %d \nSSE of nosplit : %d" % (sseSplit, sseNotSplit))
if (sseSplit + sseNotSplit) < lowestSSE:
bestCentToSpit = i
bestNewCents = centroidMat
bestClustAss = splitClustAss.copy()
lowestSSE = sseSplit + sseNotSplit
# divide it
bestClustAss[nonzero(bestClustAss[:, 0].A == 1)[0], 0] = len(centList)
bestClustAss[nonzero(bestClustAss[:, 0].A == 0)[0], 0] = bestCentToSpit
print("The bestCentToSpit is %d " % (bestCentToSpit))
print("The len of bestClustAss is %d " % (len(centList)))
# update and add center coordinate
centList[bestCentToSpit] = bestNewCents[0, :]
centList.append(bestNewCents[1, :])
# change the class which is divided into 2 parts
clusterAssment[nonzero(clusterAssment[:, 0].A == bestCentToSpit)[0], :] = bestClustAss
return centList, clusterAssment
经过几次测试,发现不如不优化。这个是通过我每次去找一个当前最优解。
最近邻
设有c个类 ω1,ω2,...,ωc ω 1 , ω 2 , . . . , ω c ,每类有 Ni N i 个样本。
待测样本的到第i类的最近距离: gi(x)=min||x−xki|| (xki表示的是第i类第k个样本,k=1,...Ni) g i ( x ) = m i n | | x − x i k | | ( x i k 表 示 的 是 第 i 类 第 k 个 样 本 , k = 1 , . . . N i )
距离我们除了可以采用欧式距离和曼哈顿距离,还可以采用明考斯基距离。
名考夫斯基距离:
加权名考夫斯基距离:
决策规则
把待测样本加g(x)最小的那个类中。
k = 1时称为最近邻分类器。
给定测试样本,若其最近邻样本为z,则最近邻分类器出错的概率就是x与z类别标记不同的概率
最近邻分类器虽简单,但是他的泛化错误率不超过贝叶斯最优分类器的错误率的两倍。
低维嵌入
当训练样本的采集密度足够大,成为”密采样(dense sample)”,但随着维度的提升,样本数目明显不够,导致数据样本稀疏,距离计算困难等难题,即“维数灾难(curse of dimensionality)”。
缓解的一个重要途径就是降维(dimension reduction).人们观测数据样本虽是高维的,但是与学习任务密切相关的也许尽是某个低维分布。
典型的多维缩放(Multiple Dimensional Scaling , MDS):
假定m个样本在原始控件的距离矩阵为 D∈Rm×m,distij D ∈ R m × m , d i s t i j 表示 xi x i 到 xj x j 的距离,目标是获取d’维空间的表示 Z∈Rd′×m Z ∈ R d ′ × m , 且任意两个样本在d’维空间中的欧氏距离等于原始空间中的距离。
令 B=ZTZ∈Rm×m B = Z T Z ∈ R m × m , 其中B为降维后样本的内积矩阵,有 bij=zTizj b i j = z i T z j
我们将为后的样本Z做中心化处理,也就是说 ∑mi=1zi=0 ∑ i = 1 m z i = 0 ,易知
其中tr(B)表示B的迹,而且有 tr(B=∑mi=1||zi||2 t r ( B = ∑ i = 1 m | | z i | | 2 ,我们再令
所以我们有 bij=−12(dist2ij−dist2i⋅−dist2⋅j+dist2⋅⋅) b i j = − 1 2 ( d i s t i j 2 − d i s t i · 2 − d i s t · j 2 + d i s t · · 2 )
通过降维前后保持不变的距离矩阵D来求取内积矩阵B.
再对B做特征值分解(eigenvalue decomposition), B=VΛVT B = V Λ V T , 其中 Λ=diag(λ1,...,λd),(λ1≥λ2≥...≥λd) Λ = d i a g ( λ 1 , . . . , λ d ) , ( λ 1 ≥ λ 2 ≥ . . . ≥ λ d ) 为特征值构成的对角矩阵.假定其中有d*个非零特征向值,他们构成了 Λ∗=diag(λ1,lambda2,...,λd∗),令V∗ Λ ∗ = d i a g ( λ 1 , l a m b d a 2 , . . . , λ d ∗ ) , 令 V ∗ 为相应的特征向量矩阵,则Z可表达为 Z=Λ1/2∗VT∗ Z = Λ ∗ 1 / 2 V ∗ T
现实中为了有效降维,往往仅需降维后的距离与原始空间中的距离尽可能的接近。
MDS算法的过程:
Advantages:
KNN不仅可以用来分类,也可以用来regression。
对于类域交叉或重叠较多的待测样本集来说,非常适合。
Disadvantages:
当样本不平衡时,会导致待测样本偏向于样本容量较大的类。
计算量大(可除去对分类作用不大的样本)。
不适合样本容量较小的类域,容易采用误分。
Others:
k值的减小会使近似误差(approximation error)减小,但估计误差(estimation error)会增大,意味着整体模型变得复杂,容易发生过拟合。
k值的增大可以减小估计误差,但增大了近似误差。这是与输入实例较远的(不相似)训练实例也会起预测作用,是预测发生错误,同样也使得模型变的简单。
k=N不可取。k一般取一个较小的数值,采用交叉验证法来选取最优的k值。
参考
压缩kNN
定义两个存储器,一个存放生成的样本集称output样本集,和original样本集。
初始化:output为空,original为原样本集,从original选择一个移到output中。
在original样本集中选择第i个样本,并使用output样本集中的样本对其进行knn,若分类错误,则将该样本移动到output样本中,若正确不做处理
直至遍历完original所有的样本。output也就是压缩后的样本集。
from numpy import *
def distEclud(vecA, vecB):
return sqrt(sum(power(vecA - vecB, 2)))
def classfiy0(inX, dataSet, labels, k):
m = shape(dataSet)[0]
cnt = zeros(m)
dis = []
for i in range(m):
tmp = distEclud(inX, dataSet[i, :])
dis.append((tmp, labels[i]))
dis.sort()
print(dis)
for i in range(k):
cnt[dis[i][1]] += 1
id = -1
mix = 0
for i in range(m):
if(mix < cnt[i]):
mix = cnt[i]
id = i
return id
待补
线性方法:对特征做线性组合,将高维数据投影到低维空间。
目的:寻找在最小均方意义下最能够代表原始数据的投影方法
我们需要寻找一个满足最近重构性和最大可分性的超平面。
最近重构性:样本点到这个超平面的距离都足够近
最大可分性:样本点到这个超平面上的投影能尽可能的分开
最近重构性
假定数据样本进行了中心化,即 ∑xi=0 ∑ x i = 0 ,再假定投影变换后得到的新坐标系为 w1,w2,...,wd w 1 , w 2 , . . . , w d , 且为规范正交基。若丢弃一部分坐标,维度降低到d’ < d,则样本点 xi x i 在低维坐标系下中的投影是 zi=(zi1;zi2;...;zid′) z i = ( z i 1 ; z i 2 ; . . . ; z i d ′ ) , 其中 zij=wTjxi z i j = w j T x i 是 xi x i 在低维下第j维的坐标。若基于 zi z i 来重构 xi x i ,则有 xi=∑d′j=1zijwj x i = ∑ j = 1 d ′ z i j w j , 那么整个训练集中,样本原点与基于投影重构的样本点 xi x i 之间的距离为:
这就是主成分分析的优化目标。
使用拉格朗日乘子法得
参考
降维后低维控件的维数d’通常是事先指定的,或通过在d’值不同的地位控件对knn(或其他开销较小的学习器)进行交叉验证来选取较好的d’值。对PCA还可以从重构的角度设置一个重构阈值,例如t = 95%, 然后选取满足不等式最小的d’
PCA是一种无参数技术,也就是说面对同样的数据,如果不考虑清洗,谁来做结果都一样,没有主观参数的介入,所以PCA便于通用实现,但是本身无法个性化的优化。
输入m条n列的数据,低维空间维数d’
输出投影矩阵 W∗=(w1,w2,..,wd′) W ∗ = ( w 1 , w 2 , . . , w d ′ )
PCA实现,输入m条n列(维)数据和d’,输出的是降维后的坐标,降维恢复后的坐标,以及投影矩阵
def pca(dataSet, topFeat=1627406066):
"""
return the data in low Dimensions and the recover data from that
"""
meanVals = mean(dataSet, axis=0)
meanRemoved = dataSet - meanVals
covMat = cov(meanRemoved, rowvar=False)
eigVals, eigVects = linalg.eig(mat(covMat))
eigValIndex = argsort(eigVals)
eigValIndex = eigValIndex[:-(topFeat+1):-1]
sortedEigVects = eigVects[:,eigValIndex]
lowData = meanRemoved * sortedEigVects
reData = (lowData * sortedEigVects.T) + meanVals
return lowData, reData, sortedEigVects
思想:给定训练集,设法将样例投影到一条直线上,是的同类样例的投影点尽可能的接近、异类样例的投影点尽量远离,在对新样本进行分类时,将其投影到同样的这条直线上,在根据投影点到为止来确定样本的类别。
这个与PCA的想法相似但又截然不同。
给定数据集 D={(xi,yi)},yi∈{0,1} D = { ( x i , y i ) } , y i ∈ { 0 , 1 } . 令 Xi,μi,∑i X i , μ i , ∑ i 分别表示第i类示例的集合、均值向量、协方差矩阵。若将数据投影到直线w上,则两类样本的中心在直线上的投影分别为 wTμ0和wTμ1 w T μ 0 和 w T μ 1 , 若将数据投影到直线w上。
欲使同类样例的投影点尽可能的接近,让同类的协方差尽可能的小,即 wT∑0w+wT∑1w w T ∑ 0 w + w T ∑ 1 w , 而欲使异类样例的投影点尽可能原理,即 ||wTμ0−wTμ1||22 | | w T μ 0 − w T μ 1 | | 2 2 尽可能大, 最大化的目标:
Sw=∑0+∑1 S w = ∑ 0 + ∑ 1
类间散度矩阵(between-class scatter matrix):
这就是我们要最大化的目标,即 Sb和Sw S b 和 S w 的广义瑞利商(generalized Rayleigh quotient).
不失一般性,我们令分母为1,那么就有
拉格朗日乘子得:
Sbw=λ(μ0−μ1) S b w = λ ( μ 0 − μ 1 ) 带回去就有
考虑数值解的稳定性,通常将 Sw S w 进行奇异值分解(SVD),即 Sw=U∑VT,S−1w=V∑−1UT S w = U ∑ V T , S w − 1 = V ∑ − 1 U T
当两类数据同先验,满足高斯分布且协方差相等时,LDA可达到最优分类
推广:
存在N个类,且第i个类示例数为mi,定义全局散度矩阵
其中 W∈Rd×(N−1) W ∈ R d × ( N − 1 )
上式同理可以转化为 SbW=λSwW S b W = λ S w W
W的闭式解则是 S−1wSb S w − 1 S b 的d’个最大非零广义特征值所对应的特征向量所组成的矩阵, d′≤N−1 d ′ ≤ N − 1
W投影到d‘维空间,LDA也被视为一种经典的监督降维技术。
输入m条n列的数据,
输出投影矩阵 W∗=(w1,w2,..,wd′) W ∗ = ( w 1 , w 2 , . . , w d ′ )
def lda(dataSet, label, topFeat=1627406066):
"""
return the data in low Dimensions and the recover data from that
"""
m, n = shape(dataSet)
d = int(max(label)) + 1
classifedData = [[] for i in range(d)]
labelAverage = []
for i in range(m):
if(int(label[i]) < len(classifedData)):
classifedData[int(label[i])].append(dataSet[i])
else :
print("Error label : " + label[i])
withinScatterMat = zeros((n, n))
for i in range(d):
labelAverage.append(mean(classifedData[i], axis=0))
tmp = mat(classifedData[i] - labelAverage[i])
withinScatterMat = withinScatterMat + tmp.T * tmp
meanVals = mean(dataSet, axis=0)
betweenScatterMat = zeros((n, n))
for i in range(d):
tmp = mat(labelAverage[i] - meanVals)
betweenScatterMat += tmp.T * tmp
ScatterMat = (withinScatterMat ** (-1)) * betweenScatterMat
eigVals, eigVects = linalg.eig(mat(ScatterMat))
print(eigVals)
eigValIndex = argsort(eigVals)
eigValIndex = [i for i in eigValIndex[:-(topFeat+1):-1] if(i > 0)]
sortedEigVects = eigVects[:,eigValIndex]
loadDataSet = dataSet * sortedEigVects
reData = loadDataSet * sortedEigVects.T
return loadDataSet, reData, sortedEigVects