TensorFlow下利用MNIST训练模型并识别自己手写的数字

最近一直在学习李宏毅老师的机器学习视频教程,学到和神经网络那一块知识的时候,我觉得单纯的学习理论知识过于枯燥,就想着自己动手实现一些简单的Demo,毕竟实践是检验真理的唯一标准!!!但是网上很多的与tensorflow或者神经网络相关的Demo教程都只是在验证官方程序的过程,而如何把这些程序变成自己可以真正利用的程序这一块的资料就比较少,就好比被“玩烂的"MNIST数据集(ML界的”hello world"),网上是有很多手写数字识别的教程,但那些利用的都是官方提供的数据集,这样就算验证成功了带来的满足感还是远远不够!废话不多说,接下来就让我来介绍一下如何使用Tensorflow和MNIST识别自己写的数字(比如下图这个我写的数字5~~)

本文也参考了某些大神博客的内容。希望能帮助和我一样刚刚起步的同学,大家多多指教。

相应的代码和官方以及自己的数据集:https://github.com/tgpcai/digit_recognition

目录:

(1)MNIST数据集简介

(2)利用MNIST数据集训练模型

(3)自己手写数字,并用matlab进行预处理

(4)将图片输入网络进行识别

(5)实践过程遇到的坑与总结


(1)MNIST数据集简介

既然我们要构建自己的数据集,那我们就必须要了解官方提供的数据集的格式,大小等一些特征。MNIST是一个巨大的手写数字数据集,被广泛应用于机器学习识别领域。MNIST有60000张训练集数据和10000张测试集数据,每一个训练元素都是28*28像素的手写数字图片,而且都是黑白色构成(这里的黑色是一个0-1的浮点数,黑色越深表示数值越靠近1)。在网上搜索一下MNIST,你可以发现图片长这样:

上图就是4张MNIST图片。这些图片并不是传统意义上的png或者jpg格式的图片,因为png或者jpg的图片格式,会带有很多干扰信息,所以我们在创建自己的数据集的时候就必须进行预处理。

 划重点:28*28像素,灰度图

(2)利用MNIST数据集训练模型,并保存模型

