Turi Create 机器学习模型实战:你也能轻松做出Prisma 风格的图片!

如果你一直有关注Apple去年所发布的消息,就会知道他们在机器学习上投入了大量心力。自他们去年在WWDC 2017上推出Core ML以来,已经有大量结合机器学习技术的应用程序涌现。

但是,开发人员经常遇到的其中一个挑战是:如何创建模型?幸运的是,Apple在去年冬天宣布从GraphLab收购了Turi Create,正正解决了我们的问题。Turi Create是Apple的工具,可以帮助开发人员简化创建客制化模型的步骤。使用Turi Create,你可以建立自己的客制化机器学习模型。

Turi Create 快速入门

如果你有关注其他机器学习教学文章,你可能会觉得奇怪,「今年Apple不是有发布一个叫Create ML的工具吗?那相较于Create ML来说,Turi Create有什么优势?」

虽然对于刚开始研究机器学习的人来说,Create ML 是一个很好的工具,但它在使用方面严重受到限制,例如只能使用文本或图像数据。虽然这已经可以完成大多数的项目,但是对于稍微复杂的机器学习应用程序(例如风格转换(Style Transfer)), Create ML 就可能会变得毫无用处。

使用Turi Create,你除了可以创建所有原本使用Create ML创建出的Core ML模型之外,更能创造更多不同类型的模型!由于Turi Create比Create ML复杂得多,因此它与其他机器学习工具如KerasTensorFlow有高度的整合性。在我们的CreateML教学之中,你看到我们可以使用Create ML制作Core ML模型的类型。以下是你可以使用Turi Create制作的演算法类型:

你可以看到列表中包含了分类器与回归器(regressors),它们都可以使用Create ML 或Turi Create 来完成。这就是为什么Turi Create被更有经验的数据科学家所青睐,因为它提供了一种在Create ML中无法提供的可定制性。

什么是风格转换?

现在你大致了解到什么是Turi Create,那么让我们来看看什么是风格转换。风格转换是一种使用另一张图像风格将图像重新组合的技术,即是什么意思?看看下面利用Prisma 创造出来的图像:

Style Transfer Example

如你所见,上面早餐餐盘的图像风格转换成漫画了。由Gatys等人发表了一篇论文,描述如何使用卷积神经网路(Convolutional Neural Networks, CNNs)将一张图像的美术风格转换到另一张图像,风格转换就开始兴起。

卷积神经网路是一种机器学习的神经网路,通常应用于图像辨识及分类。它已经成功地解决电脑视觉方面的问题,例如:脸部辨识、物件辨识等。这是一个复杂的议题,所以我不会在这里讨论太多。

构建自己的风格转换应用程序

现在你已经了解了本教学涵盖到的工具和概念,我们终于可以开始了!我们将会利用Turi Create 构建自己的风格转换模型,并把它汇入iOS 项目来看看效果!

coreml-turi-create-1

首先,在这里下载起始项目,在本次的教学中我们将会用到Python 2、Jupyter Notebook和Xcode 9。

训练风格转换模型

Turi Create是一个Python套件,但它并没有内建在macOS里面,所以让我带你快速安装它。你的macOS应该已经安装了Python,若你的设备还没有安装Pythonpip,你可以在这里了解安装流程。

安装Turi Create 及Jupyter

打开终端机并输入下列指令:

pip install turicreate==5.0b2

Python套件安装过程大约1-2分钟。与此同时,我们可以下载Jupyter Notebook。Jupyter Notebook是一个供开发人员使用、支持许多语言的编译器,它包含丰富和互动的输出视觉效果。由于Turi Create仅支持Python 2,因此请在终端机输入以下命令以安装适用于Python 2的Jupyter Notebook。

python  -m  pip install  --upgrade pip

python  -m  pip install jupyter

coreml-turi-terminal-message

当所有的套件都安装好,就可以开始创造我们的演算法了!

使用Turi Create 撰写程序

我们即将构建的风格转换模型会以梵谷的作品星夜(Starry Night)为基础。简单来说,我们创造的模型可以将任何图像转换成星夜(Starry Night)风格的复制品。

starry-night-1093721_1280

首先,下载训练数据并解压,里面有一个content资料夹和一个style资料夹。打开content资料夹,你会看到大约有70张不同的图片。这个资料夹包含了各式各样的图片,这样就可以让我们的演算法知道有什么类型的图片需要做转换。因为我们想要转换所有图像,我们就需要有多样化的图片才行。

