提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
聚类分析是一类经典的无监督学习算法,再给定样本的情况下,聚类分析通过度量特征相似度或者距离,将样本自动划分为若干类别。
为了衡量特征空间中的两个实例的相似度,可以用距离来描述。
闵可夫斯基距离Minkowski distance(闵氏距离),定义为:
给定 m 维向量样本集合 X ,对于任意 x i , x j ∈ X , x i = ( x 1 i , x 2 i , . . . , x m i ) T , x j = ( x 1 j , x 2 j , . . . , x m j ) T ,样本 x i 与样本 x j 之间的闵氏距离可定义为:给定 m 维向量样本集合 X ,对于任意 x i , x j ∈ X , x i = ( x 1 i , x 2 i , . . . , x m i ) T , x j = ( x 1 j , x 2 j , . . . , x m j ) T ,样本 x i 与样本 x j 之间的闵氏距离可定义为: 给定m维向量样本集合X,对于任意x_i,x_j∈X,x_i=(x_{1i},x_{2i},...,x_{mi})^T,x_j=(x_{1j},x_{2j},...,x_{mj})^T,样本x_i与样本x_j之间的闵氏距离可定义为:给定m维向量样本集合X,对于任意x_i,x_j∈X,x_i=(x_{1i},x_{2i},...,x_{mi})^T,x_j=(x_{1j},x_{2j},...,x_{mj})^T,样本x_i与样本x_j之间的闵氏距离可定义为: 给定m维向量样本集合X,对于任意xi,xj∈X,xi=(x1i,x2i,...,xmi)T,xj=(x1j,x2j,...,xmj)T,样本xi与样本xj之间的闵氏距离可定义为:给定m维向量样本集合X,对于任意xi,xj∈X,xi=(x1i,x2i,...,xmi)T,xj=(x1j,x2j,...,xmj)T,样本xi与样本xj之间的闵氏距离可定义为:
d i j = ∑ k = 1 m ∣ x k i − x k j ∣ p 1 p , p ≥ 1 d_{ij}=\sum_{k=1}^{m}{|x_{ki}-x_{kj}|^p}^\frac{1}{p},p≥1 dij=k=1∑m∣xki−xkj∣pp1,p≥1
当p=1时,闵氏距离称为曼哈顿距离(Manhatan distance):
d i j = ∑ k = 1 m ∣ x k i − x k j ∣ d_{ij}=\sum_{k=1}^{m}|x_{ki}-x_{kj}| dij=k=1∑m∣xki−xkj∣
当p=2时,闵氏距离称为欧氏距离(Euclidean distance):
d i j = ∑ k = 1 m ∣ x k i − x k j ∣ 2 1 2 d_{ij}=\sum_{k=1}^{m}{|x_{ki}-x_{kj}|^2}^\frac{1}{2} dij=k=1∑m∣xki−xkj∣221
当p=∞时,闵氏距离称为切比雪夫距离(Chebyshev distance):
d i j = m a x ∣ x k i − x k j ∣ d_{ij}=max|x_{ki}-x_{kj}| dij=max∣xki−xkj∣
马哈诺比斯距离(Mahalanobis distance),即马氏距离,是一种衡量各个特征之间相关性的距离度量方式。d当S为单位矩阵时,即样本各特征之间相互独立且方差为1时,马氏距离就是欧氏距离。
给定一个样本集合 X = ( x i j ) m × n ,其协方差矩阵为 S ,那么样本 x i 与样本 x j 之间的马氏距离定义为: 给定一个样本集合X=(x_{ij})_{m×n},其协方差矩阵为S,那么样本x_i与样本x_j之间的马氏距离定义为: 给定一个样本集合X=(xij)m×n,其协方差矩阵为S,那么样本xi与样本xj之间的马氏距离定义为:
d i j = [ ( x i − x j ) T S − 1 ( x i − x j ) ] 1 2 d_{ij}=[(x_i-x_j)^TS^{-1}(x_i-x_j)]^\frac{1}{2} dij=[(xi−xj)TS−1(xi−xj)]21
**相关系数(correlation coefficient)**是度量样本相似度最常用的方式。相关系数越接近1,表示两个样本越相似;相关系数越接近0,表示两个样本越不相似。样本 x i x_i xi与样本 x j x_j xj之间的相关系数定义为:
r i j = ∑ k = 1 m ( x k i − x i ‾ ) ( x k j − x j ‾ ) [ ∑ k = 1 m ( x k i − x j ‾ ) 2 ∑ k = 1 m ( x k j − x j ‾ ) 2 ] 1 2 r_{ij}=\frac{\sum_{k=1}^{m}{(x_{ki}}-\overline{x_i})(x_{kj}-\overline{x_j})}{[\sum_{k=1}^{m}{(x_{ki}-\overline{x_j})^2\sum_{k=1}^{m}{(x_{kj}-\overline{x_j})^2}}]^{\frac{1}{2}}} rij=[∑k=1m(xki−xj)2∑k=1m(xkj−xj)2]21∑k=1m(xki−xi)(xkj−xj)
**夹角余弦(angle cosine)**也是度量两个样本相似度的方式。夹角余弦越接近1,表示两个样本越相似;夹角余弦越接近0,表示两个样本越不相似。样本 x i x_i xi与样本 x j x_j xj之间的夹角余弦定义为;
A C i j = ∑ k = 1 m x k i x k j [ ∑ k = 1 m x k i 2 ∑ k = 1 m x k j 2 ] 1 2 AC_{ij}=\frac{\sum_{k=1}^{m}x_{ki}x_{kj}}{[\sum_{k=1}^{m}{x_{ki}}^2\sum_{k=1}^{m}{x_{kj}^2}]^{\frac{1}{2}}} ACij=[∑k=1mxki2∑k=1mxkj2]21∑k=1mxkixkj
给定 m × n m\times{n} m×n维度大小的看看样本集合 X = { x 1 , x 2 , . . . , x n } X=\{x_1,x_2,...,x_n\} X={x1,x2,...,xn}, k k k均值聚类是将 n n n个样本划分到 k k k个类别区域,通常 k < n k
假设使用欧式距离作为 k k k均值聚类算法的距离度量方式,则样本间的距离 d i j d_{ij} dij定义为:
d i j = ∑ k = 1 m ( x k i − x k j ) 2 = ∣ ∣ x i − x j ∣ ∣ 2 d_{ij}=\sum_{k=1}^{m}{(x_{ki-x_kj})^2}=||x_i-x_j||^2 dij=k=1∑m(xki−xkj)2=∣∣xi−xj∣∣2
定义样本与其所属类中心之间的距离总和为最终损失函数:
L ( C ) = ∑ i = 1 k ∑ C ( i ) = l ∣ ∣ x i − x l ‾ ∣ ∣ 2 L(C)=\sum_{i=1}^{k}\sum_{C(i)=l}{||x_i-\overline{x_l}||^2} L(C)=i=1∑kC(i)=l∑∣∣xi−xl∣∣2
其中, x ‾ = x ‾ 1 l , . . . , x ‾ m l \overline{x}={\overline{x}_{1l},...,\overline{x}_{ml}} x=x1l,...,xml为第 l l l个类的质心,即类的中心点; n l = ∑ i = 1 n I ( L ( i ) = l ) n_l=\sum_{i=1}^{n}{I(L(i)=l)} nl=∑i=1nI(L(i)=l)中的 ( L ( i ) = l ) (L(i)=l) (L(i)=l)表示指示函数,取值为1或0。函数 L ( C ) L(C) L(C)表示相同类中样本的相似度。所以 k k k均值聚类规约为一个优化问题进行求解:
C ∗ = a r g min C L ( C ) = a r g min C ∑ l = 1 k ∑ C ( i ) = l ∣ ∣ x i − x j ∣ ∣ 2 C^*=arg\min\limits_{C}L(C)=arg\min\limits_{C}\sum_{l=1}^{k}\sum_{C(i)=l}{||x_i-x_j||^2} C∗=argCminL(C)=argCminl=1∑kC(i)=l∑∣∣xi−xj∣∣2
根据前述过程,梳理 k k k均值聚类算法的主要流程:
k均值聚类代码编写思路
Numpy:
sklearn:
定义欧式距离
# 导入numpy库
import numpy as np
### 定义欧式距离
def euclidean_distance(x1, x2):
'''
输入:
x:向量x
y:向量y
输出:
np.sqrt(distance):欧式距离
'''
# 初始化距离
distance = 0
# 遍历并对距离的平方进行累加
for i in range(len(x1)):
distance += pow((x1[i] - x2[i]), 2)
return np.sqrt(distance)
质心初始化
### 定义质心初始化函数
def centroids_init(k, X):
'''
输入:
X:训练样本,Numpy数组
k:质心个数,也是聚类个数
输出:
centroids:质心矩阵
'''
# 样本数和特征数
m, n = X.shape
# 初始化质心矩阵,大小为质心个数×特征数
centroids = np.zeros((k, n))
# 遍历
for i in range(k):
# 每一次循环随机选择一个类别中心作为质心向量
centroid = X[np.random.choice(range(m))]
# 将质心向量分配给质心矩阵
centroids[i] = centroid
return centroids
根据质心和距离判断所属质心索引
### 定义样本的最近质心点所属的类别索引
def closest_centroid(sample, centroids):
'''
输入:
x:单个样本实例
centroids:质心矩阵
输出:
closest_i:
'''
# 初始化最近索引和最近距离
closest_i = 0
closest_dist = float('inf')
# 遍历质心矩阵
for i, centroid in enumerate(centroids):
# 计算欧式距离
distance = euclidean_distance(sample, centroid)
# 根据欧式距离判断并选择最近质心的索引
if distance < closest_dist:
closest_i = i
closest_dist = distance
return closest_i
为每个样本分配簇
### 分配样本与构建簇
def build_clusters(centroids, k, X):
'''
输入:
centroids:质心矩阵
k:质心个数,也是聚类个数
X:训练样本,Numpy数组
输出:
clusters:聚类簇
'''
# 初始化簇列表
clusters = [[] for _ in range(k)]
# 遍历训练样本
for x_i, x in enumerate(X):
# 获取样本所属最近质心的索引
centroid_i = closest_centroid(x, centroids)
# 将当前样本添加到所属类簇中
clusters[centroid_i].append(x_i)
return clusters
计算当前质心
# 计算质心
def calculate_centroids(clusters, k, X):
'''
输入:
clusters:上一步的聚类簇
k:质心个数,也就是聚类个数
X:训练样本,numpy数组
输出:
centroids:更新后的质心矩阵
'''
# 特征数
n = X.shape[1]
# 初始化质心矩阵,大小为质心个数×特征数
centroids = np.zeros((k, n))
# 遍历当前簇
for i, cluster in enumerate(clusters):
# 计算每个簇的均值作为新的质心
centroid = np.mean(X[cluster], axis=0)
# 将质心向量分配给质心矩阵
centroids[i] = centroid
return centroids
获取样本的所属聚类类别
# 获取每个样本所属的聚类类别
def get_cluster_labels(clusters, X):
'''
输入:
clusters:当前的聚集簇
X:训练样本,NUmpy数组
输出:
y_pred:预测类别
'''
# 预测结果初始化
y_pred = np.zeros(X.shape[0])
# 遍历聚类簇
for cluster_i, cluster in enumerate(clusters):
# 遍历当前簇
for X_i in cluster:
# 为每个样本分配类别簇
y_pred[X_i] = cluster_i
return y_pred
k均值聚类算法封装过程
### k均值聚类算法流程封装
def kmeans(X, k, max_iterations):
'''
输入:
X:训练样本,Numpy数组
k:质心个数(聚类个数)
max_iterations:最大迭代次数
输出:
预测类别列表
'''
# 1.初始化质心
centroids = centroids_init(k, X)
# 遍历迭代求解
for _ in range(max_iterations):
# 2.根据当前质心进行聚类
clusters = build_clusters(centroids, k, X)
# 保存当前质心
prev_centroids = centroids
# 3.根据聚类结果计算新的质心
centroids = calculate_centroids(clusters, k, X)
# 4.设定收敛条件为质心是否发生变化
diff = centroids - prev_centroids
if not diff.any():
break
# 返回最终的聚类标签
return get_cluster_labels(clusters, X)
基于Numpy的k均值聚类算法测试
# 创建测试数据
X = np.array([[0,2],[0,0],[1,0],[5,0],[5,2]])
# 设定聚类类别为2个,最大迭代次数为10次
labels = kmeans(X, 2, 10)
# 打印每个样本所属的类别标签
print(labels)
# 导入KMeans模块
from sklearn.cluster import Kmeans
# 创建k均值聚类实例并进行数据拟合
kmeans = KMeans(n_cluster=2,random_state=0).fit(x)
# 打印拟合标签
print(kmeans.labels_)
降维是一种无监督学习算法,而主成分分析 PCA是一种经典的降维算法。PCA通过正交变换将一组由线性相关变量表示的数据转换为几个由线性无关变量表示的数据,该线性无关变量就是主成分。
PCA基本思路
假设原始数据 m m m维随机变量 x = ( x 1 , x 2 , . . . , x m ) T x=(x_1,x_2,...,x_m)^T x=(x1,x2,...,xm)T,其均值向量 μ : μ = E ( x ) = ( μ 1 , μ 2 , . . . , μ m ) T μ:μ=E(x)=(μ_1,μ_2,...,μ_m)^T μ:μ=E(x)=(μ1,μ2,...,μm)T,协方差矩阵为
Σ = c o v ( x , x ) = E [ ( x − μ ) ( x − μ ) T ] \Sigma=cov(x,x)=E[(x-μ)(x-μ)^T] Σ=cov(x,x)=E[(x−μ)(x−μ)T]
由 m m m维随机变量 x x x到 m m m维随机变量 y = ( y 1 , y 2 , . . . , y m ) T y=(y_1,y_2,...,y_m)^T y=(y1,y2,...,ym)T的线性变换:
y i = α i T x = a 1 i x 1 + a 2 i x 2 + . . . + a m i x m ,其中 α i T = ( α 1 i , α 2 i , . . . , α m i ) y_i=\alpha_i^Tx=a_{1i}x_1+a_{2i}x_2+...+a_{mi}x_m,其中\alpha_i^T=(\alpha_{1i},\alpha_{2i},...,\alpha_{mi}) yi=αiTx=a1ix1+a2ix2+...+amixm,其中αiT=(α1i,α2i,...,αmi)
经过线性变换后的随机变量 y i y_i yi的均值、方差和协方差统计量可以表示为:
E ( y i ) = α i T μ i , i = 1 , . . . , m v a r ( y i ) = α i T Σ α i , i = 1 , . . . , m c o v ( y i , y j ) = α i T Σ α j , i = 1 , . . . , m E(y_i)=\alpha_i^Tμ_i,\quad i=1,...,m\\var(y_i)=\alpha_i^T\Sigma\alpha_i,\quad i=1,...,m\\cov(y_i,y_j)=\alpha_i^T\Sigma\alpha_j,\quad i=1,...,m E(yi)=αiTμi,i=1,...,mvar(yi)=αiTΣαi,i=1,...,mcov(yi,yj)=αiTΣαj,i=1,...,m
当随机变量 x x x到随机变量 y y y的线性变换满足如下条件时,变换后的 y 1 , y 2 , . . . , y m y_1,y_2,...,y_m y1,y2,...,ym分别为随机变量 x x x的第一主成分、第二主成分、…、第m主成分。
上述三个条件给出求解主成分的基本方法。根据优化目标和约束条件,使用拉格朗日乘子法求解主成分。以第一主成分为例,第一主成分的优化问题数学表达为:
max α 1 T Σ α 1 s . t . α 1 T α 1 = 1 \max\quad \alpha_1^T \Sigma\alpha_1\\s.t.\quad\alpha_1^T\alpha_1=1 maxα1TΣα1s.t.α1Tα1=1
定义拉格朗日函数:
L = α 1 T Σ α 1 − λ ( α 1 T α 1 − 1 ) L=\alpha_1^T\Sigma\alpha_1-\lambda(\alpha_1^T\alpha_1-1) L=α1TΣα1−λ(α1Tα1−1)
对 α 1 \alpha_1 α1求导并令拉格朗日函数等于0,有:
∂ L ∂ α 1 = Σ α 1 − λ α 1 = 0 \frac{\partial{L}}{\partial{\alpha_1}}=\Sigma\alpha_1-\lambda\alpha_1=0 ∂α1∂L=Σα1−λα1=0
根据矩阵特征值与特征向量的关系,由上式知 λ \lambda λ为 Σ \Sigma Σ的特征值, α 1 \alpha_1 α1为对应的单位特征向量。假设 α 1 \alpha_1 α1是 Σ \Sigma Σ的最大特征值 λ 1 \lambda_1 λ1对应的特征向量,那么 α 1 \alpha_1 α1和 λ 1 \lambda_1 λ1均为上述优化问题的最优解。因此 α 1 T x \alpha_1^Tx α1Tx为第一主成分,其方差为对应协方差矩阵的最大特征值:
v a r ( α 1 T x ) = α 1 T Σ α 1 = λ 1 var(\alpha_1^Tx)=\alpha_1^T\Sigma\alpha_1=\lambda_1 var(α1Tx)=α1TΣα1=λ1
同理,第 k k k主成分的方差的第 k k k个特征值为:
v a r ( α k T x ) = α k T Σ α k = λ k var(\alpha_k^Tx)=\alpha_k^T\Sigma\alpha_k=\lambda_k var(αkTx)=αkTΣαk=λk
PCA计算流程
PCA算法实现
import numpy as np
### PCA算法类
class PCA():
# 定义协方差矩阵计算方法
def calc_cov(self, X):
# 样本量
m = X.shape[0]
# 数据标准化
X = (X - np.mean(X, axis=0)) / np.var(X, axis=0)
return 1 / m * np.matmul(X.T, X)
# PCA 算法实现
# 输入为要进行PCA的矩阵和指定的主成分个数
def pca(self, X, n_components):
# 计算协方差矩阵,对应step1和step2
cov_matrix = self.calc_cov(X)
# 计算协方差矩阵的特征值和对应特征向量
# 对应step3
eigenvalues, eigenvectors = np.linalg.eig(cov_matrix)
# 对特征值排序,对应step4
idx = eigenvalues.argsort()[::-1]
# 取最大的前n_component组,对应step4
eigenvectors = eigenvectors[:, idx]
eigenvectors = eigenvectors[:, :n_components]
# Y=PX转换,对应step5
return np.matmul(X, eigenvectors)
PCA算法数据测试
from sklearn import datasets
import matplotlib.pyplot as plt
# 导入sklearn数据集
iris = datasets.load_iris()
X = iris.data
y = iris.target
# 将数据降维到3个主成分
X_trans = PCA().pca(X, 3)
# 颜色列表
colors = ['navy', 'turquoise', 'darkorange']
# 绘制不同类别
for c, i, target_name in zip(colors, [0,1,2], iris.target_names):
plt.scatter(X_trans[y == i, 0], X_trans[y == i, 1],
color=c, lw=2, label=target_name)
# 添加图例
plt.legend()
plt.show();
# 导入sklearn降维模块
from sklearn import decomposition
# 创建pca模型实例,主成分个数为3个
pca = decomposition.PCA(n_components=3)
# 模型拟合
pca.fit(X)
# 拟合模型并将模型应用于数据X
X_trans = pca.transform(X)
# 颜色列表
colors = ['navy', 'turquoise', 'darkorange']
# 绘制不同类别
for c, i, target_name in zip(colors, [0,1,2], iris.target_names):
plt.scatter(X_trans[y == i, 0], X_trans[y == i, 1],
color=c, lw=2, label=target_name)
# 添加图例
plt.legend()
plt.show();