読者です 読者をやめる 読者になる 読者になる

mastodonで起こった問題とその顛末

Qiitaから追放されたのでこっちに書きます。

これは何か

mastodonでソシャゲ系のインスタンスを運営しているのですが、そこであったトラブルの話です。

エロというセンシティブな問題ですが、ソシャゲはそのエロコンテンツを作っている人がまたゲームコンテンツ自体を提供しているという非常に複雑なコンテンツでもあり、端的に言ってエロ絵と親和性の高いコンテンツだと思います。
その中で、性的なコンテンツが苦手な人とそのコンテンツクリエイターとどう折り合いをつけていくか、という話に一定の手を打ったというのが本記事です。

もちろん、自分の対策が完璧であるとは全く考えていないですし、賛否両論あることはわかっています。
それを前提に何が起こったか、それに対してどういうスタンスでどういう対策を取ったかということを書きます。

皆さんの考える材料になれば幸いです。

起こったこと

4/15

  • socialgame.mstdn.cloudにて200人程度参加者がでてきた
  • その中でpawoo.netからエロ画像が連合に流れてきた
  • それ対して不快感を覚える声が一部から上がった
  • pawoo.net自体の経過を観察しつつ、現状は個人でblockなどの対策をとるようアナウンスした

4/16

  • pawoo.netから連合に流れてきた画像を何人かが笑いもの的な意味でネタにした
  • その雰囲気が全体に広がった
  • クリエイターの方からこういう事態があり傷ついたとの連絡が入った

私のスタンスと本件に対する認識

ここに関しては多分に私見が入ります。

まず、大前提として私は場を用意したのであって、コミュニティを用意したわけではありません。あくまでシステムの運営者として振る舞うべきだと考えています。
その上で、様々な発言に対していい発言も悪い発言も制限するつもりはありませんでした。その考えのもと、原則コンテンツの取捨選択は個人の裁量に任せるという判断を初日に下したわけです。

ところが、二つの要素がその状態を許容できなくしたと考えています。

  • SNSとしての規模の小ささ
    • 参加者全員で一つのコミュニティしか形成できないくらいに人数が少ない状況
  • pawoo.netの急拡大に伴う整備されていない大量のアダルトコンテンツの流入

私は、絵師さんを尊敬していますし、ソシャゲは彼らの存在があって成り立っているので、そのジャンルのインスタンスであるソシャゲインスタンスで絵師さん方が傷つくような状態は全くもって容認できませんでした。
しかし、現状上記の理由によって本インスタンス全体の雰囲気としてエロ絵を描く人を侮蔑するような雰囲気になっていたのは事実です。

かといって、一定数性的な表現に様々な理由で嫌悪感を示す人がいることも理解しています。そしてこれもまた上記のような理由でその方々にとっては無視できない状況であると認識しました。連合を見る限り個人で対応できる分量ではないこともしかり、またpawoo.netの整備が追いついていない状況なので性的なコンテンツがマスクされていない状態で流れ得る状況でもあったためです。

追記

一番の問題は、全体の雰囲気として性的コンテンツを悪い意味でネタにするような空気ができたことです。
これについては、今回は口頭のアナウンスで対応したのですが、そのきっかけとして存在した性的コンテンツを嫌悪する問題について解決を試みた、という組み立てになっています。

もちろん、直接そういう雰囲気にするな、で済めばいいのですが、どうなるかわからなかったので根本的な対応はこちらの方が近いのではないかとこういうアプローチをした次第です。

今回取った対策

結論としては、pawoo.netからの投稿を連合から除外したインスタンスと成人コンテンツ歓迎のインスタンスに分離することにしました。
また、同時にこのようなことに至った顛末をアナウンスし、その過程で絵師さんを非難するような風潮は許容できないとの旨をアナウンスしました。

