模式识别实验一
实验一、协方差矩阵和矩阵特征值、特征向量的计算
题目简介:给定一组数据,实现该组数据的协方差矩阵的计算。并用代码实现计算一个方阵的特征值和特征向量。
一、协方差部分
1.协方差的定义
协方差在概率论和统计学中用于衡量两个变量的总体误差。而方差是协方差的一种特殊情况,即当两个变量是相同的情况。 协方差表示的是两个变量的总体的误差,这与只表示一个变量误差的方差不同。
协方差计算公式的定义:
由一组数据
X
=
(
X
1
,
X
2
,
…
,
X
n
)
T
\mathbf{X}=\left(X_{1}, X_{2}, \ldots, X_{n}\right)^{T}
X=(X1,X2,…,Xn)T,其中该数据表示X中有n个变量。其中每两个变量之间就可以计算一次协方差。任意两个向量之间的协方差计算公式
cov
[
X
i
,
X
j
]
=
E
[
(
X
i
−
E
[
X
i
]
)
(
X
j
−
E
[
X
j
]
)
]
\operatorname{cov}\left[X_{i}, X_{j}\right]=E\left[\left(X_{i}-E\left[X_{i}\right]\right)\left(X_{j}-E\left[X_{j}\right]\right)\right]
cov[Xi,Xj]=E[(Xi−E[Xi])(Xj−E[Xj])]
2.协方差矩阵
协方差矩阵是指在X这个数据集当中,任意两个变量之间就可以计算一次协方差,X共有n维数据,所以就可以计算出nn个协方差数据,因为每一维度的数据都可以和自身进行一次协方差的计算。所以共有nn个数据。将这些协方差数据用矩阵的形式表示就是协方差矩阵
cov
[
X
,
X
]
=
[
cov
[
X
1
,
X
1
]
cov
[
X
1
,
X
2
]
⋯
cov
[
X
1
,
X
n
]
cov
[
X
2
,
X
1
]
cov
[
X
2
,
X
2
]
⋯
cov
[
X
2
,
X
n
]
⋮
⋮
⋱
⋮
cov
[
X
n
,
X
1
]
cov
[
X
n
,
X
2
]
⋯
cov
[
X
n
,
X
n
]
]
\operatorname{cov}[\mathbf{X}, \mathbf{X}]=\left[\begin{array}{cccc} \operatorname{cov}\left[X_{1}, X_{1}\right] & \operatorname{cov}\left[X_{1}, X_{2}\right] & \cdots & \operatorname{cov}\left[X_{1}, X_{n}\right] \\ \operatorname{cov}\left[X_{2}, X_{1}\right] & \operatorname{cov}\left[X_{2}, X_{2}\right] & \cdots & \operatorname{cov}\left[X_{2}, X_{n}\right] \\ \vdots & \vdots & \ddots & \vdots \\ \operatorname{cov}\left[X_{n}, X_{1}\right] & \operatorname{cov}\left[X_{n}, X_{2}\right] & \cdots & \operatorname{cov}\left[X_{n}, X_{n}\right] \end{array}\right]
cov[X,X]=⎣⎢⎢⎢⎡cov[X1,X1]cov[X2,X1]⋮cov[Xn,X1]cov[X1,X2]cov[X2,X2]⋮cov[Xn,X2]⋯⋯⋱⋯cov[X1,Xn]cov[X2,Xn]⋮cov[Xn,Xn]⎦⎥⎥⎥⎤
3.个人理解
- 协方差表示两个随机变量之间的线性相关性
- 协方差矩阵中的每个元素代表了两个随机变量之间的协方差
- 协方差矩阵表示一组随机变量之间的两两线性相关性
4.特征值和特征向量
4.代码实现
4.1 包的导入
这里导入numpy和random包,用来处理数据和随机数的生成
import numpy as np
import random
4.2 类的封装和函数实现
将所有要实现的功能函数进行封装,封装在一个work类下面。
函数介绍:
- 函数cov用来计算两向量之间的协方差
- 函数ccovmat(self)用来计算样本的协方差矩阵
- 函数feature(self)用来计算矩阵样本的特征值和特征向量
class work(object):
def __init__(self, samples):
self.samples = samples
self.covmat1 = []
self.ccovmat()
def cov(self, X, Y):
n = np.shape(X)[0] #特诊个数
X, Y = np.array(X), np.array(Y)
meanX, meanY = np.mean(X), np.mean(Y)
cov = sum(np.multiply(X - meanX, Y - meanY)) / (n - 1)
return cov
def ccovmat(self):
S = self.samples # 样本集
na = np.shape(S)[1] # 特征attr总数
self.covmat1 = np.full((na, na), fill_value=0.)
for i in range(na):
for j in range(na):
self.covmat1[i, j] = self.cov(S[:, i], S[:, j])
return self.covmat1
def feature(self):
b,c = np.linalg.eig(samples)
return b,c
4.3 数据集生成和函数调用
这里调用random模块生成随机数,生成一个4*4的方阵。数据集的维度可以选择,因为要计算特征值需要固定类型为方阵。计算协方差并不需要为方阵,这里为了方便使用同一个数据集,原理是一样的。
if __name__ == '__main__':
image= [[random.randint(1, 20) for j in range(1, 5)] for i in range(1, 5)]
samples = np.array(image)
print(image)
print('数据集\n', samples)
wk = work(samples)
b,c = wk.feature()
print("矩阵特征值为: {}".format(b))
print("矩阵特征向量为: \n{}".format(c))
print('协方差矩阵:\n', wk.ccovmat())
5. 运行结果
实验二、K-means聚类算法实现
K-means算法是机器学习中常见的无监督学习算法,K-means算法是最常用的一种聚类算法。算法的输入为一个样本集(或者称为点集),通过该算法可以将样本进行聚类,具有相似特征的样本聚为一类。
针对每个点,计算这个点距离所有中心点最近的那个中心点,然后将这个点归为这个中心点代表的簇。一次迭代结束之后,针对每个簇类,重新计算中心点,然后针对每个点,重新寻找距离自己最近的中心点。如此循环,直到前后两次迭代的簇类没有变化。
本次实验将实现K-means聚类算法,基于python语言实现。
2.1 算法理论部分
step1、K值的选择
k 的选择一般是按照实际需求进行决定,或在实现算法时直接给定 k 值。
说明:
- A.质心数量由用户给出,记为k,k-means最终得到的簇数量也是k
- B.后来每次更新的质心的个数都和初始k值相等
- C.k-means最后聚类的簇个数和用户指定的质心个数相等,一个质心对应一个簇,每个样本只聚类到一个簇里面
- D.初始簇为空
step2、距离度量
将对象点分到距离聚类中心最近的那个簇中需要最近邻的度量策略,在欧式空间中采用的是欧式距离,在处理文档中采用的是余弦相似度函数,有时候也采用曼哈顿距离作为度量,不同的情况实用的度量公式是不同的。这里使用欧氏空间的距离
step3、新质心的计算
对于分类后的产生的k个簇,分别计算到簇内其他点距离均值最小的点作为质心(对于拥有坐标的簇可以计算每个簇坐标的均值作为质心)
step4、是否停止K-means
质心不再改变,或给定loop最大次数loopLimit
说明:
- A当每个簇的质心,不再改变时就可以停止k-menas
- B.当loop次数超过looLimit时,停止k-means
- C.只需要满足两者的其中一个条件,就可以停止k-means
- C.如果Step4没有结束k-means,就再执行step2-step3-step4
- D.如果Step4结束了k-means,则就打印(或绘制)簇以及质心
2.2 代码实现
由于sklearn中内置了K均值聚类算法的函数和模块,这里将使用两种方法实现本次实验。方法一为聚类原理自己理解并实现,方案二为调用现有模块和API
方案一代码
这部分代码基于原理实现K均值聚类算法,计算距离,寻找新的质心……
import random
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# 计算欧拉距离
def calcDis(dataSet, centroids, k):
clalist=[]
for data in dataSet:
diff = np.tile(data, (k, 1)) - centroids
squaredDiff = diff ** 2 #平方
squaredDist = np.sum(squaredDiff, axis=1) #和 (axis=1表示行)
distance = squaredDist ** 0.5 #开根号
clalist.append(distance)
clalist = np.array(clalist) #返回一个每个点到质点的距离len(dateSet)*k的数组
return clalist
# 计算质心
def classify(dataSet, centroids, k):
# 计算样本到质心的距离
clalist = calcDis(dataSet, centroids, k)
# 分组并计算新的质心
minDistIndices = np.argmin(clalist, axis=1) #axis=1 表示求出每行的最小值的下标
newCentroids = pd.DataFrame(dataSet).groupby(minDistIndices).mean() #DataFramte(dataSet)对DataSet分组,groupby(min)按照min进行统计分类,mean()对分类结果求均值
newCentroids = newCentroids.values
# 计算变化量
changed = newCentroids - centroids
return changed, newCentroids
# 使用k-means分类
def kmeans(dataSet, k):
centroids = random.sample(dataSet, k)
changed, newCentroids = classify(dataSet, centroids, k)
while np.any(changed != 0):
changed, newCentroids = classify(dataSet, newCentroids, k)
centroids = sorted(newCentroids.tolist()) #tolist()将矩阵转换成列表 sorted()排序
# 根据质心计算每个集群
cluster = []
clalist = calcDis(dataSet, centroids, k) #调用欧拉距离
minDistIndices = np.argmin(clalist, axis=1)
for i in range(k):
cluster.append([])
for i, j in enumerate(minDistIndices): #enymerate()可同时遍历索引和遍历元素
cluster[j].append(dataSet[i])
return centroids, cluster
#函数主体部分
if __name__ == '__main__':
dataset = [[1,1],[1.2,1.2],[1,1.6],[1,2],[2,2],[2,1],[6.5,6],[7.7,7],[6,7],[7,6],[6.6,7],[7,6.6],[7,6.5],[7.2,6.9]]
centroids, cluster = kmeans(dataset, 2)
print('质心为:%s' % centroids)
print('集群为:%s' % cluster)
for j in range(len(centroids)):#用来画出最后的质心位置,用X表示
plt.scatter(centroids[j][0], centroids[j][1], marker='x', color='red', s=50, label='质心')
#用两种不同颜色的点来表示最后分成的两簇点
for i in range(len(cluster[0])):
plt.scatter(cluster[0][i][0], cluster[0][i][1], marker='o', color='green', s=40,label='原始点')
for i in range(len(cluster[1])):
plt.scatter(cluster[1][i][0], cluster[1][i][1], marker='o', color='blue', s=40,label='原始点')
plt.show()
运行结果
方案二代码
方案二主要是基于机器学习库当中内置的函数来实现K-means聚类算法。具体调用代码如下:
import matplotlib.pyplot as plt
import numpy as np
from sklearn.cluster import KMeans
from sklearn.datasets import load_iris
iris = load_iris()
X=np.array([[1,1],[1.2,1.2],[1,1.6],[1,2],[2,2],[2,1],[6.5,6],[7.7,7],[6,7],[7,6],[6.6,7],[7,6.6],[7,6.5],[7.2,6.9]])
plt.scatter(X[:, 0], X[:, 1], c = "red", marker='o', label='see')
plt.xlabel('petal length')
plt.ylabel('petal width')
plt.legend(loc=2)
plt.show()
estimator = KMeans(n_clusters=2)#构造聚类器
estimator.fit(X)#聚类
label_pred = estimator.labels_ #获取聚类标签
x0 = X[label_pred == 0]
x1 = X[label_pred == 1]
plt.scatter(x0[:, 0], x0[:, 1], c = "red", marker='o', label='label0')
plt.scatter(x1[:, 0], x1[:, 1], c = "green", marker='*', label='label1')
plt.xlabel('petal length')
plt.ylabel('petal width')
plt.legend(loc=2)
plt.show()
运行结果
- 原始点的图像:
- 聚类后点的图像