CRNN原理以及tensorflow、keras实现

前言:

目标检测是计算机视觉领域的一个大佬,就连大名鼎鼎的图像识别从某种程度上可以看做目标检测的一个特例,即图像中 only one object 同时不需要检测目标在图像中的位置。常见的 object detection 都是关于动物(猫)交通工具(飞机)常规物体(杯子)等实物,即可以通过一些视觉特征如轮廓、形状等准确的识别出物体。然而,但是,如果图像中出现了文本信息呢?如何识别视觉图像中的文本信息。显然,我们不能再通过 object 的视觉信息识别文字,我们需要提取文本目标的合适特征用于识别任务。

算法原理

文本信息都是上下文相关的,单个词语在特定的语境中会有不同的解读,所以对于文本的处理必须兼顾上文和下文。双向的LSTM可以同时处理左到右和右到左的序列数据,我有一个大胆的想法。图像中的文本也是文本,如果可以把它提取成LSTM可以处理的序列数据,那么是不是就实现了图像中文本信息的识别呢?是的,可以,这就是CRNN的出发点。

CRNN的思想就是通过CNN网络提取文本图像的特征,然后转化这些特征用于RNN(LSTM)识别具体的文本内容。(猜测Attention 机制应该可以,找机会试一下)

一般处理文本数据时最先想到的就是LSTM模型,这是因为文本的理解和处理必须在特定的语境(上下文)中进行,LSTM模型的输入通常都是学习到或者人工设定的 word-vector,CRNN的一个最大的优点就是通过预训练的CNN网络直接同图像中学习文本的表达向量,在最大程度上保证输入到LSTM的序列是最好的表达。

网络结构:

CRNN网络结构

如上图,对于图像中的STATE单词,首先通过CNN的卷积层提取特征,之后用这些特征生成LSTM需要的序列特征,在训练好的双向RNN网络中完成识别。下面会重点记录网络中的一些细节。先来看一下网络各层的具体参数。

CRNN网络层参数

首先是提取特征的CNN网络,可以是任意一款深度网络,VGG、resnet、googleNet、mobileNet等网络均可,论文中用的是VGG16,需要注意的是无论选取哪种网络要保证最终输出的特征是二维的即(N,H,W,C)中H必须为1,否则是不能用作LSTM的输入的。为了保证这一点,论文中对VGG16网络进行了微调,分别对第三、四Max-pooling 的尺寸进行改变,从(2,2)变为(1,2)。对于经典的VGG16网络,原始图像经过四个Max-pooling层后,图像的W、H变为原来的W/2^{4},H/2^{4}H/2^{4},如果对最后两个Max-pooling层进行改变后,原始图像的W则会变为W/2*2,H变为原来的1/16,所以为了确保CNN网络输出的feature的H为1,所以输入图像的H必须是32(针对VGG16)。同时,训练时为了加速LSTM,在第四和第五卷积层后增加了BatchNormalization层。

之后是Map-to-Sequence,其实就是按照从左到右的顺序把CNN最后一层卷积的输入进行拆分,而由前面的知识我们知道LSTM输入序列的长度是 W/4。

tf.squeeze(input_tensor, axis=1)

在tensorflow中的实现,input_tensor 是cnn网络最后一层卷积层的输出。

注释:下文中 tf 代表 tensorflow。

然后是LSTM层,这是一个双向深层的RNN网络,在 tf 中有两种实现方式。

tf.nn.bidirectional_dynamic_rnn
深层双向LSTM
tf.contrib.rnn.stack_bidirectional_dynamic_rnn
stack双向LSTM

论文中采用的是第二种实现方式。

最后是 Transcription Layers,即把LSTM的输出转换成具体的字符。对图像识别有所了解的同学应该知道图像识别实际上是通过 softmax 函数实现的一个多分类模型,类似的我们可以对LSTM的输出作softmax处理然后选取最大值所对应的标签(字符),但这样做是有问题的。一、标注问题,在图像识别问题中,输出位置和标签是一一对应的,但是对于文本数据,显然做不到输出位置和类别的一一对应。二、文本通常都是不定长度的,softmax输出类别数目是固定的,无法处理不定长度的label。CTC-loss,解决所有与文本相关的问题。我不懂。。。

总之,通过引入CTC-loss完美的解决了softmax搞不定的问题,既不需要逐个位置标注字符又可以输出任意长度的label。原理部分到此结束,小提示,可以先熟悉CNN和LSTM的基本原理和结构,然后理解起来还是很easy的。

下面是实现阶段。其实也没有太多可说的,需要注意的是要对输入图像进行检察,即 W/4 >= len(label),换句话说,图像的lable即文本内容的长度要小于等于图像W的1/4,否则的话网络输出的文本长度会大于输入序列的长度,这是犯罪。

当然,还有一些别的细节,这个就不一一列举了,在代码和readme中都有相应的注释,我的项目地址在:github 上传失败,把它放在下载项吧。

注意:图像tensor 的shape是(N*H*W*C)还是(N*W*H*C),这个在网络第三和第四max-pooling层的参数设置上是有区别的,就是(2,1)、(1,2)的不同。本文这里是采用的第二种。

另外,关于tensorflow fine-tuning 的一些基本操作,注释 init_op = tf.global_variables_initializer(),该用

saver = tf.train.Saver()

saver.restore(sess=sess,save_path=path_to_pre_trained_model)

