机器学习--准备数据与Numpy(八)--距离矩阵计算

距离矩阵的计算


距离矩阵参考资料


在讲距离矩阵之前,先复习一下什么是 欧式距离 :

在做分类时,常常需要估算两个样本间的相似性度量(SimilarityMeasurement),这时经常就用到两个样本间的“距离”(Distance),采用什么样的方法计算距离是很讲究,甚至关系到分类的正确与否。​

经常使用的度量方法是欧式距离,

欧氏距离是最易于理解的一种距离计算方法,源自欧氏空间中两点间的距离公式。

(1)二维平面上两点a(x1,y1)与b(x2,y2)间的欧氏距离:

(2)三维空间两点a(x1,y1,z1)与b(x2,y2,z2)间的欧氏距离:

(3)两个n维向量a(x11,x12,…,x1n)与 b(x21,x22,…,x2n)间的欧氏距离:

 也可以用表示成向量运算的形式:

例如:

k-nearest neighbor这部分中,核心就是计算训练集与测试集之元素之间的欧氏距离。要求从训练集取5000个图像,测试集取了500个图像,计 算这5000个用于训练的图像与500个用于测试的图像之间的欧氏距离,其结果就是一个5000*500的距离矩阵


给定m\times n阶矩阵X,满足X=[x_1, x_2, ...x_n]。这里第i列向量是m维向量。求n\times n矩阵,使得:

D_{ij}=||x_i-x_j||^2

这里提供4种方法,需要使用到以下Python库:

import numpy as np
import numpy.linalg as la

• 方法1:标准方法使用两层循环计算Dij

def compute_squared_EDM_method(X):
  # determin dimensions of data matrix
  m,n = X.shape
  # initialize squared EDM D
  D = np.zeros([n, n])
  # iterate over upper triangle of D
  for i in range(n):
    for j in range(i+1, n):
      D[i,j] = la.norm(X[:, i] - X[:, j])**2
      D[j,i] = D[i,j]    #*1
return D
由于是计算矩阵自身行向量之间的距离,所以结果是一个对称的三角矩阵。注意*1行代码处所做的优化。

• 方法2:利用矩阵内积dot计算Dij

在第一种方法中,我们使用了numpy的norm这个方法,这个方法从数学上讲,其计算公式是:||x_i-x_j ||=\sqrt{(x_i-x_j ) (x_i-x_j )^T }

然后我们又将这个计算结果平方后赋给D_{ij}:D_{ij}=||x_i-x_j||^2

因此,我们实际上是在计算:

D_{ij}=(x_i-x_j ) (x_i-x_j )^T

上述运算可以使用点积(即矩阵内积)来计算:

D[i,j] = np.dot(X[:,i]-X[:,j],(X[:,i]-X[:,j]).T)

def compute_squared_EDM_method2(X):
  # determin dimensions of data matrix
  m,n = X.shape
  # initialize squared EDM D
  D = np.zeros([n, n])
  # iterate over upper triangle of D
  for i in range(n):
    for j in range(i+1, n):
      d = X[:,i] - X[:,j]
      D[i,j] = np.dot(d, d)
      D[j,i] = D[i,j]
  return D

• 方法3:避免循环内点积运算,减少dot调用次数

注意在上面的方法中,dot运算被调用了n^2-n次,并且每次进行了m次乘积运算和m-1次加法运算。尽管numpy底层可能对点积运算做了优化,但这里还是存在可能进行进一步优化。请看下面的数学推导:

\begin{split}D_{ij}&=(x_i-x_j)^T(x_i-x_j)\\&=x^T_ix_i-2x^T_ix_j+x^T_jx_j\end{split}

这里x^T_ix_i,x^T_jx_j,x^T_ix_j属于格拉姆矩阵中的元素,可以通过在循环外计算矩阵,在循环内直接引用元素值即可,从而在循环内我们只需要做两次加(减)法运算:

D_{ij}=G_{ii}-2G_{ij}+G_{jj}

格拉姆矩阵的求法很简单,只需要:

G=np.dot(X.T, X)

现在代码变为:

