18 Jun 2017, 07:51

Python で勾配降下法を使って線形の単回帰分析をしてみた

Python で勾配降下法を使って線形の単回帰分析をしてみた。

はじめに

Siraj Ravel さんの新しい動画シリーズ, “The Math of Intelligence”が始まった。

機械学習のための数学を毎週カバーしていくようだ。シラバスは以下。

数学に苦手意識があるので、おもしろそうなトピックだけ Coding Challenge に取り組んでいこうと思う。

Coding Challenge

初めのトピックは Gradient Descent(勾配降下法)。

昔、勉強したのだけれども、すっかり忘れていた。以下の本がとても今回役に立った。

Coding Challenge は Kaggle のデータで、勾配降下法を使って線形の単回帰分析をすること。

今回 チャレンジした Jupyter Notebook は以下です。

データセット

Kaggle のデータを使うようにとのことなので、入門用のデータセット, House Prices を使っていこうと思う。

とりあえずいちばん簡単な方法を試したいので、家の面積と価格の相関関係に注目することにした。

  • SalePrice - the property’s sale price in dollars. This is the target variable that you’re trying to predict
  • GrLivArea: Above grade (ground) living area square feet

データをプロットしてみると正の相関関係がありそうなので、直線が引けそう。

勾配降下法

本当は、偏微分の数式とか書きたいけれど、数式の書き方がよくわからなかった orz

というわけで、説明抜きでいきなりコードですみません m(._.)m

参考リンクだけはっておきます。この記事のコードを参考にしました。

def calcurate_mean_squared_error(x, y, b, m):
    total_error = 0
    n = y.size
    for i in range(n):
        prediction = m*x[i] + b
        total_error += (y[i] - prediction)**2
    return (1/n) * total_error

def step_gradient(x, y, b, m, learning_rate):
    m_grad = 0
    b_grad = 0
    n = y.size

    #Computes the gradients
    for i in range(n):
        # Partial derivative for m
        m_grad += -(2/n) * x[i] * (y[i] - ((m*x[i]) + b))
        # Partial derivative for b
        b_grad += -(2/n) * (y[i] - (m*x[i]) + b)

    # update m and b
    m = m - (learning_rate * m_grad)
    b = b - (learning_rate * b_grad)    
    return b, m

def gradient_descent_runner(x, y, initial_b, initial_m, learning_rate, num_iterations):
    b = initial_b
    m = initial_m
    past_errors = []
    for i in range(num_iterations):
        b, m = step_gradient(x, y, b, m, learning_rate)
        if(i % 100) == 0:
            error = calcurate_mean_squared_error(x, y, b, m)
            past_errors.append(error)
            print('Error after', i, 'iterations:', error)
    return b, m, past_errors

実験結果

学習率 0.0001 だと収束しなかった。

おかしい。もっともっと学習率を小さくしても収束しない。はじめはバグっているのかと思ったけど、そうではなさそうだ。

num_iterations = 1000
learning_late = 0.0001
print("Starting gradient descent at b = {0}, m = {1}, error = {2}".format(
    initial_b, initial_m, 
    calcurate_mean_squared_error(x, y, initial_b, initial_m)))
print("Running...")
[b, m, errors] = gradient_descent_runner(x, y, initial_b, 
                                 initial_m, learning_late, num_iterations)
print("After {0} iterations b = {1}, m = {2}, error = {3}".format(
    num_iterations, b, m, calcurate_mean_squared_error(x, y, b, m)))
Starting gradient descent at b = 0, m = 1.0, error = 38434358058.74041
Running...
Error after 0 iterations: 9.29730669048e+15

/home/tsu-nera/anaconda3/envs/dlnd/lib/python3.6/site-packages/ipykernel_launcher.py:6: RuntimeWarning: overflow encountered in double_scalars

/home/tsu-nera/anaconda3/envs/dlnd/lib/python3.6/site-packages/ipykernel_launcher.py:9: RuntimeWarning: overflow encountered in double_scalars
  if __name__ == '__main__':
/home/tsu-nera/anaconda3/envs/dlnd/lib/python3.6/site-packages/ipykernel_launcher.py:14: RuntimeWarning: invalid value encountered in double_scalars

Error after 100 iterations: inf
Error after 200 iterations: nan

いろいろ学習率をいじって試した結果、0.000000385 というよくわからない数が最も適切だった。なんだこりゃ?!

標準化をためす

coursera の Machine Learning で、フィーチャースケーリング を習ったことを思い出した。

講座の動画を見返したら多変数の場合を扱っていた。

説明変数が1つでも、有効なのかな?とりあえず、標準化を試してみた。

norm_x = (x - x.mean()) / x.std()

今回は、学習率 0.0001 という、馴染みの値で収束した。うまくいったようだ。

コスト関数の値も plot した。イテレーションを繰り返すごとに減っている。

14 Jun 2017, 11:36

三目並べ(tic-tac-toe) にモンテカルロ法を試した

三目並べ(tic-tac-toe) をモンテカルロ法をつかって学習させました。

元ネタは、これ。

この本のコードは Octave でかかれているのだけれども、それを Python にポーティングしてくれた人がいた。

自分はなにをしたかというと、このコードを OpenAI Gym の tic-tac-toe に対応させてみた(tic-tac-toe は非公式)

ただ移しただけだと動かなかった。それから 10 時間くらいでばっくをしていた。頭がおかしくなる。

コード

おまけ: プルリク奮戦記

じつは、OpenAI Gym の tic-tac-toe リポジトリにバグっぽいのをみつけた。

ピンチ!いやチャンス?

自分は他人の Github リポジトリにプルリクをしたことがないのだ。

まずは気さくな感じを醸し出して質問をしてみた。Hello!! :-)

1 分も立たずに返信が帰ってきたのでビビる。はえーよ!

feel free to provide a PR!!

もうこの言葉が理解できずにガクガクした。なにかへんな英語表現を使っちゃったかな??

大丈夫、PR は Pull Reqest のことだ。って、ぅえ〜〜〜〜〜。やったことないよ〜〜〜。

プルリクエストの作法を調べる。

  1. branch 経由
  2. fork repository 経由

検索上位に引っかかるのは、初心者は branch を切る方法が勧められている。

  1. 初心者向け Github への PullRequest 方法 - Qiita
  2. Pull Request の仕方 - Qiita

バカだから何を思ったのか、fork ポタンをポチリ。。。。。fork してしまったぁぁ!!!

とりあえず、バグの修正をして、fork した自分のリポジトリに push

そして、GUI 画面から PULL REQUEST !!!!

ふう、まつこと数分・・・ 10 分でレスポンスが帰ってきた。もう心臓に悪りーよ。

  • コメントの単語が適切でない
  • 冗長な for 文の回し方をしている

その通りです、なにもいうことはありません。 こっちも急いで修正して、再コミット!!

祈った。。。そうして待つこと 15 分、ついに Merge されました〜〜〜〜!!!

うらららららららららら〜〜〜〜〜〜〜!

10 Jun 2017, 07:49

実践的な強化学習のオンライン学習教材、Practical RL をはじめた

実践的な強化学習の教材、Practical RL をやりはじめた。

これはなに

HSE と Yandex SDA というロシアの大学?で教えられている強化学習のコースが、 オンライン学習のために英語とロシア語で公開されている。

教材は、オンラインで強化学習をするためのリソースのいいとこどりをしている。

各週のディレクトリに動画や pdf のリンクがかかれた README がある。

また、”実践” とつくだけに、各週の課題が Jupyter Notebook で与えられ、OpenAI Gym を活用する。

fast.ai の講座 が予想以上によかったので、実践的とかかれると期待してしまう。

強化学習のコースなのだが、最近の動向に合わせ Deep Learning(DQN) の内容も盛り込まれている。

Deep Learning のためのライブラリは、Lasagne というライブラリが推奨されている。 しかし、Tensorflow 版の課題ノートも公開されている。

シラバス

github の README から抜粋。詳しくは、元サイトへ。

前半で、強化学習の話題をカバーしている。中盤で、Deep Learning の話題が出てくる。後半はよくわからない。

week0 Welcome to the MDP
week1 Crossentropy method and monte-carlo algorithms
week2 Temporal Difference
week3 Value-based algorithms
week4 Approximate reinforcement learning
week5 Deep reinforcement learning
week6 Policy gradient methods
week6.5 RNN recap
week7 Partially observable MDPs
week 8 Case studies 1
week 9 Advanced exploration methods
week 10 Trust Region Policy Optimization.
week 11 Model-based RL: Planning

他のコースとの比較

他のコースと比較検討して Practical RL を進めようと思ったので、その理由も書いておく。

Practical RL のデメリットは、課題 の解答が公開されていないところ。

fork している人のリポジトリを覗いて自分の解答と照らし合わせようかと思ったけれども、 参考になりそうなリポジトリがあまり見つからなかった。

ということで、自分は途中で挫折する可能性が高いので、 このコースに挫折したら次にやることも考えておく。

Learning Reinforcement Learning - WildML

強化学習の主要な話題が Jupyter Notebook と共に提供されている。うれしいことに、解答付きだ。

ただいま作成中のようなのと、学ぶ方法が Sutton 本を読むことに偏っていたのでやめた。

しかし、Practical RL に行き詰まったらこっちにシフトしようかと考えている。

UCL Course on RL

ユニバーシティ・カレッジ・ロンドンの強化学習の講義資料

この動画は、Practical RL の中で何度も引用されていてみることが進められている。

なので、Practical ML を進めたらこの講義内容はカバーできるのと、 課題が見つからなかったので止めた。

追記: 2016 年のページ見つけた。課題もしたのほうにある。

CS 294: Deep Reinforcement Learning, Spring 2017

UC Barkeley の Deep Learning と強化学習を学ぶ講義資料。

強化学習に Deep Learning を使う。ライブラリは Tensorflow.

講義動画がしっかりそろっている。

課題は 4 つある。掲示板も Reddit を使っていて課題につまったときは検索できそう。

これを選ばなかった理由は、Practical RL よりも難しそうな印象を受けたから。 Deep Learning も 強化学習もわからないのに、それを合わせた内容をバンバン紹介されても消化できなさそうだ。

また 、Practical RL からもこのコースの動画や課題(hw3)が紹介されていたので、 うまくつまみぐいできるかと思ったから。

UC Barkeley CS294 Deep Reinforcement Learning

2015 年の Barkeley の Deep Reinforcement Learning

2017 年版の元になったコースだ。内容は、短くまとまっている(4つ??)

UC Berkeley CS188 Intro to AI

2014 年の Berkeley の AI コース。

レクチャー動画や課題はしっかりしている。Project もおもしろそうだ。

扱っているものは、基本的なもので Deep うんたらはでてこないので少し古さは感じる。

An introduction series to Reinforcement Learning (RL) with comprehensive step-by-step tutorials.

Git-Base のチュートリアリル。提供元団体不明。個人かな?

このチュートリアルも作成中のようだが、前半はほぼ完成している。

README とコードベースのチューとリアルなので、 それほど英語力や数学力を求められない、今までで一番やさしそうな印象を受ける。

扱っている話題は、Q-Learning, OpenAI Gym, DQN, Deep Convolutional Q-Learning, ROS などなど、おもしろそうなものばかりだ。

Udacity Reinforcement Learning

MOOC の大御所 Udacity が提供しているコース。

短い動画がたくさんある。二人の掛け合いによって動画が進んでいき、 とっつきやすい感じはある。

課題はなく、短いので、理解が足りないところを補足する目的で見てもみてもいいかも。

Reinforcement Learning: An Introduction - Richard S. Sutton and Andrew G. Barto

言わずと知れた、強化学習のバイブル。これを腰を据えて読むのがいいのかも。

第2版には、サンプルコードが提供されているようだ。ただし、解答はなし。

Udemy Artificial Intelligence A - Z

Udemy の AI コースも, 最新の話題を扱っている。セールのときに 1200 円で買った。

Module 1: "Self Driving Car"
Module 2: "Deep Convolutional Q-Learning"
Module 3: "A3C"

なんと、フレームワークは Pytouch を使う。

  • Module1 では、シミュレータ上の車のライントレースを DQN で行う。
  • Module2 では、OpenAI Gy 上で Deep Convolutional Q-Learning を学ぶ。
  • Module3 では、A3C という発展的な話題を扱う。

これで、1200 円だ。安いと思う。

Simple Reinforcement Learning with Tensorflow

TensorFlow による 強化学習の実装チュートリアル。

以下は内容。コードはここ https://github.com/awjuliani/DeepRL-Agents

  • Part 0 — Q-Learning Agents
  • Part 1 — Two-Armed Bandit
  • Part 1.5 — Contextual Bandits
  • Part 2 — Policy-Based Agents
  • Part 3 — Model-Based RL
  • Part 4 — Deep Q-Networks and Beyond
  • Part 5 — Visualizing an Agent ’ s Thoughts and Actions
  • Part 6 — Partial Observability and Deep Recurrent Q-Networks
  • Part 7 — Action-Selection Strategies for Exploration

09 Jun 2017, 23:09

OpenAI Gym FrozenLake-v0 に 遺伝的アルゴリズム(deap)を試す

タイトルのとおり、OpenAI Gym FrozenLake-v0 に遺伝的アルゴリズムを試しました。

遺伝的アルゴリズム

遺伝的アルゴリズムとは

遺伝的アルゴリズム(genetic algorithm, 略して GA)とは、 組み合わせ問題の最適解を求めるためのアルゴリズム。

具体的には、以下の 2 〜 4 を繰り返すアルゴリズム。

1. 初期設定
2. 選択
3. 交叉
4. 突然変異

詳しくは、他のサイトに説明は譲る。この記事がわかりやすかった。

Python で遺伝的アルゴリズムやるなら Deap

Python で遺伝的アルゴリズムやるなら Deap というライブラリがある。

遺伝的アルゴリズムにも、選択、交叉、突然変異にいろいろな酒類がある。

この辺の話は, Wikipedia が詳しい。

それらをうまく抽象化して扱えるのがこのライブラリ。

解説記事はネット上にたくさんあるのだが OneMax 問題を扱っているものが多い。

なので、OpenAI の FrozenLake-v0 を題材に Deap を使ってみた。

FrozenLake-v0

GA の前に乱数を発生させて、そのポリシーにしたがって進んでいく例を書く。

結果は、こちら。https://gym.openai.com/evaluations/eval_iinFj8fUSvOOYHWXwjjAw

Score が 0.56 で、基準の 0.78 をオーバーしていない。

deap を使った GA

結果は、こちら。https://gym.openai.com/evaluations/eval_uU3CIG5yTgOhOXVM8EG3wA

Score が 0.81 と、基準値をなんとかクリアした。

deap をつかわない GA

結果は、こちら。https://gym.openai.com/evaluations/eval_YpHI9YmhQoSGcKMI1dFsw

なんと Score は 0.91 だ。

ということは、自分の deap をつかった実装はどこかバグってるなぁ?!

09 Jun 2017, 07:18

OpenAI Gym の CartPole-v0 を試したメモ

OpenAI Gym を試してみたメモです。

CartPole-v0 というゲームを動かしてみました。

OpenAI Gym

OpenAI Gym とは

OpenAI Gym is a toolkit for developing and comparing reinforcement learning algorithms.

OpenAI Gym は、強化学習アルゴリズムを開発し評価するためのツールキット。

  • gym … python テスト環境ライブラリ.
  • OpenAI Gym service … エージェントのパフォーマンスを評価するサービス。

ちょっとわかりにくいが、自分の解釈では、 いろんなゲームをプレイする環境が提供されていて、 自分の AI エージェントをつくってゲームをプレイして遊べるツール。

どんなゲーム(環境)があるの?

三目並べからシューティングゲーム、囲碁まである。詳しくは、以下。

Open AI Gym ライブラリのインストール

OpenAI Gym を pip でいれる。

pip install gym

以下のジャンルのゲームができるようになる。

  • algorithmic
  • toy_text
  • classic_control

CartPole で遊ぼう

公式のチュートリアルで紹介されている CartPole というゲームを触ってみる。

CartPole 問題

CartPole 問題は、強化学習の古典的問題らしい。

棒(pole)が動く台車(cart)の上に立っている。 棒は倒れるので、台車を右か左に動かして倒れないようにする。 より長く棒を直立させることが目的。

以下は、ルールの Google 日本語翻訳。

ポールは、作動していないジョイントによって、摩擦のないトラックに沿って動くカートに取り付けられる。システムは、カートに+1 または-1 の力を加えることによって制御される。振り子が起立して、それが転倒するのを防ぐことが目標です。ポールが直立したままのタイムステップごとに+1 の報酬が与えられます。エピソードは、ポールが垂直から 15 度を超えると終了するか、またはカートが中心から 2.4 ユニット以上移動すると終了します。
  • 1step の間に直立 h していれ報酬を 1 もらえる
  • 右(+1) or 左(-1)の行動を選択できる
  • 棒が 15 度傾くとゲームオーバー
  • 台車が中心から一定距離(2.4unit???) とゲームオーバー

コードを調べる

公式ドキュメントに乗っているコードを見ていく。

import gym
env = gym.make('CartPole-v0')
for i_episode in range(20):
    observation = env.reset()
    for t in range(100):
        env.render()
        print(observation)
        action = env.action_space.sample()
        observation, reward, done, info = env.step(action)
        if done:
            print("Episode finished after {} timesteps".format(t+1))
            break

まずは、gym というライブラリをインポートする。

gym.make(“環境名”) で動かす環境(env)を生成する。ここでは、CartPole-v0 を指定。

import gym
env = gym.make('CartPole-v0')

env の 3 つの主なメソッドは、

  • reset() - 環境(environment)をリセットする。最初の 観測(observation) を返す。
  • render() - 現在の環境の状態をレンダリングする。
  • step(a) - 行動(action)を実行し、以下を返す(new observation, reward, is done, info)
    • new observation - 行動を実行したあとの観測
    • reward - 報酬
    • is done - ゲームが終了したら True, 続いているなら False
    • info - 詳細情報

for 文で 20 エピソード分を実行する。

env.render()で以下のような図を表示する。

env.reset()を実行すると、以下の numpy 配列が帰ってくる。

# [position of cart, velocity of cart, angle of pole, rotation rate of pole]
array([-0.01717682,  0.00789496,  0.03295495, -0.0202679 ])

env.observation_space で、観測状態、env.action_space で、行動がそれぞれ帰ってくる。

Box は、n-次元の Box を、Discrete は固定された範囲の非負の数をそれぞれ表す。

print("observations:", env.observation_space)
print("actions:", env.action_space)
observations: Box(4,)
actions: Discrete(2)

CartPole-v0 では、observation は 4 次元の配列, action は 0 or 1 の数。

以下で、observation_space の上限、下限が分かる。

print(env.observation_space.high)
#> array([ 2.4       ,         inf,  0.20943951,         inf])
print(env.observation_space.low)
#> array([-2.4       ,        -inf, -0.20943951,        -inf])

以下で、action_space の数が分かる。

print(env.action_space.n)
#> 2

env.action_space.sample()で、action をランダムに選択できる。

env.action_space.sample()
#> 0
env.action_space.sample()
#> 1
env.action_space.sample()
#> 1

step()関数に action を渡すことで、action を実行できる。

done が True になるまで繰り返す。reward には、1.0 が入る。

結果を記録、アップロード

以下のように、wrappers.Monitor(env, ‘/tmp/cartpole-experiment-1’)を使うと、 実行結果を記録することができる。

import gym
from gym import wrappers
env = gym.make('CartPole-v0')
env = wrappers.Monitor(env, '/tmp/cartpole-experiment-1')
for i_episode in range(20):
    observation = env.reset()
    for t in range(100):
        env.render()
        print(observation)
        action = env.action_space.sample()
        observation, reward, done, info = env.step(action)
        if done:
            print("Episode finished after {} timesteps".format(t+1))
            break

以下のような mp4 の動画と meta 情報の json がディレクトリに作成される。

$ ll /tmp/cartpole-experiment-1
合計 32K
-rw-rw-r-- 1 tsu-nera tsu-nera  817  6 月  9 15:59 openaigym.episode_batch.0.13181.stats.json
-rw-rw-r-- 1 tsu-nera tsu-nera  412  6 月  9 15:59 openaigym.manifest.0.13181.manifest.json
-rw-rw-r-- 1 tsu-nera tsu-nera 2.0K  6 月  9 15:58 openaigym.video.0.13181.video000000.meta.json
-rw-rw-r-- 1 tsu-nera tsu-nera 4.0K  6 月  9 15:58 openaigym.video.0.13181.video000000.mp4
-rw-rw-r-- 1 tsu-nera tsu-nera 2.0K  6 月  9 15:58 openaigym.video.0.13181.video000001.meta.json
-rw-rw-r-- 1 tsu-nera tsu-nera 3.9K  6 月  9 15:58 openaigym.video.0.13181.video000001.mp4
-rw-rw-r-- 1 tsu-nera tsu-nera 2.0K  6 月  9 15:58 openaigym.video.0.13181.video000008.meta.json
-rw-rw-r-- 1 tsu-nera tsu-nera 3.8K  6 月  9 15:58 openaigym.video.0.13181.video000008.mp4

以下のようなスクリプトを実行すると、OpenAI Gym に結果をアップロードできる。

import gym
gym.upload('/tmp/cartpole-experiment-1',api_key='<api key>')

api_key は、OpenAI Gym にログインすると得られる。

$ python carpole_upload.py
[2017-06-09 16:09:39,697] [CartPole-v0] Uploading 20 episodes of training data
[2017-06-09 16:09:48,712] [CartPole-v0] Uploading videos of 3 training episodes (9058 bytes)
[2017-06-09 16:09:49,232] [CartPole-v0] Creating evaluation object from /tmp/cartpole-experiment-1 with learning curve and training video
[2017-06-09 16:09:49,618] 
****************************************************
You successfully uploaded your evaluation on CartPole-v0 to
OpenAI Gym! You can find it at:

    https://gym.openai.com/evaluations/eval_7fz1aflRO6alkzRRfWhQ

****************************************************

gist にコードをあげて、URL を取得する。

評価ページにいき、gist を貼り付ける。以下のリンクが今回の評価ページ。

追記

長い間プログラムを動作させると、manifest.json ファイルが生成されなかった。 そういう場合は、念のため env.close()を終了時に呼び出すことで、manifest.jssn が生成されるようだ。 参考にしたコードはここ.

01 Jun 2017, 04:41

Oracle Certified Java Programmer, Silver SE 8 認定資格に合格

Java SE8 Silver(Java SE 8 Programmer I 試験[1Z0-808]]) に 3 日間 で合格しました。

受験するまで

最近はディープラーニングの勉強をしているのだけれども、会社から脅された。

  • ディープラーニングが業務ににどう役に立つか説明してください。
  • ディープラーニングでどうやって会社に貢献するんですか?
  • Java の資格をさっさととらないとクビにするよ?

(´・ω・`)

ということで、Java SE8 Silver[1Z0-808]を勉強をはじめた。

Oracle はキャンペーンをやっていて、2017/5/31 までに受験すると、 一度落ちても再受験が無料のキャンペーンを実施中であることに、 528 の夕方に気づいた。

受験料がバカに高い(28728 円!)ので、落ちたらお金がもったいないし、 こっちは人生かかっているので、3 日間で死ぬ気で勉強することになった。

結果

82%の正解率で合格。(合格ライン 65%)

勉強内容

前提

Java の業務経験は、3 年前に 1 年程度。それから 2 年のブランク期間あり。

オラクル社主催の試験のランクは、ブロンズ、シルバー、ゴールドとあるけれども、 ブロンズは受けていない。

元々 Java の知識はあったので、問題をひたすら解くという対策方法をとった。

学習時間

528 の夕方 から 531 の夕方までなので、4 日間にわたるのだけれども、実質 72 時間。 そのうち、37 時間を Java に費やした。

  • (0 日目:528: 6 時間)
  • 1 日目 529: 13 時間 (黒本模試 66%, 68%)
  • 2 日目 530: 8 時間 (白本模試 75%)
  • 3 日目 531: 10 時間 (本番 82%)

参考書

売り出されている参考書は、黒本と白本と紫本の3つがある。

まず、一番評判がいい黒本を 2 周した。(1 日目)

まだ時間の余裕と心の不安があったので、白本を購入して一週した(2 日目)

最後の 3 日目は、黒本と白本の間違えたところを復習。

黒本

  • 解説が文章でかかれていて丁寧。
  • 問題の解説の前に概念の説明があるので、問題演習の延長で基礎の復習もできる。
  • 紙質が白本に比べていい。

白本

  • 解説に図を多様している。図解で直感的理解ができる。
  • 問題のすぐ下に解説があるので、解いてすぐに答えを確認できるようにつくられている。
  • 黒本よりもやさしい。
  • 出なさそうな問題もたまにある気がする。
  • アプリ版があるので、スマートフォンで満員電車のなかでも勉強できる!

紫本

  • 問題の前に、説明にベージが多く割かれている。
  • 体系的にまとまっているので基礎から学びたい人におすすめ。

受験する人へのアドバイス

どういう問題が出たかは、問題の情報漏洩になるのでいえないのだが、

黒本をやれ!!(。・ω・)ノ

そう、ひたすら黒本をやれば受かる。

自分のように白本に手を出す時間があるならば、黒本の問題を繰り返し解こう。

なぜなら・・・おっとこれは書けない。

26 May 2017, 16:28

Emacs で conda.el をつかって環境が切り替えられない時の対処方法

Emacs で、anaconda 環境を切り替える便利な eLisp に conda.el というものがある。

しかし、これがどうもうまく動かなかったので調査してみた。

環境

  • fish shell 2.5.0
  • Emacs 25.1.2

事象

conda-env-activate を使うと、切り替わったよというメッセージはでるが、 実際は Anaconda の root を使っている。指定した環境を使ってくれない。

原因

PATH に何が設定されてるか調べてみると。 (getenv "PATH")

"/home/tsu-nera/anaconda3/envs/kaggles/bin:/home/tsu-nera/.rbenv/shims:/home/tsu-nera/anaconda3/bin:/home/tsu-nera/.cask/bin:/home/tsu-nera/bin:/home/tsu-nera/go/bin:/home/tsu-nera/.rbenv/bin:/home/tsu-nera/script/scala:/home/tsu-nera/script/ruby:/home/tsu-nera/script/sh:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games:/snap/bin:/usr/lib/jvm/java-8-oracle/bin:/usr/lib/jvm/java-8-oracle/db/bin:/usr/lib/jvm/java-8-oracle/jre/bin"

つまり、二つのパスが登録されている。

  1. /home/tsu-nera/anaconda3/envs/kaggle/bin
  2. /home/tsu-nera/anaconda3/bin

使いたいのは、1 のほうで2はいらない。

なぜかわからないが、Emacs を起動すると、PATH を自動で読み込んでくれるようなのだ。

こんな機能しらないぞ!exec-path-from-shell をつかって実現する機能ではなかったか? デフォルトで PATH を引き継ぐように仕様変更されたのか??

解決方法

fish shell の環境設定ファイル(自分の場合は env.fish に分けている)、 から、anaconda のパスを削除すると、Emacs で conda-activate をつかった切り替えができる。

しかし、これでは、ふだん conda コマンドが使えない。

そこで、fish shell にこんな関数を定義した。まず Emacs を立ち上げた上で、conda を有効にする。

# anaconda
function conda-activate
    set fish_user_paths $HOME/anaconda3/bin $fish_user_paths
end

一時しのぎだけれども、これで Emacs から conda で環境を切り替えることができるようになった。

21 May 2017, 03:01

ディープラーニング(char-rnn)でバッハの音楽を生成してみた

前回、テキストで音楽を表現する方法 を調べた。今回は実際に char-rnn を利用して音楽を生成してみた。

まずは結果から

バッハのインベンションの No.1 - No.15 の全 15 曲を学習データにして、 char-rnn を利用して学習させた。結果がこちら。

無調音楽に近いような感じ。

実装

以下のサイトの記事を真似しました。

参考にした記事では、karpathy さんの char-rnn をそのままつかっていたけれども、 理解を深めるために Tensorflow で char-rnn を実装した。 といっても、Udacity で学んだことを応用しただけ。

その他、

  • テキスト形式としては、**kern 記譜法を採用。
  • 音楽を扱うライブラリは music21 を利用。

今回の Jupyter Notebook はコチラ。いつものようにコードを抜粋しながら説明。

KernScore からデータ取得

KernScores には、大量のクラシック音楽の **kern 記譜法でかかれた楽譜が置いてある。 まずは、バッハのインベンションの楽譜を取得した。

from urllib.request import urlopen
for i in range(1,15+1):
    filename = "inven{0:02d}.krn".format(i)
    file = urlopen("http://kern.humdrum.org/cgi-bin/ksdata?l=osu/classical/bach/inventions&file=%s&f=kern"%filename)
    with open("kernscore/bach/"+filename,'wb') as output:
        output.write(file.read())

データを char-rnn に食わせるために整形

データを見てみると余分なものがあることに気づく。これはいらない。

  • metadata
  • コメント
  • 小節記号
!!!COM: Bach, Johann Sebastian
!!!OPR: Inventio
!!!OTL: Inventio 1
!!!XEN: Two-part Inventions [for keyboard]
!!!ONM: No. 1
!!!ONB: In C major.
!!!SCT: BWV 772
!!!YEC: Copyright 1994, David Huron
!!!YEM: Rights to all derivative electronic formats reserved.
!! Performance duration: 1:29; performance tempo: 59.3 quarter-notes per minute.
!! (Andras Schiff, piano, "Two-Part Inventions BWV 772a-786; Three-part
!! Inventions BWV 787-801" Decca 411 974-2)
**kern  **kern
*staff2 *staff1
*clefF4 *clefG2
*k[]    *k[]
*C: *C:
*M4/4   *M4/4
*MM59.3 *MM59.3
=1- =1-
2r  16r
.   16c
.   16d
.   16e
.   16f
.   16d
.   16e
.   16c
16r 8g
16C .
16D 8cc
16E .
16F 8bM
16D .
16E 8cc
16C .

...

.   16b
=22 =22
1CC; 1C;    1e; 1g; 1cc;
==|!    ==|!
*-  *-
!!!CDT: 1685/3//-1750/7/28
!!!OCY: Deutschland
!!!AGN: polyphony
!!!AST: baroque
!!!AMT: simple quadruple
!!!SCA: Bach-Werke-Verzeichnis
!!!YOR: Bach Gesellschaft
!!!AIN: cemb; clavi
!!!EEV: 1.0
!!!RDT: 1986 November
!!!YER: 1995 May 22
!!!YEN: Canada
!!!EFL: 1/15
!!!EED: David Huron

これらを除去して、インベンション 15 曲を一つのファイルに出力する。

import glob
REP="@\n"
def trim_metadata(output_path, glob_path):
    comp_txt = open(output_path,"w")
    ll = glob.glob(glob_path)
    for song in ll:
        lines = open(song,"r").readlines()
        out = []
        found_first = False
        for l in lines:
            if l.startswith("="):
                ## new measure, replace the measure with the @ sign, not part of humdrum
                out.append(REP)
                found_first = True
                continue
            if not found_first:
                ## keep going until we find the end of the header and metadata
                continue
            if l.startswith("!"):
                ## ignore comments
                continue
            out.append(l)
        comp_txt.writelines(out)
    comp_txt.close()



output_path = "composer/bach.txt"
glob_path = "kernscore/bach/*.krn"
trim_metadata(output_path, glob_path)

char-rnn で音楽生成

ここからのコードは、Udacity で与えられたコードをほぼそのまま使った。 ハイパーパラメータは、以下のとおりに設定した。これで、学習時間が 30 分くらい。 曲の完成度からみると、これではすくないような気もする。

batch_size = 10
num_steps = 10 
lstm_size = 512
num_layers = 2
learning_rate = 0.001
keep_prob = 0.5

出力データのフォーマットを整える

出力データが 結構ぐちゃぐちゃに吐き出される。

  • 右手左手の 2 行だけでよいのに、3 行目がある。
  • 右手パートしかない
  • 左手パートしかない
  • 終端マークが途中で現れる。

これらを解消するために、**kern 記法に沿ったフォーマットで吐き出されるようにした。

ついでに、メタデータと小節番号の bar も追加。

r = []

r.append("**kern\t**kern\n")
r.append("*staff2\t*staff1\n")
r.append("*clefF4\t*clefG2\n")
r.append("*k[]\t*k[]\n")
r.append("*C:\t*C:\n")
r.append("*M4/4\t*M4/4\n")
r.append("*MM80\t*MM80\n")

bar = 1
for line in samp.splitlines():
    sp = line.split('\t')
    if sp[0] == '@':
        r.append("={bar}\t={bar}\n".format(bar=bar))
        bar += 1
    else:
        ln = len(sp)
        if ln == 1 and sp[0] != "":
            r.append(sp[0])
            r.append('\t')
            r.append('.')
            r.append('\n')
        elif ln == 1 and sp[0] == "":
            r.append(".")
            r.append('\t')
            r.append('.')
            r.append('\n')
        elif sp[0] == "*-" or sp[1] == "*-":
            continue
        else:
            r.append(sp[0])
            r.append('\t')
            r.append(sp[1])
            r.append('\n')

r.append("==|!\t==|!\n")
r.append("*-\t*-\n")

open("results/bach2ai.krn","w").writelines(r)

これで、楽譜が完成!

midi 吐き出し

あとは、これを midi に吐き出して再生するだけだ。

from music21 import *
m1 = converter.parse("results/bach2ai.krn")
m1.write('midi', fp='midi/bach2ai.mid')

Jupyter Notebook 上でも再生できる。

m1.show("midi")

おわりに

今回は、あまり満足のいく作品ができなかった。改善点としては、

  • char ベース見ていくのではなくて、音符単位(8a, 7b など)で音を見る
  • 左手、右手を分けて音を見ていく
  • 学習をもっと実施する、ハイパーパラメータの調整

などが考えられる。とはいえ、音楽をディープラーニングで生成できた。 ディープラーニングを学ぶ動機のひとつが音楽生成だったので、達成できて嬉しい。

音楽に関係する取り組みは、char-rnn ベース以外にもあるみたいだ。 今後は、これらを理解していくことにしよう。

19 May 2017, 07:49

テキストで音楽を表現する方法を調べた

テキストで音楽を表現する方法を調べた。

char-rnn で音楽生成したい

以前の記事で、char-rnn というものを試して、 坊ちゃんのテキストからテキスト生成をすることを試した。

これを音楽に応用してみたい。今回の記事はその準備として情報収集をした。

こういうテキストから LSTM を使って音楽を生成する試みは、いろんな人がやっている。 発端となっているのは、Andrej Karpathy 氏のブログ記事。

Andrej Karpathy さんは、Touch で char-rnn を実装している。

私は最近、TensorFlow や Keras による、char-rnn の実装を勉強した。

Keras が好きなので、勉強のために Keras で実装したいと考えている。

記譜法を調べた

ABC 記譜法

ABC 記法はわりと有名な記法。WikiPedia にも情報がある。

以下の特徴がある。

  • アスキー文字で音楽を表現
  • 関連ソフトウェアが豊富
  • 民族音楽に強い

ABC 記法から、五線譜に変換して再生する OSS.abcjs. これは凄い。

データベース・音源集

LSTM に応用した事例

**kern notation

マイナーな記法。

データベースにクラシック音楽の曲がたくさんあるのが魅力。

ツールとしては、music21 をつかう。これは、DeepJazz でも使われている。

music21 はデータベースが充実しているけれども、データ形式が XML 形式だから char-rnn には使えないかな。

LSTM に応用した事例

自力で midi から text 変換

そういう手法もある。欲しいデータがみつからなかったら、この手も使う。

18 May 2017, 23:06

ハイパーパラメータは可視化して決めよう!Keras で TensorBorad を使う

はじめに

とあるコンペに Keras で参加しているのだけれども、 ハイパーパラメータ、たとえば以下のようなパラメータ

  • 隠れ層のニューロンの数
  • ネットワークを何層にするか
  • 学習率

をどう決めていたかというと、直感である。

これでは、いけないなと最近気づく。もっと早く気付けよ。

モデルに最適なハイパーパラメータの決め方を調べてみた。

“ディープラーニング ハイパーパラメータ”を検索

みんな困っているようで、事例はすぐにみつかった。 たとえば、この記事なんて、まさに自分のこと!!

これはテストデータの精度に対して、試行錯誤を繰り返しながら決めていくしかありません。

StackOverFlow でも、

asically it is just trial and error. Those are called hyperparameters and should be tuned on a validation set (split from your original data into train/validation/test).

Tuning just means trying different combinations of parameters and keep the one with the lowest loss value or better accuracy on the validation set, depending on the problem.

ようは、総当たりしろ!!!ということか??

3 つの手法:

また、別のページでは、もっと効率のよい方法が紹介されている。大きく分けて、3 つあるようだ。

  • グリッドサーチ:

昔からある手法で、格子状の空間で最適なパラメータを探索する方法です。 格子の範囲を総当りするため、膨大な計算時間がかかるという課題があります。

  • ランダムサーチ :

無作為にパラメータを抽出して探索します。 グリッドサーチよりも計算時間が短くて済むというメリットがあります。

  • ベイズ最適化 (Bayesian Optimization) :

無作為にパラメータを抽出して探索します。 グリッドサーチよりも計算時間が短くて済むというメリットがあります。 ディープラーニングを含む機械学習の手法で、比較的良いハイパーパラメータを探索できることが知られています。

また、bengio 先生のおすすめレシピというのがある。

ハイパーパラメータはグリッドサーチするのではなく,ランダムサンプリングしたほうが性能が出る場合が多いよ

ベイズ最適化

ちょっとこのページは難しいが読み解くと、なにか得られそう。

ツール

scikit-learn には、GridSearchCV というものがあるらしい。いつか試そう。

hyperopt というツールもある。いつか試そう。

hyperopt の keras ラッパーもみつけた。いつか試そう。

調査結果

とりあえず、バカでも出来る全探索(グリッドサーチ)をする

目的

ここからが本題。

ニューラルネットワークのハイパーパラメータを調節したい。今回は簡単に以下の2つを調整。

  • 隠れ層の数
  • ニューロンの数

TensorBoard を Keras で使う

TensorBoard は、TensorFlow のログビューア。いい感じに可視化してくれる。

Keras で TensorBoard を使うには、Keras のコールバック機能を使う。 以下のように keras.collbacks.TensorBoard を利用する。

import keras
tbcb = keras.callbacks.TensorBoard(log_dir=log_filepath)

定義した変数を fit に渡す。

model.fit(trainX, trainY, epochs=30, batch_size=batch_size, validation_data=(validX, validY), callbacks=[tbcb])

これだけだ。簡単簡単。あとは log_filepath を指定して、tensorboard をコマンドラインから起動。port 6006 でアクセスできる。

$ tensorboard --logdir=[log_filepath]

参考: Keras から Tensorboard を使用する方法 - Qiita

複数のパラメータを追う

複数のパラメータをログするには、以下のように、log_dir に渡す文字列に変数を組み込む。

実例で示す。

import keras
from keras.models import Sequential
from keras.layers import Flatten, Dense, Dropout, BatchNormalization
from keras.optimizers import Nadam

batch_size=64

def get_model(num_layers, layer_size):
    model = Sequential()
    model.add(BatchNormalization(axis=1, input_shape=(256,3)))
    model.add(Flatten())
    for _ in range(num_layers):
        model.add(Dense(layer_size, activation='relu'))
        model.add(BatchNormalization(axis=1))
        model.add(Dropout(0.2))
    model.add(Dense(24, activation='softmax'))
    model.compile(Nadam(), loss='categorical_crossentropy', metrics=['accuracy'])
    return model

for num_layers in [2,3,4,5]:
    for layer_size in [64,128,265,512,1024]:
        log_string = 'logs/1/nl={},ls={}'.format(num_layers, layer_size)
        tbcb = keras.callbacks.TensorBoard(log_dir=log_string)
        model = get_model(num_layers, layer_size)
        model.fit(trainX, trainY, epochs=30, batch_size=batch_size, validation_data=(validX, validY), callbacks=[tbcb])

こうすると、logs/1/配下に nl=?,ls=? というディレクトリがそれぞれできて、そのなかにログがたまる。

$ tree 1
1
├── nl=2,ls=1024
│   └── events.out.tfevents.1495116649.letsnote-ubuntu
├── nl=2,ls=128
│   └── events.out.tfevents.1495115992.letsnote-ubuntu
├── nl=2,ls=256
│   └── events.out.tfevents.1495116161.letsnote-ubuntu
├── nl=2,ls=512
│   └── events.out.tfevents.1495116351.letsnote-ubuntu
├── nl=2,ls=64
│   └── events.out.tfevents.1495115856.letsnote-ubuntu
├── nl=3,ls=1024
│   └── events.out.tfevents.1495118093.letsnote-ubuntu
├── nl=3,ls=128
│   └── events.out.tfevents.1495117340.letsnote-ubuntu
├── nl=3,ls=256
│   └── events.out.tfevents.1495117513.letsnote-ubuntu
├── nl=3,ls=512
│   └── events.out.tfevents.1495117727.letsnote-ubuntu
├── nl=3,ls=64
│   └── events.out.tfevents.1495117193.letsnote-ubuntu
├── nl=4,ls=1024
│   └── events.out.tfevents.1495119950.letsnote-ubuntu
├── nl=4,ls=128
│   └── events.out.tfevents.1495119010.letsnote-ubuntu
├── nl=4,ls=256
│   └── events.out.tfevents.1495119233.letsnote-ubuntu
├── nl=4,ls=512
│   └── events.out.tfevents.1495119509.letsnote-ubuntu
├── nl=4,ls=64
│   └── events.out.tfevents.1495118840.letsnote-ubuntu
├── nl=5,ls=1024
│   └── events.out.tfevents.1495122077.letsnote-ubuntu
├── nl=5,ls=128
│   └── events.out.tfevents.1495121077.letsnote-ubuntu
├── nl=5,ls=256
│   └── events.out.tfevents.1495121291.letsnote-ubuntu
├── nl=5,ls=512
│   └── events.out.tfevents.1495121586.letsnote-ubuntu
└── nl=5,ls=64
    └── events.out.tfevents.1495120894.letsnote-ubuntu

これを tensorboard から見ると、accuracy, loss, valid loss, valid accuracy が見える。 以下の図は、valid accuracy.

闇雲にパラメータを調べるよりもよっぽど効率がよい。