jarinosuke blog

about software engineering, mostly about iOS

Swift for TensorFlow の randomUniform を読む

モチベーション

以下の Swift for TensorFlow のスレッドで Chris が発言した

https://groups.google.com/a/tensorflow.org/forum/#!topic/swift/IQcQIOslt-I

Take a look at Tensor.swift in the TensorFlow module.  We implemented this with the standard swift generics system, no special support necessary.

をみて少し気になった

また前回書いたswift-models の MNIST.swift を読むでも登場したrandomUniformに関して、どういった実装をしているのか気になり読んでみた

https://github.com/tensorflow/swift-models/blob/master/MNIST/MNIST.swift#L61-L62

読んでいくうちに結構面白いと自分でも思えたので過程をブログにすることにした

randomUniform とは

エントリポイントとなるのは以下の initializer

https://github.com/google/swift/blob/tensorflow/stdlib/public/TensorFlow/Tensor.swift#L533-L546

与えられた TensorShape の要素を全て0-1の正規分布の範囲内の値で埋め尽くすというもの

まずここで気づくのが、単純に Swift for TensorFlow が TensorFlow と Swift のブリッジング/マッパーではないということで、結構な部分(特に型まわり)が pure Swift で実装されている

また読み進める中でいくつかの _ 付きの @ attributes がある

@_fixed_layout @_versioned @_inlineable @inline(__always)

しっかり調べられていないので後で調べてまた別でブログにしたい

https://bugs.swift.org/browse/SR-260 https://stackoverflow.com/questions/46524232/access-control-in-swift-4 https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20171002/040061.html

ここからスタート、基本的に見たことのないユニットは一つずつあげていく

TensorShape

https://github.com/google/swift/blob/tensorflow/stdlib/public/TensorFlow/TensorShape.swift

配列の薄いラッパーで、次元を表現するときに使う

ExpressibleByArrayLiteral に対応しているので利用する側は配列と思って使っていても問題ない

RandomState

https://github.com/google/swift/blob/tensorflow/stdlib/public/TensorFlow/Utilities.swift#L148

擬似乱数を表現するクラス、全体的にデフォルトだと RandomState.global を使用しているところが多い

Tensor.init(randomUniform shape: TensorShape, state: RandomState? = nil)

ここでようやく元の initializer に戻れる

この initializer の実態は以下の initializer のラッパーであることがわかる

https://github.com/google/swift/blob/tensorflow/stdlib/public/TensorFlow/Tensor.swift#L543-L545

ラップされた以下の initializer を RAND_MAX で割っていることから、以下の initializer は整数値の range での正規分布を元にした random ということが推測できる

ここで登場している Scalar クラスはどこで定義されているのか分からなかった。が、行列計算におけるスカラー値を表現していることは容易にわかる

また以下にあるように、AccelerableByTensorFlow プロトコルに準拠している型は Scalar として使えるようだった

https://github.com/google/swift/blob/tensorflow/stdlib/public/TensorFlow/DataTypes.swift#L34

init(randomStandardUniform shape: TensorShape, state: RandomState? = nil)

次にラップされていた上記を読み進める前に、いくつか調べておかないといけないユニットがある

_TFHoistable

_TFHoistable() -> TensorHandle<Scalar> な closure を受け取り、その closure の返り値を return するだけのもの

https://github.com/google/swift/blob/tensorflow/stdlib/public/TensorFlow/Tensor.swift#L124

TensorHandle

TensorHandleTensor の核となるもので、実は TensorTensorHandle のラッパー

TensorHandle は ops や #tfop() を実行するための型

実際に TensorFlow とやりとりするときに、パラメータの型をより静的にコンパイラに伝えるためのものだと思った

_TFTensorFromScalars<Scalar>(_ scalars: [Scalar], shape: [Int32])

https://github.com/google/swift/blob/tensorflow/stdlib/public/TensorFlow/Tensor.swift#L94-L107

内部の実装を読むと渡された scalar の配列と shape のサイズに違いがないかチェックし、

TensorHandle を初期化している。 scalarsInitializer では TensorHandle に渡す scalars を全てアロケートしないといけないらしい。これも TensorFlow のコアに渡すためなんだろうか

