基于TensorFlow+Keras的手写数字识别

基于TensorFlow+Keras的手写数字识别

一、目的
1、编写算法训练数据集
2、编写算法预测训练数据集
3、学习并掌握识别手写数字的算法
4、在实现要求的基础上优化自己的算法,能够取得更好的预测效果
5、在满足要求的基础上,尽可能有较高的预测正确率
6、发散自己的思维,用最好的方法得到最后的结果
7、优化参数,多次调参,调优,尽可能使训练出的模型更好
8、对模型进行全面客观的评价
9、针对本实验自我目的要求掌握TensorFlow+Keras算法框架的原理
10、掌握数据处理与转换,模型训练,预测,评估
11、提交结果到Kaggle检测得分
二、内容
2.1、安装并配置环境
本次手写数字识别项目我用的TensorFlow+Keras,所以首先要有环境,先安装Anaconda,再安装TensorFlow,最后安装Keras。其实安装并不难,因为版本匹配导致了很多问题,我前后安装了不少于五次才成功安装,所以有必要在这里介绍一下:
1、 Anaconda
安装Anaconda很简单,和普通软件一样,就不作介绍,值得一提的是在“Add anaconda to the system PATH environment variable”那里还是很有必要选上的,避免后面安装一些库导致找不到的报错信息
2、 TensorFlow
首先因为各种版本问题,不建议在Anaconda的默认环境(base)下安装TensorFlow和Keras,不排除把原来的环境搞垮的可能性,所以我新建了一个环境单独做这个项目,我新建了python3.6的环境,取名为tensorflow4:
打开Anaconda Prompt,默认进入的环境是base;
输入“conda create --name tensorflow4 python=3.6”;
然后激活或者说进入环境,继续输入“activate tensorflow4”,这是行首的环境名就会变为tensorflow4;
然后安装tensorflow的版本为1.13.1,输入“pip install tensorflow==1.13.1”,这里需要注意,如果没有配置pip去国内镜像找,那么下载的就会很慢,则可以在语句后加上
-i https://pypi.tuna.tsinghua.edu.cn/simple/”,我之前配置过,就不需要加上这个链接。
然后测试是否安装成功,输入“python”就会进入python环境,在python命令下输入“import tensorflow as tf”,如果没有报错信息,说明安装成功。
3、 Keras
Keras是基于TensorFlow的,所以先安装TensorFlow。
我安装的Keras版本是2.2.1,具体的安装步骤如下,还是在tensorflow4的环境下:
依次输入以下三条指令

pip install pyyaml
pip install keras==2.2.1
conda install mingw libpython

测试Keras是否安装成功,输入“python”就会进入python环境,在python命令下输入“import keras”,如果没有报错信息,说明安装成功
2.2、数据加载和转换
Keras是一个很熟知的框架,对于出具数据的输入和输出都具有一定的格式,我们需要把从原始csv文件读取出来的数据转换成具有一定数据结构的数据,例如数组,读取的内容是文本字符串,需要转换成整型或者浮点型。
2.3、模型训练与优化
当把实验所需要的数据已经准备就绪后,就需要编写实现数字识别的算法了,我用的是TensorFlow+Keras框架,里面有大量的API供我们调用,我们需要调整损失函数的类型,迭代次数,控制神经网络的隐藏层的每层的神经元个数。输入层和输出层的神经元个数是固定的,分别是784和10。
首先把训练数据集train.csv里面的42000条数据分成训练集合(前40000条)和测试集(后2000条),训练集用于训练自己的模型,得出一个可靠度较高的分类器,用测试集去预测自己的测试集,得出的预测值与真实值相比较,得出模型的损失率和准确率,如果得出较低的损失率和较高的准确率,说明模型有比较好的效果。
优化方面就需要我对参数多做次调整,求最优的一组合,并设置合适的损失函数,神经网络的隐藏层再加一层或者调整神经元的个数。
如果出现过拟合的情况,还需要设置Dropout函数可以来阻止特征检测器的共同作用来提高神经网络的性能。

2.4、模型应用
训练好的模型就可以预测真实的数据了,test.csv文件里有28000行数据分别对应28000个数字,预测的结果分别对应写在sample_submission.csv文件里
三、数据清洗
3.1、数据切分

