Open AI Gym 初めの一歩

強化学習というのは、機械学習の一つの分野ですが、学習がうまくいっているのかどうか調べるのは難しい課題なんですよね。

そこで、Open AIのGymというツールがあります。

https://gym.openai.com/

なんか、ちょいちょい見る、トンカチみたいなのがゆらゆら揺れてる動画…。これだったんだw

やり方は、次のサイトにある通りですが

https://gym.openai.com/docs/

英語を読むのも「ええい!面倒!!」ということもあると思いますので、超簡単に記載しておきます。

pip install gym

でインスコします。

import gym
env = gym.make('CartPole-v0')
env.reset()
for _ in range(1000):
    env.render()
    env.step(env.action_space.sample()) # take a random action

これで走らせてみると、トンカチが一回転して、画面の外に消えちゃいますねw

他に、山を登る車、

'MountainCar-v0'

ってのもあります。さっきのgym.makeの中身を変えるだけです。

import gym
env = gym.make('MountainCar-v0')
env.reset()
for _ in range(1000):
    env.render()
    env.step(env.action_space.sample()) # take a random action

これも実行してみると、右往左往してまったく山を登りそうにない車が表示されます。
やはり、ランダムに何かを行ってみても何も結果は得られない… という人生の教訓を得た気になりますね!!

ちょっと進めて下記のようにします。

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

実行すると、ポールが倒れたり、画面の外に行ったりしません。

一瞬おお!と思いますが、落ち着こう。

 

observationというのをprintするようにしたので、次のようにprintされますが

[-0.06550298 -0.62622142 0.12089567 0.99951833]
[-0.07802741 -0.82273366 0.14088604 1.32759199]
[-0.09448208 -0.62964272 0.16743788 1.08210892]
[-0.10707494 -0.4370784 0.18908006 0.846299 ]
[-0.1158165 -0.24496922 0.20600604 0.6185299 ]
….
Episode finished after 16 timesteps

そもそも、この4つの数字なんだ!って話ですよね。

この答え、CartPoleのソースコード見ると書いてありました。(他のところにもかいてほしぃ…)
https://github.com/openai/gym/blob/master/gym/envs/classic_control/cartpole.py

Observation:
Type: Box(4)
Num Observation Min Max
0 Cart Position -4.8 4.8
1 Cart Velocity -Inf Inf
2 Pole Angle -24° 24°
3 Pole Velocity At Tip -Inf Inf

そして、そもそも終了条件ってなんなん??

って思うと、これもソースコード見ると書いてありました。

Episode Termination:
Pole Angle is more than ±12°
Cart Position is more than ±2.4 (center of the cart reaches the edge of the display)
Episode length is greater than 200
Solved Requirements
Considered solved when the average reward is greater than or equal to 195.0 over 100 consecutive trials.

1.ポールの角度(さっきのObservationの3つ目ですね)が±12を超える
2.カートのポジション(さっきのObservationの1つ目ですね)が±2.4を超える
3.エピソードの長さが200以上
4.解決されたと思われる 平均的な報酬が100回の連続したトライアルの中で195を超える

報酬は、1回進めて、倒れなかったら1もらえるので、200回エピソードを繰り返して、195回目まで倒れなければ、そのモデルが優秀ということで、もうOKってことなんですかね。

Actionは左に行くか、右に行くかだけですが、これにVelocityなどが加わって、どのぐらいのポジションを進むかが決定されるようです。

倒れなくなったように見えますが、そうではなく、倒れようとしたところで(doneがTrueになる)env.resetされるので、倒れなくなったように見えるだけです。

 

 

Python -(マイナス)記号をつける場所を注意しましょう

Pythonに限ったことではないと思うんですが。とりあえず、私は機械学習のことをやるまでは、あまり数学的なことにプログラミングで取り組んでいなかったので、今回思わぬところにはまってしまったので書いておきます。

下記のような式があります。