该Demo使用的模型主要是CNN卷积神经网络,该模型广泛应用于图片识别、自然语言处理等方向。有关CNN卷积神经网络的知识在我的其他博客中有详细介绍,欢迎大家一起交流!上代码:

  1 import tensorflow as tf
  2 from tensorflow.examples.tutorials.mnist import input_data
  3
  4
  5 #定义初始化权重的函数
  6 def weight_variavles(shape):
  7     w = tf.Variable(tf.truncated_normal(shape, stddev=0.1))
  8     return w
  9
 10 #定义一个初始化偏置的函数
 11 def bias_variavles(shape):
 12     b = tf.Variable(tf.constant(0.1, shape=shape))
 13     return b
 14
 15
 16 def model():
 17
 18     #1.建立数据的占位符 x [None, 784]  y_true [None, 10]
 19     with tf.variable_scope("date"):
 20         x = tf.placeholder(tf.float32, [None, 784])
 21
 22         y_true = tf.placeholder(tf.float32, [None, 10])
 23
 24     #2.卷积层1  卷积:5*5*1,32个filter,strides= 1-激活-池化
 25     with tf.variable_scope("conv1"):
 26         #随机初始化权重
 27         w_conv1 = weight_variavles([5, 5, 1, 32])
 28         b_conv1 = bias_variavles([32])
 29
 30         #对x进行形状的改变[None, 784] ----- [None,28,28,1]
 31         x_reshape = tf.reshape(x,[-1, 28, 28, 1])  #不能填None,不知道就填-1
 32
 33         # [None,28, 28, 1] -------- [None, 28, 28, 32]
 34         x_relu1 = tf.nn.relu(tf.nn.conv2d(x_reshape, w_conv1, strides=[1, 1, 1, 1], padding = "SAME") + b_conv1)
 35
 36         #池化 2*2,步长为2,【None, 28,28, 32]--------[None,14, 14, 32]
 37         x_pool1 = tf.nn.max_pool(x_relu1, ksize=[1, 2, 2, 1],strides = [1,2,2,1],padding = "SAME")
 38
 39     #3.卷积层2  卷积:5*5*32,64个filter,strides= 1-激活-池化
 40     with tf.variable_scope("conv2"):
 41         #随机初始化权重和偏置
 42         w_conv2 = weight_variavles([5, 5, 32, 64])
 43         b_conv2 = bias_variavles([64])
 44
 45         #卷积、激活、池化
 46         #[None,14, 14, 32]----------【NOne, 14, 14, 64]
 47         x_relu2 = tf.nn.relu(tf.nn.conv2d(x_pool1, w_conv2,strides=[1, 1, 1, 1], padding = "SAME") + b_conv2)
 48
 49         #池化 2*2,步长为2 【None, 14,14,64]--------[None,7, 7, 64]
 50         x_pool2 = tf.nn.max_pool(x_relu2, ksize=[1, 2, 2, 1],strides = [1,2,2,1],padding = "SAME")
 51
 52     #4.全连接层 [None,7, 7, 64] --------- [None, 7*7*64] * [7*7*64, 10]+[10] = [none, 10]
 53     with tf.variable_scope("fc"):
 54         #随机初始化权重和偏置:
 55         w_fc = weight_variavles([7 * 7 * 64, 1024])
 56         b_fc = bias_variavles([1024])
 57
 58         #修改形状 [none, 7, 7, 64] ----------[None, 7*7*64]
 59         x_fc_reshape = tf.reshape(x_pool2,[-1,7 * 7 * 64])
 60         h_fc1 = tf.nn.relu(tf.matmul(x_fc_reshape, w_fc) + b_fc)
 61
 62         # 在输出之前加入dropout以减少过拟合
 63         keep_prob = tf.placeholder("float")
 64         h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
 65
 66         w_fc1 = weight_variavles([1024, 10])
 67         b_fc1 = bias_variavles([10])
 68
 69         #进行矩阵运算得出每个样本的10个结果[NONE, 10],输出
 70         y_predict = tf.nn.softmax(tf.matmul(h_fc1_drop, w_fc1) + b_fc1)
 71
 72     return x, y_true, y_predict,keep_prob
 73
 74
 75 def conv_fc():
 76     #获取数据,MNIST_data是楼主用来存放官方的数据集,如果你要这样表示的话,那MNIST_data这个文件夹应该和这个python文件在同一目录
 77     mnist = input_data.read_data_sets(‘MNIST_data‘, one_hot=True)
 78
 79     #定义模型,得出输出
 80     x,y_true,y_predict,keep_prob = model()
 81
 82     #进行交叉熵损失计算
 83     #3.计算交叉熵损失
 84     with tf.variable_scope("soft_cross"):
 85         #求平均交叉熵损失,tf.reduce_mean对列表求平均值
 86         loss = -tf.reduce_sum(y_true*tf.log(y_predict))
 87
 88     #4.梯度下降求出最小损失,注意在深度学习中,或者网络层次比较复杂的情况下,学习率通常不能太高
 89     with tf.variable_scope("optimizer"):
 90
 91         train_op = tf.train.AdamOptimizer(1e-4).minimize(loss)
 92
 93     #5.计算准确率
 94     with tf.variable_scope("acc"):
 95
 96         equal_list = tf.equal(tf.argmax(y_true, 1), tf.argmax(y_predict, 1))
 97         #equal_list None个样本 类型为列表1为预测正确,0为预测错误[1, 0, 1, 0......]
 98
 99         accuray = tf.reduce_mean(tf.cast(equal_list, tf.float32))
100
101     init_op = tf.global_variables_initializer()
102
103     saver = tf.train.Saver()
104
105     #开启会话运行
106     with tf.Session() as sess:
107         sess.run(init_op)
108         for i in range(3000):
109             mnist_x, mnist_y = mnist.train.next_batch(50)
110             if i%100 == 0:
111                 # 评估模型准确度,此阶段不使用Dropout
112                 train_accuracy = accuray.eval(feed_dict={x:mnist_x, y_true: mnist_y, keep_prob: 1.0})
113                 print("step %d, training accuracy %g"%(i, train_accuracy))
114
115             # 训练模型,此阶段使用50%的Dropout
116             train_op.run(feed_dict={x:mnist_x, y_true: mnist_y, keep_prob: 0.5})
117         # 将模型保存在你自己想保存的位置
118         saver.save(sess, "D:/Dict/model/fc_model.ckpt")
119
120     return None
121
122 if __name__ == "__main__":
123     conv_fc()

然后在你保存模型的目录下会产生4个文件

