本系列是机器学习算法的python实践,主要是我对一些机器学习算法的学习笔记和实践记录,在每篇文章的最后,还会给出scikit-learn包中对应实现的使用指南。
KNN算法是一个简单的分类算法,它的动机特别简单:与一个样本点距离近的其他样本点绝大部分属于什么类别,这个样本就属于什么类别,算法的主要步骤如下:
1.计算新样本点与已知类别数据集中样本点的距离。
2.取前K个距离最近的(最相似的)点。
3.统计这K个点所在类别出现的频率。
4.选择出现频率最高的点作为新样本点的类别。
KNN算法的优点在于一般精度高,对于异常的噪音数据不敏感。KNN一个明显的问题是当属于某个类别c的数据点在已知类别数据集中大量存在时,一个待预测的样本点的前K个最近的点总是存在很多类别c的点,解决这个问题的方法是计算类别的频率时,按照距离进行加权,使得离得近的点比离的远一些点更能影响类别频率排序的结果。
KNN算法中K值的选定非常影响最后的结果,通常可以使用交叉检验来选取合适的k。下面是仿照sikit-learn的KNeighborsClassifier的调用方式写的KNN:
class KNN_Classifier:
def __init__(self, k):
self.k = k
self.train_data = None
self.train_labels = None
def fit(self, train_data, train_labels):
self.train_data = normalize(train_data)
self.train_labels = train_labels
def predict(self, test_data):
if (self.train_data is None) | (self.train_labels is None):
print 'fit train data first!'
pre_labels = []
train_data_size = len(self.train_labels)
# for every data point in test set
for x in normalize(test_data):
# calculate distance
sq_diff_mat = (np.tile(x, (train_data_size, 1)) - self.train_data) ** 2
distances = np.sum(sq_diff_mat, axis=1) ** .5
# get lowest k distances
sorted_dis_indices = distances.argsort()[0: self.k]
# count the times class occur
class_counts = {}
for idx in sorted_dis_indices:
label = labels[idx]
class_counts[label] = class_counts.get(label, 0) + 1
# sort class_count dict
sorted_class = sorted(class_counts.items(), key=lambda d: d[1], reverse=True)
# add max voted class to pre_labels
pre_labels.append(sorted_class[0][0])
return pre_labels
测试代码如下所示
# load data
data, labels = load_dating_data()
# split data into train set and test set
split_pos = int(len(labels) * 0.9)
train_data = normalize(data[0: split_pos])
train_labels = labels[0: split_pos]
test_data = normalize(data[split_pos: len(labels)])
test_labels = labels[split_pos: len(labels)]
# init classifier
classifier = KNN_Classifier(50)
# fit classifier
classifier.fit(train_data, train_labels)
# predict the class of test data and count error points
error_num = (test_labels != classifier.predict(test_data)).sum()
# calculate error rate and print
print 'error rate is %f' % (error_num * 1.0 / len(test_labels))
这里使用machine learning in action中的提供的dating data,将90%的数据用作训练数据集,10%的数据用作测试集,选取k=50算法得到的错误率为0.08。
下面我们来看一下如何使用scikit-learn提供的KNN实现,scikit-learn中主要提供了2种KNN,KNeighborsClassifier和RadiusNeighborsClassifier。前者使用指定的前K个近邻来预测新样本点的类别,后者则是根据一个指定的半径,使用半径内所有的点来预测。创建一个KNN分类器时有这些重要的参数:
- n_neighbors/radius: 使用近邻的个数K或半径
- algorithm: 实现KNN的具体算法,如kd树等
- metric: 距离的计算方法,默认为’minkowski’表示minkowski距离
- p: minkowski距离中的参数p,p=1表示manhattan distance(l1范数),p=2表示euclidean_distance (l2范数)
这里只列出了几个常用的参数,具体的可以参考链接。使用的方法和上面的测试代码类似,只需要将classifier替换成scikit-learn的实现就可以了。对于距离(相似度)的计算方法会在另一篇文章中总结。未完待续…