惰性学习法:简单的存储数据,一直等待,直到给定一个测试元组时才进行泛化,根据对存储的元组的相似性进行分类。kNN(k近邻)分类方法于20世纪50年代提出,由于计算密集型算法,因此到60年代之后随着计算能力增强后才逐步应用。
kNN基于类比学习,将给定的测试元组表示为n维空间中的一个点,n代表属性数目。然后使用某种距离度量方式来寻找与给定测试元组最近的k个训练元组,对这个k个训练元组的类别进行统计,返回类别数目多的类别作为未知测试元组的类别。
常用的距离度量就是欧几里得距离,也称为二范数。同时为了减小不同属性值的取值范围对距离计算的影响,一般使用最大-最小规范化将属性值都变换到[0,1]区间。
根据上述特性,可知kNN算法最适合应用在数值型属性上,对于序数型属性可以变换为数值型,标称型属性规范化后也比较好,但是二元属性则可能效果不是太好。主要优缺点:
优点:精度高,对噪声不敏感,无需数据输入假定
缺点:时间和空间复杂度高,需要确定k值(k值的确定可能需要很多经验)
下面是使用《机器学习实战》一书中的kNN算法对一个垃圾邮件的数据进行实际分类的实现。这个数据共有3065个训练样本,1536个测试样本。每个样本使用了57个特征,特征中有数值型数据和二元属性,类别标号为{0,1},0表示不是垃圾邮件,1表示是垃圾邮件。
首先是从文件读取数据:
def loadDataSet(fp): if os.path.exists(fp): try: fh = open(fp, 'r') rtnTrainSet = zeros((TRAINSET_NUM, FEATURES_NUM)) trainLabel = [] rtnTestSet = zeros((TESTSET_NUM, FEATURES_NUM)) testLabel = [] i = 0 for line in fh: line = line.strip() terms = line.split(',') if i < TRAINSET_NUM: rtnTrainSet[i,:] = terms[0:FEATURES_NUM] trainLabel.append(int(terms[FEATURES_NUM])) i += 1 else: rtnTestSet[i - TRAINSET_NUM, :] = terms[0:FEATURES_NUM] testLabel.append(int(terms[FEATURES_NUM])) i += 1 except Exception, msg: print 'An unexcepted error occur: ', msg finally: fh.close() return rtnTrainSet,trainLabel,rtnTestSet,testLabel else: print "The data file does not exists!" return None
文件每一行代表一个样本,各特征使用逗号分隔,最后一个字段为类别标号。这里面使用的是Numpy这个第三方库存储数据到矩阵中。
然后是数据的规范化处理,规范化到[0,1]区间。
def normalize(ds): minVals = ds.min(0) maxVals = ds.max(0) ranges = maxVals - minVals normDS = zeros(shape(ds)) n = ds.shape[0] normDS = ds - tile(minVals, (n, 1)) normDS = normDS / tile(ranges, (n, 1)) return normDS
最后,分类器的实现就是将每个待测试的样本输入,使用训练样本计算最近的k个邻居然后得出最多的类别。
def kNNclassify(ds, labels, k, inputX): dsSize = ds.shape[0] diff = tile(inputX, (dsSize, 1)) - ds sqDiff = diff ** 2 sqDist = sqDiff.sum(axis = 1) dist = sqDist ** 0.5 sortedDist = dist.argsort() classCount = {} for i in range(k): votedLabel = labels[sortedDist[i]] classCount[votedLabel] = classCount.get(votedLabel, 0) + 1 sortedClassCount = sorted(classCount.iteritems(), key = operator.itemgetter(1),reverse = True) return sortedClassCount[0][0]
对1536个测试数据进行分类,同时使用不同的k值,得到不同k值分类的准确率,绘制了如下的图表:
从图中可以看出当k为10的时候精确度为75%为最高,这个结果并不是太好,主要原因是垃圾邮件的57个特征中有部分二元属性,当使用kNN进行分类计算距离时会有较大影响。
最后使用k=10对训练集也进行测试,得到如下结果。
Train Accuracy: 0.9282