前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >TensorFlow2.0 实战强化专栏(一):Chars74项目

TensorFlow2.0 实战强化专栏(一):Chars74项目

作者头像
磐创AI
发布2020-03-04 16:06:22
1.9K1
发布2020-03-04 16:06:22
举报
文章被收录于专栏:磐创AI技术团队的专栏
作者 | 小猴锅

出品 | 磐创AI团队

Chars74K项目

字符识别是一种经典的模式识别问题,字符识别在现实生活中也有着非常广泛的应用,目前对于特定环境下的拉丁字符识别已经取得了很好的效果,但是对于一些复杂场景下的字符识别依然还有很多困难,例如通过手持设备拍摄以及自然场景中的图片等,Chars74K正是针对这些困难点搜集的数据集(http://www.ee.surrey.ac.uk/CVSSP/demos/chars74k/

Chars74K包含英语和坎那达语(Kannada)两种字符,在英文数据集中包括26个拉丁文字母和10个阿拉伯数字,整个英文数据集包括64种字符(0-9,a-z,A-Z),英文数据集根据采集方式又有三种不同数据集(三种英文数据集的样本数加在一起超过了74K,Chars74K的名字也是由此而来。):

1) 7705个自然图像中采集的字符数据集(EnglishImg.tgz);

2) 3410个在平板电脑上手写的字符数据集(EnglishHnd.tgz);

3) 62992个在从计算机字体合成的字符数据集(EnglishFnt.tgz)。

在本项目中我们使用第一个数据集,即从自然场景中采集到的字符数据集,部分数据如图1所示。

图1 Chars74K数据集示例(自然场景中采集的英文字符数据集)

数据集解压之后的目录结构如图2所示,解压之后的数据集包括“BadImg”和“GoodImg”,“BadImg”中的图片质量较差,因此我们使用“GoodImg”。数据集中每一个类别的图片单独放在一个文件夹中,如图2右所示。

图2 Chars74K数据集(自然场景中采集的英文字符数据集)

数据预处理

Chars74K数据集(自然场景中采集的英文字符数据集,本项目中后续提到的Chars74K数据集一律特指该数据集)里的图片大小不一,因此我们需要将其调整为统一大小,调整图像大小的代码可以在本书配套的GitHub项目中找到,这里不做介绍。另外需要注意,原始数据集中混杂了4张单通道的灰度图,需要删除这4张图片。这里可以直接使用作者处理好的数据集。

下载地址:https://pan.baidu.com/s/1KP5JRO-M87fN-93VOsSCag

接下来我们开始实现数据处理部分,首先导入需要的包:

代码语言:javascript
复制
1  import tensorflow as tf
2  from tensorflow.keras import layers
3  import datetime
4  import numpy as np
5  from PIL import Image
6  import os

接着我们定义一个“get_dataset”函数用来获取数据集:

代码语言:javascript
复制
7  def get_dataset(path):
8      """获取数据集"""
9      data_x = []
10      data_y = []
11  
12      # 获取当前路径下所有文件夹(或文件)
13      folder_name = os.listdir(path)
14  
15      # 循环遍历每个文件夹
16      for i in folder_name:
17          file_path = os.path.join(path, i)
18  
19          # 取文件夹名后三位整数作为类标
20          label = int(i[-3:])
21  
22          # 获取当前文件夹下的所有图片文件
23          filenames = os.listdir(file_path)
24  
25          for filename in filenames:
26              # 组合得到每张图片的路径
27              image_path = os.path.join(file_path, filename)
28  
29              # 读取图片
30              image = Image.open(image_path)
31              # 将image对象转为numpy数组
32              width, height = image.size
33              image_matrix = np.reshape(image, [width*height*3])
34  
35              data_x.append(image_matrix)
36              data_y.append(label)
37  
38      return data_x, data_y

第33行代码中我们将图片转换成了numpy数组,由于我们的图像是三通道的RGB图像,因此转换后的数组大小为“width * height * 3”。

模型搭建

在本项目中我们将使用VGG-Net网络模型。VGG-Net有多种级别,其网络层数从11层到19层不等(这里的层数是指有参数更新的层,例如卷积层或全连接层),其中比较常用的是16层(VGG-Net-16)和19层(VGG-Net-19)。如图3所示是VGG-Net-16的网络结构。

图3 VGG-Net-16网络结构

VGG-Net中全部使用大小为3X3的小卷积核,希望模拟出更大的“感受野”效果,VGG-Net中的池化层均使用的是大小为2X2的最大池化。VGG-Net的设计思想在ResNet和Inception模型中也都有被采用。图4所示是不同层数的VGG-Net。

图4 不同层数的VGG-Net

本项目中我们使用的是VGG-Net-13,具体实现如下:

代码语言:javascript
复制
39  def vgg13_model(input_shape, classes):
40 model = tf.keras.Sequential()
41  
42      model.add(layers.Conv2D(64, 3, 1, input_shape=input_shape,
43                              padding='same',
44                              activation='relu',
45                              kernel_initializer='uniform'))
46      model.add(layers.Conv2D(64, 3, 1, padding='same',
47                              activation='relu',
48                              kernel_initializer='uniform'))
49      model.add(layers.MaxPooling2D(pool_size=(2, 2)))
50  
51      model.add(layers.Conv2D(128, 3, 1, padding='same',
52                              activation='relu',
53                              kernel_initializer='uniform'))
54      model.add(layers.Conv2D(128, 3, 1, padding='same',
55                              activation='relu',
56                              kernel_initializer='uniform'))
57      model.add(layers.MaxPooling2D(pool_size=(2, 2)))
58  
59      model.add(layers.Conv2D(256, 3, 1, padding='same',
60                              activation='relu',
61                              kernel_initializer='uniform'))
62      model.add(layers.Conv2D(256, 3, 1, padding='same',
63                              activation='relu',
64                              kernel_initializer='uniform'))
65      model.add(layers.MaxPooling2D(pool_size=(2, 2)))
66  
67      model.add(layers.Conv2D(512, 3, 1, padding='same',
68                              activation='relu',
69                              kernel_initializer='uniform'))
70      model.add(layers.Conv2D(512, 3, 1, padding='same',
71                              activation='relu',
72                              kernel_initializer='uniform'))
73      model.add(layers.MaxPooling2D(pool_size=(2, 2)))
74  
75      model.add(layers.Conv2D(512, 3, 1, padding='same',
76                              activation='relu',
77                              kernel_initializer='uniform'))
78      model.add(layers.Conv2D(512, 3, 1, padding='same',
79                              activation='relu',
80                              kernel_initializer='uniform'))
81      model.add(layers.MaxPooling2D(pool_size=(2, 2)))
82  
83      model.add(layers.Flatten())
84      model.add(layers.Dense(4096, activation='relu'))
85  
86      model.add(layers.Dropout(0.5))
87      model.add(layers.Dense(4096, activation='relu'))
88  
89      model.add(layers.Dropout(0.5))
90      model.add(layers.Dense(classes, activation='softmax'))
91  
92      # 模型编译
93      model.compile(loss='categorical_crossentropy',
94                    optimizer='sgd',
95                    metrics=['accuracy'])
96      return model

模型训练

定义好模型后我们加载数据集并开始训练:

代码语言:javascript
复制
97  if __name__ == '__main__':
98      path = './chars74k_data'
99      data_x, data_y = get_dataset(path)
100  
101      train_x = np.array(data_x).reshape(-1, 224, 224, 3)
102      train_y = [i - 1 for i in data_y]
103      train_y = tf.keras.utils.to_categorical(train_y, 62)
104  
105      # 随机打乱数据集顺序
106      np.random.seed(116)
107      np.random.shuffle(train_x)
108      np.random.seed(116)
109      np.random.shuffle(train_y)
110  
111      cnn_model = vgg13_model(input_shape=(224,224,3), classes=62)
112      cnn_model.summary()
113  
114      # 设置TensorBoard
115      log_dir="logs/fit/"+datetime.datetime.now().strftime("%Y%m%d-%H%M%S")
116  tensorboard_callback=tf.keras.callbacks.TensorBoard(log_dir=log_dir, histogram_freq=1)
117  
118      # 当验证集上的loss不再下降时就提前结束训练
119  early_stop=tf.keras.callbacks.EarlyStopping(monitor='val_loss', min_delta=0.002, patience=10, mode='auto')
120  
121      callbacks = [tensorboard_callback, early_stop]
122      cnn_model.fit(train_x, train_y,
123                    batch_size=100, epochs=300,
124                    verbose=1, validation_split=0.2,
125                    callbacks=callbacks)

在第103行代码中,由于我们之前根据目录名得到的类标是从“1”开始的,因此需要对所有类标减1,让类标从“0”开始,以便在第104代码中将类标转换为one-hot编码。

第120行代码中,我们设置了一个callback函数“EarlyStopping”,该函数可以用来设置模型自动停止训练的条件。例如这里我们设置当“val_loss”的值有10次变化不超过0.002时则提前停止训练。参数“monitor”是要监测的指标;“min_delta”是监测指标的最小变化值;“patience”是没有变化的训练回合数;“mode”有三个值,分别是“auto”、“min”和“max”,当“mode”设置为“min”时,如果监测的指标有“patience”次没有达到“min_delta”的变化量,则停止训练,“max”同理。模型训练的结果如图5所示:

图5 训练过程中的Accuracy和Loss变化(红色为训练集,蓝色为验证集)

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-02-08,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 磐创AI 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Chars74K项目
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档