AWS lambdaでhello world
AWS lambda で hello world しようとしたら思ったよりハマったので共有。
目的
- API Gateway (GET) + lambda(nodejs) で hello world したい
手順
microservice-http-endpoint をベースに lambda ファンクションを作る
lambda ファンクションを作る
Step2 の Configure function 画面で
- コードの書き換え
- IAM Role の作成
を行います。
コードを次のように書き換えます。
console.log('Loading function'); exports.handler = function(event, context) { //console.log('Received event:', JSON.stringify(event, null, 2)); var operation = event.operation; if (event.tableName) { event.payload.TableName = event.tableName; } switch (operation) { case 'helloworld': context.succeed('helloworld'); break; case 'ping': context.succeed('pong'); break; default: context.fail(new Error('Unrecognized operation "' + operation + '"')); } };
ざっくり解説すると、event にパラメータとかが入ってきて、context が処理に必要な引数っぽいです。
このコードでやりたい事としては
- https://path?operation=helloworld で helloworld を返す
- https://path?operation=ping で pong を返す
です。
IAM Role の作成は Lambda function handler and role の Role 部分で指定すれば作成できます。設定された IAM Role を作成できるので、選択した後は名前だけ決めて作成すれば大丈夫です。
今回 Dynamo はつかわないので Basic execution role で大丈夫です。
API Gateway の設定をする
Step3 の Configure endpoints に進むと API Gateway の設定になります。ここでは、
- Security を Open に設定する
を行います。この設定をする事で認証なしで API を実行できます。
余談 この作成ウィザードで作成する場合はここで API Gateway が作成されるので、別途 API Gateway を作成する必要はなくて楽です。 ちなみに、おそらく - AWS IAM ほかのサービスからアクセスする場合に IAM で許可を出す - Open with access key 認証ありで外部からアクセスする だと思うので、EC2 などから API Gateway へアクセスしたい場合は AWS IAM を指定するとよさそうです。
API Gateway に追加設定する
GET の場合、API Gateway で追加設定しないと script 中の event へ値が渡りません。
以下の設定が必要です。
- Method Request で Query String を設定する
- Integration Request で Body Mapping Templates を設定する
設定画面は API Gateway の Resources の GET にあります。
こういう画面。(これは設定済み画面です)
- Method Request で Query String(URL Query String Parameters) に operation を設定
- Integration Request の Body Mapping Templates で application/json の Mapping を作成、中身を以下のようにする
{ "operation":"$input.params('operation')" }
すべて終わったら、上画面の Deploy API というボタンで設定を反映します。
設定反映前でも、Test でテストできるのでそこで確認してから Deploy すると良いと思います。
動かす
https://path?operation=helloworld で helloworld が帰ってきたら完成です。
参考
今年の活動について。機械学習メインになりそう。
去年の弁明
最近blogサボっててすみません。
去年はChainerのバージョンが上がる直前に同人誌つくったり、記事を書き溜めたりしていました。
そして残骸たち
とりあえずこれまでに書いたチュートリアルは古いので参考程度にとどめて頂ければ幸いです。
今年の活動
去年に続き今年も機械学習系の仕事を会社でもらったので、ディープラーニング以外の機械学習も含めてやっていこうと思っています。
夏コミも通れば同人誌を作ろうと思っているので、そちらでディープラーニングネタもやっていけたらと。
あと、
オタク機械学習勉強会やりたいフレンド募集してます
— 猫耳がほしいPrimちゃん (@dasoran) 2016年3月15日
という事で、オタクネタ縛りの機械学習勉強会を開催したいと考えています。多分5月くらい。ピザが出るので興味ある方の参加お待ちしています。
他に、AWS lambdaを使って不動産系のWebサービスを作ってみようとか、そんなことを企んでいるのでそれ系の記事もちょっと入れていけたらと思っています。
というわけで、今後ともよろしくお願いします。
Chainerチュートリアルを和訳する必要があったからかいてみた(2): GPU編
追記:
本記事はChainer 1.4以前の物になります。
現在の仕様とは大きく異なるので、参考程度にとどめてください。
-
- -
前回に引き続き2回目、今回は2章を飛ばし3章のGPUの使い方について訳しました。
また、複数GPUを使うのは必要に迫られていないので取り急ぎ省略しました。
ChainerでGPUを使う
この章では、以下の事を学びます。
- ChainerとCuPyの関係
- CuPyの基本
- ChainerでのSingle-GPUの使い方
- model-parallel計算におけるMulti-GPUの使い方
- data-parallel計算におけるMulti-GPUの使い方
この章を読んだ後、以下の事ができるようになります。
- CUDAの使えるGPUをChainerで使う
- Chainerでmodel-parallel計算を書く
- Chainerでdata-parallel計算を書く
ChainerとCuPyの関係
Note v1.3.0のリリースから、ChainerはGPUバックエンドをPyCUDAから CuPyに変更しました。CuPyはChainerで使っていたPuCUDAの機能を すべてカバーしていますが、インタフェースは異なります。
ChainerはGPU計算のバックエンドにCuPyを使用しています。特に、cupy.ndarrayクラスはChainerの為に実装されたGPU配列です。CuPyはNumPyの機能のサブセットを同じインタフェースでサポートしています。なのでCPUとGPUで共通のコードを書く事が可能です。さらにPyCUDA風のユーザー定義カーネル生成もサポートしていますので、GPU専用の速い実装も書けます。
Note chainer.cudaモジュールは沢山の重要なシンボルをCuPyからインポート しています。例えば、cupyのネームスペースはchainer内において cuda.cupyを参照しています。また、chainer.cudaモジュールはたとえ CUDAをインストールしていなくてもインポートできます。
ChainerはGPU向けに確保されたメモリプールを使用します。前のセクションで見た通り、Chainerは学習や評価するイテレーションの中で沢山の配列を初期化し、破棄します。この振る舞いはCUDAアーキテクチャに良く向いておらず、つまりCUDA上でのメモリの確保と開放(i.e.cudaMallcやcudaFree関数)はCPUの場合とGPUの場合において性能が発揮しきれない方法で同期しています。そこで、計算中のメモリの確保と開放を避けるために、ChainerはCuPyのメモリプールを標準のメモリ確保アロケーターとして使用します。ChainerはデフォルトのアロケーターをCuPyのものに変更したため、ユーザーはCuPyの関数を既存のメモリアロケーターの管理を避け直接的に使用する事が可能です。
cuda.ndarrayの基礎
Note CuPyは明示的な初期化が必要ありません。なので、cuda.init()関数は v1.3.0から廃止されました。
CuPyはNumPyインタフェースのサブセットを実装したGPU配列のバックエンドです。cupy.ndarrayクラスはこのコアで、GPU版のnumpy.ndarrayと同質です。CuPyは沢山の関数をcupy.ndarrayオブジェクトに実装しています。こちらでサポートしているサブセットのリファレンスが見られます。NumPyを理解する事はほとんどのCuPyの機能を理解する助けになるでしょう。こちらでNumPyの学習用ドキュメントが見られます。
cupy.ndarrayとnumpy.ndarrayで主に異なることは、cupy.ndarrayの場合領域がデバイス上で確保済みだということです。この確保はデフォルトでは現在のデバイス(カレントデバイス)に対して行われます。カレントデバイスは、cupy.cuda.Deviceオブジェクトで以下のように変更可能です。
with cupy.cuda.Device(1): x_on_gpu1 = cupy.array([1, 2, 3, 4, 5])
CuPyのほとんどの処理はカレントデバイスで行います。カレントデバイスではないデバイス上の配列の処理でエラーが発生するので注意してください。
Chainerは自動でデバイスを切り替え、選ぶいくつかの便利な関数を提供します。例えば、chainer.cuda.to_gpu()関数はnumpy.ndarrayオブジェクトを特定のデバイスへコピーします。
x_cpu = np.ones((5, 4, 3), dtype=np.float32) x_gpu = cuda.to_gpu(x_cpu, device=1)
これは、以下のCuPyを使ったコードと同じことです。
x_cpu = np.ones((5, 4, 3), dtype=np.float32) with cupy.cuda.Device(1): x_gpu = cupy.array(x_cpu)
GPUデバイス上からCPUへ移動したい場合はchainer.cuda.to_cpu()を用いて以下のようにできます。
x_cpu = cuda.to_cpu(x_gpu)
これは、CuPyを用いて以下のようにするのと同じです。
with x_gpu.device:
x_cpu = x_gpu.get()
Note 上記のようなwith句は適切なCUDAデバイスを使用するために必要な ものです。もし、1つのデバイスのみを使用している場合、デバイスを 切り替える必要はありません。 chainer.cuda.to_cpu()とchainer.cuda.to_gpu()関数は自動的に現在の デバイスを正しく切り替えてくれます。
Chainerはさらにchainer.cuda.get_device()という便利な関数をデバイスを選択するために提供しています。この関数は整数、CuPy配列、NumPy配列またはNone(この場合現在のデバイスを指します)を受け、適切なデバイスオブジェクトを返します。もし引数がNumPy配列の場合、ダミーデバイスオブジェクトが返ります。ダミーデバイスオブジェクトはなにもしないwith句をサポートします。ここにいくつかの例を示します。
cuda.get_device(1).use() x_gpu1 = cupy.empty((4, 3), dtype='f') # 'f' indicates float32 with cuda.get_device(1): x_gpu1 = cuda.empty((4, 3), dtype='f') with cuda.get_device(x_gpu1): y_gpu1 = x_gpu + 1
このようにNumPy配列を受け取るので、適切なデバイス切り替えをしてくれる関数をNumPyとCuPyともに適用可能なものとして書く事ができます。
def add1(x): with cuda.get_device(x): return x + 1
CuPyのNumPyとの互換性はCPU/GPU共通コードを書く事を可能にします。これはchainer.cuda.get_array_module()関数により簡単に実現できます。この関数はnumpy、cupyモジュールを引数に応じて返します。CPU/GPU共通関数は以下のように定義できます。
# Stable implementation of log(1 + exp(x)) def softplus(x): xp = cuda.get_array_module(x) return xp.maximum(0, x) + xp.log1p(xp.exp(-abs(x)))
Single GPUでニューラルネットワークを走らせる
Single-GPUの使い方は非常にシンプルです。FunctionSetをGPUへ移して、事前に配列をGPUへ入れておくだけです。このサブセクションでは、ChainerのMNIST exampleのコードを使って話を進めます。
FunctionSetオブジェクトはto_gpu()メソッドを用いて指定されたGPUへ移動できます。GPU版のパラメータと勾配がoptimizerへ渡っているか確認してください。
model = FunctionSet( l1 = F.Linear(784, 100), l2 = F.Linear(100, 100), l3 = F.Linear(100, 10), ).to_gpu() optimizer = optimizers.SGD() optimizer.setup(model)
このto_gpu()メソッドはFunctionSet自体を返します。デバイスは指定しなくてもかまいません。その場合はカレントデバイスを使用します。
それから、ミニバッチのすべての計算をGPUへ移しましょう。
batchsize = 100 datasize = len(x_train) for epoch in range(20): print('epoch %d' % epoch) indexes = np.random.permutation(datasize) for i in range(0, datasize, batchsize): x_batch = cuda.to_gpu(x_train[indexes[i : i + batchsize]]) y_batch = cuda.to_gpu(y_train[indexes[i : i + batchsize]]) optimizer.zero_grads() loss, accuracy = forward(x_batch, y_batch) loss.backward() optimizer.update()
これはほとんど元々のexampleとおなじコードですが、cuda.to_gpu()関数をミニバッチ配列に挿入しました。
Model-parallel Computation on Multiple GPUs
略
Data-parallel Computation on Multiple GPUs
略