論点としては、性的なコンテンツに嫌悪感を抱く人にとって個人のblockなどで自衛できないほど大量にそういったコンテンツが流れてくることが問題だと考えたためです。
だから、0にはできなくても絶対数を減らせれば自衛の余地があると考えました。

また、このような方々を無視すればいいのではないか、というお声もあると思うのですが、ソシャゲという非常に大きなマーケットを考えた時、そのプレイヤーはコアなオタクだけではなく既に様々な人に広がっているわけでそれだけの方がいればそれなりに性的なコンテンツに嫌悪感を抱く人はいるはずだと考えました。

もちろん、もっと人数のいるインスタンスであればhomeタイムラインをメインにして各自ホワイトリストで自衛するであったり、そういった対策は取れると思いますが現状の規模を考えるとそこまで求めるのは酷だと判断したためです。

謝罪

最後に、現在z-socialgame.mstdn.cloudやsocialgame.mstdn.cloudにおいては絵師の方々に不快な思いをさせるような状況は解消したと認識しています。
この点、運営者として大変申し訳ありませんでした。

今回、被害や不快な思いを被った絵師の方々にとっては、このような対応は納得いただけないかもしれません。

今後は、そのような不快な思いをさせてしまうようなことがないよう、むしろ絵師の方々が参加しても楽しく活用できるようできる限りのことはしていきたいと思うので、経過を見守っていただけると幸いです。

AWS lambdaでhello world

AWS lambda で hello world しようとしたら思ったよりハマったので共有。

目的

ポイント

  • GET パラメータを渡すには API Gateway で設定が必要
  • API Gateway の Resources の設定は Stage に Deploy する事で反映される

手順

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 が処理に必要な引数っぽいです。

このコードでやりたい事としては

です。

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 にあります。

f:id:sora_sakaki:20160319180150p:plain

こういう画面。(これは設定済み画面です)

  1. Method Request で Query String(URL Query String Parameters) に operation を設定
  2. Integration Request の Body Mapping Templates で application/json の Mapping を作成、中身を以下のようにする
{
    "operation":"$input.params('operation')"
}

すべて終わったら、上画面の Deploy API というボタンで設定を反映します。

設定反映前でも、Test でテストできるのでそこで確認してから Deploy すると良いと思います。

動かす

https://path?operation=helloworld で helloworld が帰ってきたら完成です。

参考

qiita.com

今年の活動について。機械学習メインになりそう。

去年の弁明

最近blogサボっててすみません。
去年はChainerのバージョンが上がる直前に同人誌つくったり、記事を書き溜めたりしていました。

そして残骸たち

f:id:sora_sakaki:20160319173128p:plain

とりあえずこれまでに書いたチュートリアルは古いので参考程度にとどめて頂ければ幸いです。

今年の活動

去年に続き今年も機械学習系の仕事を会社でもらったので、ディープラーニング以外の機械学習も含めてやっていこうと思っています。

夏コミも通れば同人誌を作ろうと思っているので、そちらでディープラーニングネタもやっていけたらと。

あと、

という事で、オタクネタ縛りの機械学習勉強会を開催したいと考えています。多分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

Chainerチュートリアルを和訳する必要があったからかいてみた(1)

追記:

本記事はChainer 1.4以前の物になります。

現在の仕様とは大きく異なるので、参考程度にとどめてください。

それより新しいものを書かれた方もいらっしゃるようなのでそちらも参考にしてください。

i101330.hatenablog.com

    • -

私的な理由により和訳を求められているので雑に必要そうなところだけ和訳します。

英語苦手ですし、奇麗に整形する気もないので、和訳の内容は保証しません。

また、指摘は大歓迎です。是非おしえてください。

2を書いたらリンク張ります。

注) Chainerではnumpyというライブラリを頻繁に使うのでそちらも参照

Introduction to Chainer

これはChainer tutorialの1章です。この章では

  • なんでChainerを作ったか
  • 簡単な順伝播、逆伝播計算
  • パラメータを持つ関数の使い方とその勾配計算法
  • model
  • パラメーター調整