https://github.com/google/swift/blob/tensorflow/stdlib/public/TensorFlow/TensorHandle.swift#L45

TensorFlow.init(randomStandardUniform shape: TensorShape, state: RandomState? = nil)

https://github.com/google/swift/blob/tensorflow/stdlib/public/TensorFlow/Tensor.swift#L521-L529

ここまででようやくメインに戻ってくることができる

やっていることとしては、TensorTensorHandle を引数にして初期化している

その TensorHandle_TFHoistable 経由で作成しており、その closure の内部で _TFTensorFromScalars を使って、ランダムに作られた Int の配列から TensorHandle を作っていることがわかる

まとめ

サンプルで使われていたある関数を起点として、いかに静的型付けと TensorFlow のコアとのブリッジングを成立させるかの足がかりが見えた。

今回見えた TensorFlow/DataTypes.swiftTensorFlow/TensorHandle.swift などはそれに強く関わる部分だと思うので、もっと調べる

https://github.com/google/swift/blob/tensorflow/stdlib/public/TensorFlow/DataTypes.swift https://github.com/google/swift/blob/tensorflow/stdlib/public/TensorFlow/TensorHandle.swift

swift-models の MNIST.swift を読む

swift-models

TensorFlow for Swift で実際に使うことができる機械学習のモデルを集めたレポジトリ

https://github.com/tensorflow/swift-models

2018/4/30 現在だと MNIST だけがある

MNIST についての説明は省略するが、MNIST 自体についてのチュートリアルはこれがおすすめ

https://www.tensorflow.org/versions/r1.1/get_started/mnist/beginners

swift-models の実際の実行方法は前に書いたブログを参考にしてほしい

ちなみに Xcode に結合された API doc はまだ見つからなかったので、以下のページを頻繁に使った

https://www.tensorflow.org/api_docs/swift/

MNIST.swift を読む

MNIST.swift はコマンドラインから実行されることを前提としている

内容はとても単純で main という関数を実行しているだけなので、main の1行目から読み進めていけばOK

1.教師データの準備

https://github.com/tensorflow/swift-models/blob/master/MNIST/MNIST.swift#L38-L53

これらの行ではファイルから実際の画像データとそれに対するラベルをロードして、Tensor オブジェクトにしている

readMnist 内で imageDatalabelData をロードする際に、それぞれ dropFirst しているのは何故なんだろう…

ロードされたデータは以下のような2次元配列となっている

ラベル: 1x60000 画像: 784x60000

要するに60000個の0-9でラベル付けされた画像がある

またロードされたラベルデータは0-9の値を取るので、one-hot Tensor に変換している

https://www.tensorflow.org/api_docs/swift/Structs/Tensor#/s:10TensorFlow0A0VAAs7NumericRzrlEACyxGACys5Int32VG15oneHotAtIndices_AG5depthx7onValuex03offK0Si4axistcfc

簡単にいうとこんな感じ

変換前: [6, 7, 3…] 変換後: [ [0, 0, 0, 0, 0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0, 0, 0] ... ]

2.ハイパーパラメータと学習パラメータの準備

https://github.com/tensorflow/swift-models/blob/master/MNIST/MNIST.swift#L55-L64

ハイパーパラメータには以下の3つが定義されている

  • イテレーションの回数
  • 学習率
  • 損失

また学習するパラメータは以下のように2段階に設定されている

  • 1層目
    • 重み w1
    • バイアス b1
  • 2層目
    • 重み w2
    • バイアス b2

3.学習ループ

ニューラルネットなので以下のようなループで進む

  • 3-1. 前方伝播 forward propagationで推測
  • 3-2. 誤差逆伝播 back propagation で誤差計測
  • 3-3. 最急降下法 gradient descent でパラメータ更新
  • 3-4. 3-1に戻る

3-1.前方伝播 forward propagationで推測

https://github.com/tensorflow/swift-models/blob/master/MNIST/MNIST.swift#L71-L75

Tensor のドット積にオペレータとして を使用している、単純な定数倍とは明確に区別したいためだろうか…

https://www.tensorflow.org/api_docs/swift/Protocols/TensorProtocol#/s:10TensorFlow0A8ProtocolPAAs7Numeric6ScalarRpzrlE003mehoixx_xtFZ

