【机器学习实战-2章】KNN算法

爱情电影亲吻镜头更多,动作电影打斗场面更多
优点:精度高、对异常值不敏感、无数据输入假定
缺点:计算复杂度高、空间复杂度高
使用数据范围:数值型和标称型
KNN:对于要预测的点,将其放到数据集中去,距这个点最近的K个点的类别是已知的,要预测点的类别就是这K个类别中占比例最大的类别。

工作原理:

  1. 存在一个样本数据集合,也称训练样本集,并且样本集中的每个数据都存在标签,即知道样本集中每一数据所属分类的对应的关系。

  2. 输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似(最邻近)的分类标签。一般来说,选择样本数据集中前k各最相似的数据。

  3. K-邻近算法一般流程
    1.收集数据:可以使用任何方法
    2.准备数据:距离计算所需要的数值,最好是结构化的数据格式
    3.分析数据
    4.训练算法:此步骤不适用于KNN算法
    5.测试数据:计算错误率
    6.使用算法:首先需要输入样本数据和结构化的输出结果,然后运行KNN判定输入数据分别属于哪个分类,最后应用对计算出的分类执行后续的处理

KNN算法的缺点

1.算法必须保存全部数据集,如果训练集很大,必须使用大量的存储空间
2.由于对数据集中的每个数据计算距离值,实际使用可能非常耗时
3.它无法给出任何数据的基础结构信息,因此我们无法知晓平均实例样本和点行实例样本具有什么特征。
from numpy import * #科学计算包
import operator #运算符模块
import logging

def createDataSet():
    """创建数据集和标签"""
    group = array([[1.0,1.1],[1.0,0.5],[0,0],[0,0.1]])
    #向量labels包含了每个数据点的标签信息,包含的元素个数为group矩阵的行数
    labels = ['A','C','B','B']
    return group,labels
def classify0(inX,dataset,labels,k):
    """KNN算法
    inX:用于分类的输入向量
    dataset:输入的训练样本集
    labels:标签向量
    k:表示用于选择最近邻居的数目
    1.计算已知类别数据集中的点与当前点之间的距离
    2.按照距离递增次序排序
    3.选取与当前点距离最小的k个点
    4.确定前K个点所在类别的出现的概率
    5.返回前k个点出现频率最高的类别作为当前点的预测类型
    """
    data_set_size = dataset.shape[0]    #获取矩阵的行数
    diff_mat = tile(inX,(data_set_size,1))-dataset  #np.tile将测试数据转换为与训练数据一样的形状并相减
    sq_diff_mat = diff_mat**2
    sq_distance = sq_diff_mat.sum(axis=1)   #np.sum(axis=1)按行相加,不保持其维度
    distances = sq_distance**0.5    #计算欧几里得距离
    sorted_dist_indicies = distances.argsort()#np.argsort()对大小进行排序,并返回元素原来所在位置的索引
    class_count = {}
    for i in range(k):
        vote_label = labels[sorted_dist_indicies[i]]
        class_count[vote_label] = class_count.get(vote_label,0) +1
    #将class_count字典分解为元组列表,然后使用itemgetter方法,按照第二个元素的次序对元组进行排序
    sorted_class_count = sorted(
        class_count.items(),
        key=operator.itemgetter(1),
        reverse=True
    )   
    return sorted_class_count[0][0]
logger = logging.getLogger()
fh = logging.FileHandler('./KNN.log',encoding='utf-8')
ch = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s-%(name)s-%(message)s')
fh.setLevel(logging.INFO)
fh.setFormatter(formatter)
ch.setFormatter(formatter)
# try:
group,labels = createDataSet()
result = classify0([1.0,0.0],group,labels,2)
print(result)
# except Exception as e:
#     print("未知错误")
logging.info("debug message")
#获取矩阵形状
# print(group.shape)
#获取矩阵的行数
# print(group.shape[0])

约会网站行为预测

