1. 磐创AI-开放猫官方网站首页
  2. 机器学习
  3. TensorFlowNews

使用 tf.keras 训练和提供 ML 模型

点击上方“磐创AI”,选择“置顶公众号”

精品文章,第一时间送达

使用 tf.keras 训练和提供 ML 模型

转载自:TensorFlow,未经允许不得二次转载


Keras 是一种高级神经网络接口,可以在多个后端上运行。其函数式 API 非常人性化且颇具灵活性,可构建各种应用。一经推出,Keras 便迅速受到青睐。2017 年,Keras API 以 tf.keras 的形式实现与核心 TensorFlow 的集成。虽然 tf.keras 和 Keras 拥有独立代码库,但彼此之间紧密耦合。自 TensorFlow 1.9 起发布更新文档编程人员指南以来,tf.keras 显然成为以 TensorFlow 构建神经网络时要使用的高级 API。

注:更新文档链接

https://www.tensorflow.org/tutorials/

编程人员指南链接

https://www.tensorflow.org/guide/keras


在这篇文章中,我们会介绍使用 tf.keras 训练、导出及提供神经网络的整个流程。例如,我们将使用 Kaggle Planet 数据集训练卷积神经网络,以预测亚马逊森林卫星图像的标签。我们的目的是说明真实用例的端到端管道。相关代码可通过 github 上的可运行笔记本获取。 请注意,您将需要安装最新版本的 TensorFlow(1.11.0,每日构建版),以根据指示完成全部操作。这只是 pip 安装,且 requirements.txt 文件会在存储区中提供。 或者,您也可以通过 Google Colab 立即运行内容!

注:github 链接

https://github.com/sdcubber/keras-training-serving/blob/master/training-and-serving-with-tf-keras.ipynb

Google Colab 链接

https://colab.research.google.com/github/sdcubber/keras-training-serving/blob/master/training-and-serving-with-tf-keras.ipynb


您可在 Kaggle 下载数据。训练数据由大约 40000 张带标签的亚马逊雨林图像组成,每张图像均与多个标签关联:

注:Kaggle 链接

https://www.kaggle.com/c/planet-understanding-the-amazon-from-space/data


只有一个 “天气” 标签:晴朗、薄雾、多云或局部多云

一个或多个 “地面” 标签:农田、裸地、住宅、道路、水域……

使用 tf.keras 训练和提供 ML 模型

天气标签:多云 地面标签:原始森林,道路


Pandas DataFrame 包含图像名称列、天气标签列和地面标签列,而系统会将这些数据编码为二进制向量。您可前往 github 以 .csv 文件的形式获取相关内容:

注:github 链接

https://github.com/sdcubber/keras-training-serving/blob/master/KagglePlanetMCML.csv


使用 tf.keras 训练和提供 ML 模型


我们希望训练出的模型能够准确预测新图像的这些标签。为此,我们会尝试使用针对天气和地面标签提供两种独立输出的网络。预测天气标签是 多类别分类问题的一个例子, 而地面标签可以建模为 多标签分类 问题。因此,两种输出的损失函数有所不同。训练模型后,我们会使用 TensorFlow Serving 导出和提供模型,,如此一来,我们就可以通过 HTTP 发送请求以获得图像的预测结果。



指定模型

我们将从头开始构建自己的模型¹。我们会采用十分经典的配置,包括一些卷积层、ReLU 激活函数和两个位于顶层的密集分类器:


   import tensorflow as tf    

2    IM_SIZE = 128 

3

4    image_input = tf.keras.Input(shape=(IM_SIZE, IM_SIZE, 3), name=’input_layer’)    

5

6    # Some convolutional layers    

   conv_1 = tf.keras.layers.Conv2D(32,    

                                       kernel_size=(3, 3),    

                                       padding=’same’,    

10                                      activation=’relu’)(image_input)    

11    conv_1 = tf.keras.layers.MaxPooling2D(padding=’same’)(conv_1)

12    conv_2 = tf.keras.layers.Conv2D(32,    

13                                      kernel_size=(3, 3),    

14                                      padding=’same’,    

15                                      activation=’relu’)(conv_1)    

16    conv_2 = tf.keras.layers.MaxPooling2D(padding=’same’)(conv_2)

17

18    # Flatten the output of the convolutional layers    

19    conv_flat = tf.keras.layers.Flatten()(conv_2)    

20

21    # Some dense layers with two separate outputs    

22    fc_1 = tf.keras.layers.Dense(128,    

23                                      activation=’relu’)(conv_flat) 

