第3章 k近邻法
1.近邻法是基本且简单的分类与回归方法。近邻法的基本做法是:对给定的训练实例点和输入实例点,首先确定输入实例点的个最近邻训练实例点,然后利用这个训练实例点的类的多数来预测输入实例点的类。
2.近邻模型对应于基于训练数据集对特征空间的一个划分。近邻法中,当训练集、距离度量、值及分类决策规则确定后,其结果唯一确定。
3.近邻法三要素:距离度量、值的选择和分类决策规则。常用的距离度量是欧氏距离及更一般的pL距离。值小时,近邻模型更复杂;值大时,近邻模型更简单。值的选择反映了对近似误差与估计误差之间的权衡,通常由交叉验证选择最优的。
常用的分类决策规则是多数表决,对应于经验风险最小化。
4.近邻法的实现需要考虑如何快速搜索k个最近邻点。kd树是一种便于对k维空间中的数据进行快速检索的数据结构。kd树是二叉树,表示对维空间的一个划分,其每个结点对应于维空间划分中的一个超矩形区域。利用kd树可以省去对大部分数据点的搜索, 从而减少搜索的计算量。
距离度量
设特征空间是维实数向量空间 ,,, ,则:,的距离定义为:
- 曼哈顿距离
- 欧氏距离
- 闵式距离minkowski_distance
In [1]:
import math
from itertools import combinations
In [2]:
def L(x, y, p=2): # x1 = [1, 1], x2 = [5,1] if len(x) == len(y) and len(x) > 1: sum = 0 for i in range(len(x)): sum += math.pow(abs(x[i] - y[i]), p) return math.pow(sum, 1 / p) else: return 0
课本例3.1
In [3]:
x1 = [1, 1] x2 = [5, 1] x3 = [4, 4]
In [4]:
# x1, x2
for i in range(1, 5): r = {'1-{}'.format(c): L(x1, c, p=i) for c in [x2, x3]} print(min(zip(r.values(), r.keys())))
python实现,遍历所有数据点,找出个距离最近的点的分类情况,少数服从多数
In [5]:
import numpy as np
import pandas as pd import matplotlib.pyplot as plt %matplotlib inline from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from collections import Counter
In [6]:
# data
iris = load_iris() df = pd.DataFrame(iris.data, columns=iris.feature_names) df['label'] = iris.target df.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label'] # data = np.array(df.iloc[:100, [0, 1, -1]])
In [7]:
df
Out[7]:
In [8]:
plt.scatter(df[:50]['sepal length'], df[:50]['sepal width'], label='0') plt.scatter(df[50:100]['sepal length'], df[50:100]['sepal width'], label='1') plt.xlabel('sepal length') plt.ylabel('sepal width') plt.legend()
Out[8]:
In [9]:
data = np.array(df.iloc[:100, [0, 1, -1]]) X, y = data[:,:-1], data[:,-1] X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
In [10]:
class KNN:
def __init__(self, X_train, y_train, n_neighbors=3, p=2): """ parameter: n_neighbors 临近点个数 parameter: p 距离度量 """ self.n = n_neighbors self.p = p self.X_train = X_train self.y_train = y_train def predict(self, X): # 取出n个点 knn_list = [] for i in range(self.n): dist = np.linalg.norm(X - self.X_train[i], ord=self.p) knn_list.append((dist, self.y_train[i])) for i in range(self.n, len(self.X_train)): max_index = knn_list.index(max(knn_list, key=lambda x: x[0])) dist = np.linalg.norm(X - self.X_train[i], ord=self.p) if knn_list[max_index][0] > dist: knn_list[max_index] = (dist, self.y_train[i]) # 统计 knn = [k[-1] for k in knn_list] count_pairs = Counter(knn) # max_count = sorted(count_pairs, key=lambda x: x)[-1] max_count = sorted(count_pairs.items(), key=lambda x: x[1])[-1][0] return max_count def score(self, X_test, y_test): right_count = 0 n = 10 for X