2017/08/18

deeplearn.jsを遠くからそっと眺めてみた

Googleから発表された機械学習のためのJavaScriptライブラリのdeeplearn.jsを眺めてみたので、纏めておきます。
オフィシャルを舐めて、どういうものか、これからどんな感じになっていくか(いってほしいか)をダラっとタレます。


注意
眺めたのはv0.1.0なので、内容が今後大幅に変更になる可能性があります(というかある)。


触った環境
MacBook Pro (Retina, 15-inch, Mid 2015)
    - プロセッサ: 2.5GHz Intel Core i7
    - メモリ: 16GB 1600 MHz DDR3
    - グラフィックス: AMD Radeon R9 M370X 2048 MB
Chrome 60.0.3112.101 (Official Build) (64ビット)
deeplearn.js v0.1.0


Exampleを眺める

何ができるかを眺めるためのExamplesが準備されています。
Model Builderが面白い(というか分かりやすい)のでおすすめです。
デフォルトでみんなだいすきMNISTをCNNで推論(Inference)するプログラムが走っています。

CPUとGPUの性能差を噛みしめる


そのまま(デフォルト値)だと、正答率が低いので、左側の[TRAIN]ボタンをクリックして学習を開始します。
すると20〜40秒ほどで、学習が収束した感じになる。早い!。左から2つ目のカラムの上にCPUとGPUを切り替えるトグルボタンがあるので、それでCPUに切り替えて実行してみると、途中で「もういいや」ってなるぐらい遅いのでGPUのありがたみを噛みしめられますん。
CPUと言ってもJSインタプリタが間に入っているのでCPUはより遅いのですが、TensorFlowのCPUとくらべてもdeeplearn.jsのGPUは速いんじゃないかなぁという体感値です(正確には計測していませんが…)

モデル定義ファイル

左カラムの「Download model」というリンクがワクワクするわけですが、次のようなJSONファイルでした。
[
    {"layerName":"Convolution","fieldSize":5,"stride":1,"zeroPad":2,"outputDepth":8},
    {"layerName":"ReLU"},{"layerName":"Max pool","fieldSize":2,"stride":2,"zeroPad":0},
    {"layerName":"Convolution","fieldSize":5,"stride":1,"zeroPad":2,"outputDepth":16},
    {"layerName":"ReLU"},
    {"layerName":"Max pool","fieldSize":2,"stride":2,"zeroPad":0},
    {"layerName":"Flatten"},
    {"layerName":"Fully connected","hiddenUnits":10}
]

わー、よみやすーい。
どうやらModel Builderのモデルを定義するためだけのファイルっぽいです。学習済みのパラメータなどは含まれないので、学習結果を保存することは今のところできなさそう。
でも必須機能だからすぐ対応するでしょうけど(対応してください)。

性能ベンチマーク

他のExamplesにBenchmarksというのがあり、CPUとGPUの性能比較とかができます。
例えばMatrix Multiplication(matmul)の性能比較とか面白いです。

我が軍(GPU)は圧倒的ではないか!
てかGPUの得意分野なので当たり前だし、CPUはJSインタプリタ挟んでるので不利だし…。といってもこの性能差を見せられるとグッときます個人的に。


TensorFlowのモデル使いたい

TensorFlowで学習したモデル(チェックポイント)を読み込んで、推論だけブラウザで実行というチュートリアルがありますので、既存モデルをdeeplearn.jsで実行することができます。
一部抜粋です。

import {CheckpointLoader, Graph} from 'deeplearnjs';
// manifest.json is in the same dir as index.html.
const reader = new CheckpointReader('.');
reader.getAllVariables().then(vars => {
  // Write your model here.
  const g = new Graph();
  const input = g.placeholder('input', [784]);
  const hidden1W = g.constant(vars['hidden1/weights']);
  const hidden1B = g.constant(vars['hidden1/biases']);
  const hidden1 = g.relu(g.add(g.matmul(input, hidden1W), hidden1B));
  ...
  ...
  const math = new NDArrayMathGPU();
  const sess = new Session(g, math);
  math.scope(() => {
    const result = sess.eval(...);
    console.log(result.getValues());
  });
});
わー、てがきーぃ。
Caffe to TensorFlowみたいに、モデル作成プログラムを自動生成するツールの登場が待たれる…。

