机器学习实战_KNN(一)

【是什么】

KNN 即 k_近邻算法(k- nearest neighbor) ,就是寻找K个邻居作为该样本的特征,近朱者赤,近墨者黑,你的邻居是什么特征,那么就认为你也具备该特征核心公式为:

 

 

数据来源:https://github.com/apachecn/AiLearning/blob/master/data/2.KNN/datingTestSet2.txt

读取数据转换成矩阵

# 提取文件中的数据 转换成矩阵
def file2matric(filename):
    """
    disc:
    param: filename: 导入数据文本
    return: 数据矩阵
    """
    f = open(filename,'r',encoding= 'utf-8')
    # 获取文件的行数
    lines_list = f.readlines()
    num_of_lines = len(lines_list)
    # 创建存放标签的列表
    class_label_list = []
    # 生成对应的空矩阵 zeros(2,3) 就是生成2行3列的0矩阵
    returnMat = np.zeros((num_of_lines,3))
    # 将文本中的数据放到矩阵中
    for i in range(num_of_lines):
        lines = lines_list[i].strip().split('\t')
        # 将文本中的前3个数据放到矩阵中
        returnMat[i,:] = lines[0:3]
        # 将标签存到列表中
        class_label_list.append(int(lines[-1]))

    # print(returnMat)
    return returnMat, class_label_list

 

利用 matplotlib 绘制散点图

def DrawScatter(dataMat,label_list):
    # 导入中文字体,及字体大小
    zhfont = FontProperties(fname='C:/Windows/Fonts/simsun.ttc', size=14)
    # 绘制绘图窗口 2行2列
    fig,ax = plt.subplots(2,2,figsize=(13,8))
    # 不同标签赋予不同颜色
    label_color = []
    for i in label_list:
        if i == 1:
            label_color.append('black')
        elif i == 2:
            label_color.append('orange')
        elif i == 3:
            label_color.append('red')
    # 开始绘制散点图 设定散点尺寸与透明度
    scatter_size = 12
    scatter_alpha = 0.5
    # ===================散点图========================
    ax[0][0].scatter(dataMat[:,0], dataMat[:,1],color = label_color,s = scatter_size ,alpha = scatter_alpha)
    ax[0][1].scatter(dataMat[:, 1], dataMat[:, 2], color=label_color, s=scatter_size , alpha=scatter_alpha)
    ax[1][0].scatter(dataMat[:, 0], dataMat[:, 2], color=label_color, s=scatter_size , alpha=scatter_alpha)

    # 坐标轴标题
    title_list = ['每年获得的飞行常客里程数和玩视频游戏所消耗时间占比',
                  '每年获得的飞行常客里程数和每周消费的冰激淋公升数',
                  '玩视频游戏所消耗时间占比和每周消费的冰激淋公升数']
    x_name_list = ['每年获得的飞行常客里程数','玩视频游戏所消耗时间占比','每周消费的冰激淋公升数']
    y_name_list = ['玩视频游戏所消耗时间占比','每周消费的冰激淋公升数','每年获得的飞行常客里程数']
    #设置图例
    didntLike = mlines.Line2D([], [], color='black', marker='.',
                      markersize=6, label='didntLike')
    smallDoses = mlines.Line2D([], [], color='orange', marker='.',
                      markersize=6, label='smallDoses')
    largeDoses = mlines.Line2D([], [], color='red', marker='.',
                      markersize=6, label='largeDoses')

    p = 0
    for i in range(2):
        for j in range(2):
            if p > 2:
                break
            # 设置坐标轴名称和标题
            plt.setp(ax[i][j].set_title(u'%s'%(title_list[p]),FontProperties = zhfont),size=9, weight='bold', color='red')
            plt.setp(ax[i][j].set_xlabel(u'%s'%(x_name_list[p]),FontProperties = zhfont), size=7, weight='bold', color='black')
            plt.setp(ax[i][j].set_ylabel(u'%s'%(y_name_list[p]),FontProperties = zhfont), size=7, weight='bold', color='black')
            p+=1
    # 添加图例
    ax[0][0].legend(handles=[didntLike, smallDoses, largeDoses])
    ax[0][1].legend(handles=[didntLike, smallDoses, largeDoses])
    ax[1][0].legend(handles=[didntLike, smallDoses, largeDoses])

    plt.show()

 

 

 对数据进行归一化处理