首先,需要把读取数据集train.csv
#导入文件数据
import csv
filtpath = “G:\0_我是U盘 对 我就是\大三下学期\机器学习\期末作业\《机器学习》期末综合实训\数字识别_原始数据\train.csv”

with open(filtpath,'r') as csvfile:
    reader = csv.reader(csvfile)
    header = next(reader)#标题
    data = [] #42000行数据
    for line in reader:
        data.append(line)

其中,header就是pixel0,pixel1…pixel4,代表2828=784个像素值,data就是42000785的
‘矩阵值’,其中第一列是数据标签,代表后784个灰度图像素数值代表的数字,有了这些数据认识,就可以对数据进行切分。
切分为训练集和测试集,每一个数据集要分标签_lables和灰度图像素值_iamge

#print(type(data))  list
#print(len(data))   #42000
#print(len(data[0])) # 28*28+1=785  1列标签 784数据
#42000条数据
#前40000条当作训练集
#后2000条当作测试集

#提取前40000行作为训练集
train_img = []  #训练数据
train_ans = []  #训练结果标签
for i in range(40000):
    train_ans.append(data[i][0]) #标签
    train_img.append(data[i][1:])  #训练数据

#提取后2000行作为测试集    
test_img = []  #测试数据
test_ans = []  #测试的真实值标签
for i in range(2000):
    test_ans.append(data[i+40000][0]) #标签
    test_img.append(data[i+40000][1:])  #训练数据
print('train_img行数:',len(train_img),'       每行像素:',len(train_img[0]))
print('train_ans标签数:',len(train_ans)) #前40000行,第一列

print('test_img:',len(test_img),'           每行像素:',len(test_img[0]))
print('test_ans标签数:',len(test_ans)) #后2000行,第一列

结果为:

train_img行数: 40000 每行像素: 784
train_ans标签数: 40000
test_img: 2000 每行像素: 784
test_ans标签数: 2000

我把data数据集切分了两大部分,训练集的灰度图train_img,训练集的标签train_ans,还有测试集的灰度图test_img,测试集的标签test_ans。
3.2、数据类型转换
根据Keras的框架要求,输入的数据是有一定要求的,要求输入的数据为数字,查看训练集和测试集的数据类型是为字符串,鉴于Keras内部计算设计权重,所以我把训练集和测试集的灰度图数字值转换为float类型,标签转化为int型,

#之前的数组都是字符串的,我们需要把数字转换为整形和浮点数
#把数据转换为浮点数,把标签转换为整形
for index in range(40000):
    for i, v,in enumerate(train_img[index]): 
        train_img[index][i] = float(v)       
for i, v in enumerate(train_ans): 
train_ans[i] = int(v)

for index in range(2000): 
    for i, v in enumerate(test_img[index]): 
        test_img[index][i] = float(v)
for i, v in enumerate(test_ans): 
    test_ans[i] = int(v) 

为了后面方面计算,和不同损失函数有不同的不同输入数据的维度,我还需要把数据转成矩阵或者数组的形式,把训练集和测试集都转换为数组和矩阵,标签和数组,灰度值为矩阵,

#前面的数据都是列表,把数组转成数组
print(type(train_img))
#把训练数据集列表转为数组
import numpy as np 
# 40000行784列 训练数据
train_data=np.zeros((40000,784)) 
# 40000个训练结果标签
train_res=np.zeros(40000) 

for i in range (40000):
    for j in range(784):
        train_data[i][j]=train_img[i][j]
for i in range(40000):
    train_res[i]=train_ans[i]
    
print(type(train_data))

结果:

<class ‘list’>
<class ‘numpy.ndarray’>

可以看出,原来的数据是用list来存储的,现在已经转为ndarray形式,把测试集也转换

#把测试数据集列表转为数组
import numpy as np 
# 2000行784列 测试数据
test_data=np.zeros((2000,784)) 
# 2000行784 测试结果标签

test_res=np.zeros(2000) 
for i in range (2000):
    for j in range(784):
        test_data[i][j]=test_img[i][j]