24    fc_1 = tf.keras.layers.Dropout(0.2)(fc_1)    

25    fc_2 = tf.keras.layers.Dense(128,    

26                                      activation=’relu’)(fc_1)    

27    fc_2 = tf.keras.layers.Dropout(0.2)(fc_2)    

28

29    # Output layers: separate outputs for the weather and the ground labels    

30    weather_output = tf.keras.layers.Dense(4,    

31                                      activation=’softmax’,    

32                                      name=’weather’)(fc_2)    

33    ground_output = tf.keras.layers.Dense(13,    

34                                      activation=’sigmoid’,    

35                                      name=’ground’)(fc_2)    

36

37    # Wrap in a Model    

38    model = tf.keras.Model(inputs=image_input, outputs=[weather_output, ground_output])    


我们有两个输出层,因此在指定模型时应将这些层以输出列表的形式传递。请注意,天气和地面输出层的激活函数并不相同。很方便的是,Model 实现 tf.keras 时会采用简便的 summary() 方法:


使用 tf.keras 训练和提供 ML 模型


编译模型时,系统会以字典的形式提供两个不同的损失函数,而此字典会将张量名称映射到损失:


   model.compile(optimizer=’adam’,    

2                        loss={‘weather’: ‘categorical_crossentropy’,

                               ‘ground’: ‘binary_crossentropy’})    


编译模型时,系统会以随机权重对其进行初始化,并允许我们选择优化算法来训练网络。


[1] 正如在 Kaggle 竞赛中所证明的那样,通过大型预训练网络进行迁移学习是取得成功的一大关键。但这里的重点并不是在 Kaggle 中获胜。如需获取有关如何实现绝佳性能的提示,请观看介绍如何处理此数据集的精彩 fast.ai 课程。

注:如何处理此数据集的精彩 fast.ai 链接

http://course.fast.ai/lessons/lesson3.html



模型训练

我们开始训练模型吧!我会在我的笔记本电脑上训练此模型,但这台电脑的内存不够,无法存储整个数据集。处理图像数据时经常会出现这种情况。Keras 提供 model.fit_generator() 方法,而该方法可以使用自定义 Python 生成器从磁盘生成图像以进行训练。不过,从 Keras 2.0.6 开始,我们可以使用 Sequence 对象(而不是生成器)实现安全的多进程处理,这意味着您可以显著提升运行速度并降低 GPU(如果您有)遇到瓶颈的风险。Keras 文档已经提供出色的示例代码,我会稍微自定义一下,以实现下列目的:


让其使用将图像名称映射到标签的 DataFrame

每隔一个周期打乱训练数据


1    import ast    

   import numpy as np    

   import math    

   import os    

5    import random    

6    from tensorflow.keras.preprocessing.image import 

7    img_to_array as img_to_array

8

9    from tensorflow.keras.preprocessing.image import load_img as load_img    

def load_image(image_path, size):    

10        # data augmentation logic such as random rotations can be added here    

11        return img_to_array(load_img(image_path, target_size=(size, size))) / 255.    

12

13    class KagglePlanetSequence(tf.keras.utils.Sequence):    

14        “””    

15        Custom Sequence object to train a model on out-of-memory datasets.     

16        “”” 

17

18        def __init__(self, df_path, data_path, im_size, batch_size, mode=’train’):    

19                “””    

20                df_path: path to a .csv file that contains columns with image names and labels    

21                data_path: path that contains the training images    

22                im_size: image size    

23                mode: when in training mode, data will be shuffled between epochs    

24                “””    

25                self.df = pd.read_csv(df_path)    

26                self.im_size = im_size    

27                self.batch_size = batch_size    

28                self.mode = mode    

29

30                # Take labels and a list of image locations in memory    

31                self.wlabels = self.df[‘weather_labels’].apply(lambda x: ast.literal_eval(x)).tolist()    

32                self.glabels = self.df[‘ground_labels’].apply(lambda x: ast.literal_eval(x)).tolist()    

33                self.image_list = self.df[‘image_name’].apply(lambda x: os.path.join(data_path, x + ‘.jpg’)).tolist()

34

35        def __len__(self):    

36                return int(math.ceil(len(self.df) / float(self.batch_size)))

37

38        def on_epoch_end(self):    

39                # Shuffles indexes after each epoch    

40                self.indexes = range(len(self.image_list))    

41                if self.mode == ‘train’:    

42                    self.indexes = random.sample(self.indexes, k=len(self.indexes))    

43

44        def get_batch_labels(self, idx):