前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >TensorFlow 2.0中的多标签图像分类

TensorFlow 2.0中的多标签图像分类

作者头像
代码医生工作室
发布2019-12-10 10:45:02
6.6K0
发布2019-12-10 10:45:02
举报
文章被收录于专栏:相约机器人相约机器人

作者 | Mohamed-Achref Maiza

来源 | Medium

编辑 | 代码医生团队

本文介绍一些在训练多标签图像分类器时可能会感兴趣的概念和工具。完整的代码可以在GitHub上找到。

https://github.com/ashrefm/multi-label-soft-f1

目录

  • 了解多标签分类
  • TensorFlow 2.0的有趣之处
  • 数据集(来自其海报的电影体裁)
  • 建立快速输入管道
  • 使用TF.Hub迁移学习
  • 模型训练与评估
  • 导出Keras模型
  • 了解多标签分类

近年来,机器学习在解决之前无法想象的规模的复杂预测任务方面显示出巨大的成功。开始使用它进行业务转型的最简单方法是,识别简单的二进制分类任务,获取足够的历史数据并训练一个好的分类器以在现实世界中很好地进行概括。总有某种方法可以将预测性业务问题归为是/否问题。如果收集标记的数据,则可以通过监督学习来解决所有这些二元问题。

还可以设计更复杂的监督学习系统来解决非二进制分类任务:

  • 多类分类:有两个以上的类,每个观测值都属于一个并且只有一个类。例如,一家电子商务公司希望根据其品牌(三星,华为,苹果,小米,索尼或其他)对智能手机等产品进行分类。
  • 多标签分类:有两个或两个以上类别,每个观测值同时属于一个或多个类别。应用示例是医学诊断,其中需要根据患者的体征和症状开出一种或多种治疗方法。通过类推,可以设计用于汽车诊断的多标签分类器。它以所有电子测量,错误,症状,行驶里程为输入,并预测万一发生汽车事故时需要更换的零件。

多标签分类在计算机视觉应用中也很常见。在捕捉新电影的海报(动作,戏剧,喜剧等)时,会利用直觉和印象来猜测新电影的内容。可能曾经在地铁站中遇到过这种情况,想从墙上的海报中猜测电影的类型。如果假设在推理过程中使用的是海报的颜色信息,饱和度,色相,图像的纹理,演员的身体或面部表情以及可以识别类型的任何形状或设计,那么也许从海报中提取那些重要图案并以类似方式从中学习的一种数值方法。如何建立可预测电影类型的深度学习模型?看看可以在TensorFlow 2.0中使用的一些技术!

TensorFlow 2.0的有趣之处

当TensorFlow在2015年由Google首次发布时,它迅速成为了世界上最受欢迎的开源机器学习库“一个为开发人员,企业和研究人员提供了一个全面的工具生态系统,希望将最新技术推向世界。谷歌宣布在今年9月底正式发布TensorFlow 2.0。新版本增加了主要功能和改进:

  • Keras完全集成,默认情况下eager execution
  • 使用tf.function可以执行更多Pythonic函数,这使TensorFlow图得到了很好的并行计算优化
  • 使用TensorFlow数据集加快输入管道,以非常有效的方式传递训练和验证数据
  • 使用TensorFlow Serving,TensorFlow Lite和TensorFlow.js在服务器,设备和Web浏览器上进行更强大的生产部署

个人非常喜欢在TensorFlow 1.x中构建自定义估算器,因为它们提供了高度的灵活性。因此,很高兴看到Estimator API得到扩展。现在可以通过转换现有的Keras模型来创建估算器。

TensorFlow 2.0现在可用

数据集(来自其海报的电影体裁)

该数据集托管在Kaggle上,并包含来自IMDB网站的电影海报。MovieGenre.csv可以下载一个csv文件。它包含每个电影的以下信息:IMDB ID,IMDB链接,标题,IMDB得分,类型和下载电影海报的链接。在此数据集中,每个电影海报可以至少属于一种流派,并且最多可以分配3个标签。海报总数约为4万张。

https://www.kaggle.com/neha1703/movie-genre-from-its-poster

需要注意的重要一点是,并非所有电影流派都以相同数量表示。其中一些可能很少出现,这对于任何ML算法而言都是艰巨的挑战。可以决定忽略少于1000个观察值的所有标签(简短,西方,音乐,体育,黑色电影,新闻,脱口秀,真人秀,游戏秀)。这意味着由于缺少对这些标签的观察,因此不会训练该模型预测这些标签。

建立快速输入管道

