思路

我们的目标是利用TensorFlow建立一个深度神经网络,可以把一个手写的汉字识别出来。为了达到这个目的,我们分以下几个步骤:
1. 收集训练数据和验证数据。也就是很多手写汉字的图片,每个图片里只有一个汉字,并且我们知道每个图片里的汉字是什么。图片就是feature,它代表的汉字就是label。
2. 把这些图片和它对应的label读入TensorFlow。
3. 构建深度神经网络来训练,得到模型。
4. 用测试集验证模型。
5. 自己手写一个汉字来让我们的模型识别。

数据的获取

你可以到百度云盘下载数据文件。它是根据中科院自动化研究所公开的汉字手写图片,处理后,将原来特殊的压缩格式转化为更容易处理的png格式的数据。

中科院原始数据地址:
训练数据
测试数据

如果你从百度云盘下载到的数据包含train的训练数据和test的测试数据,以及一个chart_dict对图片和汉字做映射的一个字典数据。train数据因为太大,被切分成了7块,包含在train.part[1-7].rar 7个文件里。一般的解压工具,你对train.part1.rar进行解压,就自动会把所有分块文件解压到train目录下。解压后,你会发现train目录里边有3755个文件夹,每个文件夹里的图片都是同一个汉字的手写体。
文件夹:

我们进入一个文件夹,比如03650,里边有很多图片,都是”餐”字的手写体图片。

数据的读取

图片的读取

数据转化的目的是把一个图片文件转化成TensorFlow能处理的多维数组,比如最终转化为一个100×80的矩阵,其中的值取值范围是0-255,代表一个像素的灰度值。100代表图片高度为100个像素,80代表图片宽度为80个像素。
TensorFlow提供了很多方法来帮助我们,一般都在tf.image下。我们通过代码看一下:


#首先我们从图片里以字符串的形式来读出图片内容:
import tensorflow as tf
sess = tf.Session()
image_string = tf.read_file("E:/data/handwriting/train.part7/train/03749/1577.png")
print(sess.run(image_string))

我们得到类似下面这样的输出:


b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00N\x00\x00\x00l\x08\x02\x00\x00\x00K\xac\x13w\x00\x00\x1bbIDATx\x9c\xcd\\\xd9o\x1b\xd7\xf9\x9d!\x87C\x0e\xc9\xe1\xbe/\xa2%Q\x92%K\x96\xb5X\x9b\xedxI\xdc\xba^`\x170\x90\x04y\xe8C\x1f\xda\xb7\xa2\xe9S\x81\xfe\ty\xe9[\x03\xb4@\x8b\x1a\x05\x02\xb4vc\xa4\x8e\xab\x18\x8e\xe2X\xb6e\x9b\xb2\x16\x8a\x12M\x91\x14Iq\xdf\x86\x1c\x923\xe4\xccp~\x0f7P\xf5\xb3\xb5Q\x12\xdd\x9eG\xcev\x0f\xe7\xce\xbd\xdf=\xdf\xf9.\xcc\xf3<\xd4\x08\x82\xc1\xe0\xb7\xdf~[*\x95\x06\x07\x07O\x9f>\xdd\xd0\xb5\xff]\x08\x1a:\xbb\\.\xff\xf3\x9f\xff|\xf5\xeaU,\x16\xe38\xaeImj\x12\x90\x86\xce.\x16\x8b\x95J%\x97\xcbA\x10\x84 \x8d]\......

接下来我们利用TensorFlow提供的方法来对图片进行解码


#首先我们从图片里以字符串的形式来读出图片内容:
with sess.as_default():
    image_decode = tf.image.decode_image(image_string,channels=1)
    image_resize = tf.image.resize_images(image_decode.eval(),[100,80])
        print(image_resize)

这时,我们得到的输出为:


[[[255.     ]
  [255.     ]
  [255.     ]
  ...
  [255.     ]
  [255.     ]
  [255.     ]]

 [[255.     ]
  [255.     ]
  [255.     ]]]

需要注意的是我们制定了decode_iamge方法的channels参数为1,这意味着我们只从图片里提取灰度值。如果是彩色照片你可以不指定这个参数,它默认会选取RGB 3个channel进行图片解码。因为每个照片的大小不一样,我们用resize_images方法把所有图片都调整到100行,80列像素的图片。

label的读取

我们下载的数据集里包含一个char_dict文件,它是用python的pickle库把一个dict对象序列化的问题,我们可以直接用pickle来load这个汉字和对应编码的dict。代码如下:


import pickle
f = open('E:/data/hwdb/char_dict','br')
dict = pickle.load(f)
print(dict)

得到的结果是这样的:


{'管': 2509, '狞': 2127, '氢': 1796, '丛': 17, '衡': 3005, '秤': 2428, '视': 3048, '犹': 2120, '址': 665, '辣': 3286, '染': 1640, '嵌': 976, '筑': 2496, '柬': 1646, '氧': 1798, '惫': 1188, '售': 586, '佬': 159, '琉': 2169, '秀': 2417, '妒': 787, '宣': 875, '何': 152, '搭': 1427, '污': 1818, '母': 1777, '服': 1581, '业': 16, '惯': 1191, '芒': 2816, '端': 2479, '猖': 2138, '筏': 2494, '俗': 187,....}

首先我们想用自定义的Estimator来训练我们的网络。为了使用Estimator,我们必须定义一个input function。下边我就直接给出定义:


def read_data(dataPath):    
    image_files=[]
    label_arr=[]
    for char in os.listdir(dataPath):
        for image in os.listdir(os.path.join(dataPath,char)):
            image_files.append(os.path.join(dataPath,char,image))
            label_arr.append(int(char))
    images = tf.convert_to_tensor(image_files)
    labels = tf.convert_to_tensor(label_arr)
    return images,labels
images,labels = read_data(trainDataPath)
images_eval,labels_eval = read_data(testDataPath)

def _parse_function(filename, label):
    image_string = tf.read_file(filename)
    image_decoded = tf.image.decode_png(image_string,channels=1)
    image_resized = tf.image.resize_images(image_decoded, [imageSize, imageSize])
    return image_resized, label

def train_input_fn(images,labels):
    dataset = tf.data.Dataset.from_tensor_slices((images,labels))
    dataset = dataset.shuffle(buffer_size=895035).repeat()
    dataset = dataset.map(_parse_function).batch(batch_size)
    return dataset.make_one_shot_iterator().get_next()

其中的train_input_fn就是我们要提供给Estimator的input function。它负责给模型提供input数据。from_tensor_slices方法把images和labels元素一一对应,并进行切片。然后我们调用shuffle对所有的元素打乱顺序来达到模型的稳定性。热后调用repeat方法来保证在模型迭代次数未到input数据可以重复被读取。然后利用map函数来进行数据的逐条转化,也就是把图片路径转化为我们需要的实际图片数据。batch方法把数据集分成多个小的批次。最后Estimator需要我们把dataset转为one_shot_iterator来提供数据。

3 对 “用TensorFlow实现一个手写汉字识别之一:数据的获取和处理”的想法;

  1. 这一组系列教程是我见过比较好的关于 estimator 的教程,包含了来自数据集的数据预处理。很实用,感谢作者!
    帮你推广了下:https://www.v2ex.com/t/463923

发表评论

电子邮件地址不会被公开。 必填项已用*标注

%d 博主赞过: