序列标注(sequence labelling),输入序列每一帧预测一个类别。OCR(Optical Character Recognition 光学字符识别)。

MIT口语系统研究组Rob Kassel收集,斯坦福大学人工智能实验室Ben Taskar预处理OCR数据集(http://ai.stanford.edu/~btaskar/ocr/ ),包含大量单独手写小写字母,每个样本对应16X8像素二值图像。字线组合序列,序列对应单词。6800个,长度不超过14字母的单词。gzip压缩,内容用Tab分隔文本文件。Python csv模块直接读取。文件每行一个归一化字母属性,ID号、标签、像素值、下一字母ID号等。

下一字母ID值排序,按照正确顺序读取每个单词字母。收集字母,直到下一个ID对应字段未被设置为止。读取新序列。读取完目标字母及数据像素,用零图像填充序列对象,能纳入两个较大目标字母所有像素数据NumPy数组。

时间步之间共享softmax层。数据和目标数组包含序列,每个目标字母对应一个图像帧。RNN扩展,每个字母输出添加softmax分类器。分类器对每帧数据而非整个序列评估预测结果。计算序列长度。一个softmax层添加到所有帧:或者为所有帧添加几个不同分类器,或者令所有帧共享同一个分类器。共享分类器,权值在训练中被调整次数更多,训练单词每个字母。一个全连接层权值矩阵维数batch_size*in_size*out_size。现需要在两个输入维度batch_size、sequence_steps更新权值矩阵。令输入(RNN输出活性值)扁平为形状batch_size*sequence_steps*in_size。权值矩阵变成较大的批数据。结果反扁平化(unflatten)。

代价函数,序列每一帧有预测目标对,在相应维度平均。依据张量长度(序列最大长度)归一化的tf.reduce_mean无法使用。需要按照实际序列长度归一化,手工调用tf.reduce_sum和除法运算均值。

损失函数,tf.argmax针对轴2非轴1,各帧填充,依据序列实际长度计算均值。tf.reduce_mean对批数据所有单词取均值。

TensorFlow自动导数计算,可使用序列分类相同优化运算,只需要代入新代价函数。对所有RNN梯度裁剪,防止训练发散,避免负面影响。

训练模型,get_sataset下载手写体图像,预处理,小写字母独热编码向量。随机打乱数据顺序,分偏划分训练集、测试集。

单词相邻字母存在依赖关系(或互信息),RNN保存同一单词全部输入信息到隐含活性值。前几个字母分类,网络无大量输入推断额外信息,双向RNN(bidirectional RNN)克服缺陷。
两个RNN观测输入序列,一个按照通常顺序从左端读取单词,另一个按照相反顺序从右端读取单词。每个时间步得到两个输出活性值。送入共享softmax层前,拼接。分类器从每个字母获取完整单词信息。tf.modle.rnn.bidirectional_rnn已实现。

实现双向RNN。划分预测属性到两个函数,只关注较少内容。_shared_softmax函数,传入函数张量data推断输入尺寸。复用其他架构函数,相同扁平化技巧在所有时间步共享同一个softmax层。rnn.dynamic_rnn创建两个RNN。
序列反转,比实现新反向传递RNN运算容易。tf.reverse_sequence函数反转帧数据中sequence_lengths帧。数据流图节点有名称。scope参数是rnn_dynamic_cell变量scope名称,默认值RNN。两个参数不同RNN,需要不同域。
反转序列送入后向RNN,网络输出反转,和前向输出对齐。沿RNN神经元输出维度拼接两个张量,返回。双向RNN模型性能更优。

    import gzip
import csv
import numpy as np from helpers import download class OcrDataset: URL = 'http://ai.stanford.edu/~btaskar/ocr/letter.data.gz' def __init__(self, cache_dir):
path = download(type(self).URL, cache_dir)
lines = self._read(path)
data, target = self._parse(lines)
self.data, self.target = self._pad(data, target) @staticmethod
def _read(filepath):
with gzip.open(filepath, 'rt') as file_:
reader = csv.reader(file_, delimiter='\t')
lines = list(reader)
return lines @staticmethod
def _parse(lines):
lines = sorted(lines, key=lambda x: int(x[0]))
data, target = [], []
next_ = None
for line in lines:
if not next_:
data.append([])
target.append([])
else:
assert next_ == int(line[0])
next_ = int(line[2]) if int(line[2]) > -1 else None
pixels = np.array([int(x) for x in line[6:134]])
pixels = pixels.reshape((16, 8))
data[-1].append(pixels)
target[-1].append(line[1])
return data, target @staticmethod
def _pad(data, target):
max_length = max(len(x) for x in target)
padding = np.zeros((16, 8))
data = [x + ([padding] * (max_length - len(x))) for x in data]
target = [x + ([''] * (max_length - len(x))) for x in target]
return np.array(data), np.array(target) import tensorflow as tf from helpers import lazy_property class SequenceLabellingModel: def __init__(self, data, target, params):
self.data = data
self.target = target
self.params = params
self.prediction
self.cost
self.error
self.optimize @lazy_property
def length(self):
used = tf.sign(tf.reduce_max(tf.abs(self.data), reduction_indices=2))
length = tf.reduce_sum(used, reduction_indices=1)
length = tf.cast(length, tf.int32)
return length @lazy_property
def prediction(self):
output, _ = tf.nn.dynamic_rnn(
tf.nn.rnn_cell.GRUCell(self.params.rnn_hidden),
self.data,
dtype=tf.float32,
sequence_length=self.length,
)
# Softmax layer.
max_length = int(self.target.get_shape()[1])
num_classes = int(self.target.get_shape()[2])
weight = tf.Variable(tf.truncated_normal(
[self.params.rnn_hidden, num_classes], stddev=0.01))
bias = tf.Variable(tf.constant(0.1, shape=[num_classes]))
# Flatten to apply same weights to all time steps.
output = tf.reshape(output, [-1, self.params.rnn_hidden])
prediction = tf.nn.softmax(tf.matmul(output, weight) + bias)
prediction = tf.reshape(prediction, [-1, max_length, num_classes])
return prediction @lazy_property
def cost(self):
# Compute cross entropy for each frame.
cross_entropy = self.target * tf.log(self.prediction)
cross_entropy = -tf.reduce_sum(cross_entropy, reduction_indices=2)
mask = tf.sign(tf.reduce_max(tf.abs(self.target), reduction_indices=2))
cross_entropy *= mask
# Average over actual sequence lengths.
cross_entropy = tf.reduce_sum(cross_entropy, reduction_indices=1)
cross_entropy /= tf.cast(self.length, tf.float32)
return tf.reduce_mean(cross_entropy) @lazy_property
def error(self):
mistakes = tf.not_equal(
tf.argmax(self.target, 2), tf.argmax(self.prediction, 2))
mistakes = tf.cast(mistakes, tf.float32)
mask = tf.sign(tf.reduce_max(tf.abs(self.target), reduction_indices=2))
mistakes *= mask
# Average over actual sequence lengths.
mistakes = tf.reduce_sum(mistakes, reduction_indices=1)
mistakes /= tf.cast(self.length, tf.float32)
return tf.reduce_mean(mistakes) @lazy_property
def optimize(self):
gradient = self.params.optimizer.compute_gradients(self.cost)
try:
limit = self.params.gradient_clipping
gradient = [
(tf.clip_by_value(g, -limit, limit), v)
if g is not None else (None, v)
for g, v in gradient]
except AttributeError:
print('No gradient clipping parameter specified.')
optimize = self.params.optimizer.apply_gradients(gradient)
return optimize import random import tensorflow as tf
import numpy as np from helpers import AttrDict from OcrDataset import OcrDataset
from SequenceLabellingModel import SequenceLabellingModel
from batched import batched params = AttrDict(
rnn_cell=tf.nn.rnn_cell.GRUCell,
rnn_hidden=300,
optimizer=tf.train.RMSPropOptimizer(0.002),
gradient_clipping=5,
batch_size=10,
epochs=5,
epoch_size=50
) def get_dataset():
dataset = OcrDataset('./ocr')
# Flatten images into vectors.
dataset.data = dataset.data.reshape(dataset.data.shape[:2] + (-1,))
# One-hot encode targets.
target = np.zeros(dataset.target.shape + (26,))
for index, letter in np.ndenumerate(dataset.target):
if letter:
target[index][ord(letter) - ord('a')] = 1
dataset.target = target
# Shuffle order of examples.
order = np.random.permutation(len(dataset.data))
dataset.data = dataset.data[order]
dataset.target = dataset.target[order]
return dataset # Split into training and test data.
dataset = get_dataset()
split = int(0.66 * len(dataset.data))
train_data, test_data = dataset.data[:split], dataset.data[split:]
train_target, test_target = dataset.target[:split], dataset.target[split:] # Compute graph.
_, length, image_size = train_data.shape
num_classes = train_target.shape[2]
data = tf.placeholder(tf.float32, [None, length, image_size])
target = tf.placeholder(tf.float32, [None, length, num_classes])
model = SequenceLabellingModel(data, target, params)
batches = batched(train_data, train_target, params.batch_size) sess = tf.Session()
sess.run(tf.initialize_all_variables())
for index, batch in enumerate(batches):
batch_data = batch[0]
batch_target = batch[1]
epoch = batch[2]
if epoch >= params.epochs:
break
feed = {data: batch_data, target: batch_target}
error, _ = sess.run([model.error, model.optimize], feed)
print('{}: {:3.6f}%'.format(index + 1, 100 * error)) test_feed = {data: test_data, target: test_target}
test_error, _ = sess.run([model.error, model.optimize], test_feed)
print('Test error: {:3.6f}%'.format(100 * error)) import tensorflow as tf from helpers import lazy_property class BidirectionalSequenceLabellingModel: def __init__(self, data, target, params):
self.data = data
self.target = target
self.params = params
self.prediction
self.cost
self.error
self.optimize @lazy_property
def length(self):
used = tf.sign(tf.reduce_max(tf.abs(self.data), reduction_indices=2))
length = tf.reduce_sum(used, reduction_indices=1)
length = tf.cast(length, tf.int32)
return length @lazy_property
def prediction(self):
output = self._bidirectional_rnn(self.data, self.length)
num_classes = int(self.target.get_shape()[2])
prediction = self._shared_softmax(output, num_classes)
return prediction def _bidirectional_rnn(self, data, length):
length_64 = tf.cast(length, tf.int64)
forward, _ = tf.nn.dynamic_rnn(
cell=self.params.rnn_cell(self.params.rnn_hidden),
inputs=data,
dtype=tf.float32,
sequence_length=length,
scope='rnn-forward')
backward, _ = tf.nn.dynamic_rnn(
cell=self.params.rnn_cell(self.params.rnn_hidden),
inputs=tf.reverse_sequence(data, length_64, seq_dim=1),
dtype=tf.float32,
sequence_length=self.length,
scope='rnn-backward')
backward = tf.reverse_sequence(backward, length_64, seq_dim=1)
output = tf.concat(2, [forward, backward])
return output def _shared_softmax(self, data, out_size):
max_length = int(data.get_shape()[1])
in_size = int(data.get_shape()[2])
weight = tf.Variable(tf.truncated_normal(
[in_size, out_size], stddev=0.01))
bias = tf.Variable(tf.constant(0.1, shape=[out_size]))
# Flatten to apply same weights to all time steps.
flat = tf.reshape(data, [-1, in_size])
output = tf.nn.softmax(tf.matmul(flat, weight) + bias)
output = tf.reshape(output, [-1, max_length, out_size])
return output @lazy_property
def cost(self):
# Compute cross entropy for each frame.
cross_entropy = self.target * tf.log(self.prediction)
cross_entropy = -tf.reduce_sum(cross_entropy, reduction_indices=2)
mask = tf.sign(tf.reduce_max(tf.abs(self.target), reduction_indices=2))
cross_entropy *= mask
# Average over actual sequence lengths.
cross_entropy = tf.reduce_sum(cross_entropy, reduction_indices=1)
cross_entropy /= tf.cast(self.length, tf.float32)
return tf.reduce_mean(cross_entropy) @lazy_property
def error(self):
mistakes = tf.not_equal(
tf.argmax(self.target, 2), tf.argmax(self.prediction, 2))
mistakes = tf.cast(mistakes, tf.float32)
mask = tf.sign(tf.reduce_max(tf.abs(self.target), reduction_indices=2))
mistakes *= mask
# Average over actual sequence lengths.
mistakes = tf.reduce_sum(mistakes, reduction_indices=1)
mistakes /= tf.cast(self.length, tf.float32)
return tf.reduce_mean(mistakes) @lazy_property
def optimize(self):
gradient = self.params.optimizer.compute_gradients(self.cost)
try:
limit = self.params.gradient_clipping
gradient = [
(tf.clip_by_value(g, -limit, limit), v)
if g is not None else (None, v)
for g, v in gradient]
except AttributeError:
print('No gradient clipping parameter specified.')
optimize = self.params.optimizer.apply_gradients(gradient)
return optimize

参考资料:
《面向机器智能的TensorFlow实践》

欢迎加我微信交流:qingxingfengzi
我的微信公众号:qingxingfengzigz
我老婆张幸清的微信公众号:qingqingfeifangz

学习笔记TF020:序列标注、手写小写字母OCR数据集、双向RNN的更多相关文章

  1. SVM学习笔记(二)----手写数字识别

    引言 上一篇博客整理了一下SVM分类算法的基本理论问题,它分类的基本思想是利用最大间隔进行分类,处理非线性问题是通过核函数将特征向量映射到高维空间,从而变成线性可分的,但是运算却是在低维空间运行的.考 ...

  2. 学习笔记TF019:序列分类、IMDB影评分类

    序列分类,预测整个输入序列的类别标签.情绪分析,预测用户撰写文字话题态度.预测选举结果或产品.电影评分. 国际电影数据库(International Movie Database)影评数据集.目标值二 ...

  3. java之jvm学习笔记六-十二(实践写自己的安全管理器)(jar包的代码认证和签名) (实践对jar包的代码签名) (策略文件)(策略和保护域) (访问控制器) (访问控制器的栈校验机制) (jvm基本结构)

    java之jvm学习笔记六(实践写自己的安全管理器) 安全管理器SecurityManager里设计的内容实在是非常的庞大,它的核心方法就是checkPerssiom这个方法里又调用 AccessCo ...

  4. 【深度学习系列】PaddlePaddle之手写数字识别

    上周在搜索关于深度学习分布式运行方式的资料时,无意间搜到了paddlepaddle,发现这个框架的分布式训练方案做的还挺不错的,想跟大家分享一下.不过呢,这块内容太复杂了,所以就简单的介绍一下padd ...

  5. 【Keras案例学习】 多层感知机做手写字符分类(mnist_mlp )

    from __future__ import print_function # 导入numpy库, numpy是一个常用的科学计算库,优化矩阵的运算 import numpy as np np.ran ...

  6. Tensorflow搭建卷积神经网络识别手写英语字母

    更新记录: 2018年2月5日 初始文章版本 近几天需要进行英语手写体识别,查阅了很多资料,但是大多数资料都是针对MNIST数据集的,并且主要识别手写数字.为了满足实际的英文手写识别需求,需要从训练集 ...

  7. Oracle 学习笔记 12 -- 序列、索引、同义词

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/Topyuluo/article/details/24232449 数据库的对象包含:表.视图.序列. ...

  8. .net学习笔记---webconfig的读与写

    System.ConfigurationManager类用于对配置文件的读取.其具有的成员如下: 一.AppSettings AppSetting是最简单的配置节,读写非常简单. 名称 说明 AppS ...

  9. LinQ实战学习笔记(三) 序列,查询操作符,查询表达式,表达式树

    序列 延迟查询执行 查询操作符 查询表达式 表达式树 (一) 序列 先上一段代码, 这段代码使用扩展方法实现下面的要求: 取进程列表,进行过滤(取大于10M的进程) 列表进行排序(按内存占用) 只保留 ...

随机推荐

  1. 间谍网络——tarjan求SCC

    洛谷传送门 看着这道题给人感觉就是tarjan求SCC,然而还得判断是否能控制全部间谍,这就得先从可以贿赂的点dfs一遍. 如果没有全部被标记了,就输出NO,再从没被标记的点里找最小的标号. 如果全被 ...

  2. vue router-link 上添加点击事件

    在vue学习中遇到给router-link 标签添加事件@click .@mouseover等无效的情况 我想要做的是鼠标移上去出现删除标签,移除标签消失的效果 原代码: <router-lin ...

  3. MyBatis注解配置动态SQL

    MySQL创建表 DROP TABLE IF EXISTS `tb_employee`; CREATE TABLE `tb_employee` ( `id` int(11) NOT NULL AUTO ...

  4. 杜教筛 && bzoj3944 Sum

    Description Input 一共T+1行 第1行为数据组数T(T<=10) 第2~T+1行每行一个非负整数N,代表一组询问 Output 一共T行,每行两个用空格分隔的数ans1,ans ...

  5. [周译见] C# 7 中的模范和实践

    原文地址:https://www.infoq.com/articles/Patterns-Practices-CSharp-7 关键点 遵循 .NET Framework 设计指南,时至今日,仍像十年 ...

  6. 【运维工程师必知必会】——MySql基础

    一.SQL语句 1.分类 DDL(data definition language)数据定义语言(create.alter.drop),管理基础数据.例如:库.表     #运维要熟练,开发也要熟练 ...

  7. OC 动态类型和静态类型

    多态 允许不同的类定义相同的方法 动态类型 程序直到执行时才能确定所属的类 静态类型 将一个变量定义为特定类的对象时,使用的是静态形态 将一个变量定义为特定类的对象时,使用的是静态类型,在编译的时候就 ...

  8. 学习MVC之租房网站(八)- 前台注册和登录

    在上一篇<学习MVC之租房网站(七)-房源管理和配图上传>完成了在后台新增.编辑房源信息以及上传房源配图的功能.到此后台开发便告一段落了,开始实现前台的功能,也是从用户的登录.注册开始. ...

  9. TCP三次握手(建立连接)/四次挥手(关闭连接)

    TCP数据包格式 顺序号(32位):用来标识从TCP源端向TCP目的端发送的数据字节流,它表示在这个报文段中的第一个数据字节的顺序号.如果将字节流看作在两个应用程序间的单向流动,则TCP用顺序号对每个 ...

  10. 常用的CI笔记

    1. thinkphp 封装好的$this->success(),就直接实现成功跳转,$this->error(),错误跳转.CI有show_error(),但是却不能直接实现跳转,所以需 ...