また sigmoid は標準関数としてサポートされている。他にも ReLU や soft-max などもされている

https://www.tensorflow.org/api_docs/swift/Functions#/s:10TensorFlow7sigmoidxxAA0A8ProtocolRzs19BinaryFloatingPoint6ScalarRpzlF

3-2.誤差逆伝播 back propagation で誤差計測

https://github.com/tensorflow/swift-models/blob/master/MNIST/MNIST.swift#L77-L83

ここでは Tensor の操作がいくつか出てくる

transpose

これは転置行列

https://www.tensorflow.org/api_docs/swift/Structs/Tensor2D#/s:10TensorFlow8Tensor2DVAAs7NumericRzrlE10transposedACyxGyF

withPermutations というのは与えられた順列を元に転置を作るぽい。デフォルトが none なので、デフォルトであれば普通の転置行列になるはず。詳細は以下

https://www.tensorflow.org/api_docs/python/tf/transpose

sum

これは任意の axis に sum をしてその axis を潰す ここでは1を指定しているので列方向に潰しているぽい

https://www.tensorflow.org/api_docs/swift/Structs/Tensor#/s:10TensorFlow0A0VAAs7NumericRzrlE3sumACyxGs5Int32V13squeezingAxesd_tF

3-3.最急降下法 gradient descent でパラメータ更新

https://github.com/tensorflow/swift-models/blob/master/MNIST/MNIST.swift#L85-L92

計算された損失に学習率をかけてパラメータを更新する

ここでの * は単純にスカラー倍を意味している

https://www.tensorflow.org/api_docs/swift/Structs/Tensor#/s:10TensorFlow0A0VAAs7NumericRzrlE1moiACyxGAF_xtFZ

まとめ

Swift API doc をみるのは面白い

https://www.tensorflow.org/api_docs/swift/

以下の stdlib/public も合わせて読む

https://github.com/google/swift/tree/tensorflow/stdlib/public/TensorFlow

Swift for TensorFlow で MNIST を実行する

Swift for TensorFlow

少し前に TensorFlow Dev Summit 2018 でアナウンスされた Swift for TensorFlow の動画は以下


Swift for TensorFlow - TFiwS (TensorFlow Dev Summit 2018)

ついにそれが先週が OSS になった

https://github.com/tensorflow/swift https://www.tensorflow.org/community/swift

色々とすごいところが多いのだけれど、このブログでは Swift for TensorFlow を動かしてみるところに注力してみる

Swift for TensorFlow のインストール

ビルドされた TensorFlow が入ってる Swift を以下のリンク先の .dmg からインストールする

https://github.com/tensorflow/swift/blob/master/Installation.md#pre-built-packages

PATH を通す

コマンドラインからビルドするだけなのでパスを通すだけで OK

export PATH=/Library/Developer/Toolchains/swift-latest/usr/bin:"${PATH}"

(fish の人用にも念のため)

set -x PATH /Library/Developer/Toolchains/swift-latest/usr/bin $PATH

(補足)Xcode からも使いたい場合は以下のリンクを参考に Toolchain を変更する

https://github.com/tensorflow/swift/blob/master/Installation.md#installation

swift-models のレポジトリを clone

以下のレポジトリを clone する https://github.com/tensorflow/swift-models

ちなみにこのレポジトリはcontributeできるので、今後他のモデルも追加されて行きそう

git clone git@github.com:tensorflow/swift-models.git

ビルド

上記の clone したディレクトリに行き、以下のコマンドを実行する

swift -O MNIST.swift

これで MNIST のビルドが始まる

ちなみに -O は compile with optimization

これでビルドができると思う

f:id:jarinosuke0808:20180429174509p:plain

grpc-swift を Carthage 経由でインストールする方法

grpc-swift

grpc-swift は現時点(2018/2/2)では Carthage には対応していない

https://github.com/grpc/grpc-swift/issues/13

このブログではどうにかして、Carthage 経由でそれをインストールするための方法を説明する

動機としてはサポートされている SPM でのインストールがとても辛かったからというのが大きい

SPM のあれこれはこの手順を読んでもらえるといくつか雰囲気をつかめるかも

また下記の内容はタベリーのライセンスページをみて、grpc-swift を使っていたので ishkawa さんにコンタクトを取って教えてもらった。感謝!

手順

1.別で管理する

何はともあれ grpc-swift をフォークもしくは、

何かしら本流とは別のバージョン管理をする必要がある

これからいくつかの変更を加えるのが理由

2.xcodeproj の生成

以下のコマンドを grpc-swift で実行する

swift package generate-xcodeproj

これを実行すると SwiftGRPC.xcodeproj というファイルが生成されるので開く

こちらのファイルもバージョン管理に入っていない場合はこれ以降で編集するので追加するといい

3. modulemap のパスの相対化

どうやら Swift Package Manager で設定された modulemap の header パスは実行環境の絶対パスが指定されるようなので、これを別環境でも動くように相対パスにする

以下を参考に BoringSSL と Czlib のそれを修正する

https://github.com/ishkawa/grpc-swift/blob/carthage-0.2.3/SwiftGRPC.xcodeproj/GeneratedModuleMap/BoringSSL/module.modulemap https://github.com/ishkawa/grpc-swift/blob/carthage-0.2.3/SwiftGRPC.xcodeproj/GeneratedModuleMap/Czlib/module.modulemap

4.zlib-example を消す

zlib-example というビルドターゲットならびにスキームがあるので両方消す

これがあると依存関係で Carthage 実行時にこれもビルドされてしまい、エラーを吐き出す

5.SPM でインストールされた zlib を Git 管理下に置く

.build/checkoutsにあるので追加する

また指定されている zlib の URL は以下が指定されているが

https://github.com/Zewo/zlib.git

こちらは変わっていて以下になっている

https://github.com/ZewoGraveyard/CZlib.git

これが原因でエラーになった場合は一度書き換えるのがいい

6.それぞれのフレームワークのbundle identifierを正しく設定する

これを行わないと実機インストールができないので、適当に com.grpc.swift などを以下のターゲットにつける

  • gRPC
  • CgRPC
  • BoringSSL
  • Czlib

終わりと補足

上記手順を踏むことでおそらく grpc-swift を Carthage でインストールすることが

できるようになるはず。残りやることはいつもの Carthage の手順のみ。

また直近のバージョンだと以下のエラーが頻発するので、現状だと 0.1.x 系を使うことがオススメ。

https://github.com/grpc/grpc-swift/issues/82

最近やっていること

今まで一年半くらいGitHub ブログを書いていたが

デザインの修正が苦手なのと流入少なめなのでまたはてなブログに戻ってきた。

年末ということもあるし最近何してるかを簡単に書く。

iOS

相変わらずメルカリで iOS エンジニアとして iOS アプリの開発をしている。

2017年はUSからJPまで、毎期色々なアプリの機能開発をした。

iOS DC Japan 2017 でも以下の発表できた。

iosdc.jp

speakerdeck.com

また最近では OSS にも貢献したい気持ちが出てきていて、

具体的には以下のようなライブラリに PR を送っている。

iOS 11 Programming を購入して、iOS 11 の進化に驚いたりもした。

ブロックチェーン・暗号通貨

ブロックチェーンや暗号通貨にも少なからず興味を持っていて

趣味で以下のような本を元に勉強したりしている。

実際に手を動かすなどはあまりしておらず、あくまでサンプルを動かすなどにとどまっている。

いまさら聞けない ビットコインとブロックチェーン

いまさら聞けない ビットコインとブロックチェーン

ビットコインとブロックチェーン:暗号通貨を支える技術

ビットコインとブロックチェーン:暗号通貨を支える技術

最初は第一印象などで敬遠していたが、興味半分で触れてみたらとても技術的に新しいし勉強になることも多い。

お金

上とも少し繋がるが、生活環境が変わったことでお金についても昔より考えるようになった。

具体的には今までなんとなくで生きてこられたキャッシュフローを見直して、

将来のための投資や運用に少しずつ行っている。

具体的な内訳としては毎月の給与を天引きで貯蓄口座と確定拠出年金に移動したり、暗号通貨をお遊び程度に買っている程度だけれど。

年明けからは WEALTHNAVITHEO も使っていく。

今後と来年の抱負

今後も iOS エンジニアとして仕事していく。

もう少し業務の幅を広げつつ、OSS への貢献や勉強会でのアウトプットなどをより積極的に行っていきたい。