手写数字识别实验是机器学习中最常见的一个示例,可以有很多种办法实现,最基础的其实就是利用knn算法,根据数字图片对应矩阵与经过训练的数字进行距离计算,最后这个距离最短,那么就认为它是哪个数字。
这里直接通过神经网络的办法来进行手写数字识别实验。不借助其他框架,编写网络,然后进行测试。这个代码其实网上有很多,并不是原创。
这里有必要说明一下手写数字的数据集,这里采用的是mnist_dataset/mnist_train.csv数据集,数据地址: https://www.kaggle.com/datasets/oddrationale/mnist-in-csv。下载之后是一个压缩包,里面包含mnist_train.csv,mnist_test.csv。
我们可以看看mnist_train.csv的部分数据:
上图中,①处表示 第一行内容 其实是标题,我们在数据处理的时候需要过滤这一行。② 表示的是label内容,也就是真实数字,它由0-9组成,也就是10个分类。③ 处表示的28 * 28矩阵,这个数字由784个数字组成。
实验过程,先使用mnist_train.csv数据训练网络,然后利用我们自己手写的数字进行测试。这里没有使用mnist_test.csv进行测试,主要是它本身就是人家进行测试的数据,我们这里自己测试。
我自己准备的数字图片如下所示:
这些图片都是根据这里测试数据mnist_train.csv数据格式的要求进行绘制的28*28像素的图片,这个图片很小,但是可以借助windows系统paint绘图工具,选择28*28像素画布,然后进行放大,最后可以在编辑区域画出这些数字。
下面给出代码:
- import os
- import numpy as np
- import scipy.special
- import imageio
-
- image_path = 'number_images'
-
-
- # 加载图片
- def load_img_number(root_dir):
- files = os.listdir(root_dir)
- file_list = []
- for file in files:
- file_path = os.path.join(root_dir, file)
- file_list.append(file_path)
- return file_list
-
-
- class neuralnetwork:
- def __init__(self, inputnodes, hiddennodes, outputnodes, learningrate):
- # 输入层
- self.inodes = inputnodes
- # 隐藏层
- self.hnodes = hiddennodes
- # 输出层
- self.onodes = outputnodes
- # 学习率
- self.lr = learningrate
- # 输入层-隐藏层权重
- self.wih = (np.random.normal(0.0, pow(self.hnodes, -0.5), (self.hnodes, self.inodes)))
- # 隐藏层-输出层权重
- self.who = (np.random.normal(0.0, pow(self.onodes, -0.5), (self.onodes, self.hnodes)))
- # 激活函数
- self.activation_function = lambda x: scipy.special.expit(x)
-
- def train(self, inputs_list, targets_list):
- inputs = np.array(inputs_list, ndmin=2).T
- targets = np.array(targets_list, ndmin=2).T
- hidden_inputs = np.dot(self.wih, inputs)
- hidden_outputs = self.activation_function(hidden_inputs)
- final_inputs = np.dot(self.who, hidden_outputs)
- final_outputs = self.activation_function(final_inputs)
- output_errors = targets - final_outputs
- hidden_errors = np.dot(self.who.T, output_errors)
- self.who += self.lr * np.dot((output_errors * final_outputs * (1.0 - final_outputs)),
- np.transpose(hidden_outputs))
- self.wih += self.lr * np.dot((hidden_errors * hidden_outputs * (1.0 - hidden_outputs)), np.transpose(inputs))
-
- def query(self, inputs_list):
- inputs = np.array(inputs_list, ndmin=2).T
- hidden_inputs = np.dot(self.wih, inputs)
- hidden_outputs = self.activation_function(hidden_inputs)
- final_inputs = np.dot(self.who, hidden_outputs)
- final_outputs = self.activation_function(final_inputs)
- return final_outputs
-
-
- input_nodes = 784
- hidden_nodes = 200
- output_nodes = 10
- learning_rate = 0.2
- # 构建模型
- model = neuralnetwork(input_nodes, hidden_nodes, output_nodes, learning_rate)
- # 准备训练数据
- training_data_file = open('mnist/mnist_train.csv', 'r')
- training_data_list = training_data_file.readlines()
- # 去掉第一行标题
- training_data_list = training_data_list[1:]
- training_data_file.close()
-
- # 训练
- for record in training_data_list:
- all_values = record.split(',')
- inputs = (np.asfarray(all_values[1:]) / 255.0 * 0.99) + 0.01
- targets = np.zeros(output_nodes) + 0.01
- targets[int(all_values[0])] = 0.99
- model.train(inputs, targets)
- pass
-
- img_list = load_img_number(image_path)
-
- for i in range(len(img_list)):
- img_name = img_list[i]
- img_arr = imageio.v2.imread(img_name, mode='L')
- img_data = 255.0 - img_arr.reshape(784)
- inputs = (img_data / 255.0 * 0.99) + 0.01
- outputs = model.query(inputs)
- label = np.argmax(outputs)
- print(f'{img_name} 识别结果是 {label}')
运行代码,打印结果:
1、识别率很感人,其实很多都识别错误。
2、多次运行,结果也不一样。
3、识别不正确的基本会认为6或者8。不知道怎么会有这种奇怪的结果。
最后,给出本示例的代码和资源:https://gitee.com/buejee/aitutorial