由于不同数据的范围波动不同,在权重一样的情况下,需要进行归一化,即将数据转换成0-1之间

# 对矩阵进行归一化处理
def dataNorm(dataMat):
    """
    :param dataMat:
    :return: 归一化后的数据集
    归一化公式: Y = (X - Xmin)/(Xmax - Xmin)
    """
    # max(0) min(0) 求出每列的最大值和最小值
    d_min = dataMat.min(0)
    d_max = dataMat.max(0)
    # 计算极差
    d_ranges = d_max - d_min
    # 创建输出矩阵
    normDataSet = np.zeros(np.shape(dataMat))
    print(normDataSet)
    # 获得矩阵行数 .shape 获取矩阵的大小  3x3
    m = dataMat.shape[0]
    # 计算 (X - Xmin) 这部分 首先要创建Xmin矩阵 将d_min扩展到m行
    # 需要使用np.tile 函数进行扩展   将d_min扩展成m行1列 变成m x 3 矩阵
    normDataSet = dataMat - np.tile(d_min,(m,1))
    print(normDataSet)
    # 计算Y
    normDataSet = normDataSet / np.tile(d_ranges,(m,1))

    print(normDataSet)
    return normDataSet

创建分类函数与分类器(kNN算法的实现)

  (ps:每次都需要将测试数据与所有训练数据进行对比,感觉比较繁琐)

def classfy_fun(test_data, train_data, labels, k):
    """

    :param test_data: 测试集
    :param train_data: 训练集
    :param labels: 训练集标签
    :param k:  KNN 算法参数 选择距离最小的个数
    :return:  分类结果
    """
    # 计算训练集的矩阵行数
    train_size = train_data.shape[0]
    # 接下来按照欧氏距离进行元素距离计算 公式
    #     将测试集扩充成与训练集相同行数 求差
    diffMat = np.tile(test_data,(train_size,1)) - train_data
    # 将差值矩阵的每个元素平方
    sq_diffMat = diffMat**2
    # 差值平方矩阵每行元素相加 axis = 1 是按行相加
    sum_diffMat = sq_diffMat.sum(axis = 1)
    # 对新的求和矩阵进行开方 得到距离值
    distances = sum_diffMat ** 0.5
    # 获得距离值中从小到大值的索引
    sorted_distant = distances.argsort()
    # 定义一个字典 存放标签 与 出现的数量
    class_count = {}
    for i in range(k):
        # 找出前k个距离值最小的对应标签
        temp_label = labels[sorted_distant[i]]
        # 将标签作为 key 存放到字典中 出现次数作为 value
        class_count[temp_label] = class_count.get(temp_label,0) + 1
    # 将字典按照value 大小进行排序
    sort_class_count = sorted(class_count.items(),key = operator.itemgetter(1))
    return sort_class_count[0][0]
    pass
# 创建分类器函数
def dating_class_test():

    # 首先获取文件,将文件分成测试集和训练集
    dating_Mat, dating_label = file2matric('datingdata.txt')
    # 设置测试集的比例
    test_ratio = 0.1
    # 数据归一化
    normMat = dataNorm(dating_Mat)
    #获得矩阵的行数
    m = normMat.shape[0]
    # 计算测试集的数量
    numTestData = int(m * test_ratio)
    # 错误分类的数量
    error_count = 0.0

    for i in range(numTestData):
        class_result = classfy_fun(dating_Mat[i,:], dating_Mat[numTestData:m,:],
                                   dating_label[numTestData:m],4 )
        print("分类结果:%s,实际分类:%s"%(class_result,dating_label[i]))
        if class_result != dating_label[i]:
            error_count += 1
    # print("错误识别的数量:%f" %error_count)
    print("正确率:%f%% \n" %((1 - error_count / numTestData)*100))

 

 从结果看 识别率还是很低的,目前k值为4 ,可以改变k值看看正确率的变化