なんとなく分かってきたので書いてみる

何ができそうかが見えてきたので、じゃぁどうやって書くのか。
それもチュートリアルがありますが、先程のTensorFlowのモデルをロードするプログラムが全てです。
Graphオブジェクトにplaceholderやらconstant、variable、reluなどで計算グラフを構築し、Sessionのevalでグラフを実行するという感じっぽいです。TensorFlowのLow Level APIに慣れている人なら読みやすいですが、同時にhigh-level APIはいねぇ〜がぁ〜という気持ちが湧き出てきます。

Model Builderがあるので、high-level APIも遠くない未来にお目見えするんじゃないかなぁとか思ったり、、、というかKeras Model的なモジュールがあれば、WebフロントでのDeep Learning活用が捗りそう。

NumPyっぽくも使える

deeplearn.jsはTensorFlowみたくグラフを定義して実行するだけでなく、NumPyみたく逐次実行APIも提供しているのが面白い。gpu.jsよりもテンソル計算に関しては使いやすそうな印象。

const shape = [2, 3];  // 2 rows, 3 columns
const a = Array2D.new(shape, [1.0, 2.0, 3.0, 10.0, 20.0, 30.0]);

ロードマップ

最後ですが、v0.1.0と産まれたてホヤホヤなdeeplearn.jsなので、現状を評価してもあまり意味はなく、Production Readyに向けてどういう方向で進んでいくのかが大事なわけで、そこんところがロードマップに纏められていました。
テキトーに纏めると、こんな感じです↓(翻訳間違ってたらゴメンナサイ)

More devices: WebGL 1.0と2.0をターゲットにしてるけどモバイルやら他のブラウザへ展開していきたいなぁ

Optimizers: SGDしかサポートしとらんけん、RMSPropやらAdamとかAdamaxとか色々サポートせにゃならん

Logical sampling: deeplearn.jsで扱う高次元配列は2Dのテクスチャ空間にマッピングしてから計算されるのでシェーダープログラム(GPU演算させる部分)が書きづらい。論理空間が定義できたら、書きやすくなるよねー。mutmulだけ論理空間でテスト実装してるけど、これを展開していけるようにしないとね…。

Batch as the outer dimension: いまbatch sizeが1しかサポートしてないけど、Logical samplingの機構ができたら何とかできるはず

Automatic TensorFlow to deeplearn.js: TFのGraphDefから自動的にdeeplearn.jsのモデルにポーティングできるようにする計画アルよ。

Dynamic batching: 計算グラフの計算処理を一括化するような計算最適化をNDArrayMathのレイヤーで実装します。←TensorFlow Foldみたいなことをやるっぽい?

Recurrence in training: 再帰をサポートしていきます。←RNNとかLSTMのことかな?


おわりに

deeplearn.jsはDefined and Run型の機械学習ライブラリで、かつ、Googleから出されているものなんでTensorFlow知ってる人にはすぐ馴染めるライブラリになりそうな印象でした。
というかAPIとか読まずに理解できますw

v0.1.0ですので、これから変化するでしょうし、このエントリ自体がオフィシャルをサラッと舐めただけの内容なので薄〜い!
このエントリを信用せずw ちゃんとオフィシャルを読んだり、手元で実装してみた方が良いと思います。
Typescriptがオススメと書いているので、実装しやすいですし、npmとかでサクッとはじめられます。


2017/07/13

anguler-cliでscssビルドエラーを解決する

angular-cliでアプリを作る

% ng new my-app --style=scss

起動する
% cd my-app 
% ng serve --open

ビルドに失敗する。
% ng serve --open
You are running version 6.5.0 of Node, which will not be supported in future
versions of the CLI. The official Node version that will be supported is 6.9 and greater.

To disable this warning use "ng set --global warnings.nodeDeprecation=false".** NG Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200 **
 10% building modules 8/9 modules 1 active ...ode_modules/style-loader/addStyles.jswebpack: wait until bundle finished: /