如果熟悉keras.preprocessing,则可能知道图像数据迭代器(例如,ImageDataGenerator和DirectoryIterator)。这些迭代器对于图像目录包含每个类的一个子目录的多类分类非常方便。但是,在多标签分类的情况下,不可能拥有符合该结构的图像目录,因为一个观察可以同时属于多个类别。

这就是tf.data API占上风的地方。

  • 快一点
  • 它提供细粒度的控制
  • 它与TensorFlow的其余部分很好地集成在一起

首先,需要编写一些函数来解析图像文件,并生成代表特征的张量和代表标签的张量。

  • 在解析功能中,可以调整图像大小以适应模型期望的输入。
  • 还可以将像素值缩放到0到1之间。这是一种常见做法,有助于加快训练的收敛速度。如果将每个像素都视为一个特征,则希望这些特征具有相似的范围,以使梯度不会失控,并且只需要一个全局学习率乘数即可。
代码语言:javascript
复制
IMG_SIZE = 224 # Specify height and width of image to match the input format of the model
CHANNELS = 3 # Keep RGB color channels to match the input format of the model
代码语言:javascript
复制
def parse_function(filename, label):
    """Function that returns a tuple of normalized image array and labels array.
    Args:
        filename: string representing path to image
        label: 0/1 one-dimensional array of size N_LABELS
    """
    # Read an image from a file
    image_string = tf.io.read_file(filename)
    # Decode it into a dense vector
    image_decoded = tf.image.decode_jpeg(image_string, channels=CHANNELS)
    # Resize it to fixed shape
    image_resized = tf.image.resize(image_decoded, [IMG_SIZE, IMG_SIZE])
    # Normalize it from [0, 255] to [0.0, 1.0]
    image_normalized = image_resized / 255.0
return image_normalized, label

要在数据集上训练模型,希望数据为:

  • 好好清洗
  • 分批
  • 批量尽快供货。

使用tf.data.Dataset抽象可以轻松添加这些功能。

代码语言:javascript
复制
BATCH_SIZE = 256 # Big enough to measure an F1-score
AUTOTUNE = tf.data.experimental.AUTOTUNE # Adapt preprocessing and prefetching dynamically to reduce GPU and CPU idle time
SHUFFLE_BUFFER_SIZE = 1024 # Shuffle the training data by a chunck of 1024 observations

AUTOTUNE将使预处理和预取工作负载适应模型训练和批量消耗。要预取的元素数量应等于(或可能大于)单个训练步骤消耗的批次数量。AUTOTUNE将提示tf.data运行时在运行时动态调整值。

现在可以创建一个函数来为TensorFlow生成训练和验证数据集。

代码语言:javascript
复制
def create_dataset(filenames, labels, is_training=True):
    """Load and parse dataset.
    Args:
        filenames: list of image paths
        labels: numpy array of shape (BATCH_SIZE, N_LABELS)
        is_training: boolean to indicate training mode
    """
    
    # Create a first dataset of file paths and labels
    dataset = tf.data.Dataset.from_tensor_slices((filenames, labels))
    # Parse and preprocess observations in parallel
    dataset = dataset.map(parse_function, num_parallel_calls=AUTOTUNE)
    
    if is_training == True:
        # This is a small dataset, only load it once, and keep it in memory.
        dataset = dataset.cache()
        # Shuffle the data each buffer size
        dataset = dataset.shuffle(buffer_size=SHUFFLE_BUFFER_SIZE)
        
    # Batch the data for multiple steps
    dataset = dataset.batch(BATCH_SIZE)
    # Fetch batches in the background while the model is training.
    dataset = dataset.prefetch(buffer_size=AUTOTUNE)
    
return dataset
代码语言:javascript
复制
train_ds = create_dataset(X_train, y_train_bin)
val_ds = create_dataset(X_val, y_val_bin)

每一批将是一对数组(一个包含要素,另一个包含标签)。特征数组将具有包含缩放像素的形状(BATCH_SIZE,IMG_SIZE,IMG_SIZE,CHANNELS)。标签数组的形状为(BATCH_SIZE,N_LABELS),其中N_LABELS是目标标签的最大数量,每个值表示影片中是否具有特定流派(0或1个值)。

使用TF.Hub迁移学习

可以在称为迁移学习的过程中使用经过预先训练的模型,而不是从头开始构建和训练新模型。视觉应用的大多数预训练模型都是在ImageNet上训练的,ImageNet是一个大型图像数据库,具有1400万幅图像,分为2万多个类别。迁移学习背后的想法是,由于这些模型是在大型和一般分类任务的上下文中进行训练的,因此可以通过提取和迁移先前学习的有意义的特征,将其用于解决更具体的任务。需要做的就是获取一个预先训练的模型,然后在其之上简单地添加一个新的分类器。新分类头将从头开始进行培训,以便将物镜重新用于多标签分类任务。