.data文件是用来记录权重,偏置等参数信息;.meta是用来记录tensorflow图的结构。以下是我的电脑的结果图:

我只运行了6000次,按照tensorflow官方文档,运行9000次左右可以达到0.992左右的正确率

(3)自己手写数字,并用matlab进行预处理

首先让我们看一下预处理的结果:->->

具体过程也就是分为3个步骤:缩小它的大小为28*28像素,并转变为灰度图,最后进行二值化处理。具体matlab的代码如下:

clear all; close all; clc;
% 改图片像素为28*28

I=imread(‘5.jpg‘); %你自己手写的数字的图片
J=imresize(I,[28,28]);
imshow(I);
figure;
imshow(J);
imwrite(J,‘new5.jpp‘);%生成28*28手写数字图片

接下来进行灰度与二值化处理

clear all;close all;clc;
% Read an input image
A = imread(‘new5.jpg‘);

% Convert the image to single-channel grayscale image
A_gray = rgb2gray(A);

figure,imhist(A_gray),title(‘hist of A_grey‘);

% Convert image to double i.e., [0,1]
A_gray = im2double(A_gray);

% Generate threhold value using Otsu‘s algorithm
otsu_level = graythresh(A_gray);

% Threshold image using Otsu‘s threshold and manually defined
% threshold values
B_otsu_thresh = im2bw(A_gray, otsu_level);
B_thresh_50 = im2bw(A_gray, 50/255);
B_thresh_100 = im2bw(A_gray, 100/255);
B_thresh_150 = im2bw(A_gray, 150/255);
B_thresh_200 = im2bw(A_gray, 200/255);

% Display original and thresholded binary images side-by-side
figure, subplot(2, 3, 1), imshow(A_gray), title(‘Original image‘);
subplot(2, 3, 2), imshow(B_otsu_thresh), title(‘Binary image using Otsu threshold value‘);
subplot(2, 3, 3), imshow(B_thresh_50), title(‘Binary image using threshold value = 50‘);
subplot(2, 3, 4), imshow(B_thresh_100), title(‘Binary image using threshold value = 100‘);
subplot(2, 3, 5), imshow(B_thresh_150), title(‘Binary image using threshold value = 150‘);
subplot(2, 3, 6), imshow(B_thresh_200), title(‘Binary image using threshold value = 200‘);
imwrite(B_otsu_thresh,‘newnew5.jpg‘);%填写你希望最终生成的数据集的名字和路径

到此就完成了对自己手写图片的预处理过程!

预处理的方法有很多,在这我在介绍一种利用OPENCV进行预处理:

import cv2

global img
global point1, point2
def on_mouse(event, x, y, flags, param):
    global img, point1, point2
    img2 = img.copy()
    if event == cv2.EVENT_LBUTTONDOWN:         #左键点击
        point1 = (x,y)
        cv2.circle(img2, point1, 10, (0,255,0), 5)
        cv2.imshow(‘image‘, img2)
    elif event == cv2.EVENT_MOUSEMOVE and (flags & cv2.EVENT_FLAG_LBUTTON):   #按住左键拖曳
        cv2.rectangle(img2, point1, (x,y), (255,0,0), 5) # 图像,矩形顶点,相对顶点,颜色,粗细
        cv2.imshow(‘image‘, img2)
    elif event == cv2.EVENT_LBUTTONUP:         #左键释放
        point2 = (x,y)
        cv2.rectangle(img2, point1, point2, (0,0,255), 5)
        cv2.imshow(‘image‘, img2)
        min_x = min(point1[0], point2[0])
        min_y = min(point1[1], point2[1])
        width = abs(point1[0] - point2[0])
        height = abs(point1[1] -point2[1])
        cut_img = img[min_y:min_y+height, min_x:min_x+width]
        resize_img = cv2.resize(cut_img, (28,28)) # 调整图像尺寸为28*28
        ret, thresh_img = cv2.threshold(resize_img,127,255,cv2.THRESH_BINARY) # 二值化
        cv2.imshow(‘result‘, thresh_img)
        cv2.imwrite(‘new5.jpg‘, thresh_img)  # 预处理后图像保存位置

def main():
    global img
    img = cv2.imread(‘5.jpg‘)  # 手写数字图像所在位置
    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 转换图像为单通道(灰度图)
    cv2.namedWindow(‘image‘)
    cv2.setMouseCallback(‘image‘, on_mouse) # 调用回调函数
    cv2.imshow(‘image‘, img)
    cv2.waitKey(0)

