如果你一直有關(guān)注Apple去年所發(fā)布的消息,就會(huì)知道他們?cè)跈C(jī)器學(xué)習(xí)上投入了大量心力。自他們?nèi)ツ暝赪WDC 2017上推出Core ML以來(lái)篙议,已經(jīng)有大量結(jié)合機(jī)器學(xué)習(xí)技術(shù)的應(yīng)用程序涌現(xiàn)癞松。
但是捅膘,開(kāi)發(fā)人員經(jīng)常遇到的其中一個(gè)挑戰(zhàn)是:如何創(chuàng)建模型翘地?幸運(yùn)的是申尤,Apple在去年冬天宣布從GraphLab收購(gòu)了Turi Create癌幕,正正解決了我們的問(wèn)題衙耕。Turi Create是Apple的工具,可以幫助開(kāi)發(fā)人員簡(jiǎn)化創(chuàng)建客制化模型的步驟勺远。使用Turi Create橙喘,你可以建立自己的客制化機(jī)器學(xué)習(xí)模型。
Turi Create 快速入門(mén)
如果你有關(guān)注其他機(jī)器學(xué)習(xí)教學(xué)文章胶逢,你可能會(huì)覺(jué)得奇怪厅瞎,「今年Apple不是有發(fā)布一個(gè)叫Create ML的工具嗎?那相較于Create ML來(lái)說(shuō)初坠,Turi Create有什么優(yōu)勢(shì)和簸?」
雖然對(duì)于剛開(kāi)始研究機(jī)器學(xué)習(xí)的人來(lái)說(shuō),Create ML 是一個(gè)很好的工具碟刺,但它在使用方面嚴(yán)重受到限制锁保,例如只能使用文本或圖像數(shù)據(jù)。雖然這已經(jīng)可以完成大多數(shù)的項(xiàng)目半沽,但是對(duì)于稍微復(fù)雜的機(jī)器學(xué)習(xí)應(yīng)用程序(例如風(fēng)格轉(zhuǎn)換(Style Transfer))爽柒, Create ML 就可能會(huì)變得毫無(wú)用處。
使用Turi Create者填,你除了可以創(chuàng)建所有原本使用Create ML創(chuàng)建出的Core ML模型之外浩村,更能創(chuàng)造更多不同類(lèi)型的模型!由于Turi Create比Create ML復(fù)雜得多占哟,因此它與其他機(jī)器學(xué)習(xí)工具如Keras和TensorFlow有高度的整合性心墅。在我們的CreateML教學(xué)之中,你看到我們可以使用Create ML制作Core ML模型的類(lèi)型榨乎。以下是你可以使用Turi Create制作的演算法類(lèi)型:
你可以看到列表中包含了分類(lèi)器與回歸器(regressors)怎燥,它們都可以使用Create ML 或Turi Create 來(lái)完成。這就是為什么Turi Create被更有經(jīng)驗(yàn)的數(shù)據(jù)科學(xué)家所青睞谬哀,因?yàn)樗峁┝艘环N在Create ML中無(wú)法提供的可定制性刺覆。
什么是風(fēng)格轉(zhuǎn)換?
現(xiàn)在你大致了解到什么是Turi Create史煎,那么讓我們來(lái)看看什么是風(fēng)格轉(zhuǎn)換谦屑。風(fēng)格轉(zhuǎn)換是一種使用另一張圖像風(fēng)格將圖像重新組合的技術(shù)驳糯,即是什么意思?看看下面利用Prisma 創(chuàng)造出來(lái)的圖像:
如你所見(jiàn)氢橙,上面早餐餐盤(pán)的圖像風(fēng)格轉(zhuǎn)換成漫畫(huà)了酝枢。由Gatys等人發(fā)表了一篇論文,描述如何使用卷積神經(jīng)網(wǎng)路(Convolutional Neural Networks, CNNs)將一張圖像的美術(shù)風(fēng)格轉(zhuǎn)換到另一張圖像悍手,風(fēng)格轉(zhuǎn)換就開(kāi)始興起帘睦。
卷積神經(jīng)網(wǎng)路是一種機(jī)器學(xué)習(xí)的神經(jīng)網(wǎng)路,通常應(yīng)用于圖像辨識(shí)及分類(lèi)坦康。它已經(jīng)成功地解決電腦視覺(jué)方面的問(wèn)題竣付,例如:臉部辨識(shí)、物件辨識(shí)等滞欠。這是一個(gè)復(fù)雜的議題古胆,所以我不會(huì)在這里討論太多。
構(gòu)建自己的風(fēng)格轉(zhuǎn)換應(yīng)用程序
現(xiàn)在你已經(jīng)了解了本教學(xué)涵蓋到的工具和概念筛璧,我們終于可以開(kāi)始了逸绎!我們將會(huì)利用Turi Create 構(gòu)建自己的風(fēng)格轉(zhuǎn)換模型,并把它匯入iOS 項(xiàng)目來(lái)看看效果夭谤!
首先棺牧,在這里下載起始項(xiàng)目,在本次的教學(xué)中我們將會(huì)用到Python 2朗儒、Jupyter Notebook和Xcode 9颊乘。
訓(xùn)練風(fēng)格轉(zhuǎn)換模型
Turi Create是一個(gè)Python套件,但它并沒(méi)有內(nèi)建在macOS里面采蚀,所以讓我?guī)憧焖侔惭b它疲牵。你的macOS應(yīng)該已經(jīng)安裝了Python,若你的設(shè)備還沒(méi)有安裝Python
或pip
榆鼠,你可以在這里了解安裝流程纲爸。
安裝Turi Create 及Jupyter
打開(kāi)終端機(jī)并輸入下列指令:
pip install turicreate==5.0b2
Python套件安裝過(guò)程大約1-2分鐘。與此同時(shí)妆够,我們可以下載Jupyter Notebook识啦。Jupyter Notebook是一個(gè)供開(kāi)發(fā)人員使用、支持許多語(yǔ)言的編譯器神妹,它包含豐富和互動(dòng)的輸出視覺(jué)效果颓哮。由于Turi Create僅支持Python 2,因此請(qǐng)?jiān)诮K端機(jī)輸入以下命令以安裝適用于Python 2的Jupyter Notebook鸵荠。
python -m pip install --upgrade pip
python -m pip install jupyter
當(dāng)所有的套件都安裝好冕茅,就可以開(kāi)始創(chuàng)造我們的演算法了!
使用Turi Create 撰寫(xiě)程序
我們即將構(gòu)建的風(fēng)格轉(zhuǎn)換模型會(huì)以梵谷的作品星夜(Starry Night)為基礎(chǔ)。簡(jiǎn)單來(lái)說(shuō),我們創(chuàng)造的模型可以將任何圖像轉(zhuǎn)換成星夜(Starry Night)風(fēng)格的復(fù)制品姨伤。
首先哨坪,下載訓(xùn)練數(shù)據(jù)并解壓,里面有一個(gè)content
資料夾和一個(gè)style
資料夾乍楚。打開(kāi)content
資料夾当编,你會(huì)看到大約有70張不同的圖片。這個(gè)資料夾包含了各式各樣的圖片徒溪,這樣就可以讓我們的演算法知道有什么類(lèi)型的圖片需要做轉(zhuǎn)換忿偷。因?yàn)槲覀兿胍D(zhuǎn)換所有圖像,我們就需要有多樣化的圖片才行臊泌。
而在Style
資料夾中就很簡(jiǎn)單地只有一張圖片:StarryNight.jpg鲤桥。這個(gè)資料夾包含了我們想要轉(zhuǎn)換的美術(shù)風(fēng)格來(lái)源。
現(xiàn)在缺虐,讓我們打開(kāi)Jupyter Notebook
開(kāi)始撰寫(xiě)代碼芜壁。輸入下列指令到終端機(jī)中:
jupyter notebook
這將會(huì)打開(kāi)Safari 并顯示這個(gè)頁(yè)面:
點(diǎn)擊New
按鈕,然后按下Python 2高氮!
備注:請(qǐng)確認(rèn)你的Jupyter Notebook是在使用Python 2,這一點(diǎn)非常重要顷牌,因?yàn)槟壳癟uri Create并不支持Python 3剪芍。譯者注: Turi Create未來(lái)可能會(huì)支持Python 3。
按下按鈕后窟蓝,將會(huì)彈出一個(gè)新頁(yè)面罪裹,這就是我們要建立模型的地方。按下第一個(gè)Cell运挫,并匯入Turi Create 套件:
import turicreate as tc
按下SHIFT+Enter 來(lái)執(zhí)行這一個(gè)Cell 中的代碼状共,等待套件匯入完成。下一步谁帕,來(lái)創(chuàng)建一個(gè)包含圖像資料夾的參考峡继。請(qǐng)確認(rèn)你已經(jīng)把代碼中的參數(shù)設(shè)為資料夾的路徑。
style = tc.load_images('/Path/To/Folder/style')
content = tc.load_images('/Path/To/Folder/content')
執(zhí)行代碼后匈挖,你應(yīng)該會(huì)收到這樣輸出訊息:
不用太擔(dān)心這樣的警告碾牌。接下來(lái),我們將輸入指令來(lái)創(chuàng)建風(fēng)格轉(zhuǎn)換模型儡循。強(qiáng)烈建議你一臺(tái)在擁有GPU運(yùn)算資源的Mac上執(zhí)行下列代碼舶吗,像是最新的MacBook Pro或iMac。如果你選擇在MacBook Air上執(zhí)行择膝,那么程序?qū)?huì)透過(guò)CPU來(lái)運(yùn)算誓琼,這可能會(huì)花上好幾天的時(shí)間。
model = tc.style_transfer.create(style, content)
執(zhí)行代碼,這可能因?yàn)槟愕脑O(shè)備而花上一段很長(zhǎng)的時(shí)間才能完成腹侣,像我在MacBook Air上透過(guò)CPU運(yùn)算就花了3天半才完成呵扛。如果你沒(méi)有足夠的時(shí)間,不用擔(dān)心筐带,你可以在這里下載最后的Core ML模型(CoreML模型名為“StarryStyle”)今穿。然而,可以的話你還是試試執(zhí)行整個(gè)程序伦籍,感受一下它是怎樣運(yùn)作的蓝晒!
你可以看到表格中包含了三個(gè)欄位: Iteration(疊代次數(shù))、Loss(損失)和Elapsed Time(花費(fèi)時(shí)間)帖鸦。在機(jī)器學(xué)習(xí)之中芝薇,會(huì)有特定函數(shù)執(zhí)行多次向前和向后運(yùn)算。當(dāng)函數(shù)向前運(yùn)算就是cost作儿,往后運(yùn)算就是loss洛二。每次執(zhí)行函數(shù)時(shí),目的是調(diào)整參數(shù)來(lái)減少Loss攻锰。因此每次更改參數(shù)時(shí)晾嘶,就會(huì)在增加一次Iteration,目標(biāo)是為了得到更少的Loss娶吞。在訓(xùn)練的過(guò)程中垒迂,你可以發(fā)現(xiàn)Loss會(huì)漸漸地變少。而Elapsed time指的就是運(yùn)算所消耗的時(shí)間妒蛇。
當(dāng)模型已經(jīng)完成訓(xùn)練机断,只需要儲(chǔ)存它就可以了!這可以簡(jiǎn)單地用一行代碼來(lái)完成绣夺!
model.export_coreml("StarryStyle.mlmodel")
就這樣完成了吏奸,你可以到函式庫(kù)看看最終的模型!
Xcode 項(xiàng)目概覽
現(xiàn)在我們已經(jīng)有了自己的模型陶耍,剩下來(lái)要做的就是將它匯入到Xcode 項(xiàng)目之中奋蔚。打開(kāi)Xcode 9 來(lái)看一下我們的項(xiàng)目。
構(gòu)建并執(zhí)行項(xiàng)目物臂,這樣可以確認(rèn)我們可以編譯此項(xiàng)目旺拉。應(yīng)用程序目前還未能運(yùn)作,當(dāng)你按下Van Gogh!
按鈕棵磷,你會(huì)發(fā)現(xiàn)什么事都沒(méi)發(fā)生蛾狗!現(xiàn)在,輪到我們來(lái)撰寫(xiě)代碼了仪媒,讓我們開(kāi)始吧沉桌!
實(shí)戰(zhàn)機(jī)器學(xué)習(xí)
首先谢鹊,將我們的模型文件(即是StarryStyle.mlmodel
)拖曳到項(xiàng)目之中,請(qǐng)確保你有勾選Copy Items If Needed
留凭,以及已經(jīng)選了目標(biāo)項(xiàng)目佃扼。
接下來(lái),我們需要在ViewController.swift
加入代碼來(lái)處理機(jī)器學(xué)習(xí)流程蔼夜,大部分的代碼會(huì)在transformImage()
函數(shù)中撰寫(xiě)兼耀。讓我們從匯入Core ML套件并調(diào)用模型開(kāi)始吧!
import CoreML
...
@IBAction func transformImage(_ sender: Any) {
// Style Transfer Here
let model = StarryStyle()
}
這行代碼簡(jiǎn)單地將Core ML模型指定為叫做model
的常數(shù)。
圖像轉(zhuǎn)換
下一步求冷,我們需要將使用者所選取的圖像轉(zhuǎn)換成可讀數(shù)據(jù)瘤运。再看看StarryStyle.mlmodel
文件,你就會(huì)發(fā)現(xiàn)它接受的圖像尺寸是256×256匠题,因此我們必須執(zhí)行轉(zhuǎn)換拯坟。在我們的transformImage()
函數(shù)下方加入一個(gè)新的函數(shù)。
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
}
這是一個(gè)輔助函數(shù)(Helper Function)韭山,與我們之前Core ML教學(xué)文章所使用的函數(shù)有點(diǎn)相似郁季。如果你已經(jīng)忘了,別擔(dān)心钱磅,讓我一步一步解釋這個(gè)函數(shù)梦裂。
- 因?yàn)槲覀兊哪P椭荒芙邮艹叽鐬?code>256 x 256的圖像,所以我們將圖片轉(zhuǎn)換為正方形续搀,接著將正方形圖像指定到另一個(gè)
newImage
的常數(shù)塞琼。 - 現(xiàn)在,我們將
newImage
轉(zhuǎn)換成為CVPixelBuffer
禁舷。如果你對(duì)CVPixelBuffer
不熟悉,它基本上是一個(gè)圖像緩沖區(qū)毅往,用來(lái)將像素存于主要記憶體中牵咙。你可以在這里了解更多關(guān)于CVPixelBuffers
的資訊。 - 取得圖片中的所有像素后攀唯,我們將它轉(zhuǎn)換成設(shè)備所對(duì)應(yīng)的RGB色彩空間洁桌。接著,將所有數(shù)據(jù)創(chuàng)建為
CGContext
侯嘀,當(dāng)我們需要渲染(或改變)某些底層的屬性時(shí)另凌,就可以簡(jiǎn)單地調(diào)用它,這是我們?cè)谙铝袃尚写a中透過(guò)轉(zhuǎn)化及縮放圖像所做的事戒幔。 - 最后吠谢,我們將圖像內(nèi)容放入當(dāng)前內(nèi)容中,渲染圖像诗茎,并移除堆疊最上層的內(nèi)容工坊。當(dāng)這些變更都完成后,回傳像素緩沖器。
這其實(shí)是一些非常進(jìn)階的Core Image
代碼王污,已經(jīng)超出了本篇教學(xué)文章的范圍罢吃。如果有某些部分不了解其實(shí)不用擔(dān)心。整段代碼的主要目的昭齐,是藉由轉(zhuǎn)換一張圖像為像素緩沖器來(lái)提取它的數(shù)據(jù)尿招,讓Core ML可以更方便地讀取它。
將風(fēng)格轉(zhuǎn)換應(yīng)用于圖像
現(xiàn)在我們有了Core ML輔助函數(shù)阱驾,讓我們回到transformImage()
并實(shí)戰(zhàn)代碼就谜。在我們聲明 model
常數(shù)的那行下面,輸入下列代碼:
let styleArray = try? MLMultiArray(shape: [1] as [NSNumber], dataType: .double)
styleArray?[0] = 1.0
Turi Create允許你將多于一種「風(fēng)格」打包到模型之中啊易,雖然這次的項(xiàng)目只有一種風(fēng)格吁伺,就是Starry Night。如果你想添加更多種風(fēng)格租谈,你可以加入更多圖片到style
資料夾中篮奄。我們將styleArray
聲明 為MLMultiArray,這是一種被Core ML所使用來(lái)作模型輸入及輸出的陣列型態(tài)割去。由于我們只有一種風(fēng)格窟却,所以只有一種形狀及數(shù)據(jù)元素,因此我們將styleArray
的數(shù)據(jù)元素設(shè)為1呻逆。
最后夸赫,只需要利用我們的模型進(jìn)行預(yù)測(cè),并將結(jié)果設(shè)置為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)")
}
}
這個(gè)函數(shù)首先檢查imageView
之中是否有圖像茬腿。在這段代碼中,我們先定義了predictionOutput
用來(lái)儲(chǔ)存模型預(yù)測(cè)的輸出結(jié)果宜雀。我們以使用者的影像以及風(fēng)格陣列作為參數(shù)切平,調(diào)用模型的prediction
方法。預(yù)測(cè)的結(jié)果是像素緩沖器辐董,但是我們無(wú)法將像素緩沖器設(shè)定為UIImageView
悴品,因此我們想出了一個(gè)非常有創(chuàng)意的方法來(lái)實(shí)現(xiàn)。
首先简烘,我們將像素緩沖器predictionOutput.stylizedImage
設(shè)置為CIImage
類(lèi)型的圖像苔严。然后,創(chuàng)建一個(gè)tempContext
變量孤澎,它是CIContext
的實(shí)例届氢。我們調(diào)用context的內(nèi)建函數(shù)(也就是createCGImage
),它從ciImage
產(chǎn)生CGImage
亥至。最后悼沈,我們可以將imageView
設(shè)置為tempImage
贱迟。這樣就完成了!如果有任何錯(cuò)誤絮供,我們可以將錯(cuò)誤印出來(lái)好好處理衣吠。
構(gòu)建并執(zhí)行項(xiàng)目。你可以從圖庫(kù)中選一張圖片壤靶,然后測(cè)試應(yīng)用程序缚俏!
你可能會(huì)注意到模型的輸出結(jié)果看起來(lái)不太接近原本的Starry Night,而這種情況可以有很多原因贮乳∮腔唬可能我們需要更多的訓(xùn)練數(shù)據(jù)?或是我們訓(xùn)練數(shù)據(jù)時(shí)需要更多次的疊代次數(shù)向拆?我強(qiáng)烈的建議你回到前面幾個(gè)步驟亚茬,再玩玩這些參數(shù),直到你滿意輸出結(jié)果為止浓恳!
總結(jié)
教學(xué)文章就到此為止了刹缝!我已經(jīng)向你介紹了Turi Create,并創(chuàng)造了你自己??的風(fēng)格轉(zhuǎn)換模型颈将,如果是在5 年前梢夯,一個(gè)人定必?zé)o法完成。你也學(xué)習(xí)到了如何將Core ML 模型匯入iOS 應(yīng)用程序中晴圾,并有創(chuàng)意地應(yīng)用它颂砸!
但是,風(fēng)格轉(zhuǎn)換只是一個(gè)開(kāi)始死姚。如我在前文提過(guò)人乓,Turi Create 可以用來(lái)創(chuàng)造各類(lèi)型的應(yīng)用程序,下面是一些幫助你更進(jìn)一步的資源:
- Apple's Gitbook on Turi Create Applications
- A Guide to Turi Create – WWDC 2018
- Turi Create Repository
如果需要完整的項(xiàng)目都毒,請(qǐng)到GitHub下載撒蟀。如果你有任何意見(jiàn)或問(wèn)題,請(qǐng)?jiān)谙旅媪粞晕赂耄c我分享你的想法。
原文:Creating a Prisma-like App with Core ML, Style Transfer and Turi Create
作者: