Logo
Section 1 / 4

关于我们手写内容的查询

创建一个包含我们自己手写体的测试数据集,观察神经网络的表现如何。

导入

关于我们手写内容的查询

创建一个包含我们自己手写体的测试数据集,观察神经网络的表现如何。

我们自己手写数字的测试数据集

我们一直使用来自 MNIST 数据集的手写数字图像。为什么不试试用自己的手写数字呢?

在这个实验中,我们将创建一个包含自己手写数字的测试数据集,并尝试不同的书写风格,以及带有噪声或抖动的图像,来观察我们的神经网络在这些情况下的表现。

我们可以使用任何喜欢的图像编辑或绘图软件来制作图像,不一定非要用 Photoshop。GIMP 是一款免费开源的替代工具,支持 Windows、Mac 和 Linux 系统。我们甚至可以用笔在纸上写字,然后用手机或相机拍照,或者使用扫描仪进行扫描。唯一的要求是图像必须为正方形(宽度等于高度),并以 PNG 格式保存。通常可以在图像编辑器的“文件 > 另存为”或“文件 > 导出”菜单中找到格式选项。以下是我们制作的一些示例图像:

...
Custom handwritten data

其中,数字 55 是我们正常手写的;数字 44 使用粉笔书写而非记号笔;数字 33 是我们自己的笔迹,但部分区域被裁剪掉了;数字 22 使用的是传统报纸或书籍字体,但做了轻微模糊处理;数字 66 故意画得歪歪扭扭,几乎像水面倒影一样不稳定;最后一张图像与前一张相同,但额外添加了噪声,目的是进一步增加神经网络识别的难度。

科学家们一直惊叹于人脑在遭受损伤后仍能出色运作的能力。这表明神经网络会将学到的知识分布到多个连接权重上,因此即使部分结构受损,整体性能依然可以保持良好。这也意味着,当输入图像存在损坏或缺失时,网络仍能较好地完成任务——这是一种非常强大的能力。而我们上面那个被裁剪过的 33 正是用来测试这种能力的。

我们需要将这些 PNG 图像缩小为 28×2828 \times 28 像素的尺寸,以便与 MNIST 数据集中的图像保持一致。这项操作可以通过图像编辑软件完成。

读取并重塑图像

Python 的库可以帮助我们读取和解码常见图像格式(包括 PNG)的数据。请看下面这段简单代码:

1import scipy.misc 2img_array = scipy.misc.imread(image_file_name, flatten=True) 3 4img_data = 255.0 - img_array.reshape(784) 5img_data = (img_data / 255.0 * 0.99) + 0.01

scipy.misc.imread() 函数用于从 PNG 或 JPG 等图像文件中提取数据,使用前需要导入 scipy.misc 模块。参数 flatten=True 表示将彩色图像的各颜色通道合并为单个灰度图层,这正是我们需要的格式。

下一行代码将 28×2828 \times 28 的二维图像数组重塑为长度为 784 的一维列表,这是输入神经网络所必需的形式。我们之前已多次执行过此类操作。新增的部分是用 255.0255.0 减去数组中的每个像素值,原因在于:通常情况下,0 表示黑色,255 表示白色;但 MNIST 数据集恰恰相反,白色像素对应 0,黑色对应 255,因此我们必须反转像素值以匹配 MNIST 的标准。

最后一行代码对数据进行常规归一化处理,使所有数值落在 0.010.01 到 1.01.0 的区间内。

