六、相似性学习
在本章中,我们将学习相似性学习并学习相似性学习中使用的各种损失函数。 当每个类别的数据集都很小时,相似性学习对我们很有用。 我们将了解可用于人脸分析的不同数据集,并建立用于人脸识别,界标检测的模型。 我们将在本章介绍以下主题:
- 相似性学习的不同算法
- 用于相似度学习的各种损失函数
- 可以使用此类模型的各种方案
- 人脸识别的完整过程
相似性学习算法
相似性学习是训练度量以计算两个实体之间的相似性的过程。 由于学习了相似性,这也可以称为度量学习。 度量可以是欧几里得或余弦或其他自定义距离函数。 实体可以是任何数据,例如图像,视频,文本或表格。 为了计算度量,需要图像的向量表示。 此表示可以是 CNN 计算的特征,如第 3 章,“图像检索”中所述。 为对象分类而学习的 CNN 可以用作计算度量的向量。 为图像分类而获得的特征向量将不是手头任务的最佳表示。 在相似性学习中,我们发现有关 CNN 的信息,这些 CNN 会为相似性学习任务生成经过训练的特征。 这里给出了相似性学习的一些应用:
- 使用生物识别比较两个人脸的人脸验证
- 用于在线查找类似产品的现实世界中的对象的视觉搜索
- 某些属性相似的产品的视觉推荐
在本章中,我们将详细了解人脸验证。 因此,让我们从可用于相似性学习的算法开始。
连体网络
顾名思义,连体网络是一种神经网络模型,其中训练该网络以区分两个输入。 连体网络可以训练 CNN,以通过两个编码器产生嵌入。 每个编码器被馈送正对或负对中的一个图像。 连体网络所需的数据少于其他深度学习算法。 最初引入连体网络来比较签名。 下图显示了一个连体网络。 权重在网络之间共享:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Qsv21gtk-1681567606691)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/451420ac-8f1b-415c-b8d7-f893cf701fee.png)]
连体网络的另一种用途是单样本学习。 单样本学习是仅举一个示例的学习技术。 在这种情况下,可以显示图像,并判断它们是否相似。 对于大多数相似性学习任务,需要一对正负对进行训练。 可以将此类数据集与可用于分类任务的任何数据集一起形成,前提是它们是欧几里得距离。 这些算法与前几章中的算法之间的主要区别在于,这些编码器试图将一个编码器与另一个编码器区分开。
对比损失
对比损失通过相似度区分图像。 使用相似性度量比较特征或潜在层,并与目标一起训练相似性得分。 在正对的情况下,目标将为 0,因为两个输入相同。 对于负数对,在余弦距离或正则欧几里得距离的情况下,潜对之间的距离最大为 0。 损耗可以由contrastive_loss
定义,在以下代码中进行解释:
def contrastive_loss(model_1, model_2, label, margin=0.1): distance = tf.reduce_sum(tf.square(model_1 - model_2), 1) loss = label * tf.square( tf.maximum(0., margin - tf.sqrt(distance))) + (1 - label) * distance loss = 0.5 * tf.reduce_mean(loss) return loss
比较两个模型的距离并计算损失。 现在,我们将定义和训练一个连体网络。 对于连体网络,我们将需要两个相同的模型。 接下来,借助以下代码,为具有给定输入的简单 CNN 定义一个函数:
def get_model(input_): input_reshape = tf.reshape(input_, [-1, 28, 28, 1], name='input_reshape') convolution_layer_1 = convolution_layer(input_reshape, 64) pooling_layer_1 = pooling_layer(convolution_layer_1) convolution_layer_2 = convolution_layer(pooling_layer_1, 128) pooling_layer_2 = pooling_layer(convolution_layer_2) flattened_pool = tf.reshape(pooling_layer_2, [-1, 5 * 5 * 128], name='flattened_pool') dense_layer_bottleneck = dense_layer(flattened_pool, 1024) return dense_layer_bottleneck
定义的模型将使用两次来定义连体网络所需的编码器。 接下来,定义两个模型的占位符。 对于每一对,输入的相似性也作为输入提供。 定义的模型相同。 还可以定义模型,以便共享权重。 此处定义了左右两个模型:
left_input = tf.placeholder(tf.float32, shape=[None, input_size]) right_input = tf.placeholder(tf.float32, shape=[None, input_size]) y_input = tf.placeholder(tf.float32, shape=[None, no_classes]) left_bottleneck = get_model(left_input) right_bottleneck = get_model(right_input)
瓶颈层是从模型中获取的,并被连接在一起。 这对于相似性学习问题至关重要。 可以创建任意数量的模型,并且可以连接最后的层,如下所示:
dense_layer_bottleneck = tf.concat([left_bottleneck, right_bottleneck], 1)
接下来,添加一个丢弃层,并从级联层中计算出对率。 然后,该过程类似于任何其他网络,如下所示:
dropout_bool = tf.placeholder(tf.bool) dropout_layer = tf.layers.dropout( inputs=dense_layer_bottleneck, rate=0.4, training=dropout_bool ) logits = dense_layer(dropout_layer, no_classes) with tf.name_scope('loss'): softmax_cross_entropy = tf.nn.softmax_cross_entropy_with_logits( labels=y_input, logits=logits) loss_operation = tf.reduce_mean(softmax_cross_entropy, name='loss') tf.summary.scalar('loss', loss_operation) with tf.name_scope('optimiser'): optimiser = tf.train.AdamOptimizer().minimize(loss_operation) with tf.name_scope('accuracy'): with tf.name_scope('correct_prediction'): predictions = tf.argmax(logits, 1) correct_predictions = tf.equal(predictions, tf.argmax(y_input, 1)) with tf.name_scope('accuracy'): accuracy_operation = tf.reduce_mean( tf.cast(correct_predictions, tf.float32)) tf.summary.scalar('accuracy', accuracy_operation) session = tf.Session() session.run(tf.global_variables_initializer()) merged_summary_operation = tf.summary.merge_all() train_summary_writer = tf.summary.FileWriter('/tmp/train', session.graph) test_summary_writer = tf.summary.FileWriter('/tmp/test') test_images, test_labels = mnist_data.test.images, mnist_data.test.labels
数据必须分别输入左右模型,如下所示:
for batch_no in range(total_batches): mnist_batch = mnist_data.train.next_batch(batch_size) train_images, train_labels = mnist_batch[0], mnist_batch[1] _, merged_summary = session.run([optimiser, merged_summary_operation], feed_dict={ left_input: train_images, right_input: train_images, y_input: train_labels, dropout_bool: True }) train_summary_writer.add_summary(merged_summary, batch_no) if batch_no % 10 == 0: merged_summary, _ = session.run([merged_summary_operation, accuracy_operation], feed_dict={ left_input: test_images, right_input: test_images, y_input: test_labels, dropout_bool: False }) test_summary_writer.add_summary(merged_summary, batch_no)
我们已经看到了如何定义一个连体网络。 定义了两个编码器,并连接了潜在空间以形成训练损失。 左右模型分别提供数据。 接下来,我们将看到如何在单个网络中执行相似性学习。
FaceNet
Schroff 等人提出的 FaceNet 模型解决了人脸验证问题。 它学习一个深层的 CNN,然后将人脸图像转换为嵌入图像。 嵌入可用于比较人脸以查看其相似程度,并可通过以下三种方式使用:
- 人脸验证考虑两个人脸,并确定它们是否相似。 人脸验证可以通过计算距离度量来完成。
- 人脸识别是用于用名字标记人脸的分类问题。 嵌入向量可用于训练最终标签。
- 人脸聚类将相似的人脸分组,就像照片应用将同一个人的照片聚在一起的方式一样。 诸如 KMeans 之类的聚类算法用于对人脸进行分组。
下图显示了 FaceNet 架构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0Vi2USZ0-1681567606693)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/b1d11aa0-8cd0-498c-873c-edc53897f6c9.png)]
经 Schroff 等人许可复制。
FaceNet 会获取一批人脸图像并进行训练。 在那一批中,将有几个正对。 在计算损耗时,考虑正对和最接近的几个负对。 挖掘选择性对可实现平稳训练。 如果所有负面因素一直都被推开,则训练不稳定。 比较三个数据点称为三元组损失。 在计算损耗时,图像被认为具有正负匹配。 底片仅被推动一定的幅度。 在此详细说明三元组损失。
三元组损失
三元组损失学习图像的得分向量。 人脸描述符的得分向量可用于验证欧几里得空间中的人脸。 在学习投影的意义上,三元组损失类似于度量学习,因此可以区分输入。 这些投影或描述符或分数向量是紧凑的表示形式,因此可以视为降维技术。 一个三元组由一个锚点,正负面组成。 锚可以是任何人的人脸,正面是同一个人的图像。 负片图像可能来自另一个人。 显然,对于给定的锚点,将会有很多负面的人脸。 通过选择当前更靠近锚点的底片,编码器将很难区分人脸,从而使其学习效果更好。 此过程称为难负例挖掘。 可以在欧氏空间中使用阈值获得更接近的负值。 下图描述了三元组损失模型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aoKyN6dX-1681567606693)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/861242f9-1c52-49ef-a891-0377251b7745.png)]
经 Schroff 等人许可复制。
TensorFlow 中的损失计算如下所示:
def triplet_loss(anchor_face, positive_face, negative_face, margin): def get_distance(x, y): return tf.reduce_sum(tf.square(tf.subtract(x, y)), 1) positive_distance = get_distance(anchor_face, positive_face) negative_distance = get_distance(anchor_face, negative_face) total_distance = tf.add(tf.subtract(positive_distance, negative_distance), margin) return tf.reduce_mean(tf.maximum(total_distance, 0.0), 0)
三元组的挖掘是一项艰巨的任务。 每个点都必须与其他点进行比较,以获得适当的锚点和正对。 三元组的挖掘如下所示:
def mine_triplets(anchor, targets, negative_samples): distances = cdist(anchor, targets, 'cosine') distances = cdist(anchor, targets, 'cosine').tolist() QnQ_duplicated = [ [target_index for target_index, dist in enumerate(QnQ_dist) if dist == QnQ_dist[query_index]] for query_index, QnQ_dist in enumerate(distances)] for i, QnT_dist in enumerate(QnT_dists): for j in QnQ_duplicated[i]: QnT_dist.itemset(j, np.inf) QnT_dists_topk = QnT_dists.argsort(axis=1)[:, :negative_samples] top_k_index = np.array([np.insert(QnT_dist, 0, i) for i, QnT_dist in enumerate(QnT_dists_topk)]) return top_k_index
由于距离计算发生在 CPU 中,因此这可能会使在 GPU 机器上的训练变慢。 FaceNet 模型是训练人脸相似模型的最新方法。
DeepNet 模型
DeepNet 模型用于学习用于人脸验证任务(例如 FaceNet)的人脸嵌入。 这是对上一部分中讨论的 FaceNet 方法的改进。 它需要对同一张脸进行多次裁剪,并通过多个编码器才能获得更好的嵌入效果。 与 FaceNet 相比,此方法具有更高的准确率,但需要更多时间进行处理。 人脸裁切在相同区域进行,并通过其各自的编码器。 然后将所有层连接起来以进行三元组损失的训练。
DeepRank
Wang 等人提出的 DeepRank 用于根据相似度对图像进行排名。 图像通过不同的模型传递,如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gsJadFrF-1681567606693)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/85e5d360-602e-4bf4-b596-09e44f152e27.png)]
经王等人许可转载。
在此也计算了三元组损耗,并且反向传播更加顺利。 然后可以将图像转换为线性嵌入以进行排名,如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j4cp4apH-1681567606694)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/e87e214e-cc3c-45bd-8629-75a2beeef0ea.png)]
经 Wang 等人许可复制。
该算法对于排名目的非常有用。
视觉推荐系统
视觉推荐系统非常适合获取给定图像的推荐。 推荐模型提供具有相似属性的图像。 根据 Shankar 等人提出的以下模型,您可以了解相似图像的嵌入,并提供以下建议:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sukDZHKL-1681567606694)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/7337cf27-f5a5-4292-b4b2-d967bd5382f9.png)]
图(a)显示了深度排名架构,图(b)显示了 VisNet 架构(经 Shankar 等人许可复制)。
这些是用于相似性学习的一些算法。 在下一节中,我们将看到如何将这些技术应用于人脸。
人脸分析
可以使用计算机视觉以多种方式分析人脸。 为此,需要考虑以下几个因素:
- 人脸检测:找到人脸位置的边界框
- 人脸标志检测:查找鼻子,嘴巴等人脸特征的空间点
- 人脸对齐:将人脸转换成正面人脸以进行进一步分析
- 属性识别:查找诸如性别,微笑等属性
- 情感分析:分析人的情感
- 人脸验证:查找两个图像是否属于同一个人
- 人脸识别:识别人脸
- 人脸聚类:将同一个人的人脸分组在一起
在以下各节中,让我们详细了解这些任务的数据集和实现。
人脸检测
人脸检测类似于对象检测,我们在第 4 章,“对象检测”中讨论过。 必须从图像中检测出人脸的位置。 可以从这里下载名为人脸检测数据集和基准(FDDB)的数据集。 。 它具有 2,845 张带有 5,171 张脸的图像。 可以从这里下载 Yang 等人提出的另一个称为宽脸的数据集。 它具有 32,203 张图像和 393,703 张人脸。 这是来自更广泛的人脸数据集的图像示例:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BXaqiovd-1681567606695)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/92e946e7-2fb7-468a-aacd-77039c30fbb4.jpg)]
由杨等人提出。 并转载自这里
数据集的比例,姿势,遮挡,表情,妆容和照明度都有很好的变化。 另一个名多属性标签的人脸(MALF)的数据集包含 5,250 张图像,其中包含 11,931 张人脸。 可以从这个链接访问 MALF。 在对象检测中使用的相同技术也可以应用于人脸检测。
人脸标志和属性
人脸标志是人脸的空间点。 空间点对应于各种人脸特征的位置,例如眼睛,眉毛,鼻子,嘴巴和下巴。 点数可能会从 5 到 78 不等,具体取决于标注。 人脸界标也称为基准点,人脸关键点或人脸姿势。 人脸标志具有许多应用,如下所示:
- 更好地进行人脸验证或识别的人脸对齐
- 跟踪视频中的人脸
- 测量人脸表情或情感
- 有助于诊断疾病
接下来,我们将看到一些带有基准点标注的数据库。
多任务人脸标志(MTFL)数据集
MTFL
数据集由 Zhang 等人提出。 并带有五个人脸标志以及性别,微笑,眼镜和头部姿势标注。 数据库中存在 12,995 张人脸。 可以从这里下载MTFL
。
这是MTFL
中存在的图像的示例:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9Telewn4-1681567606695)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/de1113bf-8f62-4752-a656-e0f79ca4b1a4.jpg)]
由张等人提出。 并转载自这里
人脸在年龄,照度,情感等方面有很多变化。 头部姿势是人脸方向的角度,以度为单位。 眼镜,微笑,性别属性等都用二元标签标注。
Kaggle 关键点数据集
Kaggle 关键点数据集带有 15 个人脸标志。 数据集中存在 8,832 张图像。 可以从这个链接下载。 图像尺寸为 96 像素 x 96 像素。
多属性人脸标志(MAFL)数据集
Zhang 等人提出的MAFL
数据集。 带有 5 种具有 40 种不同人脸属性的人脸标志。 数据库中存在 20,000 张人脸。 可以从这里下载MAFL
。 这是MAFL
中存在的图像的示例:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5bp6Xiib-1681567606696)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/0c8f1dd3-a580-402f-a9e2-66ccff7c19b1.png)]
由 Liu 等人提出。 并转载自这里
标注的属性包括尖头,带子,小胡子,卷发,戴帽子等。 这些图像也包含在CelebA
数据集中,稍后将详细讨论。
学习人脸关键点
如先前主题中所述,在计算关键人脸点时,需要定义一些参数。 我们将使用以下代码来定义这些参数:
image_size = 40 no_landmark = 10 no_gender_classes = 2 no_smile_classes = 2 no_glasses_classes = 2 no_headpose_classes = 5 batch_size = 100 total_batches = 300
接下来,为各种输入保留一些占位符。
image_input = tf.placeholder(tf.float32, shape=[None, image_size, image_size]) landmark_input = tf.placeholder(tf.float32, shape=[None, no_landmark]) gender_input = tf.placeholder(tf.float32, shape=[None, no_gender_classes]) smile_input = tf.placeholder(tf.float32, shape=[None, no_smile_classes]) glasses_input = tf.placeholder(tf.float32, shape=[None, no_glasses_classes]) headpose_input = tf.placeholder(tf.float32, shape=[None, no_headpose_classes])
接下来,使用四个卷积层构造主模型,如以下代码所示:
image_input_reshape = tf.reshape(image_input, [-1, image_size, image_size, 1], name='input_reshape') convolution_layer_1 = convolution_layer(image_input_reshape, 16) pooling_layer_1 = pooling_layer(convolution_layer_1) convolution_layer_2 = convolution_layer(pooling_layer_1, 48) pooling_layer_2 = pooling_layer(convolution_layer_2) convolution_layer_3 = convolution_layer(pooling_layer_2, 64) pooling_layer_3 = pooling_layer(convolution_layer_3) convolution_layer_4 = convolution_layer(pooling_layer_3, 64) flattened_pool = tf.reshape(convolution_layer_4, [-1, 5 * 5 * 64], name='flattened_pool') dense_layer_bottleneck = dense_layer(flattened_pool, 1024) dropout_bool = tf.placeholder(tf.bool) dropout_layer = tf.layers.dropout( inputs=dense_layer_bottleneck, rate=0.4, training=dropout_bool )
接下来,我们将使用以下代码为所有不同的任务创建一个对率分支:
landmark_logits = dense_layer(dropout_layer, 10) smile_logits = dense_layer(dropout_layer, 2) glass_logits = dense_layer(dropout_layer, 2) gender_logits = dense_layer(dropout_layer, 2) headpose_logits = dense_layer(dropout_layer, 5)
损耗是针对所有人脸特征单独计算的,如以下代码所示:
landmark_loss = 0.5 * tf.reduce_mean( tf.square(landmark_input, landmark_logits)) gender_loss = tf.reduce_mean( tf.nn.softmax_cross_entropy_with_logits( labels=gender_input, logits=gender_logits)) smile_loss = tf.reduce_mean( tf.nn.softmax_cross_entropy_with_logits( labels=smile_input, logits=smile_logits)) glass_loss = tf.reduce_mean( tf.nn.softmax_cross_entropy_with_logits( labels=glasses_input, logits=glass_logits)) headpose_loss = tf.reduce_mean( tf.nn.softmax_cross_entropy_with_logits( labels=headpose_input, logits=headpose_logits)) loss_operation = landmark_loss + gender_loss + \ smile_loss + glass_loss + headpose_loss
现在,我们将初始化优化器并开始训练,如以下代码所示:
optimiser = tf.train.AdamOptimizer().minimize(loss_operation) session = tf.Session() session.run(tf.initialize_all_variables()) fiducial_test_data = fiducial_data.test for batch_no in range(total_batches): fiducial_data_batch = fiducial_data.train.next_batch(batch_size) loss, _landmark_loss, _ = session.run( [loss_operation, landmark_loss, optimiser], feed_dict={ image_input: fiducial_data_batch.images, landmark_input: fiducial_data_batch.landmarks, gender_input: fiducial_data_batch.gender, smile_input: fiducial_data_batch.smile, glasses_input: fiducial_data_batch.glasses, headpose_input: fiducial_data_batch.pose, dropout_bool: True }) if batch_no % 10 == 0: loss, _landmark_loss, _ = session.run( [loss_operation, landmark_loss], feed_dict={ image_input: fiducial_test_data.images, landmark_input: fiducial_test_data.landmarks, gender_input: fiducial_test_data.gender, smile_input: fiducial_test_data.smile, glasses_input: fiducial_test_data.glasses, headpose_input: fiducial_test_data.pose, dropout_bool: False })
此过程可用于检测人脸特征以及界标。
人脸识别
人脸识别或人脸识别是从数字图像或视频中识别人物的过程。 让我们在下一部分中了解可用于人脸识别的数据集。
野生(LFW)数据集中的带标签的人脸
LFW
数据集包含 13,233 张人脸和 5,749 位独特的人,被视为评估人脸验证数据集的标准数据集。 精度度量可用于评估计法。 可以在这个链接中访问数据集。
YouTube 人脸数据集
YouTube faces
数据集包含 3,425 个视频剪辑,其中包含 1,595 个独特的人。 这些视频是从 YouTube 收集的。 数据集每人至少有两个视频。 该数据集被视为视频中人脸验证的标准数据集。 可以在这个链接中访问数据集。
CelebFaces 属性数据集(CelebA)
CelebA
数据集带有人物身份以及 5 个人脸标志和 40 个属性的标注。 数据库中有 10,177 位独特的人,拥有 202,599 张人脸图像。 它是可用于人脸验证,检测,界标和属性识别问题的大型数据集之一。 图像具有带有各种标注的良好人脸变化。 可以在这个链接中访问数据集。
CASIA 网络人脸数据库
CASIA
数据集带有 10,575 个独特的带标注人脸,总共有 494,414 张图像。 该数据集可以从这个链接获得。 这是可用于人脸验证和识别问题的第二大公共数据集。
VGGFace2 数据集
Cao 等人提出的VGGFace2
数据集。 被 9,131 位独特的人注解,具有 331 万张图片。 数据集可从这个链接获得。 变化包括年龄,种族,姿势,职业和照度。 这是可用于人脸验证的最大数据集。
这是数据集中存在的图像的示例:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cCu28OX0-1681567606696)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/d2c45b27-6fc0-409c-8aa6-196b8569aec2.png)]
由曹等人提出。 并转载自这里
每个唯一的人的最小,平均和最大图像数分别是 87、362.6 和 843。
计算人脸之间的相似度
人脸相似度的计算是一个多步骤问题。 必须检测人脸,然后找到基准点。 面可以与基准点对齐。 对齐的面可以用于比较。 如前所述,人脸检测类似于对象检测。 因此,为了找到人脸之间的相似性,我们将首先通过以下代码导入所需的库,以及facenet
库:
from scipy import misc import tensorflow as tf import numpy as np import os import facenet print facenet from facenet import load_model, prewhiten import align.detect_face
可以如下所示加载和对齐图像:
def load_and_align_data(image_paths, image_size=160, margin=44, gpu_memory_fraction=1.0): minsize = 20 threshold = [0.6, 0.7, 0.7] factor = 0.709 print('Creating networks and loading parameters') with tf.Graph().as_default(): gpu_options = tf.GPUOptions(per_process_gpu_memory_fraction=gpu_memory_fraction) sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options, log_device_placement=False)) with sess.as_default(): pnet, rnet, onet = align.detect_face.create_mtcnn(sess, None) nrof_samples = len(image_paths) img_list = [None] * nrof_samples for i in range(nrof_samples): img = misc.imread(os.path.expanduser(image_paths[i]), mode='RGB') img_size = np.asarray(img.shape)[0:2] bounding_boxes, _ = align.detect_face.detect_face(img, minsize, pnet, rnet, onet, threshold, factor) det = np.squeeze(bounding_boxes[0, 0:4]) bb = np.zeros(4, dtype=np.int32) bb[0] = np.maximum(det[0] - margin / 2, 0) bb[1] = np.maximum(det[1] - margin / 2, 0) bb[2] = np.minimum(det[2] + margin / 2, img_size[1]) bb[3] = np.minimum(det[3] + margin / 2, img_size[0]) cropped = img[bb[1]:bb[3], bb[0]:bb[2], :] aligned = misc.imresize(cropped, (image_size, image_size), interp='bilinear') prewhitened = prewhiten(aligned) img_list[i] = prewhitened images = np.stack(img_list) return images
现在,我们将处理图像路径以获取嵌入。 相同的代码在这里给出:
def get_face_embeddings(image_paths, model='/20170512-110547/'): images = load_and_align_data(image_paths) with tf.Graph().as_default(): with tf.Session() as sess: load_model(model) images_placeholder = tf.get_default_graph().get_tensor_by_name("input:0") embeddings = tf.get_default_graph().get_tensor_by_name("embeddings:0") phase_train_placeholder = tf.get_default_graph().get_tensor_by_name("phase_train:0") feed_dict = {images_placeholder: images, phase_train_placeholder: False} emb = sess.run(embeddings, feed_dict=feed_dict) return emb
现在,我们将使用以下代码来计算嵌入之间的距离:
def compute_distance(embedding_1, embedding_2): dist = np.sqrt(np.sum(np.square(np.subtract(embedding_1, embedding_2)))) return dist
此函数将计算嵌入之间的欧几里德距离。
寻找最佳阈值
使用前面的函数,可以计算出该系统的精度。 以下代码可用于计算最佳阈值:
import sys import argparse import os import re from sklearn.metrics import classification_report from sklearn.metrics import accuracy_score
现在,使用以下代码从文件夹中获取图像路径:
def get_image_paths(image_directory): image_names = sorted(os.listdir(image_directory)) image_paths = [os.path.join(image_directory, image_name) for image_name in image_names] return image_paths
通过嵌入时,将获得图像的距离,如以下代码所示:
def get_labels_distances(image_paths, embeddings): target_labels, distances = [], [] for image_path_1, embedding_1 in zip(image_paths, embeddings): for image_path_2, embedding_2 in zip(image_paths, embeddings): if (re.sub(r'\d+', '', image_path_1)).lower() == (re.sub(r'\d+', '', image_path_2)).lower(): target_labels.append(1) else: target_labels.append(0) distances.append(compute_distance(embedding_1, embedding_2)) # Replace distance metric here return target_labels, distances
阈值随以下代码所示而变化,并相应打印各种度量:
def print_metrics(target_labels, distances): accuracies = [] for threshold in range(50, 150, 1): threshold = threshold/100. predicted_labels = [1 if dist <= threshold else 0 for dist in distances] print("Threshold", threshold) print(classification_report(target_labels, predicted_labels, target_names=['Different', 'Same'])) accuracy = accuracy_score(target_labels, predicted_labels) print('Accuracy: ', accuracy) accuracies.append(accuracy) print(max(accuracies))
现在,借助以下代码将图像路径传递给嵌入:
def main(args): image_paths = get_image_paths(args.image_directory) embeddings = get_face_embeddings(image_paths) # Replace your embedding calculation here target_labels, distances = get_labels_distances(image_paths, embeddings) print_metrics(target_labels, distances)
最后,图像目录作为这些方法的主要参数传递,如以下代码所示:
if __name__ == '__main__': parser = argparse.ArgumentParser() parser.add_argument('image_directory', type=str, help='Directory containing the images to be compared') parsed_arguments = parser.parse_args(sys.argv[1:]) main(parsed_arguments)
在此示例中,我们采用了预先训练的模型并将其用于构建人脸验证方法。 ;
人脸聚类
人脸聚类是将同一个人的图像分组在一起以形成相册的过程。 可以提取人脸的嵌入,并且可以使用诸如 K 均值的聚类算法将同一个人的人脸合并在一起。 TensorFlow 为 KMeans 算法提供了一个称为 tf.contrib.learn.KmeansClustering
的 API。 K 均值算法将数据点分组在一起。 借助这种 KMeans 算法,可以提取专辑的嵌入内容,并且可以一起找到个人的人脸,或者换句话说,可以将聚在一起。
总结
在本章中,我们介绍了相似性学习的基础知识。 我们研究了度量学习,连体网络和 FaceNet 等算法。 我们还介绍了损失函数,例如对比损失和三重损失。 还涵盖了两个不同的领域,即排名和推荐。 最后,通过理解几个步骤(包括检测,基准点检测和相似性评分)涵盖了人脸识别的分步演练。
在下一章中,我们将了解循环神经网络及其在自然语言处理问题中的使用。 稍后,我们将使用语言模型和图像模型来对图像进行字幕。 我们将针对该问题访问几种算法,并查看两种不同类型数据的实现。
七、图像字幕生成
在本章中,我们将处理字幕图像的问题。 这涉及到检测对象,并且还提出了图像的文本标题。 图像字幕生成也可以称为图像文本转换。 曾经被认为是一个非常棘手的问题,我们现在在此方面取得了相当不错的成绩。 对于本章,需要具有相应标题的图像数据集。 在本章中,我们将详细讨论图像字幕生成的技术和应用。
我们将在本章介绍以下主题:
- 了解用于评估它们的不同数据集和指标
- 了解用于自然语言处理问题的一些技巧
- 向量模型的不同单词
- 几种用于图像字幕生成的算法
- 不良结果和改进范围
了解问题和数据集
自动生成图像标题的过程是一项重要的深度学习任务,因为它结合了语言和视觉这两个世界。 该问题的独特性使其成为计算机视觉中的主要问题之一。 用于图像字幕生成的深度学习模型应该能够识别图像中存在的对象,并能够以自然语言生成表示对象与动作之间关系的文本。 此问题的数据集很少。 其中最著名的数据集是第 4 章,“对象检测”中对象检测中涵盖的 COCO 数据集的扩展。
了解用于图像字幕生成的自然语言处理
由于必须从图像中生成自然语言,因此熟悉自然语言处理(NLP)变得很重要。 NLP 的概念是一个广泛的主题,因此我们将范围限制为与图像字幕生成相关的主题。 自然语言的一种形式是文本。 文本是单词或字符的序列。 文本的原子元素称为令牌,它是字符的序列。 字符是文本的原子元素。
为了处理文本形式的任何自然语言,必须通过删除标点符号,方括号等对文本进行预处理。 然后,必须通过将文本分隔为空格来将文本标记为单词。 然后,必须将单词转换为向量。 接下来,我们将看到向量转换如何提供帮助。
以向量形式表达单词
向量形式的单词可以帮助自己执行算术运算。 向量必须紧凑,尺寸较小。 同义词应具有相似的向量,而反义词应具有不同的向量。 可以将单词转换为向量,以便可以如下所示比较关系:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2C1CSnKV-1681567606697)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/d4061008-c857-4130-bb3e-6c1f418ea379.png)]
该向量算法使得能够在不同实体之间的语义空间中进行比较。 接下来,我们将看到如何创建可将单词转换为向量表示的深度学习模型。
将单词转换为向量
通过在大型文本语料库上训练模型,可以将单词转换为向量。 训练模型,使得给定一个单词,该模型可以预测附近的单词。 在预测附近单词的单次热编码之前,首先对单词进行单次热编码,然后进行隐藏层。 以这种方式进行训练将创建单词的紧凑表示。 可以通过两种方式获得单词的上下文,如下所示:
- 跳跃图(SkipGram):给定一个单词,尝试预测几个接近的单词
- 连续词袋(CBOW):通过给定一组词来预测一个词,从而跳过跳跃语法
下图说明了这些过程:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6vLgq4gy-1681567606697)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/2b34c3e0-28a8-4d55-8c1b-70ea045357c5.png)]
两种方法均显示出良好的结果。 单词在嵌入空间中转换为向量。 接下来,我们将看到训练嵌入空间的详细信息。
训练嵌入
可以使用如下所示的模型来训练嵌入:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XYy4Whvi-1681567606697)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/82b25c21-48e2-4de6-b9c0-03e6e245a825.png)]
如上图所示,目标词是根据上下文或历史预测的。 该预测基于 Softmax 分类器。 隐藏层将嵌入作为紧凑的表示形式学习。 请注意,这不是完整的深度学习模型,但它仍然可以正常工作。 这是嵌入的低维可视化:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A6PO8paY-1681567606698)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/1dc5ed0c-353d-4884-8251-522e97b6bc8e.png)]
使用 Softmax 分类器的嵌入的低维可视化
该可视化使用 TensorBoard 生成。 具有相似语义或不同词性的单词会一起出现。
我们已经学习了如何训练用于生成文本的紧凑表示。 接下来,我们将看到图像字幕生成的方法。
图像字幕生成方法及相关问题
已经提出了几种对图像进行字幕的方法。 直观地,将图像转换为视觉特征,并从这些特征生成文本。 生成的文本将采用词嵌入的形式。 生成文本的一些主要方法涉及 LSTM 和关注。 让我们从使用旧的生成文本的方法开始。
使用条件随机场链接图像和文本
Kulkarni 等人在论文中,提出了一种从图像中查找对象和属性并使用它来生成文本的方法。 条件随机场(CRF)。 传统上,CRF 用于结构化预测,例如文本生成。 生成文本的流程如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ujQTSszx-1681567606698)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/114a59b6-d1fe-4f78-b9c6-32c7d416cd3a.png)]
该图说明了使用 CRF 生成文本的过程(摘自 Kulkarni 等人)
CRF 的使用在以适当的前置词以连贯的方式生成文本方面存在局限性。 结果显示在这里:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vPLZ8X1U-1681567606699)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/29a03016-9c2c-4712-9dc8-cf412fb617b7.png)]
复制自 Kulkarni 等人
结果对对象和属性具有正确的预测,但无法生成良好的描述。
在 CNN 特征上使用 RNN 生成字幕
Vinyals 等人在论文中提出了一种端到端可训练的深度学习用于图像字幕生成的方法,该方法将 CNN 和 RNN 背靠背地堆叠在一起。 这是一个端到端的可训练模型。 结构如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-92mfWCiI-1681567606699)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/5bf3b8f9-fa55-4a3f-a84c-b0818fcc856a.png)]
转载自 Vinyals 等人(2015 年)
该模型可以生成以自然语言完成的句子。 CNN 和 LSTM 的展开图如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-abLLc9wt-1681567606699)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/04f54858-0741-427c-9c36-4c627f70ba34.png)]
该图说明了 CNN 和 LSTM 架构(摘自 Vinyals 等人)
这是 LSTM 的展开视图。 此处显示了一组选择性的结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BwJxC1jG-1681567606700)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/bc22e56f-e384-499e-be63-4e036405ee7c.png)]
转载自 Vinyals 等人(2015 年)
在此过程中,CNN 将图像编码为特征,RNN 从中生成句子。
使用图像排名创建字幕
Ordonez 等人在论文中,提出了一种方法对图像进行排名,然后生成标题。 此过程的流程如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pq75basb-1681567606700)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/5b285990-9992-4773-833a-720904a28188.png)]
复制自 Ordonez 等人(2015)
从排名图像中提取的高级信息可用于生成文本。 下图显示,可用于排名的图像越多,结果将越好:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vTugRRYP-1681567606701)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/fd60d31e-2040-4634-8f2b-245e258740ea.png)]
复制自 Ordonez 等人(2015)
从图像检索字幕和从字幕检索图像
Chen 等人在论文中,提出了一种从文本中检索图像和从图像中检索文本的方法。 这是双向映射。 下图显示了一个用自然语言解释图像的人和另一个在视觉上思考它的人:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WoGpj4IB-1681567606701)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/226bae31-12c2-4ec6-aa6a-5f54db2f7d6e.png)]
转载自 Chen 等人(2015)
检索字幕可以通过以下方式通过潜在空间连接图像和文本的编码器来实现:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3HgiHaC7-1681567606701)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/f4ca10b9-38aa-46a9-9839-d7844a8401c1.png)]
转载自 Chen 等人(2015)
图像中的第一个模型是用于训练的完整模型。 如图中所示,视觉特征也可以用于生成句子,反之亦然。
密集字幕
Johnson 等人在论文中,提出了一种用于密集字幕的方法。 首先,让我们看一些结果,以了解任务:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ha79Rxtp-1681567606702)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/29744e79-12f1-49c0-a1bb-cd99c44a87d2.png)]
转载自 Johnson 等人
如您所见,为图像中的对象和动作生成了单独的标题; 由此得名; 密集字幕。 这是 Johnson 等人提出的架构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LgUYqaVm-1681567606702)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/b4d4bca7-5ea6-48a2-ad5c-c5063af9ebe5.png)]
转自 Johnson 等人
该架构实质上是 Faster-RCNN 和 LSTM 的组合。 产生该区域以产生对象检测结果,并且使用该区域的视觉特征来产生字幕。
使用 RNN 的字幕生成
Donahue 等人在论文中,提出了长期循环卷积网络(LRCN) 用于图像字幕生成的任务。 此模型的架构如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tMq9Hu9G-1681567606702)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/b0a60d74-0013-443b-b167-4cc77a74bfcb.png)]
转载自 Donahue 等人
图中显示了 CNN 和 LSTM 在整个时间上的权重,这使得该方法可扩展到任意长序列。
使用多模态度量空间
Mao 等人在论文中提出了一种使用多模态嵌入空间生成字幕的方法。 下图说明了这种方法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XMrQ2raW-1681567606703)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/11930a42-1972-4cbb-a1d7-417207484a6c.png)]
转自毛等人。
Kiros 等人在论文中提出了另一种生成字幕的多模态方法,该方法可以将图像和文本嵌入同一多模态空间。 下图说明了这种方法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AtnxS8sD-1681567606703)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/c7396971-eb91-40ba-8e8e-9ea6ed8eef77.png)]
复制自 Kiros 等人
两种多模式方法都给出了良好的结果。
使用注意力网络的字幕生成
Xu 等人在论文中,提出了一种使用注意力机制进行图像字幕生成的方法。 注意力机制对图像的某些区域比其他区域赋予更多权重。 注意还可以实现可视化,向我们展示模型生成下一个单词时所关注的位置。 建议的模型如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lZkYB4yZ-1681567606703)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/16daa7fa-b24c-4855-b677-fdb975865573.png)]
转载自徐等人。
首先,从图像中提取 CNN 特征。 然后,将关注的 RNN 应用于生成单词的图像。
知道什么时候看
Lu 等人提出了一种引起关注的方法,可提供出色的结果。 知道何时看待注意力捕获的区域会产生更好的结果。 流程如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TYjXUHIX-1681567606704)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/94576f82-a74c-4394-bc55-78c027c88424.png)]
摘自 Lu 等人
注意力机制如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yxWJGZDW-1681567606704)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/15065730-7c08-4996-9a4d-efd4eaa6b377.png)]
摘自 Lu 等人
结果重点突出的区域如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xn7uIAvc-1681567606704)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/aeb23046-5281-48a0-8f43-19852a14bc89.png)]
摘自 Lu 等人
生成字幕时注意力的释放在此处可视化:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aDnpPSzO-1681567606705)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/5dc70bd2-6e0f-4e12-a79b-564d36959461.png)]
摘自 Lu 等人
我们已经看到,用于生成字幕几种方法。 接下来,我们将看到一个实现。
实现基于注意力的图像字幕生成
让我们使用以下代码从 VGG 和 LSTM 模型定义 CNN:
vgg_model = tf.keras.applications.vgg16.VGG16(weights='imagenet', include_top=False, input_tensor=input_tensor, input_shape=input_shape) word_embedding = tf.keras.layers.Embedding( vocabulary_size, embedding_dimension, input_length=sequence_length) embbedding = word_embedding(previous_words) embbedding = tf.keras.layers.Activation('relu')(embbedding) embbedding = tf.keras.layers.Dropout(dropout_prob)(embbedding) cnn_features_flattened = tf.keras.layers.Reshape((height * height, shape))(cnn_features) net = tf.keras.layers.GlobalAveragePooling1D()(cnn_features_flattened) net = tf.keras.layers.Dense(embedding_dimension, activation='relu')(net) net = tf.keras.layers.Dropout(dropout_prob)(net) net = tf.keras.layers.RepeatVector(sequence_length)(net) net = tf.keras.layers.concatenate()([net, embbedding]) net = tf.keras.layers.Dropout(dropout_prob)(net)
现在,我们已经定义了 CNN,接下来使用以下代码定义关注层:
h_out_linear = tf.keras.layers.Convolution1D( depth, 1, activation='tanh', border_mode='same')(h) h_out_linear = tf.keras.layers.Dropout( dropout_prob)(h_out_linear) h_out_embed = tf.keras.layers.Convolution1D( embedding_dimension, 1, border_mode='same')(h_out_linear) z_h_embed = tf.keras.layers.TimeDistributed( tf.keras.layers.RepeatVector(num_vfeats))(h_out_embed) Vi = tf.keras.layers.Convolution1D( depth, 1, border_mode='same', activation='relu')(V) Vi = tf.keras.layers.Dropout(dropout_prob)(Vi) Vi_emb = tf.keras.layers.Convolution1D( embedding_dimension, 1, border_mode='same', activation='relu')(Vi) z_v_linear = tf.keras.layers.TimeDistributed( tf.keras.layers.RepeatVector(sequence_length))(Vi) z_v_embed = tf.keras.layers.TimeDistributed( tf.keras.layers.RepeatVector(sequence_length))(Vi_emb) z_v_linear = tf.keras.layers.Permute((2, 1, 3))(z_v_linear) z_v_embed = tf.keras.layers.Permute((2, 1, 3))(z_v_embed) fake_feat = tf.keras.layers.Convolution1D( depth, 1, activation='relu', border_mode='same')(s) fake_feat = tf.keras.layers.Dropout(dropout_prob)(fake_feat) fake_feat_embed = tf.keras.layers.Convolution1D( embedding_dimension, 1, border_mode='same')(fake_feat) z_s_linear = tf.keras.layers.Reshape((sequence_length, 1, depth))(fake_feat) z_s_embed = tf.keras.layers.Reshape( (sequence_length, 1, embedding_dimension))(fake_feat_embed) z_v_linear = tf.keras.layers.concatenate(axis=-2)([z_v_linear, z_s_linear]) z_v_embed = tf.keras.layers.concatenate(axis=-2)([z_v_embed, z_s_embed]) z = tf.keras.layers.Merge(mode='sum')([z_h_embed,z_v_embed]) z = tf.keras.layers.Dropout(dropout_prob)(z) z = tf.keras.layers.TimeDistributed( tf.keras.layers.Activation('tanh'))(z) attention= tf.keras.layers.TimeDistributed( tf.keras.layers.Convolution1D(1, 1, border_mode='same'))(z) attention = tf.keras.layers.Reshape((sequence_length, num_vfeats))(attention) attention = tf.keras.layers.TimeDistributed( tf.keras.layers.Activation('softmax'))(attention) attention = tf.keras.layers.TimeDistributed( tf.keras.layers.RepeatVector(depth))(attention) attention = tf.keras.layers.Permute((1,3,2))(attention) w_Vi = tf.keras.layers.Add()([attention,z_v_linear]) sumpool = tf.keras.layers.Lambda(lambda x: K.sum(x, axis=-2), output_shape=(depth,)) c_vec = tf.keras.layers.TimeDistributed(sumpool)(w_Vi) atten_out = tf.keras.layers.Merge(mode='sum')([h_out_linear,c_vec]) h = tf.keras.layers.TimeDistributed( tf.keras.layers.Dense(embedding_dimension,activation='tanh'))(atten_out) h = tf.keras.layers.Dropout(dropout_prob)(h) predictions = tf.keras.layers.TimeDistributed( tf.keras.layers.Dense(vocabulary_size, activation='softmax'))(h)
在前面的代码的帮助下,我们定义了一个深度学习模型,该模型将 CNN 特征与 RNN 结合在一起,并借助注意力机制。 目前,这是生成字幕的最佳方法。
总结
在本章中,我们已经了解了与图像标题相关的问题。 我们看到了一些涉及自然语言处理和各种word2vec
模型(例如GLOVE
)的技术。 我们了解了CNN2RNN
,度量学习和组合目标等几种算法。 后来,我们实现了一个结合了 CNN 和 LSTM 的模型。
在下一章中,我们就来了解生成模型。 我们将从头开始学习和实现样式算法,并介绍一些最佳模型。 我们还将介绍很酷的生成对抗网络(GAN)及其各种应用。
八、生成模型
生成模型已经成为计算机视觉中的重要应用。 与前几章讨论的应用根据图像进行预测不同,生成模型可以为特定目标创建图像。 在本章中,我们将了解:
- 生成模型的应用
- 风格迁移算法
- 训练超分辨率图像模型
- 生成模型的实现和训练
- 当前模型的缺点
在本章的最后,您将能够实现一些出色的应用来传递样式,并理解与生成模型相关的可能性和困难。
生成模型的应用
让我们从生成模型的可能应用开始本章。 应用是巨大的。 我们将看到其中一些应用,以了解动机和可能性。
艺术风格迁移
艺术风格迁移是将艺术风格迁移到任何图像的过程。 例如,可以使用一幅图像的艺术风格和另一幅图像的内容来创建图像。 Gatys 等人在此显示了一个结合了几种不同样式的图像示例。 图像 A 是应用了样式的照片,其结果显示在其他图像中:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Dvix6KmY-1681567606705)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/0e36da01-457e-4388-8cec-85994bf97af2.png)]
转载自盖蒂斯等。
此应用引起了公众的注意,并且市场上有几种提供此功能的移动应用。
预测视频中的下一帧
可以使用生成模型从合成视频集中预测未来的帧。 在下面由 Lotter 等人提出的图像中,左侧的图像是前一帧的模型,而右侧有两种与基本事实比较的算法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SFrN7BZu-1681567606705)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/af300a68-236d-43de-b849-f7fbad8dd6b8.png)]
复制自 Lotter 等人
生成模型生成的框架将是现实的。
图像超分辨率
超分辨率是从较小的图像创建高分辨率图像的过程。 传统上,插值用于创建更大的图像。 但是插值通过提供平滑效果而错过了高频细节。 经过训练的生成模型针对此超分辨率的特定目的而创建的图像具有出色的细节。 以下是 Ledig 等人提出的此类模型的示例。 左侧是通过 4 倍缩放生成的,看起来与右侧的原件没有区别:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GE7HVjmc-1681567606705)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/96bebdcb-ce44-486b-9b87-f3f48a39b789.png)]
转自 Ledig 等人
超分辨率对于在高质量的显示器或打印件上呈现低分辨率的图像很有用。 另一个应用可能是重建高质量的压缩图像。
交互式图像生成
生成模型可用于通过交互作用创建图像。 用户可以添加编辑内容,并且可以生成图像,以反映编辑内容,如 Zhu 等人建议的那样:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Yyj5RcEB-1681567606706)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/76957f5e-d2e5-4573-a6c7-3aa162253546.png)]
复制自 Zhu 等人
如图所示,图像是根据编辑的形状和颜色生成的。 底部的绿色笔触创建了草原,矩形创建了摩天大楼,依此类推。 图像将被生成并通过用户的进一步输入进行微调。 生成的图像还可以用于检索可以利用的最相似的真实图像。 提供交互式图像生成是一种直观搜索图像的全新方法。
图像到图像的转换
图像可用于生成具有特定目标的其他图像,因此此过程称为图像到图像的转换。 此处显示了此类翻译的一些示例,以及由 Isola 等人提出的相应标准:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aDpVLYHG-1681567606706)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/a289fe70-1518-455e-91f5-22ae6a0a1de4.png)]
复制自 Isola 等人
带有标签的图像可以转换为逼真的图像以用于创意目的。 黑白图像可以转换为彩色图像。 这样的翻译对于照片编辑应用,为旧电影着色,服装设计等非常有用。
文本到图像的生成
可以从文本描述中生成图像,其步骤类似于图像到图像的翻译。 以下是一些由 Reed 等人展示的自然文本描述生成的示例:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OLjw6G9c-1681567606706)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/5f66cccf-b9c9-4609-b224-06e95897407b.png)]
复制自 Reed 等人
当前,此模型仅适用于少数几个对象。 从文本生成图像还不够实际,无法在应用中使用。
修复
修复是填充图像中的间隙的过程,如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KcEnTvpl-1681567606706)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/c3919cea-ec35-4cff-b352-15975acdb6f9.jpg)]
资料来源:https://www.flickr.com/photos/littleredelf/4756059924/
左侧的图像是正常图像,右侧的图像是经过处理的图像。 从图像中可以看到,不需要的东西已从图片中删除。 修补对于从图像中删除不需要的对象以及填充扫描图稿的空间很有用。
融合
融合是将图像的一部分平滑地粘贴到另一个图像上而没有任何伪影的过程。 此处显示的图像表示一种图像放置在另一图像上的情况,给人留下不好的印象。 图像 b 和 c 代表传统的混合技术,例如修正的泊松方法和多样条方法。
最终图像或图像 d 显示了混合生成方法的结果,该方法比 Wu 等人的其他方法提供了更好的结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zdwtGaOm-1681567606707)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/85081b77-9fc2-436d-8205-13ffb13b374a.png)]
摘自 Wu 等人
混合对于照片编辑和电影行业中的特殊效果非常有用。
转换属性
可以使用生成模型来更改图像的属性。 兰珀(Lample)等人在此显示,可以修改人的人脸以反映不同的属性,例如性别,眼镜,年龄等:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-32yZo7W2-1681567606707)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/e700b8c7-e5a1-4914-8f0a-afded0129609.png)]
转载于 Lample 等人。
更改属性既可以用于创意应用,也可以用于娱乐,也可以用于生成更多具有变化的训练数据。
创建训练数据
生成模型可用于大规模生成训练,甚至可用于完善为训练而创建的合成图像。 这是使用 Wang 等人的生成模型为交通标志识别创建的合成图像:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GsTXrg8c-1681567606707)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/b4123a9e-ccd5-4c45-9348-0935c45e318f.png)]
转载自 Wang 等人
使用这些图像可以使分类更加准确。
创建新的动画角色
生成模型可用于创建具有各种条件的新动画角色,例如人脸表情,发型,服装等,如 Jin 等人所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ifiXJsCT-1681567606707)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/e92bfe6e-601b-49e4-a8df-72cb19f7c73e.png)]
转载自 Jin 等人
创建具有不同属性的新角色可以彻底改变动画产业。
照片的 3D 模型
我们可以使用生成模型从 2D 图像创建 3D 模型,如 Wu 等人所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MgEdQJIj-1681567606708)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/08cdcdde-c328-4fec-84bc-42c38bd0a7d7.png)]
摘自 Wu 等人
从图像创建 3D 模型对于机器人技术,增强现实和动画行业很有用。 在以下各节中,我们将学习它们背后的算法。 在下一节中,我们将实现神经艺术风格的转换。
神经艺术风格迁移
我们将要实现的第一个应用是神经艺术风格转换。 在这里,我们将梵高艺术的风格迁移到图像上。 图像可以视为样式和内容的组合。 艺术风格转换技术将图像转换为看起来像具有特定绘画风格的绘画。 我们将看到如何编写这个想法。 loss
函数将比较生成的图像与照片内容和绘画风格。 因此,针对图像像素而不是针对网络权重执行优化。 通过将照片的内容与生成的图像相比较,然后是绘画风格和生成的图像,可以计算出两个值。
内容损失
由于像素不是一个好的选择,我们将使用各个层的 CNN 特征,因为它们可以更好地表示内容。 如第 3 章,“图像检索”, 所示,初始层具有高频,例如边缘,拐角和纹理。 后面的层代表对象,因此更适合内容。 后者可以比像素更好地将对象与对象进行比较。 但是为此,我们需要先使用以下代码导入所需的库:
import numpy as np from PIL import Image from scipy.optimize import fmin_l_bfgs_b from scipy.misc import imsave from vgg16_avg import VGG16_Avg from keras import metrics from keras.models import Model from keras import backend as K
现在,让我们使用以下命令加载所需的图像:
content_image = Image.open(work_dir + 'bird_orig.png')
我们将在此实例中使用以下图片:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ilzroxcz-1681567606708)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/c5f130bb-71bd-45ba-8bea-d224f13820de.png)]
当我们使用 VGG 架构提取特征时,必须从所有图像中减去所有ImageNet
图像的均值,如以下代码所示:
imagenet_mean = np.array([123.68, 116.779, 103.939], dtype=np.float32) def subtract_imagenet_mean(image): return (image - imagenet_mean)[:, :, :, ::-1]
请注意,通道是不同的。 preprocess
函数拍摄生成的图像并减去平均值,然后反转通道。 deprocess
函数由于进行了预处理步骤而使效果相反,如以下代码所示:
def add_imagenet_mean(image, s): return np.clip(image.reshape(s)[:, :, :, ::-1] + imagenet_mean, 0, 255)
首先,我们将了解如何使用其他图像中的内容创建图像。 这是根据随机噪声创建图像的过程。 此处使用的内容是某层中激活的总和。 我们将使随机噪声和图像之间的内容损失最小化,这被称为内容损失。 该损耗类似于逐像素损耗,但应用于层激活,因此将捕获内容而没有噪声。 任何 CNN 架构都可以用来转发内容图像和随机噪声。 比较这两个输出的激活,进行激活并计算均方误差。
冻结 CNN 权重时,将更新随机图像的像素。 在这种情况下,我们将冻结 VGG 网络。 现在,可以加载 VGG 模型。 生成图像对子采样技术(例如最大池化)非常敏感。 无法从最大池化中取回像素值。 因此,平均池化比最大池化更平滑。 使用平均池化转换 VGG 模型的特征用于加载模型,如下所示:
vgg_model = VGG16_Avg(include_top=False)
请注意,即使合并类型已更改,此模型的权重也与原始模型相同。 ResNet 和 Inception 模型不适合此操作,因为它们无法提供各种抽象。 我们将从模型冻结的最后一个 VGG 模型的卷积层block_conv1
中获取激活。 这是 VGG 的第三层,具有广阔的接收范围。 这里给出了相同的代码供您参考:
content_layer = vgg_model.get_layer('block5_conv1').output
现在,使用截断的 VGG 创建新模型,直到具有良好特征的层。 因此,该图像现在可以加载,并且可以用于执行前向推断,以获得实际激活的层。 使用以下代码创建 TensorFlow 变量以捕获激活:
content_model = Model(vgg_model.input, content_layer) content_image_array = subtract_imagenet_mean(np.expand_dims(np.array(content_image), 0)) content_image_shape = content_image_array.shape target = K.variable(content_model.predict(content_image_array))
让我们定义一个评估器类,以计算图像的损耗和梯度。 下列类在迭代的任意点返回损耗和梯度值:
class ConvexOptimiser(object): def __init__(self, cost_function, tensor_shape): self.cost_function = cost_function self.tensor_shape = tensor_shape self.gradient_values = None def loss(self, point): loss_value, self.gradient_values = self.cost_function([point.reshape(self.tensor_shape)]) return loss_value.astype(np.float64) def gradients(self, point): return self.gradient_values.flatten().astype(np.float64)
损失函数可以定义为特定卷积层的激活值之间的均方误差。 损失将在生成的图像和原始内容照片的层之间进行计算,如下所示:
mse_loss = metrics.mean_squared_error(content_layer, target)
可以通过考虑模型的输入来计算损耗的梯度,如下所示:
grads = K.gradients(mse_loss, vgg_model.input)
函数的输入是模型的输入,输出将是损耗和梯度值的数组,如下所示:
cost_function = K.function([vgg_model.input], [mse_loss]+grads)
此函数是确定性的要优化的,因此不需要 SGD:
optimiser = ConvexOptimiser(cost_function, content_image_shape)
可以使用简单的优化程序来优化此函数,因为它是凸的,因此是确定性的。 我们还可以在迭代的每个步骤中保存图像。 我们将以可访问梯度的方式进行定义,就像我们使用 scikit-learn 的优化程序进行最终优化一样。 注意,该损失函数是凸的,因此,简单的优化器足以满足计算要求。 可以使用以下代码定义优化器:
def optimise(optimiser, iterations, point, tensor_shape, file_name): for i in range(iterations): point, min_val, info = fmin_l_bfgs_b(optimiser.loss, point.flatten(), fprime=optimiser.gradients, maxfun=20) point = np.clip(point, -127, 127) print('Loss:', min_val) imsave(work_dir + 'gen_'+file_name+'_{i}.png', add_imagenet_mean(point.copy(), tensor_shape)[0]) return point
优化器采用loss
函数,点和梯度,然后返回更新。 需要使用以下代码生成随机图像,以使内容损失最小化:
def generate_rand_img(shape): return np.random.uniform(-2.5, 2.5, shape)/1 generated_image = generate_rand_img(content_image_shape)
这是创建的随机图像:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Dm2O9pgG-1681567606708)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/bb7d0bd4-7167-43d7-938f-163330b4bfa5.png)]
该优化可以运行 10 次迭代以查看结果,如下所示:
iterations = 10 generated_image = optimise(optimiser, iterations, generated_image, content_image_shape, 'content')
如果一切顺利,那么在迭代过程中,损失应如下图所示:
Current loss value: 73.2010421753 Current loss value: 22.7840042114 Current loss value: 12.6585302353 Current loss value: 8.53817081451 Current loss value: 6.64649534225 Current loss value: 5.56395864487 Current loss value: 4.83072710037 Current loss value: 4.32800722122 Current loss value: 3.94804215431 Current loss value: 3.66387653351
这是生成的图像,现在,它看起来几乎像只鸟。 可以运行优化以进行进一步的迭代以完成此操作:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cBcelz1F-1681567606709)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/6f9d1c9b-34ce-4c4b-ab82-4a67f76fb906.png)]
优化器拍摄图像并更新像素,以使内容相同。 虽然效果较差,但可以在一定程度上重现图像内容。 通过迭代获得的所有图像都很好地说明了图像的生成方式。 此过程不涉及批量。 在下一节中,我们将看到如何以绘画风格创建图像。
使用 Gram 矩阵的样式损失
创建具有原始图像内容的图像后,我们将看到如何仅使用样式创建图像。 样式可以认为是图像颜色和纹理的混合。 为此,我们将定义样式损失。 首先,我们将覆盖图像并将其转换为数组,如以下代码所示:
style_image = Image.open(work_dir + 'starry_night.png') style_image = style_image.resize(np.divide(style_image.size, 3.5).astype('int32'))
这是我们加载的样式图像:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ej2Hc56M-1681567606709)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/1f67b6de-e5e1-461d-b515-a96915351fd5.png)]
现在,我们将使用以下代码通过更改通道对该图像进行预处理:
style_image_array = subtract_imagenet_mean(np.expand_dims(style_image, 0)[:, :, :, :3]) style_image_shape = style_image_array.shape
为此,我们将考虑以下几层,就像我们在以下代码中所做的那样:
model = VGG16_Avg(include_top=False, input_shape=shp[1:]) outputs = {l.name: l.output for l in model.layers}
现在,我们将使用以下代码将多层作为前四个块的数组输出:
layers = [outputs['block{}_conv1'.format(o)] for o in range(1,3)]
现在创建一个新模型,该模型可以使用以下代码输出所有这些层并分配目标变量:
layers_model = Model(model.input, layers) targs = [K.variable(o) for o in layers_model.predict(style_arr)]
使用 Gram 矩阵计算样式损失。 革兰氏矩阵是矩阵及其转置的乘积。 激活值可以简单地转置和相乘。 然后将此矩阵用于计算样式和随机图像之间的误差。 Gram 矩阵会丢失位置信息,但会保留纹理信息。 我们将使用以下代码定义 Gram 矩阵:
def grammian_matrix(matrix): flattened_matrix = K.batch_flatten(K.permute_dimensions(matrix, (2, 0, 1))) matrix_transpose_dot = K.dot(flattened_matrix, K.transpose(flattened_matrix)) element_count = matrix.get_shape().num_elements() return matrix_transpose_dot / element_count
您可能现在已经知道,它是一对列之间的相关性的度量。 高度和宽度尺寸被展平。 这不包括任何本地信息,因为坐标信息被忽略。 样式损失计算输入图像的 Gram 矩阵与目标之间的均方误差,如以下代码所示:
def style_mse_loss(x, y): return metrics.mse(grammian_matrix(x), grammian_matrix(y))
现在,我们使用以下代码通过汇总各层的所有激活来计算损失:
style_loss = sum(style_mse_loss(l1[0], l2[0]) for l1, l2 in zip(style_features, style_targets)) grads = K.gradients(style_loss, vgg_model.input) style_fn = K.function([vgg_model.input], [style_loss]+grads) optimiser = ConvexOptimiser(style_fn, style_image_shape)
然后,通过创建随机图像,以与以前相同的方式解决它。 但是这次,我们还将应用高斯过滤器,如以下代码所示:
generated_image = generate_rand_img(style_image_shape)
生成的随机图像如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KD2ZUk68-1681567606709)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/6bf68978-98b1-449a-a813-639c4bda8b31.png)]
优化可以运行 10 次迭代以查看结果,如下所示:
generated_image = optimise(optimiser, iterations, generated_image, style_image_shape)
如果一切顺利,求解器应打印类似于以下的损耗值:
Current loss value: 5462.45556641 Current loss value: 189.738555908 Current loss value: 82.4192581177 Current loss value: 55.6530838013 Current loss value: 37.215713501 Current loss value: 24.4533748627 Current loss value: 15.5914745331 Current loss value: 10.9425945282 Current loss value: 7.66888141632 Current loss value: 5.84042310715
这是生成的图像:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xWqYYk2B-1681567606709)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/097eec9d-b9f7-42b3-81db-c4d68781126e.png)]
在这里,我们从随机噪声中创建了具有特定绘画风格的图像,而没有任何位置信息。 在下一节中,我们将看到如何结合使用-内容损失和样式损失。
风格迁移
现在,我们知道了如何重建图像,以及如何构建捕获原始图像样式的图像。 显而易见的想法可能是通过加权并添加两个loss
函数来将这两种方法结合起来,如以下代码所示:
w,h = style.size src = img_arr[:,:h,:w]
和以前一样,我们将获取一系列层输出以计算样式损失。 但是,我们仍然只需要一层输出来计算内容损失。 我们如何知道要抓哪一层? 如前所述,层越低,内容重构将越精确。 在将内容重建与样式相结合时,我们可以预期,对内容进行更宽松的重建将为样式带来更大的影响空间(例如:灵感)。 此外,即使没有相同的细节,后面的层也可以确保图像看起来像相同的主题。 以下代码用于此过程:
style_layers = [outputs['block{}_conv2'.format(o)] for o in range(1,6)] content_name = 'block4_conv2' content_layer = outputs[content_name]
现在,使用以下代码使用所需的输出层创建一个单独的样式模型:
style_model = Model(model.input, style_layers) style_targs = [K.variable(o) for o in style_model.predict(style_arr)]
我们还将使用以下代码为具有内容层的内容创建另一个模型:
content_model = Model(model.input, content_layer) content_targ = K.variable(content_model.predict(src))
现在,两种方法的合并就像合并它们各自的损失函数一样简单。 请注意,与我们之前的函数相反,此函数将产生三种不同类型的输出:
- 一个用于原始图像
- 一个用于模仿我们风格的图片
- 一个用于训练像素的随机图像
我们调整重建方式的一种方法是更改内容损失系数,此处为 1/10。 如果增加分母,则样式将对图像产生较大影响,而如果太大,则非结构化样式将掩盖图像的原始内容。 同样,如果它太小,则图像将没有足够的样式。 我们将在此过程中使用以下代码:
style_wgts = [0.05,0.2,0.2,0.25,0.3]
loss
函数同时包含样式和内容层,如下所示:
loss = sum(style_loss(l1[0], l2[0])*w for l1,l2,w in zip(style_layers, style_targs, style_wgts)) loss += metrics.mse(content_layer, content_targ)/10 grads = K.gradients(loss, model.input) transfer_fn = K.function([model.input], [loss]+grads) evaluator = Evaluator(transfer_fn, shp)
我们将使用以下代码像以前一样运行求解器 10 次迭代:
iterations=10 x = rand_img(shp) x = solve_image(evaluator, iterations, x)
损耗值应按如下所示打印:
Current loss value: 2557.953125 Current loss value: 732.533630371 Current loss value: 488.321166992 Current loss value: 385.827178955 Current loss value: 330.915924072 Current loss value: 293.238189697 Current loss value: 262.066864014 Current loss value: 239.34185791 Current loss value: 218.086700439 Current loss value: 203.045211792
这些结果是惊人的。 他们每个人都以艺术家的风格来完成原始图像的复制工作。 生成的图像如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-F5f3I1Ay-1681567606710)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/fe01f255-d682-4f67-8612-b692af48dd95.png)]
现在,我们将结束样式转换部分。 此操作确实很慢,但可以处理任何图像。 在下一节中,我们将看到如何使用类似的想法来创建超分辨率网络。 有几种方法可以改善这种情况,例如:
- 将高斯滤镜添加到随机图像
- 为层添加不同的权重
- 可以使用不同的层和权重来满足
- 初始化图像而不是随机图像
- 颜色可以保存
- 掩码可以用于指定所需的内容
- 任何草图都可以转换为绘画
- 绘制草图并创建图像
通过训练 CNN 输出任何图像,都可以将其转换为艺术风格。
生成对抗网络
生成对抗网络(GAN)由 Ian Goodfellow 于 2014 年发明。这是一种无监督算法,其中两个神经网络被训练为判别器和生成器。 , 同时。 该技术可以根据随机噪声生成图像,判别器可以评估是否为原始图像。 经过进一步训练后,生成器网络可以生成逼真的图像。 生成器网络通常是反卷积神经网络,而判别器是卷积神经网络。
理解这一点的一个很好的类比是,将生成器看作是伪造钱币的人,而将判别器看作是确定钱币是否为假币的警察。 生成器会根据警察的反馈不断提高伪钞的质量,直到警察无法区分伪钞和伪钞。 现在,让我们从实现开始。
原始 GAN
原始 GAN 称为原始 GAN 。 在构建模型之前,让我们定义一些对本章其余部分有用的层。 以下是convolutional_layers
,其中添加了泄漏激活和正则化:
def convolution_layer(input_layer, filters, kernel_size=[4, 4], activation=tf.nn.leaky_relu): layer = tf.layers.conv2d( inputs=input_layer, filters=filters, kernel_size=kernel_size, activation=activation, kernel_regularizer=tf.nn.l2_loss, bias_regularizer=tf.nn.l2_loss, ) add_variable_summary(layer, 'convolution') return layer
接下来,我们将使用以下代码定义与带有正则化的convolution_layer
相反的transpose_convolution_layer
:
def transpose_convolution_layer(input_layer, filters, kernel_size=[4, 4], activation=tf.nn.relu, strides=2): layer = tf.layers.conv2d_transpose( inputs=input_layer, filters=filters, kernel_size=kernel_size, activation=activation, strides=strides, kernel_regularizer=tf.nn.l2_loss, bias_regularizer=tf.nn.l2_loss, ) add_variable_summary(layer, 'convolution') return layer
接下来,我们将使用以下代码定义一个具有非线性激活的密集层:
def dense_layer(input_layer, units, activation=tf.nn.relu): layer = tf.layers.dense( inputs=input_layer, units=units, activation=activation ) add_variable_summary(layer, 'dense') return layer
现在,我们将定义一个生成器,该生成器将噪声作为输入并变为图像。 生成器由几个全连接层组成,然后是转置卷积层以对噪声进行上采样。 最后,提出了卷积层以使噪声成为单个通道。 每层之间都有批量归一化层,以使梯度平滑流动。 我们将使用以下代码定义生成器:
def get_generator(input_noise, is_training=True): generator = dense_layer(input_noise, 1024) generator = tf.layers.batch_normalization(generator, training=is_training) generator = dense_layer(generator, 7 * 7 * 256) generator = tf.layers.batch_normalization(generator, training=is_training) generator = tf.reshape(generator, [-1, 7, 7, 256]) generator = transpose_convolution_layer(generator, 64) generator = tf.layers.batch_normalization(generator, training=is_training) generator = transpose_convolution_layer(generator, 32) generator = tf.layers.batch_normalization(generator, training=is_training) generator = convolution_layer(generator, 3) generator = convolution_layer(generator, 1, activation=tf.nn.tanh) return generator
现在,我们将定义 GAN 的判别器部分,该部分可拍摄图像并尝试区分假冒商品和真实形象。 判别器是一个规则的卷积网络,上面有几个convolutional_layers
,其后是致密层。 批归一化层位于层之间。 我们将使用以下代码来定义鉴别符:
def get_discriminator(image, is_training=True): x_input_reshape = tf.reshape(image, [-1, 28, 28, 1], name='input_reshape') discriminator = convolution_layer(x_input_reshape, 64) discriminator = convolution_layer(discriminator, 128) discriminator = tf.layers.flatten(discriminator) discriminator = dense_layer(discriminator, 1024) discriminator = tf.layers.batch_normalization(discriminator, training=is_training) discriminator = dense_layer(discriminator, 2) return discriminator
创建判别器后,我们将使用以下代码创建一个噪声向量,该噪声向量将作为生成器的输入:
input_noise = tf.random_normal([batch_size, input_dimension])
可以使用 TensorFlow 中的tf.contrib.gan
模块创建 GAN 模型。 它采用了生成器和判别器方法及其相应的输入,如下所示:
gan = tf.contrib.gan.gan_model( get_generator, get_discriminator, real_images, input_noise)
现在,可以使用以下代码从gan_train
方法开始训练,该方法将gan_train_ops
方法带给生成器和判别器以损失,并对优化器进行优化,并使用以下代码:
tf.contrib.gan.gan_train( tf.contrib.gan.gan_train_ops( gan, tf.contrib.gan.gan_loss(gan), tf.train.AdamOptimizer(0.001), tf.train.AdamOptimizer(0.0001)))
通过运行此命令,将创建可从随机向量输出图像的 GAN 模型。 生成的图像不受限制,可以来自任何标签。 在下一节中,我们将使用条件 GAN 生成所需的输出。
条件 GAN
有条件的 GAN 生成带有所需标签的图像。 例如,我们可以要求模型生成数字 8,而模型将生成数字 8。为此,需要标签以及使用模型训练的噪声,如下所示:
gan = tf.contrib.gan.gan_model( get_generator, get_discriminator, real_images, (input_noise, labels))
其余的训练与原始 GAN 相似。 接下来,我们将使用 GAN 压缩图像。
对抗损失
对抗性损失是来自生成器的损失。 该损失可以与伪图像和真实图像之间的逐像素损失相结合,以形成组合的对抗性损失。 GAN 模型必须随real_images
一起提供给生成器和判别器,如下所示:
gan = tf.contrib.gan.gan_model( get_autoencoder, get_discriminator, real_images, real_images)
生成器是一个自编码器。 可以在第 3 章,“图像检索”中找到该实现。 此后,我们将使用以下代码定义损失:
loss = tf.contrib.gan.gan_loss( gan, gradient_penalty=1.0) l1_pixel_loss = tf.norm(gan.real_data - gan.generated_data, ord=1) loss = tf.contrib.gan.losses.combine_adversarial_loss( loss, gan, l1_pixel_loss, weight_factor=1)
GAN 损失的梯度是不利的。 然后,计算逐像素损失并将其添加到损失的损失中。 训练此模型将创建一个功能强大的自编码器,可用于图像压缩。
图片转换
正如我们在应用部分中所了解的,可以将一个图像转换为另一个图像。 输入图像被提供给判别器,而目标图像被提供给生成器,同时创建 GAN 模型,如下所示:
gan = tf.contrib.gan.gan_model( get_generator, get_discriminator, real_images, input_images)
除像素级损失外,最小二乘损失也用于训练模型。 可以使用以下代码进行计算:
loss = tf.contrib.gan.gan_loss( gan, tf.contrib.gan.losses.least_squares_generator_loss, tf.contrib.gan.losses.least_squares_discriminator_loss) l1_loss = tf.norm(gan.real_data - gan.generated_data, ord=1) gan_loss = tf.contrib.gan.losses.combine_adversarial_loss( loss, gan, l1_loss, weight_factor=1)
使用此技术,可以将一个图像转换为另一个图像。
InfoGAN
InfoGAN 无需任何明确的监督训练即可生成所需标签的图像。 infogan_model
接受非结构化和结构化的输入,如以下代码所示:
info_gan = tf.contrib.gan.infogan_model( get_generator, get_discriminator, real_images, unstructured_input, structured_input) loss = tf.contrib.gan.gan_loss( info_gan, gradient_penalty_weight=1, gradient_penalty_epsilon=1e-10, mutual_information_penalty_weight=1)
由于训练不稳定,因此将损失定义为罚款。 增加罚分可以在训练过程中提供更大的稳定性。
GAN 的缺点
GAN 生成的图像具有一些缺点,例如计数,透视图和全局结构。 当前正在广泛研究以改进模型。
视觉对话模型
视觉对话模型(VDM)可以基于图像进行聊天。 VDM 应用了计算机视觉,自然语言处理(NLP)和聊天机器人的技术。 它发现了主要的应用,例如向盲人解释图像,向医生解释医学扫描,虚拟伴侣等。 接下来,我们将看到解决这一难题的算法。
VDM 算法
Lu 等人提出了此处讨论的算法。 Lu 等人提出了基于 GAN 的 VDM。 生成器生成答案,判别器对这些答案进行排名。 以下是该过程的示意图:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RiBygKFW-1681567606710)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/d64d01f5-a996-4063-aac0-a64fec3d0e15.png)]
基于 GAN 技术的 VDM 架构[摘自 Lu 等人]
聊天历史,当前问题和图像将作为输入提供给生成器。 接下来,我们将看到生成器如何工作。
生成器
生成器具有编码器和解码器。 编码器将图像,问题和历史记录作为输入。 编码器首先关注 LSTM 的历史记录,并关注图像的输出。 流程如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-esRzbHjp-1681567606710)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/f1aed34d-d664-451a-a18e-50148eb5990b.png)]
转载自 Lu 等人
整个历史记录都可用,并且 LSTM 记录了聊天的历史记录。 输出伴随有产生嵌入的图像。 编码器生成的嵌入被解码器用来创建答案。 解码器由 RNN 制成。 编码器和解码器一起形成生成器,生成可能的答案。 接下来,我们将了解判别器的工作原理。
判别器
判别器从生成器获取生成的序列并对其进行排序。 排名是通过对 n 对损失学习的嵌入完成的。 n 对损失类似于三元组损失,不同之处在于使用几对正负对进行比较。 这是该模型产生的一些结果。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fkGtyoDC-1681567606711)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/981124a1-6d73-4233-a453-f44e7861a98e.png)]
转载自 Lu 等人
结果是合理的,并且比简单的判别器产生的结果更好。
总结
在本章中,我们了解了生成模型和大量应用。 我们实现它们是为了在保留内容的同时将样式从一种转换为另一种。 我们看到了 GAN 背后的直觉和经过训练的模型可以做到这一点。 最后,我们了解了视觉对话系统。
在下一章中,我们将学习用于视频分析的深度学习方法。 我们将看到如何通过摄像机,文件等访问视频内容。 我们将通过在帧级别和整个视频上应用分类来实现视频分类。 稍后,我们将看到如何跟踪视频中的对象。
九、视频分类
在本章中,我们将看到如何训练视频数据的深度学习模型。 我们将开始按帧对视频进行分类。 然后,我们将使用时间信息以获得更好的准确率。 稍后,我们将图像的应用扩展到视频,包括姿势估计,字幕和生成视频。
在本章中,我们将涵盖的以下主题:
- 视频分类的数据集和算法
- 将视频分成帧并分类
- 在单个框架级别上训练视觉特征模型
- 了解 3D 卷积及其在视频中的使用
- 在视频上合并运动向量
- 将时间信息用于目标跟踪
- 人体姿势估计和视频字幕等应用
了解视频和分类
视频不过是一系列图像。 视频沿时间方向为图像带来了新的维度。 图像的空间特征和视频的时间特征可以放在一起,比仅图像提供更好的结果。 额外的维度还导致大量空间,因此增加了训练和推理的复杂性。 用于处理视频的计算需求非常高。 视频还改变了深度学习模型的架构,因为我们必须考虑时间特征。
视频分类是用类别标记视频的任务。 类别可以在帧级别,也可以在整个视频中。 视频中可能有执行的动作或任务。 因此,视频分类可以标记视频中存在的对象或标记视频中发生的动作。 在下一部分中,我们将看到用于视频分类任务的可用数据集。
探索视频分类数据集
视频分类是视频数据研究的主要问题。 拍摄了几个视频,并标记了与数据相关的各种对象或动作。 数据集根据大小,质量和标签类型而有所不同。 有些甚至包括多个视频标签。 这些视频通常很短。 长视频可能会执行各种操作,因此可以在分别对剪切的视频片段或摘要进行分类之前在时间上进行分割。 接下来,我们将考虑一些特定数据集的细节。
UCF101
佛罗里达中部大学(UCF101)是用于动作识别的数据集。 这些视频是在 YouTube 上收集的,由逼真的动作组成。 此数据集中有 101 个操作类别。 还有另一个名为 UCF50 的数据集,它具有 50 个类别。 整个动作中该数据集中有 13,320 个视频。 这些视频具有背景,比例,姿势,遮挡和照明条件的多种变化。 动作类别分为 25 个,它们具有相似的变化,例如背景,姿势,比例,视点,照明等。
动作和每个动作的视频数显示如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZAvEJWHK-1681567606711)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/fa184ce6-4c4f-4e4e-9eb0-46906bd0b009.png)]
所有 101 个动作都分为五种类型的动作,如下所示:人与对象的交互,身体动作,人与人的交互,演奏乐器和运动。 数据集和标注可从这里下载。
接下来,我们将了解 YouTube-8M 数据集。
YouTube-8M
YouTube-8M 数据集用于视频分类问题。 数据集包含带有标签和视觉特征的视频 URL。 以下是有关数据集的一些统计信息:
- 视频 URL 的数量:700 万
- 影片剪辑的时长:450,000
- 类标签的数量:4,716
- 每个视频的平均标签数:3.4
以下是各种类型的数据集摘要:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-69ztyZV6-1681567606711)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/53ee0eb1-cef3-4906-84ef-e972b7792a36.png)]
前面的图像可以让您一眼看出数据集中可用的标签类型。 视频数据很大,因此视觉特征被计算并随数据集一起提供。 可以通过以下链接访问数据集。
其他数据集
还有更多的数据集可用于视频分类问题。 以下是更多数据集的详细信息:
- Sports-1M:拥有 1,133,158 个具有 487 个类别的视频。 标注是自动完成的。 数据集可以从以下位置下载。
- UCF-11(佛罗里达大学-11):拥有 1,600 部视频,包含 11 动作。 视频的速度为 29.97 fps(每秒帧数)。 数据集可以与
UCF101
一起下载。 - HMDB-51(人体运动数据库-51):包含 5,100 个具有 51 个动作的视频。 数据集在这里。
- Hollywood2:拥有 12 个动作的 1,707 个视频。 数据集在这里。
我们已经看到了可用于视频分类任务的数据集,以及描述和访问链接。 接下来,我们将看到如何加载视频并将其拆分为帧以进行进一步处理。
将视频分成帧
视频可以转换为帧并保存在目录中以备将来使用。 分成帧可以通过在训练过程之前对视频进行解压缩来帮助我们节省时间。 首先,让我们看一下将视频转换为帧的代码片段:
import cv2 video_handle = cv2.VideoCapture(video_path) frame_no = 0 while True: eof, frame = video_handle.read() if not eof: break cv2.imwrite("frame%d.jpg" % frame_no, frame) frame_no += 1
使用此代码段,所有前面的数据集都可以转换为帧。 请注意,这将需要大量的硬盘空间。
视频分类方法
视频必须针对几种应用进行分类。 由于视频中包含大量数据,因此还必须考虑训练和推理计算。 所有视频分类方法均受图像分类算法启发。 VGG,Inception 等标准架构用于帧级别的特征计算,然后进行进一步处理。 诸如 CNN,注意,先前章节中学习的和 LSTM 之类的概念将在此处有用。 直观地,以下方法可用于视频分类:
- 提取帧并使用在第 2 章,“图像分类”中学习的模型,以帧为基础进行分类。
- 提取在第 3 章,“图像检索”中学习的图像特征,并且可以按照第 7 章,“图像标题”中的描述,使用这些特征训练 RNN。
- 在整个视频上训练 3D 卷积网络。 3D 卷积是 2D 卷积的扩展; 我们将在以下各节中详细了解 3D 卷积的工作原理。
- 使用视频的光流可以进一步提高精度。 光流是对象运动的模式,我们将在接下来的部分中详细介绍。
我们将看到几种算法,它们在各种计算复杂性上都具有良好的准确率。 可以通过将数据集转换为帧并将其子采样为相同的长度来准备它。 一些预处理会有所帮助,例如减去 Imagenet 的均值。
为视频分类融合并行 CNN
就帧而言,由于图像的下采样,视频的预测可能不会产生良好的结果,从而丢失了精细的细节。 使用高分辨率的 CNN 将增加推理时间。 因此,Karpathy 等人建议融合两个流,它们并行运行视频分类。 进行逐帧预测有两个问题,即:
- 由于较大的 CNN 架构,预测可能需要很长时间
- 独立的预测会沿时间维度丢失信息
使用更少的参数和两个并行运行的较小编码器可以简化架构。 视频同时通过两个 CNN 编码器传递。 一个编码器需要较低的分辨率并要处理高分辨率。 编码器具有交替的卷积,规范化和合并层。 两个编码器的最后一层通过全连接层连接。 另一个编码器具有相同的大小,但仅进行中心裁剪,如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L8065AVH-1681567606711)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/1f34b3d0-4e01-4ed2-a5fa-da513c7a1155.png)]
转载自 Karpathy 等人
帧的并行处理通过对视频进行下采样来加快运行时间。 CNN 架构的参数减半,同时保持相同的精度。 这两个流称为中央凹和上下文。 以下代码段显示了流:
high_resolution_input = tf.placeholder(tf.float32, shape=[None, input_size]) low_resolution_input = tf.placeholder(tf.float32, shape=[None, input_size]) y_input = tf.placeholder(tf.float32, shape=[None, no_classes]) high_resolution_cnn = get_model(high_resolution_input) low_resolution_cnn = get_model(low_resolution_input) dense_layer_1 = tf.concat([high_resolution_cnn, low_resolution_cnn], 1) dense_layer_bottleneck = dense_layer(dense_layer_1, 1024) logits = dense_layer(dense_layer_bottleneck, no_classes)
下图显示了跨时间维度进行处理的帧:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KwqFvPne-1681567606711)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/d2211390-ff79-43d4-9b7e-842c29b2b66c.png)]
转自 Karpathy 等人
可以在不同的时间观看视频,而不是通过固定大小的剪辑。 在前面的图像中介绍了连接时间信息的三种方式。 后期融合需要更长的时间框架,而早期融合则需要几个帧。 慢速融合将后期融合和早期融合结合在一起,可获得良好效果。 该模型在Sports1M
数据集上进行了训练,该数据集具有 487 个类别,并达到了 50% 的准确率。 将同一模型应用于UCF101
时,可达到 60% 的精度。
长时间视频的分类
融合方法适用于短视频片段。 分类较长的视频很困难,因为必须计算和记住很多帧。 Ng 等人提出了两种对较长视频进行分类的方法:
- 第一种方法是在时间上合并卷积特征。 最大池用作函数
aggregation
方法。 - 第二种方法是使用 LSTM 连接处理各种可变长度视频的卷积特征。
下图显示了这两种方法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2fi1NXt7-1681567606712)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/1fc508d4-5f43-4908-aa11-13d9e0a2b336.png)]
摘自 Ng 等人
可以提取 CNN 特征并将其馈送到小型 LSTM 网络,如以下代码所示:
net = tf.keras.models.Sequential() net.add(tf.keras.layers.LSTM(2048, return_sequences=False, input_shape=input_shape, dropout=0.5)) net.add(tf.keras.layers.Dense(512, activation='relu')) net.add(tf.keras.layers.Dropout(0.5)) net.add(tf.keras.layers.Dense(no_classes, activation='softmax'))
添加 LSTM 进行特征池化可提供更好的表现。 特征以各种方式合并,如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yHSxVrC4-1681567606712)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/72c1430c-6604-4c17-abe6-133fe2d4daa9.png)]
摘自 Ng 等人
如图所示,卷积特征可以几种不同的方式聚合。 池在全连接层之前完成。 该方法在Sports1M
数据集和UCF101
数据集中的准确率分别为 73.1% 和 88.6%。 下图显示了 LSTM 方法:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MEcS08Qm-1681567606712)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/bc029064-479f-4788-8aec-077b933fe468.png)]
摘自 Ng 等人
该模型的计算量很高,因为使用了多个 LSTM。
为动作识别流式连接两个 CNN
视频中对象的运动具有有关视频中执行的动作的非常好的信息。 对象的运动可以通过光流来量化。 Simonyan 和 Zisserman 提出了一种用于动作识别的方法,该方法使用来自图像和光流的两个流。
光流通过量化观察者与场景之间的相对运动来测量运动。 可以在这里找到有关光流的详细讲座。 通过运行以下命令可以获得光流:
p1, st, err = cv2.calcOpticalFlowPyrLK(old_gray, frame_gray, p0, None, **lk_params)
一个流采用单个帧并使用常规 CNN 预测动作。 另一个流获取多个帧并计算光流。 光流通过 CNN 进行预测。 下图显示了这两个预测:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2oGitwVS-1681567606713)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/ef8e4e07-e018-48ba-b82d-53613dddb8e8.png)]
转载自 Simonyan 和 Zisserman
两种预测都可以与最终预测结合。
使用 3D 卷积的时间学习
可以使用 3D 卷积对视频进行分类。 3D 卷积运算将体积作为输入并输出,而 2D 卷积可以将 2D 或体积输出并输出 2D 图像。 区别如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zoqbp6o5-1681567606713)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/0e8faa5c-11ae-4631-b717-bcab34d2e075.png)]
复制自 Tran 等人
前两个图像属于 2D 卷积。 输出始终是图像。 同时,3D 卷积输出一个体积。 区别在于内核在 3 个方向上进行卷积运算。 Tran 等人将 3D 卷积用于视频分类。 3D 卷积模型如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V6s0kUyv-1681567606713)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/f2f9bf9d-7ffd-4229-b5cf-0b90cbf076a1.png)]
复制自 Tran 等人
以下是使用 3D 卷积的模型代码片段:
net = tf.keras.models.Sequential() net.add(tf.keras.layers.Conv3D(32, kernel_size=(3, 3, 3), input_shape=(input_shape))) net.add(tf.keras.layers.Activation('relu')) net.add(tf.keras.layers.Conv3D(32, (3, 3, 3))) net.add(tf.keras.layers.Activation('softmax')) net.add(tf.keras.layers.MaxPooling3D()) net.add(tf.keras.layers.Dropout(0.25)) net.add(tf.keras.layers.Conv3D(64, (3, 3, 3))) net.add(tf.keras.layers.Activation('relu')) net.add(tf.keras.layers.Conv3D(64, (3, 3, 3))) net.add(tf.keras.layers.Activation('softmax')) net.add(tf.keras.layers.MaxPool3D()) net.add(tf.keras.layers.Dropout(0.25)) net.add(tf.keras.layers.Flatten()) net.add(tf.keras.layers.Dense(512, activation='sigmoid')) net.add(tf.keras.layers.Dropout(0.5)) net.add(tf.keras.layers.Dense(no_classes, activation='softmax')) net.compile(loss=tf.keras.losses.categorical_crossentropy, optimizer=tf.keras.optimizers.Adam(), metrics=['accuracy'])
3D 卷积需要大量的计算能力。 3D 卷积在Sports1M
数据集上达到 90.2% 的精度。
使用轨迹的分类
Wang 等人使用身体各部分的轨迹对所执行的动作进行分类。 这项工作结合了手工制作和深度学习的特征,可以进行最终预测。 以下是分类的表示形式:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jNkiYSnD-1681567606713)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/fa390d0d-4a3e-48af-8fe6-4896c092d2e9.png)]
转载自 Wang 等人
手工制作的特征是 Fisher 向量,这些特征来自 CNN。 下图演示了轨迹和特征图的提取:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2JnsP48z-1681567606719)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/389aa340-de61-4803-baaf-6e3b78b659f3.png)]
转载自 Wang 等人
轨迹和特征图都在时间上组合在一起,以形成关于时间片段的最终预测。
多模态融合
杨等人提出了一种具有 4 个模型的多模态融合视频分类方法。 这四个模型分别是 3D 卷积特征,2D 光流,3D 光流和 2D 卷积特征。
该方法的数据流如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mG71Y5c4-1681567606719)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/9b6f98f2-2e75-4ebb-b40e-ac265467545f.png)]
转载自 Yang 等人
现在,让我们了解 Convlet 。 Convlet 是来自单个内核的小卷积输出。 下图显示了 convlet 对卷积层中空间权重的学习:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1FJRcgDc-1681567606720)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/e258e5ea-f039-40f2-b79f-4fa0df3a7b55.png)]
转载自 Yang 等人
空间权重指示卷积层中局部空间区域的区分度或重要性。 下图是在多层卷积层和全连接层上完成的多层表示融合的图示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qzqKs7mh-1681567606720)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/b4160338-5686-48dd-a31a-f6c07404edcc.png)]
转载自 Yang 等人
增强机制用于组合预测。 提升是一种可以将多个模型预测组合为最终预测的机制。
用于分类的注意力区域
注意力机制可以用于分类。 注意力机制复制了人类专注于识别活动区域的行为。 注意力机制赋予某些区域比其他区域更多的权重。 训练时从数据中学习权重方法。 注意力机制主要有两种,即:
- 柔和注意力:性质确定,因此可以通过反向传播来学习。
- 硬注意力:性质随机,这需要复杂的学习机制。 由于需要采样数据,因此也很昂贵。
以下是软关注的可视化:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ADd1KFmT-1681567606720)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/5c514fb5-7f01-4436-90e9-65ced3ea1f90.png)]
转载自 Sharma 等人
根据注意,计算并加权 CNN 特征。 对某些区域的关注或权重可以用于可视化。 Sharma 等人使用此想法对视频进行分类。 LSTM 被用作卷积特征。 LSTM 通过注意以下帧来预测区域,如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f2wCEe7Y-1681567606720)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/c835ef24-72c0-4144-9fd8-890500f2c8ac.png)]
转自 Sharma 等人。
每个 LSTM 栈都会预测位置和标签。 每个栈具有三个 LSTM 。 LSTM 栈的输入是卷积特征立方体和位置。 位置概率是注意权重。 注意的使用提高了准确率以及可视化预测的方法。
我们已经看到了各种视频分类方法。 接下来,我们将学习视频中的其他应用。
将基于图像的方法扩展到视频
图像可用于姿势估计,样式转换,图像生成,分割,字幕等等。 同样,这些应用也在视频中找到位置。 使用时间信息可以改善来自图像的预测,反之亦然。 在本节中,我们将看到如何将这些应用扩展到视频。
人体姿势回归
人体姿势估计是视频数据的重要应用,可以改善其他任务,例如动作识别。 首先,让我们看一下可用于姿势估计的数据集的描述:
- PosesInTheWild 数据集:包含 30 个带有人体姿势标注的视频。 数据集在这里。 该数据集带有人类上半身关节的标注。
- 电影院中标记的帧(FLIC):从 30 部电影中获得的人体姿势数据集,可在以下位置找到。
Pfister 等人提出了一种预测视频中人体姿势的方法。 以下是回归人体姿势的流水线:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eHArsFEx-1681567606721)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/59d379eb-94cf-453c-aeac-28b1a04e6e44.png)]
复制自 Pfister 等人
视频中的帧被获取并通过卷积网络。 融合层,并获得姿势热图。 姿势热图与光流结合以获得扭曲的热图。 合并时间范围内的扭曲热图,以生成合并的热图,得到最终姿势。
跟踪人脸标志
视频中的人脸分析需要人脸检测,界标检测,姿势估计,验证等。 计算地标对于捕获人脸动画,人机交互和人类活动识别尤其重要。 除了在帧上进行计算外,还可以在视频上进行计算。 Gu 等人提出了一种使用视频中的人脸标志的检测和跟踪的联合估计的 RNN 方法。 结果优于逐帧预测和其他先前模型。 地标由 CNN 计算,时间方面在 RNN 中编码。 综合数据用于训练。
分割视频
使用时间信息时,可以更好地分割视频。 加德(Gadde)等人提出了一种通过扭曲来组合时间信息的方法。 下图演示了该解决方案,该方法将两个帧分段并且结合了变形:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-h3VDACVw-1681567606721)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/0d1ae328-95c8-4794-b8a4-196046e5f578.png)]
转载自 Gadde 等人
下图显示了翘曲网:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dFPExXOg-1681567606721)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/8c759f74-da66-4844-a5d5-66369605ba25.png)]
转载自 Gadde 等人
在两个帧之间计算光流,将它们与变形结合在一起。 变形模块获取光流,对其进行转换,然后将其与变形的表示相结合。
生成视频字幕
第 7 章,“图像字幕生成”说明了几种组合文本和图像的方法。 同样,可以为视频生成字幕,以描述上下文。 让我们看一下可用于字幕视频的数据集列表:
- 微软研究 - 视频转文本(MSR-VTT)具有 200,000 个视频剪辑和句子对。 可以从以下网站获取更多详细信息。
- MPII 电影描述语料库(MPII-MD)可以从以下网站获取。 它有 68,000 个句子和 94 部电影。
- 蒙特利尔视频标注数据集(M-VAD)可从以下网站获得。它有 49,000 个剪辑。
- YouTube2Text 包含 1,970 个视频,包含 80,000 个描述。
姚等人提出了一种为视频添加字幕的方法。 经过训练以进行动作识别的 3D 卷积网络用于提取局部时间特征。 然后在特征上使用注意力机制以使用 RNN 生成文本。 该过程如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VJSTuyeW-1681567606721)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/71cfe3d2-2787-4234-a683-8ca292510133.png)]
转载自 Yao 等人
Donahue 等人提出了另一种视频字幕或描述方法,该方法将 LSTM 与卷积特征组合。
这类似于前面的方法,除了我们在此处使用 2D 卷积特征,如下图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-56xaszzd-1681567606722)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/fe273a12-ded0-4027-b3d1-0657dee35178.png)]
摘自 Donahue 等人
我们有几种将文本与图像结合起来的方法,例如活动识别,图像描述和视频描述技术。 下图说明了这些技术:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eJl9W1Tr-1681567606722)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/37ba89c5-80e9-4655-8e8f-9e664ddd5b29.png)]
摘自 Donahue 等人
Venugopalan 等人提出了一种使用编码器-解码器方法进行视频字幕的方法。 以下是他提出的技术的可视化:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7SiuthUQ-1681567606722)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/6935b3c7-f3bb-4876-a3d5-f6f6559d7e05.png)]
复制自 Venugopalan 等人
对于此方法,可以在图像的帧或光流上计算 CNN。
生成视频
可以使用生成模型以无监督的方式生成视频。 可以使用当前帧预测未来的帧。 Ranzato 等人提出了一种受语言模型启发的视频生成方法。 RNN 模型用于拍摄图像补丁并预测下一个补丁。
总结
在本章中,我们涵盖了与视频分类有关的各种主题。 我们看到了如何将视频拆分为帧,以及如何将图像中的深度学习模型用于各种任务。 我们介绍了一些特定于视频的算法,例如跟踪对象。 我们看到了如何将基于视频的解决方案应用于各种场景,例如动作识别,手势识别,安全应用和入侵检测。
在下一章中,我们将学习如何将上一章中训练有素的模型部署到各种云和移动平台上的生产环境中。 我们将看到不同的硬件如何影响延迟和吞吐量方面的性能。
十、部署
在本章中,我们将学习如何在各种平台上部署经过训练的模型,以实现最大吞吐量和最小延迟。 我们将了解 GPU 和 CPU 等各种硬件的性能。 我们将遵循在 Amazon Web Services,Google Cloud Platform 等平台以及 Android,iOS 和 Tegra 等移动平台上部署 TensorFlow 的步骤。
我们将在本章介绍以下主题:
- 了解影响深度学习模型训练和推理性能的因素
- 通过各种方法提高性能
- 查看各种硬件的基准并学习调整它们以实现最佳性能的步骤
- 将各种云平台用于部署
- 将各种移动平台用于部署
模型表现
性能对于深度学习模型的训练和部署都很重要。 由于大数据或大模型架构,训练通常需要更多时间。 结果模型可能更大,因此在 RAM 受限的移动设备中使用时会出现问题。 更多的计算时间导致更多的基础架构成本。 推理时间在视频应用中至关重要。 由于前面提到了性能的重要性,因此在本节中,我们将研究提高性能的技术。 降低模型复杂度是一个简单的选择,但会导致精度降低。 在这里,我们将重点介绍一些方法,这些方法可以提高性能,而准确率却没有明显的下降。 在下一节中,我们将讨论量化选项。
量化模型
深度学习模型的权重具有 32 位浮点值。 当权重量化为 8 位时,精度下降很小,因此在部署中不会注意到。 结果权重的精度似乎对深度学习模型的精度性能影响较小。 这个想法对深度学习很有趣,并且在模型大小变得至关重要时很有用。 通过用 8 位值替换 32 位浮点值,可以显着减小模型大小并提高推理速度。 实现模型量化时有很多选择。 权重可以存储在 8 位中,但推理操作可以以 32 位浮点值执行。 架构的每个组件在量化大小上的行为可能有所不同,因此,取决于层,可以选择 32 或 16 或 8 位值。
量化工作有多种原因。 通常,深度学习模型经过训练可以解决图像中的噪声,因此可以被认为是健壮的。 推理计算可以具有冗余信息,并且可以由于量化而去除冗余信息。
最新的 CPU 和 RAM 硬件已针对浮点计算进行了调整,因此在此类硬件中量化效果可能不太明显。 随着为此目的引入越来越多的硬件,这种情况正在改变。 在 GPU 中,由于内存和速度现已适应较低的精确浮点运算,因此它们在内存和速度上存在明显差异。 还有其他特殊硬件可用于运行不太精确的浮动操作。
MobileNet
霍华德(Howard)和其他人引入了一种称为 MobileNets 的新型模型,可用于移动和嵌入式应用。 MobileNets 可以用于不同的应用,例如对象检测,地标识别,人脸属性,细粒度分类,如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ths9OjWO-1681567606722)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/f363f9cf-4786-41e0-9812-d49b4c26039a.png)]
转载自霍华德等人
MobileNets 通过用深度(b)和点向卷积(c)替换标准卷积过滤器(a)和点卷积(c)来减少模型的大小和计算量,如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qVagXF2P-1681567606723)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/dab9c6a5-cbd9-485e-8e96-832c17bedf0a.png)]
转载自霍华德等人
批量归一化和激活层被添加到深度和点积卷积中,如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ElzbFibM-1681567606723)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/4ee8ae67-9658-480e-a775-5b6f45033be8.png)]
转载自霍华德等人
有两个参数会影响模型的选择:
- 乘法和加法次数:精度和多加法之间的权衡如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yNI3vvU1-1681567606723)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/9f6fad9e-9ab5-405c-a5ac-8958cb342906.png)]
转载自霍华德等人
- 模型中的参数数量:此处显示权衡:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FM4xddUX-1681567606723)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/5a370687-9b2b-4f57-80fd-87792fdfae85.png)]
转载自霍华德等人
MobileNets 已显示,可以在移动和嵌入式设备上使用的精度有所降低的情况下,可以减少模型的计算和尺寸。 在霍华德等人的文章中可以看到模型与精度之间的确切权衡。
云端部署
必须将这些模型部署在云中以用于多个应用。 我们将为此目的寻找主要的云服务提供商。
AWS
Amazon Web Services(AWS)将支持扩展到基于 TensorFlow 的模型的开发和部署。 在 Amazon 上注册 AWS,然后选择 Amazon 机器映像(AMI)之一。 AMI 是安装了所有必需软件的计算机的映像。 您不必担心安装包。 AWS 提供了深度学习 AMI(DLAMI),以简化训练和部署深度学习模型。 有几种选择。 在这里,我们将使用 Conda,因为它带有运行 TensorFlow 所需的几个包。 Python 有两个选项:版本 2 和版本 3。以下代码将在 CUDA 8 的 Python 3 上使用 Keras 2 激活 TensorFlow:
source activate tensorflow_p36
以下代码将在 CUDA 8 的 Python 2 上使用 Keras 2 激活 TensorFlow:
source activate tensorflow_p27
您可以访问这里了解更多详细信息和教程。
还可以通过执行以下给定的步骤来启动虚拟机(VM):
- 转到 Amazon AWS,然后使用您的 Amazon 帐户登录。
- 从登录页面选择虚拟机来启动:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hHm8i3X7-1681567606723)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/d8d35771-0127-4f5f-9f7a-37edc6eb0c1c.png)]
- 在下一个窗口中,单击入门,选择 EC2 实例,如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vIN0d8ti-1681567606724)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/cf4ce914-ae52-4a17-968d-206854461304.png)]
- 为 EC2 实例命名:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iZZdthQS-1681567606724)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/afc41e92-9559-4fb0-9214-91b3d9902bf4.png)]
- 选择操作系统的类型:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5miBjFxx-1681567606724)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/5d91face-5671-4648-93fd-69708c41002b.png)]
- 选择实例类型。 实例类型指示 RAM 和 CPU 大小不同的配置类型。 也有两个选项可供选择。 选择实例类型,然后单击“下一步”按钮:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KqV7TVJr-1681567606724)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/39eeaa24-3a0a-47d7-8e72-87048ae2d5f3.png)]
- 创建一个隐私增强型邮件安全证书(PEM)文件,该文件将用于登录,如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IU90M2MA-1681567606724)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/25ab45d2-9f98-4720-a642-03263e11295b.png)]
- 创建实例将花费一些时间,最后,将显示完成状态:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Udy6KsmY-1681567606725)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/06ba2432-c401-46c9-9046-5097bad8cbfc.png)]
- 接下来,单击进入 EC2 控制台按钮:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ryQ3dFLL-1681567606725)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/973f1291-4651-47fd-b924-e999c5f583a6.png)]
- 现在将创建实例; 单击连接按钮,如下所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kHMoWQ7h-1681567606725)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/9c23756d-5135-44af-9f23-d59513e9d581.png)]
- 接下来,必须将实例连接到虚拟机的命令提示符。 连接所需的说明在此步骤中给出。 您需要在之前的步骤中下载 PEM 文件。 按照显示的说明连接到系统:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5ZSmOvcl-1681567606725)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/13ef9b43-6bd6-4ef3-a9e9-526f0a69c84e.png)]
- 完成后,通过单击操作|实例状态|终止来终止实例:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-arowLqhP-1681567606726)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/c7b106a4-3f86-44a5-94fb-e8795643e0d2.png)]
安装和执行步骤可以遵循第 1 章,“入门”。
Google Cloud Platform
Google Cloud Platform(GCP)是 Google 提供的云平台,具有与 AWS 类似的功能。 通过执行以下步骤,可以使用一个简单的虚拟机来训练诸如 AWS 之类的模型:
- 使用 cloud.google.com 转到 Google Cloud Platform,然后使用您的 Gmail 帐户登录到该平台。
- 现在,通过单击“转到控制台”按钮进入控制台:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3WszSMOn-1681567606726)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/b38e13e2-2b2a-45e4-841c-fc5feed34fcd.png)]
- 进入控制台后,通过单击计算引擎进入 VM 创建页面。 右上角菜单中的 VM 实例,如以下屏幕截图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y1TQ3oz2-1681567606726)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/0a5622e5-c108-4b28-8960-1eab8e3d9574.png)]
- 然后单击
CREATE INSTANCE
按钮,以创建所需的实例:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zV5Dmboz-1681567606726)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/0671e492-4e4b-4edd-8622-8208b096056e.png)]
- 接下来,可以通过配置选择实例类型。 Zone 参数通知区域将部署实例。 通过选择靠近用户的区域,可以节省等待时间。 可以使用所需的 RAM 和 CPU 定制机器类型。 还可以选择 GPU,以进行更快的训练。 选择实例的大小,然后单击“创建”按钮,如以下屏幕截图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0UaAmzjT-1681567606727)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/ae5b0924-3d17-4afa-9e63-baa8859245ba.png)]
- 创建实例将需要几分钟。 然后,单击实例的 SSH 下拉列表,然后选择“在浏览器窗口中打开”选项,如下所示,以在浏览器中打开控制台:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wL1nWEhF-1681567606727)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/dl-cv/img/783c512d-4692-4eeb-914d-9ff3f2d430b1.png)]
使用该外壳,您可以安装 TensorFlow 并可以训练或部署模型。 有许多选项可从虚拟机的配置中选择。 根据成本和时间的权衡,可以选择配置。
GCP 具有云机器学习引擎,可在使用 TensorFlow 时为我们提供帮助。 GCP 的三个组件可以一起用于构建训练和部署基础架构:
- 用于预处理图像的 Cloud DataFlow
- 用于训练和部署模型的云机器学习引擎
- 用于存储训练数据,代码和结果的 Google Cloud Storage
可以在这个页面上找到使用云机器学习引擎建立自定义图像分类模型的出色教程。
在设备中部署模型
TensorFlow 模型也可以部署在移动设备中。 移动设备包括智能手机,无人机,家用机器人等。 数十亿智能手机可以具有可以使用深度学习的计算机视觉应用。 可以拍照并搜索,流化带有标记场景的视频等。 在移动设备中进行部署意味着深度学习模型存在于设备上,并且推断发生在设备上。 设备上部署的模型有助于解决隐私问题。 在以下主题中,我们将讨论如何在各种移动平台上部署它们。
Jetson TX2
Jetson TX2 是由 NVIDIA 提供的嵌入式设备,专门用于高效 AI 计算。 Jetson TX2 轻巧,紧凑,因此适合在无人机,公共场所等中部署。 它还附带预装的 TensorRT,这是 TensorFlow 的运行时。 您可以购买 Jetson 并在安装 TensorFlow 之前快速安装 Ubuntu,CUDA,CUDNN。 克隆这个页面,然后在命令提示符下输入以下命令。
- 首先,在以下代码的帮助下安装必备组件:
./installPrerequisites.sh
- 现在,使用以下代码克隆 TensorFlow:
./cloneTensorFlow.sh
- 接下来,使用以下代码设置所需的环境变量:
./setTensorFlowEV.sh
- 现在我们将使用以下代码构建 TensorFlow :
./buildTensorFlow.sh
- 现在,我们将使用以下代码将打包文件处理为 Wheel 文件:
./packageTensorFlow.sh
- 现在,我们将使用以下代码安装 Tensorflow:
pip install $HOME/tensorflow-1.0.1-cp27-cp27mu-linux_aarch64.whl
借助这些步骤,我们可以在 Jetson TX2 中安装 TensorFlow。
安卓
任何 Android 应用都可以使用 TensorFlow,其构建细节可以在这个页面中找到。 关于此的官方示例可以在这个页面中找到。 假设读者具有 Android 编程经验,则在 Android 设备中实现 Tensorflow 的步骤如下:
- 使用第 3 章“图像检索”中介绍的步骤,将 TensorFlow 模型导出到
.pb
文件。 - 生成二进制文件
.so
和.jar
。 - 编辑
gradle
文件以启用库加载。 - 加载并运行 Android 应用文件
iPhone
苹果使用 CoreML 框架将机器学习集成到 iPhone 应用中。 Apple 提供了可以直接集成到应用中的标准模型列表。 您可以使用 TensorFlow 训练自定义深度学习模型并将其在 iPhone 中使用。 为了部署自定义模型,您必须在 CoreML 框架模型中隐藏 TensorFlow。 谷歌发布了 tf-coreml,用于将 TensorFlow 模型转换为 CoreML 模型。 可以使用以下代码安装 TFcoreML:
pip install -U tfcoreml
可以使用以下代码导出模型:
import tfcoreml as tf_converter tf_converter.convert(tf_model_path='tf_model_path.pb', mlmodel_path='mlmodel_path.mlmodel', output_feature_names=['softmax:0'], input_name_shape_dict={'input:0': [1, 227, 227, 3]})
iPhone 可以使用导出的模型进行预测。
总结
在本章中,我们了解了如何在各种平台和设备上部署经过训练的深度学习模型。 我们已经介绍了为这些平台获得最佳性能的步骤和准则。 我们已经看到了 MobileNets 的优势,它以很小的精度权衡来减少推理时间。