第二章 K近邻法

1. 题目分析

给定一个二维空间数据集T={正实例:(5, 4), (9, 6), (4, 7);负实例:(2, 3), (8, 1), (7, 2)},试基于欧氏距离,找到数据点S(5, 3)的最近邻(k=1),并对S点进行分类预测

  • 输入:训练集数据$T = \left\{(5, 4, 1), (9, 6, 1), (4, 7, 1), (2, 3, 2), (8, 1, 2), (7, 2, 2)\right\}$;测试集$S = \left\{(5, 3, _)\right\}$;
  • 输出:测试集中样本所属类别y;
    • 根据欧氏距离,在训练集T中找出与测试集中S最邻近的k个点
    • 统计这k个点中,类别个数最多的点,作为测试样本的类别

​ k近邻法没有显式的学习过程

2. 算法分析

K近邻完整算法请走传送门,下面将拆解分析

2.1 初始化

# 定义训练集和测试数据

training_set = np.array([[5, 4, 1],
                         [9, 6, 1],
                         [4, 7, 1],
                         [2, 3, 2],
                         [8, 1, 2],
                         [7, 2, 2]])

rows = training_set.shape[0]
cols = training_set.shape[1]

S = np.array([[5, 3]])
  • 将训练数据存储为2行3列的array,最后一列是每条数据所属类别
  • 我们以1作为正例,以2作为负例

2.2 图示样本

# 方法:画散点图

def plot(sample):
    plt.xlabel('x')
    plt.ylabel('y')
    plt.axis([0, 10, 0, 10])

    x = sample[0]
    y = sample[1]
    cat = sample[2]
    if cat == 1:
        plt.scatter(x, y, c='r', marker='o')
    else:
        plt.scatter(x, y, c='b', marker='x')
  • 首先,定义好坐标轴刻度,都控制在(0, 10)之间
  • 其次,画点
    • 若为正例,画红色圈
    • 若为负例,画蓝色叉

2.3 欧氏距离

# 方法:计算样本之间的欧式距离

def compute_distance(x_train, x_test):
    distance = np.sqrt(np.square((x_train[0] - x_test[0])) + 
                       np.square((x_train[1] - x_test[1])))

    return np.array([distance, x_train[2]])
  • 即计算训练集中样本和测试集样本的距离
  • 需要保留训练集样本所属的类别,便于后面比较距离时调用,故返回的是一个array([两点之间距离,训练样本所属类别])

2.4 遍历样本

# 遍历训练集并计算距离

distance_array = np.zeros([1, 2])

for row in range(rows):
    x_train = training_set[row, :]
    x_test = S[0]

    distance = compute_distance(x_train, x_test)
    distance_array = np.row_stack((distance_array, distance))

    plot(x_train)
  • 首先,定义一个distance_array,用于保存测试样本与训练集中所有样本的距离
  • 遍历训练集中的所有样本
    • 计算两点之间的欧式距离
    • 动态扩展distance_array的大小
      • np.row_stack()可在distance_array中加入子项
      • 即插入行数据,每行数据为compute_distance()返回的结果
    • 同时,将此刻用到的训练集样本在坐标系中画出

2.5 判断类别

# 选取k值,并得到类别

k = 5

distance_array = distance_array[np.argsort(distance_array[:, 0])]
cat_array = distance_array[1: (k+1), 1].astype(np.int32)
max_cat = np.argmax(np.bincount(cat_array))

test_value = np.column_stack((S, 
                              np.array([max_cat], 
                              dtype=np.int32)))[0]

print("The test data belongs to ", max_cat)
  • 首先,选取K值,这里我们设置为5,即比较所有数据与测试样本的距离
  • 其次,我们调用np.argsort()将测试样本与训练集中样本的根据距离从小到大进行排序,得到排序后的distance_array并返回
  • 接着,由于我们初始化distance_array时,默认存入[0, 0],经排序后放在第一行;故删除第一行,取剩下所有行;且只保留类别信息存入cat_array
  • 最后,我们调用np.bincount()对cat_array中出现的类别进行统计,并调用np.argmax()取出出现次数最大值,作为测试样本的类别;并将类别加入到测试样本中,便于图示

2.6 图示样本集

# 图示所有点的位置

plot(test_value)
plt.text(test_value[0], test_value[1]+0.2, 'S')
plt.show()
  • 先画出测试样本在图中的位置

  • 并根据y坐标,标记其符号S

  • 整体展示所有样本

    超平面

3. Sklearn实现

from sklearn.neighbors import KNeighborsClassifier

# 1. 定义K值
k = 5

# 2. 根据参数要求得到训练集和测试集
x_train = training_set[:, :2]
y_train = training_set[:, 2]
x_test = S

# 3. 进行KNN分类
clf = KNeighborsClassifier(n_neighbors=k, n_jobs=1)
clf.fit(x_train, y_train)
y_predict = clf.predict(x_test)

print("The test data belongs to ", y_predict[0])