Hash: 736257371267d73edf59                                                              
Time: 7329ms
chunk    {0} polyfills.bundle.js, polyfills.bundle.js.map (polyfills) 171 kB {4} [initial] [rendered]
chunk    {1} main.bundle.js, main.bundle.js.map (main) 6.45 kB {3} [initial] [rendered]
chunk    {2} styles.bundle.js, styles.bundle.js.map (styles) 9.59 kB {4} [initial] [rendered]
chunk    {3} vendor.bundle.js, vendor.bundle.js.map (vendor) 2.18 MB [initial] [rendered]
chunk    {4} inline.bundle.js, inline.bundle.js.map (inline) 0 bytes [entry] [rendered]

ERROR in ./~/css-loader?{"sourceMap":false,"importLoaders":1}!./~/postcss-loader?{"ident":"postcss"}!./~/sass-loader/lib/loader.js?{"sourceMap":false,"precision":8,"includePaths":[]}!./src/styles.scss
Module build failed: Error: ENOENT: no such file or directory, scandir '/Users/adam/development/angular/tutorial/my-app/node_modules/node-sass/vendor'
    at Error (native)
    at Object.fs.readdirSync (fs.js:951:18)
    at Object.getInstalledBinaries (/Users/adam/development/angular/tutorial/my-app/node_modules/node-sass/lib/extensions.js:124:13)
    at foundBinariesList (/Users/adam/development/angular/tutorial/my-app/node_modules/node-sass/lib/errors.js:20:15)
    at foundBinaries (/Users/adam/development/angular/tutorial/my-app/node_modules/node-sass/lib/errors.js:15:5)
    at Object.module.exports.missingBinary (/Users/adam/development/angular/tutorial/my-app/node_modules/node-sass/lib/errors.js:45:5)
    at module.exports (/Users/adam/development/angular/tutorial/my-app/node_modules/node-sass/lib/binding.js:15:30)
    at Object. (/Users/adam/development/angular/tutorial/my-app/node_modules/node-sass/lib/index.js:14:35)
    at Module._compile (module.js:556:32)
    at Object.Module._extensions..js (module.js:565:10)
    at Module.load (module.js:473:32)
    at tryModuleLoad (module.js:432:12)
    at Function.Module._load (module.js:424:3)
    at Module.require (module.js:483:17)
    at require (internal/module.js:20:19)
    at Object. (/Users/adam/development/angular/tutorial/my-app/node_modules/sass-loader/lib/loader.js:3:14)
 @ ./src/styles.scss 4:14-189
 @ multi ./src/styles.scss

ERROR in ./src/app/app.component.scss
Module build failed: Error: ENOENT: no such file or directory, scandir '/Users/adam/development/angular/tutorial/my-app/node_modules/node-sass/vendor'
    at Error (native)
    at Object.fs.readdirSync (fs.js:951:18)
    at Object.getInstalledBinaries (/Users/adam/development/angular/tutorial/my-app/node_modules/node-sass/lib/extensions.js:124:13)
    at foundBinariesList (/Users/adam/development/angular/tutorial/my-app/node_modules/node-sass/lib/errors.js:20:15)
    at foundBinaries (/Users/adam/development/angular/tutorial/my-app/node_modules/node-sass/lib/errors.js:15:5)
    at Object.module.exports.missingBinary (/Users/adam/development/angular/tutorial/my-app/node_modules/node-sass/lib/errors.js:45:5)
    at module.exports (/Users/adam/development/angular/tutorial/my-app/node_modules/node-sass/lib/binding.js:15:30)
    at Object. (/Users/adam/development/angular/tutorial/my-app/node_modules/node-sass/lib/index.js:14:35)
    at Module._compile (module.js:556:32)
    at Object.Module._extensions..js (module.js:565:10)
    at Module.load (module.js:473:32)
    at tryModuleLoad (module.js:432:12)
    at Function.Module._load (module.js:424:3)
    at Module.require (module.js:483:17)
    at require (internal/module.js:20:19)
    at Object. (/Users/adam/development/angular/tutorial/my-app/node_modules/sass-loader/lib/loader.js:3:14)
 @ ./src/app/app.component.ts 18:17-48
 @ ./src/app/app.module.ts
 @ ./src/main.ts
 @ multi webpack-dev-server/client?http://localhost:4200 ./src/main.ts