import numpy as np
import matplotlib.pyplot as plt
import operator
class KNN_on_dateing(object):
    """
    约会网站喜好预测
    如何将数据改造成分类器可以使用的特征值。
    在约会网站上使用kNN算法
    1.收集数据:提供文本文件
    2.准备数据:使用python解析文本文件
        归一化处理:不同的数据尺度不一样,带来的影响也不同,old_value = (old_value - min)/(max - min)可以将任意取值范围的特征值转化为0到1区间内的值。其中Min和max分别为数据集中的最小特征值和最大特征值
    3.分析数据:使用matplotlib画二维扩散图
    4.训练算法:KNN算法不需要训练
    5.测试算法:使用海伦提供的部分数据作为测试样本
        通常使用训练数据的90%作为训练数据,10%作为测试数据,检测分类器的正确率。注意:10%的数据应该是随机选择的。
        测试样本和非测试样本的区别在于:测试样本是已经完成分类的数据,如果预测分类与实际分类不同,则标记为一个错误
    6.使用算法:产生简单的命令行程序,然后海伦可以输入一些特征数据以判断对方是否为自己喜欢的类型
    """
    pass
def file2matrix(filename):
    """转换文本格式为Numpy数组"""
    fr = open(filename)
    array_oflines = fr.readlines()
    number_oflines = len(array_oflines)
    #40920	8.326976	0.953952	largeDoses
    return_mat = np.zeros((number_oflines,3))
    class_label_vector_string = []
    class_label_vector = []
    index = 0
    for line in array_oflines:
        line = line.strip() #截掉所有回车字符
        list_formline = line.split('\t')
        return_mat[index,:] = list_formline[0:3]    #赋值给第index行所有数据
        #class_label_vector.append(int(list_formline[-1],base=32))   #int()将字符串转换为整数,将label转换为整数
        class_label_vector_string.append(list_formline[-1])
        index +=1
    for label in class_label_vector_string: #将label标签转换为整型数字
        if label == 'largeDoses':
            label = 1
            class_label_vector.append(label)
        elif label == 'smallDoses':
            label = 2
            class_label_vector.append(label)
        else:
            label = 3
            class_label_vector.append(label)
    return return_mat,class_label_vector
# dataset,label_vactor = file2matrix('./datingTestSet.txt')
# print(dataset)
# print(label_vactor)
def plot(x,y,label=None):
    fig = plt.figure()
    ax = fig.add_subplot(111)
    #使用label标签对数据进行区别显示
    ax.scatter(x,y,15.0*np.array(label),15.0*np.array(label))
    plt.show()
# x,y = dataset[:,0],dataset[:,1]
# plot(x,y,label_vactor)
def autoNorm(dataset):
    """对数据集进行归一化处理,提高数据处理的效率\n
    return norm_dataset,ranges,min_vals
    """
    min_vals = dataset.min(0)   #获取每列中的最小值1X3矩阵
    print("最小值矩阵的形状是",min_vals.shape)
    max_vals = dataset.max(0)
    ranges = max_vals - min_vals
    norm_dataset = np.zeros(np.shape(dataset))
    m = dataset.shape[0]    #获取矩阵的行数
    norm_dataset = dataset - np.tile(min_vals,(m,1))
    norm_dataset = norm_dataset/np.tile(ranges,(m,1))   #在numpy数组中,矩阵中的每个元素对应做数值运算,/,+,-,x.
    #如果需要矩阵除法,使用函数linalg.solve(matA,matB)
    return norm_dataset,ranges,min_vals
