使用 Node.js 将图片中的苹果变成橘子

avatar
阿里巴巴 前端委员会智能化小组 @阿里巴巴

文/ 阿里淘系 F(x) Team 牟牟

前言

为了让大家更好地学习 Pipcook 和机器学习,我们准备了实战系列教程,会分别从前端组件识别、图片风格迁移、AI 作诗以及博客自动分类,这几个具体示例来讲解如何在我们日常开发中使用 Pipcook,如果需要了解 Pipcook 1.0,请前往 github 主页

背景

图片风格转换在一些相机 App 或者照片编辑 App 中有比较多的应用,可以对图片进行各种转换。Pipcook 已经支持图片风格迁移模型,可以实现很多有趣的图片风格转换,比如将马变成斑马:

image-20200624180916681.png

或者苹果和橘子的图片互转:

image-20200624181004442.png

或者照片和各种风格艺术画之间的相互转换(每种类型需要进行一次模型训练):

68747470733a2f2f6a756e79616e7a2e6769746875622e696f2f4379636c6547414e2f696d616765732f70686f746f327061696e74696e672e6a7067.png

是不是很有趣,让我们瞧瞧如何在 Pipcook 上实现这个功能吧!

Pipeline

首先定义一个 pipeline 配置文件 cycle-gan.json,内容如下:

{
  "plugins": {
    "dataCollect": {
      "package": "@pipcook/plugins-image-classification-data-collect",
      "params": {
        "url": "https://ai-sample.oss-cn-hangzhou.aliyuncs.com/apple2orange.zip"
      }
    },
    "dataAccess": {
      "package": "@pipcook/plugins-pascalvoc-data-access"
    },
    "modelDefine": {
      "package": "@pipcook/plugins-tensorflow-cycle-gan-model-define"
    },
    "modelTrain": {
      "package": "@pipcook/plugins-image-generation-tensorflow-model-train",
      "params": {
        "niter": 50000
      }
    },
    "modelEvaluate": {
      "package": "@pipcook/plugins-image-generation-model-evaluate"
    }
  }
}

我们使用了以下插件来完成我们的 pipeline:

  • @pipcook/plugins-image-classification-data-collect 这个插件用于下载图片分类的数据集,我们需要提供 url 参数,插件会下载并解压数据集。
  • @pipcook/plugins-pascalvoc-data-access下载好了数据集后,需要将数据集转换为 pascal voc 格式才能被风格转换模型识别,所以我们采用此插件。
  • @pipcook/plugins-tensorflow-cycle-gan-model-define 基于 TensorFlow 实现的 CycleGAN 模型定义插件。
  • @pipcook/plugins-image-generation-tensorflow-model-train 使用这个插件开始模型训练,主要的参数为 niter 训练的周期,调整该参数将会影响训练时长和模型预测效果,在我们的例子中,设置到 50000 可以基本满足。
  • @pipcook/plugins-image-generation-model-evaluate 此插件在训练结束后对模型效果的评估,最终给出的是测试图片生成目标图标的各项 loss 值。

需要注意的是,CycleGAN 模型训练需要比较多的计算资源,建议使用 GPU 服务器进行训练。

训练

准备好 pipeline 配置文件后,在 shell 中执行:

