matplotlibでグラフを画像ファイルとして出力する

windowsのノートPCからLinuxのデスクトップPCへsshでつないで作業するみたいなことが多いのですが、そんなときpythonでグラフを出力したいときにいつもやり方を忘れるので備忘録。

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

plt.plot([1,2,3,4,5,4,3,2,1])
plt.savefig("hoge.png")

GoogleAppEngineでURL短縮サービスを作ってみる

転職して、Web系のプロダクトに関わるようになって一年立ちました。
フロントもバックエンドも一応触っていますが、既にある機能の改修とか小さい機能の追加がメインです。
自分で0から作れる自信がないです。
自信がないのは経験がないからだと思います。
ということで、なんらかのWebサービスを作ってみようと思います。

こういうときは、きっと一番作るのが簡単そうなものから始めるのがいいです。

一番作るのが簡単そうなWebサービスって、人によっていろいろ考えはあると思いますが、僕はURL短縮サービスなんじゃないかと思いました。
(もちろん、真面目に考えると色々大変なことはあるとは思いますが)

apache とか RDBMS とかをいちいち準備するのも面倒なので、
Google App Engineでやってみます。

URLを短縮する際の仕組みは、

1. 短縮したいURLを受け取る
2. ランダムな6文字のアルファベットを生成する
3. DataStoreに生成した文字列が保存されて入れば、2に戻りさらに文字列を生成する
4. DataStoreに生成した文字列が保存されていなければ、短縮URLとしてその文字列を採用し、短縮URLともとのURLの組を保存する(短縮URLをキーとする)

短縮URLから元のURLに戻すには、

1. DataStoreからget_by_idで短縮URLが保存されているか確認する
2. 保存されて入れば、元のURLにリダイレクト
3. 保存されていなければ、NotFoundページへ

と言う感じです。

URL
http://mijikakusuruyatsu.appspot.com/

リポジトリ
https://github.com/TakahashiShuuhei/mijikakusuruyatsu

http://mijikakusuruyatsu.appspot.com/PYahIP
とかにアクセスするとこのブログにリダイレクトしてくれます。

短縮URLなのにホスト名がやたら長いとか、全然関係ない文字列も短縮しようとしちゃうとか、問題はありますが、とりあえず作ってデプロイするという目標は達成できたのでとりあえずよしとします。

windowsからmint linuxにxrdpでリモートデスクトップ接続したら’d’

http://askubuntu.com/questions/284481/d-minimizes-all-windows-on-ubuntu-when-connecting-with-xrdp

https://bugs.launchpad.net/ubuntu/+source/xrdp/+bug/220005/comments/36

あるあるネタみたいです。
"デスクトップを表示"のショートカットの設定をdisabledにしろって、まあそうなんですが、なんというか。。。

ExtJSでカレンダーを使う

ExtensibleのCalendar Proというやつを使います。

http://ext.ensible.com/

extensible-1.6.0-rc.1.zipをダウンロードしておきます。

ExtJS 4.2.1.883と
sencha cmd 4.0.2.67を使っています

プロジェクトを作成します

sencha -sdk /path/to/ext-4.2.1.883/ generate app calendarsample /path/to/calendarsample

/path/to/calendarsampleに必要なファイル一式が展開されます。

extensibleをプロジェクトディレクトリ内にコピーします。

cp -r /path/to/extensible-1.6.0-rc.1 /path/to/calendarsample/extensible

extensible calendar proはどうやらsencha app buildでビルドできないっぽいので
http://ext.ensible.com/forum/viewtopic.php?f=2&t=763&start=10
を参考にextensible/src/Extensible.jsを修正してしまいます。

// ....

