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")
転職して、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なのにホスト名がやたら長いとか、全然関係ない文字列も短縮しようとしちゃうとか、問題はありますが、とりあえず作ってデプロイするという目標は達成できたのでとりあえずよしとします。
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にしろって、まあそうなんですが、なんというか。。。
ExtensibleのCalendar Proというやつを使います。
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サーバのフォルダにおいてやるとカレンダーが表示されると思います。
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
今回扱うのは、隠れ変数の層が一つある多層パーセプトロン。
隠れ変数間や観測変数間の接続はないものとする。
これは、入力に非線形変換が施されたロジスティック回帰とみなせる。
なんで非線形変換なんかするかって言うと、線形分離可能にするため。
ここからは実装の話
入力をで変換して、
その結果をロジスティック回帰の入力にしてやるのだからまず
ここで、重みの初期値はの範囲からランダムにとるとうまくいくってことが知られているのでそれに従う。
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ならまだしも何十万、何千万ってなってくると厳しい。
ということで、確率的勾配降下法。
を、各に対してを順々に変えながらパラメタを更新していく。
ロジスティック回帰で言えば
となる。
以下のデータを使用して手書き文字画像をクラス分類する。
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で他より十分高くなっており、学習が出来ているっぽいことが確認できた。