问题
为了提高我的B格,我打算买瓶红酒尝尝。但是我对红酒一窍不通,不知道该如何鉴别红酒质量的好坏。于是突发奇想,能否使用K-近邻算法来帮助我选择呢?
数据准备
我在网上下载了红酒的数据集。该数据集收集了红酒的11种特征及专家对每种酒的评价。这是数据的下载地址,下面列举了部分样本数据:
|序号|非挥发性酸度|挥发性酸度|柠檬酸|残留糖|氯化物|游离二氧化硫|总二氧化硫|浓度|PH值|硫酸盐|酒精度|质量|
|---|
|1|7|0.27|0.36|20.7|0.045|45|170|1.001|3|0.45|8.8|6|
|2|6.3|0.3|0.34|1.6|0.049|14|132|0.994|3.3|0.49|9.5|6|
|3|7.9|0.18|0.37|1.2|0.04|16|75|0.992|3.18|0.63|10.8|5|
数据的处理一共包括两个步骤:读取数据及数据预处理。读取数据部分将数据分成红酒数据部分及标签部分。数据预处理主要是对数据部分进行归一化处理。代码如下:
import numpy as np
import csv
def generateData(filename):
data = []
with open(filename, 'r') as csvfile:
reader = csv.reader(csvfile, delimiter=';')
data = [row for row in reader]
data = data[1:]
data = np.array(data)
label = data[:,-1]
data = data[:,:-1]
data = data.astype(np.float)
label = label.astype(np.float)
#normlize to [0,1]
minVals = data.min(0)
maxVals = data.max(0)
data = (data-minVals)/(maxVals-minVals)
return data, label
K-近邻算法
下面来写K-近邻算法的核心算法,算法的流程大概如下:首先计算测试样本与所有训练样本之间的距离,选出距离最近的K个样本,将这K个样本中标签最多的标签作为这个测试样本的标签,代码如下:
import numpy as np
import csv
import operator
def knnclassify(testData, trainData, trainLabel,k):
diff = trainData - testData
diffSq = diff**2
distances = np.sqrt(np.sum(diffSq, axis = 1))
sortedDistdicies = distances.argsort()
classCount = {}
for i in range(k):
votedLabel = trainLabel[sortedDistdicies[i]]
classCount[votedLabel] = classCount.get(votedLabel, 0) + 1
sortedClassCount = sorted(classCount.iteritems(),key=operator.itemgetter(1), reverse=True)
return sortedClassCount[0][0]
现在我买了一瓶红酒,并记录下它的各种特征,判断它等级的代码如下:
testData = np.array([0.2,0.3,0.11,0.13,0.16,0.2,0.3,0.11,0.13,0.16,0.27])
data, label = generateData('winequality-red.csv')
k=9
level = knnclassify(testData, data, label, k)
print level
从结果可以看出,我们这瓶酒的等级为5,属于中等红酒。对于我这种level来说,应该足够了。
评价分类器 在我品尝这瓶红酒的时候,我心中不禁有点不安:如果这个K-近邻分类方法“骗了”我怎么办? 作为一个严谨的人,我一定得找到一个方法来检验这个分类器的效度。
验证分类器的方法非常简单,首先我们将之前的数据一分为二,一份作为训练集,一份作为测试集。用训练集来预测测试集,因为我们已经知道测试集的真正等级,所以,我们可以通过比较我们的预测结果与真实结果之间的差异来评估分类器的效度。测试的代码为:
data, label = generateData('winequality-red.csv')
ratio = 0.1
k = 9
m = int(ratio*data.shape[0])
testData = data[:m];
trainData = data[m:]
testLabel = label[:m]
trainLabel = label[m:]
errorCount = 0
for i in range(testData.shape[0]):
predict = knnclassify(testData[i], trainData, trainLabel, k)
print "the predict is %f, the ground truth is %f" %(predict, testLabel[i])
if(predict != testLabel[i]):
errorCount = errorCount + 1;
print "the total error rate is %f" %(errorCount/float(testData.shape[0]))
最后输出的错误率为0.39。还是比较可信,至少是个及格的分数。在后面的文章中,我会尝试使用其他的分类方法,以期达到更好的结果。
关于K的选择
在前面的代码中,K的值我都取做9,但是在实践中,我们需要通过实验得出K的最佳取值。操作也非常简单,就是K取不同的值,然后得出不同的错误率,取错误率最低的那个K值作为我们最终的选择。
本篇文章的完整代码可以在这里下载