def compute_squared_EDM_method3(X):
  # determin dimensions of data matrix
  m,n = X.shape
  # compute Gram matrix
  G = np.dot(X.T, X)
  # initialize squared EDM D
  D = np.zeros([n, n])
  # iterate over upper triangle of D
  for i in range(n):
    for j in range(i+1, n):
      d = X[:,i] - X[:,j]
      D[i,j] = G[i,i] - 2 * G[i,j] + G[j,j]
      D[j,i] = D[i,j]
  return D


• 方法4:利用重复操作替代外部循环

假设距离矩阵可以表示为D = H+K-2G,与公式D_{ij}=G_{ii}-2G_{ij}+G_{jj}进行对比,有:

H_{ij}=G_{ii}, K_{ij}=G_{jj}

这里H中第i行的每一个元素,取值都为G_{ii},也就是H的每一列,都对应着格拉姆矩阵的对角阵,因此,我们可以用下面的代码来计算H:

H = np.tile(np.diag(G), (n,1))

此外,由于K= H^T,所以最终距离矩阵可以计算为

D=H+H^T-2G

现在,代码不再需要循环了:

def compute_squared_EDM_method4(x):
  m,n = X.shape
  G = np.dot(X.T, X)
  H = np.tile(np.diag(G), (n,1))
  return H + H.T - 2*G


# -*- coding:utf-8 -*-
# Author:Justin Chan
# Python:3.6

import numpy as np
import numpy.random as np_randon

X = np_randon.randint(10,size=(4,3))
print(X)

print(X[:,1])#矩阵第一列向量
print(X[1,:])#矩阵第一行向量

[[5 8 2]
 [7 4 8]
 [6 7 1]
 [2 2 0]]
[8 4 7 2]
[7 4 8]
import numpy as np
import numpy.linalg as la
import time

X = np.array([range(0, 500), range(500, 1000)])
m, n = X.shape

#方法1:标准方法使用两层循环计算Dij
#时间复杂度O(n*n)
t = time.time()
D = np.zeros([n, n])
for i in range(n):
    for j in range(i + 1, n):
        D[i, j] = la.norm(X[:, i] - X[:, j]) ** 2
        D[j, i] = D[i, j]
print(time.time() - t)


#方法2:利用矩阵内积dot计算Dij
# 时间复杂度O(n*n)*O(m)
t = time.time()
D = np.zeros([n, n])
for i in range(n):
    for j in range(i + 1, n):
        d = X[:, i] - X[:, j]
        D[i, j] = np.dot(d, d)
        D[j, i] = D[i, j]
print(time.time() - t)


#方法3:避免循环内点积运算,减少dot调用次数
# 时间复杂度O(n*n)
t = time.time()
G = np.dot(X.T, X)
D = np.zeros([n, n])
for i in range(n):
    for j in range(i + 1, n):
        D[i, j] = G[i, i] - G[i, j] * 2 + G[j,j]
        D[j, i] = D[i, j]
print(time.time() - t)


#方法4:利用重复操作替代外部循环
t = time.time()
G = np.dot(X.T, X)
#把G对角线元素拎出来,列不变,行复制n遍。
H = np.tile(np.diag(G), (n, 1))
D = H + H.T - G * 2
print(time.time() - t)
 
1.051072120666504
0.3479161262512207
0.15077900886535645
0.003982067108154297


其它习题:

1.将numpy数组保存到文件并读取。

2.用numpy解一个线性方程组。

