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

データ分析エンジニアが気まぐれに更新するブログ

勉強してきた技術などを適当に書いていければと思います。

ブログ引っ越しました

ブログ引越しをしました。

新しいURL : http://www.ie110704.net/

もともとVPSサーバーを借りていて、このサイトで立てているデモページもその上で動かしていましたが、せっかくなのでWordpressも勉強してみて、サイト運営の形式にしてみました。
ということで、当はてなブログの方は、しばらくは公開設定のままにしておきますが、1か月後ぐらいには非公開にして終了しようと思います。
新しい方でも、引き続き適当に更新していこうと思いますので、今後ともよろしくお願い申し上げます。

Chainerでニューラルネットワーク及び畳み込みニューラルネットワークを実装してみた

Python ディープラーニング Chainer 機械学習

明けました。

本年もどうぞ、よろしくお願いいたします。


機械学習系のライブラリは去年から何かしら使ってみたいなーと思っていました。

今回、Chainerを触ってみまして、MNISTの画像認識アルゴリズムを、ニューラルネットワークと畳み込みニューラルネットワークで実装してみました。

ちなみに、他にもTensorFlowも使ってみたのですが、自分としてはひとまずChainerの方がコード感覚としてしっくりきましたので、こちらを使ってみました。


Chainer

Chainerはニューラルネットワークを実装するためのライブラリです。


Chainer : http://chainer.org/


コーディングが直感的でかつ柔軟性があり、基本的なニューラルネットワークディープラーニングの他にも、リカレントニューラルネットワークや畳み込みニューラルネットワークなど、様々なタイプのニューラルネットワークを実装することができます。

ただし、柔軟である分、ある程度、ニューラルネットワークの知識が求められる部分があるのかなーと思います。

ちなみにインストールが超楽チンです。


ニューラルネットワークを実装する

今回はMNIST画像を分類するモデルをニューラルネットワークで作ってみます。

有名な問題なので、説明はほぼ省略しますが、手書きの数字の画像を画像認識して何の数字が書かれているかを予測する問題です。

画像サイズは28ピクセルで、グレースケールですので、そのまま1次元配列化して、入力層数を28*28=784とします。

これを0〜9の数字を予測しますので、出力層数は10とします。

Pythonで書いたコードが以下になります。

import numpy as np
%matplotlib inline
import matplotlib.pylab as plt
from sklearn.datasets import fetch_mldata
import chainer
import chainer.links as L
import chainer.functions as F
from chainer import Chain, optimizers, Variable, serializers

mnist = fetch_mldata('MNIST original', data_home='.')
mnist.data = mnist.data.astype(np.float32) # image data 784*70000 [[0-255, 0-255, ...], [0-255, 0-255, ...], ... ]
mnist.data /= 255 # to 0-1
mnist.target = mnist.target.astype(np.int32) # label data 70000
N = 60000
x_train, x_test = np.split(mnist.data,   [N])
t_train, t_test = np.split(mnist.target, [N])

class NN(Chain):
    def __init__(self, n_in, n_out):
        super(NN, self).__init__(
            l1 = L.Linear(n_in, 1000),
            l2 = L.Linear(1000, 1000),
            l3 = L.Linear(1000, 1000),
            l4 = L.Linear(1000, n_out, initialW=np.zeros((n_out, 1000), dtype=np.float32))
        )
    def forward(self, x):
        h = F.relu(self.l1(x))
        h = F.relu(self.l2(h))
        h = F.relu(self.l3(h))
        h = self.l4(h)
        return h
    
model = NN(784, 10)
optimizer = optimizers.Adam()
optimizer.setup(model)

n_epoch = 5
batch_size = 1000
for epoch in range(n_epoch):
    sum_loss = 0
    sum_accuracy = 0
    perm = np.random.permutation(N)
    for i in range(0, N, batch_size):
        x = Variable(x_train[perm[i:i+batch_size]])
        t = Variable(t_train[perm[i:i+batch_size]])
        y = model.forward(x)        
        model.zerograds()
        loss = F.softmax_cross_entropy(y, t)
        acc = F.accuracy(y, t)
        loss.backward()
        optimizer.update()
        sum_loss += loss.data*batch_size
        sum_accuracy += acc.data*batch_size
    print("epoch: {}, mean loss: {}, mean accuracy: {}".format(epoch, sum_loss/N, sum_accuracy/N))
    
cnt = 0
for i in range(10000):
    x = Variable(np.array([x_test[i]], dtype=np.float32))
    t = t_test[i]
    y = model.forward(x)
    y = np.argmax(y.data[0])
    if t == y:
        cnt += 1

print("accuracy: {}".format(cnt/10000))
epoch: 0, mean loss: 0.5703720599412918, mean accuracy: 0.8343000013381243
epoch: 1, mean loss: 0.14626061742504437, mean accuracy: 0.9571833292643229
epoch: 2, mean loss: 0.08734064015249411, mean accuracy: 0.9738500038782756
epoch: 3, mean loss: 0.05790263672048847, mean accuracy: 0.9827499995628993
epoch: 4, mean loss: 0.03985198233276606, mean accuracy: 0.9882166624069214
accuracy: 0.9778


交差検証により、学習用の教師ラベル付きデータを60000件、学習したモデルの予測率をテストするデータを10000件として、学習・予測をさせた結果、正解率97.78%となりました。


畳み込みニューラルネットワークを実装する

次に畳み込みニューラルネットワークで解いてみます。

畳み込みの場合は、問題の場合に応じて、フィルター、パディング、ストライドといった畳み込みニューラルネットワーク特有の設定値に基づいて入力数を設定します。

今回はフィルターは5ピクセル、パディングとストライドは特に設定せずに実装しました。

import numpy as np
%matplotlib inline
import matplotlib.pylab as plt
from sklearn.datasets import fetch_mldata
import chainer
import chainer.links as L
import chainer.functions as F
from chainer import Chain, optimizers, Variable, serializers

mnist = fetch_mldata('MNIST original', data_home='.')
mnist.data = mnist.data.astype(np.float32) # image data 784*70000 [[0-255, 0-255, ...], [0-255, 0-255, ...], ... ]
mnist.data /= 255 # to 0-1
mnist.target = mnist.target.astype(np.int32) # label data 70000
N = 60000
x_train, x_test = np.split(mnist.data,   [N])
t_train, t_test = np.split(mnist.target, [N])

# to (n_sample, channel, height, width)
x_train = x_train.reshape((len(x_train), 1, 28, 28))
x_test = x_test.reshape((len(x_test), 1, 28, 28))

class CNN(Chain):
    def __init__(self):
        super(CNN, self).__init__(
            conv1 = L.Convolution2D(1, 20, 5), # filter 5
            conv2 = L.Convolution2D(20, 50, 5), # filter 5
            l1 = L.Linear(800, 500),
            l2 = L.Linear(500, 500),
            l3 = L.Linear(500, 10, initialW=np.zeros((10, 500), dtype=np.float32))
        )
    def forward(self, x):
        h = F.max_pooling_2d(F.relu(self.conv1(x)), 2)
        h = F.max_pooling_2d(F.relu(self.conv2(h)), 2)
        h = F.relu(self.l1(h))
        h = F.relu(self.l2(h))
        h = self.l3(h)
        return h

model = CNN()
optimizer = optimizers.Adam()
optimizer.setup(model)

n_epoch = 5
batch_size = 1000
for epoch in range(n_epoch):
    sum_loss = 0
    sum_accuracy = 0
    perm = np.random.permutation(N)
    for i in range(0, N, batch_size):
        x = Variable(x_train[perm[i:i+batch_size]])
        t = Variable(t_train[perm[i:i+batch_size]])
        y = model.forward(x)
        model.zerograds()
        loss = F.softmax_cross_entropy(y, t)
        acc = F.accuracy(y, t)
        loss.backward()
        optimizer.update()
        sum_loss += loss.data*batch_size
        sum_accuracy += acc.data*batch_size
    print("epoch: {}, mean loss: {}, mean accuracy: {}".format(epoch, sum_loss/N, sum_accuracy/N))

cnt = 0
for i in range(10000):
    x = Variable(np.array([x_test[i]], dtype=np.float32))
    t = t_test[i]
    y = model.forward(x)
    y = np.argmax(y.data[0])
    if t == y:
        cnt += 1

print("accuracy: {}".format(cnt/10000))
epoch: 0, mean loss: 0.6474141379197439, mean accuracy: 0.8123833332210779
epoch: 1, mean loss: 0.12804378295938174, mean accuracy: 0.9615333338578542
epoch: 2, mean loss: 0.08095948826521635, mean accuracy: 0.9747333298126857
epoch: 3, mean loss: 0.059256415938337643, mean accuracy: 0.9818999985853831
epoch: 4, mean loss: 0.047269358299672604, mean accuracy: 0.9852833330631257
accuracy: 0.986


今回はコーディング感覚について勉強しておきたかったため、さすがにこの問題例では精度が飛躍的に上がるというわけではないですが(元々良いですし)、若干精度向上しました。


以上、ニューラルネットワークと畳み込みニューラルネットワークをChainerで実装しました。

今回はやりませんでしたが、個人的には、リカレントニューラルネットワークも今度やってみたいなーと思っています。

時系列データか自然言語処理に適用してみたいですね。


最後に、今回勉強で参考にした書籍を挙げておきます。



「ゼロから作るDeep Learning」は、Chainerではないのですが、ニューラルネットワークや畳み込みニューラルネットワークの仕組みを理解するには、とても解りやすい書籍なので、オススメです。

Chinerは、ある程度ニューラルネットワークの仕組みを理解していれば結構動かせちゃいますので、ニューロンや入力層・隠れ層・出力層の構成、誤差逆伝播で重みを更新していくことなどの仕組みを抑えておくと良いと思います。

統計検定を受験してみた2

統計検定は毎年春秋と実施していて、準1級は春、1級は秋、2級以下の各レベルは春秋両方実施しています。

前回ダメだった準1級は春しか行わないので、「2級は大丈夫なのだろうか」と思い、この秋に2級を受けてみました。

前回はろくに勉強せず、直前に過去問を確認する有様でしたので、今回は念のため、下記の問題集を購入し、一通り解いておきました。



ひとまず、一通り解いてみましたところ...



実施正解数問題数
2013年秋3334
2014年春3134
2014年秋3535
2015年春3233
2015年秋3335



大丈夫すぎ。


ということで合否発表を迎えましたが、さすがに今回は問題なく、無事合格でした。


何か違った意味で面食らった気はしましたが、ひとまずは心置きなく準1級に向けて勉強を開始できそうです。

Open AI GymのFrozenLakeを強化学習(Q学習)で解いてみた

Python Open AI Gym 強化学習 機械学習

Google子会社DeepMind社により開発された、人工知能を搭載したコンピューター囲碁プログラム「AlphaGo(アルファ碁)」の活躍により、深層学習や強化学習の注目度がさらに上がった気がします。

このアルファ碁で使われている深層強化学習(Deep Q-Network)を実装してみようと思い、であれば、まずは強化学習を勉強したいということで、ひとまず今回は強化学習をPythonで実装してみました。


強化学習

強化学習(Reinforcement Learning)は、「ある環境内におけるエージェントが、現在の状態を観測し、取るべき行動を決定する問題を扱う機械学習の一種」です。(Wikipediaより)

機械学習の書籍では、機械学習の種類として「教師なし学習」と「教師あり学習」に加えて、3つ目に「強化学習」がよく挙げられていることが多い気がします。

ある環境内に置かれたエージェントは、環境の状態ごとに施策(行動を決定するルール)に基づいて行動を決定して、次の状態に遷移して報酬を受け取ります。

これを繰り返して、より多くの累積報酬を得られるような施策を学習する方法が強化学習です。


強化学習は結構以前から研究されているようで、書籍も古くからあるようです。

最近出版された書籍では、下記などは強化学習に関する概要が掴めて参考になります。




今回は、有限マルコフ決定過程な環境において、上記のような、より多い累積報酬を得る施策として、行動価値関数Qを用いた学習方法を実装します。


Q学習

エージェントが各状態において選択できる行動の中で、最も行動価値関数の値が高い行動を選択するように学習する方法をQ学習と言います。

学習は、エージェントが行動を選択し、遷移した状態とその時に得られた報酬を用いて、その時の行動価値関数Qの値を更新することを繰り返すことで行います。

例えば、エージェントの学習率が  \alpha 、割引率が  \gamma であるとして、状態  s_t のエージェントが行動  a_t を選択し、報酬  r を得て、状態が  s_{t+1} に遷移したとすると、このときの行動価値関数Qの値  Q(s_t, a_t) を次の式で更新します。


 \displaystyle Q_{s_t, a_t} \leftarrow Q_{s_t, a_t}+\alpha\{r_{t+1}+\gamma\max_{a_{t+1}}Q(s_{t+1}, a_{t+1})-Q(s_t, a_t)\}


学習率は、エージェントが行動の価値をどのくらいの割合で学習するかを表すパラメータであり、割引率は、将来もらえる報酬を現在の価値としてどのくらいの割合とするかを表すパラメータになります。

行動の選択は、今回は、基本的には行動価値関数の価値が高い行動を選びつつ、一定確率εでランダムに行動を選ぶように行動を選択させる、ε-greedy法という手法を用います。

他にもボルツマン分布に従う確率で行動を選択する方法などもあるようです。


ちなみに、冒頭で記した深層強化学習(Deep Q-Network)は、最適な行動価値関数Qを直接得ることが困難なくらい状態や行動が複雑な場合に、その関数の近似を得るためにディープラーニングを用いるといった手法になります。

これについてはまた後日まとめます。


Open AI Gym

上記のような強化学習のプログラムを実行するには、強化学習で解く問題(環境)をプログラム上で用意しなければなりませんが、そういった強化学習用の環境を提供するプラットフォームとして「Open AI Gym」というものがあります。


Open AI Gym : https://gym.openai.com/


Open AI Gymが提供してくれるのは、環境(行動空間、状態空間、報酬)のみです。

これに対して、強化学習アルゴリズムを自分で実装して、環境に試してみることで、強化学習プログラムの評価を行うことができます。


FrozenLakeをQ学習で解く

実際に、提供されている環境を選んで、Q学習で解いてみました。

今回はToy textの「FrozenLake-v0」という環境にしました。

簡単に環境の説明をすると、下記のような、4×4のマスを想定し、Sがエージェントの現在地、Fが道、Hが穴(落ちるとゲームオーバーでマイナス報酬を得る)、Gがゴール(ここに来て初めてプラス報酬を得る)で、Gに向かうようエージェントを行動(移動)させるといった、経路探索の問題になります。


SFFF
FHFH
FFFH
HFFG


Pythonで強化学習で解いたプログラムが下記になります。

本当はクラス化するべきだと思いますが、ひとまずは一直線に学習するだけの流れでコーディングしました。

import gym
import numpy as np
%matplotlib inline
import matplotlib.pylab as plt

env = gym.make("FrozenLake-v0")
n_obs = env.observation_space.n
n_act = env.action_space.n
q = np.zeros([n_obs, n_act])
epoch_cnt = 20000
max_steps = 200
epsilon = 0.001
gamma = 0.9
alpha = 0.9

rewards = np.zeros(epoch_cnt)
for epoch in range(epoch_cnt):
    pobs = env.reset()
    done = False
    for step in range(max_steps):
        pact = np.argmax(q[pobs, :])
        pact = np.random.choice(np.where(q[pobs, :] == q[pobs, pact])[0])
        if np.random.rand() <= epsilon:
            pact = env.action_space.sample()
        obs, reward, done, _ = env.step(pact) # return observation, reward, done, info
        if not done:
            q[pobs, pact] += alpha * (reward - q[pobs, pact] + gamma * np.max(q[obs, :]))
        else:
            q[pobs, pact] += alpha * (reward - q[pobs, pact])
        pobs = obs
        rewards[epoch] = reward
        if done:
            break
rates = np.average(rewards.reshape([epoch_cnt//1000, 1000]), axis = 1)
plt.plot(rates)
plt.savefig("result.png") 

f:id:itoeiji110704:20161214092652p:plain


最初はすぐに穴に落ちたり、なかなかゴールにたどり着けていませんが、エピソード数を重ねるごとに、だんだんとゴールするようになって報酬を得るように学習しています。


さて、今回はQ学習でFrozenLakeを解きましたが、他にもOpen AI Gymの中ですと、Atariのゲームなどは解いてみたいですね。

Deep Q-NetworkでQ関数に畳み込みニューラルネットワークを使うことになるのですが、処理性能もかなりの物が求められると思います。

GPUも必要になってくると思いますので、とりあえず一番の課題はお金ですね←

e-Stat APIをPythonから使って地理データを可視化してみた

Python データ可視化 Leaflet.js

e-Stat API

e-Stat : https://www.e-stat.go.jp/SG1/estat/eStatTopPortal.do

e-Stat API : http://www.e-stat.go.jp/api/


e-Statは日本政府が調査した統計データを閲覧・ダウンロードできるよう管理されたポータルサイトです。

このサイトからよく人口データなどをダウンロードして利用しており、以前からお世話になっているのですが、実はAPI機能も提供しているようで、前々から少し気になっておりましたので、この度、PythonからAPI経由でデータ取得をして、可視化するところまでをやってみました。


まずはAPI利用登録が必要ですが、こちらは簡単で、メールアドレスや所属などを入力するだけです。


ユーザー登録をし、ログインをしますと、アプリケーションIDを取得するページに行くことができます。



APIを自分が公開しているWebサイトから利用したい場合は、そのWebサイトのURLを入力します。

今回のように試験的に利用したいだけであれば http://localhost/ を入力しておけば良いようです。

これで「発行」を押すと、「appId」にアプリケーションIDが発行されます。

これをリクエストパラメータで渡すことで、データを取得することが出来るようです。


API仕様と公式ページの「提供データ」によれば、統計表情報取得「getStatsList」で統計表情報取得ができ、パラメータの政府統計コードは、例えば「国勢調査」は「00200521」とのこと。

これをリクエストパラメータに設定してあげてアクセスすると、これまでに調査された国勢調査のデータ一覧が確認できます。

取得したい国勢調査のデータのデータIDを確認して、統計データ取得「getStatsData」でアクセスすると、項目などの情報とデータが取得できます。

今回は、項目の絞り込みなどのパラメータも確認して、「getSimpleStatsData」でCSVで取得することにしました。

CSVでデータを落としてきてしまえば、あとはPythonでデータ加工をするのと同じですので、適当にデータフレーム化しておきます。


folium

Pythonで地図を可視化する方法の紹介です。

過去記事で地図クライアントツールのLeaflet.jsを使ったことがありましたが、実はこれをPython上で動作させるモジュール「folium」というものがありますので、これを使います。


folium : https://github.com/python-visualization/folium



使い方がLeaflet.jsと似ていますので、分かりやすいです。

さらにgeojsonやtopojsonを読み込むことができますので、日本の都道府県データのgeojsonと、e-Stat APIからダウンロードして加工したデータフレームを紐付けて、このfoliumでLeaflet上に可視化します。

実装が下記になります。

import numpy as np
import pandas as pd
import urllib.request
import folium
from IPython.display import display

appid = "###################################"
api_version = "2.1"
base_url = "http://api.e-stat.go.jp/rest/{api_version}/app/".format(api_version=api_version)

"""
get_type = "getStatsList"
stats_code = "00200521" # 国勢調査
url = base_url + "{get_type}?appId={appid}&statsCode={stats_code}".format(
        api_version=api_version,
        get_type=get_type,
        appid=appid,
        stats_code=stats_code,
        )
print(url) # 確認して取得したいデータのIDを調べる
"""

"""
get_type="getStatsData"
stats_data_id="0003148596" # 最新の調査ID
url = base_url + "{get_type}?appId={appid}&statsDataId={stats_data_id}".format(
        api_version=api_version,
        get_type=get_type,
        appid=appid,
        stats_data_id=stats_data_id
        )
print(url) # 確認して取得したい項目パラメータを調べる
"""

# load data from e-stat api
get_type="getSimpleStatsData"
stats_data_id="0003148596"
cd_cat_01="0000" # 国籍->全て
cd_cat_02="0000" # 性別->男女
cd_cat_03="00710" # 集計地域->全域
lv_area="2" # 集計レベル->都道府県レベル
section_header_flg="2" # セクションヘッダー->無し
url = base_url + "{get_type}?appId={appid}&statsDataId={stats_data_id}&cdCat01={cd_cat_01}&cdCat02={cd_cat_02}&cdCat03={cd_cat_03}&lvArea={lv_area}&sectionHeaderFlg={section_header_flg}".format(
        api_version=api_version,
        get_type=get_type,
        appid=appid,
        stats_data_id=stats_data_id,
        cd_cat_01=cd_cat_01,
        cd_cat_02=cd_cat_02,
        cd_cat_03=cd_cat_03,
        lv_area=lv_area,
        section_header_flg=section_header_flg,
        )
d = urllib.request.urlopen(url).read().decode("utf8")
dlines = d.splitlines()[2:]
jcodes = []
names = []
populations = []
for line in dlines:
    line2 = line.replace('"', "").split(",")
    jcode = line2[8]
    name = line2[9]
    population = line2[13]
    jcode = jcode[0:2]
    population = int(population)
    jcodes.append(jcode)
    names.append(name)
    populations.append(population)
df = pd.DataFrame({"jcode" : jcodes, "name" : names, "population" : populations})
display(df)

# create leaflet map by folium
location = [39.702053, 141.15448379999998]
tiles="Stamen Toner"
zoom_start = 5
map = folium.Map(location=location, tiles=tiles, zoom_start=zoom_start)
map.choropleth(
    geo_path="japan.geojson",
    data=df,
    columns=["jcode", "population"],
    key_on="properties.JCODE",
    threshold_scale=[10000, 50000, 100000, 150000, 200000, 300000],
    fill_color="YlGnBu", fill_opacity=0.7, line_opacity=0.2)
display(map)


データをただ可視化するだけであれば他のツールでも構いませんが、データ分析のライブラリが豊富なPythonで可視化ができましたので、何かしら分析をした地域データを可視化する場合には、この環境は便利ではないかなと思います。


ちなみに、今回一番苦戦したのは、データ加工とかでもなく、foliumとかでもなく、e-StatのAPIの使い方を理解することでした笑

もともと細かく項目や集計単位を設定してダウンロードできるサイトでしたから、これをAPIにするとなると、かなりのパラメータを渡さないといけなくなるだろうなぁと思っていました。

ある程度機能を制限して、妥協したデータを落としてくる形になるのかなーと思っていたのですが、まさかほとんどできるとは...。

初めて使う人は、とりあえず最初は普通にポータルサイトからデータをダウンロードしてくるところから慣れておいた方が、API使う時も混乱せずに済むのかなーと思います。

JupyterNotebookをリモートサーバーで起動してローカルブラウザ接続できるようにしてみた

Python

ちょっと作業環境的なお話。


私はいつもデータのクレンジングや分析、可視化はレンタルサーバーCentOS)上でやっていて、rbenvだったり、pyenvだったり、Rはコンソールから普通に使ったり、R-studio-serverでブラウザから使ったりしています。

Python分析者がよく扱うNumpyやMatplotlibなどの数値解析ライブラリも、手動でインストールして使っていたのですが、結構、周りの分析者やエンジニアは、Anaconda+JupyterNotebookの環境で作業をしていることが多いです。(というか私も会社PCのWindowsでは、Anaconda+JupyterNotebookを使いますが)

で、レンタルサーバーではpyenvを中心にPythonのバージョン管理をしていますが、pyenvからAnacondaを導入して、JupyterNotebookをサーバー起動して他端末のブラウザから分析できるようにしてみようということで、設定をしてみました。


pyenvは導入されていて、すでにPythonのバージョン管理を行っている状態とします。

下記コマンドでpyenvからAnacondaをインストールして、Anacondaを使用するディレクトリを作成します。(グローバルでも問題ないならばグローバルでもOK)

[] pyenv update
[] pyenv install anaconda3-4.0.0
[] pyenv rehash
[] pyenv versions
[] cd 任意のディレクトリ
[] pyenv local anaconda3-4.0.0
[] pyenv rehash
[] pyenv versions
[] python --version
Python 3.5.1 :: Anaconda 4.0.0 (64-bit)


この状態で、JupyterNotebookを実行すると、テキストベースのブラウザが立ち上がって、何だかよくわからない状態になります。(実行しても、終了コマンド[Q]で閉じることは可能です)

下記コマンドで、JupyterNotebookのconfigファイルを生成します。

[] jupyter notebook --generate-config
Writing default config to: <パス>/jupyter_notebook_config.py


このファイルを下記のように編集します。

<略>
143 ## The IP address the notebook server will listen on.
144 #c.NotebookApp.ip = 'localhost'
145 c.NotebookApp.ip = '*' # ローカル以外から接続できるようにする
<略>
179 ## The directory to use for notebooks and kernels.
180 #c.NotebookApp.notebook_dir = ''
181 c.NotebookApp.notebook_dir = '<ディレクトリパス>'  # 開始ディレクトリ
<略>
182 ## Whether to open in a browser after starting. The specific browser used is
183 #  platform dependent and determined by the python standard library `webbrowser`
184 #  module, unless it is overridden using the --browser (NotebookApp.browser)
185 #  configuration option.
186 #c.NotebookApp.open_browser = True
187 c.NotebookApp.open_browser = False # ブラウザが自動で開かないようにする。これないとサーバー側でもテキストベースのブラウザが立ち上がってしまい、ブラウザをいちいち終了させないといけない
<略>
197 ## The port the notebook server will listen on.
198 #c.NotebookApp.port = 8888
199 c.NotebookApp.port = 9999 # ポート番号変更
<略>


以上の編集を終えて保存すれば、http://<サーバーのIPアドレス>:9999でJupyterNotebookに接続できます。


ちなみによくグラフをプロットとかしたくなりますが、画像はその場でプロットは出来ないため、下記コードのように画像保存して閲覧する形になるようです。

ちょっとここは不便...

import numpy as np
import matplotlib
matplotlib.use('Agg')
import matplotlib.pylab as plt

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

x = np.arrange(-5.0, 5.0, 0.1)
y = sigmoid(x)
plt.plot(x, y)
plt.ylim(-0.1, 1.1)
plt.savefig("hoge.png")

f:id:itoeiji110704:20161027101943j:plain



また、この状態ですとIPとポート番号が分かれば、誰でも接続可能です。

普通にポート番号などで制限をかける方法もありますが、JupyterNotebook自体にも、パスワードを設定することができます。


パスワードを設定するには、まずiPythonコンソールで、パスワードを作成する必要があります。

下記のようにしてiPythonコンソールを起動して、パスワードを作成します。

[] iPython

In [1]: from notebook.auth import passwd
In [2]: passwd()
Enter password:
Verify password:
Out[2]: 'sha1:3fe6951c5fa7:458ff078f0ad39a0b4a7cc8f07f33479e896975d' # これをコピー
In [3]:exit


そして、先ほどのconfigファイルを下記部分を編集してコピーしたパスワードを貼り付けます。

<略>
190 ## Hashed password to use for web authentication.
191 #
192 #  To generate, type in a python/IPython shell:
193 #
194 #    from notebook.auth import passwd; passwd()
195 #
196 #  The string should be of the form type:salt:hashed-password.
197 #c.NotebookApp.password = ''
198 c.NotebookApp.password = 'sha1:3fe6951c5fa7:458ff078f0ad39a0b4a7cc8f07f33479e896975d' # ここに貼り付け
<略>


保存して、http://<サーバーのIPアドレス>:9999にアクセスすると、パスワード入力のログイン形式になっていることが確認できます。



これでリモートサーバーでJupyterNotebookを起動して、ローカルブラウザからアクセスして分析できるようになりました。

Epoch.jsでリアルタイム可視化をしてみたかった

データ可視化 JavaScript

挫折系タイトル←


IoTっぽく、リアルタイムに送信されていくるデータを、Web上で可視化することは出来ないかと思い、少し調べてみましたが...。

ひとまず、こんなJavaScriptライブラリが見つかりましたので、紹介します。


Epoch.js : http://epochjs.github.io/epoch/


D3.jsベースの、リアルタイムにデータを可視化するためのライブラリのようです。

実際に少し使ってみました。

※画像をクリックするとページを移動します。

function init(){
  var line = new _line();
  line.draw();
  var area = new _area();
  area.draw();
  var bar = new _bar();
  bar.draw();
}
function _line(){
  this.targetId = "line";
  this.target = $("#" + this.targetId);
  this.width = this.target.parent().width();
  this.height = this.target.parent().height();
  this.time;
  this.data;
  this.type = "time.line";
  this.axes = ["left", "bottom"];
  this.ticks = {"time": 10};
  this.range = [-30, 30];
  this.val;
  this.nextData;
  this.update;
  this.timeout;
}
_line.prototype.draw = function(){
  this.time = (new Date()).getTime()/1000;
  this.val = 0;
  this.data = [{label: "layer 1", values: [{time: this.time, y: this.val}], range: this.range}];
  this.target = this.target.addClass("epoch my-colors")
    .css("width", this.width + "px")
    .css("height", this.height + "px")
    .epoch({
      type: this.type,
      data: this.data,
      axes: this.axes,
      ticks: this.ticks,
      range:{left: this.range},
    });
  var that = this;
  this.update = function(){
    that.time = (new Date()).getTime()/1000;
    if(that.val == 30){
      that.val -= 1;
    }else if(that.val == -30){
      that.val += 1;
    }else{
      var r = Math.random();
      if(r <= 0.5){
        that.val += 1;
      }else{
        that.val -= 1;
      }
    }
    that.nextData = [{time: that.time, y: that.val}];
    that.target.push(that.nextData);
    that.timeout = setTimeout(function(){
      that.update();
    }, 1000);
  }
  this.update();
}
function _area(){
  this.targetId = "area";
  this.target = $("#" + this.targetId);
  this.width = this.target.parent().width();
  this.height = this.target.parent().height();
  this.time;
  this.data;
  this.type = "time.area";
  this.axes = ["left", "bottom"];
  this.ticks = {"time": 10};
  this.range = [-30, 30];
  this.val;
  this.nextData;
  this.update;
  this.timeout;
}
_area.prototype.draw = function(){
  this.time = (new Date()).getTime()/1000;
  this.val = 0;
  this.data = [{label: "layer 1", values: [{time: this.time, y: this.val}], range: this.range}];
  this.target = this.target.addClass("epoch my-colors")
    .css("width", this.width + "px")
    .css("height", this.height + "px")
    .epoch({
      type: this.type,
      data: this.data,
      axes: this.axes,
      ticks: this.ticks,
      range:{left: this.range},
    });
  var that = this;
  this.update = function(){
    that.time = (new Date()).getTime()/1000;
    if(that.val == 30){
      that.val -= 1;
    }else if(that.val == -30){
      that.val += 1;
    }else{
      var r = Math.random();
      if(r <= 0.5){
        that.val += 1;
      }else{
        that.val -= 1;
      }
    }
    that.nextData = [{time: that.time, y: that.val}];
    that.target.push(that.nextData);
    that.timeout = setTimeout(function(){
      that.update();
    }, 1000);
  }
  this.update();
}
function _bar(){
  this.targetId = "bar";
  this.target = $("#" + this.targetId);
  this.width = this.target.parent().width();
  this.height = this.target.parent().height();
  this.time;
  this.data;
  this.type = "time.bar";
  this.axes = ["left", "bottom"];
  this.ticks = {"time": 10};
  this.range = [-30, 30];
  this.val;
  this.nextData;
  this.update;
  this.timeout;
}
_bar.prototype.draw = function(){
  this.time = (new Date()).getTime()/1000;
  this.val = 0;
  this.data = [{label: "layer 1", values: [{time: this.time, y: this.val}], range: this.range}];
  this.target = this.target.addClass("epoch my-colors")
    .css("width", this.width + "px")
    .css("height", this.height + "px")
    .epoch({
      type: this.type,
      data: this.data,
      axes: this.axes,
      ticks: this.ticks,
      range:{left: this.range},
    });
  var that = this;
  this.update = function(){
    that.time = (new Date()).getTime()/1000;
    if(that.val == 30){
      that.val -= 1;
    }else if(that.val == -30){
      that.val += 1;
    }else{
      var r = Math.random();
      if(r <= 0.5){
        that.val += 1;
      }else{
        that.val -= 1;
      }
    }
    that.nextData = [{time: that.time, y: that.val}];
    that.target.push(that.nextData);
    that.timeout = setTimeout(function(){
      that.update();
    }, 1000);
  }
  this.update();
}

ただし、ソースを見れば分かりますように、データはクライアント側(JavaScript)で適当に生成しているだけですので、これじゃただのアニメーション可視化と変わらないです。

これをサーバー側でデータを生成し続けて、それを通信取得しにいくとなると、従来であれば全件リロードになってしまいます。

それをリアルタイムに通信して可視化を更新していけるような作りにしたかったのですが、今はまだ手軽に出来るものではないのかもしれませんね...。

恐らく可視化云々よりも、通信技術の問題になりそうですし、それが可能になれば、普通のD3.jsのアニメーションでも色々と遊べそうな気はします。

引き続き、調査が必要ぽいです。