码迷,mamicode.com
首页 > 编程语言 > 详细

实验二 K-近邻算法及应用

时间:2021-05-24 15:46:26      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:red   named   mamicode   取出   k-近邻算法   排序   com   理解   order   

班级 机器学习实验-计算机18级
实验内容 K-近邻算法及应用
姓名 程王宇
学号 3180701339

【实验目的】

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   #导入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: # 当两个特征的维数相等时,并且维度大于1时。
        sum = 0
        for i in range(len(x)):# 用range函数来遍历x所有的维度,x与y的维度相等。
            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]}
    # 一条语句循环两次x2、x3,当x2时,当前i产生一个值,当x3时,当前i产生一个值。
    print(min(zip(r.values(), r.keys())))

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

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#查看数据集的内容

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

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、


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)
            #np.linalg.norm函数
            #np.linalg.norm(x, ord=None, axis=None, keepdims=False)
            #① x: 表示矩阵(也可以是一维)
            #② ord:范数类型
            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]))
            # 首先max函数里面选取出  knn_list 里面最大的距离数据
            # 然后取出它的索引存在 max_index
            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_list的数据 目标是找到距离小的点        
        # 统计
        knn = [k[-1] for k in knn_list]
        # 解析列表 把对应的类别放入新的列表
        count_pairs = Counter(knn)
        #  Counter类的目的是用来跟踪值出现的次数。它是一个无序的容器类型,以字典的键值对形式存储,其中元素作为key
        max_count = sorted(count_pairs, key=lambda x:x)[-1]
        #Lambda()函数
        #a = lambda x,y,z:(x+8)*y-z
        #print(a(5,6,8))
        return max_count
        # 最后进行排序 得出出现次数最多的那个类别 作为最终的分类结果。     
    def score(self, X_test, y_test):
        right_count = 0
        # 分类正确的次数 初始为 0
        n = 10
        for X, y in zip(X_test, y_test):
            label = self.predict(X)
            # print("预测类别为:{}".format(label))
            # print("实际类别为: {}".format(y))
            # print("")
            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)

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

15、

from sklearn.neighbors import KNeighborsClassifier

16、

clf_sk = KNeighborsClassifier()
clf_sk.fit(X_train, y_train)

17、

clf_sk.score(X_test, y_test)

18、

# 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、

# 对构建好的kd树进行搜索,寻找与目标点最近的样本点:
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)

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)

实验截图

1、
技术图片

2、
技术图片

3、
技术图片
4、
技术图片
5、
技术图片
6、
技术图片
7、
技术图片
8、
技术图片
9、
技术图片
10
技术图片
11、
技术图片
12、
技术图片
13、
技术图片
14、
技术图片
15、
技术图片
16、
技术图片
17、
技术图片
18、
技术图片
19、
技术图片
20、
技术图片
21、
技术图片
22、
技术图片
23、
技术图片
24、
技术图片

讨论K*邻算法的优缺点

优点
1.简单易用
2.没有显式的训练过程,在训练过程中仅仅是把训练样本保存起来,训练时间开销为0,是懒惰学*(lazy learning) 的著名代表 。
3.预测效果好
4.对异常值不敏感
缺点
1、效率低下
2、高度数据相关
3、预测的结果不具有可解释性

K*邻算法常见的应用场景:

1、python中的文本自动分类
2、社交网站的数据分类
3、手写识别系统的数据分类
4、聚类分析,多分类领域

实验小结

通过这次实验,主要理解K-近邻算法原理,基本实现算法K近邻算法。可以看出K值的选择会对K近邻算法的结果会产生重大影响。其次就是掌握常见的距离度量方法,在度量算法中,主要理解曼哈顿距离、欧氏距离、闵式距离算法。实验发现,K近邻算法一般选择k个最相似数据中出现次数最多的分类,作为新数据的分类。

实验二 K-近邻算法及应用

标签:red   named   mamicode   取出   k-近邻算法   排序   com   理解   order   

原文地址:https://www.cnblogs.com/cwy183/p/14783196.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!