Task
使用上一篇讲述到的代码做一个MNIST的手写数字分类的模型:能够做多类别分类来识别任何数字。
给train data和test data计算准确率,输出confusion矩阵
Hint
1、对每一个数字:创建一个二元分类器: this digit vs all other digit
2、给每一个数字创建不同的二元分类感受器
3、定义一个函数来分类输入的数字
如果外面将10个感受器的权重合到一个矩阵上,我们就能将10个感受器的结果通过一次矩阵乘法完成。最可能的数字将通过argmax操作输出出来
Think
前文Perceptron一节中我们构建了一个二元的分类器,即可以分辨两个数字的的神经网络,那么我们分辨10个数字的神经网络便在此基础上来改造
最直接的想法就是提示中给我们的思路:仍然使用一个二元分类器,用于分类这个数字 和其他数字
于是乎答案呼之欲出了,我们仍然用一个特定的数字i来作为这个数字 的数据,对于其他数字 的数据,我们则将0~9中除了i以外的所有数字的数据混合在一起,就得到了其他数字 的数据集,随后我们用同样的方法训练一个二元分类器,看上去我们的任务就将圆满完成了!!
Let’s do it
neccessary lib
1 2 3 4 5 6 import matplotlib.pyplot as pltimport numpy as npimport pickleimport gzipimport osimport random
copy functions
我们直接把Perceptron中的训练函数摘抄过来
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 def train (positive_examples, negative_examples, num_iterations = 100 ): num_dims = positive_examples.shape[1 ] weights = np.zeros((num_dims,1 )) pos_count = positive_examples.shape[0 ] neg_count = negative_examples.shape[0 ] report_frequency = 10 for i in range (num_iterations): pos = random.choice(positive_examples) neg = random.choice(negative_examples) z = np.dot(pos, weights) if z < 0 : weights = weights + pos.reshape(weights.shape) z = np.dot(neg, weights) if z >= 0 : weights = weights - neg.reshape(weights.shape) if i % report_frequency == 0 : pos_out = np.dot(positive_examples, weights) neg_out = np.dot(negative_examples, weights) pos_correct = (pos_out >= 0 ).sum () / float (pos_count) neg_correct = (neg_out < 0 ).sum () / float (neg_count) return weights
以及判断分类器正确率的函数
1 2 3 4 def accuracy (weights, test_x, test_labels ): ones = np.c_[test_x,np.ones(len (test_x))] res = np.dot(ones , weights) return (res.reshape(test_labels.shape)*test_labels>=0 ).sum ()/float (len (test_labels))
load data set
again,笔记中给出的下载代码看上去没法在win中运行,Suchan这里和Perceptron一节中一样,直接下载现成的zip文件
然后稍微替换一下dataset的加载方式
1 2 3 4 5 with gzip.open ('./mnist.pkl.gz' , 'rb' ) as mnist_pickle: MNIST = pickle.load(mnist_pickle)
copy一下教程对数据的处理,why not?
1 2 3 4 5 6 7 8 9 print (MNIST['Train' ]['Features' ][0 ][130 :180 ])print (MNIST['Train' ]['Labels' ][0 ])features = MNIST['Train' ]['Features' ].astype(np.float32) / 256.0 labels = MNIST['Train' ]['Labels' ] fig = plt.figure(figsize=(10 ,5 )) for i in range (10 ): ax = fig.add_subplot(1 ,10 ,i+1 ) plt.imshow(features[i].reshape(28 ,28 )) plt.show()
process
笔记中给出了一个已有的函数
1 2 3 4 5 6 7 8 9 10 def set_mnist_pos_neg (positive_label, negative_label ): positive_indices = [i for i, j in enumerate (MNIST['Train' ]['Labels' ]) if j == positive_label] negative_indices = [i for i, j in enumerate (MNIST['Train' ]['Labels' ]) if j == negative_label] positive_images = MNIST['Train' ]['Features' ][positive_indices] negative_images = MNIST['Train' ]['Features' ][negative_indices] return positive_images, negative_images
函数要求我们输入两个数字,一个这个数字 一个其他数字
但是根据前文的思路来说,我们没办法将其他数字 这个概念填入到这个函数中,我们不妨改造一个函数,用于输入一个数字,获取到它的数据集即可
1 2 3 4 5 6 def get_mnist_pos (lable ): positive_indices = [i for i, j in enumerate (MNIST['Train' ]['Labels' ]) if j == lable] positive_images = MNIST['Train' ]['Features' ][positive_indices] return positive_images
最终计算准确率之前,我们还要定义一个改造数据的函数:即我们最终的测试集也需要包含这个数字 和其他数字
回想我们这个架构的工作流程:对于这个数字 ,我们将输出一个正值,对于其他数字 我们应该一个负值,那么很容易想到我们构造的label就应该是:对于当前数字 我们给一个+1,对于其他数字 我们给一个-1,到时候其做点乘,如果结果正确,二者点乘的结果就应该是正值
1 2 3 4 5 6 7 8 9 def gen_test_data (test_pos , test_label , this_num ): for i in range (10 ): if i == this_num: continue other_num_pos = get_mnist_pos(i) split_pre , split_post = np.split(other_num_pos, split_num) test_pos = np.concatenate((test_pos, split_post),axis=0 ) test_label = np.concatenate((test_label , np.full(split_post.shape[0 ] , -1 )),axis=0 ) return test_pos , test_label
然后我们来对每一个数字进行一次训练,将训练出来的weight返回出去
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 def train_this_num (this_num ): this_num_pos = get_mnist_pos(this_num) other_num_pos = np.array([1 ,1 ]) flag = True for i in range (10 ): if i == this_num: continue tem_pos = get_mnist_pos(i) if flag: flag = False other_num_pos = tem_pos else : other_num_pos = np.concatenate((other_num_pos, tem_pos),axis=0 ) train_x , test_x = np.split(this_num_pos, [2000 ]) train_x = np.c_[train_x,np.ones(len (train_x))] tain_y = np.full((train_x.shape[0 ]), this_num) test_y = np.full((test_x.shape[0 ]) , this_num) other_num_pos = np.c_[other_num_pos,np.ones(len (other_num_pos))] weight = train(train_x , other_num_pos , 1000 ) test_x , test_y = gen_test_data(test_x , test_y , this_num) ac = accuracy(weight, test_x, test_y) print ("{} : accuracy = {}" .format (this_num, ac)) return weight
好了,经过以上代码,我们运行能够得到我们的结果:
看上去结果还是可以接受。
至于最终定义一个函数来判断这里省略吧,其实也就是把全部的weight组合在一起,点乘输出一个向量,得到正值的就是我们的结果