webpack: Failed to compile.

node-sassをインストールする
% npm install node-sass --save-dev

もう一度、起動する。
% ng serve --open
You are running version 6.5.0 of Node, which will not be supported in future
versions of the CLI. The official Node version that will be supported is 6.9 and greater.

To disable this warning use "ng set --global warnings.nodeDeprecation=false".** NG Live Development Server is listening on localhost:4200, open your browser on http://localhost:4200 **
 10% building modules 6/9 modules 3 active ...ode_modules/style-loader/addStyles.jswebpack: wait until bundle finished: /
Hash: 3a75be2f4a4fec742f94                                                              
Time: 7565ms
chunk    {0} polyfills.bundle.js, polyfills.bundle.js.map (polyfills) 171 kB {4} [initial] [rendered]
chunk    {1} main.bundle.js, main.bundle.js.map (main) 5.28 kB {3} [initial] [rendered]
chunk    {2} styles.bundle.js, styles.bundle.js.map (styles) 10.7 kB {4} [initial] [rendered]
chunk    {3} vendor.bundle.js, vendor.bundle.js.map (vendor) 2.18 MB [initial] [rendered]
chunk    {4} inline.bundle.js, inline.bundle.js.map (inline) 0 bytes [entry] [rendered]
webpack: Compiled successfully.

起動した!

2017/03/22

TensorFlowでXLAを使えるようにする

このブログ記事は2017年3月22日に開催されたRecap of TensorFlow DEV SUMMIT 2017の私が担当したXLAに関する発表の補足資料です。
XLAの概要を知りたい方は発表資料を御覧ください。


TensorFlow1.0にXLAというコンパイルの仕組みが実験的に導入されましたので、それを試す方法を記します。
XLAの概要や狙いなどはGoogle Developers Japanの記事が参考になります。

XLAはJITとAOTの2つのコンパイラから使われます。
御存知の通り、JITはランタイム上で実行時にコンパイルされ、AOTはランタイムを含まない静的な実行バイナリ(Cライブラリ)を作るコンパイラです。

ここでは、JITとAOTを試しに使ってみる方法をご紹介します。
まずは「A: TensorFlowのビルド」を説明し、「B: JITコンパイルを試す」でJITコンパイルする方法について説明し、「C: tfcompileを試す」でAOTコンパイラのビルドと使い方について説明します。

A: TensorFlowのビルド

プロダクションではXLAがどうやらONになっていないので、自分でXLAがONのTensorFlowをビルドします。

(※基本的にオフィシャルドキュメントに従えばビルドできるはず…)

0. 環境

GCEのGPUインスタンス(Ubuntu16.10 + vCPU:4 + RAM:15GB + Tesla K80)

1. ビルドツール「bazel」のインストール

オフィシャルサイトの手順に従えば躓くこと無くインストールできます。
私はrecommendedになっている「Using Bazel custom APT repository 」にしたがってインストールしました。

2. TensorFlowの依存ライブラリをインストール

$ sudo apt-get install python-numpy python-dev python-pip python-wheel

3. NVIDIA系ライブラリのインストール

以下の3つをインストールします。インストール方法はコチラを参考にどうぞ。
CUDA Toolkitとそれに準拠するドライバのインストール
ディープラーニング用ライブラリcuDNNのインストール
libcupi-devのインストール

4. TFをビルド

まずソースコードをcloneしてきます。
$ git clone https://github.com/tensorflow/tensorflow

gccのバージョンを4にします。
以下のコマンドでgccのバージョンを確認します。
$ gcc -v
gcc version 6.2.0 20161005

nvcc(Nvidia CUDA Compiler)がgcc4までしか対応していないのでgcc-4.9をインストールします。
$ sudo add-apt-repository ppa:ubuntu-toolchain-r/test
$ sudo apt-get update
$ sudo apt-get install gcc-4.9