if __name__ == ‘__main__‘:
    main()

以上两种方法都可以,甚至还有大神利用PS自己生成数据集,感兴趣的同学可以自己去搜索一下~

(4)将图片输入网络进行识别

完成图像预处理后,即可将图片输入到网络中进行识别

  1 from PIL import Image, ImageFilter
  2 import tensorflow as tf
  3 import matplotlib.pyplot as plt
  4
  5
  6 def imageprepare():
  7     im = Image.open(‘C:/Users/tgp/Desktop/newnew5.jpg‘)
  8     plt.imshow(im)
  9     data = list(im.getdata())
 10     result = [(255-x)*1.0/255.0 for x in data]
 11     return result
 12
 13
 14 #定义初始化权重的函数
 15 def weight_variavles(shape):
 16     w = tf.Variable(tf.truncated_normal(shape, stddev=0.1))
 17     return w
 18
 19 #定义一个初始化偏置的函数
 20 def bias_variavles(shape):
 21     b = tf.Variable(tf.constant(0.0, shape=shape))
 22     return b
 23
 24
 25 def model():
 26     tf.reset_default_graph()
 27     #1.建立数据的占位符 x [None, 784]  y_true [None, 10]
 28     with tf.variable_scope("date"):
 29         x = tf.placeholder(tf.float32, [None, 784])
 30
 31         #y_true = tf.placeholder(tf.float32, [None, 10])
 32
 33
 34
 35     #2.卷积层1  卷积:5*5*1,32个filter,strides= 1-激活-池化
 36     with tf.variable_scope("conv1"):
 37         #随机初始化权重
 38         w_conv1 = weight_variavles([5, 5, 1, 32])
 39         b_conv1 = bias_variavles([32])
 40
 41         #对x进行形状的改变[None, 784] ----- [None,28,28,1]
 42         x_reshape = tf.reshape(x,[-1, 28, 28, 1])  #不能填None,不知道就填-1
 43
 44         # [None,28, 28, 1] -------- [None, 28, 28, 32]
 45         x_relu1 = tf.nn.relu(tf.nn.conv2d(x_reshape, w_conv1, strides=[1, 1, 1, 1], padding = "SAME") + b_conv1)
 46
 47         #池化 2*2,步长为2,【None, 28,28, 32]--------[None,14, 14, 32]
 48         x_pool1 = tf.nn.max_pool(x_relu1, ksize=[1, 2, 2, 1],strides = [1,2,2,1],padding = "SAME")
 49
 50     #3.卷积层2  卷积:5*5*32,64个filter,strides= 1-激活-池化
 51     with tf.variable_scope("conv2"):
 52         #随机初始化权重和偏置
 53         w_conv2 = weight_variavles([5, 5, 32, 64])
 54         b_conv2 = bias_variavles([64])
 55
 56         #卷积、激活、池化
 57         #[None,14, 14, 32]----------【NOne, 14, 14, 64]
 58         x_relu2 = tf.nn.relu(tf.nn.conv2d(x_pool1, w_conv2,strides=[1, 1, 1, 1], padding = "SAME") + b_conv2)
 59
 60         #池化 2*2,步长为2 【None, 14,14,64]--------[None,7, 7, 64]
 61         x_pool2 = tf.nn.max_pool(x_relu2, ksize=[1, 2, 2, 1],strides = [1,2,2,1],padding = "SAME")
 62
 63     #4.全连接层 [None,7, 7, 64] --------- [None, 7*7*64] * [7*7*64, 10]+[10] = [none, 10]
 64     with tf.variable_scope("fc"):
 65         #随机初始化权重和偏置:
 66         w_fc = weight_variavles([7 * 7 * 64, 1024])
 67         b_fc = bias_variavles([1024])
 68
 69         #修改形状 [none, 7, 7, 64] ----------[None, 7*7*64]
 70         x_fc_reshape = tf.reshape(x_pool2,[-1,7 * 7 * 64])
 71         h_fc1 = tf.nn.relu(tf.matmul(x_fc_reshape, w_fc) + b_fc)
 72
 73         keep_prob = tf.placeholder("float")
 74         h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)
 75
 76         w_fc1 = weight_variavles([1024, 10])
 77         b_fc1 = bias_variavles([10])
 78
 79         #进行矩阵运算得出每个样本的10个结果[NONE, 10]
 80         #y_predict = tf.matmul(h_fc1_drop, w_fc1) + b_fc1
 81         y_predict = tf.nn.softmax(tf.matmul(h_fc1_drop, w_fc1) + b_fc1)
 82     return x, y_predict,keep_prob
 83
 84
 85 def conv_fc():
 86     #获取数据
 87     result = imageprepare()
 88
 89     #定义模型,得出输出
 90     x,y_predict,keep_prob = model()
 91
 92     init_op = tf.global_variables_initializer()
 93
 94     saver = tf.train.Saver()
 95
 96     #开启会话运行
 97     #tf.reset_default_graph()
 98     with tf.Session() as sess:
 99         sess.run(init_op)