for i in range(2000):
    test_res[i]=int(test_ans[i])

最终,数据就统一编程以下方式:
训练集:
train_data:灰度值,矩阵
train_tes:标签,向量
测试集:
test_data:灰度值,矩阵
test_res:灰度值,向量

四、模型训练和测试
4.1、导入TensorFlow+Keras库

第一步就是先测试Tenflow和Keras在Jupyter能不能正常使用:

#测试tensorflow
import tensorflow as tf
hello=tf.constant("wangyakun is a good man!")
sess=tf.Session()
print(sess.run(hello))

结果:

b'wangyakun is a good man!'
#测试keras
import keras

结果:

Using TensorFlow backend.

结果表明TensorFlow和Keras都可以正常在Jupyter使用。
下面就导入所需要的包,Keras需要把标签09转换为onehot编码,onehot编码是一组0,1代表09,对应位置为1的下标就是原来的数字,
比如1对应的onehot编码是[0 1 0 0 0 0 0 0 0 0],5对应的onehot编码就是[0 0 0 0 0 1 0 0 0 0],
所以需要导入onehot编码需要的包to_categorical,优化器optimizers,神经网络需要layers,regularizers。

#导入需要用的包
from keras.utils import to_categorical
from keras import models,layers,regularizers
from keras.optimizers import RMSprop

4.2、建立模型:TensorFlow+Keras+神经网络+优化
把训练集和测试集的标签都用onehot编码表示:

#train_data   train_res
#test_data    test_res
train_res = to_categorical(train_res)
test_res = to_categorical(test_res)

下面就开始搭建神经网络,建立模型的过程也包括了优化,全连接的神经网络为Dense,
 输入层:
输入层为28*28=784的灰度图像素值,所以输入神经元的个数input_shape=784,分成16类,units=16,激活函数用’relu’
 输出层:
输出层就是0~9就是十个数字,把初步分类的后的16类在分到10类,激活函数为’softmax’,属于概率最大的那一类

network = models.Sequential()
network.add(layers.Dense(units=16,activation='relu',input_shape=(28*28,),))
network.add(layers.Dense(units=10, activation='softmax'))

查看一下这个只有输入层和输出层的模型的信息:

print(network.summary())

在这里插入图片描述

Output shape: 16代表输入输入层的输出为16中,输出层的输出有10中 Param:
12560:输入层输入784,输出16,每一个输出都有一个偏置
784 x 16 + 16 = 12560
170:输出层输入16,输出10,每一个输出都有一个偏置
16 x 10 + 10 = 170

因为这个模型没有隐藏层,在输入层784直接分到16类,效果并不满意只取得到90%左右的只呢个确率,下面着重讲解优化后的模型:
优化后的神经网络模型中间加了一层隐藏层,因为神经网络中间还有一些传递消息后处理的过程,现在的神经网络为:
 输入层:
输入层为28*28=784的灰度图像素值,所以输入神经元的个数input_shape=784,分类由原来的16变成128,units=128,激活函数用’relu’
 隐藏层:
隐藏层的输入神经元的个数就是输入层的输出为128,分成16类,units=16,激活函数用’relu’
 输出层:
输出层就是0~9就是十个数字,输入神经元的个数就是隐藏层输出为16,分到10类,units=10,激活函数为’softmax’.
同时,我们加入了kernel_regularizer=regularizers.l1(0.0001)和network.add(layers.Dropout(0.01))
来优化过拟合的问题。

##下面开始搭建神经网络
#全连接的神经网络   Dense
#函数为 relu 函数
#输入层为28*28个神经元
#隐藏层为16个
#输出层为10个
#每一类的概率是多少,求概率,用softmax函数
#network = models.Sequential()
#network.add(layers.Dense(units=16, activation='relu', input_shape=(28*28,),))
#network.add(layers.Dense(units=10, activation='softmax'))

#16最好
network = models.Sequential()
network.add(layers.Dense(units=128, activation='relu', input_shape=(28*28,),
kernel_regularizer=regularizers.l1(0.0001)))
network.add(layers.Dropout(0.01))
network.add(layers.Dense(units=16, activation='relu',kernel_regularizer=regularizers.l1(0.0001)))
network.add(layers.Dropout(0.01))
network.add(layers.Dense(units=10, activation='softmax'))