コンフィグを設定します。
$ ./configure
以下の設定項目だけ注意して設定して下さい。
・gccのパスは先にインストールしたgcc-4.9を指定
・CUDA supportをON
・XLA just-in-time compilerをON

コンフィグが整ったらビルドします。
$ bazel build --config=opt --config=cuda //tensorflow/tools/pip_package:build_pip_package

凄く時間がかかります。私の環境では2時間ぐらい掛かりました。
ちなみに、TF自体をCPU環境に特殊化し高速化したい場合、このStack Overflowのようにビルドコマンドにオプションを追加するとSSEやAVX、FMAが利用できます。
ビルドが完了したらwheelパッケージを作ります。

$ bazel-bin/tensorflow/tools/pip_package/build_pip_package /tmp/tensorflow_pkg

これで/tmp/tensorflow_pkg配下に「tensorflow-1.0.1-cp27-cp27mu-linux_x86_64.whl」というwheelファイルが作られます。

あとは、これをpip installするだけですが、実験要素を含んだ不安定なTensorFlowなので、virtualenvの中にインストールしておいたほうが良いと思います。

$ virtualenv --system-site-packages .venv
$ source .venv/bin/activate
$ pip install /tmp/tensorflow_pkg/tensorflow-1.0.1-cp27-cp27mu-linux_x86_64.whl

これでXLAが使えるTensorflowのビルドとインストールが完了しました。
では、実際にJITコンパイルしてみます。

B: JITコンパイルを試す

サンプルコードがあるので、そちらを使います。
JITの使用方法をオフィシャルサイトで確認しておくと、理解しやすいと思います。
$ cd tensorflow/tensorflow/examples/tutorials/mnist/
$ python mnist_softmax_xla.py --data_dir=./data

実行が完了するとtimeline.ctf.jsonというファイルが生成されますので、それをChrome Trace Event Profileで表示できます。
Chromeを開いてURLに「chrome://tracing」と入力するとファイルを読み込めるので、そこからtimeline.ctf.jsonを読み込みます。

残念ながら、現バージョン(1.0.1)のソースコードで私がビルドしたTensorFlowでは、XLAのJITコンパイルが動いていないようでした…(対象のソースコードを色々とイジって試してみたのですが、どれもJITコンパイルされませんでした)
うまくJITコンパイルされると、
これ↓が

こう↓なるみたいです。

_XlaLaunchというOperationがJITコンパイルされたOperationということらしいです。

※ちなみに、1.0.1で公開されているサンプルプログラムのmnist_softmax_xla.pyでは--xlaオプションが効かない(単純にこのプログラムのバグ)ため、XLAをON/OFFしたい場合は、ソースコードを修正する必要があります

B: tfcompileを試す

tfcompileはXLAの仕組みを使ってTensorFlowのコードを実行可能なライブラリにコンパイルしてくれるツールです。
何が凄いって、TensorFlowのランタイムを必要とせずTensorFlowのグラフを実行できるので、実行ファイルのサイズが小さくなったり、レイテンシを低下させたりと、推論フェーズ(実際にMachine Learningをプロダクションで利用するフェーズ)に対して強力なツールとなります。
推論エンジンとして最適化するとGPUがなくてもレイテンシを上げることができるので、サーバではCPUインスタンスで運用できたり(GPUインスタンス高いしドライバとかアップデートの問題とか運用に手間が必要…)、モバイルでの利用も見えていきます。
XLAの本命はこれだと個人的に考えています。

1. tfcompileをビルドする

TFをビルドするときと同じようにbazelを使います。
$ bazel build --config=opt --config=cuda //tensorflow/compiler/aot:tfcompile

これだけでtfcompileが完成します。
実際tfcompileはbazelのマクロとして利用します。

2. サンプルで動作を確認

動作概要をオフィシャルサイトで確認しておくと理解しやすいかと思います。
まずグラフデータが必要になりますので、グラフデータを作ります。
$ cd tensorflow/compiler/aot/tests
$ python make_test_graphs.py

