本文是个人学习笔记,该篇主要学习KNN算法理论和应用范围,并应用KNN算法解决Kaggle入门级Digit Recognizer,也是个人入坑ML和Kaggle的开端,希望能够有始有终。
K最近邻分类算法(K-NearestNeighbor),由Cover和Hart在1968年提出,是最简单的机器学习算法之一,主要被应用于文本分类、相似推荐。
基本原理:
从训练样本集中选择k个与测试样本“距离”最近的样本,该k个样本中出现频率最高的类别即作为测试样本的类别,其中参数k的取值通常不大于20。
实现流程:
算法目标:未知类别样本集分类;
输入信息:待分类目标样本集和已分类学习样本集;
输出结果:已分类目标样本集;
算法步骤:
(a) 确定k值,即每个待分类样本要到它k个已分类样本邻居;
(b) 根据距离度量公式(如:欧氏距离),确定待分类样本集和已分类样本集中,距离该样本点最近的k个样本;
(c) 统计该k个邻居样本中,各类别的数量。最终将待分类样本点定义为数量最多的类别;
优缺点:
算法简单,易于实现,不需要参数估计,不需要事先训练;但由于不进行事先训练,而在输入待分类样本集时才开始,导致KNN计算量特别大;同时训练样本必须存储在本地,消耗大量内存。
在数据分析和数据挖掘中,个体间的“距离”实际是对个体间差异大小的空间衡量,距离越远说明个体间的差异越大,然后根据实际差异情况进行相关性分析。
假设:X个体和Y个体都包含了N个维的特征,即X=( x1,x2,x3,…xn),Y=(y1,y2,y3,…yn)
欧几里得距离(欧式距离):
欧式距离是最常用的距离度量,计算多维空间中各点之间的绝对距离。因为距离计算是基于各维度特征的绝对数值,所以需要保证各维度指标在相同的刻度级别。
公式:
dist(X,Y)=∑i=1n(xi−yi)2−−−−−−−−−−√
马哈拉诺比斯距离(马氏距离):
马氏距离是欧式距离的改进版本,即基于各指标维度进行标准化后再使用欧氏距离,规避了指标度量的差异;
曼哈顿距离:
曼哈顿距离来源于城市区块距离,是将多个维度上的距离进行求和后的结果;
公式:
dist(X,Y)=∑i=1n|xi−yi|
切比雪夫距离:
切比雪夫距离起源于国际象棋中国王的走法,国际象棋国王每次只能往周围的8格中走一步,那么如果要从棋盘中A格 (x1,y1) 走到B格 (x2,y2) 最少需要走几步。
公式:
dist(X,Y)=limp→∞(∑i=1n|xi−yi|p)1/p
明可夫斯基距离(明氏距离):
明氏距离是欧式距离的推广版本,是多个距离度量公式的概括性表述。
公式:
dist(X,Y)=(∑i=1n|xi−yi|p)1/p
当p值为1时,即为曼哈顿距离,p值为2时,即为欧式距离,而当p趋向于无穷时,即为切比雪夫距离;
Python 代码1:
import pandas as pd
import numpy as np
import time
import operator
def data_load():
# 利用pandas读取csv文件内容
train_ttl=pd.read_csv('/Users/Sweet-home/PycharmProjects/Kaggle/reco data/train.csv')
train_label=pd.DataFrame(train_ttl['label'])
train_data=pd.DataFrame(train_ttl.ix[:,1:])
test_data=pd.read_csv('/Users/Sweet-home/PycharmProjects/Kaggle/reco data/test.csv')
# dataframe归整化
train_data[train_data!=0]=1
test_data[test_data!=0]=1
return train_data,train_label,test_data
#inX:1*n dataSet:m*n labels:m*1
def classify(inX, dataSet, labels, k): #确定类别
diffmats = (dataSet-inX)**2#计算KNN 欧式距离
sumdiffs=diffmats.sum(axis=1)
distances=sumdiffs**0.5
sortedDistIndicies = distances.argsort()#根据距离排序
classcount={}
for i in range(k):#统计各标签值count
votelabel = labels.ix[sortedDistIndicies[i],0]
classcount[votelabel] = classcount.get(votelabel,0) + 1
# 根据字典值降序排序
sortedClassCount = sorted(classcount.items(), key=operator.itemgetter(1), reverse=True)
# 返回最相似label值
return sortedClassCount[0][0]
if __name__=='__main__':
start = time.clock()
traindata,trainlabel,testdata=data_load()#加载raw data
m,n=testdata.shape
result_labels=[]
result={}
for i in range(m):#对test样本一次确定其类别
result_label=classify(testdata.ix[i],traindata,trainlabel,8)
result_labels.append(result_label)
#将结果转化成Dataframe结构
ImageId=np.arange(m)+1
result['Label']=result_labels
result_frame=pd.DataFrame(result,index=ImageId)
#导出结果
result_frame.to_csv('/Users/Sweet-home/PycharmProjects/Kaggle/reco data/result.csv')
end = time.clock()
print('总耗时:', end - start)
Python 代码2:
import pandas as pd
import numpy as np
import time
from sklearn.neighbors import KNeighborsClassifier
def data_load():
# 利用pandas读取csv文件内容
train_ttl=pd.read_csv('D:\Program files\JetBrains\digit recognizer\Raw data\\train.csv')
train_label=pd.DataFrame(train_ttl['label'])
train_data=pd.DataFrame(train_ttl.ix[:,1:])
test_data=pd.read_csv('D:\Program files\JetBrains\digit recognizer\Raw data\\test.csv')
# dataframe归整化
test_data[test_data!=0]=1
# train_data[train_data!=0]=1
m,n=train_data.shape#这里似乎因为dataframe太大,用bool判断更改时总会异常跳出,所以选择循环更改
for i in range(m):
for j in range(n):
if train_data.ix[i,j]!=0:
train_data.ix[i,j]=1
return train_data,train_label,test_data
#利用Python Sklearn包,进行test样本集分类判别
def knn_classify(traindata,trainlabel,testdata):
knn_clf = KNeighborsClassifier(n_neighbors=5, algorithm='kd_tree', weights='distance', p=3)#设置参数
knn_clf.fit(traindata,trainlabel.values.ravel())#训练Train样本
knn_result=knn_clf.predict(testdata)#预测Test样本
return knn_result
if __name__=='__main__':
start = time.clock()
traindata,trainlabel,testdata=data_load()#加载raw data
m,n=testdata.shape
result_labels=knn_classify(traindata,trainlabel,testdata)
#将结果转化成Dataframe结构
result={}
ImageId=np.arange(m)+1
result['Label']=result_labels
result_frame=pd.DataFrame(result,index=ImageId)
#导出结果
result_frame.to_csv('D:\Program files\JetBrains\digit recognizer\Raw data\\result.csv')
end = time.clock()
print('总耗时:', end - start)#接近5小时