[(x[0],x[2],r*((-1)**(x[1]!=hoge))) for x in trainExamples]

これは、次のように分解できるかと思います。

trainExamples =[
                 [3,1,2],
                 [4,-1,1]
                 ]
hoge = 1
r = -1

for x in trainExamples:
    a = x[0]
    b = x[2]
    c = r* ((-1) ** x[1] != hoge)

    print(a, b, c)

次の

c = r* ((-1) ** x[1] != hoge)

部分なんですけど、

x[1] != hoge

はTrue かFalseなので、1か0ということだと思います。

つまりは -1**True か -1**Falseってことですね。

 

しかし、

-1**True

ってやっても

-1**False

ってやっても、答えは -1 なんです。ん?そもそも判定の意味ないぞー?

って思ってたら、元のコードをよく見ると

(-1)**True

(-1)**False

ってやらなきゃいけなかったんですね( ゚Д゚)

 

(-1)**True は -1

(-1)**False は 1

です。

 

-1**True、-1**False とか、1に1乗するか、1を0乗するかした後、マイナス1をかけているので、どちらも-1になるわけです。

カッコの場所でエライ違いだった。

はぁー、むずかし。

 

ちなみに、0乗とか?-1乗とか?についてさっぱり忘れてしまったので、次のサイトさんを参考にさせて頂きました。m(_ _)m

べき乗とは何か。ゼロ乗・マイナス乗・分数乗・無理数乗ってどういう意味?

https://atarimae.biz/archives/20521

 

PHPでデバッガを使うメリット 8選

弊社のスタッフさんに

「僕はPHPではデバッガ使わないです。」

って言われて衝撃を受けました。

私は自分の中でデバッガを使うのは当たり前!便利だし!と思っていたけれども、すぐに

「デバッガはここがいいんだよーん」

と力説もできなかったので、すごすごと引き下がりチキンなので「PHP デバッガ メリット」とかググってみました。

しかし、デバッガの使い方とかが上位に出てきて、あんまり「これだ!」ってのなかったので、自分で書いちゃいます。いつか同じことを聞かれた誰かが読んでくれるといいな~ と思います。

というわけで、私が思う、PHPでデバッガを使うメリットを、ランキング形式でお届けします♪

①var_dumpデバッグは(以下print_r、echoでも同じ)、var_dumpと書くのが面倒

ハエある第一位は、これ。

そして、書いたら消さないといけないのだ。それも面倒。うっかりコミットしちゃったりもする。どっかに書かれたvar_dumpが残っていやしないかい~??

②var_dumpデバッグは、見づらい。

型とか文字数とか…あと、階層になってるのを自分で解読しないといけないのがつらい。脳みそは別のことに使いたい。

③var_dumpデバッグは、2つ以上の変数の中身を見る時に、自分で名前をつけて出力しないといけない。

あんまりピンとこないと思うので、そろそろコードを書くとしよう…。

$hogehoge = "ほげほげ";

$clilin = "クリリン";

print"これはhogehogeの分"; //これが面倒!
var_dump($hogehoge);

print"これはクリリンの分"; //これが面倒!
var_dump($clilin);

上記例のように、変数が$hogehogeと$ukikiの二つがあって、二つの中身を見たい時、var_dumpの前に、この変数は何の変数だよと説明しないと、いけない。

④var_dumpデバッグだと、関数やループなどがあった時のその後の状態を監視するのに、いちいちvar_dumpで出力しないといけない。

これも下記のコードサンプルで。

$hogehoge = "ほげほげ";

$hogehoge = changeString($hogehoge);

print"これはchangeStringの後のhogehoge";
var_dump($hogehoge);

$hogehoge = changeParameter($hogehoge);

print"これはchangeParameterの後のhogehoge";
var_dump($hogehoge);

関数などを通過した後の変数の中身を調べるために、その都度var_dumpを書かないといけないのです。面倒!!

⑤プログラムの流れや実行順序がわかる

