最近看了苏剑林的几篇博客, 我忽然对keras不是那么抵触了,才发现之前认为Keras使用不灵活完全是因为的认识不够深入。所以我准备使用Tensorflow 2.0中的tf.Keras来 构建Yolo v3,在tensorflow中我们可以更加灵活的优化我们的数据输入管道,这次介绍一下多输入的model如何结合tf.data,基础的使用方式在这里学习。

起因

大家都知道,YOLO v3是一个多输出的模型,在构建模型的时候,label需要多个,所以模型输入也是多个。如果输入是numpy数组,可以保存不同维度的数组,但是tf.Tensor只能保存相同尺寸的数组。

然后在model.fit中使用tf.data,一般情况下是保证返回值对应x、y,但是现在yolo需要输入[x1,x2,x3,x4],y,这需要tf.data返回一个包含元组的列表。

解决方案

首先我尝试在map中直接返回嵌套的列表,但是因为我使用的是py_function,返回值不能嵌套(暂时没有尝试纯tensorflow的函数能否返回嵌套列表)。还好找了一个好用的方法:tf.data.dataset.zip,可以将两个dataset对象制作成嵌套列表,所以只需要小改动程序如下,把样本与标签分开制作,然后再合并即可!:

def parser(lines):
image_data = []
box_data = []
for line in lines:
image, box = get_random_data(line.numpy().decode(), input_shape, random=True)
image_data.append(image)
box_data.append(box)

image_data = np.array(image_data)
box_data = np.array(box_data)

y_true = [tf.convert_to_tensor(y, tf.float32) for y in preprocess_true_boxes(box_data, input_shape, anchors, num_classes)]
image_data = tf.convert_to_tensor(image_data, tf.float32)
return (image_data, *y_true)

x_set = (tf.data.Dataset.from_tensor_slices(annotation_lines).
apply(tf.data.experimental.shuffle_and_repeat(batch_size * 300, seed=66)).
batch(batch_size, drop_remainder=True).
map(lambda lines: py_function(parser, [lines], [tf.float32] * (1 + len(anchors) // 3))))
y_set = tf.data.Dataset.from_tensors(tf.zeros(batch_size, tf.float32)).repeat()
dataset = tf.data.Dataset.zip((x_set, y_set))

sample = next(iter(dataset))




train_set = create_dataset(lines[:num_train], batch_size, input_shape, anchors, num_classes)

model.fit(train_set,
epochs=20,
steps_per_epoch=max(1, num_train // batch_size),
callbacks=[logging, checkpoint])