Aknowledgement

TensorFlow核心团队在共享预训练的模型和有关如何将其与tf.kerasAPI 一起使用的教程方面做得很好。

中心

迁移学习FrançoisChollet

什么是TensorFlow Hub?

在软件开发中必不可少的一个概念是重新使用通过库提供的代码的想法。可以加快开发速度并提高效率。对于从事计算机视觉或NLP任务的机器学习工程师,知道从头开始训练复杂的神经网络体系结构需要花费多长时间。TensorFlow Hub是一个允许发布和重用预制ML组件的库。使用TF.Hub,重新训练预训练模型的顶层以识别新数据集中的类变得很容易。TensorFlow Hub还可以分发没有顶层分类层的模型。这些可以用来轻松地进行转移学习。

下载无头模型

来自tfhub.dev的任何与Tensorflow 2兼容的图像特征矢量URL都可能对数据集很有趣。唯一的条件是确保准备的数据集中图像特征的形状与要重用的模型的预期输入形状相匹配。

首先,准备特征提取器。将使用MobileNet V2的预训练实例,其深度乘数为1.0,输入大小为224x224。实际上,MobileNet V2是一大类神经网络体系结构,其主要目的是加快设备上的推理速度。它们的大小不同,具体取决于深度乘数(隐藏的卷积层中的要素数量)和输入图像的大小。

代码语言:javascript
复制
import tensorflow_hub as hub
 
feature_extractor_url = "https://tfhub.dev/google/imagenet/mobilenet_v2_100_224/feature_vector/4"
feature_extractor_layer = hub.KerasLayer(feature_extractor_url,
                                         input_shape=(IMG_SIZE,IMG_SIZE,CHANNELS))

在这里使用的特征提取器接受形状为(224、224、3)的图像,并为每个图像返回1280个长度的向量。

应该冻结要素提取器层中的变量,以便训练仅修改新的分类层。通常,与处理特征提取器的原始数据集相比,使用非常小的数据集时,这是一个好习惯。

代码语言:javascript
复制
feature_extractor_layer.trainable = False

仅当训练数据集很大且与原始ImageNet数据集非常相似时,才建议微调特征提取器。

附上分类头

现在,可以将特征提取器层包装在tf.keras.Sequential模型中,并在顶部添加新层。

代码语言:javascript
复制
model = tf.keras.Sequential([
    feature_extractor_layer,
    layers.Dense(1024, activation='relu', name='hidden_layer'),
    layers.Dense(N_LABELS, activation='sigmoid', name='output')
])

模型摘要如下所示:

MobileNet中的2.2M参数已冻结,但在密集层中有1.3K可训练的参数。需要在最终的神经元中应用S型激活函数,以计算出每种流派的概率得分。这样就可以依靠多个逻辑回归在同一模型中同时进行训练。每个最终神经元将充当一个单一类别的单独的二进制分类器,即使提取的特征对于所有最终神经元而言都是相同的。

使用此模型生成预测时,应该期望每个流派都有一个独立的概率得分,并且所有概率得分不一定总和为1。这与在多类分类中使用softmax层(其中概率得分的总和)不同。输出等于1。

模型训练与评估

在准备好数据集并通过在预先训练的模型之上附加多标签神经网络分类器来构成模型之后,可以继续进行训练和评估,但首先需要定义两个主要功能:

  • 损失函数:您需要它来度量过渡批次的模型误差(成本)。它必须是可区分的,以便在神经网络中反向传播错误并更新权重。
  • 评估功能:它应该代表您真正关心的最终评估指标。与损失函数不同,它必须更加直观才能理解模型在现实世界中的性能。

假设要使用Macro F1-score @ threshold 0.5来评估模型的性能。它是每个标签固定概率阈值为0.5时获得的所有F1分数的平均值。如果它们在多标签分类任务中具有相同的重要性,则对所有标签取平均值是非常合理的。在此根据TensorFlow中的大量观察结果提供此指标的实现。

