我对机器学习很陌生,作为一个学习练习,我尝试在CNTK中实现卷积递归神经网络来识别图像中可变长度的文本。其基本思想是将CNN的输出作为一个序列输入RNN,然后用CTC作为损失函数。我遵循了“cntk208:使用连接主义时间分类(CTC)标准训练声学模型”教程,它展示了CTC的基本用法。不幸的是,在训练过程中,我的网络收敛到只输出空白标签,而不输出其他内容,因为出于某种原因,这会导致最小的损失。在
我给我的网络提供尺寸为(1,32,96)的图像,我会动态生成它们来显示一些随机字母。作为标签,我给它一个由CTC在索引0处所需的一个热编码字母序列(这都是numpy数组,因为我使用自定义数据加载)。我发现要使forward_backward()函数工作,我需要确保它的两个输入使用相同长度的动态轴,这是通过使标签字符串的长度与网络输出长度相同,并在下面的代码中使用to_sequence_like()来实现的(我不知道如何做得更好使用to_sequence_like()的副作用是,在评估此模型时,我需要传递伪标签数据)。在
alphabet = "0123456789abcdefghijklmnopqrstuvwxyz"
input_dim_model = (1, 32, 96) # images are 96 x 32 with 1 channel of color (gray)
num_output_classes = len(alphabet) + 1
ltsm_hidden = 256
def bidirectionalLTSM(features, nHidden, nOut):
a = C.layers.Recurrence(C.layers.LSTM(nHidden))(features)
b = C.layers.Recurrence(C.layers.LSTM(nHidden), go_backwards=True)(features)
c = C.splice(a, b)
r = C.layers.Dense(nOut)(c)
return r
def create_model_rnn(features):
h = features
h = bidirectionalLTSM(h, ltsm_hidden, ltsm_hidden)
h = bidirectionalLTSM(h, ltsm_hidden, num_output_classes)
return h
def create_model_cnn(features):
with C.layers.default_options(init=C.glorot_uniform(), activation=C.relu):
h = features
h = C.layers.Convolution2D(filter_shape=(3,3),
num_filters=64,
strides=(1,1),
pad=True, name='conv_0')(h)
#more layers...
h = C.layers.BatchNormalization(name="batchnorm_6")(h)
return h
x = C.input_variable(input_dim_model, name="x")
label = C.sequence.input((num_output_classes), name="y")
def create_model(features):
#Composite(x: Tensor[1,32,96]) -> Tensor[512,1,23]
a = create_model_cnn(features)
a = C.reshape(a, (512, 23))
#Composite(x: Tensor[1,32,96]) -> Tensor[23,512]
a = C.swapaxes(a, 0, 1)
#is there a better way to convert to sequence and still be compatible with forward_backwards() ?
#Composite(x: Tensor[1,32,96], y: Sequence[Tensor[37]]) -> Sequence[Tensor[512]]
a = C.to_sequence_like(a, label)
#Composite(x: Tensor[1,32,96], y: Sequence[Tensor[37]]) -> Sequence[Tensor[37]]
a = create_model_rnn(a)
return a
#Composite(x: Tensor[1,32,96], y: Sequence[Tensor[37]]) -> Sequence[Tensor[37]]
z = create_model(x)
#LabelsToGraph(y: Sequence[Tensor[37]]) -> Sequence[Tensor[37]]
graph = C.labels_to_graph(label)
#Composite(y: Sequence[Tensor[37]], x: Tensor[1,32,96]) -> np.float32
criteria = C.forward_backward(C.labels_to_graph(label), z, blankTokenId=0)
err = C.edit_distance_error(z, label, squashInputs=True, tokensToIgnore=[0])
lr = C.learning_rate_schedule(0.01, C.UnitType.sample)
learner = C.adadelta(z.parameters, lr)
progress_printer = C.logging.progress_print.ProgressPrinter(50, first=10, tag='Training')
trainer = C.Trainer(z, (criteria, err), learner, progress_writers=[progress_printer])
#some more custom code ...
#below is how I'm feeding the data
while True:
x1, y1 = custom_datareader.next_minibatch()
#x1 is a list of numpy arrays containing training images
#y1 is a list of numpy arrays with one hot encoded labels
trainer.train_minibatch({x: x1, label: y1})
网络很快会聚,尽管不是我想要的地方(左边是网络输出,右边是我给它的标签):
^{pr2}$我的问题是如何让网络学会输出正确的字幕。我想补充一点,我成功地用同样的技术训练了一个模型,但是它是用Pythorch制造的,所以图像或标签不太可能是问题所在。另外,有没有更好的方法将卷积层的输出转换成带动态轴的序列,这样我就可以在forward_backward()函数中使用它?在
在CNTK中,有很多事情使得训练CRNN模型变得困难(标签格式的正确方法很棘手,整个标签图的转换,没有转录误差度量等)。以下是正确工作的模型实现:
https://github.com/BenjaminTrapani/SceneTextOCR/tree/master
它依靠CNTK的一个分支来修复图像读取器的错误,提供一个转录错误函数,并提高文本格式读取器的性能。它还提供了一个从mjsynth数据集生成文本格式标签的应用程序。以下是如何设置标签格式以供参考:
^{pr2}$513528
是序列ID,应该与同一样本对应的图像数据序列ID匹配。textLabel
用于为minibatch源创建流。在C++中创建如下流:^ {CD3>}是CTC解码的空白字符的索引。“:”前面的其他值是标签的字符代码。
1
是对序列中的每个向量进行1-热编码。有一堆尾随的空白字符,以确保序列与支持的最大序列长度一样长,因为在编写本文时,CTC丢失函数实现不支持可变长度序列。在CNTK学习者默认使用聚合梯度来适应不同小批量规模的分布式训练。然而,聚合梯度对于像adadelta这样的adagrad风格的学习者并不一样。请尝试使用_mean_gradient=True:
相关问题 更多 >
编程相关推荐