これはいくつかのパターンのグラフデータを作成するプログラムです。
実行するとtest_graph_tfmatmul.pbというファイルが作成されます。
これがグラフデータです。
これにconfigファイルを足すと、実行可能なバイナリとヘッダーファイル(すなわちライブラリ)が生成されます。

以下のコマンドがそれを実行してくれます。
$ bazel build :test_graph_tfmatmul

対応するBUILDの設定はこのようになっています。
tf_library(
    name = "test_graph_tfmatmul",
    testonly = 1,
    config = "test_graph_tfmatmul.config.pbtxt",
    cpp_class = "foo::bar::MatMulComp",
    graph = "test_graph_tfmatmul.pb",
    tags = ["manual"],
)

configでConfigurationファイル”test_graph_tfmatmul.config.pbtxt”を指定しています。
cpp_classでC++のクラス名(名前空間付き)を指定します。
graphは先ほどのグラフファイルを指定します。
Configファイルの中身は以下のようになっています。
feed {
  id { node_name: "x_hold" }
  shape {
    dim { size: 2 }
    dim { size: 3 }
  }
}
feed {
  id { node_name: "y_hold" }
  shape {
    dim { size: 3 }
    dim { size: 2 }
  }
}
fetch {
  id { node_name: "x_y_prod" }
}

feedがグラフの入力ノードで受け取る多次元配列の形を規定します。ノードが複数あれば、その数分feedを規定します。
fetchは出力ノードです。こちらも同じく、出力ノードの数だけfetchを規定します。

これをビルドすると、ビルド結果が//bazel-genfiles/tensorflow/compiler/aot/testsの中にtest_graph_tfmatmu.{h|o}が生成されます。この場所はbaselのライブラリ/includeパスに含まれているので、生成されたファイルを移動させる必要はありません。

ライブラリが完成しました。
これを利用するプログラムを書きます。こちらもbazelのビルドツールを利用すると以下のようになり簡単です。

cc_binary(
    name = "my_binary",
    testonly = 1,
    srcs = [
        "my_code.cc",
    ],
    deps = [
        ":test_graph_tfmatmul",
        "//third_party/eigen3",
    ],
    linkopts = [
          "-lpthread",
    ]
)

nameは生成される実行ファイルの名前を指定します。
depsは依存するライブラリです。「:(コロン)」始まりはbazelのタスク名です。
srcsは実際のソースコードです。先ほど作成したヘッダーファイルをincludeして利用するプログラムです。

my_code.ccの中身は以下のとおりです。
#define EIGEN_USE_THREADS
#define EIGEN_USE_CUSTOM_THREAD_POOL

#include 
#include "third_party/eigen3/unsupported/Eigen/CXX11/Tensor"
#include "tensorflow/compiler/aot/tests/test_graph_tfmatmul.h" // generated

int main(int argc, char** argv) {
  Eigen::ThreadPool tp(2);  // Size the thread pool as appropriate.
  Eigen::ThreadPoolDevice device(&tp, tp.NumThreads());

  foo::bar::MatMulComp matmul;
  matmul.set_thread_pool(&device);

  // Set up args and run the computation.
  const float args[12] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
  std::copy(args + 0, args + 6, matmul.arg0_data());
  std::copy(args + 6, args + 12, matmul.arg1_data());
  matmul.Run();

  // Check result
  if (matmul.result0(0, 0) == 58) {
    std::cout << "Success" << std::endl;
  } else {
    std::cout << "Failed. Expected value 58 at 0,0. Got:"
              << matmul.result0(0, 0) << std::endl;
  }

  return 0;
}

ライブラリのヘッダーファイルをincludeして、そのクラス名のfoo::bar::MatMulCompを使ったプログラムです。
グラフの出力ノードの値が正しければ(58なら)、「Success」と標準出力するプログラムです。

これをビルドします。
$ bazel build :my_binary

ビルドされた実行ファイル(my_binary)は下記に配置されます。
//bazel-bin/tensorflow/compiler/aot/tests/my_binary

実行するとたしかに「Success」という文字を得ることが出来ます。

