关于本站
1、基于Django+Bootstrap开发
2、主要发表本人的技术原创博客
3、本站于 2015-12-01 开始建站
上次写了一篇k-近邻算法的简单分类使用方法,包括什么是k-近邻算法以及基本的实现代码。
这次看《机器学习实战》2.2的实例,讲解修正数据和如何计算错误率以及把数据图形化。
该实例大致意思是从约会网站获取数据,通过下面3个维度的数据:
1)每年获得的飞行常客里程数;
2)玩视频游戏所耗时间百分比;
3)每周消费的冰淇淋公升数。
把约会对象划分成3类:
1)不喜欢的人;
2)魅力一般的人;
3)极具魅力的人。
所需的数据我已经上传到百度网盘,大家下载即可:datingTestSet2.txt。
该文件数据如下图:
前3列对应3个维度的数据,第4列是划分的对应类别,一共1000条数据。当然,这1000条数据,只有部分数据会来拿作为训练集,部分用于测试错误率。
在上次k-近邻算法的简单分类文章中所创建的knn.py文件同个目录下,创建analysis.py文件并加入如下代码:
#coding:utf-8 import numpy as np from knn import classify0 #k-近邻算法
knn.py文件是包含k-近邻算法的代码,可以再次拿来使用。
《机器学习实战》书中从文件读取的代码过于复杂。我整理如下:
def get_data_from_file(file_path): ''' 从文件获取数据 file_path:文件地址 ''' dataset = [] labels = [] with open(file_path, 'r') as f: line = f.readline() #逐行读取文件,该方法可避免文件过大,用readlines读取导致内存不够 while line != '': line = line.strip() data = line.split('\t') #用tab拆分 #获取数据 dataset.append(map(float, data[:3])) labels.append(int(data[-1])) line = f.readline() #读取下一行 #把数据转成numpy的数组并返回 return np.array(dataset), labels
我们把上面网盘中的文件datingTestSet2.txt下载放到同个目录下。用如下方法获取数据:
if __name__ == '__main__': file_path = 'datingTestSet2.txt' dataset, labels = get_data_from_file(file_path)
《机器学习实战》书中,先讲画图,再讲修正数据。实际上,倒过来讲更好。
你仔细观察,可以发现3个维度的数据差异比较大。
其中第1列,也就是飞行常客里程数,该数据要比第2,3列的数据大很多。若直接用这些原始数据计算,会因为另外两个参数数据过小,导致计算出来的欧式距离包含这两个参数的成分变得很小。
那么我们需要修正数据,消除大数值的影响。如下代码修正:
def auto_norm(dataset): '''修正第1个参数的数值,消除大数值的影响(让数值范围变成0~1)''' #求第一维度的最大最小值 min_vals = dataset.min(axis = 0) max_vals = dataset.max(axis = 0) rng_vals = max_vals - min_vals #值域 #每个数值减去其所在列的最小值,再除以范围 m = dataset.shape[0] norm = dataset - np.tile(min_vals, (m, 1)) norm = norm/np.tile(rng_vals, (m,1)) #返回修正后的数值,值域,最小值 return norm, rng_vals, min_vals
numpy数组的特性和tile方法上一篇文章已经讲解,这里就不再赘述。
在刚刚的main代码修改如下:
if __name__ == '__main__': #获取数据 file_path = 'datingTestSet2.txt' dataset, labels = get_data_from_file(file_path) #修正数据 dataset_norm, rng_vals, min_vals = auto_norm(dataset)
我们得到这个dataset_norm之后,不直观也不容易分析。需要把数据图形化,方便观察。
绘制图表,需要安装matplotlib库。安装方法自行网上搜索。使用方法网上也大把,可以看看这个:
在《机器学习实战》书中绘制的是2D平面图表。而我们的数据是3维的,需要绘制一个3D图表。
先加入如下引用:
import matplotlib.pyplot as plt # 绘制图表 from mpl_toolkits.mplot3d import Axes3D #3D支持
再添加绘制3D图表方法:
def chart(dataset, labels): #二维散点图 """fig = plt.figure() ax = fig.add_subplot(111) ls = 15.*np.array(labels) ax.scatter(norm[:,0], norm[:,1], ls, ls) plt.show()""" #三维散点图 ls = 15.*np.array(labels) #创建一个三维的绘图工程 ax = plt.subplot(111, projection='3d') ax.scatter(dataset[:,0],dataset[:,1],dataset[:,2], ls,ls,ls) #设置坐标轴 ax.set_xlabel('sports') ax.set_ylabel('play') ax.set_zlabel('eat') plt.show()
绘制二维图表的代码也放在里面。再修改main部分的代码:
if __name__ == '__main__': #获取数据 file_path = 'datingTestSet2.txt' dataset, labels = get_data_from_file(file_path) #修正数据 dataset_norm, rng_vals, min_vals = auto_norm(dataset) #绘图 chart(dataset_norm, labels)
用python运行该文件,可看到下图:
可鼠标拖动和保存、调整等。不同颜色的点,代表不同类型的人。这样可以直观看出数据分布。
为了更好使用该模型,需要给出一个合理的k值。
随机训练集中的10%数据,用k-近邻算法得到对应的分类。再和实际的分类对比,看看是否正确。统计计算对应的错误率。
#测试数据(主要看k值) def test(dataset, labels, k): #测试方法,从dataset随机取出10%的数据。鉴于数据本身就具有随机性,直接去前10%即可 ratio = 0.1 m = dataset.shape[0] test_num = int(m*ratio) test_rng = dataset[test_num:m] test_label = labels[test_num:m] err_count = 0 for i in range(test_num): #通过k-近邻算法得到标签 result_label = classify0(dataset[i], test_rng, test_label, k)[0] #对比标签,错误累计 if result_label != labels[i]: err_count += 1 print("all:%s, test:%s. k=%s, err:%s, error ratio: %.2f" % (m, test_num, k, err_count, err_count/float(test_num)))
我们可以给定一些k值,计算。修改main部分的代码:
if __name__ == '__main__': #获取数据 file_path = 'datingTestSet2.txt' dataset, labels = get_data_from_file(file_path) #修正数据 dataset_norm, rng_vals, min_vals = auto_norm(dataset) #绘图 #chart(dataset_norm, labels) #计算错误率,获取最佳的k值 for k in range(3,11): test(norm, labels, k)
运行结果如下:
从图中可以看出,当k=4时,错误率最小。那我们可以确定k为4。
确定k之后,就可以拿这个算法来使用。如下代码,我给一些数据,判断该约会对象应该归入哪一类:
if __name__ == '__main__': #获取数据 file_path = 'datingTestSet2.txt' dataset, labels = get_data_from_file(file_path) #修正数据 dataset_norm, rng_vals, min_vals = auto_norm(dataset) #绘图 #chart(dataset_norm, labels) #计算错误率,获取最佳的k值 #for k in range(3,11): # test(norm, labels, k) #测试发现k=4时,错误率最低 k = 4 #使用算法 ff_miles = 10000 play_games = 10 ice_cream = 0.5 item = np.array([ff_miles, play_games, ice_cream]) norm_item = (item - min_vals)/rng_vals #消除特征 result = classify0(norm_item, norm, labels, k)[0] classes = [u'不喜欢的人', u'魅力一般的人', u'极具魅力的人'] print(u"飞行常客公里数:%s\n玩视频游戏时间占比:%s%%\n每周消费冰淇淋公斤数:%s\n为%s" % (ff_miles, play_games, ice_cream, classes[result-1]))
可以得到如下结果:
点击查看相关目录。
相关专题: 机器学习实战