100         print(result)
101         saver.restore(sess, "D:/Dict/model/fc_model.ckpt")
102         prediction = tf.argmax(y_predict,1)
103         predint = prediction.eval(feed_dict={x: [result],keep_prob: 1.0}, session=sess)
104         print(predint)
105         print("recognize result: %d" %predint[0])
106
107
108     return None
109
110 if __name__ == "__main__":
111     conv_fc()

运行结果如下:

(5)实践过程遇到的坑与总结

  • 刚开始写训练模型的代码的时候,我认为不需要防止过拟合这个处理过程,所以在我的模型里面没有防止过拟合这一操作,直接导致的结果是:在训练模型的时候效果非常不错,但是当真正拿自己手写数字去识别的时候,经常把‘4’和‘9’搞错。随便我在输出层和全连接层中间添加了一些代码用于防止过拟合,这样训练出的模型表现结果尚佳!由此可见,在训练模型的时候防止过拟合的操作还是非常有必要的。
  • 有关随机初始化权重和偏置的函数的选择:利用tf.truncated_normal()这个函数随机初始化权重训练出的模型的表现效果比利用tf.random_nomal()这个函数训练出的模型表现的更好,上网查询了一下,发现这两个函数有一下的区别:tf.truncated_normal的输出如字面意思是截断的,而截断的标准是2倍的stddev。使用tf.truncated_normal的输出是不可能出现[-2,2]以外的点的,而如果shape够大的话,tf.random_normal却会产生2.2或者2.4之类的输出。也就是说使用tf.random_normal产生的初始权重的值比tf.truncated_normal产生的大,这对于神经网络而言是致命的,因为这样非常容易产生梯度消失的问题。
  • 在随机初始化权重和偏置的时候,方差不能设置的过大,若方差过大,则在训练的时候准确率一直维持在很低的位置,容易产生梯度消失的问题。
  • 保存模型尽量以.ckpt结果,反正楼主一开始没有以.ckpt结尾,带来了很多麻烦,然后加上这个后缀,啥问题都消失了~(可能是玄学,不加可能也行的通,但是加了一定不会错~~)


以上就是本次实践的全部过程,欢迎大家交流讨论。

原文地址:https://www.cnblogs.com/XDU-Lakers/p/10526748.html

时间: 2024-11-09 14:54:50

TensorFlow下利用MNIST训练模型并识别自己手写的数字的相关文章

利用mnist训练集生成的caffemodel对mnist测试集与自己手写的数字进行测试

从一到二:利用mnist训练集生成的caffemodel对mnist测试集与自己手写的数字进行测试 通过从零到一的教程,我们已经得到了通过mnist训练集生成的caffemodel,主要包含下面四个文件: 接下来就可以利用模型进行测试了.关于测试方法按照上篇教程还是选择bat文件,当然python.matlab更为方便,比如可以迅速把识别错误的图片显示出来. 一.均值文件mean.binaryproto 在进行分类之前首先需要产生所有图片的平均值图片,真正分类时的每个图片都会先减去这张平均值图片

从零到一:caffe-windows(CPU)配置与利用mnist数据集训练第一个caffemodel

一.前言 本文会详细地阐述caffe-windows的配置教程.由于博主自己也只是个在校学生,目前也写不了太深入的东西,所以准备从最基础的开始一步步来.个人的计划是分成配置和运行官方教程,利用自己的数据集进行训练和利用caffe来实现别人论文中的模型(目前在尝试的是轻量级的SqueezeNet)三步走.不求深度,但求详细.因为说实话caffe-windows的配置当初花了挺多时间的,目前貌似还真没有从头开始一步步讲起的教程,所以博主就争取试着每一步都讲清楚吧. 这里说些题外话:之所以选择Sque

