Core ML 教學:如何利用 Python 和 Turi Create 來建立自訂模型


近年,越來越多人應用機器學習 (Machine Learning) 演算法,以解決問題或執行複雜的功能。利用機器學習,我們可以使用大數據來執行複雜的功能,例如圖像分類與語音辨識。

此外,Apple 最近公佈了一個框架 Core ML,以簡化將機器學習模型整合在 macOS 、 iOS 、 tvOS 與 watchOS 裝置上的程序。Apple 也提供了 Code ML 模型樣板來測試這個框架。

首次公佈 Core ML 時,軟體開發者要建立自己的 Core ML 模型是挺困難的,因為開發者需要先具備機器學習的經驗。

然而,感謝 GraphLab 和 Apple,現在我們有了 Turi Create,一個讓我們可以很簡單地建立 Core ML 模型的框架。Turi Create 提供必須的機器學習演算法例如 K-nearest neighbor (KNN),和進階的深度學習演算法如 Residual Networks (ResNet),以協助我們建立自己的機器學習模型。

在本教學中,我們會示範如何建立一個自訂圖像分類 Core ML 模型,並將它整合至一個 iOS 應用程式中。過程中我們會用到 Python 2.7Turi CreateSwift 4.0 、和 Core ML

在開始前,我們需要:

目前 Turi Create 只能支援 Python 2.7

教學開始

首先,我們要利用 Python package manager pip 來安裝 Turi Create。在你安裝 Python 時,這個 Package manager 會同時安裝在你的電腦內。

要安裝 Turi Create,請開啟 Terminal.app,並輸入下列指令:

$ pip install turicreate

安裝好 Python package 後,我們將以下列步驟來建立一個新的 Python 專案:

  1. 建立一個資料夾,命名為 MLClassifier
  2. 開啟 Xcode,建立一個新檔案
  3. xcode-create-new-file

  4. 從任何裝置下選擇一個空白 (Empty) 檔案
  5. xcode-empty-file

  6. 將檔案命名為 classifier.py
  7. 將檔案儲存到 MLClassifier 資料夾

建立 DataSet

在這個範例中,我們將建立一個機器學習模型,以飯和湯兩個類別來分類圖片。所以,我們需要建立一個有這兩類圖片 Dataset。這個 Dataset 是用來訓練模型的,你可以下載我正在用的 Dataset,並儲存到MLClassifier資料夾,或是依照下列步驟建立自己的 Dataset:

  1. MLClassifier 資料夾內,建立一個新資料夾 dataset
  2. dataset 資料夾內,建立兩個新資料夾,分別命名為 ricesoup
  3. rice 資料夾內,增加至少 100 張飯的圖片。
  4. soup 資料夾內,增加至少 100 張湯的圖片。

你的 dataset 資料夾應該像這樣:

classifier-folders

實作 Turi Create

Turi Create 簡化了開發自訂機器學習模型的過程。你不需要機器學習的經驗,都可以為你的 App 增加建議 (Recommendations) 、物件偵測 (Object detection) 、圖像分類 (Image classification) 、圖像相似度 (Image similarity) 、或是活動分類 (Activity classification) 等功能。

  • Turi Create 官方文件 (https://github.com/apple/turicreate)

現在,我們將 turicreate 嵌入到 Python 專案,並標註剛剛建立的 Dataset 內每張圖片。跟著以下的步驟來實作與設定這個框架:

  1. 打開 classifier.py 檔案,並加入下列指令來匯入 turicreate
  2. import turicreate as turi
    
  3. 設定 Dataset 的路徑:
  4. url = "dataset/"
    
  5. 然後,加入下列指令來讀取 Dataset 資料夾的圖像:
  6. data = turi.image_analysis.load_images(url)
    
  7. 繼續加入下列程式碼,以根據同一個資料夾路徑來定義圖像類別:
  8. data["foodType"] = data["path"].apply(lambda path: "Rice" if "rice" in path else "Soup")
    

    > 圖像分類的目標,就是要將一個圖像分類到某個預先設定的標籤。

  9. 最後,將新標註好的 Dataset 儲存為 rice_or_soup.sframe,我們將使用它來訓練模型:
  10. data.save("rice_or_soup.sframe")
    
  11. 在 Turi Create 預覽新標籤的 Dataset:
  12. data.explore()
    

訓練並輸出機器學習模型

是時候訓練並輸出機器學習模型來使用了!

我們將使用 turicreate 提供的其中一個架構 SqueezeNet 來訓練機器學習模型。

使用 SqueezeNet,是因為它不需要太多時間來訓練;然而,若要取得更準確的結果,我建議用 ResNet-50。接下來的實作步驟,兩種架構都適用:

  1. classifier.py 檔案內,讀取之前儲存了的 rice_or_soup.sframe。請在檔案加入下列程式碼:
  2. dataBuffer = turi.SFrame("rice_or_soup.sframe")
    
  3. 接著,我們剛設定的 dataBuffer 物件 90% 設為訓練 Data,剩下的 10% 為測試 Data:
  4. trainingBuffers, testingBuffers = dataBuffer.random_split(0.9)
    
  5. 繼續插入下列程式碼,使用訓練 Data 和 SqueezeNet 架構建立圖片分類功能:
  6. model = turi.image_classifier.create(trainingBuffers, target="foodType", model="squeezenet_v1.1")
    

    你也可以使用 ResNet-50,以得到更準確的結果:

    model = turi.image_classifier.create(trainingBuffers, target="foodType", model="resnet-50")
    
  7. 接著,我們利用測試 Data 來評估模型的準確度:
  8. evaluations = model.evaluate(testingBuffers)
    print evaluations["accuracy"]
    
  9. 最後,插入下列程式碼來儲存模型,並輸出圖像分類模型為 CoreML 模型:
  10. model.save("rice_or_soup.model")
    model.export_coreml("RiceSoupClassifier.mlmodel")
    

你的 classifier.py 檔案應該如下圖所示:

python-ml

現在是時候來執行我們的程式了!請開啟 Terminal.app,將路徑設到 MLClassifier 資料夾,然後這樣執行 classifier.py 檔案:

python classifier.py

你應該會看到下列訊息,請耐心等候。

building-ml-model

幾分鐘後,你的 CoreML 模型就準備好,可以實現在任何 iOS 、 macOS 、 tvOS 、 或 watchOS 的應用程式上了!

整合 Core ML 模型到 iOS App

現在,我們準備要整合剛建立的自訂 CoreML 模型到 iOS App。如果你有讀過我們的 Core ML 入門介紹「初探 Core ML:學習建立一個圖像識別 App」,你應該大概知道如何將 CoreML 模型整合到 iOS App 內,所以這個部份我會精簡說明。

  1. 首先,建立一個新的 iOS 專案,並使用 Single Application 的模版。你可以自訂一個專案名字,但要確認你是使用 Swift 來做本次範例開發。
  2. 要應用剛建立的模型,我們要將訓練好的 CoreML 模型 (RiceSoupClassifier.mlmodel 檔案) 拖拉到專案中。
  3. add-core ml-model

  4. 匯入 CoreML 框架到你的專案,然後將下列程式碼加到 ViewController.swift 檔案內:
  5. import CoreML
    
  6. 建立 CoreML 模型物件:
  7. let mlModel = RiceSoupClassifier()
    
  8. 建立使用者介面:
  9.     var importButton:UIButton = {
            let btn = UIButton(type: .system)
            btn.setTitle("Import", for: .normal)
            btn.setTitleColor(.white, for: .normal)
            btn.backgroundColor = .black
            btn.frame = CGRect(x: 0, y: 0, width: 110, height: 60)
            btn.center = CGPoint(x: UIScreen.main.bounds.width/2, y: UIScreen.main.bounds.height*0.90)
            btn.layer.cornerRadius = btn.bounds.height/2
            btn.tag = 0
            return btn
        }()
        
        var previewImg:UIImageView = {
            let img = UIImageView()
            img.frame = CGRect(x: 0, y: 0, width: 350, height: 350)
            img.contentMode = .scaleAspectFit
            img.center = CGPoint(x: UIScreen.main.bounds.width/2, y: UIScreen.main.bounds.height/3)
            return img
        }()
        
        var descriptionLbl:UILabel = {
            let lbl = UILabel()
            lbl.text = "No Image Content"
            lbl.frame = CGRect(x: 0, y: 0, width: 350, height: 50)
            lbl.textColor = .black
            lbl.textAlignment = .center
            lbl.center = CGPoint(x: UIScreen.main.bounds.width/2, y: UIScreen.main.bounds.height/1.5)
            return lbl
        }()
    
  10. ViewController 加入 UIImagePickerController 協定:
  11. class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate
    
  12. 建立一個 importFromCameraRoll 方法:
  13. @objc func importFromCameraRoll() {
        if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
            let imagePicker = UIImagePickerController()
            imagePicker.delegate = self
            imagePicker.sourceType = .photoLibrary;
            imagePicker.allowsEditing = true
            self.present(imagePicker, animated: true, completion: nil)
        }
    }
    
  14. viewDidLoad 方法內,將 importFromCameraRoll 方法連接到匯入按鈕,並把按鈕加到 Subview:
  15. importButton.addTarget(self, action: #selector(importFromCameraRoll), for: .touchUpInside)
    self.view.addSubview(previewImg)
    self.view.addSubview(descriptionLbl)
    self.view.addSubview(importButton)
    
  16. 然後,建立一個 UIImage 的 Extension,並加入下列功能將 UIImage 物件轉換至 CVPixelBuffer
  17. extension UIImage {
        func buffer(with size:CGSize) -> CVPixelBuffer? {
            if let image = self.cgImage {
                let frameSize = size
                var pixelBuffer:CVPixelBuffer? = nil
                let status = CVPixelBufferCreate(kCFAllocatorDefault, Int(frameSize.width), Int(frameSize.height), kCVPixelFormatType_32BGRA , nil, &pixelBuffer)
                if status != kCVReturnSuccess {
                    return nil
                }
                CVPixelBufferLockBaseAddress(pixelBuffer!, CVPixelBufferLockFlags.init(rawValue: 0))
                let data = CVPixelBufferGetBaseAddress(pixelBuffer!)
                let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
                let bitmapInfo = CGBitmapInfo(rawValue: CGBitmapInfo.byteOrder32Little.rawValue | CGImageAlphaInfo.premultipliedFirst.rawValue)
                let context = CGContext(data: data, width: Int(frameSize.width), height: Int(frameSize.height), bitsPerComponent: 8, bytesPerRow: CVPixelBufferGetBytesPerRow(pixelBuffer!), space: rgbColorSpace, bitmapInfo: bitmapInfo.rawValue)
                context?.draw(image, in: CGRect(x: 0, y: 0, width: image.width, height: image.height))
                CVPixelBufferUnlockBaseAddress(pixelBuffer!, CVPixelBufferLockFlags(rawValue: 0))
                
                return pixelBuffer
            }else{
                return nil
            }
        }
    }
    
  18. 將下列方法加入到 ViewController,以分析匯入的圖像,並利用 UILabel 來顯示分析結果:
  19. func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
        if let image = info[UIImagePickerControllerOriginalImage] as? UIImage {
            previewImg.image = image
            if let buffer = image.buffer(with: CGSize(width:224, height:224)) {
                guard let prediction = try? mlModel.prediction(image: buffer) else {fatalError("Unexpected runtime error")}
                descriptionLbl.text = prediction.foodType
                print(prediction.foodTypeProbability)
            }else{
                print("failed buffer")
            }
        }
        dismiss(animated:true, completion: nil)
    }
    

準備測試

好了,都完成了!執行 App,並按下 Import 按鈕來試一下吧。現在,你應該了解如何建立一個自訂機器學習模型,並實作到你的應用程式中了!

coreml-demo

你可以在Github下載完整的教學專案。如果你對本篇教學有任何問題或想法,歡迎在下面留言告訴我。

譯者簡介:Oliver Chen-工程師,喜歡美麗的事物,所以也愛上 Apple,目前在 iOS 程式設計上仍是新手,正研讀 Swift 與 Sketch 中。生活另一個身份是兩個孩子的爸,喜歡和孩子一起玩樂高,幻想著某天自己開發的 App,可以讓孩子覺得老爸好棒!。聯絡方式:電郵 [email protected]

原文Creating a Custom Core ML Model Using Python and Turi Create


充滿熱誠的軟體開發員,享受開發軟體解決問題,讓我們的生活更容易。曾經連續三年獲得 Apple 全球開發者大會 (WWDC) 的學生獎學金,現在在 Magic.af (http://magic.af) 與他的團隊工作,開發手機軟體給影片創作人。

blog comments powered by Disqus
Shares
Share This