实验二 K-近邻算法及应用 一.实验目的 二.实验内容 三、实验报告要求 四.实验过程及结果 五.实验小结


博客班级 AHPU机器学习
作业要求 作业要求
作业目标 熟练掌握代码编写
学号 3180701209

1.理解K-近邻算法原理,能实现算法K近邻算法;
2.掌握常见的距离度量方法;
3.掌握K近邻树实现算法;
4.针对特定应用场景及数据,能应用K近邻解决实际问题。

二.实验内容

1.实现曼哈顿距离、欧氏距离、闵式距离算法,并测试算法正确性。
2.实现K近邻树算法;
3.针对iris数据集,应用sklearn的K近邻算法进行类别预测。
4.针对iris数据集,编制程序使用K近邻树进行类别预测。

三、实验报告要求

1.对照实验内容,撰写实验过程、算法及测试结果;
2.代码规范化:命名规则、注释;
3.分析核心算法的复杂度;
4.查阅文献,讨论K近邻的优缺点;
5.举例说明K近邻的应用场景。

四.实验过程及结果

实验代码及注释

(1)、

//导入数据
import math
from itertools import combinations

(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)、

x1 = [1, 1]
x2 = [5, 1]
x3 = [4, 4]

(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())))

输出结果:
实验二 K-近邻算法及应用
一.实验目的
二.实验内容
三、实验报告要求
四.实验过程及结果
五.实验小结

(5)、

//导入numpy和pandas库
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

(6)、

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]])

(7)、

df # 将建好的表显示在屏幕上查看

实验二 K-近邻算法及应用
一.实验目的
二.实验内容
三、实验报告要求
四.实验过程及结果
五.实验小结

(8)、

# 绘制数据散点图
plt.scatter(df[:50]['sepal length'], df[:50]['sepal width'], label='0') # 绘制前50个数据的散点图
plt.scatter(df[50:100]['sepal length'], df[50:100]['sepal width'], label='1') # 绘制50-100个数据的散点图
plt.xlabel('sepal length')
plt.ylabel('sepal width') # 设置x,y轴坐标名
plt.legend() # 绘图

实验二 K-近邻算法及应用
一.实验目的
二.实验内容
三、实验报告要求
四.实验过程及结果
五.实验小结

(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)

(10)、绘制模型图像,定义一些基本的信息

# 建立一个类KNN,用于k-近邻的计算
class KNN:
    #初始化
    def __init__(self, X_train, y_train, n_neighbors=3, p=2): # 初始化数据,neighbor表示邻近点,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]))
           #再取出训练集剩下的点,然后与n_neighbor个点比较大叫,将距离大的点更新
           #保证knn_list列表中的点是距离最小的点
            
        for i in range(self.n, len(self.X_train)): 
               '''此处 max(num,key=lambda x: x[0])用法:
               x:x[]字母可以随意修改,求最大值方式按照中括号[]里面的维度,
               [0]按照第一维,
               [1]按照第二维
               '''
            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]  # 将标签升序排列
        return max_count
    
    # 计算测试算法的正确率
    def score(self, X_test, y_test):
        right_count = 0 
        n = 10
        for X, y in zip(X_test, y_test):
            label = self.predict(X)
            if label == y:
                right_count += 1
        return right_count / len(X_test)

(11)、

clf = KNN(X_train, y_train)

(12)、

clf.score(X_test, y_test)# 计算正确率

实验二 K-近邻算法及应用
一.实验目的
二.实验内容
三、实验报告要求
四.实验过程及结果
五.实验小结

(13)、

test_point = [6.0, 3.0]
print('Test Point: {}'.format(clf.predict(test_point)))

(14)、

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.plot(test_point[0], test_point[1], 'bo', label='test_point')
plt.xlabel('sepal length')
plt.ylabel('sepal width')
plt.legend()

实验二 K-近邻算法及应用
一.实验目的
二.实验内容
三、实验报告要求
四.实验过程及结果
五.实验小结

(15)、

from sklearn.neighbors import KNeighborsClassifier

(16)、

clf = KNN(X_train, y_train)

(17)、

clf.score(X_test, y_test)

输出结果:
实验二 K-近邻算法及应用
一.实验目的
二.实验内容
三、实验报告要求
四.实验过程及结果
五.实验小结

(18)、

# 建造kd树
# kd-tree每个结点中主要包含的数据结构如下
 class KdNode(object):
   def __init__(self, dom_elt, split, left, right):
       self.dom_elt = dom_elt # k维向量节点(k维空间中的一个样本点)
       self.split = split # 整数(进行分割维度的序号)
       self.left = left # 该结点分割超平面左子空间构成的kd-tree
       self.right = right # 该结点分割超平面右子空间构成的kd-tree

class KdTree(object):
   def __init__(self, data):
       k = len(data[0]) # 数据维度
 
       def CreateNode(split, data_set): # 按第split维划分数据集exset创建KdNode
           if not data_set: # 数据集为空
              return None
              # key参数的值为一个函数,此函数只有一个参数且返回一个值用来进行比较
              # operator模块提供的itemgetter函数用于获取对象的哪些维的数据,参数为需要获取的数据在对象
              #data_set.sort(key=itemgetter(split)) # 按要进行分割的那一维数据排序
              data_set.sort(key=lambda x: x[split])
              split_pos = len(data_set) // 2 # //为Python中的整数除法
              median = data_set[split_pos] # 中位数分割点 
              split_next = (split + 1) % k # cycle coordinates
 
              # 递归的创建kd树
              return KdNode(median, split, 
                           CreateNode(split_next, data_set[:split_pos]), # 创建左子树
                           CreateNode(split_next, data_set[split_pos + 1:])) # 创建右子树
 
       self.root = CreateNode(0, data) # 从第0维分量开始构建kd树,返回根节点