而在Style资料夹中就很简单地只有一张图片:StarryNight.jpg。这个资料夹包含了我们想要转换的美术风格来源。

现在,让我们打开Jupyter Notebook开始撰写代码。输入下列指令到终端机中:

jupyter notebook

这将会打开Safari 并显示这个页面:

coreml-turi-create-4

点击New按钮,然后按下Python 2!

备注:请确认你的Jupyter Notebook是在使用Python 2,这一点非常重要,因为目前Turi Create并不支持Python 3。译者注: Turi Create未来可能会支持Python 3

按下按钮后,将会弹出一个新页面,这就是我们要建立模型的地方。按下第一个Cell,并汇入Turi Create 套件:

import  turicreate as  tc

按下SHIFT+Enter 来执行这一个Cell 中的代码,等待套件汇入完成。下一步,来创建一个包含图像资料夹的参考。请确认你已经把代码中的参数设为资料夹的路径。

style  =  tc.load_images('/Path/To/Folder/style')

content  =  tc.load_images('/Path/To/Folder/content')

执行代码后,你应该会收到这样输出讯息:

coreml-turi-create-6

不用太担心这样的警告。接下来,我们将输入指令来创建风格转换模型。强烈建议你一台在拥有GPU运算资源的Mac上执行下列代码,像是最新的MacBook Pro或iMac。如果你选择在MacBook Air上执行,那么程序将会透过CPU来运算,这可能会花上好几天的时间。

model  =  tc.style_transfer.create(style,  content)

执行代码,这可能因为你的设备而花上一段很长的时间才能完成,像我在MacBook Air上透过CPU运算就花了3天半才完成。如果你没有足够的时间,不用担心,你可以在这里下载最后的Core ML模型(CoreML模型名为“StarryStyle”)。然而,可以的话你还是试试执行整个程序,感受一下它是怎样运作的!

coreml-turi-create-7

你可以看到表格中包含了三个栏位: Iteration(叠代次数)Loss(损失)Elapsed Time(花费时间)。在机器学习之中,会有特定函数执行多次向前和向后运算。当函数向前运算就是cost,往后运算就是loss。每次执行函数时,目的是调整参数来减少Loss。因此每次更改参数时,就会在增加一次Iteration,目标是为了得到更少的Loss。在训练的过程中,你可以发现Loss会渐渐地变少。而Elapsed time指的就是运算所消耗的时间。

当模型已经完成训练,只需要储存它就可以了!这可以简单地用一行代码来完成!

model.export_coreml("StarryStyle.mlmodel")

image

就这样完成了,你可以到函式库看看最终的模型!

coreml-turi-create-9

Xcode 项目概览

现在我们已经有了自己的模型,剩下来要做的就是将它汇入到Xcode 项目之中。打开Xcode 9 来看一下我们的项目。

turi create demo app

构建并执行项目,这样可以确认我们可以编译此项目。应用程序目前还未能运作,当你按下Van Gogh!按钮,你会发现什么事都没发生!现在,轮到我们来撰写代码了,让我们开始吧!

实战机器学习

首先,将我们的模型文件(即是StarryStyle.mlmodel)拖曳到项目之中,请确保你有勾选Copy Items If Needed,以及已经选了目标项目。

Import Core ML Model

接下来,我们需要在ViewController.swift加入代码来处理机器学习流程,大部分的代码会在transformImage()函数中撰写。让我们从汇入Core ML套件并调用模型开始吧!

import  CoreML

...

@IBAction func  transformImage(_  sender:  Any)  {

    // Style Transfer Here

    let  model  =  StarryStyle()

}

这行代码简单地将Core ML模型指定为叫做model的常数。

图像转换

下一步,我们需要将使用者所选取的图像转换成可读数据。再看看StarryStyle.mlmodel文件,你就会发现它接受的图像尺寸是256×256,因此我们必须执行转换。在我们的transformImage()函数下方加入一个新的函数。

