Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Caffe2によるディープラーニングの基礎と実践 rev 1.1

1,003 views

Published on

Caffe2の使い方とディープラーニングの基本を初心者向けに紹介するセミナー資料です。以前制作したCaffe 1.0向けの教材をCaffe2向けに改訂しました。
https://www.slideshare.net/KotaYamaguchi1/caffe-71288204

Published in: Software
  • Be the first to comment

Caffe2によるディープラーニングの基礎と実践 rev 1.1

  1. 1. Caffe2によるディープラーニ ングの基礎と実践 山口 光太 (株) サイバーエージェント
  2. 2. 自己紹介 山口 光太 Research Scientist (株) サイバーエージェント AI Lab コンピュータビジョン、機械学習、Webメディアの分析 経歴・学歴 助教, 情報科学研究科, 東北大学 2014-2017 PhD, Computer Science, Stony Brook University 2009- 2014 修士, 情報理工, 東京大学 2006-2008
  3. 3. 目標 • ディープラーニングと画像認識の理解 • Caffe2フレームワークの基本を理解 • PythonによるCaffe2の使い方を学習
  4. 4. Caffe2によるディープラーニング Caffe2って? ディープラーニングの基本 なぜ今、深層学習? ニューラルネットワークの基本 様々なネットワーク 誤差逆伝播法 畳み込みニューラルネットワーク (CNN) 代表的なCNNアーキテクチャ Caffe2フレームワーク Caffe2の特徴 Caffe2のAPI概要 Model Zoo Caffe2のビルドと環境設定 Buildの手順 Dockerで環境構築する方法 Pythonについて Pythonと数値計算 Caffe2のPython API Jupyter環境 Caffe2の具体的な使い方 線形回帰 一般物体認識 手書き文字の学習 学習データの準備について 転移学習 ONNX Q&A
  5. 5. Caffe2 • Caffe 1.0の流れを汲むディープラーニン グフレームワーク – 大規模分散学習 – モバイル動作 – ハードウェアサポートの拡大 – 新しい演算方式への対応 – Facebookアプリでの実使用テスト • 軽量、モジュラー、スケーラビリティ
  6. 6. Caffe 1.0 • Convolutional Architecture for Fast Feature Embedding – 前身はDeCAF (Deep Convolutional Activation Feature) • Yangqing Jiaら当時Berkeleyのメンバーが中心に2013年頃開 発したオープンソースのディープラーニングフレームワーク – コードを書かなくても設定ファイルだけで動かせる – 容易に拡張できるコード設計 – C++実装、GPUをシームレスに使い分けて高速な実行 – PythonとMatlabからも使える – Githubの活発な開発コミュニティ • 画像認識向き
  7. 7. Caffe2: 細粒度の演算 パラメータも明示的な変数として分離、演算子単位のモジュール構造へ
  8. 8. Caffe2: Model Zoo • Berkeley-trained models • Network in Network model • Models from the BMVC-2014 paper "Return of the Devil in the Details: Delving Deep into Convolutional Nets” • Models used by the VGG team in ILSVRC-2014 • Places-CNN model from MIT. • GoogLeNet GPU implementation from Princeton. • Fully Convolutional Networks for Semantic Segmentation (FCNs) • CaffeNet fine-tuned for Oxford flowers dataset • CNN Models for Salient Object Subitizing. • Deep Learning of Binary Hash Codes for Fast Image Retrieval • Places_CNDS_models on Scene Recognition • Models for Age and Gender Classification. • GoogLeNet_cars on car model classification • ParseNet: Looking wider to see better • SegNet and Bayesian SegNet … モデルの公開レポジトリ、さらにCaffe1のモデルを移植して使用可能
  9. 9. ONNX • フレームワーク間でのネットワー ク相互利用フォーマット – PyTorch – Caffe2 – CNTK – (開発中) MXNet – (開発中) Tensorflow – (開発中) Chainer • PyTorchで学習したモデルを Caffe2でモバイルデプロイという 使い方
  10. 10. Caffe2 開発状況 2017年11月時点で は v0.8.1 アクティブなコ ミットが続く 安定版のリリース はもう少し先か
  11. 11. ディープラーニングフレームワーク Caffe2 Torch / PyTorch Keras / Tensorflo w MXNet Chainer CNTK Symbolic Imperative Symbolic / Imperative* Symbolic / Imperative* Imperative Symbolic ONNX ONNX ONNX experimental ONNX experimental ONNX Facebook Facebook Google Amazon PFN Microsoft その他: DyNet, Caffe 1.0, Theano, Nnabla, … 開発スタイルに合わせて適切なものを選択すると良い *Eager *Gluon
  12. 12. プログラミングスタイル Symbolic A = Variable('A') B = Variable('B') C = B * A D = C + Constant(1) # compiles the function f = compile(D) d = f(A=np.ones(10), B=np.ones(10)*2) Imperative import numpy as np a = np.ones(10) b = np.ones(10) * 2 c = b * a d = c + 1 • 記述をすぐに実行 • 最適化が難しい • デバッグが容易 • 計算グラフを最初にコンパイル • 最適化容易 • デバッグが難しい ImperativeなPyTorchで設計開発、SymoblicなCaffe2でデプロイという使い分け
  13. 13. Caffe2の向き不向き 向き • 今後モバイル環境でディープ ラーニングアプリケーション をデプロイ • 既存のCaffe 1.0資産がある • PyTorchや他フレームワークを 使っていて異なる環境でデプ ロイしたい • 大規模分散学習 不向き • ディープラーニングを使った 最先端の研究 – GANで動画生成、新しい機械翻訳 モデル、など • 初心者がディープラーニング を一から学ぶ教材 • コードベースが安定したフ レームワークを使いたい
  14. 14. ディープラーニングの基本
  15. 15. ディープラーニング • 深い層構造を持った機械学習モデル – 人工ニューラルネットワーク (Artificial Neural Networks) – 大量のパラメータに対し大量のデータを用い て学習 • Caffeはディープラーニングのためのソフ トウェアフレームワーク
  16. 16. なぜディープラーニング? • 圧倒的な性能 Result in ILSVRC over the years ILSVRCでの毎年のエラー率の推移 物体カテゴリ認識 slide credit: Jia Deng 物体カテゴリ検出 http://image-net.org/challenges/ilsvrc+coco2016 人間は0.04 程度? 2012年からディープ ラーニング登場
  17. 17. ILSVRC 画像分類タスクSVRC image classification (CLS) task Steel drum 1000 object classes 1,431,167 images CLS-LOC slide credit: Jia Deng Error 0.03
  18. 18. ILSVRC 物体検出タスク slide credit: Jia Deng Error 0.077
  19. 19. 畳み込みニューラルネットワーク • 画像認識によく使われるディープ・ ニューラルネットワーク Y. LeCun, L. Bottou, Y. Bengio, and P. Haffner, Gradient-based learning applied to document recognition, Proceedings of the IEEE 86(11): 2278–2324, 1998. Many slide credits: Rob Fergus (NYU)
  20. 20. これまでの画像認識との違い R G RED APPLE 従来の画像認識手法 ディープラーニング 1) 画像特徴の抽出 2) データ点の判別 RED APPLE 特徴表現から判別まで複数の情報変換を学習 データから全て学習 人が設計
  21. 21. 浅い vs 深いアーキテクチャ Image/ Video Pixels Object Class Object Class Image/ Video Pixels 従来手法: 浅いアーキテクチャ ディープラーニング: 深いアーキテクチャ … 手動設計の 特徴抽出手法 学習できる 識別器 Layer 1 Layer N 単純な 識別器 HOG, SIFT SVM, Random Forest Neural networks
  22. 22. ニューラルネットワーク • 多数の非線形関数ユニットが結合して構 成される数理モデル – 複雑な関数を近似できる R G Y = F(X) x1 x2 x3 x4 y1 y2 Input layer Hidden layer Output layer
  23. 23. ニューロン 脳の神経細胞
  24. 24. パーセプトロン x1 x2 xd w1 w2 w3 x3 wd Sigmoid関数: 入力 重み (Weights) . . . t e t    1 1 )( 出力: (wx + b) 人工的な神経細胞のモデル関数 パーセプトロンの積み重ねが ニューラルネットワーク
  25. 25. Hubel-Wiesel Architecture • D. Hubel and T. Wiesel (1959, 1962, ノー ベル賞1981) • 視覚野は simple, complex, hyper-complex 細胞の階 層構造 Source
  26. 26. ユニットの形 𝜎(𝑧) = 1 1 + 𝑒−𝑧 非線形変換 (活性化関数)線形変換 + Sigmoid Tanh tanh(𝑧) = 𝑒2𝑧 − 1 𝑒2𝑧 + 1 relu(𝑧) = max(𝑧, 0) ReLU 𝑧 = 𝑊𝒙 + 𝑏 Inner product 𝑧 = conv(𝒘, 𝒙) Convolution softmax(𝑧) = 𝑒−𝑧 𝑧′ 𝑒−𝑧′ Softmax
  27. 27. 様々なニューラルネットワーク 順伝播型ニューラルネット 再帰型ニューラルネット (RNN) 畳み込みニューラルネット (CNN) • 結合が再帰的なもの • 系列データの利用 • 画像のフィルタ演算がユニット • 画像などの空間配列データに利用 その他、オートエンコーダ、ボルツマンマシン
  28. 28. 学習と予測 学習データ モデル テストデータ 結果 学習 予測 Y = F(X) {(X,Y)} F
  29. 29. 学習: モデル推定 • ニューラルネットワークの重みが損失関 数を小さくするように最適化 – 例: 二乗誤差 min 𝑊 𝑖 𝑦𝑖 − 𝑦 (𝒙𝑖, 𝑊) 2 正解値 出力値 ネットワークのパラメータ (ニューロンの重み) 入力値 𝑦𝒙
  30. 30. 勾配降下法 (Gradient descent) • 非線形関数の最適化手法 • 局所的に線形近似、勾配 方向に現在の解を更新 損失関数 (Loss function) L(X, W) = || y – y(x, W) ||2 Wt Wt+1 e 𝑊𝑡+1 ⟵ 𝑊𝑡 − 𝜀𝛻𝐿(𝑋, 𝑊𝑡) 損失関数の勾配学習率 現在の パラメータ 学習率:どれだけ現在の解を動かすか W
  31. 31. 確率的勾配降下法 (SGD: Stochastic Gradient Descent) • 勾配計算をデータセットからのランダム サンプル(=バッチ)で近似したもの – 損失関数の微分は計算量が大きいため 𝑊𝑡+1 ⟵ 𝑊𝑡 − 𝜀𝛻 𝐿(𝑋, 𝑊𝑡) 𝐿( 𝑋, 𝑊𝑡) = 𝑖∈ 𝐷 𝑦𝑖 − 𝑦 (𝒙𝑖, 𝑊) 2 学習データ D バッチ D’バッチロス バッチの勾配
  32. 32. モーメンタム (Momentum) • 確率的勾配降下法ではバッチによって勾 配が変動するので、前回の更新と今回の 勾配を使ってパラメータ更新を安定化 𝜃𝑡+1 ⟵ 𝜃𝑡 + 𝜇𝛿𝜃𝑡 − 𝜀𝛻 𝐿 𝑋, 𝜃𝑡 𝛿𝜃𝑡 = 𝜃𝑡 − 𝜃𝑡−1 モーメンタム 今回の勾配値前回の更新 学習係数 (Learning rate)
  33. 33. 誤差逆伝播法 (Back propagation) • ニューラルネットワークの学習時に効率 的に微分計算する手法 – 微分のチェインルール(連鎖律) – パラメータの勾配計算に使う 𝒙 𝑦 順伝播 𝐿 逆伝播 𝜕𝐿 𝜕𝒙 𝐿
  34. 34. 逆伝播計算 𝜕𝐿 𝜕𝑥𝑖,𝑗 = 𝑘 𝜕𝐿 𝜕𝑥𝑖+1,𝑘 𝜕𝑥𝑖+1,𝑘 𝜕𝑥𝑖,𝑗 j 𝜕𝒙𝑖+1 𝜕𝑥𝑖,𝑗 𝜕𝐿 𝜕𝑥𝑖,𝑗 𝜕𝐿 𝜕𝑥𝑖+1,1 𝜕𝐿 𝜕𝑥𝑖+1,2 𝜕𝐿 𝜕𝑥𝑖+1,3 第i層 第i+1層 第 i+1 層の出力についての偏微分があれば、第 i 層の出力に ついての損失関数の偏微分がチェインルールで計算可能 𝜕𝑥𝑖+1,𝑘 𝜕𝑥𝑖,𝑗 = 𝑤𝑖,𝑗,𝑘 𝑥𝑖+1,𝑘(𝑥𝑖+1,𝑘 − 1) 例: sigmoidの場合
  35. 35. パラメータ勾配の計算 最終的に求めたいもの: 損失関数Lに対するパラメータwの偏微分 𝜕𝐿 𝜕𝑤𝑖,𝑗,𝑘 = 𝜕𝐿 𝜕𝑥𝑖+1,𝑘 𝜕𝑥𝑖+1,𝑘 𝜕𝑤𝑖,𝑗,𝑘 1. 損失に対する層出力の偏微 分を逆伝播で計算 𝐿 𝜕𝐿 𝜕𝒙 2. 損失に対するパラメータの 偏微分は層ごとに解析的に計算 𝜕𝑥𝑖+1,𝑘 𝜕𝑤𝑖,𝑗,𝑘 = 𝑥𝑖,𝑗 𝑥𝑖+1,𝑘(𝑥𝑖+1,𝑘 − 1) 例: sigmoidの場合
  36. 36. よく使われる損失関数 Euclidean (二乗誤差) Cross Entropy (交差エントロピー) 𝑖 𝑦𝑖 − 𝑦𝑖 2 − 𝑖 𝑝 𝑖 𝑝𝑖 ln 𝑝𝑖 二値分類: Sigmoid + Cross entropy 多クラス分類: Softmax + Cross entropy 回帰問題に利用 出力が確率分布の場合に利用
  37. 37. 学習データの分割 学習 Train 検証 Validation テスト Test • SGDでパラメー タの学習に利用 • 学習時の現在の性 能確認用 • ハイパーパラメー タの調整に利用 • 学習係数 • モーメンタム • 性能評価に利用 • 最後まで取っておく • Validationと兼ねる 場合もあり • データは一般的に3つまたは2つに分割 • 分割割合は8:1:1など、一般に学習データが多めだが、5:5などもあり
  38. 38. 学習の進捗 反復数 (iteration): バッチをSGDに投入した回数 エポック数(epoch): 学習データセットの一周回を使った回数 Validationデータの性能 Trainデータの損失 batch epoch
  39. 39. 過学習、局所解 Validation error Training error #epochs loss データに対してモデルのパラメータ数が多すぎると汎化誤差が増大
  40. 40. 初期値問題 Fine-tuned #epochs loss パラメータの初期値によって局所解で収束 Scratch学習ではなく学習済みモデルからのFine-tuningなどで解決 Scratch
  41. 41. 勾配消失問題 (Vanishing Gradient) 𝐿 𝜕𝐿 𝜕𝒙 非線形変換 微分値が 小さい • チェインルールで微分を計算 していくと、小さな値の掛け 合わせで微分が0に近づく問題 • 深層ネットワークの学習の難 しさの主要な要因 • LSTMやResNetなどで解決
  42. 42. 重み減衰 (Weight decay) • ネットワークの重みの発散を防ぐ正則化 • 一般にごく小さなλ値 min 𝑊 𝑖 𝑦𝑖 − 𝑦 (𝒙𝑖, 𝑊) 2 + 𝜆 2 𝑊 2 損失関数 正則化項
  43. 43. Batch Normalization • バッチ単位の平均と分散を計算 – テスト時は訓練データかインスタンス単位 • 入力を正規化 • 正則化効果、大きな学習係数 𝜇 𝐵 = 1 𝑚 𝑖 𝑚 𝒙𝑖 𝜎 𝐵 2 = 1 𝑚 𝑖 𝑚 𝒙𝑖 − 𝜇 𝐵 2 𝒙𝑖 = 𝒙𝑖 − 𝜇 𝐵 𝜎 𝐵 2 + 𝜀 𝒚𝑖 = 𝛾 𝒙𝑖 + 𝛽 [Ioffe 2015]
  44. 44. Dropout • 一定の確率でユニットの出力を0に • 一種の正則化、過学習を防ぐために有用 – Batch Normalizationがあれば不要のことも [Hinton 2012] x1 x2 x3 x4 y1 y2
  45. 45. その他テクニック • データ拡張 (data augmentation) – データにノイズを加えてデータ数拡張 • Data-dependent weight initialization – パラメータをデータ分布に従って初期化 • SGD improvement: AdaGrad, ADAM – 学習率の自動調整 • Model ensemble – 複数モデルを組み合わせて予測 • Weight normalization – 重みの正則化の一種
  46. 46. 畳み込みニューラルネットワーク (CNN) • 画像認識向けにフィルタ演算ユニットを組み込んだ ニューラルネット • 前段は複数回の畳み込みで画像特徴の抽出 • 後段ではより不変的な特徴、最後は分類確率出力 Y. LeCun, L. Bottou, Y. Bengio, and P. Haffner, Gradient-based learning applied to document recognition, Proceedings of the IEEE 86(11): 2278–2324, 1998.
  47. 47. 古典的CNNの基本構造 • 順伝播の特徴抽出層 – 畳み込み演算 – 非線形変換 – プーリング – (正規化) • 畳み込みのフィルタ の重みをバックプロ パゲーションで学習 入力画像 畳み込み (学習対象) 非線形変換 プーリング 特徴量マップ レイヤー
  48. 48. 1. 畳み込み (Convolution) • 依存関係は局所的 • 位置不変 • パラメータ数が少ない • ストライドは1以上も可 • メモリと演算量削減 Input Feature Map . . . Slide credit: Rob Fergus
  49. 49. 2. 非線形変換 (Non-linearity) • 画素ごとに適用 • よく使われるもの – ReLU: max(x, 0) • バックプロパゲーショ ンが簡単 • 学習速度も速い – Sigmoid: 1/(1+exp(x)) – Tanh Slide credit: Rob Fergus ReLU Tanh Sigmoid
  50. 50. 3. プーリング (Spatial pooling) • SUM, MAX, AVERAGEなど • 領域は重ねても重ねなくてもOK – 微小な空間変化への対応 – 大きな Receptive field (入力空間範囲) Max Sum Slide credit: Rob Fergus
  51. 51. 4. 正規化 (Normalization) • 特徴マップの中またはマップ間で • プーリングの前後どちらでも良い Feature Maps Feature Maps After Contrast Normalization Slide credit: Rob Fergus
  52. 52. CNNの内部表現 M. Zeiler and R. Fergus, Visualizing and Understanding Convolutional Networks, arXiv preprint, 2013 Slide credit: Rob Fergus
  53. 53. Layer 1 Filters Slide credit: Rob Fergus
  54. 54. Layer 1: Top-9 Patches Slide credit: Rob Fergus
  55. 55. Layer 4: Top-9 Patches Slide credit: Rob Fergus
  56. 56. Layer 4: Top-9 Patches Slide credit: Rob Fergus
  57. 57. 代表的なCNNアーキテクチャ • LeNet [LeCun 1998] • AlexNet [Krizhevsky 2012] • VGG [Simonyan 2014] • GoogLeNet [Szegedy 2014] • ResNet [He 2015] • Xception [Challet 2016] • PolyNet [Lin 2016]
  58. 58. AlexNet • ImageNet 2012最高性能 • 畳み込み5層+全結合層3層 • サイズが比較的小さく、基準手法としてよく使われるCNN [Krizhevsky 2012] https://leonardoaraujosantos.gitbooks.io/artificial-inteligence/content/image_folder_7/AlexNet_0.jpg
  59. 59. VGG [Simonyan 2014] https://www.cs.toronto.edu/~frossard/post/vgg16/ • ImageNet 2014 runner-up • AlexNetを拡張し、多段の畳み込み層、小さなフィルタを 導入したアーキテクチャ
  60. 60. GoogLeNet http://joelouismarino.github.io/ Inception moduleに よる複数経路の演算 Inception modules Stem Auxiliary classifiers Output (Inception V1) ImageNet 2014最高性能 [Szegedy 2013]
  61. 61. ResNet [He 2015] http://felixlaumon.github.io/ https://culurciello.github.io/tech/2016/06/04/nets.html • ImageNet 2015最高性能 • Residual block (Identity + Conv)に よって超深層を実現
  62. 62. DenseNet [Huang 2017] • Dense blockでパラメータ削減し つつResNetよりも高性能化
  63. 63. アーキテクチャとモデルの大きさ https://culurciello.github.io/tech/2016/06/04/nets.html
  64. 64. RNN • 再帰型の結合を持つニューラルネット • 可変長の系列データに対して使用 – 時系列の予測 • 再帰的結合は展開したものと等価 http://colah.github.io/posts/2015-08-Understanding-LSTMs/
  65. 65. Long Short-Term Memory (LSTM) • RNNのユニットに長期短期記憶を組み入れたもの • 長距離の依存関係、勾配消失問題に対処可能 http://colah.github.io/posts/2015-08-Understanding-LSTMs/
  66. 66. ディープラーニングまとめ • 深層ニューラルネットワーク – 複数のユニットを階層的に結合して構築され る数理モデル – 学習: 損失関数を最小化するように確率的勾 配降下法でネットワークのパラメータを更新 • 畳み込みニューラルネット (CNN) – 画像認識のために畳み込み演算を基本とする ニューラルネットワーク
  67. 67. CAFFE2フレームワーク
  68. 68. Caffe2の特徴 • Tensorflow同様にSymbolicスタイル • 速さやスケーラビリティを追求 – 逆に深層学習モデルを実験的に研究開発する 用途には使いにくい – PyTorch development  Caffe2 deployment • Caffe 1.0学習済みのモデルが使用可能 • 画像認識系用途に強み • まだ開発途上
  69. 69. Caffe2の機能 学習 • モデル定義を作成 • データを準備 • 学習を実行 • 学習したモデルパラメー タを保存 デプロイ • モデル定義とパラメータ を準備 • アプリケーションで利用 exec exec init_net
  70. 70. ブロブ (blob) データを格納するN次元の配列 データやレイヤーのパラメータに利用 … C H W N N x C x H x W サンプル数 チャネル 高さ 幅 画像の場合
  71. 71. ワークスペース (Workspace) • シンボリック計算の実行環境 – ワークスペースにブロブ(変数)を用意し、 データを代入して利用 from caffe2.python import core, workspace import numpy as np # Create random tensor of three dimensions x = np.random.rand(4, 3, 2) print(x) print(x.shape) workspace.FeedBlob("my_x", x) x2 = workspace.FetchBlob("my_x") print(x2)
  72. 72. オペレーター (Operators) • ブロブに演算を施す関数のようなもの – 通常は複数を組み合わせたHelperを使用 Relux y # Create an operator op = core.CreateOperator( "Relu", # The type of operator that we want to run ["X"], # A list of input blobs by their names ["Y"], # A list of output blobs by their names )
  73. 73. 様々なオペレーター Accumulate AccumulateHistogram Accuracy Add AddPadding Alias Allgather Allreduce And Append AtomicAppend AtomicFetchAdd AveragePool AveragePoolGradient AveragedLoss AveragedLossGradient BatchBoxCox BatchMatMul BatchOneHot BatchToSpace BooleanMask BooleanMaskLengths BooleanUnmask Broadcast Cast CheckAtomicBool CheckCounterDone CheckDatasetConsistency Checkpoint Clip ClipGradient Col2Im CollectTensor ComputeOffset Concat ConcatTensorVector ConditionalSetAtomicBool ConstantFill Conv ConvGradient ConvTranspose ConvTransposeGradient Copy CopyCPUToGPU CopyFromCPUInput CopyGPUToCPU CopyOnDeviceLike CosineEmbeddingCriterion CosineEmbeddingCriterionGradient CosineSimilarity CosineSimilarityGradient CountDown CountUp CreateAtomicBool CreateCommonWorld CreateCounter CreateMutex ... https://caffe2.ai/docs/operators-catalogue.html
  74. 74. ネット (Nets) • オペレーターで構成される計算グラフ (プログラム) FCW b Sigmoid loss x y Cross Entropy accuracy
  75. 75. ネットの構築 from caffe2.python import core net = core.Net("my_first_net") X = net.GaussianFill([], ["X"], mean=0.0, std=1.0, shape=[2, 3], run_once=0) W = net.GaussianFill([], ["W"], mean=0.0, std=1.0, shape=[5, 3], run_once=0) b = net.ConstantFill([], ["b"], shape=[5,], value=1.0, run_once=0) Y = X.FC([W, b], ["Y"]) Core APIを使った一番原始的な方法
  76. 76. Model helper/brewを使う場合 from caffe2.python import model_helper, brew model = model_helper.ModelHelper(name="train_net") fc1 = brew.fc(model, "data", "fc1", 100, 10) from caffe2.python import model_helper model = model_helper.ModelHelper(name="train_net") # init your weight and bias as w and b w = model.param_init_net.XavierFill([], "fc_w", shape=[10, 100]) b = model.param_init_net.ConstantFill([], "fc_b", shape=[10, ]) # finally building FC fc1 = model.FC(["data", w, b], "fc1") 新しいbrew APIでさらに短縮可能に パラメータの初期化込みでOperator作成
  77. 77. 順伝播と逆伝播 forward and backward • 順伝播 (forward) – 入力から出力へ • 逆伝播 (backward) – 誤差を使って勾配を出 力から入力へ http://caffe.berkeleyvision.org/tutorial/net_layer_blob.html forward backward
  78. 78. Directed Acyclic Graph (DAG) • Caffe2のnetはループなしの有向グラフ • RNN / LSTM cellは一つのOperatorとして 実装
  79. 79. 損失関数 • Forward時にOperatorで計算 – E.g., SoftmaxWithLoss operator – 出力はスカラ – Weightパラメータで重み付け lossSoftmax WithLoss Wx+b label
  80. 80. データ入力 • データを読み込むオペレータか、ワークスペー スに都度代入 – 通常はあらかじめ指定フォーマットでファイル用意 – e.g., TensorProtosDBInput: Key-value DB形式 (LMDBなど) – 他の形式も可、簡単な前処理のオペレータも存在 data Tensor Protos DBInput
  81. 81. 学習 • バックプロパゲーションはGradient Operatorを使う # Add gradient operator before initialization. model = model_helper.ModelHelper(name="my first net") ... model.AddGradientOperators([loss])
  82. 82. GPUサポート • CPUとGPUで透過的 に利用可能 – ビルド設定 – 実行時にデバイス切り 替え • 学習にはほぼ必須 [NVIDIA GTX 1080]
  83. 83. オンラインドキュメント https://caffe2.ai/
  84. 84. CAFFE2のビルドと環境設定
  85. 85. Caffe2の入手 • 手軽に試すな らDocker • 基本はGithub 経由で入手し、 ビルド • Cloud利用なら 環境が用意あ り
  86. 86. 注意 • 2017/11時点でビルド済みパッケージの配 布なし、Python3.x系使用不可 • Caffe2は開発途上のため将来的にビルドや 配布環境に変更がありえます • 最新版はビルドに失敗することも多々あ るため、試すだけならDocker利用推奨
  87. 87. Caffe2の動作環境について • Linux/Macプラットフォーム推奨 – WindowsからはDocker利用、ただし実用には 不向き – AWS利用という方法も • GPU利用にはCUDAライブラリ • その他BLAS, Boost, OpenCVなど
  88. 88. Docker • コンテナ化による仮想実行環境構築プラットフォーム – 開発環境と大規模サービス展開で同一環境 – コンテナにより完全仮想化よりも軽量動作 – nvidia-dockerによりGPUも使用可 Container PC Cloud Docker Runtime App Container Runtime App Container Runtime App Jupyter Ubuntu, Caffe2 Windows, Mac, Linux
  89. 89. Dockerの入手 • Windows / Mac / Linux https://www.docker.com/
  90. 90. nvidia-docker • Linux/MacのGPUホス トで実行する場合は nvidia-dockerを Githubから入手 • ドライバーの事前イ ンストール必須 • 詳細はREADMEを https://github.com/NVIDIA/nvidia-docker
  91. 91. DockerでCaffe2環境構築 • 仮想環境の実行(CPUのみ) • GPUあり docker pull caffe2ai/caffe2 # to test nvidia-docker run -it caffe2ai/caffe2 python -m caffe2.python.operator_test.relu_op_test # to interact nvidia-docker run -it --rm –p 8888:8888 caffe2ai/caffe2:latest /bin/bash docker pull caffe2ai/caffe2 # to interact docker run -it --rm –p 8888:8888 caffe2ai/caffe2:c2v0.8.1.cpu.full.ubuntu14.04 /bin/bash
  92. 92. Dockerイメージのビルド • Dockerイメージを自分でビルドする場合 はCaffe2のGitリポジトリ使用 • 自分で Dockerfile を記述して作成可能 git clone https://github.com/caffe2/caffe2.git cd caffe2 # CPU-onlyイメージのビルド docker build -t mycaffe2:cpu docker/ubuntu-14.04-cpu-all-options
  93. 93. ビルドのワークフロー 1. プラットフォームの準備 – macOS, Ubuntu Linux 16.04, or 14.04 2. 依存ライブラリのインストール 3. ビルド – GitとCMake 4. Python環境の設定
  94. 94. Ubuntu Linux 16.04の場合 • 事前に必要なパッケージをインストール sudo apt-get update sudo apt-get install -y --no-install-recommends build-essential cmake git libgoogle-glog-dev libprotobuf-dev libgflags-dev protobuf-compiler python-dev python-pip python-pydot libgtest-dev libiomp-dev libleveldb-dev liblmdb-dev libopencv-dev libopenmpi-dev libsnappy-dev openmpi-bin openmpi-doc sudo pip install numpy protobuf flask future graphviz hypothesis jupyter matplotlib pydot python-nvd3 pyyaml requests scikit-image scipy setuptools six tornado
  95. 95. GPUを使う場合 • CUDA関連ライブラリをインストール • (Optional) cuDNNをインストール sudo apt-get update && sudo apt-get install wget -y --no-install-recommends wget "http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/x86_64/cuda-repo- ubuntu1604_8.0.61-1_amd64.deb" sudo dpkg -i cuda-repo-ubuntu1604_8.0.61-1_amd64.deb sudo apt-get update sudo apt-get install cuda CUDNN_URL="http://developer.download.nvidia.com/compute/redist/cudnn/v5.1/cudnn-8.0-linux- x64-v5.1.tgz" wget ${CUDNN_URL} sudo tar -xzf cudnn-8.0-linux-x64-v5.1.tgz -C /usr/local rm cudnn-8.0-linux-x64-v5.1.tgz && sudo ldconfig
  96. 96. cuDNN • オプションでNVIDIA のcuDNNライブラリ を使用可 – 通常のGPU演算からさ らに演算速度が向上 • インストール方法 https://developer.nvidia.com/cudnn 1. NVIDIAサイトにユーザー登録し、ダウンロード 2. 手順に従ってシステムにインストール
  97. 97. CMakeビルドとインストール • Githubからソースを入手しCMakeビルド git clone --recursive https://github.com/caffe2/caffe2.git cd caffe2 mkdir build cd build cmake .. make -j4 sudo make install # No error below if everything succeeds. python -c 'from caffe2.python import core' # Check GPU functionality with the following. python -m caffe2.python.operator_test.relu_op_test 詳細は https://caffe2.ai/docs/getting-started.html にて
  98. 98. Anaconda • Pythonのデータ分析関連 のライブラリをひとまと めにパッケージしたもの • Caffe2にも使用可能 • 便利だが、ライブラリの ビルドパスや依存関係で 問題が発生することも • pyenv経由のインストー ルがオススメ https://www.continuum.io/
  99. 99. トラブルシュート • ビルドエラーが出た場合は焦らずエラー 内容を検索 – ビルドの依存ライブラリが不足していたりパ スの設定ミスがないか確認 • Anacondaパッケージで依存関係のコンフ リクトが起きる場合もある – ヒント: lddツールでコンフリクトを発見 ldd build/lib/libcaffe2.so
  100. 100. Cloud環境 Microsoft Azure
  101. 101. PYTHONについて
  102. 102. なぜPython? • インタラクティブにCaffe2を使う方法 – Caffe2ではNumpyを使ったAPIを提供 – 学習時にPythonを使い、モバイルデプロイ時 にC++も使用可 • 科学技術計算向けライブラリが充実 – 機械学習ではPythonが標準的 • フリー
  103. 103. Jupyter Webブラウザからプログラムを動かすインタラクティブシェル環境 - ノートブック形式によるPythonプログラミング - UNIXターミナルの起動 - 通常のテキストファイル編集 - プラグインによりPython以外の言語にも対応
  104. 104. Jupyter notebook
  105. 105. Jupyterのインストール pip install jupyter jupyter notebook jupyter notebook --generate-config インストールはpipでOK 起動、デフォルトではブラウザも立ち上げ 起動時のデフォルトオプションを作成する場合 jupyter notebook --help その他ヘルプ
  106. 106. Docker演習: Jupyterの起動 docker run -it --rm -p 8888:8888 caffe2ai/caffe2:c2v0.8.1.cpu.full.ubuntu14.04 jupyter notebook --ip '*' --allow-root --no-browser Docker consoleから以下のコマンドを実行 • caffe2ai/caffe2インスタンスを立ち上げ、 c2v0.8.1.cpu.full.ubuntu14.04 バージョン指定 • -it 起動はinteractive terminal環境で • --rm 終了後は自動でインスタンス削除、再利用する場合は使わない • -p 8888:8888 インスタンスのTCPポート8888をホストのTCPポート8888 に接続 • インスタンスは jupyter notebook コマンドを実行 • --ip '*' はnotebookから全てのIPアドレスをLISTEN • --allow-root はユーザーがrootでも実行許可 • --no-browser インスタンス内ブラウザの自動起動なし • インスタンス起動後はWeb browserを開き localhost:8888 に接続、画面 上に表示されるトークンを使ってログイン • 終了は Ctrl-C, Y で
  107. 107. Docker演習: Jupyterの起動画面 [I 02:31:27.033 NotebookApp] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation). [C 02:31:27.033 NotebookApp] Copy/paste this URL into your browser when you connect for the first time, to login with a token: http://localhost:8888/?token=a24c6a0e11243ef0d0888b7a01e04c0a4ffb136509017e91 Docker console Browser
  108. 108. Docker演習: Notebookの作成 Newメニューから Python 2 ノートブックを新規作成 新規ノートブック画面
  109. 109. Jupyter環境 tips !ls UNIXコマンドの実行: ! をつけてコマンド入力 np.nonzero? 関数のヘルプは関数名の最後に ? を print("Hello, jupyter!") Shift+Enterでセルの実行、他にも便利なショートカット %matplotlib inline Matplotlibのプロットはインライン表示可能
  110. 110. Pythonの基本 # 条件分岐、ループの文法 if A and B: print('A and B') for i in range(10): print(i) # データ型 [int, float, bool, str, list, dict] [7, 1.6, True, 'hello', [], {}] 一般的なデータ型やコンテナ型を利用可能 制御構造 (if文, forループ) はインデントで記述 # 演算子 1 + 2 1 - 2 2 * 3 4 / 2 True and True True or False 一般的な演算が利用可能
  111. 111. コンテナ # list (リスト) X = [] for i in range(10): X.append(i ** 2) print(X) # [0, 1, 4, 9, 16, ...] # 内包表記 X = [i ** 2 for i in range(10)] print(X[2]) # => 4 # dict (辞書) X = {} for i in range(10): X[i ** 2] = i print(X) # {0: 0, 1: 1, 4: 2, ...} # 内包表記 X = {i ** 2: i for i in range(10)} print(X[4]) # => 2 コンテナは他のデータを格納 例: 2乗列 例: 2乗整数から整数へのマップ
  112. 112. 文字列 # 文字列はシングルクオートまたはダブルクオートで text1 = 'シングルクオートの例' text2 = "ダブルクオートの例" # 文字列のフォーマットは{}で template = 'I have a {}. I have an {}. Ah, {}' print(template.format('pen', 'apple', 'apple-pen')) # => I have a pen. I have an apple. Ah, apple-pen. print('{2}, {1}, {0}'.format('a', 'b', 'c')) # => c, b, a print('{0:+f}, {1:-1.4f}'.format(3.14, -3.14)) # => +3.14, -3.1400
  113. 113. 関数とクラス def sign(x): '''符号を返す関数''' if x > 0: return 'positive' else: return 'negative' for x in [-1, 0, 1]: print(sign(x)) 関数定義はdefブロックで class MyClass: '''クラスの例''' i = 12345 def f(self): return 'hello world' x = MyClass() print(x.f()) クラス定義も同様
  114. 114. コメントとHeredoc # コメントは#で始める text = ''' Heredocは3つのシングルクオート(') またはダブルクオート(") で囲った複数行の文字列 ''' text2 = """ ダブルクオートの例 """ def func(): '''関数のヘルプにはHeredocが使われる''' return 0
  115. 115. Python 2とPython 3 • Python 2.x系列とPython 3.x系列は非互換なので注意 # print文は関数に print 'Python 2' # 整数の除算 3 / 2 # => 1 # unicodeとbytes type(b'hello') # => str # xrangeの廃止 for i in xrange(10): # print文は関数に print('Python 3') # 整数の除算 3 / 2 # => 1.5 # unicodeとbytes type(b'hello') # => bytes # xrangeの廃止 for i in range(10): Python 3 (Caffe2未対応)Python 2
  116. 116. • 作成したファイルは他からモジュールとして呼び出した りパッケージとして利用可能 Pythonモジュールとパッケージ import myutils myutils.func() import mypackage.apple from mypackage import apple myutils.py mypackage/ mypackage/__init__.py mypackage/apple.py mypackage/pineapple.py プログラムファイル
  117. 117. Python環境のインストール sudo apt-get install python python-pip sudo apt-get install python3 python3-pip Python 2の場合 Python 3の場合 curl -L https://raw.githubusercontent.com/pyenv/pyenv-installer/master/bin/pyenv-installer | bash pyenv install anaconda3-5.0.0 pyenv global pyenv+anaconda3の場合 https://github.com/pyenv/pyenv Docker環境ではあらかじめインストール済み
  118. 118. Pythonと数値計算 • Pythonの科学技術計算パッケージ群 – ほぼMATLABと等価な機能を提供 – インストールはpipやapt-get、Anacondaなど Python Python本体 Numpy 数値計算のコアライブラリ Scipy 科学技術計算ライブラリ Matplotlib プロット用ライブラリ IPython 高機能インタラクティブシェル pandas データ解析用ライブラリ Sympy シンボリック計算 nose ユニットテスト
  119. 119. pip • Pythonのパッケージマネージャ – PyPIレポジトリに登録されているPythonライ ブラリは全てpipコマンドで入手可能 • インストールはコマンドラインから pip install numpy scipy matplotlib [Python software foundation]
  120. 120. Numpy • 多次元配列を扱うライブラリ import numpy as np # numpyをnpという名前でインポート # ネストされたリストから2次元Numpy配列を作成 X = np.array([[1, 2], [3, 4]]) print(X) # =>[[1 2] [3 4]] • Pythonリストとの変換 • スライス、整数、論理インデックスによる部分読み出し • 演算 • ブロードキャスト
  121. 121. Numpy配列のインデックス # Numpy配列の作成 X = np.array([[1, 2], [3, 4], [5, 6]]) # スライス print(X[1:, :]) #=> [[3 4] # [5 6]] # 整数インデックス print(X[[0, 1, 2], [0, 1, 0]]) #=> [1 4 5] # 論理インデックス print(X[X > 2]) #=> [3 4 5 6] 1 2 3 4 5 6 1 2 3 4 5 6 1 2 3 4 5 6
  122. 122. Numpy配列の演算 x = np.array([[1, 2],[3, 4]], dtype=np.float64) y = np.array([[5, 6],[7, 8]], dtype=np.float64) print(x + y, np.add(x, y)) print(x - y, np.subtract(x, y)) print(x * y, np.multiply(x, y)) print(x / y, np.divide(x, y)) print(np.sqrt(x)) print(np.dot(x, y)) print(np.sum(x)) # 全ての要素の和 print(np.sum(x, axis=0)) # 列ごとに和 print(np.sum(x, axis=1)) # 行ごとに和
  123. 123. Numpy配列のブロードキャスト # 配列の作成 x = np.array([[ 1, 2, 3], [ 4, 5, 6], [ 7, 8, 9], [10, 11, 12]]) v = np.array([1, 0, 1]) # ブロードキャスト y = x + v print(y) #=> "[[ 2 2 4] # [ 5 5 7] # [ 8 8 10] # [11 11 13]]"
  124. 124. Matplotlib import numpy as np import matplotlib.pyplot as plt # 正弦波のxy座標を計算 x = np.arange(0, 3*np.pi, 0.1) y_sin = np.sin(x) y_cos = np.cos(x) # Matplotlibでプロット表示 plt.plot(x, y_sin) plt.plot(x, y_cos) plt.show()
  125. 125. SciPy, Matplotlibでの画像の扱い # scikit-image, matplotlibをインポート import numpy as np from skimage.io import imread import matplotlib.pyplot as plt # 画像を読み込み img = imread('images/cat.jpg') # 画像を表示 plt.imshow(img) plt.show()
  126. 126. Caffe2のPython API # 画像の読み込みや前処理はskimageなどで img = skimage.io.imread(...) ... # 学習済みnetを読み込み p = workspace.Predictor(init_net, predict_net) # 推論実行時はrun results = p.run([img]) # 結果をNumpy形式に変換 results = np.asarray(results)
  127. 127. Caffe2のチュートリアル資料 • caffe2/caffe2/python/tutorials に公 式チュートリアル
  128. 128. CAFFE2の具体的な使い方
  129. 129. Caffe2の具体的な使い方 1. 線形回帰の記述 – 学習とテストの一連の機械学習の流れ 2. 一般画像認識 – デプロイ時のCaffe2の使い方 3. 手書き文字識別器の学習 – Caffe2での学習にまつわる基本 4. ファインチューニング – 実用的な画像認識モデルの学習方法 5. 学習データの準備 – 手元にあるデータを使うために
  130. 130. https://gist.github.com/kyamagu
  131. 131. 1. 線形回帰 • 単純な線形回帰モデルの学習と予測 𝑦 = 𝒘 ∙ 𝒙 + 𝑏 目的: 学習とテストの一連の機械学習の流れの理解
  132. 132. 線形回帰 𝑦 = 𝒘 ∙ 𝒙 + 𝑏 学習: データに合致するパラメータw, bを求める 予測: 学習したパラメータを使って新しいxからyを求める min 𝒘,𝑏 1 𝑁 𝑖 𝑦𝑖 − 𝒘 ∙ 𝒙𝑖 + 𝑏 2
  133. 133. 線形回帰: データの準備 # 今回は人工的にデータを生成し、雑音を加える import numpy as np import matplotlib.pyplot as plt train_label = np.linspace( -10, 10, 1000, dtype=np.float32).reshape( (1000, 1)) train_data = np.concatenate(( -train_label + 4 * np.random.random((1000, 1)), train_label + 3 * np.random.random((1000, 1))), axis=1).astype(np.float32) # プロット、色の濃さはyの値 plt.scatter(train_data[:,0], train_data[:,1], c=train_label) plt.grid("on") plt.show() 𝑦 = 𝑤1 𝑥1 + 𝑤2 𝑥2 + 𝑏 この例では以下のモデル フィッティングに使う データを乱数から作成 x1 x2
  134. 134. 線形回帰: モデル記述 from caffe2.python import brew, model_helper, optimizer, workspace # デプロイ用モデル、入力からfc線形和で出力 def build_deploy_model(model, data): return brew.fc(model, data, "pred", 2, 1) # 学習用モデル、予測モデルに正解値からのバックプロパゲーションを加える def build_train_model(model, data, label): data = model.StopGradient(data) label = model.StopGradient(label) pred = build_deploy_model(model, data) loss = model.net.SquaredL2Distance([pred, label], "loss") model.AddGradientOperators([loss]) opt = optimizer.build_sgd(model, base_learning_rate=1e-5) for param in model.GetOptimizationParamInfo(): opt(model.net, model.param_init_net, param) return model
  135. 135. 線形回帰: モデルの閲覧 deploy_model = model_helper.ModelHelper("deploy_model") build_deploy_model(deploy_model, "data") print(str(deploy_model.net.Proto())) print(str(deploy_model.param_init_net.Proto())) name: "deploy_model" op { input: "data" input: "pred_w" input: "pred_b" output: "pred" name: "" type: "FC" } external_input: "data" external_input: "pred_w" external_input: "pred_b" name: "deploy_model_init" op { output: "pred_w" name: "" type: "XavierFill" arg { name: "shape" ints: 1 ints: 2 } } op { output: "pred_b" name: "" type: "ConstantFill" arg { name: "shape" ints: 1 } } Caffe2では内部にProtocol bufferフォーマットを利用 デプロイ用ネットワーク構造とパラメータ初期化設定
  136. 136. 線形回帰: モデルの描画 from caffe2.python import net_drawer from IPython import display graph = net_drawer.GetPydotGraph(deploy_model.net.Proto().op, "deploy", rankdir="LR") display.Image(graph.create_png(), width=800) net_drawerでグラフ描画が可能
  137. 137. 線形回帰: 学習の実行 # workspaceとモデルの初期化 workspace.ResetWorkspace() train_model = model_helper.ModelHelper("train_model") train_model = build_train_model(train_model, "data", "label") workspace.FeedBlob("data", train_data) workspace.FeedBlob("label", train_label) workspace.RunNetOnce(train_model.param_init_net) # パラメータブロブの初期化 workspace.CreateNet(train_model.net) # モデルの内部ブロブの初期化 losses = [] for epoch in range(0, 10): workspace.FeedBlob("data", train_data) # ここでは1バッチ=1エポック workspace.FeedBlob("label", train_label) # 一般的にはここでミニバッチループ workspace.RunNet(train_model.net, 10) # 10バッチ一度に実行 losses.append(workspace.FetchBlob("loss").sum() / train_label.size) print("epoch={:2g}, loss={:g}".format(epoch, losses[-1])) epoch= 0, loss=0.462717 epoch= 1, loss=0.251159 epoch= 2, loss=0.244591 epoch= 3, loss=0.244353 epoch= 4, loss=0.244312 epoch= 5, loss=0.244279 epoch= 6, loss=0.244247 epoch= 7, loss=0.244218 epoch= 8, loss=0.24419 epoch= 9, loss=0.244164 出力例:
  138. 138. 線形回帰: モデルの書き出し from caffe2.python.predictor import predictor_exporter deploy_model = model_helper.ModelHelper("deploy_model") build_deploy_model(deploy_model, "data") exporter = predictor_exporter.PredictorExportMeta( predict_net=deploy_model.net.Proto(), parameters=[str(param) for param in deploy_model.params], inputs=["data"], outputs=["pred"]) predictor_exporter.save_to_db("minidb", "/tmp/deploy.minidb", exporter) モデルの保存、predictor_exporterを使う • ここではdeploy_modelを書き出しているが、train_modelを保存して学習を 中断、再開することが可能 • 具体例は /caffe2/caffe2/python/examples/resnet50_trainer.py
  139. 139. 線形回帰: 予測 # テストデータを生成 test_label = np.linspace( -10, 10, 100, dtype=np.float32).reshape( (100, 1)) test_data = np.concatenate(( -test_label + 4 * np.random.random((100, 1)), test_label + 3 * np.random.random((100, 1))), axis=1).astype(np.float32) # 保存したモデルの読み込み predict_net = predictor_exporter.prepare_prediction_net( "/tmp/deploy.minidb", "minidb") # あとはデータを入力してRunNetOnce() workspace.FeedBlob("data", test_data) workspace.RunNetOnce(predict_net) prediction = workspace.FetchBlob("pred")
  140. 140. 線形回帰: 予測結果の解釈 # プロット、色の濃さはyの値 plt.scatter(test_data[:,0], test_data[:,1], c=test_label) plt.title("Ground truth") plt.grid("on") plt.show() plt.figure() plt.scatter(test_data[:,0], test_data[:,1], c=prediction) plt.title("Prediction") plt.grid("on") plt.show()
  141. 141. 線形回帰: モバイルデプロイ from caffe2.python.predictor.mobile_exporter import Export init_net, predict_net = Export(workspace, deploy_model.net, deploy_model.params) with open("/tmp/init_net.pb", "wb") as f: f.write(init_net.SerializeToString()) with open("/tmp/predict_net.pb", "wb") as f: f.write(predict_net.SerializeToString()) モバイル向け、Zoo公開の場合はmobile_expoterから書き出し with open("/tmp/init_net.pb", "rb") as f: init_net = f.read() with open("/tmp/predict_net.pb", "rb") as f: predict_net = f.read() predictor = workspace.Predictor(init_net, predict_net) prediction = predictor.run([test_data]) モデルはPredictor APIで読み込んで利用
  142. 142. 線形回帰: プログラム主要部 import numpy as np import matplotlib.pyplot as plt from caffe2.python import brew, model_helper, optimizer, workspace from caffe2.python.predictor import predictor_exporter # 学習、テストデータを生成 train_label = np.linspace( -10, 10, 1000, dtype=np.float32).reshape( (1000, 1)) train_data = np.concatenate(( -train_label + 4 * np.random.random((1000, 1)), train_label + 3 * np.random.random((1000, 1))), axis=1).astype(np.float32) test_label = np.linspace( -10, 10, 100, dtype=np.float32).reshape( (100, 1)) test_data = np.concatenate(( -test_label + 4 * np.random.random((100, 1)), test_label + 3 * np.random.random((100, 1))), axis=1).astype(np.float32) # デプロイ用モデル、入力からfc線形和で出力 def build_deploy_model(model, data): return brew.fc(model, data, "pred", 2, 1) # 学習用モデル、予測モデルに正解値からのバックプロパゲーションを加える def build_train_model(model, data, label): data = model.StopGradient(data) label = model.StopGradient(label) pred = build_deploy_model(model, data) loss = model.net.SquaredL2Distance([pred, label], "loss") model.AddGradientOperators([loss]) opt = optimizer.build_sgd(model, base_learning_rate=1e-5) for param in model.GetOptimizationParamInfo(): opt(model.net, model.param_init_net, param) return model # モデルの学習 workspace.ResetWorkspace() train_model = model_helper.ModelHelper("train_model") train_model = build_train_model(train_model, "data", "label") workspace.FeedBlob("data", train_data) workspace.FeedBlob("label", train_label) workspace.RunNetOnce(train_model.param_init_net) workspace.CreateNet(train_model.net) losses = [] for epoch in range(0, 10): workspace.FeedBlob("data", train_data) workspace.FeedBlob("label", train_label) workspace.RunNet(train_model.net, 10) losses.append(workspace.FetchBlob("loss").sum() / train_label.size) print("epoch={:2g}, loss={:g}".format(epoch, losses[-1])) # モデルの保存 deploy_model = model_helper.ModelHelper("deploy_model") build_deploy_model(deploy_model, "data") exporter = predictor_exporter.PredictorExportMeta( predict_net=deploy_model.net.Proto(), parameters=[str(param) for param in deploy_model.params], inputs=["data"], outputs=["pred"]) predictor_exporter.save_to_db("minidb", "/tmp/deploy.minidb", exporter) # 保存したモデルを読み込みテストデータから予測 workspace.ResetWorkspace() predict_net = predictor_exporter.prepare_prediction_net( "/tmp/deploy.minidb", "minidb") workspace.FeedBlob("data", test_data) workspace.RunNetOnce(predict_net) prediction = workspace.FetchBlob("pred")
  143. 143. 学習からテストまでのポイント • データの準備 • デプロイ用と学習用モデルを記述 – デプロイ時は入力から出力まで一方向 – 学習時には損失関数から誤差逆伝播とSGD – 一般には更にテスト用モデル (validation or test) を使用 • モデルの保存と読み込み – predictor_exporterを利用 – もう学習しない場合はモバイルデプロイ用にエク スポート
  144. 144. 2. 一般画像認識 • 配布されている学習済みのSqueezeNetモデルを使って画像分類 • Model Zooの使い方 • 画像の前処理について .312 tabby, tabby cat .237 tiger cat .123 Egyptian cat .101 red fox, Vulpes vulpes .007 lynx, catamount 分類結果と確率 目的: デプロイ時のCaffe2の使い方の理解 Caffe2に含まれるcaffe2/python/tutorials/Loading_Pretrained_Models.ipynbに相当
  145. 145. SqueezeNet • AlexNet同等の性能を持つ軽 量なCNNモデル • Caffe2ではModel Zooで配布 SqueezeNet: AlexNet-level accuracy with 50x fewer parameters and <0.5MB model size Forrest N. Iandola, Song Han, Matthew W. Moskewicz, Khalid Ashraf, William J. Dally, Kurt Keutzer arXiv:1602.07360, 2016
  146. 146. デプロイ時のワークフロー 1. モデルのダウンロード 2. 学習済みモデルをファイルから読み込み 3. 画像の前処理 – 画像サイズやNumpy配列の調整など 4. ネットワークで計算 5. 結果の解釈
  147. 147. Model Zooからのダウンロード Model Zooの公開モデルはダウンロード可能 !python -m caffe2.python.models.download -i squeezenet ただし、v0.8.1ではスクリプトにバグあり。Dockerイメージでは以下のファイ ルを修正すること DOWNLOAD_BASE_URL = "https://s3.amazonaws.com/caffe2/models/" DOWNLOAD_BASE_URL = "https://s3.amazonaws.com/download.caffe2.ai/models/" /usr/local/caffe2/python/models/download.py (Line 24) !sed -i "s,s3.amazonaws.com/caffe2/,s3.amazonaws.com/download.caffe2.ai/," /usr/local/caffe2/python/models/download.py 以下のコマンドで修正可能 Jupyter notebookからUNIX shellを実行するときは ! をコマンドの先頭につけること
  148. 148. 配布モデルの中身 from caffe2.proto import caffe_pb2 from caffe2.python import net_drawer from IPython import display PREDICT_NET = "/usr/local/caffe2/python/models/squeezenet/pred ict_net.pb" predict_net = caffe2_pb2.NetDef() with open(PREDICT_NET, "rb") as f: predict_net.ParseFromString(f.read()) print(predict_net) graph = net_drawer.GetPydotGraphMinimal( predict_net.op, "squeezenet", rankdir="TB") display.Image(graph.create_png(), width=300) • 配布モデルはNetDef形式 name: "squeezenet" op { input: "data" input: "conv1_w" input: "conv1_b" output: "conv1" type: "Conv" ... } ...
  149. 149. 画像の前処理 • Caffe2のSqueezeNetはNx3x227x227 (NCHW)のBGR配列浮動小数点画像を入力 • 一般的な8-bit RGB画像(HWC)からスケー ル、切り出し、フォーマット変換処理
  150. 150. 画像の前処理例 import numpy as np import skimage.io import skimage.transform def rescale(img, input_height, input_width): aspect = img.shape[1] / float(img.shape[0]) if aspect > 1: return skimage.transform.resize(img, (input_width, int(aspect * input_height))) elif aspect < 1: return skimage.transform.resize(img, (int(input_width/aspect), input_height)) else: return skimage.transform.resize(img, (input_width, input_height)) def crop_center(img, cropx, cropy): y, x, c = img.shape startx = x // 2 - (cropx // 2) starty = y // 2 - (cropy // 2) return img[starty:starty+cropy, startx:startx+cropx] img = skimage.io.imread("/caffe2/caffe2/python/tutorials/images/flower.jpg") img = skimage.img_as_float(img).astype(np.float32) img = rescale(img, 227, 227) img = crop_center(img, 227, 227) img = img.swapaxes(1, 2).swapaxes(0, 1) # HWC to CHW dimension img = img[(2, 1, 0), :, :] # RGB to BGR color order img = img * 255 - 128 # Subtract mean = 128 img = img[np.newaxis, :, :, :].astype(np.float32)
  151. 151. 前処理済み画像の分類 • 入力画像に前処理を施し、CNNで確率値を出力します • 配布のSqueezeNetは"data"ブロブを入力 (バグ?) • Predictor出力はカテゴリごとの確率を表すベクトルです from caffe2.python import workspace # Docker環境では以下の場所にダウンロード済みモデルが格納 INIT_NET = "/usr/local/caffe2/python/models/squeezenet/init_net.pb" PREDICT_NET = "/usr/local/caffe2/python/models/squeezenet/predict_net.pb" with open(INIT_NET, "rb") as f: init_net = f.read() with open(PREDICT_NET, "rb") as f: predict_net = f.read() workspace.FeedBlob("data", img) p = workspace.Predictor(init_net, predict_net) # run the net and return prediction results = p.run([])
  152. 152. 結果の解釈 985 SqueezeNet 0 999985 • どのカテゴリの確率値が最大だったかをargmax()で調べてみましょう • 985番のカテゴリが一番確率が高いという結果 • このカテゴリの名前はdaisyです results[0].flatten().argmax() with open("/caffe2/caffe2/python/tutorials/inference_codes.txt", "r") as f: codes = eval(f.read()) print(codes[985]) 'daisy'
  153. 153. デプロイ時のポイント • モデルをPredictorに読み込み – Model Zoo公開モデルはダウンロード利用可 • データは前処理を施してBlobにコピー • 推論はrun計算でOK • 結果はベクトルで得られるので、意味を 解釈する方法を用意
  154. 154. 3. 手書き文字識別器の学習 • MNIST手書き数字データセッ トからCNNを学習 – LeNetモデルを学習 • データベースの使い方 • SGDの使い方 • 学習ループの構成 目的: Caffe2での学習にまつわる基本を理解する MNISTデータセット Caffe2に含まれるcaffe2/tutorials/MNIST.ipynbに相当
  155. 155. MNISTデータについて • 機械学習の標準的ベンチマーク • 今回はCaffe2公式配布のLMDB形式を使用 The MNIST Database of handwritten digits Yann LeCun, Corinna Cortes, Christopher JC Burges http://yann.lecun.com/exdb/mnist/ import os, requests, zipfile, StringIO if not os.path.exists("./mnist"): r = requests.get("http://download.caffe2.ai/databases/mnist-lmdb.zip", stream=True) z = zipfile.ZipFile(StringIO.StringIO(r.content)) z.extractall("./mnist")
  156. 156. データ形式について TensorProtosDBInput LMDBデータベース / LevelDBデータベース key value 0001 TensorProtos 0002 TensorProtos 0003 TensorProtos ... ... • LMDBなどのDBにシリアライズして格納されたTensorProtos • TensorProtosDBInputオペレーターで計算時にはバッチを読み込み
  157. 157. モデルの構築 • データ入力 • CNN計算 • 性能評価 • 学習ロジック
  158. 158. LeNet: データ入力 • TensorProtosDBInput: LMDBなどのデータ ベースからバッチデータを取得するOperator • データの浮動小数点への変換もここで実行 %matplotlib inline import numpy as np import matplotlib.pyplot as plt from caffe2.python import core, model_helper, workspace, brew, optimizer def AddInput(model, batch_size=100, db="mnist/train-nchw-lmdb", db_type="lmdb"): """ データベース入力Operatorの定義 """ data_uint8, label = model.TensorProtosDBInput( [], ["data_uint8", "label"], batch_size=batch_size, db=db, db_type=db_type) data = model.Cast(data_uint8, "data", to=core.DataType.FLOAT) data = model.Scale(data, data, scale=float(1./256)) data = model.StopGradient(data) label = model.StopGradient(label) return data, label
  159. 159. LeNet: CNN計算 def AddLeNetModel(model, data): """ LeNetの定義 """ conv1 = brew.conv(model, data, "conv1", dim_in=1, dim_out=20, kernel=5) pool1 = brew.max_pool(model, conv1, "pool1", kernel=2, stride=2) conv2 = brew.conv(model, pool1, "conv2", dim_in=20, dim_out=50, kernel=5) pool2 = brew.max_pool(model, conv2, "pool2", kernel=2, stride=2) fc3 = brew.fc(model, pool2, "fc3", dim_in=50 * 4 * 4, dim_out=500) fc3 = brew.relu(model, fc3, fc3) pred = brew.fc(model, fc3, "pred", 500, 10) softmax = brew.softmax(model, pred, "softmax") return softmax • 2層のconv層 / 2層のfc層 / softmax予測 data conv1 pool1 conv2 pool2 fc3/relu3 pred label loss softmax
  160. 160. LeNet: 学習ロジック def AddAccuracy(model, softmax, label): """ 正答率の計算用Operator """ accuracy = brew.accuracy(model, [softmax, label], "accuracy") return accuracy def AddTrainingOperators(model, softmax, label): """ 学習に関わるOperator、損失計算とSGD更新 """ xent = model.LabelCrossEntropy([softmax, label], "xent") loss = model.AveragedLoss(xent, "loss") model.AddGradientOperators([loss]) opt = optimizer.build_sgd(model, base_learning_rate=0.1, policy="step", stepsize=1, gamma=0.999) for param in model.GetOptimizationParamInfo(): opt(model.net, model.param_init_net, param)
  161. 161. SGD学習率のポリシー • "fixed": 初期設定の学習率に固定 • "step": stepsize反復毎にgamma倍に引き下げ – その他、"exp", "inv", "linearWarmup", "constantWarmup" • build_sgdの代わりにbuild_adagradや build_adamなどの適応的学習率決定アルゴリズ ムも使用可能
  162. 162. モデルの構築 # 学習用モデルの構築 train_model = model_helper.ModelHelper(name="mnist_train") data, label = AddInput(train_model, db="./mnist/mnist-train-nchw-lmdb") softmax = AddLeNetModel(train_model, data) AddAccuracy(train_model, softmax, label) AddTrainingOperators(train_model, softmax, label) # テスト用モデルの構築 test_model = model_helper.ModelHelper(name="mnist_test", init_params=False) data, label = AddInput(test_model, db="./mnist/mnist-test-nchw-lmdb") softmax = AddLeNetModel(test_model, data) AddAccuracy(test_model, softmax, label) # デプロイ用モデルの構築 deploy_model = model_helper.ModelHelper(name="mnist_deploy", init_params=False) AddLeNetModel(deploy_model, "data") Data CNN Train logic Accuracy train x x x x test x x x deploy x
  163. 163. 学習ループの構成 # パラメータの初期化を一度だけ実行し、計算グラフを構築 workspace.RunNetOnce(train_model.param_init_net) workspace.CreateNet(train_model.net, overwrite=True) # 学習経過の記録用に配列を用意 total_iters = 600 accuracy = np.zeros(total_iters) loss = np.zeros(total_iters) # 学習ループを手動で構築 for i in range(total_iters): workspace.RunNet(train_model.net) accuracy[i] = workspace.FetchBlob("accuracy") loss[i] = workspace.FetchBlob("loss") if i % 60 == 0: print("TRAIN: accuracy={:1.4f}, loss={:1.4f}".format( accuracy[i], loss[i])) TRAIN: accuracy=0.0300, loss=2.3979 TRAIN: accuracy=0.9700, loss=0.0960 TRAIN: accuracy=0.9400, loss=0.2413 TRAIN: accuracy=0.9600, loss=0.1277 TRAIN: accuracy=0.9700, loss=0.0813 TRAIN: accuracy=0.9800, loss=0.0852 TRAIN: accuracy=0.9700, loss=0.1052 TRAIN: accuracy=0.9700, loss=0.0723
  164. 164. 学習経過のプロット plt.plot(loss, "b") plt.plot(accuracy, "r") plt.xlabel("Iteration") plt.legend(("Loss", "Accuracy"), loc="upper right") ロスが減少して収束、正答率 が上昇したら学習は上手く進 んでいます 参考までに、MNISTでは99.8% の正答率可能 [Wan 2013] http://rodrigob.github.io/are_we_there_yet/build/classification_datasets_results.html
  165. 165. 学習後の確認 from caffe2.python import visualize # バッチから複数画像を取得して表示 plt.figure() data = workspace.FetchBlob("data") visualize.NCHW.ShowMultiple(data) # バッチの最初の画像の分類確率を表示 plt.figure() plt.xticks(range(10)) softmax = workspace.FetchBlob("softmax") plt.bar(np.arange(10), softmax[0])
  166. 166. 学習のポイント • モデル構造を設定しループを構成 – データベースを使うとループがシンプルに • 学習経過は必ず観察すること • SGDパラメータの設定項目は進捗にシビ アに効いてくるので適切な値を探す
  167. 167. テストデータでの性能評価 # テストデータでの正答率とロスを計算する関数 def validate(model, data_size=10000, batch_size=100): batch_per_epoch = int(data_size / batch_size) accuracy, loss = 0, 0 for i in range(batch_per_epoch): workspace.RunNet(test_model.net) accuracy += workspace.FetchBlob("accuracy") loss += workspace.FetchBlob("loss") accuracy /= batch_per_epoch # 平均を取ってテスト正答率を算出 loss /= batch_per_epoch # 平均ロスを算出 print("TEST: accuracy={:1.4f}, loss={:1.4f}".format(accuracy, loss)) return accuracy workspace.RunNetOnce(test_model.param_init_net) workspace.CreateNet(test_model.net, overwrite=True) validate(test_model) TEST: accuracy=0.9795, loss=0.2239
  168. 168. 学習ループの改良: 学習と検証 • エポック毎にテストデータで性能検証 def train_epoch(model, data_size=60000, batch_size=100, print_freq=100): for i in range(int(data_size / batch_size / print_freq)): workspace.RunNet(model.net, num_iter=print_freq) accuracy = float(workspace.FetchBlob("accuracy")) loss = float(workspace.FetchBlob("loss")) print("TRAIN: accuracy={:1.4f}, loss={:1.4f}".format(accuracy, loss)) workspace.ResetWorkspace() workspace.RunNetOnce(train_model.param_init_net) workspace.RunNetOnce(test_model.param_init_net) workspace.CreateNet(train_model.net, overwrite=True) workspace.CreateNet(test_model.net, overwrite=True) total_epochs = 5 # CPUでは計算時間がかかるため注意 test_accuracy = [] for epoch in range(total_epochs): train_epoch(train_model) accuracy = validate(test_model) test_accuracy.append(accuracy)
  169. 169. 学習ループの改良: 正答率の推移 • テストデータの正答率をプロット plt.plot(test_accuracy) plt.xticks(range(total_epochs)) plt.xlabel("epochs") plt.ylabel("Accuracy") plt.grid("on")
  170. 170. 学習進捗管理に使うオペレータ def AddBookkeepingOperators(model): """ 学習時の進捗表示用Operator """ # Printオペレータはブロブの数値を表示 model.Print("accuracy", [], to_file=1) model.Print("loss", [], to_file=1) # Summaryオペレータはモデルのパラメータの概要を記録 for param in model.params: model.Summarize(param, [], to_file=1) model.Summarize(model.param_to_grad[param], to_file=1) # Checkpointオペレータは一定間隔でモデルをファイルに保存 model.Checkpoint(["optimizer_iteration"] + model.params, [], db="mnist_lenet_checkpoint_%05d.minidb", db_type="minidb", every=100) AddBookkeepingOperators(train_model) > tail accuracy.log Tensor accuracy of type float. Dims: (): 1 Tensor accuracy of type float. Dims: (): 1 Tensor accuracy of type float. Dims: (): 0.97 Tensor accuracy of type float. Dims: (): 0.99 Tensor accuracy of type float. Dims: (): 1 > tail conv1_b.summary -0.425186 0.299175 -0.0871603 0.201798 -0.425075 0.299447 -0.0870625 0.20185 -0.424453 0.303427 -0.0881043 0.201905 -0.425046 0.301677 -0.0871686 0.202028 -0.424014 0.30425 -0.0875321 0.202193 -0.424247 0.303955 -0.0875132 0.202263 > ls *.minidb mnist_lenet_checkpoint_00060.minidb mnist_lenet_checkpoint_00100.minidb mnist_lenet_checkpoint_00120.minidb mnist_lenet_checkpoint_00180.minidb mnist_lenet_checkpoint_00200.minidb mnist_lenet_checkpoint_00240.minidb mnist_lenet_checkpoint_00300.minidb mnist_lenet_checkpoint_00360.minidb mnist_lenet_checkpoint_00400.minidb mnist_lenet_checkpoint_00420.minidb • 学習の進捗とともに推移情報をファイルに 保存するオペレータが用意されている • あとから学習情報をプロットしたり学習を 再開するために使用
  171. 171. 学習が上手くいかない場合 • モデルを改良 – 例えばLeNetの層数を増やすなど • 最適化手法を調整 – 学習率(base_lr)を10倍単位で変更 – モーメンタムを学習率と反比例して変更 – 学習率のポリシーを変更 • まずはfixedで適切な範囲を見つけると良い • SGDアルゴリズムを変更 – AdaGradやAdamに変更 • データにノイズを加える (Augmentation)
  172. 172. それでも上手くいかない場合 • データが悪いケース – データ数が少なすぎる – そもそも判別が難しすぎる – データの分布が偏りすぎている • 例: 99%が負例のデータでは常に負と予測しても正答率 99%になってしまう • モデルが悪いケース – 損失関数が不適切 – データの前処理が不十分
  173. 173. 4. 学習データの準備について • Caffe2の入力データ – Operatorを使用 – 直接blob入力 • TensorProtosDBInput 形式のデータベース 作成方法 目的: データを扱う方法を知る data 学習データ CNN DB or Python?
  174. 174. 入力方式の比較 TensorProtosDBInput Blob入力 TensorProtosを格納した Binary DBデータベース Pythonで任意のデータを読み込み workspace.FeedBlob() で入力 I/O性能が高い I/O性能が低い 入力データをあらかじめ変換する必要 準備が簡単 他にも独自のOperatorを開発する方法
  175. 175. Binary DB形式について • Key-value形式のデータベース • Caffe2はTensorProtosという Protocol bufferを格納 // TensorProtos stores multiple TensorProto objects in one single proto. message TensorProtos { repeated TensorProto protos = 1; } // TensorProto stores serialized Tensor objects. message TensorProto { // The dimensions in the tensor. repeated int64 dims = 1; enum DataType {...} optional DataType data_type = 2 [default = FLOAT]; ... } key value 0001 TensorProtos 0002 TensorProtos 0003 TensorProtos ... ...
  176. 176. IrisデータセットのDB構築 • 4次元の特徴量から3 クラスのアイリスの 品種を判別 • 配布されるデータ セットからDB構築 – データはテキストファ イルにCSVで格納 https://archive.ics.uci.edu/ml/datasets/Iris 5.1,3.5,1.4,0.2,Iris-setosa 4.9,3.0,1.4,0.2,Iris-setosa 4.7,3.2,1.3,0.2,Iris-setosa 4.6,3.1,1.5,0.2,... 特徴ベクトル ラベル
  177. 177. Irisデータセットの入手 %matplotlib inline import urllib2 # for downloading the dataset from the web. import numpy as np import matplotlib.pyplot as plt from StringIO import StringIO from caffe2.python import core, utils, workspace from caffe2.proto import caffe2_pb2 f = urllib2.urlopen('https://archive.ics.uci.edu/ml/machine-learning-databases/iris/iris.data') raw_data = f.read() print(raw_data) 5.1,3.5,1.4,0.2,Iris-setosa 4.9,3.0,1.4,0.2,Iris-setosa 4.7,3.2,1.3,0.2,Iris-setosa 4.6,3.1,1.5,0.2,Iris-setosa データのダウンロード
  178. 178. データセットの前処理 random_index = np.random.permutation(150) features = features[random_index] labels = labels[random_index] データのランダム並び替え train_features = features[:100] train_labels = labels[:100] test_features = features[100:] test_labels = labels[100:] データをTrain/Testに分割 # load the features to a feature matrix. features = np.loadtxt( StringIO(raw_data), dtype=np.float32, delimiter=',', usecols=(0, 1, 2, 3)) # load the labels to a feature matrix label_converter = lambda s : { 'Iris-setosa':0, 'Iris-versicolor':1, 'Iris-virginica':2}[s] labels = np.loadtxt( StringIO(raw_data), dtype=np.int, delimiter=',', usecols=(4,), converters={4: label_converter}) CSVデータの読み込み:テキストのラベル名は整数に変換
  179. 179. Irisデータセットの中身 legend = ['rx', 'b+', 'go'] plt.title("Training data distribution, feature 0 and 1") for i in range(3): plt.plot(train_features[train_labels==i, 0], train_features[train_labels==i, 1], legend[i]) plt.figure() plt.title("Testing data distribution, feature 0 and 1") for i in range(3): plt.plot(test_features[test_labels==i, 0], test_features[test_labels==i, 1], legend[i]) データの散布図を表示
  180. 180. TensorProtosの作成 • TensorProtosはTensorProtoの配列を表すオブジェクト • TensorProtosを作成し、Numpy配列をTensorに変換して 追加 feature_and_label = caffe2_pb2.TensorProtos() feature_and_label.protos.extend([ utils.NumpyArrayToCaffe2Tensor(features[0]), utils.NumpyArrayToCaffe2Tensor(labels[0])]) print(str(feature_and_label)) protos { dims: 4 data_type: FLOAT float_data: 5.40000009537 float_data: 3.0 float_data: 4.5 float_data: 1.5 } protos { data_type: INT32 int32_data: 1 }
  181. 181. DBの構築 def write_db(db_type, db_name, features, labels): # DBを作成し、トランザクション開始 db = core.C.create_db(db_type, db_name, core.C.Mode.write) transaction = db.new_transaction() for i in range(features.shape[0]): # 全ての特徴、ラベルのペアについてTensorProtosを作成 feature_and_label = caffe2_pb2.TensorProtos() feature_and_label.protos.extend([ utils.NumpyArrayToCaffe2Tensor(features[i]), utils.NumpyArrayToCaffe2Tensor(labels[i])]) # 作成したTensorProtosをDBに格納、ここでSerialize transaction.put( 'train_%03d'.format(i), feature_and_label.SerializeToString()) # トランザクションを終了し、DBをディスクに保存 del transaction del db write_db("minidb", "iris_train.minidb", train_features, train_labels) write_db("minidb", "iris_test.minidb", test_features, test_labels) 全ての特徴、ラベルのペアをTensorProtoに変換してDBに格納 DBはminidb形式やlmdb形式が選択可能
  182. 182. DBの使い方 model = model_helper.ModelHelper("example_reader") model.TensorProtosDBInput([], ["feature", "label"], batch_size=16, db="iris_train.minidb", db_type="minidb") print(model.Proto()) name: "example_reader" op { input: "dbreader_iris_train.minidb" output: "feature" output: "label" name: "" type: "TensorProtosDBInput" arg { name: "batch_size" i: 16 } } external_input: "dbreader_iris_train.minidb" TensorProtosDBInputオペレータで構築したDBを指定
  183. 183. DBからの読み出し workspace.RunNetOnce(model.param_init_net) # 初期化を忘れずに workspace.CreateNet(model.net) workspace.RunNet(model.net) X = workspace.FetchBlob("feature") Y = workspace.FetchBlob("label") 通常通りworkspaceからBlobとして読み出し workspace.RunNet(model.net) X = workspace.FetchBlob("feature") Y = workspace.FetchBlob("label") (array([[ 6.69999981, 3. , 5. , 1.70000005], [ 5.5 , 2.4000001 , 3.79999995, 1.10000002], ... (array([[ 7.5999999 , 3. , 6.5999999 , 2.0999999 ], [ 5.4000001 , 3.4000001 , 1.5 , 0.40000001], ... もう一度runすると次のバッチを読み出し
  184. 184. DB形式の使い所 TensorProtosDBInput • 高速I/O、大規模データ向き • 学習ループは自動 • 事前DB構築が必須 • Data augmentationが困難 • エポック毎に読み込み処理 が固定(overfittingの可能 性) workspace.FeedBlob • 低速I/O • 学習ループは手動 • Data augmentation自在 • エポック毎に読み込み処 理を変更可能(順番を shuffleなど) # 学習ループ for epoch in range(total_epochs): for batch in range(total_batches): workspace.RunNet(train_model.net) # 学習ループ for epoch in range(total_epochs): for x, y in data_loader(): workspace.FeedBlob("x", x) workspace.FeedBlob("y", y) workspace.RunNet(train_model.net)
  185. 185. 5. ファインチューニング(転移学習) • 学習済みSqueezeNetを初 期値として新しいデータ セットで再学習 • 数千データしか用意でき ない場合に実用的な手法 • モデルの改造について • 今回はGPU使用前提 目的: 実用的な画像認識モデルの学習手順を理解する 一般物体識別用 SqueezeNet tabby cat tiger cat Egyptian cat 犬猫分類用 SqueezeNet Dog Cat https://gist.github.com/kyamagu/6cff70840c10ca374e069a3a7eb00cb4 今回のnotebookは以下URLで配布
  186. 186. SqueezeNetの移植 data conv1 ... fire9/concat conv10 pool10/softmax data conv1 ... fire9concat conv10 pool10/softmax ImageNetモデル 犬猫分類モデル tabby cat tiger cat Egyptian cat ... dog cat 1000クラス = 1000次元 2クラス = 2次元* 犬猫の写真 を分類 conv10のパラ メータ差替え *注: 1次元でも可、簡単のため2次元で行う
  187. 187. NetとNetDef core.Net • PythonのNet構築API • NetDefに保存、読み込み caffe2_pb2.NetDef • Netのデータ構造を定義する Protocol buffer • Protocol bufferを編集すること でoperatorを変更可 • 直接差し替えも可能だが、追 加はNet APIの方が簡単 from caffe2.python import core init_net = core.Net(init_net_proto) init_net.XavierFill( [], "conv10_w", shape=[5, 512, 1, 1], ) init_net.ConstantFill( [], "conv10_b", shape=[5], ) modified_init_net = init_net.Proto() from caffe2.proto import caffe2_pb2 init_net_proto = caffe2_pb2.NetDef() with open("init_net.pb", "rb") as f: init_net_proto.ParseFromString(f.read()) # 50, 51番のoperatorを削除 init_net_proto.op.pop(51) init_net_proto.op.pop(50)
  188. 188. 画像の前処理 import numpy as np import skimage.io import skimage.transform def rescale(img, input_height, input_width): aspect = img.shape[1] / float(img.shape[0]) if aspect > 1: return skimage.transform.resize(img, (input_width, int(aspect * input_height))) elif aspect < 1: return skimage.transform.resize(img, (int(input_width/aspect), input_height)) else: return skimage.transform.resize(img, (input_width, input_height)) def crop_center(img, cropx, cropy): y, x, c = img.shape startx = x // 2 - (cropx // 2) starty = y // 2 - (cropy // 2) return img[starty:starty+cropy, startx:startx+cropx] def prepare_image(img_path): img = skimage.io.imread(img_path) img = skimage.img_as_float(img) img = rescale(img, 227, 227) img = crop_center(img, 227, 227) img = img.swapaxes(1, 2).swapaxes(0, 1) # HWC to CHW dimension img = img[(2, 1, 0), :, :] # RGB to BGR color order img = img * 255 - 128 # Subtract mean = 128 return img.astype(np.float32)
  189. 189. データセットの読み出し import os, glob, random def make_batch(iterable, batch_size=1): length = len(iterable) for index in range(0, length, batch_size): yield iterable[index:min(index + batch_size, length)] class DogsCatsDataset(object): """ Dogs and cats dataset reader """ def __init__(self, split="train", data_dir="dogs-vs-cats/"): self.categories = {"dog": 0, "cat": 1} self.image_files = list(glob.glob(os.path.join(data_dir, split, "*.jpg"))) self.labels = [self.categories.get(os.path.basename(path).strip().split(".")[0], -1) for path in self.image_files] def __getitem__(self, index): image = prepare_image(self.image_files[index]) label = self.labels[index] return image, label def __len__(self): return len(self.labels) def read(self, batch_size=50, shuffle=True): """Read (image, label) pairs in batch""" order = list(range(len(self))) if shuffle: random.shuffle(order) for batch in make_batch(order, batch_size): images, labels = [], [] for index in batch: image, label = self[index] images.append(image) labels.append(label) yield np.stack(images).astype(np.float32), np.stack(labels).astype(np.int32).reshape((batch_size,))
  190. 190. モデルの構築1: SqueezeNet from caffe2.python import core, workspace, model_helper, optimizer, brew from caffe2.python.modeling import initializers from caffe2.python.modeling.parameter_info import ParameterTags from caffe2.proto import caffe2_pb2 PREDICT_NET = "/usr/local/caffe2/python/models/squeezenet/predict_net.pb" INIT_NET = "/usr/local/caffe2/python/models/squeezenet/init_net.pb" def AddPredictNet(model, predict_net_path): predict_net_proto = caffe2_pb2.NetDef() with open(predict_net_path, "rb") as f: predict_net_proto.ParseFromString(f.read()) model.net = core.Net(predict_net_proto) # Fix dimension incompatibility model.Squeeze("softmaxout", "softmax", dims=[2, 3])
  191. 191. モデルの構築2: 初期化方法の変更 def AddInitNet(model, init_net_path, out_dim=2, params_to_learn=None): init_net_proto = caffe2_pb2.NetDef() with open(init_net_path, "rb") as f: init_net_proto.ParseFromString(f.read()) # Define params to learn in the model. for op in init_net_proto.op: param_name = op.output[0] if params_to_learn is None or op.output[0] in params_to_learn: tags = (ParameterTags.WEIGHT if param_name.endswith("_w") else ParameterTags.BIAS) model.create_param( param_name=param_name, shape=op.arg[0], initializer=initializers.ExternalInitializer(), tags=tags, ) # Remove conv10_w, conv10_b initializers at (50, 51) init_net_proto.op.pop(51) init_net_proto.op.pop(50) # Add new initializers for conv10_w, conv10_b model.param_init_net = core.Net(init_net_proto) model.param_init_net.XavierFill([], "conv10_w", shape=[out_dim, 512, 1, 1]) model.param_init_net.ConstantFill([], "conv10_b", shape=[out_dim])
  192. 192. モデルの構築3: 学習用設定 def AddTrainingOperators(model, softmax, label): xent = model.LabelCrossEntropy([softmax, label], "xent") loss = model.AveragedLoss(xent, "loss") brew.accuracy(model, [softmax, label], "accuracy") model.AddGradientOperators([loss]) opt = optimizer.build_sgd(model, base_learning_rate=0.1) for param in model.GetOptimizationParamInfo(): opt(model.net, model.param_init_net, param) train_model = model_helper.ModelHelper("train_net") AddPredictNet(train_model, PREDICT_NET) # Use params_to_learn=None to learn everything. AddInitNet(train_model, INIT_NET, params_to_learn=["conv10_w", "conv10_b"]) AddTrainingOperators(train_model, "softmax", "label")
  193. 193. GPU使用の設定 def SetDeviceOption(model, device_option): # Clear op-specific device options and set global device option. for net in ("net", "param_init_net"): net_def = getattr(model, net).Proto() net_def.device_option.CopyFrom(device_option) for op in net_def.op: # Some operators are CPU-only. if op.output[0] not in ("optimizer_iteration", "iteration_mutex"): op.ClearField("device_option") op.ClearField("engine") setattr(model, net, core.Net(net_def)) device_option = caffe2_pb2.DeviceOption() device_option.device_type = caffe2_pb2.CUDA device_option.cuda_gpu_id = 0 SetDeviceOption(train_model, device_option) • CPUの場合はcaffe2_pb2.CUDAをcaffe2_pb2.CPUに変更すれば良いが、学 習に多大な時間がかかる • GPUマシンでnvidia-dockerで実行する場合は以下のように起動 nvidia-docker run -it --rm -p 8888:8888 caffe2ai/caffe2:c2v0.8.1.cuda8.cudnn7.ubuntu16.04 jupyter notebook --no-browser --ip "*" --allow-root
  194. 194. fine-tuningの実行 workspace.ResetWorkspace() # Initialization. train_dataset = DogsCatsDataset("train") for image, label in train_dataset.read(batch_size=1): workspace.FeedBlob("data", image, device_option=device_option) workspace.FeedBlob("label", label, device_option=device_option) break workspace.RunNetOnce(train_model.param_init_net) workspace.CreateNet(train_model.net, overwrite=True) # Main loop. batch_size = 50 print_freq = 50 losses = [] for epoch in range(5): for index, (image, label) in enumerate(train_dataset.read(batch_size)): workspace.FeedBlob("data", image, device_option=device_option) workspace.FeedBlob("label", label, device_option=device_option) workspace.RunNet(train_model.net) accuracy = float(workspace.FetchBlob("accuracy")) loss = workspace.FetchBlob("loss").mean() losses.append(loss) if index % print_freq == 0: print("[{}][{}/{}] loss={}, accuracy={}".format( epoch, index, int(len(train_dataset) / batch_size), loss, accuracy))
  195. 195. 学習の経過確認 import matplotlib.pyplot as plt %matplotlib inline plt.plot(losses) plt.xlabel("iterations") plt.ylabel("loss") plt.grid("on") [0][0/500] loss=1.15868854523, accuracy=0.600000023842 [0][50/500] loss=0.10267546773, accuracy=0.959999978542 [0][100/500] loss=0.0946362018585, accuracy=0.959999978542 [0][150/500] loss=0.131896346807, accuracy=0.939999997616 [0][200/500] loss=0.0796595662832, accuracy=0.959999978542 [0][250/500] loss=0.0999855622649, accuracy=0.939999997616 [0][300/500] loss=0.08723885566, accuracy=0.980000019073 [0][350/500] loss=0.0652658939362, accuracy=0.959999978542 [0][400/500] loss=0.0490036420524, accuracy=0.980000019073 [0][450/500] loss=0.0513261109591, accuracy=0.959999978542 [1][0/500] loss=0.109811611474, accuracy=0.959999978542 [1][50/500] loss=0.17768985033, accuracy=0.899999976158 [1][100/500] loss=0.070137001574, accuracy=0.959999978542 [1][150/500] loss=0.103964686394, accuracy=0.959999978542 [1][200/500] loss=0.0276749264449, accuracy=0.980000019073 [1][250/500] loss=0.0539846345782, accuracy=0.959999978542 [1][300/500] loss=0.0995031148195, accuracy=0.959999978542 [1][350/500] loss=0.154732093215, accuracy=0.920000016689 [1][400/500] loss=0.0426763668656, accuracy=0.980000019073 [1][450/500] loss=0.0599067881703, accuracy=0.959999978542
  196. 196. 学習済みモデルの利用 deploy_model = model_helper.ModelHelper("deploy_net") AddPredictNet(deploy_model, PREDICT_NET) SetDeviceOption(deploy_model, device_option) test_dataset = DogsCatsDataset("test1") image, _ = test_dataset[0] image = image[np.newaxis, :] workspace.FeedBlob("data", image, device_option=device_option) workspace.RunNetOnce(deploy_model.net) result = workspace.FetchBlob("softmax")[0] skimage.io.imshow(test_dataset.image_files[0]) print("dog={:g} cat={:g}".format(result[0], result[1])) dog=8.07451e-09 cat=1
  197. 197. Scratch vs. Fine-tuning ランダム初期化に比べ、ファインチューニングではすぐに収束 差し替えパラメータのみ学習させて収束後、End-to-endで全てのパラ メータを再学習させると更に収束 Scratch (初期値) Fine-tuning (転移学習)
  198. 198. ファインチューニングのポイント • 学習済みのネットワークを初期値として 新しいデータからモデルを再学習 – 比較的小規模のデータにディープラーニング を用いる実用的な手法 • operatorに入力するパラメータを差し替え て通常通りに学習
  199. 199. ONNX
  200. 200. ONNX Open Neural Network Exchange • フレームワーク間でのネットワー ク相互利用フォーマット • PyTorchで学習したモデルを Caffe2でモバイルデプロイという 使い方 – 他にApple CoreML、NNVMの選択肢
  201. 201. onnx-caffe2の入手 https://github.com/onnx/onnx-caffe2 pip install onnx-caffe2 インストール
  202. 202. ONNXモデルをCaffe2で実行 import onnx import onnx_caffe2.backend import numpy as np # Prepare an image in NCHW format img = np.random.randn(1, 3, 224, 224).astype(np.float32) # Load the ONNX model model = onnx.load("assets/squeezenet.onnx") # Run the ONNX model with Caffe2 outputs = onnx_caffe2.backend.run_model(model, [img]) convert-onnx-to-caffe2 assets/squeezenet.onnx --output predict_net.pb --init-net-output init_net.pb 直にモデルを読み込んで使う方法 モデルをCaffe2フォーマットに変換する方法
  203. 203. Caffe2からONNXモデル出力 import onnx from onnx_caffe2.frontend import caffe2_net_to_onnx_model from caffe2.proto import caffe2_pb2 # Data format spec data_type = onnx.TensorProto.FLOAT data_shape = (1, 3, 224, 224) value_info = {"data": (data_type, data_shape)} predict_net = caffe2_pb2.NetDef() with open("predict_net.pb", "rb") as f: predict_net.ParseFromString(f.read()) init_net = caffe2_pb2.NetDef() with open("init_net.pb", "rb") as f: init_net.ParseFromString(f.read()) # Convert onnx_model = caffe2_net_to_onnx_model(predict_net, init_net, value_info) predict_netとinit_net、データ入力の情報からONNXモデルを生成
  204. 204. (参考) NNVM Compiler: Open Compiler for AI Frameworks http://www.tvmlang.org/2017/10/06/nnvm-compiler-announcement.html フロントエンドフレームワークからバックエンドハードウェアへのコンパイラ
  205. 205. Caffe2によるディープラーニング の基礎と実践 ディープラーニングと画像認識の理解 • ニューラルネット、CNN Caffe2フレームワークの基本を理解 • ビルド方法、Workspace, Operator PythonによるCaffe2の使い方を学習 • データ形式、学習、テスト
  206. 206. APPENDIX
  207. 207. Androidアプリ • Androidでの物体検出デモアプリ https://github.com/bwasti/AICamera git clone https://github.com/bwasti/AICamera.git git submodule init && git submodule update cd app/libs/caffe2 git submodule init && git submodule update
  208. 208. 参考資料 Caffe2 documentation https://caffe2.ai Caffe2 tutorials: Githubに資料や具体例 https://github.com/caffe2/caffe2

×