Impulse開発チームの塚田です。
今回は、DatabricksのDeep Learning Pipelinesを、spark-shell上で触ってみました。 内容はほぼ下記を実行したものなので、英語余裕で読めるぜ!って方はこちらを見てください。
Deep Learning Pipelines — Databricks Documentation
Deep Learning Pipelines
Deep Learning PipelinesはSparkでDeep Learningをスケーラブルに実行するためのhigh-level APIを提供するパッケージです。 TensorFlowやTensorFlowをバックエンドとしたKerasをサポートしています。 現在は画像データに特化した下記を実行することができます。
- Sparkで画像を操作する
- 画像をDataFrameとして読み込むAPI
- 転移学習
- 学習済みモデルを特徴抽出器として使用
- Deep Learningのモデルを使ったtrasform
- 下記を使用してTransformerを構成
- 学習済みモデル(Inception v3)
- TensorFlow Graph
- Keras
- 下記を使用してTransformerを構成
- モデルをSQL functionとしてデプロイする
- 分散ハイパーパラメータチューニング (comming soon...)
今回は下記を使って、猫と犬の判別をしてみたいと思います。
- Sparkで画像を操作する
- 転移学習
実行環境
ハードウェアの概要 | |
---|---|
機種名 | MacBook Pro |
機種ID | MacBookPro13,2 |
プロセッサ名 | Intel Core i5 |
プロセッサ速度 | 2.9 GHz |
プロセッサの個数 | 1 |
コアの総数 | 2 |
二次キャッシュ(コア単位) | 256 KB |
三次キャッシュ | 4 MB |
メモリ | 16 GB |
- spark 2.2.0
- python 2.7.13
準備
使用するのは猫と犬の画像です。 下記から取得しました。(Kaggleへの登録が必要です)
https://www.kaggle.com/c/dogs-vs-cats-redux-kernels-edition
Dataからtrain.zipをダウンロードしてください。 解凍後、適当な枚数を選りすぐり下記のようにします。 (猫、犬ともに30枚ずつで実行しました)
- /images/ - cat/ - cat.0.jpg - ... - cat.29.jpg - dog/ - dog.0.jpg - ... - dog.29.jpg
Pythonパッケージのインストール
下記のインストールが必要です
pip install tensorflow keras h5py
spark-shell実行
pythonのAPIしかないので、pysparkを実行します。 packagesにspark-deep-learningを指定します。
pyspark --packages databricks:spark-deep-learning:0.1.0-spark2.1-s_2.11 --driver-memory 2G --executor-memory 2G
画像ファイルから入力データを作成
学習に使う画像データはSparkのDataFrameとして取り込みます。 ディレクトリを指定すると直下にある画像ファイルを読み込みます。
# データの読み込み from sparkdl import readImages from pyspark.sql.functions import lit cat_df = readImages("/images/cat").withColumn("label", lit(1)) dog_df = readImages("/images/dog").withColumn("label", lit(0)) cat_train, cat_test = cat_df.randomSplit([0.6, 0.4], 0) dog_train, dog_test = dog_df.randomSplit([0.6, 0.4], 0) train_df = cat_train.unionAll(dog_train) test_df = cat_test.unionAll(dog_test)
中身を見てみると、画像のサイズ等の情報と中身のバイナリが入ってそうですね。
>>> train_df.show(100, False) +---------------------------+---------------------------+-----+ |filePath |image |label| +---------------------------+---------------------------+-----+ |file:/images/cat/cat.19.jpg|[RGB,223,320,3,[B@5db7511c]|1 | |file:/images/cat/cat.2.jpg |[RGB,396,312,3,[B@3562ebce]|1 | |file:/images/cat/cat.23.jpg|[RGB,256,334,3,[B@5d07ef8a]|1 | |file:/images/cat/cat.25.jpg|[RGB,500,345,3,[B@55547685]|1 | |file:/images/cat/cat.26.jpg|[RGB,374,500,3,[B@320cf282]|1 | |file:/images/cat/cat.27.jpg|[RGB,479,370,3,[B@10925396]|1 | |file:/images/cat/cat.28.jpg|[RGB,270,286,3,[B@720b85b1]|1 | |file:/images/cat/cat.29.jpg|[RGB,375,499,3,[B@41d352b] |1 | |file:/images/cat/cat.3.jpg |[RGB,414,500,3,[B@51d05fc8]|1 | |file:/images/cat/cat.6.jpg |[RGB,303,400,3,[B@53dca887]|1 | |file:/images/cat/cat.8.jpg |[RGB,345,461,3,[B@7dd49940]|1 | |file:/images/cat/cat.9.jpg |[RGB,425,320,3,[B@100d5fbc]|1 | |file:/images/cat/cat.0.jpg |[RGB,374,500,3,[B@43707c6a]|1 | |file:/images/cat/cat.10.jpg|[RGB,499,489,3,[B@75de6c13]|1 | |file:/images/cat/cat.11.jpg|[RGB,410,431,3,[B@2c99f571]|1 | |file:/images/cat/cat.13.jpg|[RGB,315,499,3,[B@5693afe1]|1 | |file:/images/cat/cat.15.jpg|[RGB,353,405,3,[B@16401a75]|1 | |file:/images/cat/cat.16.jpg|[RGB,258,448,3,[B@10f8525a]|1 | |file:/images/dog/dog.19.jpg|[RGB,225,299,3,[B@73a91f49]|0 | |file:/images/dog/dog.2.jpg |[RGB,199,187,3,[B@388e5a7a]|0 | |file:/images/dog/dog.23.jpg|[RGB,403,499,3,[B@2e0b6cac]|0 | |file:/images/dog/dog.25.jpg|[RGB,375,499,3,[B@7cb391b5]|0 | |file:/images/dog/dog.26.jpg|[RGB,224,300,3,[B@47db9a3d]|0 | |file:/images/dog/dog.27.jpg|[RGB,375,499,3,[B@d4c4536] |0 | |file:/images/dog/dog.28.jpg|[RGB,432,287,3,[B@78d7363f]|0 | |file:/images/dog/dog.29.jpg|[RGB,376,500,3,[B@18093ea9]|0 | |file:/images/dog/dog.3.jpg |[RGB,375,499,3,[B@39855e7] |0 | |file:/images/dog/dog.6.jpg |[RGB,488,499,3,[B@6c12c557]|0 | |file:/images/dog/dog.8.jpg |[RGB,500,469,3,[B@2db57024]|0 | |file:/images/dog/dog.9.jpg |[RGB,500,368,3,[B@7c98ff4d]|0 | |file:/images/dog/dog.0.jpg |[RGB,375,499,3,[B@3d6eba54]|0 | |file:/images/dog/dog.10.jpg|[RGB,292,269,3,[B@472cf8d3]|0 | |file:/images/dog/dog.11.jpg|[RGB,101,135,3,[B@419e9442]|0 | |file:/images/dog/dog.13.jpg|[RGB,428,362,3,[B@7ac779ab]|0 | |file:/images/dog/dog.15.jpg|[RGB,374,500,3,[B@3233c0bd]|0 | |file:/images/dog/dog.16.jpg|[RGB,380,500,3,[B@12f95c48]|0 | +---------------------------+---------------------------+-----+
転移学習
Deep Learning Pipelinesにより学習済みモデルを使用して学習を行います。 現状は下記のモデルがサポートされています。
- InceptionV3
InceptionV3の出力を特徴量とし、決定木により猫と犬を分類します。
DeepImageFeaturizer
のインスタンスを作ってPipeline
を作るだけですね。
# 学習 from pyspark.ml.classification import DecisionTreeClassifier from pyspark.ml import Pipeline from sparkdl import DeepImageFeaturizer featurizer = DeepImageFeaturizer(inputCol="image", outputCol="features", modelName="InceptionV3") dt = DecisionTreeClassifier(labelCol="label", featuresCol="features") p = Pipeline(stages=[featurizer, dt]) p_model = p.fit(train_df)
出来上がったモデルを使ってtransformします。
# テスト from pyspark.ml.evaluation import MulticlassClassificationEvaluator tested_df = p_model.transform(test_df) evaluator = MulticlassClassificationEvaluator(metricName="accuracy") print("Test set accuracy = " + str(evaluator.evaluate(tested_df.select("prediction", "label"))))
Test set accuracy = 0.75
何も考えずに作った割には合ってる気もしますが、もうちょっと中身について見ていきます。
DataFlameの中身を見てみると、DeepImageFeaturizer
の出力がfeatures
カラムに入っています。
>>> tested_df.show() +--------------------+--------------------+-----+--------------------+-------------+-----------+----------+ | filePath| image|label| features|rawPrediction|probability|prediction| +--------------------+--------------------+-----+--------------------+-------------+-----------+----------+ |file:/images/cat/...|[RGB,374,500,3,[B...| 1|[0.0,0.8633406162...| [0.0,17.0]| [0.0,1.0]| 1.0| |file:/images/cat/...|[RGB,374,500,3,[B...| 1|[0.0,0.0,0.0,0.0,...| [0.0,17.0]| [0.0,1.0]| 1.0| |file:/images/cat/...|[RGB,499,431,3,[B...| 1|[0.0,0.0,0.229576...| [0.0,17.0]| [0.0,1.0]| 1.0| |file:/images/cat/...|[RGB,345,500,3,[B...| 1|[0.0,0.0516236834...| [0.0,17.0]| [0.0,1.0]| 1.0| |file:/images/cat/...|[RGB,374,500,3,[B...| 1|[0.0,0.0,0.0,0.0,...| [0.0,17.0]| [0.0,1.0]| 1.0| |file:/images/cat/...|[RGB,375,499,3,[B...| 1|[0.12656563520431...| [0.0,17.0]| [0.0,1.0]| 1.0| |file:/images/cat/...|[RGB,144,175,3,[B...| 1|[0.0,0.0,0.195755...| [18.0,0.0]| [1.0,0.0]| 0.0| |file:/images/cat/...|[RGB,499,495,3,[B...| 1|[0.0,0.0,0.0,0.0,...| [18.0,0.0]| [1.0,0.0]| 0.0| |file:/images/cat/...|[RGB,280,300,3,[B...| 1|[0.79150134325027...| [0.0,17.0]| [0.0,1.0]| 1.0| |file:/images/cat/...|[RGB,224,300,3,[B...| 1|[1.65047085285186...| [0.0,17.0]| [0.0,1.0]| 1.0| |file:/images/cat/...|[RGB,267,320,3,[B...| 1|[0.86000078916549...| [18.0,0.0]| [1.0,0.0]| 0.0| |file:/images/cat/...|[RGB,375,499,3,[B...| 1|[0.0,0.0,0.0,0.14...| [0.0,17.0]| [0.0,1.0]| 1.0| |file:/images/dog/...|[RGB,348,215,3,[B...| 0|[0.0,0.1367801278...| [18.0,0.0]| [1.0,0.0]| 0.0| |file:/images/dog/...|[RGB,332,500,3,[B...| 0|[0.21633519232273...| [18.0,0.0]| [1.0,0.0]| 0.0| |file:/images/dog/...|[RGB,499,415,3,[B...| 0|[0.0,0.0,0.884287...| [18.0,0.0]| [1.0,0.0]| 0.0| |file:/images/dog/...|[RGB,371,499,3,[B...| 0|[0.93737035989761...| [18.0,0.0]| [1.0,0.0]| 0.0| |file:/images/dog/...|[RGB,500,274,3,[B...| 0|[0.56643348932266...| [0.0,1.0]| [0.0,1.0]| 1.0| |file:/images/dog/...|[RGB,287,300,3,[B...| 0|[0.10650488734245...| [18.0,0.0]| [1.0,0.0]| 0.0| |file:/images/dog/...|[RGB,376,499,3,[B...| 0|[0.11714683473110...| [18.0,0.0]| [1.0,0.0]| 0.0| |file:/images/dog/...|[RGB,264,299,3,[B...| 0|[0.0,0.0,0.0,0.0,...| [0.0,1.0]| [0.0,1.0]| 1.0| |file:/images/dog/...|[RGB,499,327,3,[B...| 0|[0.21827600896358...| [0.0,17.0]| [0.0,1.0]| 1.0| |file:/images/dog/...|[RGB,161,98,3,[B@...| 0|[0.04027611017227...| [18.0,0.0]| [1.0,0.0]| 0.0| |file:/images/dog/...|[RGB,386,500,3,[B...| 0|[0.0,0.0,1.286264...| [18.0,0.0]| [1.0,0.0]| 0.0| |file:/images/dog/...|[RGB,335,272,3,[B...| 0|[0.0,1.5273251533...| [18.0,0.0]| [1.0,0.0]| 0.0| +--------------------+--------------------+-----+--------------------+-------------+-----------+----------+
featuresの中身は配列になっています。 長さは131072です。
>>> len(tested_df.head()['features']) 131072
決定木は下記の用になっています。
>>> print(p_model.stages[1].toDebugString) DecisionTreeClassificationModel (uid=DecisionTreeClassifier_46609281014c140920a8) of depth 2 with 5 nodes If (feature 38387 <= 0.6593263149261475) If (feature 26 <= 0.5600484609603882) Predict: 0.0 Else (feature 26 > 0.5600484609603882) Predict: 1.0 Else (feature 38387 > 0.6593263149261475) Predict: 1.0
featuresの数の割に木が短いです。 単純に学習データを多くすれば精度を上げられそうです。 (今回はとりあえず動かすだけなので大分サンプル数を絞っています)
また、indexからfeaturesのラベルを逆引きする手段がDeepImageFeaturizer
になさそうなので、26と38387が何の値なのかさっぱりわかりません。
indexとラベルのマッピングが取れる手段が欲しいですね。
おわりに
Deep Learning Pipelinesを使うとDeep Learningのことを大して知らなくても実行できます。
まだ画像しか扱えませんし使える学習済みモデルも少ないですが、その辺の選択肢が増えて来ると夢が広がりますよね。