C: おわりに

TensorFlow1.0からexperimental機能として実装されたXLAについて、JIT/AOTの両方で使う方法と実際に使う手順を説明しました。
まだうまく動かなかったり、思ったような性能を得られなかったりするので、あくまで実験機能として使うと良いと思います。

JITは学習フェーズの"自動"高速化、AOTは推論フェーズの低レイテンシ・省メモリ化など、Machine Learningの開発から運用までがトータルにサポートされる期待の機能ですので、今後も動向をチェックしていきたいと思います。

2017/03/14

GCEのGPUインスタンスでTensorFlowを動かす方法

Google Compute Engine(GCE)にGPUインスタンス(インスタンスにGPUを追加できる)が利用できるようになったので、TensorFlow(TF)を動かした。 

1. GPUインスタンスを作る

オフィシャルドキュメントに沿って作るもよし、Cloud Consoleから作るもよし。

GPUが使えるゾーンには現時点(2017/03/14)では制限があるので気を付ける。
OSの選択は気を付ける。GPU(NVIDIAのCUDA)がサポートしているOSにする。
また、各OS毎にCUDAツールキットのインストール方法が異なるので、気を付ける。
私はUbuntu 16.10のOSを選択。

1.1 CUDA Toolkit 8.0をインストールする

インスタンスが生成されたらSSHとかでログインして下記のスクリプトを実行する。
これでCUDA Toolkit 8.0がインストールされる。
#!/bin/bash
echo "Checking for CUDA and installing."
# Check for CUDA and try to install.
if ! dpkg-query -W cuda; then
  # The 16.04 installer works with 16.10.
  curl -O http://developer.download.nvidia.com/compute/cuda/repos/ubuntu1604/x86_64/cuda-repo-ubuntu1604_8.0.61-1_amd64.deb
  dpkg -i ./cuda-repo-ubuntu1604_8.0.61-1_amd64.deb
  apt-get update
  apt-get install cuda -y
fi

他(バージョンも含めて)のOSの場合、インストールスクリプトが異なるので、オフィシャルドキュメントを参考にして下さい。

2. GPUの動作確認

nvidia-smiを使ってGPUが認識しているかを確認する。
$ nvidia-smi
Tue Mar 14 01:56:30 2017       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 375.26                 Driver Version: 375.26                    |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  Tesla K80           Off  | 0000:00:04.0     Off |                    0 |
| N/A   35C    P8    27W / 149W |     13MiB / 11439MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|    0      2145    G   /usr/lib/xorg/Xorg                              13MiB |
+-----------------------------------------------------------------------------+

コノような内容がコンソールに表示されたらGPU(0番デバイスにTesla K80)が認識されている。

3. cuDNNを追加

すでにドライバとCUDA Toolkitがインストールされているので次に、TensorFlowを動くようにする。
cuDNNはNVIDIAから取得できるが、アカウント登録が必要になるため、コチラから登録する。連絡がくるまで1日要することもあるので、お早めに。
アカウントがあれば先のURLのDownloadボタンから、必要なcudnnライブラリをダウンロードする。
今回の例だと、CUDA 8.0とLinuxの条件に当てはまる「cuDNN v5.1 Library for Linux」が適切。

.tgz形式のファイルがダウンロードできるので、それを展開して、適切な場所にコピーしてインストール完了。
$ tar zxvf cudnn-8.0-linux-x64-v5.1.tgz
$ sudo cp ./cuda/include/cudnn.h /usr/local/cuda/include
$ sudo cp ./cuda/lib64/libcudnn* /usr/local/cuda/lib64 
sudo chmod a+r /usr/local/cuda/lib64/libcudnn*

4. CUDA Compute Capabilityをインストール

$ sudo apt-get install libcupti-dev


5. TFをインストール

TFのインストールも簡単になったものです。下記コマンド1つでインストールできます。
Python2.7とPython3系ではインストール方法が異なるので気をつけて下さい。
TFはまだバージョン更新が激しいのでvirtualenvを作っておくと幸せになれると思います。
$ mkdir tf && cd tf
$ virtualenv --system-site-packages .venv
$ source .venv/bin/activate
(.venv)$ pip install --upgrade tensorflow-gpu