3.自己实现一遍例题。




  • 17
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
### 回答1: 好的,下面是Python实现K-means聚类算法对iris鸢尾花数据集的聚类工作的代码: ```python import numpy as np import pandas as pd import matplotlib.pyplot as plt from sklearn import datasets # 加载iris鸢尾花数据集 iris = datasets.load_iris() X = iris.data[:, :2] # 取前两个特征 y = iris.target # 定义K-means算法 class KMeans: def __init__(self, k=3, max_iter=100): self.k = k # 聚类数量 self.max_iter = max_iter # 最大迭代次数 def fit(self, X): # 随机初始化聚类中心 self.centroids = X[np.random.choice(range(len(X)), self.k, replace=False)] for i in range(self.max_iter): # 计算每个样本到聚类中心的距离 distances = np.sqrt(((X - self.centroids[:, np.newaxis])**2).sum(axis=2)) # 分配每个样本到最近的聚类中心 labels = np.argmin(distances, axis=0) # 更新聚类中心 new_centroids = np.array([X[labels == j].mean(axis=0) for j in range(self.k)]) if np.allclose(new_centroids, self.centroids): break # 聚类中心不再改变 self.centroids = new_centroids return self def predict(self, X): distances = np.sqrt(((X - self.centroids[:, np.newaxis])**2).sum(axis=2)) return np.argmin(distances, axis=0) # 聚类 kmeans = KMeans(k=3) kmeans.fit(X) labels = kmeans.predict(X) # 可视化聚类结果 plt.scatter(X[:, 0], X[:, 1], c=labels) plt.xlabel('Sepal length') plt.ylabel('Sepal width') plt.show() ``` 运行代码后,会显示一张图形,表示对iris鸢尾花数据集的聚类结果。其中,不同颜色的点表示不同的聚类簇。 ### 回答2: K-means聚类算法是一种常用的无监督学习算法,可以对数据进行聚类。在对iris鸢尾花数据集进行聚类工作时,可以使用K-means算法将鸢尾花数据分成不同的簇。 首先,我们需要加载鸢尾花数据集,这可以使用Python中的scikit-learn库来完成。代码如下: from sklearn.datasets import load_iris import numpy as np iris = load_iris() X = iris.data 接下来,我们需要实现K-means算法的主要步骤。首先,需要随机初始化K个簇的中心点。然后,将每个数据点分配给最近的中心点。接着,根据分配的数据点更新每个簇的中心点。重复这个过程,直到簇的中心点不再发生变化或达到最大迭代次数。 代码如下: def k_means(X, k, max_iters): n_samples, n_features = X.shape # 随机初始化K个簇的中心点 random_indices = np.random.choice(range(n_samples), k) centers = X[random_indices] for _ in range(max_iters): # 分配样本到最近的中心点 distances = np.linalg.norm(X[:, np.newaxis] - centers, axis=2) labels = np.argmin(distances, axis=1) # 更新中心点 new_centers = np.array([X[labels == i].mean(axis=0) for i in range(k)]) # 判断中心点是否发生变化 if np.all(centers == new_centers): break centers = new_centers return labels, centers 最后,我们可以调用k_means函数进行聚类,并输出结果。 代码如下: k = 3 max_iters = 100 labels, centers = k_means(X, k, max_iters) print(labels) print(centers) 以上代码将输出聚类结果和每个簇的中心点。聚类结果为一个包含每个数据点所属簇的标签的数组,中心点为一个矩阵,每行表示一个簇的中心点。 通过以上步骤,我们在编程中实现了K-means聚类算法对iris鸢尾花数据集的聚类工作。 ### 回答3: K-means是一种常用的聚类算法,适用于无监督学习任务。它通过将数据点划分为K个簇,每个簇内的数据点相似度较高,不同簇的数据点相似度较低。在对iris鸢尾花数据集进行聚类工作时,首先需要对数据集进行预处理。 1. 加载数据集:使用相关的程序包(如scikit-learn)加载iris鸢尾花数据集。 2. 数据预处理:对于聚类算法来说,数据预处理的主要目标是将数据转换为数值型,并且进行标准化处理,以提高聚类效果。 3. 初始化聚类中心:由于K-means算法是一种基于中心的聚类算法,需要初始化K个聚类中心。可以使用随机选择的方式从数据集中选择K个作为初始聚类中心。 4. 迭代更新聚类中心:K-means算法的核心是通过迭代的方式更新聚类中心,直至满足停止条件。具体步骤如下: - 将每个数据点分配到距离最近的聚类中心; - 根据分配结果,更新每个聚类的中心(聚类中心是属于该簇内所有数据点的均值); - 检查聚类中心的变化量是否小于设定的阈值,若满足停止条件,则停止迭代,否则返回第一步。 5. 输出聚类结果:将聚类结果可视化或输出为结果文件,以便进一步分析和解释。 综上所述,通过编程实现K-means聚类算法对iris鸢尾花数据集的聚类工作,可以得到对iris数据集的聚类结果,给出样本属于哪一类鸢尾花的判断结果,为进一步的机器学习任务提供基础数据基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值