1# python notebook for Make Your Own Neural Network 2# code for a 3-layer neural network, and code for learning the MNIST dataset 3 4# helper to load data from PNG image files 5#import imageio 6import scipy.misc 7# glob helps select multiple files using patterns 8import glob 9 10import numpy 11# library for plotting arrays 12import matplotlib.pyplot 13 14# our own image test data set 15our_own_dataset = [] 16 17for image_file_name in glob.glob('own_images/2828_my_own_?.png'): 18 print ("loading ... ", image_file_name) 19 # use the filename to set the correct label 20 label = int(image_file_name[-5:-4]) 21 # load image data from png files into an array 22 img_array = scipy.misc.imread(image_file_name, flatten=True) 23 # reshape from 28x28 to list of 784 values, invert values 24 img_data = 255.0 - img_array.reshape(784) 25 # then scale data to range from 0.01 to 1.0 26 img_data = (img_data / 255.0 * 0.99) + 0.01 27 print(numpy.min(img_data)) 28 print(numpy.max(img_data)) 29 # append label and image data to test data set 30 record = numpy.append(label,img_data) 31 print(record) 32 our_own_dataset.append(record) 33 pass 34 35matplotlib.pyplot.imshow(our_own_dataset[3][1:].reshape(28,28), cmap='Greys', interpolation='None') 36matplotlib.pyplot.savefig("output/samplePlot2.png") 37 38print(our_own_dataset[0])

测试我们的数据集

我们需要修改基础神经网络程序,使其使用 MNIST 训练数据集进行训练,但在测试阶段不再使用 MNIST 测试集,而是改用我们自己手写的图像数据进行测试。

