本文涉及到的是中国大学慕课《人工智能实践:Tensorflow笔记》第四讲第七节的内容,通过tensorflow进行神经网络的简单应用,实现给图识物。如下图所示,输入一个手写数字图片,网络能够识别并返回准确的数字。
给图识物的实现步骤
tensorflow中的predict函数可以根据输入给出预测结果,通过前向传播过程实现已有网络的应用。
predict(输入数据, batch_size=整数)
注:predict 参数详解。
(1)x:输入数据,Numpy 数组(或者 Numpy 数组的列表,如果模型有多个输出);
(2)batch_size:整数,由于 GPU的特性,batch_size最好选用 8,16,32,64……,如果未指定,默认为 32;
(3)verbose: 日志显示模式,0 或 1;
(4)steps: 声明预测结束之前的总步数(批次样本),默认值 None;
(5)返回:预测的 Numpy 数组(或数组列表)。
由predict实现程序应用仅需三步,
第一步,复现模型,重新搭建网络,用于数据的输入和输出。网络的结构和参数与之前的相同。
model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(10, activation='softmax')])
第二步,加载参数,即读取已保存的参数。这样就能使第一步搭建的网络变成经过训练的网络,具有处理输入数据的能力。
model_save_path = './checkpoint/mnist.ckpt' # 给出参数保存的路径
model.load_weights(model_save_path) # 导入参数
第三步,输出预测结果。对待预测数据进行预处理,得到与训练数据相同格式的数据,输入到网络中,得到预测结果。通常这一步需要经过多个操作。
result = model.predict(x_predict) # x_predict为待预测数据
代码实现
代码实现的重点在于前面已经训练过神经网络模型,并将参数进行了保存。即DL with python(10)——TensorFlow实现神经网络参数的打印保存中的相关代码得以正常运行,并将结果(checkpoint文件夹)与以下代码保存在同一文件夹中。
# 导入相关模块
from PIL import Image
import numpy as np
import tensorflow as tf
# 第一步,复现模型,搭建网络
model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(10, activation='softmax')])
# 第二步,加载参数,即读取已保存的参数
model_save_path = './checkpoint/mnist.ckpt' # 给出参数保存的路径
model.load_weights(model_save_path) # 导入参数
# 第三步,识别并输出结果
preNum = int(input("input the number of test pictures:")) # 输入要执行多少次图像识别任务
for i in range(preNum): # 读入待识别的图片
image_path = input("the path of test picture:") # 输入要识别的图片名称
img = Image.open(image_path) # 输入图片
img = img.resize((28, 28), Image.ANTIALIAS) # 将图片转换为28*28的格式
img_arr = np.array(img.convert('L')) # 转换为灰度图
img_arr = 255 - img_arr # 输入的是白底黑字,训练用的是黑底白字,颜色取反
img_arr = img_arr / 255.0 # 归一化
print("img_arr:",img_arr.shape) # 打印输入的格式
x_predict = img_arr[tf.newaxis, ...] # 在二维的img_arr前面增加一个新的维度
print("x_predict:",x_predict.shape) # 打印最终输入的格式
result = model.predict(x_predict) # 得到识别结果
pred = tf.argmax(result, axis=1) # 得到result中最大数值的索引(即识别结果)
sess = tf.compat.v1.Session()
print(sess.run(pred)) # 打印识别结果
注:
1、输出结果 pred 是张量,需要引用sess然后再print,直接print 打印出来是 tf.Tensor([1],shape=(1,), dtype=int64);
2、去掉二值化,会出现无法收敛的问题,因此需要对数据集进行归一化。
不同的预处理方法
由于给出的待识别图片是白底黑字,用于网络训练的图片是黑底白字。因此需要将待识别图片进行转换,使得网络可以正确处理。
在前面的代码中,图片预处理的方法是第三步中的一句代码:
img_arr = 255 - img_arr # 输入的是白底黑字,训练用的是黑底白字,颜色取反
还可以将这一句换成以下代码,将图片变为只有黑色和白色的高对比度图片。使用嵌套for循环,遍历图片的每一个像素点,将灰度值<200的像素点变为255,纯白色;将其余的像素点变为0,纯黑色。这种预处理在保留图片有用信息的同时,滤去了背景噪声,图片更干净,当阈值(200)选择合理时,识别效果会更好。
就我的观察而言,这一种预处理方法的效果更好。
for i in range(28):
for j in range(28):
if img_arr[i][j] < 200:
img_arr[i][j] = 255
else:
img_arr[i][j] = 0
批量识别
在课程中讲解的代码实现的是根据命令调用一张图片,并给出识别结果。这是一种简单的应用,逐一识别,可以直观的体现识别的效果。在科研方面,通常需要的是对大量的数据进行批量识别。如一次性地输出1000张图片的识别结果。要实现这种应用,需要对前面的代码进行改进。
对第三步进行了修改
# 导入相关模块
from PIL import Image
import numpy as np
import tensorflow as tf
import os
# 第一步,复现模型,搭建网络
model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(10, activation='softmax')])
# 第二步,加载参数,即读取已保存的参数
model_save_path = './checkpoint/mnist.ckpt' # 给出参数保存的路径
model.load_weights(model_save_path) # 导入参数
# 第三步,预测结果
path = './mnist_test' # 输入文件夹地址
files = os.listdir(path) # 读入文件夹
preNum = len(files) # 统计要执行多少次图像识别任务
num = []
for i in range(preNum): # 读入待识别的图片
image_path = path +'//' +files[i] # 输入要识别的图片名称
img = Image.open(image_path) # 输入图片
img = img.resize((28, 28), Image.ANTIALIAS) # 将图片转换为28*28的格式
img_arr = np.array(img.convert('L')) # 转换为灰度图
# 第一种预处理方法
#img_arr = 255 - img_arr # 输入的是白底黑字,训练用的是黑底白字,颜色取反
# 第二种预处理方法
for i in range(28):
for j in range(28):
if img_arr[i][j] < 200:
img_arr[i][j] = 255
else:
img_arr[i][j] = 0
img_arr = img_arr / 255.0 # 归一化
x_predict = img_arr[tf.newaxis, ...] # 在二维的img_arr前面增加一个新的维度
result = model.predict(x_predict) # 得到识别结果
pred = tf.argmax(result, axis=1) # 得到result中最大数值的索引(即识别结果)
sess = tf.compat.v1.Session()
a = sess.run(pred) # 将pred的值保存到a中
num.append(int(a)) # 将a逐个添加到num列表中
print("图片名称:识别结果")
xinhua = dict(zip(files,num)) # 将图片名称和对应识别结果组合成字典
for key,value in xinhua.items(): # 将图片名称和对应识别结果对应输出
print('{key}:{value}'.format(key = key, value = value))
第一种预处理方法的运行结果,可以看到,只有60%的准确率,比较菜。不排除数量太少的原因,有待进一步研究。
图片名称:识别结果
0.png:6
1.png:3
2.png:2
3.png:3
4.png:6
5.png:5
6.png:6
7.png:7
8.png:8
9.png:3
第二种预处理方法的运行结果,可以看到,准确率达到了100%,比较好。
图片名称:识别结果
0.png:0
1.png:1
2.png:2
3.png:3
4.png:4
5.png:5
6.png:6
7.png:7
8.png:8
9.png:9
下一步就是用我自己的数据生成数据集,并进行一系列的网络训练和测试,期待有不错的结果。