$ pipcook run cycle-gan.json --verbose
start loading plugin @pipcook/plugins-image-classification-data-collect
downloading dataset ...
unzip and collecting data...
create annotation file...
start loading plugin @pipcook/plugins-pascalvoc-data-access
create a result "b1192e00-20ff-4872-adbc-6f7d408ea0fc" for plugin "@pipcook/plugins-pascalvoc-data-access@0.6.3"
start loading plugin @pipcook/plugins-cycle-gan-model-define
create a result "6dda48eb-52da-425e-a95f-748fb8180954" for plugin "@pipcook/plugins-cycle-gan-model-define@0.6.0"
start loading plugin @pipcook/plugins-image-generate-tensorflow-model-train
Model: "dis_B"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_2 (InputLayer)         [(None, 128, 128, 3)]     0         
_________________________________________________________________
conv2d_18 (Conv2D)           (None, 64, 64, 64)        3136      
_________________________________________________________________
leaky_re_lu (LeakyReLU)      (None, 64, 64, 64)        0         
_________________________________________________________________
conv2d_19 (Conv2D)           (None, 32, 32, 128)       131200    
_________________________________________________________________
instance_normalization2d_17  (None, 32, 32, 128)       64        
_________________________________________________________________
leaky_re_lu_1 (LeakyReLU)    (None, 32, 32, 128)       0         
_________________________________________________________________
conv2d_20 (Conv2D)           (None, 16, 16, 256)       524544    
_________________________________________________________________
instance_normalization2d_18  (None, 16, 16, 256)       32        
_________________________________________________________________
leaky_re_lu_2 (LeakyReLU)    (None, 16, 16, 256)       0         
_________________________________________________________________
conv2d_21 (Conv2D)           (None, 16, 16, 512)       2097664   
_________________________________________________________________
instance_normalization2d_19  (None, 16, 16, 512)       32        
_________________________________________________________________
leaky_re_lu_3 (LeakyReLU)    (None, 16, 16, 512)       0         
_________________________________________________________________
conv2d_22 (Conv2D)           (None, 16, 16, 1)         8193      
=================================================================
Total params: 2,764,865
Trainable params: 2,764,865
Non-trainable params: 0
_________________________________________________________________
Model: "dis_A"
_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_4 (InputLayer)         [(None, 128, 128, 3)]     0         
_________________________________________________________________
conv2d_41 (Conv2D)           (None, 64, 64, 64)        3136      
_________________________________________________________________
leaky_re_lu_4 (LeakyReLU)    (None, 64, 64, 64)        0         
_________________________________________________________________
conv2d_42 (Conv2D)           (None, 32, 32, 128)       131200    
_________________________________________________________________
instance_normalization2d_37  (None, 32, 32, 128)       64        
_________________________________________________________________
leaky_re_lu_5 (LeakyReLU)    (None, 32, 32, 128)       0         
_________________________________________________________________
conv2d_43 (Conv2D)           (None, 16, 16, 256)       524544    
_________________________________________________________________
instance_normalization2d_38  (None, 16, 16, 256)       32        
_________________________________________________________________
leaky_re_lu_6 (LeakyReLU)    (None, 16, 16, 256)       0         
_________________________________________________________________
conv2d_44 (Conv2D)           (None, 16, 16, 512)       2097664   
_________________________________________________________________
instance_normalization2d_39  (None, 16, 16, 512)       32        
_________________________________________________________________
leaky_re_lu_7 (LeakyReLU)    (None, 16, 16, 512)       0         
_________________________________________________________________
conv2d_45 (Conv2D)           (None, 16, 16, 1)         8193      
=================================================================
Total params: 2,764,865
Trainable params: 2,764,865
Non-trainable params: 0
_________________________________________________________________
create a result "b15b7472-d430-4289-9216-a7e2f9ee852c" for plugin "@pipcook/plugins-image-generate-tensorflow-model-train@0.6.0"
start loading plugin @pipcook/plugins-cycle-gan-model-evaluate

训练预计持续时间12小时,训练完成后,将在当前目录生成output文件夹,里面就是我们训练好的模型了。我们可以使用 npm 对它进行安装:

$ cd output && npm install

风格转换

此时,模型已经准备就绪,我们找一张苹果的图片和一张橘子的图片:

image-20200624185223088.pngimage-20200624185732954.png

const predict = require('./output');
const fs = require('fs');
function saveToFile (base64Data, file) {
  var dataBuffer = new Buffer(base64Data, 'base64');
  fs.writeFile(file, dataBuffer, function(err) {
    if (err) {
      console.log('保存文件失败', err.message);
    } else {
        console.log('保存文件成功:', file);
    }
  });  
}
(async () => {
  const orange_from_apple = await predict({ path: '/path/to/apple.jpg', predictType: 'a2b' });
  saveToFile(orange_from_apple, '/path/to/save/orange_from_apple.jpg'); // 生成的橘子图片
  const apple_from_orange = await predict({ path: '/path/to/orange.jpg', predictType: 'b2a' });
  saveToFile(apple_from_orange, '/path/to/save/apple_from_orange.jpg'); // 生成的苹果图片
})();

保存为 apple2orage.js, 然后运行:

$ node ./apple2orange.js
保存文件成功: /path/to/save/orange_from_apple.jpg
保存文件成功: /path/to/save/apple_from_orange.jpg

转换之后的图片:

image-20200624185423938.pngimage-20200624191134063.png

将代码包装一下,一个可以提供苹果和橘子互转的图片风格转换服务就完成啦!

数据集

图片风格转换的数据集目录结构如图:

image-20200628103233103.png

其中 train 为训练数据集,用于模型训练,test 为测试数据集,用于模型训练结束后验证模型准确性。

测试数据集和训练数据集包含 A,B 两个文件夹,这两个文件夹内的数据不需要配对,也就是说,A 和 B 文件夹内是无关联的图片,在我们这个例子中,我们只需要在 A 文件夹内放入任意苹果的图片,B 文件夹内放入任意橘子的图片,就可以进行模型训练了。这对我们收集数据来说非常友好,比如你可以将人脸部照片放入 A 文件夹, 二次元的脸部照片放入 B 文件夹,不需要任何标注,就可以完成照片二次元相互转换模型数据集的整理。

进阶

了解了 Pipcook 风格转换的实现后,是不是有上手试一试的冲动呢?同学们可以尝试下面的训练集,也可以自己整理训练数据完成想要的风格变换模型。

总结

读者到这里已经学会如识使用 Pipcook 实现图片风格迁移,下一篇文章,我们将使用 Pipcook 来看看如何通过文本创作类模型来创作中文诗歌。