難しい言葉でいうと、コールスタックがわかるってことです。意外と大事で、このメソッドどっから呼び出されてるんだろう~ とか、不具合の原因が上流のメソッドにある場合も多いからです。

下記のような関数もあるようですが、やっぱり書くのは面倒ですよね。

debug_backtrace()

⑥プログラム実行中に、変数の値を書き換えられる

上記の④までは、かなりアナログな説明で、駆け出しプログラマーでも理解できると思いますが、そろそろ、デバッガの真の実力について語る時が来たようだ
(`・ω・´)

ここでこんな値が来たら、こう動いてほしい… その動作を確実に把握できますし、意外な値が入ってきたとしたときの動作も確認できます。

⑦プログラム実行中に、ちょっとした式が書ける

Expressionsという機能で、とある変数に何かしたらどうなるんだろう?ということを、式を書いて検証できます。

式をプログラムに書いて、実行!とする試行錯誤の手間が省けますよね。

⑧プログラムの中身が自分で思った通りになっているか確認できる

えー、実のところ、私にはこれが意味深いかな。なんというか、いろんな変数、いろんな関数の動作が、意図したタイミングで意図した通りになっているか、確認したい。

そのために、デバッガを使うことが多いです。そうすると、安心もできますしね。

 


 

もちろん、デバッガは他にも機能があって、非常に便利なのですが、私が思うメリット8選は上記でした。

弊社では、スタッフさんに自由に開発してもらっているので、開発スタイルは個人に任せています。(新人に教えるのはまた別)

デバッガ使うのイヤって気持ちも、わからないでもないんですよね。
デバッガ起動するのに、多少の時間がかかりますからね。

PHPって高速だから、一瞬で実行できるし、ちょっとしたことを調べたいなら、画面出力デバッグでいいと思うんです。

なんで、最終的には早く、品質がいいものができればいいんですけどね。

Python リスト内包表記2 リストの中に複雑な関数を書く

以前、

Python リストの中に関数を書く

って投稿を書きましたが、リスト内包表記の例文として出している

counts = [self.Nsa[(s, a)] if (s, a) in self.Nsa else 0 for a in range(self.game.getActionSize())]

について、掘り下げていなかったので、掘り下げますね!

分解すると、リスト内包表記は

[リストの中身になるもの for i in リストの中の要素の数]

です。

なので、一見複雑なこの式は

self.Nsa[(s, a)] if (s, a) in self.Nsa else 0

の三項演算子でのif文判定と

for a in range(self.game.getActionSize())

の要素の数の部分に分けられます。

なんというか、三項演算子の存在を忘れていたので、前半の書き方について、しばらくわかりませんでした(;^ω^)

この例題だとこの例題で完結しないので、簡単な例です。

s = 1
list = [1,2,3,4]

list2 = list if s in list else 0
print(list2) #[1, 2, 3, 4]が出力される

counts = [ list if s in list else 0 for a in range(10)]
print(counts) #[[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], ...] [1,2,3,4]が10個あるリストが出力される

Python 可変性オブジェクトの引数は、関数に渡すと呼び出し側も変わってしまう

私は一番慣れている言語がJava, PHPなので、Pythonのこの流儀になれずによく間違ってしまうので、書いておきます。

def changer(x,y):
    x = 2
    y[0] = ["hogehoge"]

x = 0
L = ["hage", "moke"]

changer(x , L)

print(x) # 0 が出力されます
print(L) # [['hogehoge'], 'moke'] が出力されます

 

xは整数で不変性オブジェクトなので上書きされません。Lはリストで可変性オブジェクトなので変化があります。

ちなみに、下記のように丸ッとリストを変更してしまうと、呼び出し側が変更されませんので、気を付けましょう。

def changer(x,y):
    x = 2
    y = ["hogehoge", "ukiki"]

x = 0
L = ["hage", "moke"]

changer(x , L)
print(x) # 0 が出力されます
print(L) #['hage', 'moke']が出力されます