代码语言:javascript
复制
def macro_f1(y, y_hat, thresh=0.5):
    """Compute the macro F1-score on a batch of observations (average F1 across labels)
    
    Args:
        y (int32 Tensor): labels array of shape (BATCH_SIZE, N_LABELS)
        y_hat (float32 Tensor): probability matrix from forward propagation of shape (BATCH_SIZE, N_LABELS)
        thresh: probability value above which we predict positive
        
    Returns:
        macro_f1 (scalar Tensor): value of macro F1 for the batch
    """
    y_pred = tf.cast(tf.greater(y_hat, thresh), tf.float32)
    tp = tf.cast(tf.math.count_nonzero(y_pred * y, axis=0), tf.float32)
    fp = tf.cast(tf.math.count_nonzero(y_pred * (1 - y), axis=0), tf.float32)
    fn = tf.cast(tf.math.count_nonzero((1 - y_pred) * y, axis=0), tf.float32)
    f1 = 2*tp / (2*tp + fn + fp + 1e-16)
    macro_f1 = tf.reduce_mean(f1)
    return macro_f1

此度量不可微分,因此不能用作损失函数。相反可以将其转换为可以最小化的可区分版本。将由此产生的损失函数称为软F1损失宏!

通常,使用传统的二进制交叉熵来优化模型是可以的,但是宏soft-F1损失带来了非常重要的好处,决定在某些情况下利用这些好处。

使用宏soft F1损失训练模型

指定学习率和训练时期数(整个数据集的循环数)。

代码语言:javascript
复制
LR = 1e-5 # Keep it small when transfer learning
EPOCHS = 30

编译模型以配置训练过程。

代码语言:javascript
复制
model.compile(
  optimizer=tf.keras.optimizers.Adam(learning_rate=LR),
  loss=macro_soft_f1,
  metrics=[macro_f1])

现在,可以传递(特征,标签)的训练数据集以适合模型,并指定一个单独的数据集进行验证。验证集的性能将在每个时期之后进行测量。

代码语言:javascript
复制
history = model.fit(train_ds,
  epochs=EPOCHS,
  validation_data=create_dataset(X_val, y_val_bin))

30个时期后,可能会在验证集上看到收敛。

显示预测

看看将模型用于验证集中某些已知电影的海报时的预测结果。

注意到该模型可以正确实现“浪漫”。是因为“爱的事”海报上的红色标题吗?

该模型建议使用“泰坦之战”的新标签怎么办?“ Sci-Fi”标签似乎很准确,并且与这部电影有关。请记住在原始数据集中,每个海报最多给出3个标签。也许可以通过使用模型来推荐更有用的标签!

导出Keras模型

训练和评估模型后,可以将其导出为TensorFlow保存的模型,以备将来使用。

代码语言:javascript
复制
from datetime import datetime
t = datetime.now().strftime("%Y%m%d_%H%M%S")
export_path = "./models/soft-f1_{}".format(t)
tf.keras.experimental.export_saved_model(model, export_path)
print("Model with macro soft-f1 was exported in this path: '{}'".format(export_path))

可以稍后通过指定包含.pb文件的导出目录的路径来重新加载tf.keras模型。

代码语言:javascript
复制
reloaded = tf.keras.experimental.load_from_saved_model(export_path,
                                                       custom_objects={'KerasLayer':hub.KerasLayer})

注意custom_objects词典中的“ KerasLayer”对象。这是用于构成模型的TF.Hub模块。

总结

  • 多标签分类:当一个观察的可能标签数目大于一个时,应该依靠多重逻辑回归来解决许多独立的二元分类问题。使用神经网络的优势在于,可以在同一模型中同时解决许多问题。小批量学习有助于减少训练时的内存复杂性。
  • TensorFlow数据API:tf.data使构建快速输入管道以训练和评估TensorFlow模型成为可能。使用tf.data.Dataset抽象,可以将观察值收集为一对代表图像及其标签的张量分量,对其进行并行预处理,并以非常容易和优化的方式进行必要的改组和批处理。
  • TensorFlow Hub:迁移学习从未如此简单。
  • TF.Hub提供来自大型预训练ML模型的可重用组件。可以加载包装为keras层的MobileNet功能提取器,并在其顶部附加自己的完全连接的层。可以冻结预训练的模型,并且在训练过程中仅更新分类图层的权重。
  • 直接为宏F1优化:通过引入宏软F1损失,可以训练模型以直接增加关心的指标:宏F1得分@阈值0.5。应用此定制的损失函数可能会发现有趣的好处。
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-12-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 相约机器人 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
GPU 云服务器
GPU 云服务器(Cloud GPU Service,GPU)是提供 GPU 算力的弹性计算服务,具有超强的并行计算能力,作为 IaaS 层的尖兵利器,服务于深度学习训练、科学计算、图形图像处理、视频编解码等场景。腾讯云随时提供触手可得的算力,有效缓解您的计算压力,提升业务效率与竞争力。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档