をやります。

が分かるでしょう。

Chainerの中心的コンセプト

Chainerは柔軟なニューラルネットワークフレームワークです。一つの大きな目的は柔軟性です。なので複雑な構造をシンプルに、直感的に書けることが必要です。

そのために、Chainerでは既存の多くのフレームワークと異なり定義してから実行ではなくて、定義とともに実行(Define by Run)する方法を取ります。

Note
このチュートリアル中のサンプルコードは以下のimportが済んでいる事が前提です。

import numpy as np
from chainer import cuda, Function, FunctionSet, gradient_check, Variable, optimizers
import chainer.functions as F

順伝播/逆伝播計算

"Define by Run" なので、Chainerでは順伝播計算はネットワークの定義そのものです。まず、順伝播計算を始めるために、入力配列をVariableオブジェクトに変換しましょう。以下で簡単な1変数の ndarray(numpyの配列オブジェクト)から始めましょう。

>>> x_data = np.array([5], dtype=np.float32)
>>> x = Variable(x_data)
Warning

現在Chainerは32bit floatしかサポートしていません。

Variableオブジェクトは簡単な計算が可能です。例えば、

{ \displaystyle
y = x^2 - 2x +1
}

は、

>>> y = x**2 - 2 * x + 1

のように書けます。

結果のyもVariableオブジェクトで、値にはdata属性でアクセスできます。

>>> y.data
array([ 16.], dtype=float32)

yは結果の値だけを持っている訳じゃありません。yは計算の記録も持っているので、これの微分も計算できます。backword()メソッドを呼ぶと計算できます。

>>> y.backward()

これによって、誤差逆伝播が実行されます。その時、勾配はxのgrad属性に入ります。

>>> x.grad
array([ 8.], dtype=float32)

もちろん、仲介的な変数の勾配も計算できます。Chainerにおいては、メモリ効率の観点から標準で仲介的な変数の勾配配列を開放するようにしています。その代わり前の勾配情報は、retain_grad引数にTrueを指定すると得られます。

>>> z = 2*x
>>> y = x**2 - z + 1
>>> y.backward(retain_grad=True)
>>> z.grad
array([-1.], dtype=float32)

これらの計算はすべて複数の要素を持つ配列でもできます。一つ、固定された複数要素を持つ配列から逆伝播を計算したい場合は初期エラー値をセットしておく必要があります。これは簡単にできて、以下のように出力のgrad属性にセットするだけです。

>>> x = Variable(np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float32))
>>> y = x**2 - 2*x + 1
>>> y.grad = np.ones((2, 3), dtype=np.float32)
>>> y.backward()
>>> x.grad
array([[  0.,   2.,   4.],
       [  6.,   8.,  10.]], dtype=float32)
Note

沢山のVariableオブジェクトを返す関数がfunctionモジュールに定義されています。

実際に計算する複雑な関数のために、それらを組み合わせて使えば自動で逆伝播が
計算できます。

パラメータを持つ関数(各位訳語つけてくれ!)

ニューラルネットワークを書くのに、パラメータを持つ関数を使い、そのパラメータを調整する必要があります。前述のとおり、functionsモジュールで既に定義されている関数の中にはパラメータを持つ関数が含まれています。

基本的なパラメータを持つ関数の一つはLinear関数(別名全結合層、アフィン変換)です。この関数は  f(x) = Wx + b で表され、行列  W とベクトル  b がパラメータになります。この線形関数は三次元から二次元への関数として定義され、

>>> f = F.Linear(3, 2)
Note

ほとんどの関数はミニバッチ入力しか受け付けていません。入力の一つ目の次元を
バッチの次元として考えます。線形関数の場合では、入力は必ず(N, 3)次元の形を
していなければいけません。Nはミニバッチのサイズです。

線形関数のパラメータはwとa属性に格納されています。デフォルトでは、行列Wはランダムに初期化され、ベクトルbは0で初期化されます。