基于tensorflow的MNIST手写识别

这个例子,是学习tensorflow的人员通常会用到的,也是基本的学习曲线中的一环.我也是! 这个例子很简单,这里,就是简单的说下,不同的tensorflow版本,相关的接口函数,可能会有不一样哟.在TensorFlow的中文介绍文档中的内容,有些可能与你使用的tensorflow的版本不一致了,我这里用到的tensorflow的版本就有这个问题. 另外,还给大家说下,例子中的MNIST所用到的资源图片,在原始的官网上,估计很多人都下载不到了.我也提供一下下载地址. 我的tensorflow的版

数据挖掘入门系列教程(八)之使用神经网络(基于pybrain)识别数字手写集MNIST

目录 数据挖掘入门系列教程(八)之使用神经网络(基于pybrain)识别数字手写集MNIST 下载数据集 加载数据集 构建神经网络 反向传播(BP)算法 进行预测 F1验证 总结 参考 数据挖掘入门系列教程(八)之使用神经网络(基于pybrain)识别数字手写集MNIST 在本章节中,并不会对神经网络进行介绍,因此如果不了解神经网络的话,强烈推荐先去看<西瓜书>,或者看一下我的上一篇博客:数据挖掘入门系列教程(七点五)之神经网络介绍 本来是打算按照<Python数据挖掘入门与实践>

图片训练:使用卷积神经网络(CNN)识别手写数字

这篇文章中,我们将使用CNN构建一个Tensorflow.js模型来分辨手写的数字.首先,我们通过使之“查看”数以千计的数字图片以及他们对应的标识来训练分辨器.然后我们再通过此模型从未“见到”过的测试数据评估这个分辨器的精确度. 一.运行代码 这篇文章的全部代码可以在仓库TensorFlow.js examples中的tfjs-examples/mnist 下找到,你可以通过下面的方式clone下来然后运行这个demo: $ git clone https://github.com/tensor

识别手写数字增强版 - pytorch从入门到入道(一)

1.快速开始 1.1 定义神经网络类,继承torch.nn.Module,文件名为digit_recog.py 1 import torch.nn as nn 2 3 4 class Net(nn.Module): 5 def __init__(self): 6 super(Net, self).__init__() 7 self.conv1 = nn.Sequential(nn.Conv2d(1, 6, 5, 1, 2) 8 , nn.ReLU() 9 , nn.MaxPool2d(2, 2)

C++使用matlab卷积神经网络库MatConvNet来进行手写数字识别

环境:WIN10(64 bit)+VS2010(64 bit)+Matlab2015b(64 bit) 关于MatConvNet的介绍参考:http://www.vlfeat.org/matconvnet/ Github下载地址为:https://github.com/vlfeat/matconvnet/ 我们的目的是将MatConvNet自带的手写数字识别DEMO移植到一个简单的WIN32 DEMO中使用,主要过程有以下几个步骤: (1)配置MatConvNet,然后将手写数字识别DEMO编译

《神经网络和深度学习》系列文章一:使用神经网络识别手写数字

出处: Michael Nielsen的<Neural Network and Deep Leraning> 本节译者:哈工大SCIR硕士生 徐梓翔 (https://github.com/endyul) 声明:我们将不定期连载该书的中文翻译,如需转载请联系[email protected],未经授权不得转载. “本文转载自[哈工大SCIR]微信公众号,转载已征得同意.” 使用神经网络识别手写数字 感知机 sigmoid神经元 神经网络的结构 用简单的网络结构解决手写数字识别 通过梯度下降法学

BP神经网络识别手写数字项目解析及代码

这两天在学习人工神经网络,用传统神经网络结构做了一个识别手写数字的小项目作为练手.点滴收获与思考,想跟大家分享一下,欢迎指教,共同进步. 平常说的BP神经网络指传统的人工神经网络,相比于卷积神经网络(CNN)来说要简单些. 人工神经网络具有复杂模式和进行联想.推理记忆的功能, 它是解决某些传统方法所无法解决的问题的有力工具.目前, 它日益受到重视, 同时其他学科的发展, 为其提供了更大的机会.1986 年, Romelhart 和Mcclelland提出了误差反向传播算法(Error Back