zoukankan      html  css  js  c++  java
  • 实验二 K-近邻算法及应用

    博客班级 班级链接
    作业要求 作业要求链接
    学号 3180701115

    一、实验目的

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

    二、实验内容

    1、实现曼哈顿距离、欧氏距离、闵式距离算法,并测试算法正确性。
    2、实现K近邻树算法;
    3、针对iris数据集,应用sklearn的K近邻算法进行类别预测。
    4、 熟悉iris数据集,并能使用感知器算法对该数据集构建模型并应用。

    三、实验报告要求

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

    四、实验过程

    k*邻法
    k邻法是一种基本的分类与回归方法。k邻法的输入为实例的特征向量,对应于特征空间的点;输出为实例的类别,可以取多值。k邻法思想:1. 根据给定的距离度量方法,找出训练数据集中与实例x最相邻的k个点;2. 在k 个点中,根据分类决策规则,决定x 的类别。 k邻法中,当训练数据集、距离度量、k值、分类决策规则确定后,对于任何一个新的输入实例,它所属的类唯一的确定;这相当于将特征空间划分为一些子空间,确定子空间里每一个点所属的类。

    KNN算法的一般流程————

    1.收集数据:可以使用任何方法

    2.准备数据:距离计算所需要的数值,最后是结构化的数 据格式。

    3.分析数据:可以使用任何方法

    4.训练算法: (此步骤kNN)中不适用

    5.测试算法:计算错误率

    6.使用算法:首先需要输入样本数据和结构化的输出结果, 然后运行k-*邻算法判定输入数据分别属于哪个分类, 最后应用对计算出的分类执行后续的处理。

    import math
    #导入数学运算函数
    from itertools import combinations
    

    itertools模块是python的一个内置模块,它提供了非常有用的用于操作迭代对象的函数。
    Python的itertools库中提供了combinations方法可以轻松的实现排列组合。
    p = 1 曼哈顿距离
    p = 2 欧氏距离
    p = inf 闵式距离minkowski_distance

    #计算欧式距离
    def L(x, y, p=2):
    # x1 = [1, 1], x2 = [5,1]  在这里,实例是两个二维特征 x1 = [1, 1], x2 = [5,1]
        if len(x) == len(y) and len(x) > 1:
        # 当两个特征的维数相等时,并且维度大于1时。
            sum = 0
            # 目前总的损失函数值为0
            for i in range(len(x)): # 用range函数来遍历x所有的维度,x与y的维度相等。
                sum += math.pow(abs(x[i] - y[i]), p)
                # math.pow( x, y )函数是计算x的y次方。
            return math.pow(sum, 1/p)# 距离公式。
        else:
            return 0
    
    # 课本例3.1
    #数据准备
    x1 = [1, 1]
    x2 = [5, 1]
    x3 = [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())))
        print(min(zip(r.values(), r.keys())))
    

    结果:

    python实现,遍历所有数据点,找出n个距离最*的点的分类情况,少数服从多数

    import numpy as np
    import pandas as pd
    import matplotlib.pyplot as plt
    %matplotlib inline
    from sklearn.datasets import load_iris
    ##载入Fisher的鸢尾花数据
    from sklearn.model_selection import train_test_split
    from collections import Counter
    # data
    iris = load_iris()#中文名是安德森鸢尾花卉数据集
    df = pd.DataFrame(iris.data, columns=iris.feature_names)#是一个表格 
    #加入一列为分类标签
    df['label'] = iris.target# 表头字段就是key
    df.columns = ['sepal length', 'sepal width', 'petal length', 'petal width', 'label']
    # 选择其中的4个特征进行训练
    # data = np.array(df.iloc[:100, [0, 1, -1]])
    
    
    df
    #输出表格
    

    结果:

    #数据进行可视化
    #将标签为0、1的两种花,根据特征为长度和宽度打点表示
    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()
    

    结果:

    #取数据,并且分成训练和测试集合
    data = np.array(df.iloc[:100, [0, 1, -1]])
    #按行索引,取出第0列第1列和最后一列,即取出sepal长度、宽度和标签
    X, y = data[:,:-1], data[:,-1]
    #X为sepal length,sepal width y为标签 
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
    # train_test_split函数用于将矩阵随机划分为训练子集和测试子集
    #定义模型
    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个点,放入空的列表,列表中存放预测点与训练集点的距离及其对应标签
            # 取距离最小的k个点:先取前k个,然后遍历替换
            # knn_list存“距离”和“label”
            knn_list = []
            for i in range(self.n):
                #np.linalg.norm 求范数
                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_index = knn_list.index(max(knn_list, key=lambda x: x[0]))
                dist = np.linalg.norm(X - self.X_train[i], ord=self.p)
                #g更新最*邻中距离比当前点远的点
                if knn_list[max_index][0] > dist:
                    knn_list[max_index] = (dist, self.y_train[i])
            # 统计
            # 统计分类最多的点,确定预测数据的分类
            knn = [k[-1] for k in knn_list]
            #counter为计数器,按照标签计数
            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)
    

    max(num,key=lambda x: x[0])用法:
    x:x[]字母可以随意修改,求最大值方式按照中括号[]里面的维度,
    [0]按照第一维,
    [1]按照第二维

    clf = KNN(X_train, y_train)
    clf.score(X_test, y_test)
    

    #预测点
    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')
    plt.xlabel('sepal length')
    plt.ylabel('sepal width')
    plt.legend()
    

    结果:

    from sklearn.neighbors import KNeighborsClassifier
    clf_sk = KNeighborsClassifier()
    clf_sk.fit(X_train, y_train)
    

    结果:

    clf_sk.score(X_test, y_test)
    

    kd树

    # 建造kd树
    # kd-tree 每个结点中主要包含的数据如下:
    class KdNode(object):
        def __init__(self, dom_elt, split, left, right):
            self.dom_elt = dom_elt#结点的父结点
            self.split = split#划分结点
            self.left = left#做结点
            self.right = right#右结点
    
    class KdTree(object):
        def __init__(self, data):
            k = len(data[0])#数据维度
            #print("创建结点")
            #print("开始执行创建结点函数!!!")
            def CreateNode(split, data_set):
                #print(split,data_set)
                if not data_set:#数据集为空
                    return None
                #print("进入函数!!!")
                data_set.sort(key=lambda x:x[split])#开始找切分平面的维度
                #print("data_set:",data_set)
                split_pos = len(data_set)//2 #取得中位数点的坐标位置(求整)
                median = data_set[split_pos]
                split_next = (split+1) % k #(取余数)取得下一个节点的分离维数
                return KdNode(
                    median,
                    split,
                    CreateNode(split_next, data_set[:split_pos]),#创建左结点
                    CreateNode(split_next, data_set[split_pos+1:]))#创建右结点
            #print("结束创建结点函数!!!")
            self.root = CreateNode(0, data)#创建根结点
                
    #KDTree的前序遍历
    def preorder(root):
        print(root.dom_elt)
        if root.left:
            preorder(root.left)
        if root.right:
            preorder(root.right)
    # 遍历kd树
    #KDTree的前序遍历
    def preorder(root):
        print(root.dom_elt)
        if root.left:
            preorder(root.left)
        if root.right:
            preorder(root.right)
                   
    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)#表示数据的无
            
            nodes_visited = 1
            s = kd_node.split #数据维度分隔
            pivot = kd_node.dom_elt #切分根节点
            
            if target[s] <= pivot[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# 得到叶子结点,此时为nearest
            dist = temp1.nearest_dist #update distance
            
            nodes_visited += temp1.nodes_visited
            print("nodes_visited:", nodes_visited)
            if dist < max_dist:
                max_dist = dist
            
            temp_dist = abs(pivot[s]-target[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)))#计算目标点到邻近节点的Distance
            
            if temp_dist < dist:
                
                nearest = pivot #更新最近点
                dist = temp_dist #更新最近距离
                max_dist = dist #更新超球体的半径
                print("输出数据:" , nearest, dist, max_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"))  # 从根节点开始递归
    # 数据测试
    data= [[2,3],[5,4],[9,6],[4,7],[8,1],[7,2]]
    kd=KdTree(data)
    preorder(kd.root)
    

    结果:

    # 导包
    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)]
    # 输入数据进行测试
    ret = find_nearest(kd, [3,4.5])
    print (ret)
    

    结果:

    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)
    

    结果:

  • 相关阅读:
    Palindrome Partitioning
    triangle
    Populating Next Right Pointers in Each Node(I and II)
    分苹果(网易)
    Flatten Binary Tree to Linked List
    Construct Binary Tree from Inorder and Postorder Traversal(根据中序遍历和后序遍历构建二叉树)
    iOS系统navigationBar背景色,文字颜色处理
    登录,注销
    ios 文字上下滚动效果Demo
    经常崩溃就是数组字典引起的
  • 原文地址:https://www.cnblogs.com/pianyu666/p/14797550.html
Copyright © 2011-2022 走看看