>>> f.W
array([[ 1.33545339, -0.01839679,  0.7662735 ],
       [-1.21562171, -0.44784674, -0.07128379]], dtype=float32)
>>> f.b
array([ 0.,  0.], dtype=float32)

パラメータを持つ関数のインスタンスは普通の関数のように振る舞います。

>>> x = Variable(np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float32))
>>> y = f(x)
>>> y.data
array([[ 3.5974803 , -2.3251667 ],
       [ 9.84747124, -7.52942371]], dtype=float32)

パラメータの勾配はbackword()メソッドで計算されます。一つ気をつけるのは、この勾配は上書きされるわけではなく、加算されていきます。なので改めて計算する前に必ず勾配を0にしなければいけません。線形関数の勾配はgWとgb属性にあるので、

>>> f.gW.fill(0)
>>> f.gb.fill(0)

のように0をセットします。

Note

この処理はFunctionSetとOptimizerで簡略化されます。次のセクションをみて
ください。

今、勾配は単純にbackwordメソッドを呼ぶ事で以下のように計算できます。

>>> y.grad = np.ones((2, 2), dtype=np.float32)
>>> y.backward()
>>>
>>> f.gW
array([[ 5.,  7.,  9.],
       [ 5.,  7.,  9.]], dtype=float32)
>>> f.gb
array([ 2.,  2.], dtype=float32)

FunctionSet

ほとんどのニューラルネットワークは多変数のパラメータを持つ関数を含みます。FunctionSetを使うと、それらを簡単に管理できるようになります。このクラスはキーワード引数で初期化された属性を持つ簡単なオブジェクトのように振る舞います。

>>> model = FunctionSet(
...     l1 = F.Linear(4, 3),
...     l2 = F.Linear(3, 2),
... )
>>> model.l1
<chainer.functions.linear.Linear object at 0x7f7f03e4f350>
>>> model.l2
<chainer.functions.linear.Linear object at 0x7f7f03e4f590>

さらに、後から属性をセットする事で追加する事もできます。

>>> model.l3 = F.Linear(2, 2)

modelはまさにこれらの属性を格納する関数を持つオブジェクトで、順伝播計算の中でこれらの関数が使えます。

>>> x = Variable(np.array([[1, 2, 3, 4], [5, 6, 7, 8]], dtype=np.float32))
>>> h1 = model.l1(x)
>>> h2 = model.l2(h1)
>>> h3 = model.l3(h2)

FunctionSetはパラメータと勾配を集める特徴を持っています。すべてのパラメータと勾配のタプルがFunctionSet.parametersとFunctionSet.gradientsプロパティに抽出されています。

Optimizer

このセクションで説明するOptimizerは最後のChainerの主な機能です。Optimizerはパラメータと勾配のタプルを算出する数値最適化アルゴリズムを実行します。沢山のアルゴリズムがoptimizersモジュールに実装されています。以下に単純なアルゴリズムの一つであるStochastic Gradient Descentを示します。

>>> optimizer = optimizers.SGD()
>>> optimizer.setup(model)

FunctionSetを使って用意したパラメータと勾配を最適化するために、setup()メソッドに指定します。

Note(ちょっと英語分からなかった)

Optimizerは渡された値が実体かどうか区別できないので、一度Optimizerに参照を
渡すと勝手に値は変更され、その後はOptimizerが変更するパラメータと勾配を
すべての順伝播/逆伝播計算を通じて使う事になります。

最適化を実行すると、まず勾配を計算する必要があります。0で初期化するには簡単にzero_grads()メソッドが使えます。

>>> optimizer.zero_grads()

一つ前のセクションでは手動で0にしました。この行では等価で簡単な勾配の初期化ができます。

勾配を計算した次に、update()メソッドを1回最適化イテレーションをまわすために実行します。

>>> (compute gradient)
>>> optimizer.update()

