在Caffe中使用Baidu warpctc实现CTC Loss的计算
2017-08-10 10:45
423 查看
CTC(Connectionist Temporal Classification) Loss 函数多用于序列有监督学习,优点是不需要对齐输入数据及标签。本文内容并不涉及CTC Loss的原理介绍,而是关于如何在Caffe中移植Baidu美研院实现的warp-ctc,并利用其实现一个LSTM
+ CTC Loss的验证码识别demo。下面这张图引用自warp-ctc的项目页面。本文介绍内容的相关代码可以参见我的GitHub项目warpctc-caffe
本节介绍了如何将
首先,我们将
由于
对Caffe的修改就是这么简单,之后我们需要修改
Loss的计算。
另外,需要将warp-ctc中包含CUDA代码的相关头文件后缀名改为
对于详细的修改配置,还请参见GitHub相应的代码文件。
编译没有问题后,我们可以编写
Loss计算的全部API接口。
使用
设置
stream。
调用
调用
其中,在第三步中计算
这部分的实现代码分别位于
本部分相关代码位于
使用
使用
将图像的每一列看做一个time step,网络模型使用
模型训练过程中,数据输入使用
使用
demo。默认生成的图片大小为
使用
在Caffe中已经有了
Caffe中Batch的内存布局顺序为
另外,LSTM需要第二个输入,用于指示时序信号的起始位置。在代码中,我新加入了一个名为
indicator序列。
在某次试验中,迭代50,000次,实验过程中的损失函数变化如下:
在验证集上的精度变化如下:
最终模型的精度在98%左右。考虑到本实验只是简单堆叠了两层的LSTM,并使用CTC Loss进行训练,能够轻易达到这一精度,可以在一定程度上说明CTC Loss的强大。
至于该实验的具体细节,可以参考repo的相关具体代码实现。
+ CTC Loss的验证码识别demo。下面这张图引用自warp-ctc的项目页面。本文介绍内容的相关代码可以参见我的GitHub项目warpctc-caffe
移植warp-ctc
本节介绍了如何将warp-ctc的源码在Caffe中进行编译。
首先,我们将
warp-ctc的项目代码从GitHub上clone下来。在Caffe的
include/caffe和
src/caffe下分别创建名为
3rdparty的文件夹,将warp-ctc中的头文件和实现文件分别放到对应的文件夹下。之后,我们需要对其代码和配置进行修改,才能在Caffe中顺利编译。
由于
warp-ctc中使用了
C++11的相关技术,所以需要修改Caffe的
Makefile文件,添加
C++11支持,可以参见Makefile。
对Caffe的修改就是这么简单,之后我们需要修改
warp-ctc中的代码文件。这里的修改多且乱,边改边试着编译,所以可能其中也有不必要的修改。最后的目的就是能够使用GPU进行CTC
Loss的计算。
warp-ctc提供了CPU多线程的计算,这里我直接将相应的
openmp并行化语句删掉了。
另外,需要将warp-ctc中包含CUDA代码的相关头文件后缀名改为
cuh,这样才能够通过编译。否则编译器会给出找不到
__host__和
__device__等等关键字的错误。
对于详细的修改配置,还请参见GitHub相应的代码文件。
实现CTC
Loss计算
编译没有问题后,我们可以编写ctc_loss_layer实现CTC Loss的计算。在实现时,注意参考文件
ctc.h。这个文件中给出了使用
warp-ctc进行CTC
Loss计算的全部API接口。
ctc_loss_layer继承自
loss_layer,主要是前向和反向计算的实现。由于
warp-ctc中只对单精度浮点数
float进行支持,所以,对于双精度网络参数,直接将其设置为
NOT_IMPLEMENTED,如下所示。
1234567891011 | template <>void CtcLossLayer<double>::Forward_cpu( const vector<Blob<double>*>& bottom, const vector<Blob<double>*>& top) { NOT_IMPLEMENTED;}template <>void CtcLossLayer<double>::Backward_cpu(const vector<Blob<double>*>& top, const vector<bool>& propagate_down, const vector<Blob<double>*>& bottom) { NOT_IMPLEMENTED;} |
warp-ctc相关接口进行CTC Loss计算的步骤如下:
设置
ctcOptions,指定使用CPU或GPU进行计算,并指定CPU计算的线程数和GPU计算的CUDA
stream。
调用
get_workspace_size()函数,预先为计算分配空间(分配的内存根据计算平台不同位于CPU或GPU)。
调用
compute_ctc_loss()函数,计算
loss和
gradient。
其中,在第三步中计算
gradient时,可以直接将对应
blob的
cpu/gpu_diff指针传入,作为
gradient。
这部分的实现代码分别位于
include/caffe/layers和
src/caffe/layers/下。
验证码数字识别
本部分相关代码位于examples/warpctc文件夹下。实验方案如下。
使用
Python中的
capycha进行包含
0-9数字的验证码图片的产生,图片中数字个数从
1到
MAX_LEN不等。
使用
10作为
blank_label,将所有的标签序列在后面补
blank_label以达到同样的长度
MAX_LEN。
将图像的每一列看做一个time step,网络模型使用
image data->2LSTM->fc->CTC Loss,简单粗暴。
模型训练过程中,数据输入使用
HDF5格式。
数据产生
使用captcha生成验证码图片。这里是一个简单的API
demo。默认生成的图片大小为
160x60。我们将其长宽缩小一半,使用
80x30的彩色图片作为输入。
使用
python中的
h5py模块生成
HDF5格式的数据文件。将全部图片分为两部分,80%作为训练集,20%作为验证集。
LSTM的输入
在Caffe中已经有了lstm_layer的实现。
lstm_layer要求输入的序列
blob为
TxNx...,也就是说我们需要将输入的image进行转置。
Caffe中Batch的内存布局顺序为
NxCxHxW。我们将图像中的每一列作为一个time step输入的xx向量。所以,在代码中使用了liuwei的SSD工作中实现的
permute_layer进行转置,将
W维度放到最前方。与之对应的参数定义如下:
123456789101112 | layer { name: "permuted_data" type: "Permute" bottom: "data" top: "permuted_data" permute_param { order: 3 # W order: 0 # N order: 1 # C order: 2 # H }} |
ContinuationIndicator的layer,产生对应的time
indicator序列。
训练
在某次试验中,迭代50,000次,实验过程中的损失函数变化如下:在验证集上的精度变化如下:
最终模型的精度在98%左右。考虑到本实验只是简单堆叠了两层的LSTM,并使用CTC Loss进行训练,能够轻易达到这一精度,可以在一定程度上说明CTC Loss的强大。
至于该实验的具体细节,可以参考repo的相关具体代码实现。
相关文章推荐
- 使用开源Grid平台-GridGain实现网格计算
- 使用开源Grid平台-GridGain实现网格计算
- 使用开源Grid平台-GridGain实现网格计算
- 使用开源Grid平台-GridGain实现网格计算
- 使用开源Grid平台-GridGain实现网格计算
- 使用开源Grid平台-GridGain实现网格计算
- 使用RMI编写客户端-服务器应用程序,实现分布式计算
- 使用开源Grid平台-GridGain实现网格计算
- 【Java数据结构的实现】之系列三栈的实现(使用栈计算后缀表达式)
- 使用vector实现矩阵,并计算N阶矩阵的行列式
- 关于使用定义函数来实现对于数组c中的元素进行筛选最大值最小值,计算平均值等操作
- 使用js实现两点之间距离计算功能
- 使用PHP实现计算两个日期间隔的年、月、周、日数
- 使用开源Grid平台-GridGain实现网格计算
- 使用开源Grid平台-GridGain实现网格计算
- 进化计算简介和遗传算法的实现--AForge.NET框架的使用(六)
- 使用开源Grid平台-GridGain实现网格计算
- 使用vector实现矩阵,并计算N阶矩阵的行列式
- 使用开源Grid平台-GridGain实现网格计算
- 使用开源Grid平台-GridGain实现网格计算