# normilized_dataset,ranges,min_vals,norm_label_vactor = auto_norm(dataset,label_vactor)
# print(normilized_dataset)
def classify0(inX,dataset,labels,k):
        """KNN算法
        inX:用于分类的输入向量
        dataset:输入的训练样本集
        labels:标签向量
        k:表示用于选择最近邻居的数目
        1.计算已知类别数据集中的点与当前点之间的距离
        2.按照距离递增次序排序
        3.选取与当前点距离最小的k个点
        4.确定前K个点所在类别的出现的概率
        5.返回前k个点出现频率最高的类别作为当前点的预测类型
        """
        data_set_size = dataset.shape[0]    #获取矩阵的行数
        diff_mat = np.tile(inX,(data_set_size,1))-dataset  #np.tile将测试数据转换为与训练数据一样的形状并相减
        sq_diff_mat = diff_mat**2
        sq_distance = sq_diff_mat.sum(axis=1)   #np.sum(axis=1)按行相加,不保持其维度
        distances = sq_distance**0.5    #计算欧几里得距离
        sorted_dist_indicies = distances.argsort()#np.argsort()对大小进行排序,并返回元素原来所在位置的索引
        class_count = {}
        for i in range(k):
            #取出排名前k个的特征的label标签
            vote_label = labels[sorted_dist_indicies[i]] 
            #计算前k个label中不同label的的个数,取个数最多的为预测数据的label
            class_count[vote_label] = class_count.get(vote_label,0) +1  #Return the value for key if key is in the dictionary, else default.
        #将class_count字典分解为元组列表,然后使用itemgetter方法,按照第二个元素的次序对元组进行排序
        sorted_class_count = sorted(
            class_count.items(),
            key=operator.itemgetter(1),
            reverse=True
        )   
        return sorted_class_count[0][0]
def datingClassTest():
    """测试分类器效果,最后得到合适的训练集大小"""
    ho_ratios = [0.02,0.05,0.10,0.15]
    error_ratios = []
    for ho_ratio in ho_ratios:
        #对KNN来说,训练集越大,测试结果准确率越高
        dating_data_mat,dating_labels = file2matrix('./datingTestSet.txt')
        norm_mat,ranges,min_val = autoNorm(dating_data_mat)
        m = norm_mat.shape[0]
        num_test_vecs = int(m*ho_ratio) #用以区分测试数据集和训练数据集
        error_count = 0.0
        for i in range(num_test_vecs):
            """i->num_test_vecs 测试集,num_test_vects->m(norm_data_shape[0])训练集"""
            classifier_result = classify0(norm_mat[i,:],norm_mat[num_test_vecs:m,:],dating_labels[num_test_vecs:m],3)
            print("the classifier came back with:%d, the real answer is: %d" % (classifier_result,dating_labels[i]))
            if (classifier_result != dating_labels[i]): error_count +=1.0
        error_ratio = error_count/float(num_test_vecs)
        error_ratios.append(error_ratio)
        print("the ho_ratio is %f,the total error rate is:%f" % (ho_ratio,error_ratio))
    return ho_ratios,error_ratios
# ho_ratios,error_ratios = datingClassTest()
# #可视化错误率与测试集大小之间的关系
# plt.plot(ho_ratios,error_ratios)
# plt.xlabel('ho_ratio')
# plt.ylabel('error_ratio')
# plt.show()
def classifyPerson():
    """约会网站预测函数"""
    result_list = ['not at all','in small doses','in large doses']
    percent_tats = float(input('percentage of time spent playing video games?'))
    ff_miles = float(input('frequent flier miles earned per year?'))
    ice_Cream = float(input('liters of ice cream consumed per year?'))
    dating_data_mat,dating_labels = file2matrix('./datingTestSet.txt')
    normMat,ranges,min_val = autoNorm(dating_data_mat)
    inArr = np.array([ff_miles,percent_tats,ice_Cream])
    classifier_result = classify0((inArr-min_val)/ranges,normMat,dating_labels,3)
    print("You will probably like the person:",result_list[classifier_result-1])
classifyPerson()

手写体数字识别

from PIL import Image
import numpy as np
import os
import operator
def introduction():
    """
    KNN算法实现手写体识别
    1.收集数据:提供文本文件
    2.准备数据:编写函数classify0(),将图像转换为分类器使用的list格式
    3.分析数据:在python命令提示符中检查数据,确保它符合要求
    4.训练算法:此步骤不适用于k-邻近算法
    5.测试算法:编写函数使用提供的部分数据集作为测试样本,测试样本与非测试样本的区别在于测试样本是已经完成分类的数据,如果测试分类与实际类别不同,则标记为一个错误
    6.使用算法:可从图像中提取数据,并完成数字识别。
    """
    pass