完整代码

  1 #!/usr/bin/python
  2 # -*- coding: UTF-8 -*-
  3 """
  4    【KNN 实战】
  5 
  6 """
  7 import  numpy as np
  8 import matplotlib.pyplot as plt
  9 import matplotlib.lines as mlines
 10 from matplotlib.font_manager import FontProperties
 11 import operator
 12 
 13 # 提取文件中的数据 转换成矩阵
 14 def file2matric(filename):
 15     """
 16     disc:
 17     param: filename: 导入数据文本
 18     return: 数据矩阵
 19     """
 20     f = open(filename,'r',encoding= 'utf-8')
 21     # 获取文件的行数
 22     lines_list = f.readlines()
 23     num_of_lines = len(lines_list)
 24     # 创建存放标签的列表
 25     class_label_list = []
 26     # 生成对应的空矩阵 zeros(2,3) 就是生成2行3列的0矩阵
 27     returnMat = np.zeros((num_of_lines,3))
 28     # 将文本中的数据放到矩阵中
 29     for i in range(num_of_lines):
 30         lines = lines_list[i].strip().split('\t')
 31         # 将文本中的前3个数据放到矩阵中
 32         returnMat[i,:] = lines[0:3]
 33         # 将标签存到列表中
 34         class_label_list.append(int(lines[-1]))
 35 
 36     # print(returnMat)
 37     return returnMat, class_label_list
 38 
 39 def DrawScatter(dataMat,label_list):
 40     # 导入中文字体,及字体大小
 41     zhfont = FontProperties(fname='C:/Windows/Fonts/simsun.ttc', size=14)
 42     # 绘制绘图窗口 2行2列
 43     fig,ax = plt.subplots(2,2,figsize=(13,8))
 44     # 不同标签赋予不同颜色
 45     label_color = []
 46     for i in label_list:
 47         if i == 1:
 48             label_color.append('black')
 49         elif i == 2:
 50             label_color.append('orange')
 51         elif i == 3:
 52             label_color.append('red')
 53     # 开始绘制散点图 设定散点尺寸与透明度
 54     scatter_size = 12
 55     scatter_alpha = 0.5
 56     # ===================散点图========================
 57     ax[0][0].scatter(dataMat[:,0], dataMat[:,1],color = label_color,s = scatter_size ,alpha = scatter_alpha)
 58     ax[0][1].scatter(dataMat[:, 1], dataMat[:, 2], color=label_color, s=scatter_size , alpha=scatter_alpha)
 59     ax[1][0].scatter(dataMat[:, 0], dataMat[:, 2], color=label_color, s=scatter_size , alpha=scatter_alpha)
 60 
 61     # 坐标轴标题
 62     title_list = ['每年获得的飞行常客里程数和玩视频游戏所消耗时间占比',
 63                   '每年获得的飞行常客里程数和每周消费的冰激淋公升数',
 64                   '玩视频游戏所消耗时间占比和每周消费的冰激淋公升数']
 65     x_name_list = ['每年获得的飞行常客里程数','玩视频游戏所消耗时间占比','每周消费的冰激淋公升数']
 66     y_name_list = ['玩视频游戏所消耗时间占比','每周消费的冰激淋公升数','每年获得的飞行常客里程数']
 67     #设置图例
 68     didntLike = mlines.Line2D([], [], color='black', marker='.',
 69                       markersize=6, label='didntLike')
 70     smallDoses = mlines.Line2D([], [], color='orange', marker='.',
 71                       markersize=6, label='smallDoses')
 72     largeDoses = mlines.Line2D([], [], color='red', marker='.',
 73                       markersize=6, label='largeDoses')
 74 
 75     p = 0
 76     for i in range(2):
 77         for j in range(2):
 78             if p > 2:
 79                 break
 80             # 设置坐标轴名称和标题
 81             plt.setp(ax[i][j].set_title(u'%s'%(title_list[p]),FontProperties = zhfont),size=9, weight='bold', color='red')
 82             plt.setp(ax[i][j].set_xlabel(u'%s'%(x_name_list[p]),FontProperties = zhfont), size=7, weight='bold', color='black')
 83             plt.setp(ax[i][j].set_ylabel(u'%s'%(y_name_list[p]),FontProperties = zhfont), size=7, weight='bold', color='black')
 84             p+=1
 85     # 添加图例
 86     ax[0][0].legend(handles=[didntLike, smallDoses, largeDoses])
 87     ax[0][1].legend(handles=[didntLike, smallDoses, largeDoses])
 88     ax[1][0].legend(handles=[didntLike, smallDoses, largeDoses])
 89     plt.savefig('.\\123.png', bbox_inches='tight')
 90     plt.show()
 91 
 92 # 对矩阵进行归一化处理
 93 def dataNorm(dataMat):
 94     """
 95     :param dataMat:
 96     :return: 归一化后的数据集
 97     归一化公式: Y = (X - Xmin)/(Xmax - Xmin)
 98     """
 99     # max(0) min(0) 求出每列的最大值和最小值
