一、问题描述
手写数字被存储在EXCEL表格中,行表示一个数字的标签和该数字的像素值,有多少行就有多少个样本。
一共42000个样本
二、KNN
KNN最邻近规则,主要应用领域是对未知事物的识别,即判断未知事物属于哪一类,判断思想是,基于欧几里得定理,判断未知事物的特征和哪一类已知事物的的特征最接近;
K最近邻(k-Nearest Neighbor,KNN)分类算法,是一个理论上比较成熟的方法,也是最简单的机器学习算法之一。该方法的思路是:如果一个样本在特征空间中的k个最相似(即特征空间中最邻近)的样本中的大多数属于某一个类别,则该样本也属于这个类别。KNN算法中,所选择的邻居都是已经正确分类的对象。该方法在定类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。 KNN方法虽然从原理上也依赖于极限定理,但在类别决策时,只与极少量的相邻样本有关。由于KNN方法主要靠周围有限的邻近的样本,而不是靠判别类域的方法来确定所属类别的,因此对于类域的交叉或重叠较多的待分样本集来说,KNN方法较其他方法更为适合。
KNN算法不仅可以用于分类,还可以用于回归。通过找出一个样本的k个最近邻居,将这些邻居的属性的平均值赋给该样本,就可以得到该样本的属性。更有用的方法是将不同距离的邻居对该样本产生的影响给予不同的权值(weight),如权值与距离成正比(组合函数)。
该算法在分类时有个主要的不足是,当样本不平衡时,如一个类的样本容量很大,而其他类样本容量很小时,有可能导致当输入一个新样本时,该样本的K个邻居中大容量类的样本占多数。 该算法只计算“最近的”邻居样本,某一类的样本数量很大,那么或者这类样本并不接近目标样本,或者这类样本很靠近目标样本。无论怎样,数量并不能影响运行结果。可以采用权值的方法(和该样本距离小的邻居权值大)来改进。该方法的另一个不足之处是计算量较大,因为对每一个待分类的文本都要计算它到全体已知样本的距离,才能求得它的K个最近邻点。目前常用的解决方法是事先对已知样本点进行剪辑,事先去除对分类作用不大的样本。该算法比较适用于样本容量比较大的类域的自动分类,而那些样本容量较小的类域采用这种算法比较容易产生误分。
K-NN可以说是一种最直接的用来分类未知数据的方法。基本通过下面这张图跟文字说明就可以明白K-NN是干什么的
简单来说,K-NN可以看成:有那么一堆你已经知道分类的数据,然后当一个新数据进入的时候,就开始跟训练数据里的每个点求距离,然后挑离这个训练数据最近的K个点看看这几个点属于什么类型,然后用少数服从多数的原则,给新数据归类。
算法步骤:
step.1---初始化距离为最大值
step.2---计算未知样本和每个训练样本的距离dist
step.3---得到目前K个最临近样本中的最大距离maxdist
step.4---如果dist小于maxdist,则将该训练样本作为K-最近邻样本
step.5---重复步骤2、3、4,直到未知样本和所有训练样本的距离都算完
step.6---统计K-最近邻样本中每个类标号出现的次数
step.7---选择出现频率最大的类标号作为未知样本的类标号
三、分析
从训练集中找到和新数据最接近的k条记录,根据他们的主要分类来决定新数据的类别。
要点:
1.如何判断他们最接近,怎么决定他们的距离(非常决定性能的因素)
欧式距离、余弦定理求相似度……
2.按照k个里最邻近的那个还是类别最多的那个还是权重加权的?
类别最多、权重加权、最近……
手写识别存储文件MNIST train.csv 中,0:label(数字具体是什么),1-784pixels(灰度值)
余弦定理——判断他们之间的相似度。
两个向量,当两个向量夹角比较小的时候,会比较相似,夹角比较大的时候,不相似。
四、实现
# -*- coding: utf-8 -*- # CopyRight by heibanke import pandas as pd import numpy as np import time def normalize(x): """ linalg.norm(x), return sum(abs(xi)**2)**0.5 apply_along_axis(func, axis, x), """ #归一化——公式的被除数 #这里的1表示,x按照行传递给np.linalg.norm,因为np.linalg.norm没有参数 #所以,默认是求它的二范式,因为是二范式,所以就是平方和 norms = np.apply_along_axis(np.linalg.norm, 1, x) + 1.0e-7 #这里返回的实际上就是求角度的公式中分式下边的值,因为测试机的值不影响,所以只求训练集的 #就可以,所以在nearest_neighbor里边没有求值这一项,因为这里计算了已经 return x / np.expand_dims(norms, -1)#-1表示最后一个轴 #作者自己想到的一个归一化的方法 def normalize2(x): """ linalg.norm(x), return sum(abs(xi)**2)**0.5 apply_along_axis(func, axis, x), """ norms = np.apply_along_axis(np.mean, 1, x) + 1.0e-7 return x - np.expand_dims(norms, -1) def nearest_neighbor(norm_func,train_x, train_y, test_x): train_x = norm_func(train_x) test_x = norm_func(test_x) # cosine #test_x就是测试数据,train_x是训练数据,分别相当于j,i corr = np.dot(test_x, np.transpose(train_x)) argmax = np.argmax(corr, axis=1) #然后找一个最大值作为预测值 preds = train_y[argmax] #上边如果采用for循环的形式会更好看一些,但是运行速度会慢一些。 #采用矩阵的形式会快 return preds def validate(preds, test_y): count = len(preds) #对所有正确的进行sum()求和 correct = (preds == test_y).sum() #然后对争取的的总和除以数据总和 return float(correct) / count if __name__=='__main__': TRAIN_NUM = 2200 TEST_NUM = 4200 # Read data 42000 data = pd.read_csv('train.csv') train_data = data.values[0:TRAIN_NUM,1:] train_label = data.values[0:TRAIN_NUM,0] test_data = data.values[TRAIN_NUM:TEST_NUM,1:] test_label = data.values[TRAIN_NUM:TEST_NUM,0] #这里采用三种方式,一种是公式型的归一化,第二种是减去均值的归一化,第三种没有归一化,直接就是两个向量相关 norm_funcs = [normalize,normalize2,lambda x:x] for norm_f in norm_funcs: t = time.time() preds = nearest_neighbor(norm_f,train_data, train_label, test_data) #acc求出来的就是准确率 acc = validate(preds, test_label) print("%s Validation Accuracy: %f, %.2fs" % (norm_f.__name__,acc, time.time() - t))
这是黑板课老师给的代码,他提出了三种归一化方法。再次我提出异议,最好的归一化方法1,实际上就是公式的一部分,而归一化2和3实际上都不对,没有正确运用公式,因为在nearest_neighbor函数中使用的并不是完整的公式,没有除以他们的平方和,加上归一化1,才正好拼出来公式的形态。所以,正确做法只有一个,那就归一化1,甚至不应该叫做归一化,而是正常的正确的一个阶梯步骤。
下边用库函数的knn和svm试一下:
# -*- coding: utf-8 -*- # CopyRight by heibanke import pandas as pd from sklearn.decomposition import PCA from sklearn.neighbors import KNeighborsClassifier from sklearn import svm import time if __name__=='__main__': # maximum num is 42000 TRAIN_NUM = 22000 TEST_NUM = 42000 """ train_data: 训练数据 train_label: 训练数据的正确输出 test_data: 测试数据 test_label: 测试数据的正确输出 """ data = pd.read_csv('train.csv') train_data = data.values[0:TRAIN_NUM,1:] train_label = data.values[0:TRAIN_NUM,0] test_data = data.values[TRAIN_NUM:TEST_NUM,1:] test_label = data.values[TRAIN_NUM:TEST_NUM,0] t = time.time() # PCA, principal components analysis # http://blog.jobbole.com/86905/ # http://ufldl.stanford.edu/wiki/index.php/%E4%B8%BB%E6%88%90%E5%88%86%E5%88%86%E6%9E%90 pca = PCA(n_components=0.8) train_x = pca.fit_transform(train_data) test_x = pca.transform(test_data) #knn regression neighbor = KNeighborsClassifier(n_neighbors=4) neighbor.fit(train_x, train_label) preds = neighbor.predict(test_x) acc = float((preds == test_label).sum())/len(test_x) print("KNN Validation Accuracy: %f, %.2fs" % (acc, time.time() - t)) # 22000, 96.7% #svm(Support Vector Machine) regression # 统计学习方法 # PRML t = time.time() pca = PCA(n_components=0.8,whiten=True) train_x = pca.fit_transform(train_data) test_x = pca.transform(test_data) svc = svm.SVC(kernel='rbf',C=10) svc.fit(train_x, train_label) preds = svc.predict(test_x) acc = float((preds == test_label).sum())/len(test_x) print("SVM Validation Accuracy: %f, %.2fs" % (acc, time.time() - t)) # 22000, 97.8%
参考:http://www.cnblogs.com/sunshinewang/p/6893503.html