1# python notebook for Make Your Own Neural Network 2# code for a 3-layer neural network, and code for learning the MNIST dataset 3# this version trains using the MNIST dataset, then tests on our own images 4 5import numpy 6# scipy.special for the sigmoid function expit() 7import scipy.special 8# library for plotting arrays 9import matplotlib.pyplot 10 11# helper to load data from PNG image files 12import scipy.misc 13# glob helps select multiple files using patterns 14import glob 15 16# neural network class definition 17class neuralNetwork: 18 19 20 # initialise the neural network 21 def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate): 22 # set number of nodes in each input, hidden, output layer 23 self.inodes = inputnodes 24 self.hnodes = hiddennodes 25 self.onodes = outputnodes 26 27 # link weight matrices, wih and who 28 # weights inside the arrays are w_i_j, where link is from node i to node j in the next layer 29 # w11 w21 30 # w12 w22 etc 31 self.wih = numpy.random.normal(0.0, pow(self.inodes, -0.5), (self.hnodes, self.inodes)) 32 self.who = numpy.random.normal(0.0, pow(self.hnodes, -0.5), (self.onodes, self.hnodes)) 33 34 # learning rate 35 self.lr = learningrate 36 37 # activation function is the sigmoid function 38 self.activation_function = lambda x: scipy.special.expit(x) 39 40 pass 41 42 43 # train the neural network 44 def train(self, inputs_list, targets_list): 45 # convert inputs list to 2d array 46 inputs = numpy.array(inputs_list, ndmin=2).T 47 targets = numpy.array(targets_list, ndmin=2).T 48 49 # calculate signals into hidden layer 50 hidden_inputs = numpy.dot(self.wih, inputs) 51 # calculate the signals emerging from hidden layer 52 hidden_outputs = self.activation_function(hidden_inputs) 53 54 # calculate signals into final output layer 55 final_inputs = numpy.dot(self.who, hidden_outputs) 56 # calculate the signals emerging from final output layer 57 final_outputs = self.activation_function(final_inputs) 58 59 # output layer error is the (target - actual) 60 output_errors = targets - final_outputs 61 # hidden layer error is the output_errors, split by weights, recombined at hidden nodes 62 hidden_errors = numpy.dot(self.who.T, output_errors) 63 64 # update the weights for the links between the hidden and output layers 65 self.who += self.lr * numpy.dot((output_errors * final_outputs * (1.0 - final_outputs)), numpy.transpose(hidden_outputs)) 66 67 # update the weights for the links between the input and hidden layers 68 self.wih += self.lr * numpy.dot((hidden_errors * hidden_outputs * (1.0 - hidden_outputs)), numpy.transpose(inputs)) 69 70 pass 71 72 73 # query the neural network 74 def query(self, inputs_list): 75 # convert inputs list to 2d array 76 inputs = numpy.array(inputs_list, ndmin=2).T 77 78 # calculate signals into hidden layer 79 hidden_inputs = numpy.dot(self.wih, inputs) 80 # calculate the signals emerging from hidden layer 81 hidden_outputs = self.activation_function(hidden_inputs) 82 83 # calculate signals into final output layer 84 final_inputs = numpy.dot(self.who, hidden_outputs) 85 # calculate the signals emerging from final output layer 86 final_outputs = self.activation_function(final_inputs) 87 88 return final_outputs 89 90 91# number of input, hidden and output nodes 92input_nodes = 784 93hidden_nodes = 200 94output_nodes = 10 95 96# learning rate 97learning_rate = 0.1 98 99# create instance of neural network 100n = neuralNetwork(input_nodes,hidden_nodes,output_nodes, learning_rate) 101 102# load the mnist training data CSV file into a list 103training_data_file = open("mnist_train_100.csv", 'r') 104training_data_list = training_data_file.readlines() 105training_data_file.close() 106 107# train the neural network 108 109# epochs is the number of times the training data set is used for training 110epochs = 10 111 112for e in range(epochs): 113 # go through all records in the training data set 114 for record in training_data_list: 115 # split the record by the ',' commas 116 all_values = record.split(',') 117 # scale and shift the inputs 118 inputs = (numpy.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01 119 # create the target output values (all 0.01, except the desired label which is 0.99) 120 targets = numpy.zeros(output_nodes) + 0.01 121 # all_values[0] is the target label for this record 122 targets[int(all_values[0])] = 0.99 123 n.train(inputs, targets) 124 pass 125 pass 126 127# our own image test data set 128our_own_dataset = [] 129 130# load the png image data as test data set 131for image_file_name in glob.glob('own_images/2828_my_own_?.png'): 132 133 # use the filename to set the correct label 134 label = int(image_file_name[-5:-4]) 135 136 # load image data from png files into an array 137 print ("loading ... ", image_file_name) 138 img_array = scipy.misc.imread(image_file_name, flatten=True) 139 140 # reshape from 28x28 to list of 784 values, invert values 141 img_data = 255.0 - img_array.reshape(784) 142 143 # then scale data to range from 0.01 to 1.0 144 img_data = (img_data / 255.0 * 0.99) + 0.01 145 print(numpy.min(img_data)) 146 print(numpy.max(img_data)) 147 148 # append label and image data to test data set 149 record = numpy.append(label,img_data) 150 our_own_dataset.append(record) 151 152 pass 153 154# test the neural network with our own images 155 156# record to test 157item = 2 158 159# plot image 160matplotlib.pyplot.imshow(our_own_dataset[item][1:].reshape(28,28), cmap='Greys', interpolation='None') 161matplotlib.pyplot.savefig("output/samplePlot2.png") 162 163# correct answer is first value 164correct_label = our_own_dataset[item][0] 165# data is remaining values 166inputs = our_own_dataset[item][1:] 167 168# query the network 169outputs = n.query(inputs) 170print (outputs) 171 172# the index of the highest value corresponds to the label 173label = numpy.argmax(outputs) 174print("network says ", label) 175# append correct or incorrect to list 176if (label == correct_label): 177 print ("match!") 178else: 179 print ("no match!") 180 pass

结果如何?成功了!下图总结了使用我们自己手写图像进行查询的结果:

...
Results of querying with our own images

可以看到,神经网络正确识别了我们创建的所有图像,包括那个被故意损坏的 33。只有添加了噪声的 66 识别失败。

使用我们自己的手写图像进行测试,尤其是真实手写体,证明了这个神经网络确实有效。

此外,观察神经网络在图像严重变形或破损的情况下仍能识别到什么程度,是一件很有趣的事。我们会惊讶于其强大的鲁棒性。

一些建议的改进方案

神经网络的内部原理