100     d_min = dataMat.min(0)
101     d_max = dataMat.max(0)
102     # 计算极差
103     d_ranges = d_max - d_min
104     # 创建输出矩阵
105     normDataSet = np.zeros(np.shape(dataMat))
106     print(normDataSet)
107     # 获得矩阵行数 .shape 获取矩阵的大小  3x3
108     m = dataMat.shape[0]
109     # 计算 (X - Xmin) 这部分 首先要创建Xmin矩阵 将d_min扩展到m行
110     # 需要使用np.tile 函数进行扩展   将d_min扩展成m行1列 变成m x 3 矩阵
111     normDataSet = dataMat - np.tile(d_min,(m,1))
112     print(normDataSet)
113     # 计算Y
114     normDataSet = normDataSet / np.tile(d_ranges,(m,1))
115 
116     print(normDataSet)
117     return normDataSet
118 
119 def classfy_fun(test_data, train_data, labels, k):
120     """
121 
122     :param test_data: 测试集
123     :param train_data: 训练集
124     :param labels: 训练集标签
125     :param k:  KNN 算法参数 选择距离最小的个数
126     :return:  分类结果
127     """
128     # 计算训练集的矩阵行数
129     train_size = train_data.shape[0]
130     # 接下来按照欧氏距离进行元素距离计算 公式
131     #     将测试集扩充成与训练集相同行数 求差
132     diffMat = np.tile(test_data,(train_size,1)) - train_data
133     # 将差值矩阵的每个元素平方
134     sq_diffMat = diffMat**2
135     # 差值平方矩阵每行元素相加 axis = 1 是按行相加
136     sum_diffMat = sq_diffMat.sum(axis = 1)
137     # 对新的求和矩阵进行开方 得到距离值
138     distances = sum_diffMat ** 0.5
139     # 获得距离值中从小到大值的索引
140     sorted_distant = distances.argsort()
141     # 定义一个字典 存放标签 与 出现的数量
142     class_count = {}
143     for i in range(k):
144         # 找出前k个距离值最小的对应标签
145         temp_label = labels[sorted_distant[i]]
146         # 将标签作为 key 存放到字典中 出现次数作为 value
147         class_count[temp_label] = class_count.get(temp_label,0) + 1
148     # 将字典按照value 大小进行排序
149     sort_class_count = sorted(class_count.items(),key = operator.itemgetter(1))
150     return sort_class_count[0][0]
151     pass
152 # 创建分类器函数
153 def dating_class_test():
154 
155     # 首先获取文件,将文件分成测试集和训练集
156     dating_Mat, dating_label = file2matric('datingdata.txt')
157     # 设置测试集的比例
158     test_ratio = 0.1
159     # 数据归一化
160     normMat = dataNorm(dating_Mat)
161     #获得矩阵的行数
162     m = normMat.shape[0]
163     # 计算测试集的数量
164     numTestData = int(m * test_ratio)
165     # 错误分类的数量
166     error_count = 0.0
167 
168     for i in range(numTestData):
169         class_result = classfy_fun(dating_Mat[i,:], dating_Mat[numTestData:m,:],
170                                    dating_label[numTestData:m],4 )
171         print("分类结果:%s,实际分类:%s"%(class_result,dating_label[i]))
172         if class_result != dating_label[i]:
173             error_count += 1
174     # print("错误识别的数量:%f" %error_count)
175     print("正确率:%f%% \n" %((1 - error_count / numTestData)*100))
176 
177 def main():
178     # reMat, label = file2matric('datingdata.txt')
179     # DrawScatter(reMat,label )
180     # dataNorm(reMat)
181     # 测试分类情况
182     dating_class_test()
183     pass
184 
185 
186 if __name__ =='__main__':
187     main()
View Code

 

posted on 2019-09-30 15:00  wangxiaobei2019  阅读(...)  评论(... 编辑 收藏

导航