zoukankan      html  css  js  c++  java
  • 机器识别-实验二


    博客班级 AHPU机器学习
    作业要求 K-近邻算法及应用
    作业目标 理解K-近邻算法原理,能实现算法K近邻算法
    学号 3180701118


    【实验目的】

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

    【实验内容】

    1.实现曼哈顿距离、欧氏距离、闵式距离算法,并测试算法正确性。

    一、闵可夫斯基距离
    闵可夫斯基距离又叫做闵氏距离,是一组距离的定义,
    其计算公式为如下图

    根据q取值的不同,闵氏距离可分为曼哈顿距离、欧氏距离和切比雪夫距离等。

    1. 曼哈顿距离
      曼哈顿距离标明两个点在标准坐标系上的绝对轴距总和。
      当q=1时的一阶闵氏距离称为绝对值距离,又叫做曼哈顿距离。其计算公式为:

    2. 欧氏距离

    当q=2时,二阶闵氏距离称为欧几里得距离或欧氏距离。欧氏距离是坐标系内两点的直线距离。其计算公式如下:

    1. 切比雪夫距离

    当→∞时,闵氏距离可以转化为切比雪夫距离(Chebyshev distance),其计算公式为:

    代码:
    def L(x, y, p):
    # 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
    

    测试:

    2.实现K近邻树算法;

    kd树:
    kd树是一种对k维空间中的实例点进行存储以便对其进行快速检索的树形数据结构。
    kd树是二叉树,表示对k kk维空间的一个划分。构造kd树相当于不断地用垂直于坐标轴的超平面将k kk维空间切分,构成一系列的k维超矩形区域。kd树的每个结点对应于一个k kk维超矩形区域。

    构造kd树的方法如下:
    构造根结点,使根结点对应于k kk维空间中包含所有实例点的超矩形区域;通过下面的递归方法,不断地对k kk维空间进行切分,生成子结点。在超矩形区域(结点)上选择一个坐标轴和在此坐标轴上的一个切分点,确定一个超平面,这个超平面通过选定的切分点并垂直于选定的坐标轴,将当前超矩形区域切分为左右两个子区域 (子结点);这时,实例被分到两个子区域。这个过程直到子区域内没有实例时终止(终止时的结点为叶结点)。在此过程中,将实例保存在相应的结点上。

    通常,依次选择坐标轴对空间切分,选择训练实例点在选定坐标轴上的中位数 (median)为切分点,这样得到的kd树是平衡的。注意,平衡的kd树搜索时的效率未必是最优的。
    构造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  # 周期坐标
    
                # 递归的创建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)
    
    # 对构建好的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"))  # 从根节点开始递归
    
    

    3.针对iris数据集,应用sklearn的K近邻算法进行类别预测。

    原始iris数据集:

    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
    
    # 导入数据
    iris = load_iris()#导入iris数据集,是安德森鸢尾花卉数据集。iris_data是一个类似字典的对象。
    df = pd.DataFrame(iris.data, columns=iris.feature_names)#DataFrame生成二维数据表,列标为iris表的特征名
    df['label'] = iris.target#iris的每个样本都包含了品种信息,即目标属性(第5列,也叫target或label)
    df.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']#df的列标
    # data = np.array(df.iloc[:100, [0, 1, -1]])
    print(df)
    

    输出结果:
    sepal length sepal width petal length petal width label
    0 5.1 3.5 1.4 0.2 0
    1 4.9 3.0 1.4 0.2 0
    2 4.7 3.2 1.3 0.2 0
    3 4.6 3.1 1.5 0.2 0
    4 5.0 3.6 1.4 0.2 0
    5 5.4 3.9 1.7 0.4 0
    6 4.6 3.4 1.4 0.3 0
    7 5.0 3.4 1.5 0.2 0
    8 4.4 2.9 1.4 0.2 0
    9 4.9 3.1 1.5 0.1 0
    10 5.4 3.7 1.5 0.2 0
    11 4.8 3.4 1.6 0.2 0
    12 4.8 3.0 1.4 0.1 0
    13 4.3 3.0 1.1 0.1 0
    14 5.8 4.0 1.2 0.2 0
    15 5.7 4.4 1.5 0.4 0
    16 5.4 3.9 1.3 0.4 0
    17 5.1 3.5 1.4 0.3 0
    18 5.7 3.8 1.7 0.3 0
    19 5.1 3.8 1.5 0.3 0
    20 5.4 3.4 1.7 0.2 0
    21 5.1 3.7 1.5 0.4 0
    22 4.6 3.6 1.0 0.2 0
    23 5.1 3.3 1.7 0.5 0
    24 4.8 3.4 1.9 0.2 0
    25 5.0 3.0 1.6 0.2 0
    26 5.0 3.4 1.6 0.4 0
    27 5.2 3.5 1.5 0.2 0
    28 5.2 3.4 1.4 0.2 0
    29 4.7 3.2 1.6 0.2 0
    .. ... ... ... ... ...
    120 6.9 3.2 5.7 2.3 2
    121 5.6 2.8 4.9 2.0 2
    122 7.7 2.8 6.7 2.0 2
    123 6.3 2.7 4.9 1.8 2
    124 6.7 3.3 5.7 2.1 2
    125 7.2 3.2 6.0 1.8 2
    126 6.2 2.8 4.8 1.8 2
    127 6.1 3.0 4.9 1.8 2
    128 6.4 2.8 5.6 2.1 2
    129 7.2 3.0 5.8 1.6 2
    130 7.4 2.8 6.1 1.9 2
    131 7.9 3.8 6.4 2.0 2
    132 6.4 2.8 5.6 2.2 2
    133 6.3 2.8 5.1 1.5 2
    134 6.1 2.6 5.6 1.4 2
    135 7.7 3.0 6.1 2.3 2
    136 6.3 3.4 5.6 2.4 2
    137 6.4 3.1 5.5 1.8 2
    138 6.0 3.0 4.8 1.8 2
    139 6.9 3.1 5.4 2.1 2
    140 6.7 3.1 5.6 2.4 2
    141 6.9 3.1 5.1 2.3 2
    142 5.8 2.7 5.1 1.9 2
    143 6.8 3.2 5.9 2.3 2
    144 6.7 3.3 5.7 2.5 2
    145 6.7 3.0 5.2 2.3 2
    146 6.3 2.5 5.0 1.9 2
    147 6.5 3.0 5.2 2.0 2
    148 6.2 3.4 5.4 2.3 2
    149 5.9 3.0 5.1 1.8 2

    [150 rows x 5 columns]

    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()#添加图例
    plt.show()#显示图片
    
    

    输出:

    4.针对iris数据集,编制程序使用K近邻树进行类别预测。

    data = np.array(df.iloc[:100, [0, 1, -1]])
    #将列表list或元组tuple转换为ndarray数组,iloc函数只根据行列号对数据进行索引,行到100,列为0,1,-1
    X, y = data[:,:-1], data[:,-1]#x,y数据的取值。x取所有行,列到倒数第二列;y取所有行,列为最后一列
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
    '''
    train_test_split()是分离器函数,用于将数组或矩阵划分为训练集和测试集,
    函数样式为:X_train, X_test, y_train, y_test = train_test_split(train_data, train_target, test_size, random_state,shuffle)
    train_data:待划分的样本数据
    train_target:待划分的对应样本数据的样本标签
    test_size:1)浮点数,在0 ~ 1之间,表示样本占比(test_size = 0.3,则样本数据中有30%的数据作为测试数据,记入X_test,其余70%数据记入X_train,
     同时适用于样本标签); 2)整数,表示样本数据中有多少数据记入X_test中,其余数据记入X_train)
    '''
    
    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):#先取n_neighbers个点,放入空列表.
                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)):
                '''
                range()函数创建一个包含指定范围的元素的数组,
                再取剩下的n-n_neighbers个点,然后与n_neihbers个点比大小,将距离大的点更新出局,保证knn_list里面是距离小的点。
                '''
                max_index = knn_list.index(max(knn_list, key=lambda x: x[0]))#求原来表格里的最大值
                #knn_list为对象,key=lambda x: x[0] 为对前面的对象中的第一维数据的值进行求最大值。key=lambda 变量:变量[维数]
                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])#找出与X最邻近的n_neighbors个点
    
            #取出最后一列值(类别值),计算最邻近的n_neighbors个点多数属于某个类
            knn = [k[-1] for k in knn_list]
            count_pairs = Counter(knn)
    #         max_count = sorted(count_pairs, key=lambda x: x[-1])#以类别数最多的作为被分类的类别
              # 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): # score就是一个预测正确率
            right_count = 0
            n = 10
            for X, y in zip(X_test, y_test):
                label = self.predict(X)
                if label == y:#判断是否正确
                    right_count += 1#正确+1
            return right_count / len(X_test)#计算正确率
    
    clf = KNN(X_train, y_train)
    print(clf.score(X_test, y_test))#输出得分率
    

    输出:
    1.0

    test_point = [6.0, 3.0]
    print('Test Point: {}'.format(clf.predict(test_point)))#返回该点的预测标签
    

    输出:
    Test Point: 1.0

    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')#画[6.0, 3.0]
    plt.xlabel('sepal length')
    plt.ylabel('sepal width')
    plt.legend()
    plt.show()
    

    输出:

  • 相关阅读:
    【JAVA笔记——道】JAVA对象销毁
    【JAVA笔记——道】并发编程CAS算法
    httpClientUtil的get请求
    python基础 day11 下 ORM介绍 sqlalchemy安装 sqlalchemy基本使用 多外键关联 多对多关系 表结构设计作业
    python基础 day11 上 数据库介绍 mysql 数据库安装使用 mysql管理 mysql 数据类型 常用mysql命令 事务 索引 python 操作mysql ORM sqlachemy学习
    Python基础 Day10 Gevent协程 SelectPollEpoll异步IO与事件驱动 Python连接Mysql数据库操作 RabbitMQ队列 RedisMemcached缓存 Paramiko SSH Twsited网络框架
    python基础 day9 进程、与线程区别 python GIL全局解释器锁 线程 进程
    python基础 day8 Socket语法及相关 SocketServer实现多并发
    python基础 day7 面向对象高级语法部分 异常处理 异常处理 Socket开发基础
    python基础 day6 面向对象的特性:封装、继承、多态 类、方法、
  • 原文地址:https://www.cnblogs.com/zssb/p/14750101.html
Copyright © 2011-2022 走看看