查看一下这个模型的信息

##查看神经网络的信息
print(network.summary())

在这里插入图片描述

Output shape: 128代表输入输入层的输出为128类,16代表隐藏层的输出为16类,输出层的输出有10类 Param:
100480:输入层输入784,输出128,每一个输出都有一个偏置
784 x 128+128 = 100480
2064:隐藏层输入128,输出16,每一个输出都有一个偏置
128 x 16+16 = 2064
170:输出层输入16,输出10,每一个输出都有一个偏置
16 x 10+10 = 170

然后就可以去编译我们的模型,用训练集去训练,训练用fit函数,损失函数用
categorical_crossentropy,优化器设为optimizer=RMSprop(lr=0.001),并限定训练20个回合。

#编译
network.compile(optimizer=RMSprop(lr=0.001),loss='categorical_crossentropy',metrics=['accuracy'])
#network.compile(optimizer=RMSprop(lr=0.001),loss='sparse_categorical_crossentropy',metrics=['accuracy'])
#训练网络,用fit函数 epochs表示训练多少个回合,
network.fit(train_data,train_res,epochs=20,batch_size=128,verbose=2)

结果:

Epoch 1/20

  • 2s - loss: 0.1478 - acc: 0.9799 Epoch 2/20
  • 1s - loss: 0.1463 - acc: 0.9800 Epoch 3/20
  • 1s - loss: 0.1445 - acc: 0.9811 Epoch 4/20
  • 1s - loss: 0.1439 - acc: 0.9809 Epoch 5/20
  • 1s - loss: 0.1397 - acc: 0.9810 Epoch 6/20
  • 1s - loss: 0.1460 - acc: 0.9802 Epoch 7/20
  • 1s - loss: 0.1433 - acc: 0.9809 Epoch 8/20
  • 1s - loss: 0.1441 - acc: 0.9804 Epoch 9/20
  • 1s - loss: 0.1399 - acc: 0.9815 Epoch 10/20
  • 1s - loss: 0.1395 - acc: 0.9821 Epoch 11/20
  • 1s - loss: 0.1423 - acc: 0.9811 Epoch 12/20
  • 1s - loss: 0.1376 - acc: 0.9817 Epoch 13/20
  • 1s - loss: 0.1398 - acc: 0.9811 Epoch 14/20
  • 1s - loss: 0.1348 - acc: 0.9820 Epoch 15/20
  • 1s - loss: 0.1394 - acc: 0.9819 Epoch 16/20
  • 1s - loss: 0.1412 - acc: 0.9817 Epoch 17/20
  • 2s - loss: 0.1403 - acc: 0.9812 Epoch 18/20
  • 1s - loss: 0.1394 - acc: 0.9815 Epoch 19/20
  • 1s - loss: 0.1338 - acc: 0.9823 Epoch 20/20
  • 1s - loss: 0.1372 - acc: 0.9820

可以看出,优化以后的模型字训练集上可以达到98.2%的准确率。
然后用predict预测一下测试集,然后和真实标签做对比,评估函数evaluate对模型做出评价

y_pre = network.predict(test_data)#用训练集的data预测,和训练级集合一直的结果比较
test_loss,test_accuracy = network.evaluate(test_data,test_res)

print("test_loss:",test_loss,"     test_accuracy:",test_accuracy)

结果:

2000/2000 [==============================] - 0s 80us/step test_loss:
0.2228182411789894 test_accuracy: 0.97

可以看到,在测试集上达到了97%的准确率

然后再看一下测试集测试的结果:

import numpy as np
from keras.utils import to_categorical
#y_pre是onehot编码,转换为数字放在 pre_list 里面
pre_list = [np.argmax(one_hot) for one_hot in y_pre]
#print(pre_list)
print("预测的前100个:")    
print(pre_list[:100])
print("真实的前100个:")
print(test_ans[:100])

结果:

预测的前100个: [2, 3, 9, 7, 8, 3, 4, 1, 0, 9, 7, 9, 1, 3, 7, 6, 3, 2, 1, 8,
5, 7, 0, 7, 3, 2, 8, 0, 0, 8, 6, 9, 8, 6, 6, 6, 3, 3, 5, 3, 4, 3, 3,
5, 0, 4, 4, 9, 5, 7, 3, 9, 3, 7, 2, 8, 4, 6, 4, 4, 7, 6, 1, 5, 9, 8,
0, 1, 4, 3, 6, 2, 1, 4, 6, 7, 3, 0, 4, 3, 6, 6, 6, 1, 9, 7, 6, 6, 1,
6, 6, 9, 4, 2, 9, 6, 3, 9, 1, 9]
真实的前100个: [2, 3, 9, 7, 8, 3, 4, 1, 0,
9, 7, 9, 1, 3, 7, 6, 2, 2, 1, 8, 5, 7, 0, 7, 3, 2, 8, 0, 0, 8, 6, 7,
8, 6, 6, 6, 3, 3, 5, 3, 4, 7, 3, 5, 0, 4, 4, 9, 5, 7, 3, 9, 3, 7, 2,
8, 4, 6, 4, 4, 7, 6, 1, 5, 9, 8, 0, 1, 4, 3, 6, 2, 1, 4, 6, 7, 3, 0,
4, 3, 6, 6, 6, 1, 9, 7, 6, 6, 1, 6, 5, 9, 4, 2, 9, 6, 5, 9, 1, 9]

预测集是2000个数据,我们预测时候,打印出前100个预测值和真实的作比较,发现100个里面有3个预测错误。
五、模型性能评估和优化
上面的模型介绍到,优化前的模型预测的准确率只有90%左右,这里介绍优化后的模型。
在上面的代码中,在模型编译训练时可以看到,设置了训练20个回合,每个回合用时都在1秒左右,并且损失率低于0.15,准确率高于98%左右
在测试集上的准确率也达到了97%
在Kaggle上面检测得分为0.96546
在这里插入图片描述
六、检测结果

训练集准确率 98.2%
训练集准确率 97%
Kaggle得分 0.96546
得出的模型效果还不错就可以预测我们的预测数据了,test.csv文件里有28000个数据需要分类0~10,同样的需要把文件读取进来,把灰度图像素值转换为矩阵和float数据类型。

filtpath = "G:\\0_我是U盘 对 我就是\\大三下学期\\机器学习\\期末作业\\《机器学习》期末综合实训\\数字识别_原始数据\\test.csv"
with open(filtpath,'r') as csvfile:
    reader = csv.reader(csvfile)
    header = next(reader)#标题
    data = [] #28000行数据
    for line in reader:
        data.append(line)
   # print(header)
#data为28000*784的向量
print(len(data))
print(len(data[0]))

28000
784

pre_data =np.zeros((28000,784))
for index in range(28000):
    for i, v,in enumerate(data[index]): pre_data[index][i] = float(v)
#pre_data是 float 型二维矩阵
print(len(pre_data))#28000
print(len(pre_data[0]))#784

28000
784

数据格式清洗好之后,就可以对数据进行预测了,预测出的结果集pre_res是onehot编码,需要对onehot解码为的0~9数字,用argmax函数进行解码,得出的结果放在pre_res_number里。

pre_res = network.predict(pre_data)#预测结果,对需要预测的数据进行预测
pre_res_number = [np.argmax(one_hot) for one_hot in pre_res]

查看一下pre_res_number,这就是预测出的28000个的数字了

print(len(pre_res_number))
print(pre_res_number)

在这里插入图片描述

然后把预测的28000个数字写到对应行号sample_submission.csv文件里就完成了。

import pandas as pd
result_file = "G:\\0_我是U盘 对 我就是\\大三下学期\\机器学习\\期末作业\\《机器学习》期末综合实训\\数字识别_原始数据\\sample_submission.csv"

image_id = pd.Series(range(1,len(pre_res_number)+1))
result = pd.DataFrame({'ImageID': image_id,'Label':pre_res_number})
result.to_csv(result_file,index = False)

查看一下是否写入,打开sample_submission.csv文件检验一下:

在这里插入图片描述
表明成功写入预测结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值