もし、CPUバージョンのTFをインストールしたいならtensorflow-gpuをtensorflowにして下さい。
Python3系を利用している場合はpipをpip3にして下さい。
これでインストールが完了しました。

5.1 動作確認

$ python
>>> import tensorflow as tf
>>> sess = tf.Session()

これでErrorが表示されなければ(Warningは表示されます)正常動作しています。

2017/02/08

GAE/pyのプロダクションのDatastoreをdev_appserverにコピーする方法

ある程度運用しているGAEのDatastoreをまんまローカルにコピーして、dev_appserverで動かす方法。

0. remote_apiの設定

何はともあれremote_apiを使えるようにする必要がある。
app.yamlに下記を追加
builtins:
- remote_api: on

とりあえずデプロイ。デプロイしたアプリのバージョンをdefaultにする必要はない。
ここではバージョンを10として説明します。

1. Datastoreのダウンロード

下記のコマンドでDatastoreのエンティティをダウンロードする

% appcfg.py download_data --application=YOUR_APP --url=http://10-dot-YOUR_APP.appspot.com/_ah/remote_api --filename=datastore.sqlite

YOUR_APPはGAEアプリの名前。
--urlで指定する時、デプロイしたバージョンのサーバ(remote_apiが使えるサーバ)にアクセスする。
必要なkindだけをダウンロードしたい場合は--kindなどで指定できる。
datastore.sqliteというファイル名でダウンロードされる。ファイル名は任意。

2. dev_appserverにアップロード

dev_appserverを起動する。
% dev_appserver.py app.yaml
下記コマンドでdev_appserverに先ほどダウンロードしたdatastoreのデータをアップロードする。
% appcfg.py upload_data --application=dev~YOUR_APP --url=http://localhost:8080/_ah/remote_api --filename=./datastore.sqlite
applicationで指定する名前に注意。
アプリ名の前に「dev~」が付きます。
urlではdev_appserverのURLを指定する。
実行すると、アップロードが開始され、完了するとサーバのDatastoreがローカルに構築されている。

3. 401エラー

dev_appserverにデータストアのデータをアップロードすると、
Refreshing due to a 401
というエラーが表示され、dev_appserver側のログでも
INFO     2017-02-08 04:49:05,871 module.py:787] default: "GET /_ah/remote_api?rtok=584784529929 HTTP/1.1" 401 57
という401エラーが出る場合がある。
remote_apiがadmin権限でしかアクセスできないが、appcfg.pyの認証系がうまく動いていない場合に発生するっぽい。
ちょっと強引な方法だが、remote_apiのadminチェックを切ってしまえばOK。
まず、下記ファイルを開く。
${GCLOUD_ROOT}/platform/google_appengine/google/appengine/ext/remote_api/handler.py
その中に、def CheckIsAdmin(self)メソッドがあるので、そのメソッドの返り値を無条件でreturn Trueとする。メソッドの一行目にreturn TrueとすればOK。
def CheckIsAdmin(self)
    return True
注意)アップロード処理が終わったら、この修正をもとに戻しておくことをおすすめします。

4. おまけ

dev_appserverのdatastoreの保存先ファイルをバックアップしておくことで、いつでもデータストアを復元できて便利です。
たとえば本エントリーでdatastoreをアップロードし終わったら、そのファイルを保存しておき、いつでもその状態でdev_appserverを起動できます。
まずアップロードする前に下記手順でdev_appserverを起動します。
% touch datastore.db
% dev_appserver.py --datastore_path=./datastore.db app.yaml
そして、appcfg.pyを使ってデータストアのデータをアップロードします。
アップロードが完了したらdev_appstoreを停止し、datastore.dbをバックアップします。
% mv datastore.db datastore.master.db
今後、dev_appserverを起動するときに、以下の手順で起動すると、かならず元のdatastoreが復元されるので色々便利だと思います。
% cp datastore.master.db datastore.db
% dev_appserver.py --datastore_path=./datastore.db app.yaml