Optimizerはさらにパラメータと勾配の操作に関係したいくつかの機能を備えています。たとえば、weight decayやgradient clippingなど。

Example: Multi-layer Perceptron on MNIST

まだ!

matplotlibでグラフを吐かせる

長い年月を再びあけて、Chainerをいじるのを再会した榊そらです。

Chainerいじるのに何か出そうとしたとき、matplotlibを使ってグラフを出そうとしたのですが、
いいブログはあったんですがPython3で動かなかったので追加情報としてブログにまとめます。

各種情報

  • python: 3.4.3
  • matplotlib: 1.4.3
  • numpy: 1.9.2

ソースはこちらbicycle1885.hatenablog.com

問題は表示部分で、今回の環境、おそらくPython3だと show を実行しないとだめです。

import numpy as np
import matplotlib.pyplot as plt

x = np.arange(-3, 3, 0.1)
y = np.sin(x)
plt.plot(x, y)

plt.show()

ちなみにこれはipython notebookでも同様でした。

また、plt.show()の代わりに、plt.savefig('foo.png')のようにファイル名を指定すると簡単にpngにできて便利です。
実際検証でデータを取るときはsavefigとclf、グラフの削除を使って

import numpy as np
import matplotlib.pyplot as plt

x = np.arange(-3, 3, 0.1)
y = np.sin(x)
plt.plot(x, y)

plt.savefig('sin.png')

plt.clf()
y = np.cos(x)
plt.plot(x, y)

plt.savefig('cos.png')

と、こんな感じでどんどん画像を作っていくといい感じですね。

あとこれがうれしい。
とりあえず描く — matplotlib 1.0 documentation

elasticsearchのBulk APIでは改行してはならない

諸事情で最近elasticsearchを使っているんですが、しばらくはまったのでメモ。

elasticsearchには、Bulk APIというデータをまとめて追加したり消したりする便利なAPIがあるんですが、これがリクエストにJSONもどきを求めるんですね。

{"index":{"_index":"nekomimi","_type":"group"}}
{"id":"hoge"}

これで{"id":"hoge"}が挿入できます。1行目に命令を、2行目にデータを流すという感じですね。
これを続けて、

{"index":{"_index":"nekomimi","_type":"group"}}
{"id":"hoge"}
{"index":{"_index":"nekomimi","_type":"group"}}
{"id":"hage"}
{"index":{"_index":"nekomimi","_type":"group"}}
{"id":"neko"}

のように3つまとめてデータを入れることもできてこういうところが便利なAPIです。

しかし、これが複雑な場合にハマりました。

{"index":{"_index":"nekomimi","_type":"group"}}
{"id":2,"users":[
  {"user":139340004,"updated_at":"Tue Aug 11 05:55:23 +0000 2015","tweets":[630980415889670100]},
  {"user":84863232,"updated_at":"Tue Aug 11 05:55:12 +0000 2015","tweets":[630980273216172000]},
  {"user":95602570,"updated_at":"Tue Aug 11 05:53:55 +0000 2015","tweets":[630980241591107600]}
]}

このように複雑でひとまず手でデータを作成したわけですよ。
これ死にます。

正しくはこうです。

{"index":{"_index":"nekomimi","_type":"group"}}
{"id":2,"users":[{"user":139340004,"updated_at":"Tue Aug 11 05:55:23 +0000 2015","tweets":[630980415889670100]},{"user":84863232,"updated_at":"Tue Aug 11 05:55:12 +0000 2015","tweets":[630980273216172000]},{"user":95602570,"updated_at":"Tue Aug 11 05:53:55 +0000 2015","tweets":[630980241591107600]}]}

つまりデータはデータで1行じゃないといけないんですね。

そもそもこんなJSONもどきを投げさせるな

追記(2015/08/19 16:36)

この記事を投稿した直後に大谷さんよりなぜこのような形なのかコメントをいただきました。ありがとうございます。