1、距离度量
在机器学习算法中,我们经常需要计算样本之间的相似度,通常的做法是计算样本之间的距离。
设 x x x 和 y y y 为两个向量,求它们之间的距离。
1.1 欧氏距离(Euclidean distance)
欧几里得度量(euclidean metric)(也称欧氏距离)是一个通常采用的距离定义,指在 m m m 维空间中两个点之间的真实距离,或者向量的自然长度(即该点到原点的距离)。在二维和三维空间中的欧氏距离就是两点之间的实际距离。
距离公式:
d ( x , y ) = ∑ i ( x i − y i ) 2 d\left( x,y \right) = \sqrt{\sum_{i}^{}(x_{i} - y_{i})^{2}} d(x,y)=i∑(xi−yi)2
1.2 曼哈顿距离(Manhattan distance)
想象你在城市道路里,要从一个十字路口开车到另外一个十字路口,驾驶距离是两点间的直线距离吗?显然不是,除非你能穿越大楼。实际驾驶距离就是这个“曼哈顿距离”。而这也是曼哈顿距离名称的来源,曼哈顿距离也称为城市街区距离(City Block distance)。
距离公式:
d ( x , y ) = ∑ i ∣ x i − y i ∣ d(x,y) = \sum_{i}^{}|x_{i} - y_{i}| d(x,y)=i∑∣xi−yi∣
1.3 切比雪夫距离(Chebyshev distance)
在数学中,切比雪夫距离(Chebyshev distance)或是L∞度量,是向量空间中的一种度量,二个点之间的距离定义是其各坐标数值差绝对值的最大值。以数学的观点来看,切比雪夫距离是由一致范数(uniform norm)(或称为上确界范数)所衍生的度量,也是超凸度量(injective metric space)的一种。
距离公式:
d ( x , y ) = max i ∣ x i − y i ∣ d\left( x,y \right) = \max_{i}\left| x_{i} - y_{i} \right| d(x,y)=imax∣xi−yi∣
若将国际象棋棋盘放在二维直角座标系中,格子的边长定义为1,座标的 x x x 轴及 y y y 轴和棋盘方格平行,原点恰落在某一格的中心点,则王从一个位置走到其他位置需要的步数恰为二个位置的切比雪夫距离,因此切比雪夫距离也称为棋盘距离。例如位置F6和位置E2的切比雪夫距离为4。任何一个不在棋盘边缘的位置,和周围八个位置的切比雪夫距离都是1。
1.4 闵可夫斯基距离(Minkowski distance)
闵氏空间指狭义相对论中由一个时间维和三个空间维组成的时空,为俄裔德国数学家闵可夫斯基最先表述。他的平坦空间(即假设没有重力,曲率为零的空间)的概念以及表示为特殊距离量的几何学是与狭义相对论的要求相一致的。闵可夫斯基空间不同于牛顿力学的平坦空间。 p p p 取1或2时的闵氏距离是最为常用的, p = 2 p= 2 p=2 即为欧氏距离,而 p = 1 p =1 p=1 时则为曼哈顿距离。
当 p p p 取无穷时的极限情况下,可以得到切比雪夫距离。
距离公式:
d ( x , y ) = ( ∑ i ∣ x i − y i ∣ p ) 1 p d\left( x,y \right) = \left( \sum_{i}^{}|x_{i} - y_{i}|^{p} \right)^{\frac{1}{p}} d(x,y)=(i∑∣xi−yi∣p)p1
1.5 汉明距离(Hamming distance)
汉明距离是使用在数据传输差错控制编码里面的,汉明距离是一个概念,它表示两个(相同长度)字对应位不同的数量,我们以表示两个字,之间的汉明距离。对两个字符串进行异或运算,并统计结果为1的个数,那么这个数就是汉明距离。
距离公式:
d ( x , y ) = 1 N ∑ i 1 x i ≠ y i d\left( x,y \right) = \frac{1}{N}\sum_{i}^{}1_{x_{i} \neq y_{i}} d(x,y)=N1i∑1xi=yi
1.6 余弦相似度(Cosine Similarity)
余弦相似性通过测量两个向量的夹角的余弦值来度量它们之间的相似性。0度角的余弦值是1,而其他任何角度的余弦值都不大于1;并且其最小值是-1。从而两个向量之间的角度的余弦值确定两个向量是否大致指向相同的方向。两个向量有相同的指向时,余弦相似度的值为1;两个向量夹角为90°时,余弦相似度的值为0;两个向量指向完全相反的方向时,余弦相似度的值为-1。这结果是与向量的长度无关的,仅仅与向量的指向方向相关。余弦相似度通常用于正空间,因此给出的值为0到1之间。
二维空间为例,上图的 a a a 和 b b b 是两个向量,我们要计算它们的夹角θ。余弦定理告诉我们,可以用下面的公式求得:
cos θ = a 2 + b 2 − c 2 2 a b \cos\theta = \frac{a^{2} + b^{2} - c^{2}}{2ab} cosθ=2aba2+b2−c2
假定 a a a 向量是 [ x 1 , y 1 ] \left\lbrack x_{1},y_{1} \right\rbrack [x1,y1] , b b b 向量是 [ x 2 , y 2 ] \left\lbrack x_{2},y_{2} \right\rbrack [x2,y2],两个向量间的余弦值可以通过使用欧几里得点积公式求出:
cos ( θ ) = A ⋅ B ∥ A ∥ ∥ B ∥ = ∑ i = 1 n A i × B i ∑ i = 1 n ( A i ) 2 × ∑ i = 1 n ( B i ) 2 \cos\left( \theta \right) = \frac{A \cdot B}{\parallel A \parallel \parallel B \parallel} = \frac{\sum_{i = 1}^{n}A_{i} \times B_{i}}{\sqrt{\sum_{i = 1}^{n}(A_{i})^{2} \times \sqrt{\sum_{i = 1}^{n}(B_{i})^{2}}}} cos(θ)=∥A∥∥B∥A⋅B=∑i=1n(Ai)2×∑i=1n(Bi)2∑i=1nAi×Bi
cos ( θ ) = A ⋅ B ∥ A ∥ ∥ B ∥ = ( x 1 , y 1 ) ⋅ ( x 2 , y 2 ) x 1 2 + y 1 2 × x 2 2 + y 2 2 = x 1 x 2 + y 1 y 2 x 1 2 + y 1 2 × x 2 2 + y 2 2 \cos\left( \theta \right) = \frac{A \cdot B}{\parallel A \parallel \parallel B \parallel} = \frac{\left( x_{1},y_{1} \right) \cdot \left( x_{2},y_{2} \right)}{\sqrt{x_{1}^{2} + y_{1}^{2}} \times \sqrt{x_{2}^{2} + y_{2}^{2}}} = \frac{x_{1}x_{2} + y_{1}y_{2}}{\sqrt{x_{1}^{2} + y_{1}^{2}} \times \sqrt{x_{2}^{2} + y_{2}^{2}}} cos(θ)=∥A∥∥B∥A⋅B=x12+y12×x22+y22(x1,y1)⋅(x2,y2)=x12+y12×x22+y22x1x2+y1y2
如果向量 a a a 和 b b b 不是二维而是 n n n 维,上述余弦的计算法仍然正确。假定 A A A 和 B B B 是两个 n n n 维向量, A A A 是 [ A 1 , A 2 , … , A n ] \left\lbrack A_{1},A_{2},\ldots,A_{n} \right\rbrack [A1,A2,…,An], B B B是 [ B 1 , B 2 , … , B n ] \left\lbrack B_{1},B_{2},\ldots,B_{n} \right\rbrack [B1,B2,…,Bn],则 A A A 与 B B B 的夹角余弦等于:
cos ( θ ) = A ⋅ B ∥ A ∥ ∥ B ∥ = ∑ i = 1 n A i × B i ∑ i = 1 n ( A i ) 2 × ∑ i = 1 n ( B i ) 2 \cos\left( \theta \right) = \frac{A \cdot B}{\parallel A \parallel \parallel B \parallel} = \frac{\sum_{i = 1}^{n}A_{i} \times B_{i}}{\sqrt{\sum_{i = 1}^{n}(A_{i})^{2}} \times \sqrt{\sum_{i = 1}^{n}(B_{i})^{2}}} cos(θ)=∥A∥∥B∥A⋅B=∑i=1n(Ai)2×∑i=1n(Bi)2∑i=1nAi×Bi
2、KNN 算法
1. k k k 近邻法是基本且简单的分类与回归方法。 k k k 近邻法的基本做法是:对给定的训练实例点和输入实例点,首先确定输入实例点的 k k k 个最近邻训练实例点,然后利用这 k k k 个训练实例点的类的多数来预测输入实例点的类。
2. k k k 近邻模型对应于基于训练数据集对特征空间的一个划分。 k k k 近邻法中,当训练集、距离度量、 k k k 值及分类决策规则确定后,其结果唯一确定。
3. k k k 近邻法三要素:距离度量、 k k k 值的选择和分类决策规则。常用的距离度量是欧氏距离。 k k k 值小时, k k k 近邻模型更复杂; k k k 值大时, k k k 近邻模型更简单。 k k k 值的选择反映了对近似误差与估计误差之间的权衡,通常由交叉验证选择最优的 k k k。
常用的分类决策规则是多数表决,对应于经验风险最小化。
4. k k k 近邻法的实现需要考虑如何快速搜索 k 个最近邻点。 kd 树是一种便于对 k 维空间中的数据进行快速检索的数据结构。kd 树是二叉树,表示对 k k k 维空间的一个划分,其每个结点对应于 k k k 维空间划分中的一个超矩形区域。利用 kd 树可以省去对大部分数据点的搜索,从而减少搜索的计算量。
3、KNN 算法实现鸢尾花数据集分类
# @Time : 2021/12/9 15:28
# @Author : xiao cong
# @Function : 鸢尾花数据集实现 KNN 算法
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split # 划分训练集和测试集
from collections import Counter
from sklearn.neighbors import KNeighborsClassifier
# 导入鸢尾花数据集
iris = load_iris()
df = pd.DataFrame(iris.data, columns=['sepal length', 'sepal width', 'petal length', 'petal width'])
df["label"] = iris.target
# 可视化长和宽
plt.figure(figsize=(12, 8))
plt.scatter(df[:50]["sepal length"], df[:50]["sepal width"], c="b", label="0")
plt.scatter(df[50:100]["sepal length"], df[50:100]["sepal width"], c="r", label="1")
plt.xlabel('sepal length', fontsize=18)
plt.ylabel('sepal width', fontsize=18)
plt.legend()
plt.show()
# **************************************************************************************
"""
numpy 实现 KNN 算法
"""
class KNN:
def __init__(self, X_train, y_train, k):
self.X_train = X_train
self.y_train = y_train
self.k = k
def Normalize(self, X):
maxVal = np.max(X, axis=0)
minVal = np.min(X, axis=0)
ranges = maxVal - minVal
normX = (X - minVal) / ranges
return normX, ranges, minVal
def predict(self, X_point):
# X_train, ranges, minVal = self.Normalize(self.X_train) # 此处并不适合归一化
# X_point = (X_point - minVal) / ranges
dist = np.sqrt(np.sum((X_point - self.X_train) ** 2, axis=1))
sortedDistIndex = dist.argsort() # 从小到大排序,记录下标号
index = sortedDistIndex[:self.k] # 取最近的前k个点下标
labels = list() # 存储相应的标签值
for i in index:
labels.append(self.y_train[i])
count_pairs = dict(Counter(labels))
maxCount = max(count_pairs, key=count_pairs.get) # 字典中value最大值所对应的的key
return maxCount
def score(self, X_test, y_test):
right = 0
for X, y in zip(X_test, y_test):
label = self.predict(X)
if label == y:
right += 1
return right / len(X_test)
data = np.array(df.iloc[:150, [0, 1, -1]])
X, y = data[:, :2], data[:, -1]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3)
clf = KNN(X_train, y_train, k=5)
print("numpy 方法")
print(clf.score(X_test, y_test))
test_point = np.array([6.0, 3.0]).reshape(1, -1) # 必须是二维数组
print('Test Point: {}'.format(clf.predict(test_point)))
# ***********************************************************************************
"""
sklearn 方法
"""
clf_skl = KNeighborsClassifier(n_neighbors=5)
clf_skl.fit(X_train, y_train)
print("sklearn 方法")
print(clf_skl.score(X_test, y_test))
print("Test Point".format(clf_skl.predict(test_point)))