Create MLを使ってSwiftで機械学習モデルを構築する

この記事は[JX通信社 Advent Calendar 2018](https://qiita.com/advent-calendar/2018/jxpress)の13日目です。

WWDC 2018で発表された[Create ML](https://developer.apple.com/documentation/createml/)について、今回は表形式データを使った`住宅価格を予測`する機械学習モデルの構築を例に紹介していきます。

# Create ML

Create MLは機械学習モデルを構築するためのフレームワークです。
Xcode 10のPlaygroundで簡単にモデルを構築することができます。

# ワークフロー

[Create ML - Overview](https://developer.apple.com/documentation/createml/)

<img src="https://docs-assets.developer.apple.com/published/efbf968cc0/0edb3e43-a85c-41d0-9156-b0ee0b7b1d37.png" width=50%>

## 学習データ(Data)

`住宅価格を予測`するモデルを構築するために[Kaggle](https://www.kaggle.com/)で公開されているデータを使ってトレーニングを行います。
[Boston Housing](https://www.kaggle.com/c/boston-housing)(ボストン近郊の不動産情報)の`train.csv`を使って実際にコードを書いていきます。CSV内のデータ構成は次の通りです。`medv`の予測をするモデルを構築するのが今回の目的です。

|カラム|説明|
|------|-----|
|crim|一人当たりの犯罪率|
|zn|25,000平方フィート以上の住宅地の割合|
|indus|非小売業の土地面積の割合|
|chas|チャールズ川沿いか否か|
|nox|窒素酸化物濃度|
|rm|平均部屋数|
|age|1940年以前に建てられた住宅の割合|
|dis|5つのボストン雇用センターまでの距離の加重平均|
|rad|高速道路へのアクセスのしやすさ|
|tax|10,000ドルあたりの固定資産税の税率|
|ptratio|生徒と教師の比率|
|black|黒人居住者の割合|
|lstat|低所得者の割合|
|medv|住宅価格の中央値(1000ドル単位)|

## トレーニング(Training)

データが用意できたら次はトレーニングです。
CSVファイルを読み込んだ後に、[randomSplit(by:seed:)](https://developer.apple.com/documentation/createml/mldatatable/3006087-randomsplit)でトレーニングとテスト用のサブセットに分割します。CSVが元からトレーニングとテスト用に分かれている場合は不要です。
あとはアルゴリズムを指定して`Run Playground`でトレーニングを開始します。

```swift
import CreateML
import Foundation

do {
    let trainingCSV = URL(fileURLWithPath: "path/to/train.csv")
    let houseData = try MLDataTable(contentsOf: trainingCSV)
    let (trainingData, testData) = houseData.randomSplit(by: 0.8, seed: 0)

    let housePricer = try MLLinearRegressor(trainingData: trainingData, targetColumn: "medv")
} catch {
    print(error)
}
```

### サポートされているアルゴリズム

#### 回帰

https://developer.apple.com/documentation/createml/mlregressor

|手法|Type|
|----|----|
|線形回帰|MLLinearRegressor|
|決定木|MLDecisionTreeRegressor|
|ランダムフォレスト|MLRandomForestRegressor|
|_ブースティング木_|MLBoostedTreeRegressor|

#### 分類

https://developer.apple.com/documentation/createml/mlclassifier

|手法|Type|
|----|----|
|決定木|MLDecisionTreeClassifier|
|ランダムフォレスト|MLRandomForestClassifier|
|ブースティング木|MLBoostedTreeClassifier|
|ロジスティック回帰|MLLogisticRegressionClassifier|
|サポートベクターマシン|MLSupportVectorClassifier|

## モデルの評価(Evaluation)

トレーニングしたモデルのパフォーマンスは`metrics`で確認することができます。

```swift
import CreateML
import Foundation

do {
    let trainingCSV = URL(fileURLWithPath: "path/to/train.csv")
    let houseData = try MLDataTable(contentsOf: trainingCSV)
    let (trainingData, testData) = houseData.randomSplit(by: 0.8, seed: 0)

    let housePricer = try MLLinearRegressor(trainingData: trainingData, targetColumn: "medv")

    let metrics = housePricer.evaluation(on: testData)
    
    // Max error: 23.29
    // Root mean squared error: 5.67
    print(metrics)
} catch {
    print(error)
}
```

回帰アルゴリズムの場合、`metrics`には予測値とテストデータの値との誤差が記録されています。
最大誤差(`Max Error`)と二乗平均平方根誤差(`Root-Mean-Square Error`)でモデルのパフォーマンスを測定し、その誤差を最小化することがトレーニングの目的です。

メトリクスについては、Apple DeveloperサイトのDocumentationページよりも、AppleがCreate MLと同じく機械学習ツールとして提供している[Turi Create](https://github.com/apple/turicreate)の[ドキュメント](https://apple.github.io/turicreate/docs/userguide/)の説明がより分かりやすいです。

# モデルの書き出しとアプリへの組み込み

学習が完了したらモデルを書き出してアプリに組み込みましょう。
パスを指定して`write(to:)`または`write(toFile:)`で書き出します。

```swift
import CreateML
import Foundation

do {
    let trainingCSV = URL(fileURLWithPath: "path/to/train.csv")
    let houseData = try MLDataTable(contentsOf: trainingCSV)
    let (trainingData, testData) = houseData.randomSplit(by: 0.8, seed: 0)

    let housePricer = try MLLinearRegressor(trainingData: trainingData, targetColumn: "medv")

    // ...

    try housePricer.write(to: URL(fileURLWithPath: OUTPUT_PATH))
} catch {
    print(error)
}
```

書き出したモデルをアプリのプロジェクトに追加したら次のように呼び出します。
プロジェクトに追加した時に、モデルクラスとInput/Output用の構造体が自動生成されています。

```swift
import CoreML

do {
    let input = HousePricerInput(
        ID: 3,
        crim: 0.02729,
        zn: 0,
        indus: 7.07,
        chas: 0,
        nox: 0.469,
        rm: 7.185,
        age: 61.1,
        dis: 4.9671,
        rad: 2,
        tax: 242,
        ptratio: 17.8,
        black: 392.83,
        lstat: 4.03)

    let housePricer = try HousePricer(contentsOf: HousePricer.urlOfModelInThisBundle)
    let output = try housePricer.prediction(input: input)
    print(output.medv)
} catch {
    print(error)
}
```

# まとめ

Create MLについて表形式データを使ったサンプルを紹介しました。
今はまだ情報が少なく、Create MLを使った画像分類の紹介記事はいくつかあるのですが、テキストと表形式データを使った記事が少ないと感じていたため今回の記事を書くことにしました。
機械学習に興味はあるものの、なかなか手が付けられずにいたアプリエンジニアの方々の学習のきっかけになれば幸いです。

WWDC 2017でのCore MLの発表、そして2018でのCreate MLとAppleが力を入れている分野なので、このタイミングで機械学習に触れておけば次のWWDCはより楽しめるものになるはずです。