Ext.define('Extensible', {
    // ここから
    calendar: {
        data: {},
        dd: {},
        form: {
            field: {}
        },
        gadget: {},
        menu: {},
        template: {},
        util: {},
        view: {}
    },
    data: {},
    form: {
        field: {},
        recurrence: {
            option: {}
        }
    },
    lang: {},

    // ここまで追加

    singleton: true,

    // ...

ライブラリの中身をいじらなきゃいけないとか微妙すぎますが他の方法が見つかりませんでしたのでこれでいきます。

Viewport.jsを修正します

   // ....
   items: [{
        xtype: 'extensible.calendarpanel',
        store: Ext.create('Extensible.calendar.data.MemoryCalendarStore')
    }],
    // ....

calendarsample/.sencha/app/sencha.cfgのクラスパスにextensible/srcを追加します

app.classpath=${app.dir}/extensible/src,${app.dir}/app,${app.dir}/app.js

あと、extensibleのリソース類もコピーしてもらうためにsencha.cfgのapp.resource.pathsも修正します

app.resource.paths=${app.dir}/resources,${app.dir}/extensible/resources

extensibleのcssをデプロイする方法が良く分かりませんでした。

ってことで無理やりコピーします。

build.xmlを編集します

 <target name="-after-build">
      <copy file="${basedir}/extensible/resources/css/extensible-all.css"
            todir="${basedir}/build/production/calendarsample/resources" />
    </target>


また、index.htmlを修正します。

<!DOCTYPE HTML>
<html>
<head>
    <meta charset="UTF-8">
    <title>calendarsample</title>
    <link rel="stylesheet" href="resources/extensible-all.css">
    <!-- <x-compile> -->
        <!-- <x-bootstrap> -->
            <link rel="stylesheet" href="bootstrap.css">
            <script src="ext/ext-dev.js"></script>
            <script src="bootstrap.js"></script>
            <script src="extensible/src/Extensible.js"></script>
        <!-- </x-bootstrap> -->
        <script src="app.js"></script>
    <!-- </x-compile> -->
</head>
<body></body>
</html>

ここまできたらビルドします。

プロジェクトのトップディレクトリで

sencha app build

これでbuild/production/calendarsampleフォルダ下にデプロイに必要なファイルだけが展開されます。

build/production/calenarsampleフォルダをhttpサーバのフォルダにおいてやるとカレンダーが表示されると思います。


Google Cloud EndpointsをつかってGoogle App Engine上にREST apiを実装してみる

GAE上で動くREST apiを作りたかったのでちょっと調べたところ、Google Cloud Endpointsなるものを使うと楽に出来そうなので試して見ます。

基本的に本家チュートリアル
https://developers.google.com/appengine/docs/java/endpoints/getstarted/backend/
をなぞってみた作業ログです。

1. プロジェクトの作成
[ファイル] > [新規] > [その他] で [Mavenプロジェクト]を選択して[次へ]

[デフォルト・ワークスペース・ロケーションの使用]のみチェックした状態で[次へ]


[groupId]がcom.google.appengine.archetypes, [artifactId]がskeleton-archetypeであるアーキタイプを選択して[次へ]
(アーキタイプが一覧に表示されない場合は前回記事を参考に追加する)


[groupId]に com.google.devrel.samples.helloendpoints
[artifactId]に helloendpoints
を入力して[完了]

これでhelloendpointsという名前のプロジェクトが出来た。

2. プロジェクトの設定
2.1. 依存関係の追加

pom.xmlを開き[依存関係]タブ内左側の[追加]ボタンから、以下の二つを追加する

<dependency>
    <groupId>com.google.appengine</groupId>
    <artifactId>appengine-endpoints</artifactId>
    <version>${appengine.target.version}</version>
</dependency>
<dependency>
    <groupId>javax.inject</groupId>
    <artifactId>javax.inject</artifactId>
    <version>1</version>
</dependency>

pom.xmlを保存する。

2.2. プラグインの設定

pom.xmlを編集する。
ただし、本家チュートリアルのままやるとディレクトリの設定が変だって怒られるので以下のように修正する。

本家

   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.3</version>
    <configuration>
        <webXml>${project.build.directory}/generated-sources/appengine-endpoints/WEB-INF/web.xml</webXml>
        <webResources>
            <resource>
                <!-- this is relative to the pom.xml directory -->
                <directory>${project.build.directory}/generated-sources/appengine-endpoints</directory>
                <!-- the list has a default value of ** -->
                <includes>
                    <include>WEB-INF/*.discovery</include>
                    <include>WEB-INF/*.api</include>
                </includes>
            </resource>
        </webResources>
    </configuration>
</plugin>
||

変更後
>|xml|
   <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-war-plugin</artifactId>
    <version>2.3</version>
    <configuration>
        <webXml>${basedir}/src/main/webapp/WEB-INF/web.xml</webXml>
        <webResources>
            <resource>
                <!-- this is relative to the pom.xml directory -->
                <directory>${basedir}/src/main/webapp/WEB-INF</directory>
                <!-- the list has a default value of ** -->
                <includes>
                    <include>WEB-INF/*.discovery</include>
                    <include>WEB-INF/*.api</include>
                </includes>
            </resource>
        </webResources>
    </configuration>
</plugin>
||

appengine-maven-pluginのほうはそのまま使用
>|xml|
<plugin>
              <groupId>com.google.appengine</groupId>
              <artifactId>appengine-maven-plugin</artifactId>
              <version>${appengine.target.version}</version>
              <configuration>
                <enableJarClasses>false</enableJarClasses>
              </configuration>
              <executions>
                <execution>
                  <goals>
                    <goal>endpoints_get_discovery_doc</goal>
                  </goals>
                </execution>
              </executions>
            </plugin>

なんかeclipseだとpom.xmlでエラーが出てますがとりあえずmvn installが通るのでそのままいきます

3. Getを実装してみる

https://developers.google.com/appengine/docs/java/endpoints/getstarted/backend/write_api

からそのままHelloGreetingとGreetingsクラスをコピってきます。

本家チュートリアルはここで

mvn appengine:devserver

して

http://localhost:8080/_ah/api/explorer

にアクセスすると使えるapiのリストが表示されるよって書いてありますがエラーになってしまいました。なんかweb.xmlの設定が必要なようです。

ということでweb.xmlに以下を追加します

 <servlet>
  <servlet-name>SystemServiceServlet</servlet-name>
  <servlet-class>com.google.api.server.spi.SystemServiceServlet</servlet-class>
  <init-param>
   <param-name>services</param-name>
   <param-value>com.google.devrel.samples.helloendpoints.Greetings</param-value>
  </init-param>
 </servlet>
 <servlet-mapping>
  <servlet-name>SystemServiceServlet</servlet-name>
  <url-pattern>/_ah/spi/*</url-pattern>
 </servlet-mapping>

これで再び先ほどのURLにアクセスすると以下のようなページが表示されます。


getGreetingを選んで実行してみるとjsonが帰ってきているのが分かります


データストアとの連携とかバリデーションとか調べることはありますが、とりあえずapiの実装の流れがなんとなく分かったのでOKとします

多層パーセプトロン

ディープラーニングチュートリアル
http://deeplearning.net/tutorial/intro.html

今回はMultilayer perceptron を読んでいきます。
http://deeplearning.net/tutorial/mlp.html

多層パーセプトロン

今回扱うのは、隠れ変数の層が一つある多層パーセプトロン
隠れ変数間や観測変数間の接続はないものとする。

これは、入力に非線形変換が施されたロジスティック回帰とみなせる。

なんで非線形変換なんかするかって言うと、線形分離可能にするため。

モデル

ただしは入力ベクトルのサイズ、
は出力ベクトルのサイズ


行列形式で書けば、

はバイアスベクトル
は重み行列
は活性化関数

で隠れ変数の状態を計算可能

ただし、 は入力変数と隠れ変数をつなぐ重み行列で, は隠れ変数の数

sとしてはtanhシグモイド関数がよくつかわれる
ここではtanhを使う

するとさっきの式は

となる。

Gとしてsoftmax関数を採用すれば、これは前回やったロジスティック回帰とまったく同じ(入力が変換されている点をのぞいて)

多層パーセプトロンを学習させるには、全パラメタを更新していかなければならない。

の計算はTheanoがしてくれるからここではくわしくは触れない。

ロジスティック回帰から多層パーセプトロン

ここからは実装の話

入力をで変換して、
その結果をロジスティック回帰の入力にしてやるのだからまず

ここで、重みの初期値はの範囲からランダムにとるとうまくいくってことが知られているのでそれに従う。

class HiddenLayer(object):
    def __init__(self, rng, input, n_in, n_out, activation=T.tanh):
        self.input = input

        # 初期値の設定
        W_values = numpy.asarray(rng.uniform(
                low=-numpy.sqrt(6. / (n_in + n_out)),
                high=numpy.sqrt(6. / (n_in + n_out)),
                size=(n_in, n_out)), dtype=theano.config.floatX)
        if activation == theano.tensor.nnet.sigmoid:
            W_values *= 4
            
        self.W = theano.shared(value=W_values, name='W')

        b_values = numpy.zeros((n_out), dtype=theano.config.floatX)
        self.b = theano.shared(value=b_values, name='b')
        
        # W, b と inputと outputの関係を定義
        self.output = activation(T.dot(input, self.W) + self.b)
        self.params = [self.W, self.b]

class MLP(object):
  def __init__(self, rng, input, n_in, n_hidden, n_out):
      # 入力を一旦h(x)で変換
      self.hiddenLayer = HiddenLayer(rng = rng, input = input,
                               n_in = n_in, n_out = n_hidden,
                               activation = T.tanh)

      # h(x)で変換されたものを入力としてロジスティック回帰を行う
      self.logRegressionLayer = LogisticRegression(
                                  input=self.hiddenLayer.output,
                                  n_in=n_hidden,
                                  n_out=n_out)

ここで行っているのはあくまでもTheanoのシンボル同士の関係性を定義しているだけ。

そして、ここではL1とL2制約を用いる。

これは過学習を抑えるためだとおもう

      self.L1 = abs(self.hiddenLayer.W).sum() \
          + abs(self.logRegressionLayer.W).sum()

      self.L2_sqr = (self.hiddenLayer.W ** 2).sum() \
          + (self.logRegressionLayer.W ** 2).sum()

      self.negative_log_likelihood = self.logRegressionLayer.negative_log_likelihood
      self.errors = self.logRegressionLayer.errors

      self.params = self.hiddenLayer.params + self.logRegressionLayer.params

前回のロジスティック回帰では、負の対数尤度を最小化したが、今回は負の対数尤度 + L1制約 + L2制約を最小化する

"ロジスティック回帰に一段隠れ変数の層を加えたのはその変換によって線形分離可能にするため"

であったはずで、その結果の学習方法が

"ロジスティック回帰の負の対数尤度 + L1制約 + L2制約の最小化"

というのがちょっとピンとこない

単純に、"隠れ変数の層への変換で線形分離可能"になれば、負の対数尤度関数は小さくなるだろうし、モデルの自由度が増えた分、過学習しやすくなっているから制約条件を入れるって言うのもわかるけどあとでちゃんと確認した方が良さそう

ということで学習部分

      gparams = []
      for param in classifier.params:
          gparam  = T.grad(cost, param)
          gparams.append(gparam)

      updates = []

      for param, gparam in zip(classifier.params, gparams):
          updates.append((param, param - learning_rate * gparam))

          train_model = theano.function(inputs=[index], outputs=cost,
                                        updates=updates,
                                        givens={
                  x: train_set_x[index * batch_size:(index + 1) * batch_size],
                  y: train_set_y[index * batch_size:(index + 1) * batch_size]})

おわりに

前回は問題自体も解法自体も初めて扱ったのでやることが多かったけれど、今回はアルゴリズムがちょこっと変わっただけだったので前回の内容が理解できていれば比較的理解しやすかったのではないかと思いました。

ロジスティック回帰

ディープラーニングをやってみたいので
http://deeplearning.net/tutorial/intro.html
を読んでます。

自分なりにまとめていきます。

今回は
http://deeplearning.net/tutorial/logreg.html#logreg

ロジスティック回帰

ロジスティック回帰ってのはクラス分類問題の一種。

次元入力ベクトルが入力されたとき、それがどのクラスに所属するのかってのを識別する問題で、学習モデルに

を使う奴。

これはパラメタがで与えられたとき、入力番目のクラスである確率。

クラスが個なら、列の行列。

が入力されたとき、を最大にするを分類するとする。

回帰のよくあるパターンは学習データに対する尤度関数を最大化するとかそういうやつ。

今回のモデルの対数尤度関数は以下。

最小化問題にしたいから対数尤度関数に-1をかけてこのを最小化する事を考える。


が解ければそのときのを使えばいいんだけど、今後のことを考えて反復してパラメタを更新しながら最適化する方法をとる。

最急降下法確率的勾配降下法

でパラメタライズされる関数の値を最小化したいとき、以下の反復を適当な初期値から始めて繰り返すことでの最小値を与えるを得ることが出来る。

この方法が最急降下法

ところで、機械学習において、訓練データに対して、
みたいな形の関数がよく出てきてこれを最小化するってパターンがよくある。

これを最急降下法でとくと

となって、一回のパラメタの更新につきN回の偏微分が必要になる。

Nが10とか100ならまだしも何十万、何千万ってなってくると厳しい。

ということで、確率的勾配降下法

を、各に対してを順々に変えながらパラメタを更新していく。

ロジスティック回帰で言えば

となる。

Theanoを使ってロジスティック回帰を確率的勾配降下法で解く

データ

以下のデータを使用して手書き文字画像をクラス分類する。

http://deeplearning.net/data/mnist/mnist.pkl.gz

pythonでこのファイルを読むには以下を実行する

import cPickle
import gzip
f = gzip.open('mnist.pkl.gz')
train_set, valid_set, test_set = cPickle.load(f)
f.close()

手書き文字画像は28×28ピクセルのグレー画像で各ピクセルは0から1の値をとる(0は黒、1は白)。

データがどう格納されているのか確認する。
train_setは要素を二つもつタプル型で、1つ目は画像データの集合、2つ目はそれらの画像につけられたラベルの集合でこれらはどちらもnumpy.ndarray型

>> type(train_set)
tuple
>> train_images = train_set[0]
>> type(train_images)
numpy.ndarray
>> train_images.size / (28*28)
50000
>> train_labels = train_set[1]
>> type(train_labels)
numpy.ndarray
>> train_labels.size
50000
>> train_labels[0]
5 # <= 下の画像は数字の'5'を書いたもの
>> image_data = train_images[0]
>> image_data.shape
(784,)
>> image = image_data.reshape(28, 28)
>> import matplotlib.pyplot as plt
>> plt.imshow(image)
>> plt.show()

一つの画像は784(=28*28)要素のベクトルとして表される。これを入力とし、それに付けられたラベルをとして学習してを求める。

実装

まずは

から

import numpy
import theano
import theano.tensor as T

n_in = 28 * 28 # ベクトル化した入力画像の次元
n_out = 10     # 出力ベクトルの次元
input = T.matrix('x') # 

W = theano.shared(value=numpy.zeros((n_in, n_out), dtype=theano.config.floatX),
                  name='W', borrow=True)
b = theano.shared(value=numpy.zeros((n_out,), dtype=theano.config.floatX),
                  name='b', borrow=True)
p_y_given_x = T.nnet.softmax(T.dot(input, W) + b)

p_y_given_x_func = theano.function([input], p_y_given_x)

# Wの要素もbの要素もすべて0の状態で図示したデータが各クラスに分類される確率を表示してみる
p_y_given_x_func(train_images[0].reshape(1,784))
# => array([[ 0.1,  0.1,  0.1,  0.1,  0.1,  0.1,  0.1,  0.1,  0.1,  0.1]])

つぎはこれ

negative_log_likelihood = -T.log(p_y_given_x)

negative_log_likelihood_func = theano.function(inputs=[input, y], outputs=negative_log_likelihood[0,y])

# Wの要素もbの要素もすべて0の状態で図示したデータが入力され、それを数字の'3'だと分類したときの尤度を表示してみる
negative_log_likelihood_func(train_images[0].reshape(1,784), [3])
# => array([[ 2.30258509]])

最後にこれ


cost = negative_log_likelihood[0,y]
g_W = T.grad(cost=cost, wrt=W)
g_b = T.grad(cost=cost, wrt=b)
learning_rate = 0.1
updates =[(W, W - learning_rate * g_W), (b, b - learning_rate * g_b)]

ここまでくれば学習データをつかってパラメタを更新していく事が出来る。

train_model = theano.function(inputs=[input, y], outputs=cost, updates=updates)
i = 0
train_model(train_images[i].reshape(1, 784), [train_labels[i]])
i = i + 1

最後の二行を繰り替えしていくと新たな画像が入力されて、それをもとにパラメタが更新されていく。

3000枚ほどで学習してからふたたび図示したデータが各クラスに分類される確率を表示してみる。

 p_y_given_x_func(train_images[0].reshape(1,784))
# => array([[  3.79957900e-03,   1.98228237e-07,   1.53438291e-04,
#              1.91115501e-02,   1.97851087e-10,   9.45596439e-01,
#              3.04181820e-05,   2.23148970e-03,   1.02881405e-02,
#              1.87887465e-02]])

'5'に分類される確率が0.945で他より十分高くなっており、学習が出来ているっぽいことが確認できた。

まとめ

理屈と実装がどうなっているかを確認したかったのでとりあえず目標は達成できた。

本家にはどこまで学習を行うか/どこで学習をやめるか的な話もされていてコードにそれが実装されているのでそれも参照の事。

あと本家では最急降下法確率的勾配降下法の中間的な手法を使っている。

theanoの型についてもう少しちゃんと勉強しないと、サンプルにないことをやろうとする度につまづくので次回までにちゃんと勉強しようと思う。