# KDTree的前序遍历 
def preorder(root): 
    print (root.dom_elt) 
    if root.left: # 节点不为空
       preorder(root.left) 
    if root.right: 
       preorder(root.right)

(19)、


from math import sqrt
from collections import namedtuple

#定义一个namedtuple,分别存放最近坐标点、最近距离和访问过的节点数
result = namedtuple("Result_tuple", "nearest_point nearest_dist nodes_visited")
 
def find_nearest(tree, point):
	k = len(point) # 数据维度
	def travel(kd_node, target, max_dist):
		if kd_node is None: 
			return result([0] * k, float("inf"), 0) # python中用float("inf")和float("-inf")表示正负
		nodes_visited = 1
 
		s = kd_node.split # 进行分割的维度
		pivot = kd_node.dom_elt # 进行分割的“轴”
 
		if target[s] <= pivot[s]: # 如果目标点第s维小于分割轴的对应值(目标离左子树更近)
			nearer_node = kd_node.left # 下一个访问节点为左子树根节点
			further_node = kd_node.right # 同时记录下右子树
		else: # 目标离右子树更近
			nearer_node = kd_node.right # 下一个访问节点为右子树根节点
			further_node = kd_node.left
		
		temp1 = travel(nearer_node, target, max_dist) # 进行遍历找到包含目标点的区域
 
		nearest = temp1.nearest_point # 以此叶结点作为“当前最近点”
		dist = temp1.nearest_dist # 更新最近距离
 
		nodes_visited += temp1.nodes_visited 

		if dist < max_dist: 
			max_dist = dist # 最近点将在以目标点为球心,max_dist为半径的超球体内
 
		temp_dist = abs(pivot[s] - target[s]) # 第s维上目标点与分割超平面的距离
		if max_dist < temp_dist: # 判断超球体是否与超平面相交
		return result(nearest, dist, nodes_visited) # 不相交则可以直接返回,不用继续判断
 
		#---------------------------------------------------------------------- 
		# 计算目标点与分割点的欧氏距离 
		temp_dist = sqrt(sum((p1 - p2) ** 2 for p1, p2 in zip(pivot, target))) 
 
		if temp_dist < dist: # 如果“更近”
			nearest = pivot # 更新最近点
			dist = temp_dist # 更新最近距离
			max_dist = dist # 更新超球体半径
 
		# 检查另一个子结点对应的区域是否有更近的点
		temp2 = travel(further_node, target, max_dist) 
 
		nodes_visited += temp2.nodes_visited
		if temp2.nearest_dist < dist: # 如果另一个子结点内存在更近距离
			nearest = temp2.nearest_point # 更新最近点
			dist = temp2.nearest_dist # 更新最近距离

		return result(nearest, dist, nodes_visited)
	
	return travel(tree.root, point, float("inf")) # 从根节点开始递归

(20)、

data = [[2,3],[5,4],[9,6],[4,7],[8,1],[7,2]]
kd = KdTree(data)
preorder(kd.root)

输出结果:
实验二 K-近邻算法及应用
一.实验目的
二.实验内容
三、实验报告要求
四.实验过程及结果
五.实验小结

(21)、

from time import clock
from random import random
# 产生一个k维随机向量,每维分量值在0~1之间
def random_point(k):
return [random() for _ in range(k)]
# 产生n个k维随机向量
def random_points(k, n):
return [random_point(k) for _ in range(n)]

(22)、

ret = find_nearest(kd, [3,4.5])
print (ret)

(23)、

N = 400000
t0 = clock()
kd2 = KdTree(random_points(3, N)) # 构建包含四十万个3维空间样本点的kd树
ret2 = find_nearest(kd2, [0.1,0.5,0.8]) # 四十万个样本点中寻找离目标最近的点
t1 = clock()
print ("time: ",t1-t0, "s")
print (ret2)

输出结果:
实验二 K-近邻算法及应用
一.实验目的
二.实验内容
三、实验报告要求
四.实验过程及结果
五.实验小结

五.实验小结

(1)psp表格

psp2.1 任务内容 计划完成需要的时间(min) 实际完成需要的时间(min)
Planning 计划 100 80
Development 开发 200 250
Analysis 需求分析(包括学习新技术) 20 40
Design Spec 生成设计文档 30 50
Design Review 设计复审 5 10
Coding Standard 代码规范 3 5
Design 具体设计 10 12
Coding 具体编码 80 90
Code Review 代码复审 60 70
Test 测试(自我测试,修改代码,提交修改) 10 15
Reporting 报告 9 6
Test Report 测试报告 3 2
Size Measurement 计算工作量 3 2
Postmortem & Process Improvement Plan 事后总结,并提出过程改进计划 5 3

(2)心得和经验

了解、学到了很多知识,通过查阅书本、网上资料来理解理论知识。