func  pixelBuffer(from image:  UIImage)  ->  CVPixelBuffer?  {

    // 1

    UIGraphicsBeginImageContextWithOptions(CGSize(width:  256,  height:  256),  true,  2.0)

    image.draw(in:  CGRect(x:  0,  y:  0,  width:  256,  height:  256))

    let  newImage  =  UIGraphicsGetImageFromCurrentImageContext()!

    UIGraphicsEndImageContext()

    // 2

    let  attrs  =  [kCVPixelBufferCGImageCompatibilityKey:  kCFBooleanTrue,  kCVPixelBufferCGBitmapContextCompatibilityKey:  kCFBooleanTrue]  as  CFDictionary

    var  pixelBuffer  :  CVPixelBuffer?

    let  status  =  CVPixelBufferCreate(kCFAllocatorDefault,  256,  256,  kCVPixelFormatType_32ARGB,  attrs,  &pixelBuffer)

    guard  (status  ==  kCVReturnSuccess)  else  {

        return  nil

    }

    // 3

    CVPixelBufferLockBaseAddress(pixelBuffer!,  CVPixelBufferLockFlags(rawValue:  0))

    let  pixelData  =  CVPixelBufferGetBaseAddress(pixelBuffer!)

    // 4

    let  rgbColorSpace  =  CGColorSpaceCreateDeviceRGB()

    let  context  =  CGContext(data:  pixelData,  width:  256,  height:  256,  bitsPerComponent:  8,  bytesPerRow:  CVPixelBufferGetBytesPerRow(pixelBuffer!),  space:  rgbColorSpace,  bitmapInfo:  CGImageAlphaInfo.noneSkipFirst.rawValue)

    // 5

    context?.translateBy(x:  0,  y:  256)

    context?.scaleBy(x:  1.0,  y:  -1.0)

    // 6

    UIGraphicsPushContext(context!)

    image.draw(in:  CGRect(x:  0,  y:  0,  width:  256,  height:  256))

    UIGraphicsPopContext()

    CVPixelBufferUnlockBaseAddress(pixelBuffer!,  CVPixelBufferLockFlags(rawValue:  0))

    return  pixelBuffer

}

这是一个辅助函数(Helper Function),与我们之前Core ML教学文章所使用的函数有点相似。如果你已经忘了,别担心,让我一步一步解释这个函数。

  1. 因为我们的模型只能接受尺寸为256 x 256的图像,所以我们将图片转换为正方形,接着将正方形图像指定到另一个newImage的常数。
  2. 现在,我们将newImage转换成为CVPixelBuffer。如果你对CVPixelBuffer不熟悉,它基本上是一个图像缓冲区,用来将像素存于主要记忆体中。你可以在这里了解更多关于CVPixelBuffers的资讯。
  3. 取得图片中的所有像素后,我们将它转换成设备所对应的RGB色彩空间。接着,将所有数据创建为CGContext,当我们需要渲染(或改变)某些底层的属性时,就可以简单地调用它,这是我们在下列两行代码中透过转化及缩放图像所做的事。
  4. 最后,我们将图像内容放入当前内容中,渲染图像,并移除堆叠最上层的内容。当这些变更都完成后,回传像素缓冲器。

这其实是一些非常进阶的Core Image代码,已经超出了本篇教学文章的范围。如果有某些部分不了解其实不用担心。整段代码的主要目的,是藉由转换一张图像为像素缓冲器来提取它的数据,让Core ML可以更方便地读取它。

将风格转换应用于图像

现在我们有了Core ML辅助函数,让我们回到transformImage()并实战代码。在我们声明 model常数的那行下面,输入下列代码:

let  styleArray  =  try?  MLMultiArray(shape:  [1]  as  [NSNumber],  dataType:  .double)

styleArray?[0]  =  1.0

Turi Create允许你将多于一种「风格」打包到模型之中,虽然这次的项目只有一种风格,就是Starry Night。如果你想添加更多种风格,你可以加入更多图片到style资料夹中。我们将styleArray声明 为MLMultiArray,这是一种被Core ML所使用来作模型输入及输出的阵列型态。由于我们只有一种风格,所以只有一种形状及数据元素,因此我们将styleArray的数据元素设为1。

coreml-turi-create-14

最后,只需要利用我们的模型进行预测,并将结果设置为imageView

if  let  image  =  pixelBuffer(from:  imageView.image!)  {

    do  {

        let  predictionOutput  =  try  model.prediction(image:  image,  index:  styleArray!)

        let  ciImage  =  CIImage(cvPixelBuffer:  predictionOutput.stylizedImage)

        let  tempContext  =  CIContext(options:  nil)

        let  tempImage  =  tempContext.createCGImage(ciImage,  from:  CGRect(x:  0,  y:  0,  width:  CVPixelBufferGetWidth(predictionOutput.stylizedImage),  height:  CVPixelBufferGetHeight(predictionOutput.stylizedImage)))

        imageView.image  =  UIImage(cgImage:  tempImage!)

    }  catch  let  error  as  NSError  {

        print("CoreML Model Error: \(error)")

    }

}

这个函数首先检查imageView之中是否有图像。在这段代码中,我们先定义了predictionOutput用来储存模型预测的输出结果。我们以使用者的影像以及风格阵列作为参数,调用模型的prediction方法。预测的结果是像素缓冲器,但是我们无法将像素缓冲器设定为UIImageView,因此我们想出了一个非常有创意的方法来实现。

首先,我们将像素缓冲器predictionOutput.stylizedImage设置为CIImage类型的图像。然后,创建一个tempContext变量,它是CIContext的实例。我们调用context的内建函数(也就是createCGImage),它从ciImage产生CGImage。最后,我们可以将imageView设置为tempImage。这样就完成了!如果有任何错误,我们可以将错误印出来好好处理。

构建并执行项目。你可以从图库中选一张图片,然后测试应用程序!

Core ML Style Transfer Demo

你可能会注意到模型的输出结果看起来不太接近原本的Starry Night,而这种情况可以有很多原因。可能我们需要更多的训练数据?或是我们训练数据时需要更多次的叠代次数?我强烈的建议你回到前面几个步骤,再玩玩这些参数,直到你满意输出结果为止!

总结

教学文章就到此为止了!我已经向你介绍了Turi Create,并创造了你自己​​的风格转换模型,如果是在5 年前,一个人定必无法完成。你也学习到了如何将Core ML 模型汇入iOS 应用程序中,并有创意地应用它!

但是,风格转换只是一个开始。如我在前文提过,Turi Create 可以用来创造各类型的应用程序,下面是一些帮助你更进一步的资源:

如果需要完整的项目,请到GitHub下载。如果你有任何意见或问题,请在下面留言,与我分享你的想法。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏小红豆的数据分析

acmer之路(3)四月第一周日志

这一周加入了院足球队,好久都没有感受到在赛场上奔腾的感觉了。不过正好赶上清明,因此也算有充足的时间来码代码。这个月一共码了20题,排名终于冲进了四万名。

13110
来自专栏PPV课数据科学社区

【工具】用R软件绘制中国分省市地图

【注】新版本的maptools包对很多函数进行了修改,对于修改的内容,文章中用红色的文字进行了说明。 鉴于最近有不少人在讨论用R软件绘制地图的问题,我也就跟着凑...

54990
来自专栏简书专栏

Scipy入门

标题中的英文首字母大写比较规范,但在python实际使用中均为小写。 建议读者安装anaconda,这个集成开发环境自带了很多包。 作者推荐到2018年8月...

23010
来自专栏ATYUN订阅号

NLP项目:使用NLTK和SpaCy进行命名实体识别

命名实体识别(NER)是信息提取的第一步,旨在在文本中查找和分类命名实体转换为预定义的分类,例如人员名称,组织,地点,时间,数量,货币价值,百分比等。NER用于...

99440
来自专栏机器之心

资源 | Facebook开源DrQA的PyTorch实现:基于维基百科的问答系统

选自GitHub 机器之心编译 参与:Panda 今年 4 月,斯坦福大学和 Facebook 人工智能研究所在 arXiv 发布了一个基于维基百科的开放域问题...

47250
来自专栏数据小魔方

带实际执行进度的甘特图

今天要跟大家分享的图标是带实际执行进度的甘特图! ▽▼▽ 由于本图所用到的技巧和思路特别复杂,过程相对繁琐,所以本案例的介绍会省略掉很多细节性的步骤,否则图文会...

40250
来自专栏数据小魔方

R语言可视化——数据地图应用(东三省)

今天是一个案例应用,采用东北三省地图进行离散颜色映射,让大家感受下R语言在地理信息空间可视化方面的强大功能,同时也会对之前强调过的地图配色技巧进行应用。 加载工...

1.1K50
来自专栏BestSDK

Pytorch 0.3.0 发布:新增张量函数,支持模型移植

根据官方文档的介绍,此次增加了多个函数和功能,多方面的性能均实现提升。 重大变更 0.3 版本中删掉了 Variable.reinforce() 等随机函数,因...

37880
来自专栏逍遥剑客的游戏开发

有向无环图的自动布局算法

41550
来自专栏一心无二用,本人只专注于基础图像算法的实现与优化。

SSE图像算法优化系列二:高斯模糊算法的全面优化过程分享(二)。

      相关链接: 高斯模糊算法的全面优化过程分享(一)      在高斯模糊算法的全面优化过程分享(一)一文中我们已经给出了一种相当高性能的高斯模糊过程,...

46560

扫码关注云+社区

领取腾讯云代金券