实验目的
最近打算系统性的开始学习机器学习,买了几本书,也找了很多练手的东西,这个系列算是记录下自己的学习过程吧,从最基本的KNN算法开始;
实验介绍
语言: Python
GitHub地址:luuuyi/Knn
实验步骤
1)原理介绍
k近邻算法是一种基本分类和回归方法。k近邻算法:即是给定一个训练数据集,对新的输入实例,在训练数据集中找到与该实例最邻近的k个实例,这k个实例的多数属于某个类,就把该输入实例分类到这个类中。
举一个例子,大家看下图:
-
如果k=3,绿色圆点的最邻近的3个点是2个红色小三角形和1个蓝色小正方形,少数从属于多数,基于统计的方法,判定绿色的这个待分类点属于红色的三角形一类。
-
如果k=5,绿色圆点的最邻近的5个邻居是2个红色三角形和3个蓝色的正方形,还是少数从属于多数,基于统计的方法,判定绿色的这个待分类点属于蓝色的正方形一类。
2)简单实现
看下面两个函数:
def createTmpData():
group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
labels = ['A','A','B','B']
return group, labels
def classify0(in_data,datas,labels,k):
datas_height = datas.shape[0]
diff_array = tile(in_data,[datas_height,1]) - datas
diff_array_power2 = diff_array**2
distance_array = diff_array_power2.sum(axis=1)
distance_array = distance_array**0.5
sorted_index = distance_array.argsort()
labels_dict = {}
for i in range(k):
label = labels[sorted_index[i]]
labels_dict[label] = labels_dict.get(label,0) + 1
sorted_datas = sorted(labels_dict.iteritems(),key=operator.itemgetter(1),reverse=True)
return sorted_datas[0][0]
第一个函数很简单的生成了一个临时的测试样本,第二个函数就是一个精简的KNN算法的核心部分,输入有待测试的数据和样本数据集,还有最重要的k值,这里的距离计算为欧式距离,简单来说就是以前 根号(x的平方加上有的平方), 这里要主要的是为了简化算法的理解和实现,只选取了样本的二维特征,但是在实际应用中对象的特征可能远远大于二维。
3)实验应用
再简单实现了算法之后,我们拿到一个约会信息的数据集,数据集主要包含了三个人物的特征(每年飞行距离,每天花在娱乐上的时间百分比,每年的冰淇淋消耗分量),并且给出这个人的标签(分别为值得约会,一般,不值得约会):
对于数据的处理,可以先读取到内存之后进行预处理,比如说上述的数据,大家可以看到在每年的飞行距离这个特征中,其数量级是完爆其他两个特征的,也就是说该特征对于结果的影响是极大于其他两个特征的,这不符合KNN算法的思想,所以读取数据之后需要做一个归一化处理:
def autoNormal(datas):
height = datas.shape[0]
max_array = datas.max(0)
min_array = datas.min(0)
range_array = max_array - min_array
normaled_data = (datas-tile(min_array,(height,1)))/tile(range_array,(height,1))
return normaled_data, range_array, min_array
处理完毕之后得益于Python语言强大的工具库,我们可以用matplotlib库来可视化一下数据(选取其中两个):
def drawImage(datas,labels):
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(datas[:,1],datas[:,2],5.0*array(labels),15.0*array(labels))
plt.show()
三种颜色分别代表的是不喜欢,一般,喜欢,横纵坐标就分别是其中两个特征。
结合刚才的核心算法的实现,我们选择90%的数据用来生成数据模型,然后用剩余的10%去进行测试,最终统计下模型的正确分类能力,也就是错误率为多少,这里选择的评估方法为0-1损失,测试误差即绝对的错误率。
def datingClassTest():
datas, labels = loadDataFromFile('datingTestSet2.txt')
height = datas.shape[0]
ratio = 0.1
test_nums = int(0.1*height)
normaled_datas, range_array, min_array = autoNormal(datas)
test_datas = normaled_datas[0:test_nums,:]
error_count = 0.0
for i in range(test_nums):
result_label = classify0(test_datas[i],normaled_datas[test_nums:,:],labels[test_nums:],3)
print "The class result is: %d, the real answer is %d" % (result_label,labels[i])
if(result_label != labels[i]): error_count += 1.0
print "The Total error rate is: %f" % (error_count/int(0.1*height))
执行完毕之后可以看一下输出:
百分之5的错误率,正确分类的能力已经超过了90%,还能接受,不断地调整参数k可以观察错误率的不同,最终选择一个最为合适的参数。
通过自己手动输入参数以后去评估:
def datingClassPerson():
result_labels = ['not like', 'a little like', 'like']
percents_of_play = float(raw_input('Enter your percents_of_play:'))
fly_distance = float(raw_input('Enter your fly_distance:'))
ice = float(raw_input('Enter your ice:'))
datas, labels = loadDataFromFile('datingTestSet2.txt')
normaled_datas, range_array, min_array = autoNormal(datas)
test_data = array([fly_distance,percents_of_play,ice])
classed_label = classify0((test_data-min_array)/range_array,normaled_datas,labels,3)
print "The Result is: %s" % (result_labels[classed_label-1])
结果为:
4)注意事项
关于k近邻的k值我们应该怎么选取呢。
请看下面这张图:
上图中有俩类,一个是黑色的圆点,一个是蓝色的长方形,现在我们的待分类点是红色的五边形。
如果选取k=1,我们可以看到,图中离红色样本最近的是黑色样本,根据KNN的计算方法,会将样本直接分类到黑色的数据集中;因此,在k值很小的时候,我们很容易学习到噪声,也就非常容易判定为噪声类别;当k值为中间值时,此刻大部分在范围内的样本都为蓝色,即将样本分类为蓝色;但是k的增大并不意味着模型变得可靠,当k的值为N,也即是k=N的时候:
可以感受到这个时候被分类点的最终结果其实就是很单纯的样本集的最大数量样本,这样的分类结果也不可靠。
所以在KNN算法中,对于k值得选取,通常采取交叉验证法来选取最优的k值,反复使用样本数据,对样本数据进行切片之后训练验证。
参考: