iOSエンジニアのしだです。最近はiOSを書いていませんが、iOSエンジニアと言い続けたいと思います。
TensorFlow Developer Summit 2018 にアナウンスされていた Swift for TensorFlow が先日公開されたのでさわってみます。
準備
Swift for TensorFlow は、TensorFlow API の単純なラッパーというわけでなくSwift のコンパイラと言語が拡張されているということで、それ用のSwiftをインストールする必要があります。
インストール手順 に従ってビルド済みのパッケージをダウンロードして手順に従ってインストールできます。
ニューラルネットワークの実装
UCLA のアドミッションのデータを使って、入力層、隠れ層、出力層の3層のニューラルネットワークを作ってみます。
tensorflow/swift-models に含まれている MNIST のサンプルコード を元に実装しています。
データセット
// ダウンロード $ curl -o /tmp/binary.csv https://stats.idre.ucla.edu/stat/data/binary.csv // データサンプル $ python >>> import pandas as pd >>> pd.read_csv('/tmp/binary.csv').head() admit gre gpa rank 0 0 380 3.61 3 1 1 660 3.67 3 2 1 800 4.00 1 3 1 640 3.19 4 4 0 520 2.93 4
GRE スコア、 GPA と rank(1 ~ 4) を元に admit: 0, 1
を予測します。
前処理を行い、GRE と GPA の値は標準化して、rank は カテゴリー変数として4つにバラします。
入力層は6つ、隠れ層を3つ、出力層を1つのニューラルネットワークにします。
Swift の実装
より単純にするためにバイアスはなしで計算しています。
#!/usr/bin/env swift -O import Foundation import TensorFlow // ... 前処理など func main() { /// CSV ファイルパス let csvPath = "/tmp/binary.csv" print("Loading csv...", csvPath) /// CSV を読み込んで前処理を行いTensorを返す /// 学習用のデータ /// - x: Tensor<Float> /// - y: Tensor<Float> /// テスト用のデータ /// - xValid: Tensor<Float> /// - yValid: Tensor<Float> let (x, y, xValid, yValid) = readAdmissions(path: csvPath) print("----------") /// 学習回数 let epochs = 100 /// 学習率 let learningRate = Float(0.01) var w1 = Tensor<Float>(randomUniform: [6, 3]) var w2 = Tensor<Float>(randomUniform: [3, 1]) var losses: [Float] = [] for _ in 0..<epochs { /// 順伝播 let z1 = x ⊗ w1 let h1 = sigmoid(z1) let z2 = h1 ⊗ w2 let pred = sigmoid(z2) /// 誤差逆伝播 /// - シグモイド関数: f(x) = 1 / (1 + e−x) /// - シグモイド関数の微分: f'(x) = f(x)(1 − f(x)) let dz2 = (y - pred) * pred * (1 - pred) let dw2 = h1.transposed() ⊗ dz2 let dz1 = dz2 ⊗ w2.transposed() * h1 * (1 - h1) let dw1 = x.transposed() ⊗ dz1 /// 重みを更新 w1 += learningRate * dw1 w2 += learningRate * dw2 /// lossを計算 let loss = dz2.squared().mean(squeezingAxes: 1, 0).scalarized() losses.append(loss) } /// バリデーション用のデータを使って精度を計算する let hidden = sigmoid(xValid ⊗ w1) let out = sigmoid(hidden ⊗ w2) let predictions = out.scalars.map { $0 > 0.5 ? 1.0 : 0.0 }.map { Float($0) } let y_ = yValid.scalars let accuracy = zip(predictions, y_).map { $0 == $1 ? 1 : 0 }.mean print(losses) print("accuracy: ", accuracy) } main()
実行結果
loss も下がって学習してそうです。精度も 73%でした。
$ export PATH=/Library/Developer/Toolchains/swift-latest/usr/bin:"${PATH}" $ ./main.swift IMPLICIT COPY TO HOST OF: %2157 = builtin "__tfop_Reshape,$in,$in"(<<NULL OPERAND>>, <<NULL OPERAND>>) : $TensorHandle<Float> // user: %2159 IMPLICIT COPY TO HOST BY: %2159 = apply %1594(%2157, %1593) : $@convention(method) (@owned TensorHandle<Float>, @thin Float.Type) -> Float // users: %2228, %2160 ./main.swift:134:60: warning: value implicitly copied to the host, use .toHost() to make transfer explicit let loss = dz2.squared().mean(squeezingAxes: 1, 0).scalarized() ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~ Loading csv... /tmp/binary.csv ---------- [0.015572316, 0.0163158029, 0.0163647104, 0.0159751847, 0.0154276136, 0.0148837836, 0.0144050093, 0.01400341, 0.013672946, 0.0134026865, 0.0131816845, 0.0130004203, 0.0128510511, 0.0127272531, 0.0126239872, 0.0125372233, 0.0124637503, 0.0124010015, 0.0123469178, 0.0122998478, 0.0122584607, 0.0122216837, 0.0121886451, 0.01215865, 0.0121311238, 0.0121056084, 0.0120817311, 0.0120591866, 0.0120377317, 0.0120171662, 0.0119973291, 0.0119780907, 0.0119593414, 0.0119410008, 0.0119229928, 0.0119052669, 0.0118877795, 0.0118704876, 0.0118533643, 0.0118363844, 0.0118195312, 0.0118027842, 0.0117861321, 0.0117695658, 0.011753073, 0.0117366444, 0.0117202755, 0.0117039569, 0.0116876885, 0.011671463, 0.0116552738, 0.0116391191, 0.0116229914, 0.0116068907, 0.0115908086, 0.011574747, 0.0115586976, 0.0115426602, 0.0115266349, 0.0115106134, 0.0114945937, 0.0114785749, 0.0114625553, 0.0114465347, 0.0114305085, 0.0114144739, 0.0113984356, 0.011382388, 0.0113663347, 0.0113502732, 0.0113342032, 0.0113181276, 0.0113020455, 0.0112859569, 0.0112698618, 0.0112537667, 0.0112376707, 0.0112215756, 0.0112054823, 0.0111893946, 0.0111733172, 0.0111572472, 0.0111411922, 0.0111251511, 0.0111091277, 0.0110931266, 0.0110771498, 0.011061199, 0.011045279, 0.0110293915, 0.0110135376, 0.0109977219, 0.010981949, 0.0109662153, 0.0109505299, 0.010934893, 0.0109193055, 0.0109037701, 0.0108882915, 0.010872866] accuracy: 0.73
まとめ
Python for TensorFlow は sess.run
で実行を明示しますが、Swift の場合、どこで実行されるのか意識して実装する必要があるように感じました。
本当は Playground 上で計算させて loss をグラフ表示するようにしたかったですが、Playground が動かなすぎて諦めました 😭
Swift のコンパイル時にTF Graph が作成されたり技術的に面白そうなので、 Swift for TensorFlow を引き続き見ていきたいと思います。