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.

Caffeで始めるディープラーニング

36,294 views

Published on

これからディープラーニングを用いて画像認識を始めたい方に向けた2017年1月23日開催の日本語技術セミナー資料です。ディープラーニングの基礎からPython言語を使ってCaffeフレームワークを使うための実践方法まで解説します。

Published in: Technology

Caffeで始めるディープラーニング

  1. 1. Caffeで始める ディープラーニング 山口光太
  2. 2. 目標 • ディープラーニングと画像認識の理解 • Caffeフレームワークの基本を理解 • PythonによるCaffeの使い方を学習
  3. 3. Caffeで始めるディープラーニング Caffeって? ディープラーニングの基本 なぜ今、深層学習? ニューラルネットワークの基本 様々なネットワーク 誤差逆伝播法 畳み込みニューラルネットワーク (CNN) 代表的なCNNアーキテクチャ Caffeフレームワーク Caffeの特徴 Caffeの構造 Model Zoo Caffeのビルドと環境設定 Buildの手順、GPU設定 Dockerで環境構築する方法 (15min break) Pythonについて Pythonと数値計算 CaffeのPythonインタフェース Jupyter環境 Caffeの具体的な使い方 線形回帰 一般物体認識 手書き文字の学習 転移学習 学習データの準備について (15min break) Caffeの高度な使い方 新しいレイヤーの開発 SSDで物体検出 LSTMsでシーケンスモデル FCNsによるピクセル単位の予測 Q&A 12:30 12:45 13:25 13:45 14:00 14:15 14:35 15:30 15:45 16:10
  4. 4. Caffeフレームワーク • Convolutional Architecture for Fast Feature Embedding – 前身はDeCAF (Deep Convolutional Activation Feature) • Yangqing Jiaら当時Berkeleyのメンバーが中心に2013年頃開 発したオープンソースのディープラーニングフレームワーク – コードを書かなくても設定ファイルだけで動かせる – 容易に拡張できるコード設計 – C++実装、GPUをシームレスに使い分けて高速な実行 – PythonとMatlabからも使える – Githubの活発な開発コミュニティ • 画像認識向き [wikipedia]
  5. 5. Caffeを使ってできること 画像分類 • デモコードはオープン、Caffeに収録 http://demo.caffe.berkeleyvision.org Slide credit [DIY Deep Learning for Vision]
  6. 6. Caffeを使ってできること シーン認識 http://places.csail.mit.edu/ B. Zhou et al. NIPS 14 Slide credit [DIY Deep Learning for Vision]
  7. 7. Fast R-CNN - 畳み込みは一度だけ - 投影して検出 Ross Girshick, Shaoqing Ren, Kaiming He, Jian Sun Faster R-CNN - End-to-endで候補生成と検出 - 一枚あたり200 ms - Region Proposal Net + Fast R-CNN papers + code online R-CNNs: Region-based Convolutional Networks Slide credit [DIY Deep Learning for Vision] Caffeを使ってできること 物体検出
  8. 8. FCNアーキテクチャによるセマンティックセグメンテーション - End-to-endの学習 - 効率的な推論と学習 100 ms per-imageの予測 - マルチモーダル, マルチタスク FCNアーキテクチャを使うと - セグメンテーション - デノイズ - 奥行き推定 - オプティカルフロー Jon Long* & Evan Shelhamer*, Trevor Darrell. CVPR’15 CVPR'15 paper and code + models Caffeを使ってできること 領域分割 Slide credit [DIY Deep Learning for Vision]
  9. 9. Caffeの利用 Yahoo Japan News Image Recommendation select and crop images for news - ニュースのパーソナライズ やおすすめの重複除去 - ニュースやレストランの写 真をキュレートしておすす めに活用 - ユーザの写真アルバムの 整理 Slide credit [DIY Deep Learning for Vision]
  10. 10. Caffeの利用 Pinterest - 大規模なビジョン処理: アップ ロードされた画像はCaffeで - 検索のためのディープラーニ ング: 250ms以下で10億を超え る画像からの検索 - ~400万 リクエスト/日 - Caffe, FLANN, Thrift, ...を使っ たプラットフォーム [example credit Andrew Zhai, Pinterest] Slide credit [DIY Deep Learning for Vision]
  11. 11. Embedded Caffe • 同じモデルを同じ開発環境で • CUDAプラットフォームです ぐ動作 • OpenCLポートも開発進行中 • Androidポートも CUDA Jetson TX1, TK1 Android lib, demo OpenCL branch Slide credit [DIY Deep Learning for Vision] Caffeは組み込み向けCUDAハードウェアやモバイルでも動作
  12. 12. ディープラーニングの基本
  13. 13. ディープラーニング • 深い層構造を持った機械学習モデル – 人工ニューラルネットワーク (Artificial Neural Networks) – 大量のパラメータに対し大量のデータを用い て学習 • Caffeはディープラーニングのためのソフ トウェアフレームワーク
  14. 14. なぜディープラーニング? • 圧倒的な性能 Result in ILSVRC over the years ILSVRCでの毎年のエラー率の推移 物体カテゴリ認識 slide credit: Jia Deng 物体カテゴリ検出 http://image-net.org/challenges/ilsvrc+coco2016 人間は0.04 程度? 2012年からディープ ラーニング登場
  15. 15. ILSVRC 画像分類タスクSVRC image classification (CLS) task Steel drum 1000 object classes 1,431,167 images CLS-LOC slide credit: Jia Deng Error 0.03
  16. 16. ILSVRC 物体検出タスク slide credit: Jia Deng Error 0.077
  17. 17. 畳み込みニューラルネットワーク • 画像認識によく使われるディープ・ ニューラルネットワーク 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)
  18. 18. これまでの画像認識との違い R G RED APPLE 従来の画像認識手法 ディープラーニング 1) 画像特徴の抽出 2) データ点の判別 RED APPLE 特徴表現から判別まで複数の情報変換を学習 データから全て学習 人が設計
  19. 19. 浅い vs 深いアーキテクチャ Image/ Video Pixels Object Class Object Class Image/ Video Pixels 従来手法: 浅いアーキテクチャ ディープラーニング: 深いアーキテクチャ … 手動設計の 特徴抽出手法 学習できる 識別器 Layer 1 Layer N 単純な 識別器 HOG, SIFT SVM, Random Forest Neural networks
  20. 20. ニューラルネットワーク • 多数の非線形関数ユニットが結合して構 成される数理モデル – 複雑な関数を近似できる R G Y = F(X) x1 x2 x3 x4 y1 y2 Input layer Hidden layer Output layer
  21. 21. ニューロン 脳の神経細胞
  22. 22. パーセプトロン x1 x2 xd w1 w2 w3 x3 wd Sigmoid関数: 入力 重み (Weights) . . . t e t    1 1 )( 出力: (wx + b) 人工的な神経細胞のモデル関数 パーセプトロンの積み重ねが ニューラルネットワーク
  23. 23. Hubel-Wiesel Architecture • D. Hubel and T. Wiesel (1959, 1962, ノー ベル賞1981) • 視覚野は simple, complex, hyper-complex 細胞の階 層構造 Source
  24. 24. ユニットの形 𝜎(𝑧) = 1 1 + 𝑒−𝑧 非線形変換 (活性化関数)線形変換 + Sigmoid Tanh tanh(𝑧) = 𝑒2𝑧 − 1 𝑒2𝑧 + 1 relu(𝑧) = max(𝑧, 0) ReLU 𝑧 = 𝑊𝒙 + 𝑏 Inner product 𝑧 = conv(𝒘, 𝒙) Convolution softmax(𝑧) = 𝑒−𝑧 𝑧′ 𝑒−𝑧′ Softmax
  25. 25. 様々なニューラルネットワーク 順伝播型ニューラルネット 再帰型ニューラルネット (RNN) 畳み込みニューラルネット (CNN) • 結合が再帰的なもの • 系列データの利用 • 画像のフィルタ演算がユニット • 画像などの空間配列データに利用 その他、オートエンコーダ、ボルツマンマシン
  26. 26. 学習と予測 学習データ モデル テストデータ 結果 学習 予測 Y = F(X) {(X,Y)} F
  27. 27. 学習: モデル推定 • ニューラルネットワークの重みが損失関 数を小さくするように最適化 – 例: 二乗誤差 min 𝑊 𝑖 𝑦𝑖 − 𝑦 (𝒙𝑖, 𝑊) 2 正解値 出力値 ネットワークのパラメータ (ニューロンの重み) 入力値 𝑦𝒙
  28. 28. 勾配降下法 (Gradient descent) • 非線形関数の最適化手法 • 局所的に線形近似、勾配 方向に現在の解を更新 損失関数 (Loss function) L(X, W) = || y – y(x, W) ||2 Wt Wt+1 e 𝑊𝑡+1 ⟵ 𝑊𝑡 − 𝜀𝛻𝐿(𝑋, 𝑊𝑡) 損失関数の勾配学習率 現在の パラメータ 学習率:どれだけ現在の解を動かすか W
  29. 29. 確率的勾配降下法 (SGD: Stochastic Gradient Descent) • 勾配計算をデータセットからのランダム サンプル(=バッチ)で近似したもの – 損失関数の微分は計算量が大きいため 𝑊𝑡+1 ⟵ 𝑊𝑡 − 𝜀𝛻 𝐿(𝑋, 𝑊𝑡) 𝐿( 𝑋, 𝑊𝑡) = 𝑖∈ 𝐷 𝑦𝑖 − 𝑦 (𝒙𝑖, 𝑊) 2 学習データ D バッチ D’バッチロス バッチの勾配
  30. 30. モーメンタム (Momentum) • 確率的勾配降下法ではバッチによって勾 配が変動するので、前回の更新と今回の 勾配を使ってパラメータ更新を安定化 𝜃𝑡+1 ⟵ 𝜃𝑡 + 𝜇𝛿𝜃𝑡 − 𝜀𝛻 𝐿 𝑋, 𝜃𝑡 𝛿𝜃𝑡 = 𝜃𝑡 − 𝜃𝑡−1 モーメンタム 今回の勾配値前回の更新 学習係数 (Learning rate)
  31. 31. 誤差逆伝播法 (Back propagation) • ニューラルネットワークの学習時に効率 的に微分計算する手法 – 微分のチェインルール(連鎖律) – パラメータの勾配計算に使う 𝒙 𝑦 順伝播 𝐿 逆伝播 𝜕𝐿 𝜕𝒙 𝐿
  32. 32. 逆伝播計算 𝜕𝐿 𝜕𝑥𝑖,𝑗 = 𝑘 𝜕𝐿 𝜕𝑥𝑖+1,𝑘 𝜕𝑥𝑖+1,𝑘 𝜕𝑥𝑖,𝑗 j 𝜕𝒙𝑖+1 𝜕𝑥𝑖,𝑗 𝜕𝐿 𝜕𝑥𝑖,𝑗 𝜕𝐿 𝜕𝑥𝑖+1,1 𝜕𝐿 𝜕𝑥𝑖+1,2 𝜕𝐿 𝜕𝑥𝑖+1,3 第i層 第i+1層 第 i+1 層の出力についての偏微分があれば、第 i 層の出力に ついての損失関数の偏微分がチェインルールで計算可能 𝜕𝑥𝑖+1,𝑘 𝜕𝑥𝑖,𝑗 = 𝑤𝑖,𝑗,𝑘 𝑥𝑖+1,𝑘(𝑥𝑖+1,𝑘 − 1) 例: sigmoidの場合
  33. 33. パラメータ勾配の計算 最終的に求めたいもの: 損失関数Lに対するパラメータwの偏微分 𝜕𝐿 𝜕𝑤𝑖,𝑗,𝑘 = 𝜕𝐿 𝜕𝑥𝑖+1,𝑘 𝜕𝑥𝑖+1,𝑘 𝜕𝑤𝑖,𝑗,𝑘 1. 損失に対する層出力の偏微 分を逆伝播で計算 𝐿 𝜕𝐿 𝜕𝒙 2. 損失に対するパラメータの 偏微分は層ごとに解析的に計算 𝜕𝑥𝑖+1,𝑘 𝜕𝑤𝑖,𝑗,𝑘 = 𝑥𝑖,𝑗 𝑥𝑖+1,𝑘(𝑥𝑖+1,𝑘 − 1) 例: sigmoidの場合
  34. 34. よく使われる損失関数 Euclidean (二乗誤差) Cross Entropy (交差エントロピー) 𝑖 𝑦𝑖 − 𝑦𝑖 2 − 𝑖 𝑝 𝑖 𝑝𝑖 ln 𝑝𝑖 二値分類: Sigmoid + Cross entropy 多クラス分類: Softmax + Cross entropy 回帰問題に利用 出力が確率分布の場合に利用
  35. 35. 学習データの分割 学習 Train 検証 Validation テスト Test • SGDでパラメー タの学習に利用 • 学習時の現在の性 能確認用 • ハイパーパラメー タの調整に利用 • 学習係数 • モーメンタム • 性能評価に利用 • 最後まで取っておく • Validationと兼ねる 場合もあり • データは一般的に3つまたは2つに分割 • 分割割合は8:1:1など、一般に学習データが多めだが、5:5などもあり
  36. 36. 学習の進捗 反復数 (iteration): バッチをSGDに投入した回数 エポック数(epoch): 学習データセットの一周回を使った回数 Validationデータの性能 Trainデータの損失 batch epoch
  37. 37. 過学習、局所解 Validation error Training error #epochs loss データに対してモデルのパラメータ数が多すぎると汎化誤差が増大
  38. 38. 初期値問題 Fine-tuned #epochs loss パラメータの初期値によって局所解で収束 Scratch学習ではなく学習済みモデルからのFine-tuningなどで解決 Scratch
  39. 39. 勾配消失問題 (Vanishing Gradient) 𝐿 𝜕𝐿 𝜕𝒙 非線形変換 微分値が 小さい • チェインルールで微分を計算 していくと、小さな値の掛け 合わせで微分が0に近づく問題 • 深層ネットワークの学習の難 しさの主要な要因 • LSTMやResNetなどで解決
  40. 40. 重み減衰 (Weight decay) • ネットワークの重みの発散を防ぐ正則化 • 一般にごく小さなλ値 min 𝑊 𝑖 𝑦𝑖 − 𝑦 (𝒙𝑖, 𝑊) 2 + 𝜆 2 𝑊 2 損失関数 正則化項
  41. 41. Dropout • 一定の確率でユニットの出力を0に • 一種の正則化、過学習を防ぐために有用 [Hinton 2012] x1 x2 x3 x4 y1 y2
  42. 42. その他テクニック • データ拡張 (data augmentation) – データにノイズを加えてデータ数拡張 • Data-dependent weight initialization – パラメータをデータ分布に従って初期化 • Batch normalization – バッチ単位で出力を正規化し、勾配消失回避 • SGD improvement: AdaGrad, ADAM – 学習率の自動調整 • Model ensemble – 複数モデルを組み合わせて予測 • Weight normalization – 重みの正則化の一種
  43. 43. 畳み込みニューラルネットワーク (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.
  44. 44. CNNの基本的な構造 • 順伝播の特徴抽出層 – 畳み込み演算 – 非線形変換 – プーリング – (正規化) • 畳み込みのフィルタ の重みをバックプロ パゲーションで学習 入力画像 畳み込み (学習対象) 非線形変換 プーリング 特徴量マップ レイヤー
  45. 45. 1. 畳み込み (Convolution) • 依存関係は局所的 • 位置不変 • パラメータ数が少ない • ストライドは1以上も可 • メモリと演算量削減 Input Feature Map . . . Slide credit: Rob Fergus
  46. 46. 2. 非線形変換 (Non-linearity) • 画素ごとに適用 • よく使われるもの – ReLU: max(x, 0) • バックプロパゲーショ ンが簡単 • 学習速度も速い – Sigmoid: 1/(1+exp(x)) – Tanh Slide credit: Rob Fergus ReLU Tanh Sigmoid
  47. 47. 3. プーリング (Spatial pooling) • SUM, MAX, AVERAGEなど • 領域は重ねても重ねなくてもOK – 微小な空間変化への対応 – 大きな Receptive field (入力空間範囲) Max Sum Slide credit: Rob Fergus
  48. 48. 4. 正規化 (Normalization) • 特徴マップの中またはマップ間で • プーリングの前後どちらでも良い Feature Maps Feature Maps After Contrast Normalization Slide credit: Rob Fergus
  49. 49. CNNの内部表現 M. Zeiler and R. Fergus, Visualizing and Understanding Convolutional Networks, arXiv preprint, 2013 Slide credit: Rob Fergus
  50. 50. Layer 1 Filters Slide credit: Rob Fergus
  51. 51. Layer 1: Top-9 Patches Slide credit: Rob Fergus
  52. 52. Layer 4: Top-9 Patches Slide credit: Rob Fergus
  53. 53. Layer 4: Top-9 Patches Slide credit: Rob Fergus
  54. 54. 代表的なCNNアーキテクチャ • LeNet [LeCun 1998] • AlexNet [Krizhevsky 2012] • VGG [Simonyan 2014] • GoogLeNet [Szegedy 2014] • ResNet [He 2015] • Xception [Challet 2016] • PolyNet [Lin 2016]
  55. 55. AlexNet • ImageNet 2012最高性能 • 畳み込み5層+全結合層3層 • サイズが比較的小さく、基準手法としてよく使われるCNN [Krizhevsky 2012] https://leonardoaraujosantos.gitbooks.io/artificial-inteligence/content/image_folder_7/AlexNet_0.jpg
  56. 56. VGG [Simonyan 2014] https://www.cs.toronto.edu/~frossard/post/vgg16/ • ImageNet 2013最高性能 • AlexNetを拡張し、多段の畳み込み層、小さなフィルタを 導入したアーキテクチャ
  57. 57. GoogLeNet http://joelouismarino.github.io/ Inception moduleに よる複数経路の演算 Inception modules Stem Auxiliary classifiers Output (Inception V1) ImageNet 2014最高性能 [Szegedy 2013]
  58. 58. ResNet [He 2015] http://felixlaumon.github.io/ https://culurciello.github.io/tech/2016/06/04/nets.html • ImageNet 2015最高性能 • Residual block (Identity + Conv)に よって超深層を実現
  59. 59. アーキテクチャとモデルの大きさ https://culurciello.github.io/tech/2016/06/04/nets.html
  60. 60. RNN • 再帰型の結合を持つニューラルネット • 可変長の系列データに対して使用 – 時系列の予測 • 再帰的結合は展開したものと等価 http://colah.github.io/posts/2015-08-Understanding-LSTMs/
  61. 61. Long Short-Term Memory (LSTM) • RNNのユニットに長期短期記憶を組み入れたもの • 長距離の依存関係、勾配消失問題に対処可能 http://colah.github.io/posts/2015-08-Understanding-LSTMs/
  62. 62. ディープラーニングまとめ • 深層ニューラルネットワーク – 複数のユニットを階層的に結合して構築され る数理モデル – 学習: 損失関数を最小化するように確率的勾 配降下法でネットワークのパラメータを更新 • 畳み込みニューラルネット (CNN) – 画像認識のために畳み込み演算を基本とする ニューラルネットワーク
  63. 63. CAFFEフレームワーク
  64. 64. DLフレームワークあれこれ Caffe Torch Tensorflow Theano Chainer C++ / Python / Matlab Lua C++ / Python Python Python Berkeley Facebook Google U Montreal PFI/PFN 画像認識向き 転移学習 配布モデル RNNは面倒 新しいレイヤー の開発は手間 モジュラーな構 造で使いやすい Luaは良くも悪 くもある 配布モデル 深層学習だけ じゃない スケーラブル、 ただし小規模で は遅め Kerasなどラッ パー 計算グラフの元 祖 RNNも容易 Keras, Lasagne などのラッパー APIは低レベル 使いやすい Pythonコード 動作は遅め ユーザーはほぼ 日本 他にもMXNet, CNTK (MS), MatConvNetなど
  65. 65. Caffeの特徴 • 画像認識に強み – 逆にその他のタスクには少し使いにくい • 単一ホストでの計算速度 • モジュール単位のコード設計 • 設定ファイルのみで使える • 学習済みのモデルが広く配布されている
  66. 66. Caffeの機能 学習 • モデル定義を作成 • 学習パラメータを作成 • データを準備 • 学習を実行 • 学習したモデルを保存 テスト • モデル定義と学習済みモ デルを準備 • アプリケーションで利用 SolverNet Net weights
  67. 67. ネット (net) • モデル定義 – NNの構造をレイヤー 単位で記述 • Prototxtフォーマット – Protocol bufferのテキ スト形式 – Pythonから生成する APIあり name: "CaffeNet" layer { name: "data" type: "Input" top: "data" input_param { shape: { dim: 10 dim: 3 dim: 227 dim: 227 } } } layer { name: "conv1" type: "Convolution" bottom: "data" top: "conv1" convolution_param { num_output: 96 kernel_size: 11 stride: 4 } } https://developers.google.com/protocol-buffers/
  68. 68. レイヤー (layer) • ニューラルネットの各層で の演算を実装するもの – レイヤーの集合がネット • レイヤーの種類、入力 (bottom)、出力(top)、各種 パラメータを記述 layer { name: "conv1" type: "Convolution" bottom: "data" top: "conv1" convolution_param { num_output: 96 kernel_size: 11 stride: 4 } } conv1 data conv1
  69. 69. 様々なレイヤー • Vision layers – Convolution – Pooling – LRN • Loss layers – Softmax with loss – Euclidean – Hinge – Sigmoid Cross-Entropy • Activation layers – ReLU – Sigmoid – Tanh • Data layers – Database – HDF5 – Images – Dummy • Common layers – Inner product – Split – Flatten – Reshape – Concatenation – Elementwise
  70. 70. ブロブ (blob) レイヤーを流れるデータを格納する4次元の配列 データやレイヤーのパラメータに利用 … C H W N N x C x H x W サンプル数 チャネル 高さ 幅
  71. 71. レイヤーとブロブ http://caffe.berkeleyvision.org/tutorial/net_layer_blob.html layer { name: "conv1" type: "Convolution" bottom: "data" top: "conv1" convolution_param { num_output: 96 kernel_size: 11 stride: 4 } } レイヤーには通常、dataとdiffの二種類のブロブがあり、 レイヤー間で順伝播、逆伝播のやりとりに使われる dataとdiffの ブロブ dataとdiffの ブロブ 演算
  72. 72. ネットの例 http://caffe.berkeleyvision.org/tutorial/net_layer_blob.html name: "LogReg" layer { name: "mnist" type: "Data" top: "data" top: "label" data_param { source: "input_leveldb" batch_size: 64 } } layer { name: "ip" type: "InnerProduct" bottom: "data" top: "ip" inner_product_param { num_output: 2 } } layer { name: "loss" type: "SoftmaxWithLoss" bottom: "ip" bottom: "label" top: "loss" }
  73. 73. 順伝播と逆伝播 forward and backward • 順伝播 (forward) – BottomからTopへ演算 – Dataブロブに計算経過 が格納 • 逆伝播 (backward) – TopからBottomへ微分 演算 – Diffブロブに計算経過 が格納 http://caffe.berkeleyvision.org/tutorial/net_layer_blob.html forward backward
  74. 74. Directed Acyclic Graph (DAG) • Caffeのnetはループな しの有向グラフ • bottomとtopをつなげ る • RNN / LSTMは一つの レイヤーとして実装 LRCN joint vision-sequence model GoogLeNet Inception Module SDS two-stream net Slide credit [DIY Deep Learning for Vision]
  75. 75. 損失関数 • Forward時にLossレイ ヤーで計算 – E.g., SoftmaxWithLoss – Topブロブはスカラ値 • Loss weightを設定し て複数Lossに重みを 設定可能 layer { name: "loss" type: "SoftmaxWithLoss" bottom: "pred" bottom: "label" top: "loss" loss_weight: 1 }
  76. 76. データレイヤー • 入力データを扱うレイヤー – Datumフォーマットで画像が格納されたLMDBデータ 形式がよく使われる – 他の形式も可、簡単な前処理が指定可能 layer { name: "mnist" type: "Data" # データベース入力 top: "data" # 1番目は画像ブロブ top: "label" # 2番目はラベルのブロブ data_param { source: "examples/mnist/mnist_train_lmdb" # ファイルパス backend: LMDB # LMDB形式を指定 batch_size: 64 # バッチ数 } transform_param { scale: 0.00390625 } # ピクセル値を1/255 }
  77. 77. ソルバー • 学習に使うSGDアル ゴリズム • 学習時のパラメータ を設定 – アルゴリズムのパラ メータ – ディスプレイ表示 – スナップショット • Prototxtフォーマット test_iter: 100 test_interval: 500 base_lr: 0.01 display: 100 max_iter: 10000 lr_policy: "inv" gamma: 0.0001 power: 0.75 momentum: 0.9 weight_decay: 0.0005 snapshot: 5000 snapshot_prefix: "examples/mnist/lenet" solver_mode: GPU net: "examples/mnist/lenet.prototxt"
  78. 78. ソルバーとスナップショット • ソルバーで設定した間隔で、現在のモデ ルを自動で保存 snapshot: 5000 snapshot_prefix: examples/mnist/lenet snapshot_diff: false snapshot_after_train: true examples/mnist/lenet_iter_5000.caffemodel examples/mnist/lenet_iter_5000.solverstate examples/mnist/lenet_iter_10000.caffemodel examples/mnist/lenet_iter_10000.solverstate examples/mnist/lenet_iter_15000.caffemodel examples/mnist/lenet_iter_15000.solverstate
  79. 79. GPUサポート • CPUとGPUで透過的 に利用可能 – ソルバーの設定を切り 替えるだけ • 学習にはほぼ必須 [NVIDIA GTX 1080]
  80. 80. コマンドラインの使い方 # LeNetを学習、Solverの中にどのネットを使うか指定してある caffe train -solver examples/mnist/lenet_solver.prototxt # 2番目のGPUデバイスを使う caffe train -solver examples/mnist/lenet_solver.prototxt -gpu 2 # スナップショットを使って学習を途中からやりなおす場合 caffe train -solver examples/mnist/lenet_solver.prototxt -snapshot examples/mnist/lenet_iter_5000.solverstate 学習 テスト # 学習したネットを使ってテストセットからバッチ100反復分の計算 caffe test -model examples/mnist/lenet_train_test.prototxt -weights examples/mnist/lenet_iter_10000.caffemodel -gpu 0 -iterations 100
  81. 81. Model Zoo • コミュニティが学習 済みモデルを配布 • 最先端のモデルをす ぐに利用可能 • ダウンロードは1行 で https://github.com/BVLC/caffe/wiki/Model-Zoo ./scripts/download_model_from_gist.sh
  82. 82. オンラインドキュメント http://caffe.berkeleyvision.org/
  83. 83. CAFFEのビルドと環境設定
  84. 84. Caffeの入手 • 入手はGithubから git clone https://github.com/BVLC/caffe
  85. 85. Caffeのコード構成 CaffeのC++ヘッダー Matlab関連 学習済みネットワークなど Python関連 ダウンロードなどのスクリプト CaffeのC++ソース C++ツール群のソース 使用例など ドキュメントの生成先 Docker関連ファイル データセット置き場 CMake関連ファイル
  86. 86. Caffeの動作環境について • Linuxプラットフォーム推奨 (e.g., Ubuntu) – Windowsポートも存在 – DockerやAWS利用という方法も • GPU利用にはCUDAライブラリ • その他BLAS, Boost, OpenCVなど
  87. 87. ビルドのワークフロー 1. プラットフォームの準備 – CUDA利用可能なGPU付きのコンピュータ – Ubuntu Linux 16.04 or 14.04 2. 依存ライブラリのインストール 3. ビルド – MakefileまたはCMake 4. (オプション) Python環境の設定
  88. 88. Ubuntu Linuxの場合 • 事前に必要なパッケージをインストール sudo apt-get install libprotobuf-dev libleveldb-dev libsnappy-dev libopencv-dev libhdf5-serial-dev protobuf-compiler libatlas-base-dev libgflags-dev libgoogle-glog-dev liblmdb-dev python-dev sudo apt-get install --no-install-recommends libboost-all-dev NVIDIAのサイトから CUDA パッケージをダウンロード、インストール https://developer.nvidia.com/cuda-downloads sudo dpkg -i cuda-repo-ubuntu1404_8.0.44-1_amd64.deb sudo apt-get update sudo apt-get install cuda
  89. 89. ビルド: Makefileの場合 • Makefile.configファイ ルを作成 – 使用ライブラリや Pythonの設定など • 準備できたらコマン ドラインからビルド # cuDNN acceleration switch (uncomment to build with cuDNN). USE_CUDNN := 1 # CPU-only switch (uncomment to build without GPU support). # CPU_ONLY := 1 # Uncomment to support layers written in Python (will link against Python libs) WITH_PYTHON_LAYER := 1 cp Makefile.config.example Makefile.config # Makefile.configを開いて編集 make -j4 all make test make runtest -j4オプションは並列ビルドの数
  90. 90. ビルド: CMakeの場合 • Makefile.configの代わ りにCMakeを設定 – CMakeについて cd caffe mkdir build cd build cmake .. make –j4 all make install make runtest https://cmake.org/
  91. 91. cuDNN • オプションでNVIDIA のcuDNNライブラリ を使用可 – 通常のGPU演算からさ らに演算速度が向上 • インストール方法 https://developer.nvidia.com/cudnn 1. NVIDIAサイトにユーザー登録し、ダウンロード 2. 手順に従ってシステムにインストール 3. CaffeのMakefile.configで USE_CUDNN := 1 を設定
  92. 92. Pythonインタフェースのビルド • 事前準備: Pythonとpipが使えること – Anacondaを利用することも可能 • ビルド – configファイルでパスの設定をしておく • Caffeで使うPythonパッケージの入手 – requirements.txtファイルにパッケージ一覧 for req in $(cat requirements.txt); do pip install $req; done sudo apt-get install python python-pip make pycaffe
  93. 93. Anaconda • Pythonのデータ分析関連 のライブラリをひとまと めにパッケージしたもの – フリー • CaffeのPythonインタ フェースの準備にも使用 可能 • 便利だが、ライブラリの 依存関係で問題が発生す ることも https://www.continuum.io/
  94. 94. MATLABインタフェースのビルド • MATLABからCaffeを利用したい場合は Makefile.configでビルド設定可能 • ビルドはMake # This is required only if you will compile the matlab interface. # MATLAB directory should contain the mex binary in /bin. MATLAB_DIR := /usr/local make matcaffe
  95. 95. トラブルシュート • ビルドエラーが出た場合は焦らずエラー 内容を検索 – ビルドの依存ライブラリが不足していたりパ スの設定ミスがないか確認 • Anacondaパッケージで依存関係のコンフ リクトが起きる場合もある – ヒント: lddツールでコンフリクトを発見 ldd build/lib/libcaffe.so
  96. 96. Windows対応について • GithubにてWindows対応ブランチ開発 • cuDNN含むGPU対応あり • DLフレームワーク全般でUNIX環境前提のた め、どうしてもWindowsが必要な場合のみ • Dockerを利用する方法も https://github.com/BVLC/caffe/tree/windows
  97. 97. Docker対応について • Caffeは簡単にDocker対応 • コンテナ化による仮想実行環境構築プラットフォーム – 開発環境と大規模サービス展開で同一環境 – コンテナにより完全仮想化よりも軽量動作 – nvidia-dockerによりGPUも使用可 Container PC Cloud Docker Runtime App Container Runtime App Container Runtime App Jupyter Ubuntu, Caffe Windows, Mac, Linux
  98. 98. Dockerで環境構築する手順 • Dockerイメージのビルド (cpu) • 仮想環境の実行 docker build -t caffe:cpu docker/standalone/cpu docker run –ti caffe:cpu [command] # 例: caffe trainを使う docker run –ti caffe:cpu caffe train –solver=lenet.prototxt
  99. 99. PYTHONについて
  100. 100. なぜPython? • インタラクティブにCaffeを使う方法 – CaffeではNumpyを使ったAPIを提供 • 科学技術計算向けライブラリが充実 – 機械学習ではPythonが標準的 • フリー – MATLABはライセンス購入必須
  101. 101. CaffeのPython APIの例 # caffeのインポート import caffe # GPUのモードの指定 caffe.set_mode_gpu() caffe.set_device(0) # ソルバーの作成 solver = caffe.SGDSolver('/path/to/solver.prototxt') # 学習のメインループ for i in range(100): solver.step(1) if i % 10 == 0: print(solver.net.blobs['loss'].data[0]) # モデルの保存 solver.snapshot()
  102. 102. 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 一般的な演算が利用可能
  103. 103. コンテナ # 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乗整数から整数へのマップ
  104. 104. 文字列 # 文字列はシングルクオートまたはダブルクオートで 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
  105. 105. 関数とクラス 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()) クラス定義も同様
  106. 106. コメントとHeredoc # コメントは#で始める text = ''' Heredocは3つのシングルクオート(') またはダブルクオート(") で囲った複数行の文字列 ''' text2 = """ ダブルクオートの例 """ def func(): '''関数のヘルプにはHeredocが使われる''' return 0
  107. 107. 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 3Python 2
  108. 108. • 作成したファイルは他からモジュールとして呼び出した りパッケージとして利用可能 Pythonモジュールとパッケージ import myutils myutils.func() import mypackage.apple from mypackage import apple myutils.py mypackage/ mypackage/__init__.py mypackage/apple.py mypackage/pineapple.py プログラムファイル
  109. 109. Python環境のインストール sudo apt-get install python python-pip sudo apt-get install python3 python3-pip Python 2の場合 Python 3の場合 Anacondaの場合: 公式サイトからダウンロード https://www.continuum.io/
  110. 110. Pythonと数値計算 • Pythonの科学技術計算パッケージ群 – ほぼMATLABと等価な機能を提供 – インストールはpipやapt-get、Anacondaなど Python Python本体 Numpy 数値計算のコアライブラリ Scipy 科学技術計算ライブラリ Matplotlib プロット用ライブラリ IPython 高機能インタラクティブシェル pandas データ解析用ライブラリ Sympy シンボリック計算 nose ユニットテスト
  111. 111. pip • Pythonのパッケージマネージャ – PyPIレポジトリに登録されているPythonライ ブラリは全てpipコマンドで入手可能 • インストールはコマンドラインから pip install numpy scipy matplotlib [Python software foundation]
  112. 112. Numpy • 多次元配列を扱うライブラリ import numpy as np # numpyをnpという名前でインポート # ネストされたリストから2次元Numpy配列を作成 X = np.array([[1, 2], [3, 4]]) print(X) # =>[[1 2] [3 4]] • Pythonリストとの変換 • スライス、整数、論理インデックスによる部分読み出し • 演算 • ブロードキャスト
  113. 113. 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
  114. 114. 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)) # 行ごとに和
  115. 115. 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]]"
  116. 116. 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()
  117. 117. SciPy, Matplotlibでの画像の扱い # SciPy, Matplotlibをインポート import numpy as np from scipy.misc import imread import matplotlib.pyplot as plt # 画像を読み込み img = imread('caffe/examples/images/cat.jpg') # 画像を表示 plt.imshow(img) plt.show()
  118. 118. CaffeのPython API # 学習済みnetをTESTモードで読み込み net = caffe.Net('net.prototxt', 'net.caffemodel', caffe.TEST) # 各レイヤーのBlobにアクセスするときはNumpy形式 image = caffe.io.load_image('test.jpg') preprocessed = preprocess(image) net.blobs['data'].data[...] = preprocessed net.forward() result = net.blobs['prob'].data[0, :]
  119. 119. Caffeをimportするために # Pythonプログラム中からPathを追加する方法 import sys sys.path.append('/path/to/caffe/python') import caffe # BashなどでUNIXの環境変数を設定する方法 export PYTHONPATH=$PYTHONPATH:/path/to/caffe/python # .bashrcファイルなどに記述しておけば、以降はPythonを起動して すぐにCaffeをimportできるようになる ビルド済みのCaffeに対してPathを設定しておく必要あり PythonはPYTHONPATH環境変数を参照する 環境変数がなくてもプログラム中でPathの設定はできる
  120. 120. Jupyter Webブラウザからプログラムを動かすインタラクティブシェル環境 - ノートブック形式によるPythonプログラミング - UNIXターミナルの起動 - 通常のテキストファイル編集 - プラグインによりPython以外の言語にも対応
  121. 121. Jupyter notebook
  122. 122. Jupyterのインストールと起動 pip install jupyter jupyter notebook jupyter notebook --generate-config インストールはpipでOK 起動、デフォルトではブラウザも立ち上げ 起動時のオプションを作成する場合 jupyter notebook --help その他ヘルプ
  123. 123. CAFFEの具体的な使い方
  124. 124. Caffeの具体的な使い方 1. 線形回帰の記述 – 学習とテストの一連の機械学習の流れ 2. 一般画像認識 – テスト時のCaffeの使い方 3. 手書き文字識別器の学習 – Caffeでの学習にまつわる基本 4. ファインチューニング – 実用的な画像認識モデルの学習方法 5. 学習データの準備 – 手元にあるデータを使うために
  125. 125. 1. 線形回帰 • 単純な線形回帰モデルの学習と予測 𝑦 = 𝒘 ∙ 𝒙 + 𝑏 目的: 学習とテストの一連の機械学習の流れの理解
  126. 126. ワークフロー 1. 学習データの用意 2. モデルの記述 3. ソルバーの記述 4. ソルバーの実行 5. テストデータの用意 6. 学習済みモデルを使った予測 7. 結果の解釈 学習 テスト
  127. 127. 線形回帰 layer { name: "x" type: "DummyData" top: "x" top: "label" dummy_data_param { shape { dim: 10 dim: 2 } shape { dim: 10 dim: 1 } } } layer { name: "y" type: "InnerProduct" bottom: "x" top: "y” inner_product_param { num_output: 1 } } layer { name: "loss" type: "Euclidean" bottom: "y" bottom: "label" top: "loss" } 𝑦 = 𝒘 ∙ 𝒙 + 𝑏 Prototxtファイル 𝒙 𝑦 InnerProduct label DummyData Euclidean loss
  128. 128. 線形回帰: データの準備 # 学習データの用意: 今回は人工的にデータを生成し、雑音を加える import numpy as np Y = np.linspace(-10, 10, 100).reshape((100, 1)) X = np.concatenate(( Y + 4 * np.random.random((100, 1)), Y + 3 * np.random.random((100, 1))), axis=1) 𝑦 = 𝑤1 𝑥1 + 𝑤2 𝑥2 + 𝑏 この例では以下のモデルフィッティングに使うデータをを乱数から作成
  129. 129. 線形回帰: Pythonでのモデル記述 import caffe from caffe.net_spec import layers as L, params as P def linear_regression(batch_size=10): '''線形回帰をPython NetSpec APIで指定する関数: 2次元の変数の回帰''' net = caffe.NetSpec() net.x, net.label = L.DummyData(ntop=2, shape=[{'dim': [batch_size, 2]}, {'dim': [batch_size, 1]}]) net.y = L.InnerProduct(net.x, num_output=1) net.loss = L.EuclideanLoss(net.y, net.label) return str(net.to_proto()) # モデルをファイルに出力 with open('linear_regression.prototxt', 'w') as f: f.write(linear_regression())
  130. 130. 線形回帰: ソルバーの準備 # Solverの記述: # caffe.proto.caffe_pb2.SolverParameter()関数もあるが使いにくい solver_params = ''' base_lr: 0.01 max_iter: 1000 lr_policy: "fixed" momentum: 0.8 weight_decay: 1.0 snapshot_prefix: "linear_regression" solver_mode: CPU net: "linear_regression.prototxt" ''' # Solverをファイルに出力 with open('lr_solver.prototxt', 'w') as f: f.write(solver_params)
  131. 131. 線形回帰: ソルバーの実行 # Solverの読み込み solver = caffe.SGDSolver('lr_solver.prototxt') batch_size = 10 # SGDによる最適化のメインループ for epoch in range(20): loss = 0 # Epoch毎にデータセットからバッチ単位で入力し、バックプロパゲーション for offset in range(0, 100, batch_size): solver.net.blobs['x'].data[...] = X[offset:offset+batch_size, :] solver.net.blobs['label'].data[...] = Y[offset:offset+batch_size, :] solver.step(1) loss += np.sum(solver.net.blobs['loss'].data) # Epoch毎のロスを表示 print('epoch={}, loss={}'.format(epoch, loss) epoch=0, loss=245.071829081 epoch=1, loss=116.476937354 epoch=2, loss=106.749805808 epoch=3, loss=42.7126669586 epoch=4, loss=43.6113110781 epoch=5, loss=22.7367181778 epoch=6, loss=27.3585570753 epoch=7, loss=19.8172527105 epoch=8, loss=22.8411263227 epoch=9, loss=19.8282705545 出力例:
  132. 132. 線形回帰: モデルの保存とテスト # Solverで現在のスナップショット作成 solver.snapshot() # 学習済みのモデルはファイルからTESTモードで読み込み可能 net = caffe.Net('linear_regression.prototxt', 'linear_regression_iter_200.caffemodel', caffe.TEST) # 使い方はソルバーの実行時と同様、入力を入れてforward計算 net.blobs['x'].data[...] = X[0:10, :] net.forward() Yhat = net.blobs['y'].data[...] # ただし、Lossの計算はテスト時には不要なので一般には # テスト用モデルのprototxtファイル(deployファイル)を作成し、 # 学習済みのパラメータのみ読み込む
  133. 133. 線形回帰: 予測例 # Yhatは全てのXに対する予測点の配列 import matplotlib.pyplot as plt %matplotlib inline plt.plot(Y) plt.plot(Yhat, 'r.') plt.show() %matplotlib inline はJupyterでブラウザ画面に プロットを描画するための 指示
  134. 134. 線形回帰: 全てのプログラム import numpy as np import caffe from caffe.net_spec import layers as L, params as P # データの準備 Y = np.linspace(-10, 10, 100).reshape((100, 1)) X = np.concatenate(( Y + 4 * np.random.random((100, 1)), Y + 3 * np.random.random((100, 1))), axis=1) batch_size = 10 def linear_regression(batch_size=10): '''モデルの記述''' net = caffe.NetSpec() net.x, net.label = L.DummyData(ntop=2, shape=[{'dim': [batch_size, 2]}, {'dim': [batch_size, 1]}]) net.y = L.InnerProduct(net.x, num_output=1) net.loss = L.EuclideanLoss(net.y, net.label) return str(net.to_proto()) with open('linear_regression.prototxt', 'w') as f: f.write(linear_regression()) # Solverの記述: solver_params = ''' base_lr: 0.01 max_iter: 1000 lr_policy: "fixed" momentum: 0.8 weight_decay: 1.0 snapshot_prefix: "linear_regression" solver_mode: CPU net: "linear_regression.prototxt" ''' # Solverをファイルに出力 with open('lr_solver.prototxt', 'w') as f: f.write(solver_params) # Solverの読み込みと学習ループ solver = caffe.SGDSolver('lr_solver.prototxt') for epoch in range(20): loss = 0 for offset in range(0, 100, batch_size): solver.net.blobs['x'].data[...] = X[offset:offset+batch_size, :] solver.net.blobs['label'].data[...] = Y[offset:offset+batch_size, :] solver.step(1) loss += np.sum(solver.net.blobs['loss'].data) # Epoch毎のロスを表示 print('epoch={}, loss={}'.format(epoch, loss) # モデルを保存 solver.snapshot() # 学習済みモデルの読み込み net = caffe.Net('linear_regression.prototxt', 'linear_regression_iter_2000.caffemodel', caffe.TEST) # データから予測: 今回は学習データそのまま Yhat = np.zeros_like(Y) for offset in range(0, 100, batch_size): net.blobs['x'].data[...] = X[offset:offset+batch_size, :] net.forward() Yhat[offset:offset+batch_size, :] = net.blobs['y'].data[...] # プロット import matplotlib.pyplot as plt %matplotlib inline plt.plot(Y) plt.plot(Yhat, 'r.') plt.show()
  135. 135. 学習からテストまでのポイント • データの準備 • Pythonを使ってモデルのファイルを生成 – 直接Prototxtファイルを作成編集も可能 – ソルバーも設定をPrototxtで書き出し • ソルバーを実行するプログラムを作成し、実 行、最後にモデルを保存 – caffe.SGDSolver() • テスト時には保存したモデルを読み込み – caffe.Net()
  136. 136. 2. 一般画像認識 • 配布されている学習済みのCaffeNetモデルを使って画像分類 .312 tabby, tabby cat .237 tiger cat .123 Egyptian cat .101 red fox, Vulpes vulpes .007 lynx, catamount 分類結果と確率 目的: テスト時のCaffeの使い方の理解 Caffeに含まれるexamples/00-classification.ipynbに相当
  137. 137. CaffeNet data conv1/relu1 pool1 norm1 conv2/relu2 pool2 norm2 conv3/relu3 conv4/relu4 conv5/relu5 pool5 fc6/relu6/drop6 fc7/relu7/drop7 fc8 prob • AlexNet [Krizhevsky 2012]のCaffe実装 • ImageNet 2012のWinner • 画像認識で使うCNNの基本的モデル • 5段のConv層と3段のFC層から構成 • Caffeではライセンスフリーで配布 • 以下ディレクトリにPrototxtなど models/bvlc_reference_caffenet/
  138. 138. テスト時のワークフロー 1. CaffeNetのインポート 2. 学習済みモデルをファイルから読み込み 3. 画像の前処理 – 画像サイズやNumpy配列の調整など 4. ネットワークでForward計算 5. 結果の解釈
  139. 139. 学習済みのネットワークの読込 # numpyとmatplotlibを使うためにインポートします import numpy as np import matplotlib.pyplot as plt import caffe caffe.set_mode_cpu() # CPUモードでCaffeを動かします。GPUでも可 caffe_root = '/home/user/caffe/' model_def = caffe_root + 'models/bvlc_reference_caffenet/deploy.prototxt' model_weights = caffe_root + 'models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel' net = caffe.Net(model_def, # ニューラルネットの構造を記述したファイル model_weights, # ニューラルネットのパラメータを保存したファイル caffe.TEST) # 学習ではなくテストモードでネットワークを作成 cd /path/to/caffe scripts/download_model_binary.py models/bvlc_reference_caffenet Caffeでは学習済みのImageNetモデルをスクリプトでダウンロード可能
  140. 140. 新しい画像に対する前処理 # ImageNetの平均画像を読み込みます(Caffeと一緒に配布されています) mu = np.load(caffe_root + 'python/caffe/imagenet/ilsvrc_2012_mean.npy') mu = mu.mean(1).mean(1) # BGRの平均ピクセル値を計算します print 'mean-subtracted values:', zip('BGR', mu) # 'data'という名前でTransformerを作ります transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape}) transformer.set_transpose('data', (2,0,1)) # 色チャネルを最初の次元に動かす transformer.set_mean('data', mu) # データセットの平均値を引き算 transformer.set_raw_scale('data', 255) # スケールを[0, 1]から[0, 255]へ transformer.set_channel_swap('data', (2,1,0)) # RGBからBGRへ • 配布されているImageNetモデルは3x227x227のBGR配列の浮動小数点画像 を扱うようになっています • これに合うように画像の前処理を行い、Blobに格納できるようにします • CaffeにはTransformerという前処理のためのクラスが付属しています
  141. 141. Forward計算 # ニューラルネットが受け取るBlob配列の形状を指定します # デフォルト値でよければやらなくても大丈夫です net.blobs['data'].reshape(50, # バッチサイズ 3, # 3色(BGR)画像 227, 227) # 画像の大きさは227x227 # テスト画像を読み込み、前処理をしてBlobに格納します image = caffe.io.load_image(caffe_root + 'examples/images/cat.jpg') transformed_image = transformer.preprocess('data', image) net.blobs['data'].data[...] = transformed_image # フォワード計算をします output = net.forward() • 入力画像に前処理を施し、CNNで確率値を出力します • 今回のテスト画像は猫です
  142. 142. 結果の解釈(1) # バッチの中の最初の行が1000次元のカテゴリ確率分布のベクトルになっています output_prob = output['prob'][0] print('predicted class is:', output_prob.argmax()) predicted class is: 281 CaffeNet 0 999281 • どのカテゴリの確率値が最大だったかをargmax()で調べてみましょう • 281番のカテゴリが一番確率が高いという結果
  143. 143. 結果の解釈(2) # Softmax出力をソートしてトップ5の予測を取得 top_inds = output_prob.argsort()[::-1][:5] # 逆順ソートしてトップ5抜き出し print('probabilities and labels:') print(zip(output_prob[top_inds], labels[top_inds])) # ImageNetの1000カテゴリのラベルがテキストファイルに保存してあるので読み込み labels_file = caffe_root + 'data/ilsvrc12/synset_words.txt' labels = np.loadtxt(labels_file, str, delimiter='t') print('output label:', labels[output_prob.argmax()]) output label: n02123045 tabby, tabby cat [(0.31243637, 'n02123045 tabby, tabby cat'), (0.2379719, 'n02123159 tiger cat'), (0.12387239, 'n02124075 Egyptian cat'), (0.10075711, 'n02119022 red fox, Vulpes vulpes'), (0.070957087, 'n02127052 lynx, catamount')] • カテゴリの名前を表示するには番号とラベルの対応を見てみます tabby cat = ブチ猫
  144. 144. テスト時のポイント • モデルをPrototxtと学習済みのcaffemodel ファイルから読み込み • データは前処理を施してBlobにコピー • 推論はforward計算をするだけでOK • 結果はベクトルで得られるので、意味を 解釈する方法を用意
  145. 145. 3. 手書き文字識別器の学習 • MNIST手書き数字データセッ トからCNNを学習 – LeNetモデルを学習 • データレイヤーの使い方 • ソルバーの使い方 • 学習ループの構成 目的: Caffeでの学習にまつわる基本を理解する MNISTデータセット Caffeに含まれるexamples/01-learning-lenet.ipynbに相当
  146. 146. MNISTデータについて • 機械学習の標準的ベンチマーク • 今回はCaffeで標準のLMDB形式を使用 • データの入手と変換はCaffe付属のスクリプトで cd /path/to/caffe # バイナリ形式のデータをダウンロード data/mnist/get_mnist.sh # データの変換ツールもCaffeに付属していますexamples/mnist/create_mnist.sh The MNIST Database of handwritten digits Yann LeCun, Corinna Cortes, Christopher JC Burges http://yann.lecun.com/exdb/mnist/
  147. 147. Caffe版LeNet • 2層のconv層 • 1層のfc層 data conv1 pool1 conv2 pool2 fc1/relu1 score label loss
  148. 148. LeNetの構築 import caffe from caffe.net_spec import layers as L, params as P def lenet(lmdb, batch_size): # Caffe版LeNet: 線形変換と非線形変換を組み合わせたもの n = caffe.NetSpec() n.data, n.label = L.Data(batch_size=batch_size, backend=P.Data.LMDB, source=lmdb, transform_param=dict(scale=1./255), ntop=2) n.conv1 = L.Convolution(n.data, kernel_size=5, num_output=20, weight_filler=dict(type='xavier')) n.pool1 = L.Pooling(n.conv1, kernel_size=2, stride=2, pool=P.Pooling.MAX) n.conv2 = L.Convolution(n.pool1, kernel_size=5, num_output=50, weight_filler=dict(type='xavier')) n.pool2 = L.Pooling(n.conv2, kernel_size=2, stride=2, pool=P.Pooling.MAX) n.fc1 = L.InnerProduct(n.pool2, num_output=500, weight_filler=dict(type='xavier')) n.relu1 = L.ReLU(n.fc1, in_place=True) n.score = L.InnerProduct(n.relu1, num_output=10, weight_filler=dict(type='xavier')) n.loss = L.SoftmaxWithLoss(n.score, n.label) return n.to_proto() with open('mnist/lenet_auto_train.prototxt', 'w') as f: f.write(str(lenet('mnist/mnist_train_lmdb', 64))) with open('mnist/lenet_auto_test.prototxt', 'w') as f: f.write(str(lenet('mnist/mnist_test_lmdb', 100))) • まずはLeNetモデルを記述し、Prototxtに書き出します
  149. 149. データレイヤーについて layer { name: "data" type: "Data" top: "data" top: "label" transform_param { scale: 0.00392156862745 } data_param { source: "mnist/mnist_train_lmdb" batch_size: 64 backend: LMDB } } data LMDBデータベース / LevelDBデータベース key value 0001 datum 0002 datum 0003 datum ... ... • LMDB/LevelDBに格納されたDatum形式のデータを読み込むレイヤー • Forward/backward計算をする際にバッチを読み込み
  150. 150. ソルバーの設定 # バッチサイズにこの値をかけた値がちょうどテストデータのサイズと一致するようにしましょう test_iter: 100 # Pythonで独自にループ中にテストする場合は自動テストは不要です # 大きな値にしておくと実行されません test_interval: 500 # 以下のパラメータは勾配降下法での学習の成否に大きく影響します # 自分のデータや問題設定に適切な値を試行錯誤する必要があります type: "SGD" base_lr: 1e-5 momentum: 0.9 weight_decay: 0.0005 # SGDの学習率のポリシーも学習の進行に多大な影響があります lr_policy: "inv" gamma: 0.0001 power: 0.75 solver_params = ''' test_iter: 100 ''' with open('mnist/lenet_auto_solver.prototxt', 'w') as f: f.write(solver_params) 上記のような内容をファ イルに書き出します
  151. 151. SGD学習率のポリシー base_lr: 0.01 # begin training at a learning rate of 0.01 = 1e-2 lr_policy: "step" # learning rate policy: drop the learning rate in "steps" # by a factor of gamma every stepsize iterations gamma: 0.1 # drop the learning rate by a factor of 10 # (i.e., multiply it by a factor of gamma = 0.1) stepsize: 10000 # drop the learning rate every 10K iterations momentum: 0.9 STEPポリシー: 一定反復毎に学習率をgamma倍に引き下げ base_lr: 0.01 # begin training at a learning rate of 0.01 = 1e-2 lr_policy: "fixed" # learning rate policy: keep constant max_iter: 100000 # finish at 100K iterations momentum: 0.9 FIXEDポリシー: 初期設定の学習率を保持 その他にexp, inv, multistep, ploy, sigmoidポリシー、詳細はドキュメント参照 モメンタムと学習率の例 momentum: 0.9 base_lr: 0.01 momentum: 0.99 base_lr: 0.001 http://caffe.berkeleyvision.org/tutorial/solver.html
  152. 152. ソルバーの設定例 # The train/test net protocol buffer definition train_net: "mnist/lenet_auto_train.prototxt" test_net: "mnist/lenet_auto_test.prototxt" # test_iter specifies how many forward passes the test should carry out. # In the case of MNIST, we have test batch size 100 and 100 test iterations, # covering the full 10,000 testing images. test_iter: 100 # Carry out testing every 500 training iterations. test_interval: 500 # The base learning rate, momentum and the weight decay of the network. base_lr: 0.01 momentum: 0.9 weight_decay: 0.0005 # The learning rate policy lr_policy: "inv" gamma: 0.0001 power: 0.75 # Display every 100 iterations display: 100 # The maximum number of iterations max_iter: 10000 # snapshot intermediate results snapshot: 5000 snapshot_prefix: "mnist/lenet" 今回はこの内容で 'mnist/lenet_auto_solver.prototxt'に保存 します
  153. 153. ソルバーの読み込みと動作確認 # ソルバーを読み込み solver = caffe.SGDSolver('mnist/lenet_auto_solver.prototxt') solver.step(1) # これでLMDBからデータを読み込み1回backward計算をします • ミニバッチを1ステップ動かして、ネットワークの重みが更新され たか可視化してみましょう # 可視化、ややこしいですがconv1の重みを4x5個抜き出して表示しています from pylab import * imshow(solver.net.params['conv1'][0].diff[:, 0].reshape(4, 5, 5, 5) .transpose(0, 2, 1, 3).reshape(4*5, 5*5), cmap='gray'); axis('off') conv1のパラメータの可視化 何らかの模様が見えていればOKです
  154. 154. 学習ループの構成: 学習経過のログと指定間隔でのテスト niter = 200 test_interval = 25 train_loss = zeros(niter) # 損失関数(loss)の値を保存します test_acc = zeros(int(np.ceil(niter / test_interval))) # 正答率を保存します output = zeros((niter, 8, 10)) # 出力を保存します for it in range(niter): # メインのソルバーループ: 本当に大事なのはsolver.step(1)のみ solver.step(1) # 学習ロスを記録 train_loss[it] = solver.net.blobs['loss'].data # 最初のテストバッチの出力を記録 # (新しいデータを読み込まないようにフォワード処理をconv1でやる) solver.test_nets[0].forward(start='conv1') output[it] = solver.test_nets[0].blobs['score'].data[:8] # テストをソルバーとは別に独自の指定間隔でやる if it % test_interval == 0: print('Iteration', it, 'testing...') correct = 0 for test_it in range(100): solver.test_nets[0].forward() correct += sum(solver.test_nets[0].blobs['score'].data.argmax(1) == solver.test_nets[0].blobs['label'].data) test_acc[it // test_interval] = correct / 1e4
  155. 155. 学習経過のプロット _, ax1 = subplots() ax2 = ax1.twinx() ax1.plot(arange(niter), train_loss) ax2.plot(test_interval * arange(len(test_acc)), test_acc, 'r') ax1.set_xlabel('iteration') ax1.set_ylabel('train loss') ax2.set_ylabel('test accuracy') ax2.set_title('Test Accuracy: {:.2f}'.format(test_acc[-1])) ロスが減少して収束、正答率 が上昇したら学習は上手く進 んでいます 参考までに、MNISTでは最高 で99.8%の正答率 [Wan 2013] http://rodrigob.github.io/are_we_there_yet/build/classification_datasets_results.html
  156. 156. 学習のポイント • モデル構造とソルバーを設定しループを構成 – データレイヤーを使うとループの記述が若干シン プルに – ログの取得方法次第ではPythonを使わなくても • 学習経過は必ず観察すること • ソルバーの設定項目は学習の進捗にシビアに 効いてくるので適切な値を探す
  157. 157. 学習が上手くいかない場合 • モデルを改良 – 例えばLeNetの層数を増やすなど • 最適化手法を調整 – 学習率(base_lr)を10倍単位で変更 – モーメンタムを学習率と反比例して変更 – 学習率のポリシーを変更 • まずはfixedで適切な範囲を見つけると良い • ソルバーのアルゴリズムを変更 – SGDからAdaDeltaやAdamに変更
  158. 158. それでも上手くいかない場合 • データが悪いケース – データ数が少なすぎる – そもそも判別が難しすぎる – データの分布が偏りすぎている • 例: 99%が負例のデータでは常に負と予測しても正答率 99%になってしまう • モデルが悪いケース – 損失関数が不適切 – データの前処理が不十分
  159. 159. 4. ファインチューニング(転移学習) • 学習済みCaffeNetを初期 値として新しいデータ セットで再学習 • 数千データしか用意でき ない場合に実用的な手法 • ImageDataレイヤー • モデルの改造について 目的: 実用的な画像認識モデルの学習手順を理解する Caffeに含まれるexamples/02-fine-tuning.ipynbに相当 一般物体識別用 CaffeNet tabby cat tiger cat Egyptian cat Style分類用 CaffeNet Melancholy HDR Pastel
  160. 160. CaffeNetのレイヤー入れ替え data conv1 ... fc7 fc8 prob data conv1 ... fc7 fc8_flickr prob ImageNetモデル Style分類モデル tabby cat tiger cat Egyptian cat ... Melancholy HDR Pastel ... 1000クラス = 1000次元 5クラス = 5次元 データレイヤー の新規指定 Flickrの写真を 5カテゴリに分類
  161. 161. 学習済みのモデルを初期値として 読み込む方法 # PythonではNetのcopy_from()メソッドを使う solver = SGDSolver('solver.prototxt') solver.net.copy_from('pretrained.caffemodel') layer { name: "fc8" type: "InnerProduct" bottom: "fc7" top: "fc8" inner_product_param { num_output: 1000 } } 1. Prototxtのモデル記述を変更 • 最終FCレイヤーのnameと出力の数を変更 layer { name: "fc8_flickr" type: "InnerProduct" bottom: "fc7" top: "fc8" inner_product_param { num_output: 5 } } 2. ソルバーで学習済みモデルのパラメータを読み込む • nameが一致するレイヤーが読み込まれる
  162. 162. 転移学習の手順 data conv1 ... fc7 fc8_flickr prob 1. 学習済みのレイヤーは学習率を0 にし、新しく組み込んだfc8層の み学習 2. 収束したら、次に全てのレイ ヤーに学習率を設定し、End-to- endの学習 • 学習率は1.の時よりも小さく
  163. 163. ImageDataレイヤー 画像ファイルから直接読み込むためのデータレイヤー 用意するもの 1. 画像データ 2. 正解ラベルをカテゴリ番号で 記述したテキストファイル images/10344996196_1117743cfe.jpg 0 images/10878451835_36f42c8288.jpg 0 images/2219475505_cddf77b23f.jpg 3 images/13303239423_a3fa85f3ea.jpg 4 ... ファイルパス カテゴリラベル train.txt val.txt
  164. 164. ImageDataレイヤー layer { name: "data" type: "ImageData" bottom: "data" top: "label" transform_param { mirror: true crop_size: 227 mean_file: "data/ilsvrc12/imagenet_mean.binaryproto" } image_data_param { source: "data/flickr_style/train.txt" batch_size: 50 new_height: 256 new_width: 256 } }
  165. 165. Scratch vs. Fine-tuning ランダム初期化に比べ、ファインチューニングではすぐに収束 fc8の収束後、End-to-endで学習を進めると更に収束 Scratch (初期値) Fine-tuning (転移学習)
  166. 166. CaffeNetのPython記述 from caffe.net_spec import layers as L, params as P import tempfile weight_param = dict(lr_mult=1, decay_mult=1) bias_param = dict(lr_mult=2, decay_mult=0) learned_param = [weight_param, bias_param] frozen_param = [dict(lr_mult=0)] * 2 def conv_relu(bottom, ks, nout, stride=1, pad=0, group=1, param=learned_param, weight_filler=dict(type='gaussian', std=0.01), bias_filler=dict(type='constant', value=0.1)): conv = L.Convolution(bottom, kernel_size=ks, stride=stride, num_output=nout, pad=pad, group=group, param=param, weight_filler=weight_filler, bias_filler=bias_filler) return conv, L.ReLU(conv, in_place=True) def fc_relu(bottom, nout, param=learned_param, weight_filler=dict(type='gaussian', std=0.005), bias_filler=dict(type='constant', value=0.1)): fc = L.InnerProduct(bottom, num_output=nout, param=param, weight_filler=weight_filler, bias_filler=bias_filler) return fc, L.ReLU(fc, in_place=True) def max_pool(bottom, ks, stride=1): return L.Pooling(bottom, pool=P.Pooling.MAX, kernel_size=ks, stride=stride) 参照: caffe/examples/02-fine-tuning.ipynb
  167. 167. def caffenet(data, label=None, train=True, num_classes=1000, classifier_name='fc8', learn_all=False): """Returns a NetSpec specifying CaffeNet, following the original proto text specification (./models/bvlc_reference_caffenet/train_val.prototxt).""" n = caffe.NetSpec() n.data = data param = learned_param if learn_all else frozen_param n.conv1, n.relu1 = conv_relu(n.data, 11, 96, stride=4, param=param) n.pool1 = max_pool(n.relu1, 3, stride=2) n.norm1 = L.LRN(n.pool1, local_size=5, alpha=1e-4, beta=0.75) n.conv2, n.relu2 = conv_relu(n.norm1, 5, 256, pad=2, group=2, param=param) n.pool2 = max_pool(n.relu2, 3, stride=2) n.norm2 = L.LRN(n.pool2, local_size=5, alpha=1e-4, beta=0.75) n.conv3, n.relu3 = conv_relu(n.norm2, 3, 384, pad=1, param=param) n.conv4, n.relu4 = conv_relu(n.relu3, 3, 384, pad=1, group=2, param=param) n.conv5, n.relu5 = conv_relu(n.relu4, 3, 256, pad=1, group=2, param=param) n.pool5 = max_pool(n.relu5, 3, stride=2) n.fc6, n.relu6 = fc_relu(n.pool5, 4096, param=param) if train: n.drop6 = fc7input = L.Dropout(n.relu6, in_place=True) else: fc7input = n.relu6 n.fc7, n.relu7 = fc_relu(fc7input, 4096, param=param) if train: n.drop7 = fc8input = L.Dropout(n.relu7, in_place=True) else: fc8input = n.relu7 # always learn fc8 (param=learned_param) fc8 = L.InnerProduct(fc8input, num_output=num_classes, param=learned_param) # give fc8 the name specified by argument `classifier_name` n.__setattr__(classifier_name, fc8) if not train: n.probs = L.Softmax(fc8) if label is not None: n.label = label n.loss = L.SoftmaxWithLoss(fc8, n.label) n.acc = L.Accuracy(fc8, n.label) # write the net to a temporary file and return its filename with tempfile.NamedTemporaryFile(delete=False) as f: f.write(str(n.to_proto())) return f.name
  168. 168. 新しいCaffeNetの記述 def style_net(train=True, learn_all=False, subset=None): '''ImageDataレイヤーと新しいFC8レイヤーを組み入れたNetのPrototxt ファイルを書き出し、そのパスを返す関数''' if subset is None: subset = 'train' if train else 'test' source = caffe_root + 'data/flickr_style/%s.txt' % subset transform_param = dict(mirror=train, crop_size=227, mean_file=caffe_root + 'data/ilsvrc12/imagenet_mean.binaryproto') style_data, style_label = L.ImageData( transform_param=transform_param, source=source, batch_size=50, new_height=256, new_width=256, ntop=2) return caffenet(data=style_data, label=style_label, train=train, num_classes=num_classes, classifier_name='fc8_flickr', learn_all=learn_all) net = caffe.Net(style_net(train=False, subset='train'), weights, caffe.TEST) 上記のような関数を作ることで、以下のようにNetを作成可能
  169. 169. Solverの記述 from caffe.proto import caffe_pb2 def solver(train_net_path, test_net_path=None, base_lr=0.001): '''ソルバーのPrototxtファイルを書き出し、パスを返す関数''' s = caffe_pb2.SolverParameter() s.train_net = train_net_path if test_net_path is not None: s.test_net.append(test_net_path) s.test_interval = 1000 # テストは1000反復毎 s.test_iter.append(100) # テスト時は100バッチ使用 s.iter_size = 1 s.max_iter = 100000 s.type = 'SGD' s.base_lr = base_lr s.lr_policy = 'step' s.gamma = 0.1 s.stepsize = 20000 s.momentum = 0.9 s.weight_decay = s.display = 1000 s.snapshot = 10000 s.snapshot_prefix = caffe_root + 'models/finetune_flicker/finetune_flickr s.solver_mode = caffe_pb2.SolverParameter.GPU with tempfile.NamedTemporaryFile(delete=False) as f: f.write(str(s)) return f.name
  170. 170. 学習のメインループ def run_solvers(niter, solvers, disp_interval=10): """複数のソルバーをniter回反復させ、記録した損失と正答率を返す。 `solvers`は (name, solver) のタプルのリスト""" blobs = ('loss', 'acc') loss, acc = ({name: np.zeros(niter) for name, _ in solvers} for _ in blobs) for it in range(niter): for name, s in solvers: s.step(1) # SGDステップを一回進める loss[name][it], acc[name][it] = (s.net.blobs[b].data.copy() for b in blobs) if it % disp_interval == 0 or it + 1 == niter: loss_disp = '; '.join('%s: loss=%.3f, acc=%2d%%' % (n, loss[n][it], np.round(100*acc[n][it])) for n, _ in solvers) print('%3d) %s' % (it, loss_disp)) # 全てのネットからパラメータを保存 weight_dir = tempfile.mkdtemp() weights = {} for name, s in solvers: filename = 'weights.%s.caffemodel' % name weights[name] = os.path.join(weight_dir, filename) s.net.save(weights[name]) return loss, acc, weights
  171. 171. Solverの実行 niter = 200 weights = 'models/bvlc_reference_caffenet/bvlc_reference_caffenet.caffemodel' # Style用CaffeNetのソルバーを作成: 今回はfc8の学習と、その後のend-to-endの学習 solver_filename = solver(style_net(train=True)) solver = caffe.get_solver(solver_filename) solver.net.copy_from(weights) # ソルバーを実行 print('Running solvers for %d iterations...' % niter) loss, acc, weights = run_solvers(niter, [('pretrained', solver)]) print('Done.') del solver # ソルバーは終わったら消去 # End-to-endのソルバーを作成 solver_filename = solver(style_net(train=True, learn_all=True), base_lr=0.001) solver = caffe.get_solver(solver_filename) solver.net.copy_from(weights['pretrained']) # 新しいソルバーを実行 print('Running solvers for %d iterations...' % niter) _, _, finetuned_weights = run_solvers(niter, [('end-to-end', solver)]) print('Done.')
  172. 172. ファインチューニングによる 正答率の違い 初期化 fc8学習後 end-to-end学習後 Scratch 23.6% 39.2% Fine-tuned 50.0% 53.6% top 5 predicted style labels = (1) 55.67% Melancholy (2) 27.21% HDR (3) 16.46% Pastel (4) 0.63% Detailed (5) 0.03% Noir 2000枚のFlickr画像5カテゴリの場合
  173. 173. ファインチューニングのポイント • 学習済みのネットワークを初期値として新しい データからモデルを再学習 – 比較的小規模のデータにディープラーニングを用い る実用的な手法 – どれを使えばわからなければ、とりあえずCaffeNetを ファインチューニングするところから始める • 最終層のレイヤーの名前と出力の数を変更 • データレイヤーを変更 – データの準備はImageDataレイヤーを使うと簡単 • 学習は新しいレイヤーのみを最初に、その後End- to-endの学習
  174. 174. 5. 学習データの準備について • Caffeで入力データを 扱うレイヤーは複数 あり、一長一短 • PythonからのLMDB + Datum形式の扱い方 目的: データを扱う方法を知る data 学習データ CNN File? DB? HDF5? Python?
  175. 175. 入力レイヤーの比較 DataLayer ImageDataLayer HDF5DataLayer PythonLayer Datumを格納した LMDB/LevelDB データベース 画像ファイルとラ ベルを記述したテ キストファイル HDF5形式のファ イルとそれを列挙 したテキスト Pythonコードで入 力を記述 I/O性能が高い 複数入力でも使用 可能 準備が最も簡単 比較的速い ベクトルや複数入 力など複雑なデー タが使用可能 Pythonで扱える限 り何でもデータを 使用可能 入力データをあら かじめ変換する必 要 複数入力には複数 のDBが必要 画像カテゴリ分類 にしか使えない I/O性能は比較的 遅い 入力データをあら かじめHDF5形式 で用意する必要 内容によるが遅い 他にもDummyDataLayerやWindowDataLayer
  176. 176. LMDB+Datum形式について • Key-value形式のデータベース • CaffeはDatumというProtocol bufferを格納 message Datum { optional int32 channels = 1; optional int32 height = 2; optional int32 width = 3; // the actual image data, in bytes optional bytes data = 4; optional int32 label = 5; // Optionally, the datum could also hold float data. repeated float float_data = 6; // If true data contains an encoded image that need to be decoded optional bool encoded = 7 [default = false]; } key value 0001 datum 0002 datum 0003 datum ... ...
  177. 177. ImagaDataからLMDBへの変換 ImageData形式からはCaffe付属のC++ツールで変換 ./build/tools/convert_imageset [FLAGS] ROOTFOLDER/ LISTFILE DB_NAME ./build/tools/convert_imageset -backend lmdb -check_size true -encode_type jpg -encoded true -resize_height 256 -resize_width 256 -shuffle true 使用例 画像のリサイズやデータの シャッフルなどを指定可能
  178. 178. PythonからのDatumの作成 from caffe.proto.caffe_pb2 import Datum import numpy as np from PIL import Image def make_encoded_datum(image_path, label=0): '''JPGエンコードの画像と正解ラベルを格納したDatumを作成''' datum = Datum() datum.data = open(image_path, 'rb').read() datum.encoded = True datum.label = label return datum.SerializeToString() def make_plain_datum(image_path, label=0): '''圧縮なしの画像と正解ラベルを格納したDatumを作成''' image = np.asarray(Image.open(image_path)) datum = Datum() datum.data = image.tostring() # Python3はtobytes() datum.height = image.shape[0] datum.width = image.shape[1] datum.channels = image.shape[2] datum.label = label return datum.SerializeToString() 実際は前処理として画像のリサイズなどをする場合が多い。PILライブラリを使う
  179. 179. PythonからのLMDBの作成 import lmdb import itertools def grouper(iterable, n=100): '''Iteratorから大きさNのBatchを作る関数''' it = iter(iterable) while True: chunk = tuple(itertools.islice(it, n)) if not chunk: return yield chunk # 以下はLMDBをtrain.lmdbというパスに作成する例 # map_sizeはデータベースのサイズの上限で、大きな値にしておきます # dataset_reader()というimage_pathとlabelのIteratorを返す関数があると思ってください with lmdb.open('train.lmdb', map_size=(1<<40), create=True) as env: for batch in grouper(dataset_reader()): with env.begin(write=True) as txn: # Transaction毎にディスク書き込み for image_path, label in batch: serialized_datum = make_encoded_datum(image_path, label) txn.put(image_path, serialized_datum) # Keyは識別できれば何でも良い LMDBはTransactionをサポート 大きなデータの書き込みはバッチ単位で
  180. 180. 複雑な入力とDataLayer • 複数のDBファイルを用意 – Keyは順番が一致するよう に必ず対応するペアで同じ ものを使う – Datumのfloat_dataフィー ルドにベクトルデータを格 納することも可能 • Netに複数のレイヤーを 指定 • バグ回避のため複数GPU デバイス使用は避ける layer { name: "data" type: "Data" top: "data" data_param { source: "train_data.lmdb" batch_size: 64 backend: LMDB } } layer { name: "label" type: "Data" top: "label" data_param { source: "train_labels.lmdb" batch_size: 64 backend: LMDB } }
  181. 181. HDF5形式 • 数値データのためのファイル – NumpyやMatlab形式と同様のもの • Pythonからはh5pyライブラリ使用 HDF Group https://www.hdfgroup.org/ h5py http://www.h5py.org/ layer { name: "data" type: "HDF5Data" top: "image" top: "vector_data" top: "label" hdf5_data_param { source: "train.txt" batch_size: 32 } } train.txt batch1.h5 batch1.h5 batch1.h5 テキストファイル: ファイル名を列挙 HDF5ファイル群: 変数毎にN次元配列
  182. 182. 学習データ準備のポイント • データ形式はタスクに応じて望ましいも のを選択 – 単純な画像分類ならImageDataでOK – 性能を求めるならDB形式 – 複雑入力はHDF5 – PythonLayerは更に複雑なデータの扱いに • Pythonには各フォーマットのAPIがあるの で必要に応じて使う
  183. 183. CAFFEの高度な使い方
  184. 184. Caffeの高度な使い方 1. 新しいレイヤーの開発 2. SSDで物体検出 3. LSTMsでシーケンスモデル 4. FCNsによるピクセル単位の予測
  185. 185. 1. 新しいレイヤーの開発 • 用意されているレイヤーで 不十分な場合の選択肢 – PythonLayerでレイヤー実装 – C++/CUDAでレイヤー実装 • 例 – 入力データが特殊 – 損失関数が特殊 custom data conv1 pool1 conv2 pool2 fc1/relu1 custom loss
  186. 186. Python Layer layer { type: 'Python' name: 'loss' top: 'loss' bottom: 'ipx' bottom: 'ipy' python_param { # module名 (Pythonファイル名) # $PYTHONPATHに含まれている必要 module: 'pyloss' # PythonのClass名 layer: 'EuclideanLossLayer' } # 損失関数の場合はloss_weightを指定 loss_weight: 1 } • CaffeのC++コードからPythonを呼び出して処理を行うレイヤー • ビルド時にWITH_PYTHON_LAYER := 1としてコンパイル prototxt pyloss.py class EuclideanLossLayer(caffe.Layer): def setup(self, bottom, top): if len(bottom) != 2: raise Exception("...")
  187. 187. PythonLayerの例 import caffe import numpy as np class EuclideanLossLayer(caffe.Layer): """ Compute the Euclidean Loss in the same manner as the C++ EuclideanLossLayer to demonstrate the class interface for developing layers in Python. """ def setup(self, bottom, top): # check input pair if len(bottom) != 2: raise Exception("Need two inputs to compute distance.") def reshape(self, bottom, top): # check input dimensions match if bottom[0].count != bottom[1].count: raise Exception("Inputs must have the same dimension.") # difference is shape of inputs self.diff = np.zeros_like(bottom[0].data, dtype=np.float32) # loss output is scalar top[0].reshape(1) def forward(self, bottom, top): self.diff[...] = bottom[0].data - bottom[1].data top[0].data[...] = np.sum(self.diff**2) / bottom[0].num / 2. def backward(self, top, propagate_down, bottom): for i in range(2): if not propagate_down[i]: continue if i == 0: sign = 1 else: sign = -1 bottom[i].diff[...] = sign * self.diff / bottom[i].num レイヤーの初期化 ブロブのサイズ変更 Forward計算 Backward計算
  188. 188. C++によるレイヤー開発 include/caffe/layers/my_loss_layer.h # 宣言 src/caffe/layers/my_loss_layer.cpp # CPU実装 src/caffe/layers/my_loss_layer.cu # GPU実装 src/caffe/test/test_my_loss_layer.cpp # テスト実装 通常は以下の4種類のファイルを作成 場合によっては新しいProtocol bufferの定義も src/caffe/proto/caffe.proto # Protobuf定義 あとは通常と同様にビルド 実装内容はPythonLayerと同様setup, reshape, forward, backwardの計算 具体例は既存のレイヤーを参照すると良い
  189. 189. レイヤー開発の注意点 • まずは既存のコードを参考に • 最初に試すならPythonレイヤー • Caffeは自動で微分計算しない – 必ずbackward計算を手動で実装する必要 – 微分計算を導出してからコーディングしよう – テストは必ず実装して動作確認すること
  190. 190. 2. SSDで物体検出 https://github.com/weiliu89/caffe/tree/ssd W Liu et al, SSD: Single Shot MultiBox Detector, ECCV 2016 ECCV 2016で発表の高速、高精度な物体検出モデル
  191. 191. SSDの検出手法 物体領域の候補を密に推定し、 それが物体かどうか判定 物体領域の候補を荒く推定し、 物体かどうかと位置ずれを推定 Slide credit: Dragomir Anguelov
  192. 192. SSDの構造 Slide credit: Dragomir Anguelov CNNでFeature mapを計算したのち、Anchor毎に物体の確率と位置ズレを予測
  193. 193. SSDのCaffe実装入手とビルド https://github.com/weiliu89/caffe/tree/ssd git clone https://github.com/weiliu89/caffe.git cd caffe git checkout ssd # Modify Makefile.config according to your Caffe installation. cp Makefile.config.example Makefile.config make -j8 # Make sure to include $CAFFE_ROOT/python to your PYTHONPATH. make py make test -j8 # (Optional) make runtest -j8 ダウンロードはGithubから ビルドは一般的な手順で wget http://www.cs.unc.edu/~wliu/projects/SSD/models_VGGNet_VOC0712_SSD_300x300.tar.gz tar –xvf models_VGGNet_VOC0712_SSD_300x300.tar.gz 学習済みモデルの入手
  194. 194. SSDによる物体検出の準備 https://github.com/weiliu89/caffe/blob/ssd/examples/ssd_detect.ipynb model_def = 'models/VGGNet/VOC0712/SSD_300x300/deploy.prototxt' model_weights = 'models/VGGNet/VOC0712/SSD_300x300/VGG_VOC0712_SSD_300x300_iter_60000.caffemodel' net = caffe.Net(model_def, # defines the structure of the model model_weights, # contains the trained weights caffe.TEST) # use test mode (e.g., don't perform dropout) # input preprocessing: 'data' is the name of the input blob == net.inputs[0] transformer = caffe.io.Transformer({'data': net.blobs['data'].data.shape}) transformer.set_transpose('data', (2, 0, 1)) transformer.set_mean('data', np.array([104,117,123])) # mean pixel transformer.set_raw_scale('data', 255) # [0,255] range instead of [0,1] transformer.set_channel_swap('data', (2,1,0)) # BGR order instead of RGB モデルの読み込みや前処理は基本の分類と同様
  195. 195. SSDによる物体検出の実行 https://github.com/weiliu89/caffe/blob/ssd/examples/ssd_detect.ipynb transformed_image = transformer.preprocess('data', image) net.blobs['data'].data[...] = transformed_image # Forward pass. detections = net.forward()['detection_out'] # Parse the outputs. det_label = detections[0,0,:,1] det_conf = detections[0,0,:,2] det_xmin = detections[0,0,:,3] det_ymin = detections[0,0,:,4] det_xmax = detections[0,0,:,5] det_ymax = detections[0,0,:,6] # Get detections with confidence higher than 0.6. top_indices = [i for i, conf in enumerate(det_conf) if conf >= 0.6] top_conf = det_conf[top_indices] top_label_indices = det_label[top_indices].tolist() top_labels = get_labelname(labelmap, top_label_indices) top_xmin = det_xmin[top_indices] top_ymin = det_ymin[top_indices] top_xmax = det_xmax[top_indices] top_ymax = det_ymax[top_indices] Forward計算も 通常どおり 結果はBBOXごとに ラベル、尤度、座標 として出てくる 結果から尤度の高い 検出のみを抜き出し
  196. 196. SSDによる物体検出の結果表示 https://github.com/weiliu89/caffe/blob/ssd/examples/ssd_detect.ipynb colors = plt.cm.hsv(np.linspace(0, 1, 21)).tolist() plt.imshow(image) currentAxis = plt.gca() for i in xrange(top_conf.shape[0]): xmin = int(round(top_xmin[i] * image.shape[1])) ymin = int(round(top_ymin[i] * image.shape[0])) xmax = int(round(top_xmax[i] * image.shape[1])) ymax = int(round(top_ymax[i] * image.shape[0])) score = top_conf[i] label = int(top_label_indices[i]) label_name = top_labels[i] display_txt = '%s: %.2f'%(label_name, score) coords = (xmin, ymin), xmax-xmin+1, ymax-ymin+1 color = colors[label] currentAxis.add_patch(plt.Rectangle(*coords, fill=False, edgecolor=color, linewidth=2)) currentAxis.text(xmin, ymin, display_txt, bbox={'facecolor':color, 'alpha':0.5}) 結果をMatplotlibで表示
  197. 197. SSDについて • 高速、高精度な物体検出器 • 学習データの準備手順などはGithubのド キュメント参照 https://github.com/weiliu89/caffe/tree/ssd
  198. 198. 3. LSTMでシーケンス学習 • 時系列などのシーケンスのための再帰的 ネットワーク LST M xt yt htht-1 データ列: x = <x1, x2, x3, ..., xT> 列の長さ: T LSTMの内部パラメータ: W LSTMの状態遷移: h0 := 0 for t = 1, 2, 3, ..., T: <yt, ht> = LSTMW(xt, ht-1) slide credit: Jeff Donahue
  199. 199. シーケンスの学習 • 系列を展開したネットワークと等価 LST M x1 z1 h10 LST M x2 z2 h2 LST M xT zT hThT-1... slide credit: Jeff Donahue
  200. 200. シーケンスの例: 画像の言語記述 mage Description CNN LSTM LSTM LSTM LSTM LSTM tial ut a<BOS> man is talking <EOS> slide credit: Jeff Donahue
  201. 201. LSTM Layer layer { name: "lstm1" type: "Lstm" bottom: "data" bottom: "cont" top: "lstm1" recurrent_param { num_output: 15 weight_filler { type: "gaussian" std: 0.1 } bias_filler { type: "constant" } } } • 入力は data と cont の2つ • 出力は1つ • 内部のパラメータblobは3つ • データblobはメモリと出力の2つ
  202. 202. 入力データのフォーマット • 1つ目は単語ベクトルなどの入力データの表現 (T x N x D) • 2つ目は "cont" (continuation) データで、シーケンスの継続を示す (T x N) • Caffeの制限: バッチ毎にシーケンスは途切れる slide credit: Jeff Donahue
  203. 203. 単語列の扱いSequence Input Format • Words are usually represented as one-hot vectors 0 0 0 . . 0 1 0 . . 0 “bee” 8000D vector; all 0s except index of wordvocabulary with 8000 words a the am . . cat bee dog . . run 0 1 2 . . 3999 4000 4001 . . 7999 • 単語は通常one-hotベクトルで表現  次元が大きくなる問題 slide credit: Jeff Donahue
  204. 204. 単語列の扱いSequence Input Format • EmbedLayer projects one-hot vector 0 0 0 . . 0 1 0 . . 0 “bee” 8000D vector; all 0s except index of word 4000 (index of the word “bee” in our vocabulary) layer { type: “Embed” embed_param { input_dim: 8000 num_output: 500 } } 500D embedding of “bee” 2.7e-2 1.5e-1 3.1e+2 … • EmbedLayerを使ってone-hotベクトルを射影して表現 slide credit: Jeff Donahue
  205. 205. シーケンスの学習について更に • 画像記述の具体例は以下のforkを参照 – examples/ 以下にLRCN [Donahue 2014]のMS COCOモデルの実装 https://github.com/jeffdonahue/caffe/tree/recurrent-rebase-cleanup
  206. 206. 4. FCN • CNNによるセグメンテーションモデル https://github.com/shelhamer/fcn.berkeleyvision.org J Long et al, Fully Convolutional Networks for Semantic Segmentation, CVPR 2015
  207. 207. ピクセル単位の予測に適用可能 pixels in, pixels out monocular depth estimation (Liu et al. 2015) boundary prediction (Xie & Tu 2015) semantic segmentation slide credit: Jon Long
  208. 208. モデルの入手 https://github.com/shelhamer/fcn.berkeleyvision.org git clone https://github.com/shelhamer/fcn.berkeleyvision.org.git ダウンロードはGithubから あらかじめCaffeをビルドしておくこと 学習済みのモデルはリポジトリに含まれるcaffemodel-urlファイルから
  209. 209. 学習済みモデルでセグメンテーション import numpy as np from PIL import Image import caffe # load image, switch to BGR, subtract mean, and make dims C x H x W for Caffe im = Image.open('pascal/VOC2010/JPEGImages/2007_000129.jpg') in_ = np.array(im, dtype=np.float32) in_ = in_[:,:,::-1] in_ -= np.array((104.00698793, 116.66876762, 122.67891434)) in_ = in_.transpose((2, 0, 1)) # load net net = caffe.Net('voc-fcn8s/deploy.prototxt', 'voc-fcn8s/fcn8s-heavy-pascal.caffemodel', caffe.TEST) # shape for input (data blob is N x C x H x W), set data net.blobs['data'].reshape(1, *in_.shape) net.blobs['data'].data[...] = in_ # run net and take argmax for prediction net.forward() out = net.blobs['score'].data[0].argmax(axis=0) 前処理は手動で 結果は確率値のマップ
  210. 210. FCNの学習 • 中身はCNN+Upsampling – VGG16などのカテゴリ分類用モデルを初期値 としてファインチューニングする skip layers skip to fuse layers! interp + sum interp + sum dense output Skip layers form end-to-end, joint learning of semantics and location slide credit: Jon Long
  211. 211. FCNの学習 import caffe import surgery, score # from FCN github import numpy as np import os import setproctitle setproctitle.setproctitle(os.path.basename(os.getcwd())) weights = '../ilsvrc-nets/vgg16-fcn.caffemodel' # init caffe.set_device(int(sys.argv[1])) caffe.set_mode_gpu() solver = caffe.SGDSolver('solver.prototxt') solver.net.copy_from(weights) # surgeries interp_layers = [k for k in solver.net.params.keys() if 'up' in k] surgery.interp(solver.net, interp_layers) # scoring val = np.loadtxt('../data/pascal/VOC2010/ImageSets/Main/val.txt', dtype=str) for _ in range(50): solver.step(8000) score.seg_tests(solver, False, val, layer='score') VGG16などから ファインチューニング必須 upsamplingなどモデル内の パラメータを学習前に 手動設定必要 pascalcontext-fcn32s/solve.py
  212. 212. FCNの学習の注意点 • 大規模なデータセットの構築は困難 – 全てのピクセルにラベルのついたデータを準 備する必要あり • 学習について • 初期値として学習済みのネットワークを使用しない とほぼ確実に局所解 • 32sから学習し、16s、8sと細かいものを次々ファイ ンチューニングする • 詳細はGithubドキュメント https://github.com/shelhamer/fcn.berkeleyvision.org
  213. 213. Caffeで始めるディープラーニング ディープラーニングと画像認識の理解 • ニューラルネット、CNN Caffeフレームワークの基本を理解 • ビルド方法、NetやBlob PythonによるCaffeの使い方を学習 • データ形式、学習、テスト
  214. 214. 参考資料 Caffe documentation http://caffe.berkeleyvision.org/ Caffeのソースコードに豊富な例題が含まれている https://github.com/BVLC/caffe Dockerを使ったDeep learning実習 https://kyamagu.github.io/dl-workshop-2016/

×