https://download.youkuaiyun.com/download/zshluckydogs/11026575

Keras实现

在tensorflow实现的基础上做了以下调整:

1、用ResNet50代替VGG16获得更好的图像特征表达

2、舍弃LSTM层加速训练(单卡1080ti,batch_size=32,训练时间长达15小时,

如果保留LSTM层训练时间会更久,当然如果你的硬件设备算力足够的话可以带上LSTM层。)

为什么batch_size 

Inference:

1、https://zhuanlan.zhihu.com/p/43534801

2、论文原文 An End-to-End Trainable Neural Network for Image-based Sequence Recognition and Its Application to Scene Text Recognition

 

 

### CRNN手写汉字识别模型原理图与架构详解 CRNN(Convolutional Recurrent Neural Network)是一种结合了卷积神经网络(CNN)和循环神经网络(RNN)的混合模型,主要用于解决基于图像的序列识别问题,例如场景文字识别或手写汉字识别。以下是关于CRNN手写汉字识别模型的具体结构及其工作原理: #### 1. **整体架构** CRNN的整体架构可以分为三个主要部分:卷积层、循环层以及转录层。 - **卷积层 (CNN)** 卷积层的主要功能是从输入的手写汉字图片中提取空间特征。通过一系列卷积操作和池化操作,CNN能够捕捉到字符的关键形状信息并降低维度[^1]。对于手写汉字识别任务而言,这一阶段的目标是对复杂的笔画结构进行高效建模。 - **循环层 (RNN/LSTM/GRU)** 循环层接收来自卷积层的空间特征,并将其转化为时间序列数据以便进一步分析。由于手写字体通常表现为一种沿水平方向排列的时间序列模式,因此采用双向LSTM(Long Short-Term Memory)或者GRU(Gated Recurrent Unit)来捕获上下文依赖关系是非常有效的[^2]。这种机制允许模型理解当前时刻的状态不仅取决于局部像素分布,还受到前后帧的影响。 - **转录层 (CTC Loss Function)** 转录层利用连接时序分类(CTC, Connectionist Temporal Classification)损失函数完成最终预测输出的任务。它解决了传统方法需要精确标注每个位置对应标签的问题,在训练过程中自动学习如何分配概率给不同长度目标字符串中的各个元素[^3]。 #### 2. **具体流程描述** 假设我们有一张包含多个连续书写的中文字符图像作为输入,则经过上述三步处理之后得到的结果将是这些字符组成的文本串形式表达。下面详细介绍每一步骤的操作细节: ##### 输入准备 原始灰度化的单通道或多通道彩色图片被调整大小至固定高度但保持宽度可变以适应各种尺寸下的样本需求;随后送入后续模块之前还需做标准化预处理去除光照变化等因素干扰[^4]。 ##### 特征抽取(CNN) 使用若干组堆叠起来的标准二维卷积核滤波器依次作用于整个画面区域之上形成逐级抽象表示向量集合——即所谓的高级语义概念编码版本代替低层次物理属性刻画方式呈现出来供下一步计算单元调用参考依据之一。 ##### 序列映射(RNN/Bi-LSTM) 此时已获得一组按照行扫描顺序组织而成的一维数组列表代表各片段内部潜在关联线索所在之处待挖掘整理成连贯叙述链条模样展现给人类读者易于辨认解读样子出现眼前屏幕前头像框里边儿啦! 最后借助专门定制版别的优化算法实现最大似然估计准则指导下寻找最佳匹配候选解集当中唯一正确选项确认完毕结束全部运算过程返回预期答案告知请求方知晓情况即可满足其查询意图达成共识圆满解决问题困惑疑虑消除干净利索不留尾巴哈~ ```python import tensorflow as tf def crnn_model(input_shape=(32, None, 1), num_classes=63): inputs = tf.keras.layers.Input(shape=input_shape) # CNN Feature Extraction Layer cnn_features = tf.keras.Sequential([ tf.keras.layers.Conv2D(64, kernel_size=3, activation='relu', padding='same'), tf.keras.layers.MaxPooling2D(pool_size=2), tf.keras.layers.Conv2D(128, kernel_size=3, activation='relu', padding='same'), tf.keras.layers.MaxPooling2D(pool_size=2), tf.keras.layers.Conv2D(256, kernel_size=3, activation='relu', padding='same'), tf.keras.layers.Conv2D(256, kernel_size=3, activation='relu', padding='same'), tf.keras.layers.MaxPooling2D(pool_size=(2, 1)), tf.keras.layers.Conv2D(512, kernel_size=3, activation='relu', padding='same'), tf.keras.layers.BatchNormalization(), tf.keras.layers.Conv2D(512, kernel_size=3, activation='relu', padding='same'), tf.keras.layers.BatchNormalization(), tf.keras.layers.MaxPooling2D(pool_size=(2, 1)) ])(inputs) shape = cnn_features.shape new_shape = (-1, int(shape[2]) * int(shape[3])) rnn_input = tf.reshape(cnn_features, [-1, shape[1], new_shape[-1]]) # RNN Sequence Modeling Layer blstm_output = tf.keras.layers.Bidirectional( tf.keras.layers.LSTM(units=256, return_sequences=True))(rnn_input) dense_layer = tf.keras.layers.Dense(num_classes)(blstm_output) model = tf.keras.Model(inputs=inputs, outputs=dense_layer) return model ```
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nobrody

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值