+4

Tìm hiểu về CoreML: machine learning trên iOS

I. Giới thiệu

Trong những năm trở lại đây, machine learning nổi lên là một lĩnh vực khá hot trong thế giới công nghệ thông tin. Nguồn nhân lực đổ vào nghiên cứu machine learning là rất lớn, mỗi năm có hàng nghìn bài báo nghiên cứu khoa học được giới thiệu, các công ty công nghệ lớn nhất thế giới như Google, Microsoft, Apple,... cũng dành rất nhiều tài lực vào nghiên cứu lĩnh vực này.

Trước đây, việc áp dụng machine learning lên ứng dụng mobile là một việc khá khó khăn, các lập trình viên phải sử dụng thư viện của các bên thứ 3, gửi các api lên server của các bên khác để thực hiện quá trình học máy và trả về kết quả. quá trình này sử dụng request mạng, làm giảm trải nghiệm của người dùng. Tuy nhiên, trong WWDC 2017, Apple đã mang tới cho giới lập trình viên 1 công cụ tuyệt vời: CoreML framework. Đây là một framework tuyệt vời, giúp chúng ta có thể tích hợp các mô hình học máy ứng dụng iOS. Điều tuyệt vời nhất của thư viện này là ở chỗ nó cung cấp cho chúng ta các API rất đơn giản để làm việc, chúng ta không cần phải có những kiến thức cao siêu của machine learning, về học máy, về mạng nơron, ...

Trong bài viết này, tôi sẽ giới thiệu đến các bạn về thư viện CoreML này. Thông qua bài viết này, chúng ta sẽ tạo một ứng dụng nhận diện ảnh, với đầu vào là 1 ảnh của 1 vật, và đầu ra là dự đoán của máy về vật đó là gì. ok, let's start!

II. Nội dung

1. Tạo project

Trong bài viết này chúng ta tập chung tìm hiểu về CoreML, vì vậy chúng ta sẽ không đi sâu về những vấn đề khác. Việc tạo template project cũng khá đơn giản, các bạn cần những kiến thức cơ bản về ios là có thể làm được:

  • Đầu tiên, chúng ta tạo project với tên CoreMLTutorial, ngôn ngữ Swift.
  • Tiếp theo chúng ta vào storyboard, nhúng ViewController vào UINavigationController, thêm 2 UIBarButtonItem vào 2 bên trái phải của Navigation Item, đổi tên nút tuỳ thích hoặc như mình bên hình dưới (đặt tên sao cũng được không quan trọng)
  • Thêm 1 UIImageView vào chính giữa màn của ViewController, thêm constraint cho imageView đó với height và width là 299, và căn chính giữa với super view
  • Thêm 1 UILabel phía dưới UIImageView bên trên, thêm constraint sao cho đẹp và build không vỡ layout tuỳ ý các bạn
  • kéo các IBOutlet cho UIImageView và UILabel, kéo IBAction cho 2 nút trái phải của Navigation Item vào ViewController.swift như code bên dưới đây.

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var imageView: UIImageView!
    
    @IBOutlet weak var resultLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
    }
    
    @IBAction func cameraButtonClicked(_ sender: Any) {
        
    }
    
    @IBAction func addButtonClicked(_ sender: Any) {
        
    }
    
}

OK, chúng ta đã có 2 nút camera và add để thêm ảnh từ camera hoặc photo libraty của máy, có 1 imageView để hiển thị ảnh đã chọn, ảnh này sẽ làm đầu vào cho CoreML để nhận diện ảnh, và có 1 resultLabel để hiển thị đầu ra của CoreML.

2. Thêm code cơ bản

Bây giờ chúng ta sẽ thêm code cho các hàm lấy ảnh, vì bài này chỉ tập chung vào CoreML nên chúng ta chỉ nói qua trong phần này. Các bạn implement ViewController.swift như sau:

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var imageView: UIImageView!
    
    @IBOutlet weak var resultLabel: UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
    }
    
    @IBAction func cameraButtonClicked(_ sender: Any) {
        // 1
        if !UIImagePickerController.isSourceTypeAvailable(.camera) {
            return
        }
        
        let cameraPicker = UIImagePickerController()
        cameraPicker.delegate = self
        cameraPicker.sourceType = .camera
        cameraPicker.allowsEditing = false
        
        present(cameraPicker, animated: true)
    }
    
    @IBAction func addButtonClicked(_ sender: Any) {
        // 2
        let picker = UIImagePickerController()
        picker.allowsEditing = false
        picker.delegate = self
        picker.sourceType = .photoLibrary
        present(picker, animated: true)
    }
}

extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
        // 3
        dismiss(animated: true, completion: nil)
    }
    
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
        // 4
        picker.dismiss(animated: true)
        guard let image = info["UIImagePickerControllerOriginalImage"] as? UIImage else {
            return
        }
        
        imageView.image = image
        resultLabel.text = "Analyzing Image..."
    }
}

Bên trên chúng ta lần lượt làm những việc sau:

  • 1: Implement hàm cameraButtonClicked(_ 😃 để mở camera và chụp ảnh
  • 2: Implement hàm addButtonClicked(_ 😃 để vào photo library chọn ảnh
  • 3: xử lý hàm của UIImagePickerControllerDelegate khi người dùng cancel chụp ảnh/chọn ảnh
  • 4: xử lý khi người dùng đã chụp/chọn xong ảnh. Ở đây chúng ta đổi chữ trên resultLabel và hiển thị ảnh trên imageView

Tiếp theo, chúng ta cần vào Info.plist và thêm các thông tin cấp quyền chụp ảnh và chọn ảnh. Từ iOS 10, chúng ta phải thông báo cho người dùng lý do tại sao chúng ta cần sử dụng camera/photo library. các bạn thêm "Privacy – Camera Usage Description" và "Privacy – Photo Library Usage Description" như hình sau:

3. Tích hợp CoreML data model

Để có thể sử dụng CoreML, trước tiên chúng ta cần có core ML data model để CoreML sử dụng. đây là dạng data model được training từ trước, các bạn có thể tự build data model của riêng mình, hoặc đơn giản hơn là download data model trừ trang develop của Apple tại đây

Trong bài viết này, tôi sẽ sử dụng "Inception v3" data model, các bạn có thể vào link bên trên, tìm đến phần "Inception v3" và download model. Sau khi download về máy, các bạn kéo thả vào project để thêm data model. Sau khi thực hiện thêm data model chúng ta sẽ được như hình sau:

Các bạn hãy để ý các thông tin của data model này, data model này là dạng Neural Network, và nó có model class là Inceptionv3. Model class là class để chúng ta tương tác với data model này. Các bạn có thể click vào mũi tên để xem cụ thể các hàm trong class.

Các thông tin trong phần "Model Evaluation Parameters" là các thông tin quan trọng, data model này yêu cầu đầu vào là ảnh với kích thước 299x299, và đầu ra là 2 giá trị dạng Dictionary và String.

4. Sử dụng CoreML nhận diện ảnh

Sau 3 bước trên, chúng ta đã có đủ mọi thứ cần thiết để sử dụng CoreML. Bây giờ chúng ta bắt đầu implement CoreML vào ứng dụng. Các bạn mở ViewController.swift và thêm các đoạn code sau:

import thư viện CoreML:

import CoreML

thêm property data model và khởi tạo trong viewwillappear(_😃:

    var model: Inceptionv3!
    
    override func viewWillAppear(_ animated: Bool) {
        model = Inceptionv3()
    }

Như bên trên đã đề cập, data model này yêu cầu đầu vào là ảnh với kích thước 299x299, nên chúng ta phải convert ảnh sang đúng yêu cầu này. Các bạn sửa lại hàm imagePickerController(_ :, didFinishPickingMediaWithInfo : ) như sau:

    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
        picker.dismiss(animated: true)
        guard let image = info["UIImagePickerControllerOriginalImage"] as? UIImage else {
            return
        }
        
        resultLabel.text = "Analyzing Image..."
        // 1
        UIGraphicsBeginImageContextWithOptions(CGSize(width: 299, height: 299), true, 2.0)
        image.draw(in: CGRect(x: 0, y: 0, width: 299, height: 299))
        let newImage = UIGraphicsGetImageFromCurrentImageContext()!
        UIGraphicsEndImageContext()
        // 2
        let attrs = [kCVPixelBufferCGImageCompatibilityKey: kCFBooleanTrue, kCVPixelBufferCGBitmapContextCompatibilityKey: kCFBooleanTrue] as CFDictionary
        var pixelBuffer : CVPixelBuffer?
        let status = CVPixelBufferCreate(kCFAllocatorDefault, Int(newImage.size.width), Int(newImage.size.height), kCVPixelFormatType_32ARGB, attrs, &pixelBuffer)
        guard (status == kCVReturnSuccess) else {
            return
        }
        
        CVPixelBufferLockBaseAddress(pixelBuffer!, CVPixelBufferLockFlags(rawValue: 0))
        let pixelData = CVPixelBufferGetBaseAddress(pixelBuffer!)
        
        let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
        let context = CGContext(data: pixelData, width: Int(newImage.size.width), height: Int(newImage.size.height), bitsPerComponent: 8, bytesPerRow: CVPixelBufferGetBytesPerRow(pixelBuffer!), space: rgbColorSpace, bitmapInfo: CGImageAlphaInfo.noneSkipFirst.rawValue) //3
        
        context?.translateBy(x: 0, y: newImage.size.height)
        context?.scaleBy(x: 1.0, y: -1.0)
        // 3
        UIGraphicsPushContext(context!)
        newImage.draw(in: CGRect(x: 0, y: 0, width: newImage.size.width, height: newImage.size.height))
        UIGraphicsPopContext()
        CVPixelBufferUnlockBaseAddress(pixelBuffer!, CVPixelBufferLockFlags(rawValue: 0))
        imageView.image = newImage
    }

Trong đoạn code bên trên:

  • 1: resize ảnh về size 299x299
  • 2: biến đổi ảnh để có ảnh RGB. Dạng CVPixelBuffer là dạng mà Inceptionv3 yêu cầu đầu vào
  • 3: lấy output từ ảnh đã biến đổi để hiển thị ra màn hình Bài viết này tập chung tìm hiểu về CoreML, nên tôi sẽ không nói nhiều về đoạn code bên trên. Các hàm bên trên là của thư viện Core Image, với các bạn chưa làm quen với Core Image các bạn có thể tìm hiểu thêm về Core Image tại đây. Trong phạm vi bài viết này, các bạn chỉ cần hiểu đoạn code trên biến đổi ảnh của chúng ta thành ảnh mà Inceptionv3 yêu cầu để thực hiện nhận diện ảnh.

Cuối cùng, chúng ta sử dụng Inceptionv3 data model để nhận dạng ảnh. các bạn thêm đoạn code sau vào cuối hàm imagePickerController(_ :, didFinishPickingMediaWithInfo 😃 :

    guard let prediction = try? model.prediction(image: pixelBuffer!) else {
            return
        }
        
        resultLabel.text = "\(prediction.classLabel)."

Vậy là chúng ta đã hoàn thành việc nhận diện ảnh rồi đó. WTF??? nhận diện ảnh bằng 1 dòng code thôi á??? đúng vậy, chúng ta chỉ cần 1 dòng code bên trên để nhận dạng ảnh thôi.

Build project và chạy thử, lấy ảnh bất kỳ để nhận dạng, các bạn sẽ được kết quả như hình sau:

OK, vậy là ứng dụng của chúng ta đã có thể nhận dạng ảnh cho chúng ta. Kết quả output có thể chưa hoàn toàn chính xác, tuy nhiên có thể nhận dạng như vậy là rất tuyệt vời rồi đúng không ạ? Ngoài Inceptionv3 data model, các bạn có thể sử dụng các data model khác, tỉ lệ chính xác nhận dạng phụ thuộc vào data model các bạn sử dụng.

III. Kết luận

Trên đây tôi đã giới thiệu đến các bạn về CoreML và cách sử dụng data model của CoreML để nhận dạng ảnh. Cảm ơn Apple với framework tuyệt vời này, chúng ta có thể làm những việc tưởng chừng vô cùng phức tạp một cách vô cùng đơn giản. Hi vọng trong tương lai gần, chúng ta sẽ có nhiều data model hơn nữa, với độ chính xác trong nhận dạng cao hơn nữa, để phục vụ cho ứng dụng của chúng ta. Qua bài viết này, tôi cũng hi vọng đã mang đến cho các bạn một chút kiến thức về CoreML, giúp ích các bạn trong quá trình viết ứng dụng.

Cuối cùng, xin cảm ơn các bạn đã theo dõi bài viết này, have a nice day ^_^!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí