使用tf.keras搭建全连接网络,实现mnist数据集的模型训练和数字识别。
文章目录
1.自制数据集,解决本领域应用
(1)数据集结构
两个包含图片的文件夹:
mnist_train_jpg_60000:60000张图片
mnist_test_jpg_10000:10000张图片
0_5.jpg,1_7.jpg等图片都是黑底白字灰度图,28x28个像素点,每个像素点都是0-255之间的整数,纯黑色0,纯白色255。
两个标签文本:
mnist_train_jpg_60000.txt
mnist_test_jpg_10000.txt
文本中有两列:图片名 标签
0_5.jpg 5
2_9.jpg 9
(2)构造函数,替换load_data(),给x_train,y_train,x_test,y_test赋值
def generateds(图片路径,标签文件)
将图片灰度值数据拼接到图片列表,把标签数据拼接到标签列表,顺序一致就可以。
(3)制作并使用数据集的demo
# 1.导入一些模块
import tensorflow as tf
from PIL import Image # 用于打开一张图片
import numpy as np # 用于数据格式转换
import os # 路径
# 2.路径和存储文件
train_path = './mnist_image_label/mnist_train_jpg_60000/' # 训练集图片路径
train_txt = './mnist_image_label/mnist_train_jpg_60000.txt' # 训练集标签文件
x_train_savepath = './mnist_image_label/mnist_x_train.npy' # 训练集输入特征存储文件
y_train_savepath = './mnist_image_label/mnist_y_train.npy' # 训练集标签存储文件
test_path = './mnist_image_label/mnist_test_jpg_10000/' # 测试集图片路径
test_txt = './mnist_image_label/mnist_test_jpg_10000.txt' # 测试集标签文件
x_test_savepath = './mnist_image_label/mnist_x_test.npy' # 测试集输入特征存储文件
y_test_savepath = './mnist_image_label/mnist_y_test.npy' # 测试集标签存储文件
# 3.制作数据集的函数
def generateds(path, txt): # 图片路径,标签文件
f = open(txt, 'r') # 以只读的形式打开txt
contents = f.readlines() # 读取文件中所有的行,每行为一个单位
f.close()
x, y_ = [], []
for content in contents: # 逐行读出
value = content.split() # 以空格分开
img_path = path + value[0]
img = Image.open(img_path)
img = np.array(img.convert('L')) # 图片变为8位宽度的灰度值
img = img / 255. # 数据归一化
x.append(img)
y_.append(value[1])
print('load:' + content)
x = np.array(x)
y_ = np.array(y_)
y_ = y_.astype(np.int64)
return x, y_
# 4.加载数据
if os.path.exists(x_train_savepath) and os.path.exists(y_train_savepath) and os.path.exists(
x_test_savepath) and os.path.exists(y_test_savepath): # 判断x_train,y_train,x_test,y_test是否存在
print("----------------Load Datasets-------------")
x_train_save = np.load(x_train_savepath)
y_train = np.load(y_train_savepath)
x_test_save = np.load(x_test_savepath)
y_test = np.load(y_test_savepath)
x_train = np.reshape(x_train_save, (len(x_train_save), 28, 28))
x_test = np.reshape(x_test_save, (len(x_test_save), 28, 28))
else: # 不存在,则调用generateds()函数制作数据集
print('----------------Generate Datasets--------------')
x_train, y_train = generateds(train_path, train_txt)
x_test, y_test = generateds(test_path, test_txt)
# 保存制作好的数据集,下次训练可以直接使用
print('----------------Save Datasets------------------')
x_train_save = np.reshape(x_train, (len(x_train), -1))
x_test_save = np.reshape(x_test, (len(x_test), -1))
np.save(x_train_savepath, x_train_save)
np.save(y_train_savepath, y_train)
np.save(x_test_savepath, x_test_save)
np.save(y_test_savepath, y_test)
# 5.搭建网络
model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(10, activation='softmax')
])
# 6.配置参数
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
metrics=['sparse_categorical_accuracy'])
# 7.训练
model.fit(x_train, y_train,batch_size=32,epochs=5,validation_data=(x_test, y_test),validation_freq=1)
# 8.打印网络参数
model.summary()
2.数据增强,扩充数据集
(1)理解
对图像的增强,就是对图像的简单形变,用来应对因拍照角度不同引起的图片变形。
数据增强在小数据量上可以增加模型泛化性。
(2)函数
# 1.设置数据增强参数
image_gen_train = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=所有数据乘以该数值
rotation_range=随机旋转角度数范围
width_shift_range=随机宽度偏移量
height_shift_range=随机高度偏移量
horizontal_flip=是否随机水平翻转
zoom_range=随机缩放的范围[1-n, 1+n]
)
# 2.对输入特征进行数据增强
x_train = x_train.reshape(x_train.shape[0], 28, 28 ,1) # 增加一个维度,使数据与网络结构匹配
image_gen_train.fit(x_train) # 此处的x_train要输入一个四维数据,彩色图不用增加维度
# 3.变动model.fit
# 将x_train,y_train,batch打包,其余相同
model.fit(image_gen_train.flow(x_train,y_train,batch=32),...)
例子:
image_gen_train = tf.keras.preprocessing.image.ImageDataGenerator(
rescale=1. / 1., # 如果是图像,分母为255,可以归一化到0-1
rotation_range=45, # 随机45度旋转
width_shift_range=.15, # 宽度偏移
height_shift_range=.15, # 高度偏移
horizontal_flip=False, # 水平翻转
zoom_range=0.5 # 将图像随机缩放阈量50%
)
3.断点续训,存取模型
(1)读取模型:model.load_weights(路径文件名)
checkpoint_save_path = "./mnist.ckpt" # 定义存放模型的路径和文件名
if os.path.exists(checkpoint_save_path + '.index'): # 判断是否有索引表 因为生成ckpt文件时,会同步生成索引表
print('--------load the model---------')
model.load_weights(checkpoint_save_path)
(2)保存模型:使用回调函数
# 1.设置保存方法
cp_callback = tf.keras.callbacks.ModelCheckpoint(
filepath=路径文件名,
save_weights_only=True/False # 是否只保留模型参数
save_best_only=True/False) # 是否只保留最有结果
# 2.训练时加入callbacks选项,记录到history中
history = model.fit(...,callbacks=[cp_callback])
4.参数提取,把参数存入文本
(1)理解:将模型的参数保存在文本中。
(2)函数:model.trainable_variables可以返回模型中可训练的参数,使用print函数打印出来。
但是,直接print中间很多数据会被省略号代替,所以要设置print输出格式。
np.set_printoptions(threshold=超出多少省略显示)
例子:
# 1.设置print格式
import numpy as np
np.set_printoptions(threshold=np.inf) # np.inf表示无限大,打印所有内容
# 2.打印参数
print(model.trainable_variables)
# 3.将参数写入文本
file = open('./weights.txt', 'w')
for v in model.trainable_variables:
file.write(str(v.name) + '\n')
file.write(str(v.shape) + '\n')
file.write(str(v.numpy()) + '\n')
file.close()
5.acc/loss可视化,查看训练效果
(1)说明:
history = model.fit()。其实,在执行训练过程中,同步记录了
训练集loss(loss)、
测试集loss(val_loss)、
训练集准确率(sparse_categorical_accuracy)、
测试集准确率(val_sparse_categorical_accuracy)。
可以使用history.history[]提取出来。
(2)方法:
# 提取acc和loss
acc = history.history['sparse_categorical_accuracy']
val_acc = history.history['val_sparse_categorical_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']
# 绘制acc和loss曲线
plt.subplot(1, 2, 1)
plt.plot(acc, label='Training Accuracy')
plt.plot(val_acc, label='Validation Accuracy')
plt.title('Training and Validation Accuracy')
plt.legend()
plt.subplot(1, 2, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.title('Training and Validation Loss')
plt.legend()
plt.show()
6.应用程序,给图识物
(1)说明:编写一个前向传播算法,使用训练好的模型,将输入图片识别出来。
(2)函数:predict(输入特征,batch_size=整数),可以返回前向传播的计算结果。
(3)程序
# 1.复现模型(前向传播)
model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(128,activation='relu'),
tf.keras.layers.Dense(10,activation='softmax')
])
# 2.加载参数
model.load_weights(model_save_path)
# 3.数据预处理
# 4.预测结果
result = model.predict(x_predict)
完整代码:(希望自己能背过)
代码1—训练模型
# 1.导入一些模块
import tensorflow as tf
from PIL import Image # 用于打开一张图片
import os # 路径
from matplotlib import pyplot as plt # 绘图
from tensorflow.keras.preprocessing.image import ImageDataGenerator # 数据增强
import numpy as np # 用于数据格式转换
# 2.路径
train_path = './mnist_image_label/mnist_train_jpg_60000/' # 训练集图片路径
train_txt = './mnist_image_label/mnist_train_jpg_60000.txt' # 训练集标签文件
x_train_savepath = './mnist_image_label/mnist_x_train.npy' # 训练集输入特征存储文件
y_train_savepath = './mnist_image_label/mnist_y_train.npy' # 训练集标签存储文件
test_path = './mnist_image_label/mnist_test_jpg_10000/' # 测试集图片路径
test_txt = './mnist_image_label/mnist_test_jpg_10000.txt' # 测试集标签文件
x_test_savepath = './mnist_image_label/mnist_x_test.npy' # 测试集输入特征存储文件
y_test_savepath = './mnist_image_label/mnist_y_test.npy' # 测试集标签存储文件
# 3.制作数据集的函数
def generateds(path, txt): # 图片路径,标签文件
f = open(txt, 'r') # 以只读的形式打开txt
contents = f.readlines() # 读取文件中所有的行,每行为一个单位
f.close()
x, y_ = [], []
for content in contents: # 逐行读出
value = content.split() # 以空格分开
img_path = path + value[0]
img = Image.open(img_path)
img = np.array(img.convert('L')) # 图片变为8位宽度的灰度值
img = img / 255. # 数据归一化
x.append(img)
y_.append(value[1])
print('load:' + content)
x = np.array(x)
y_ = np.array(y_)
y_ = y_.astype(np.int64)
return x, y_
# 4.加载数据
if os.path.exists(x_train_savepath) and os.path.exists(y_train_savepath) and os.path.exists(
x_test_savepath) and os.path.exists(y_test_savepath): # 判断x_train,y_train,x_test,y_test是否存在
print("----------------Load Datasets-------------")
x_train_save = np.load(x_train_savepath)
y_train = np.load(y_train_savepath)
x_test_save = np.load(x_test_savepath)
y_test = np.load(y_test_savepath)
x_train = np.reshape(x_train_save, (len(x_train_save), 28, 28))
x_test = np.reshape(x_test_save, (len(x_test_save), 28, 28))
else: # 不存在,则调用generateds()函数制作数据集
print('----------------Generate Datasets--------------')
x_train, y_train = generateds(train_path, train_txt)
x_test, y_test = generateds(test_path, test_txt)
print('----------------Save Datasets------------------')
x_train_save = np.reshape(x_train, (len(x_train), -1))
x_test_save = np.reshape(x_test, (len(x_test), -1))
np.save(x_train_savepath, x_train_save)
np.save(y_train_savepath, y_train)
np.save(x_test_savepath, x_test_save)
np.save(y_test_savepath, y_test)
# 5.数据增强
x_train = x_train.reshape(x_train.shape[0], 28, 28, 1)
image_gen_train = ImageDataGenerator(
rescale=1. / 1., # 如果是图像,分母为255,可以归一化到0-1
rotation_range=45, # 随机45度旋转
width_shift_range=.15, # 宽度偏移
height_shift_range=.15, # 高度偏移
horizontal_flip=True, # 水平翻转
zoom_range=0.5 # 将图像随机缩放阈量50%
)
image_gen_train.fit(x_train)
# 6.搭建网络
model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(10, activation='softmax')
])
# 7.配置参数
model.compile(optimizer='adam',
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False),
metrics=['sparse_categorical_accuracy'])
# 8.设置调用和保存模型
# 调用模型
checkpoint_save_path = "./checkpoint/mnist.ckpt"
if os.path.exists(checkpoint_save_path + '.index'):
print("--------------load model--------------")
model.load_weights(checkpoint_save_path)
# 保存模型
cp_callback = tf.keras.callbacks.ModelCheckpoint(filepath=checkpoint_save_path,
save_weights_only=True,
save_best_only=True)
# 9.训练
history = model.fit(image_gen_train.flow(x_train, y_train, batch_size=32),
epochs=5, validation_data=(x_test, y_test), validation_freq=1,
callbacks=[cp_callback])
# 提取acc和loss
acc = history.history['sparse_categorical_accuracy']
val_acc = history.history['val_sparse_categorical_accuracy']
loss = history.history['loss']
val_loss = history.history['val_loss']
# 10.模型参数
# 打印网络参数
model.summary()
# 保存训练好的模型参数
np.set_printoptions(threshold=np.inf)
print(model.trainable_variables)
file = open('./weights.txt', 'w')
for v in model.trainable_variables:
file.write(str(v.name) + '\n')
file.write(str(v.shape) + '\n')
file.write(str(v.numpy()) + '\n')
file.close()
# 11.绘制acc和loss曲线
plt.subplot(1, 2, 1)
plt.plot(acc, label='Training Accuracy')
plt.plot(val_acc, label='Validation Accuracy')
plt.title('Training and Validation Accuracy')
plt.legend()
plt.subplot(1, 2, 2)
plt.plot(loss, label='Training Loss')
plt.plot(val_loss, label='Validation Loss')
plt.title('Training and Validation Loss')
plt.legend()
plt.show()
代码2:使用训练好的模型,给图出结果
import tensorflow as tf
import numpy as np
from PIL import Image
# 模型参数存储的路径
model_save_path = './checkpoint/mnist.ckpt'
# 1.复现模型(前向传播)
model = tf.keras.models.Sequential([
tf.keras.layers.Flatten(),
tf.keras.layers.Dense(128, activation='relu'),
tf.keras.layers.Dense(10, activation='softmax')
])
# 2.加载参数
model.load_weights(model_save_path)
# 3.数据预处理
preNum = int(input("要执行多少次图像识别任务:"))
for i in range(preNum):
image_path = input("请输入图像路径:\n")
img = Image.open(image_path)
img = img.resize((28, 28), Image.ANTIALIAS) # resize成28*28的标准尺寸
img_arr = np.array(img.convert('L')) # 转换为灰度图---和训练集图片一致
### 方法1
# 训练集图片是黑底白字,输入的图片是白底黑字
# 所以,要让每个像素点等于255减去当前值,相当于颜色取反
# img_arr = 255 - img_arr
### 方法2
# 让输入图片变为只有黑色和白色的高对比度图片,滤去了图片噪声,图片更干净
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 # 图片归一化
# 神经网络训练都是batch送入网络的,所以在img_array的前面添加一个维度
# 28*28的二位数据--->1*28*28的三维数据
x_predict = img_arr[tf.newaxis, ...]
# 4.预测结果
result = model.predict(x_predict)
pred = tf.argmax(result, axis=1)
print('识别结果是:%d' % pred.numpy())