def image2txt():
    """收集数据将图片转化为txt格式
    调整图片大小32X32
    """
    img=Image.open("G:/PM/data_analyse/testdata/7.png")
    img_to_txt=open("G:/PM/data_analyse/testdata/7.txt","a")
    #img.save("G:/PM/netcrawler_learning/7.png")#保存图片
    width=img.size[0]
    height=img.size[1]
    #k=img.getpixel((1,9))#获取像素点(1,9)的颜色
    #RGB:(0,0,0)black;(255,255,255)white
    #print(k)
    for i in range(0,height):#高在外层循环
        for j in range(0,width):
            clor=img.getpixel((j,i))#注意genpixel参数顺序与循环的匹配(宽,高)
            clorall=clor[0]+clor[1]+clor[2]
            if clorall==0:
                img_to_txt.write("1")#若为黑色,写入1
            else:
                img_to_txt.write("0")
        img_to_txt.write("\n")#换行
    img_to_txt.close()
def img2vector(filename):
    """准备数据:将图片转换为向量,循环读出文件的前32行,并将每行的头32个字符值存储在Numpy数组中"""
    return_vect = np.zeros((1,1024))
    fr = open(filename)
    for i in range(32):
        line_str = fr.readline()
        for j in range(32):
            return_vect[0,32*i+j] = int(line_str[j])
    return return_vect
def classify0(inX,dataset,labels,k):
    """KNN算法
    inX:用于分类的输入向量
    dataset:输入的训练样本集
    labels:标签向量
    k:表示用于选择最近邻居的数目
    1.计算已知类别数据集中的点与当前点之间的距离
    2.按照距离递增次序排序
    3.选取与当前点距离最小的k个点
    4.确定前K个点所在类别的出现的概率
    5.返回前k个点出现频率最高的类别作为当前点的预测类型
    """
    data_set_size = dataset.shape[0]    #获取矩阵的行数
    diff_mat = np.tile(inX,(data_set_size,1))-dataset  #np.tile将测试数据转换为与训练数据一样的形状并相减
    sq_diff_mat = diff_mat**2
    sq_distance = sq_diff_mat.sum(axis=1)   #np.sum(axis=1)按行相加,不保持其维度
    distances = sq_distance**0.5    #计算欧几里得距离
    sorted_dist_indicies = distances.argsort()#np.argsort()对大小进行排序,并返回元素原来所在位置的索引
    class_count = {}
    for i in range(k):
        vote_label = labels[sorted_dist_indicies[i]]
        class_count[vote_label] = class_count.get(vote_label,0) +1
    #将class_count字典分解为元组列表,然后使用itemgetter方法,按照第二个元素的次序对元组进行排序
    sorted_class_count = sorted(
        class_count.items(),
        key=operator.itemgetter(1),
        reverse=True
    )   
    return sorted_class_count[0][0]
def handwritingClassTest():
    """手写数字识别"""
    hw_labels = []
    training_filelist = os.listdir('./M_02_KNN_digits/trainingDigits')
    m = len(training_filelist)
    training_mat = np.zeros((m,1024))
    for i in range(m):
        #获取label
        filename_str = training_filelist[i]
        file_str = filename_str.split('.')[0]
        classnum_str = int(file_str.split('_')[0])
        hw_labels.append(classnum_str)
        #获取文本信息
        training_mat[i,:] = img2vector('./M_02_KNN_digits/trainingDigits/%s' % filename_str)
    testfile_list = os.listdir('./M_02_KNN_digits/testDigits')
    error_count = 0.0
    mTest = len(testfile_list)
    for i in range(mTest):
        filename_str = testfile_list[i]
        file_str = filename_str.split('.')[0]
        classnum_str = int(file_str.split('_')[0])
        vector_of_test = img2vector('./M_02_KNN_digits/testDigits/%s' % filename_str)
        classifier_result = classify0(vector_of_test,training_mat,hw_labels,3)
        print("the classifier came back with:%d,the real answer is:%d" % (classifier_result,classnum_str))
        if (classifier_result != classnum_str): error_count += 1.0
    print("\nthe total number of errors is:%d" % error_count)
    print("\nthe total error rate is:%f" % (error_count/float(mTest)))
handwritingClassTest()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值