備忘録的な

プログラミングや機械学習に関する備忘録

はてなブログで数式を書く時の疑問点

はてなブログでは,

[tex: e^{i\pi} + 1 = 0]

とかくと,
 e^{i\pi} + 1 = 0
と表示されます.

また,冒頭に

<p style="display: none;">[tex: ]</p>

と書いておけば,

\begin{align}
e^{i\pi} + 1 = 0
\end{align}

と書いても
\begin{align}
e^{i\pi} + 1 = 0
\end{align}
と表示されます.

ですが,

[tex: \sigma]

は,
 \sigma
となりますが,

\begin{align}
\sigma
\end{align}

は,
\begin{align}
\sigma
\end{align}
となります.

なんで?

\begin{align}
\delta
\end{align}

は,
\begin{align}
\delta
\end{align}
なのに.

論文:Reward function and initial values : Better choices for accelerated Goal-directed Reinforcement Learning

The International Conference on Artificial Neural Networks (ICANN) 2006 の論文.
hal.archives-ouvertes.fr

強化学習において,報酬と初期値の決め方大事だよね,という話.

2値報酬&一様初期値の場合

ゴール状態s_gの報酬がr_g,それ以外の報酬がr_\inftyのとき,もしr_g = r_\inftyであれば,
\begin{align} \forall s \forall a Q^*(s,a) = Q_\infty = \frac{r_\infty}{1-\gamma} \end{align}
となりますが,これはr_g \neq r_\inftyでも,s_gが十分遠ければ成り立ちます.
このとき,r_g < Q_\inftyだと学習できませんよね,とここまでは当たり前の話.

次に学習時ですが,Q値の更新式は,初期値をQ_iとすると,
\begin{array}
Q Q\left(s,a\right) &\leftarrow& Q_i + \alpha[r_\infty + (\gamma - 1)Q_i]\\ &\leftarrow& Q_i + \alpha(1-\gamma)(Q_\infty - Q_i)
\end{array}
と書け,ここから,

  •  Q_i \geq Q_\inftyなら,未訪状態を探索しやすくなり,学習初期の探索が進みやすい
  •  Q_i < Q_\inftyだと,探索済みの状態を選びやすいため学習が進みにくく避けた方が良い

ということが分かります.
言われてみれば確かにな,という感じなのですが,特に各状態で得られる報酬のばらつきが大きく,事前にその予測が難しい場合に, Q_i < Q_\inftyになっていないかということは,あまり今まで気にしていなかったので勉強になりました.

実験では,Gridworldにおいて, r_g=1, r_\infty=0としたときに, Q_i=0とするよりも Q_\infty < Q_i < r_gとした方が,学習初期の成績が良いよ,ということを言っているのですが,当然前記設定だと,いつまでも探索を続けることになり,学習後期のスコアは Q_i=0のときの方が断然よく,う~んという感じ.

連続値報酬&非一様初期値の場合

Progress estimatorという考え方が提案されていて,例えばGridworldのケースでは,状態 s s'の距離を d(s, s')としたときに,
\begin{align}
r(s, a, s') = -d^2(s', s_g)
\end{align}
とする方法が提案されていますが,これは袋小路から抜け出せなくなりやすいため良くない,代わりに


 \displaystyle r(s, a, s') = \beta e^{-\frac{d(s', s_g)^2}{2\sigma^2}}

とするのが良いと提案しています,が正直う~ん.

このケースではそりゃ良くなるでしょうけど,どの程度一般化できるのか疑問なのと,そもそもこんな風に適切な報酬を設定できるケースが現実の問題においてどのくらいあるのでしょう.

初期値の与え方についても同様に,Goal biasと称して


 \displaystyle Q_i(s, a) = \beta e^{-\frac{d(s, s_g)^2}{2\sigma^2}} + \delta + Q_\infty

を提案していますが,上記と同じ疑問が残ります.

なんか誤読しているのでしょうか...

scikit-learnのStratifiedKFoldの結果が0.19とそれ以前で変わる件

scikit-learnのStratifiedKFoldが0.18.2から0.19.0への変更で、実行結果が異なるようになりました。
それも、shuffle=Trueとしたときだけです。
StratifiedKFoldは識別の際にラベルの数の偏りを考慮した分割をするため有用ですが、
このことを覚えておいた方がよさそうです。

0.18.1 0.18.2 0.19.0
KFold 0.96 0.96 0.96
KFold(shuffle) 0.9533333 0.9533333 0.9533333
cross_val_score 0.9599673 0.9599673 0.9599673
StratifiedKFold 0.9599673 0.9599673 0.9599673
StratifiedKFold(shuffle) 0.9603758 0.9603758 0.9665033

検証用コードはこちら

from sklearn.utils import shuffle
from sklearn.datasets import load_iris
from sklearn.svm import LinearSVC
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import KFold, StratifiedKFold

iris = load_iris()
X, y = shuffle(iris.data, iris.target, random_state=0)

clf = LinearSVC(random_state=0)

score = cross_val_score(clf, X, y, cv=KFold()).mean()
print('KFold: {s}'.format(s=score))

score = cross_val_score(clf, X, y, cv=KFold(shuffle=True, random_state=0)).mean()
print('KFold (shuffle): {s}'.format(s=score))

score = cross_val_score(clf, X, y).mean()
print('cross_val_score: {s}'.format(s=score))

score = cross_val_score(clf, X, y, cv=StratifiedKFold()).mean()
print('StratifiedKFold: {s}'.format(s=score))

score = cross_val_score(clf, X, y, cv=StratifiedKFold(shuffle=True, random_state=0)).mean()
print('StratifiedKFold (shuffle): {s}'.format(s=score))

論文:Simple Nearest Neighbor Policy Method for Continuous Control Tasks

サマリー

ICLR 2018に投稿されてdouble-bline review中の論文。

  • 主張:強化学習の難しさには、タスク自体の難しさ最適化の難しさの2種類があり、これらは分けて考えるべきである
  • 提案:最近傍法に基づく最適化不要な強化学習手法
  • 考察
    • Double PendulumやCart Poleなどの有名なベンチマークテストは、学習不要な提案手法でも簡単に解くことができた。つまり、これらはタスク自体が簡単であると言える。強化学習手法の良し悪しを適切に評価するためには、難しいタスクで評価すべき
    • タスクを解くことができても、得られた方策の行動は実環境には適さないような極端なものになることがある。よって方策(行動)の質の評価も重要

Nearest Neighbor Policy Method

強化学習に最近傍法を適用した、パラメータ推定が不要な2種類の手法を提案

定式化
  •  D:トラジェクトリ(状態、行動、報酬の系列)の集合
  •  B \subseteq D:全トラジェクトリ集合のうちバッファに保存されるもの
  •  s_0:初期状態
  •  d(\cdot , \cdot ):距離関数
  •  \tau:報酬の閾値
NN-1
  • エピソード開始時に、初期状態 s_0^*の最近傍となるトラジェクトリ \hat{T}をバッファ Bから探す
  • 得られたトラジェクトリの行動 \hat{a_t}にノイズ \epsilonを加えたものを行動とする
  • テスト時にはノイズを付与しない
  • エピソード終了時、得られた報酬の和が閾値 \tauを超えていたら、そのトラジェクトリをバッファに加える
NN-2
  • NN-1と異なり、時刻tの状態と行動と累積報酬和のタプルを保存する
  • 各状態で最近傍のタプルを検索し行動を決める
  • NN-1同様、行動にはノイズを付与し、テスト時には付与しない
  • エピソード終了時、得られた報酬の和が閾値 \tauを超えていたら、すべてのタプルをバッファに加える
Trainable NN Plicy

本論文の趣旨とは異なるが、提案手法は、距離関数と閾値 \tauを学習させる余地がある。

Experiment Settings

Environments
  • タスクはSparse ReachreSparse Half-CheetahSparse Cartpole SwingupSparse Double PendulumSparse Mountain Car
  • Sparseとは、報酬を、成功したときだけ1とし、それ以外は0にするということ。タスクを難しくしている。
Evaluation Metrics
  • 評価指標はタスク成功率。さらに、アルゴリズムの効率の良さを評価するために、成功率があらかじめ定めた閾値を超えるまでの試行回数も評価する。
  • 加えて、方策(行動)の質を評価するために、行動のノルムの平均値も評価指標とする(なお、行動はすべて a\in \mathbb{R}^1
Algorithm Settings
  •  \tauは1、距離関数はユークリッド距離
  • ノイズはオルンシュタイン=ウーレンベック過程( \sigma =0.2)に従う。理由は予備実験で正規分布より良かったから
  • NN-2では過去3状態と現状態をつなげた状態を近傍探索に用いる。また、計算コスト削減のためバッファサイズを適宜決める

Results and Analysis

Performances of the NN-1 and NN-2 Policies
  • Sparse Mountain Carは全然ダメだった。これはタスクが難しいと考えられる
  • Sparse ReacherとSparse Double PendulumはNN-1,2ともに成功率9割を超えた。Sparse Cart PoleはNN-2でframe skip=4にしたら成功率ほぼ100%になった。難しいといわれるSparse Half CheetahもNN-1,2ともに成功率70%を超えた。
Perceived Quality of the Nearest Neighbor Policies
  • 最適化ありの手法では、行動の自然さを制約に加えることができるが、提案手法では難しい。そのためタスクは達成できてもおかしな挙動をする場合がある
  • 最適化ありの手法で、行動のノルムを明示的に正則項として加えた場合と比較すると、提案手法は行動のノルムが大きい(ただし比較対象はスパースではない報酬系
  • 行動の自然さを明示的に制約に加えない場合、最適化ありの手法でも提案手法でも、行動のノルムの大きさは大して変わらない

強化学習の教科書はSuttonの本や、「これからの強化学習」、「速習 強化学習」が有名ですが、私はこちらがお薦めです。説明が理解しやすく、サンプルコードはOctaveですが、別言語への再実装も容易だと思います。

「ゼロから作るDeep Learning」を読んだ

評判に違わずとても良い本でした。
副題に「Pythonで学ぶディープラーニングの理論と実装」とあるように実装を重視しており、単純パーセプトロンから多層パーセプトロン、畳み込みニューラルネットワークまで、丁寧な解説で実装方法を理解することができます。

本書の特徴の一つは、ニューラルネットワークの学習方法である誤差逆伝搬法を、一般的な合成関数の微分の連鎖律として数式ベースで説明するのではなく、「計算グラフ」という考え方で説明している点だと思います。
計算グラフは、下図のように計算の過程をノードとエッジで表したものです。詳細は本を読んでいただきたいのですが、計算グラフを用いることで、誤差逆伝搬法の仕組みとその実装イメージを理解しやすくなっています。

f:id:canard0328:20171002213108p:plain
シグモイド関数の計算グラフ

その他にも本書では、ソフトマックス関数実装時のオーバーフロー対策、重みの初期値はどうするのが良いか、CNNのプーリングのウィンドウサイズとストライドは同じ値にするのが一般的、など理論だけでなく、実装し使う上で役に立つ情報が満載です。

ただ、後半のディープラーニングの話にいくほど、紙面上での実装の説明は少なくなっていくので、Githubにあるソースコードを追う必要があります。欲を言えば、その分コードのコメントをもう少し充実させてほしいと思うかもしれません。

少し気になったのは、4章で、従来の機械学習は、人の考えた特徴量(SIFTやHOGなど)をSVMなどの入力としていたのに対し、ディープラーニングでは人の特徴量設計が不要で、人のアイディアが介在しない、という記述があることです。
画像認識の場合、SIFTやHOGのような特徴量を用いずに、ピクセルの値をそのまま入力として、従来に比べて高い精度を達成できたというのは事実ですが、それをもって、ディープラーニングが「特徴量の生成が不要」、「人のアイディアが介在しない」と言ってしまうのは適切ではない、ということも既に色々なところで言われていることであり、本書を読んだ初学者がそのような認識をもってしまわないか心配です。

もう1点、6章で過学習について述べており、過学習対策をしなかった場合、訓練データに対する認識精度はほぼ100%なのに対し、テストデータの認識精度は隔たりがある(70%強)が、Weidht decayを用いることで、訓練データの認識精度が100%にならず、テストデータの認識精度との隔たりが小さくなったと言っています。しかし、このときのテストデータの認識精度をみると70%強で、過学習対策をしなかったときとほとんど変わりません。
これでは、なぜ過学習がいけないのか、が十分に伝わらないのではないでしょうか。
とは言っても、自分でいろいろパラメータをいじってみると分かるのですが、本書で例題として取り上げているMNISTという手書き数字認識問題は、問題が簡単すぎて「過学習してしまうことにより、テストデータに対する認識精度が悪化してしまう」状態を作り出すのが難しいという問題があります。

と細かな点を挙げましたが、本書はディープラーニングの基礎を実装できるレベルで理解したい方に最適だと思います。


おまけ

4章で数値微分について説明し、数値微分は実装が簡単だが遅いので、という流れで5章の誤差逆伝搬法に入るのですが、どの程度遅いのかに関する言及がなかったので試してみました。
f:id:canard0328:20171002222537p:plain
横軸はログスケールの計算時間で、縦軸が認識精度です。計算に使用したPCはこちらのLG gramで、決して早いPCではありません。
精度がサチったあたりを拡大したのが次の図です。
f:id:canard0328:20171002222852p:plain
だいたい、認識精度97.5%のあたりでサチるのですが、そこまで到達するのにかかる時間が、数値微分では341秒かかったのに対し、誤差逆伝搬法では71秒でした。

軽量ノートPC LG gramのメモリ増強

長いこと持ち運び用のノートPC(1kg以下)を探していたのですが,気がついたらAmazonでLGのgramが安くなっていたので買ってしまいました.
このPCはメモリ4GBということで購入を躊躇していたのですが,以下の記事をみるとメモリの積み増しができそうだということで,メモリ(8GB)も合わせて購入.
togetter.com

japanese.engadget.com

結果としてメモリ増設上手くいきましたので,他の人の参考になればと思い分解,メモリ増設の記録を共有します.
(当然ですが作業は自己責任でお願いします)

こちらが購入品です.
f:id:canard0328:20170617170255j:plain
早速裏面を開けます.使うものはマイナスの精密ドライバー.最初もう少し大きい普通のドライバーを使おうとしたところ,上手くいかず塗装が少し剥げてしまったので,慌てて100円ショップに行って精密ドライバーを購入しました.
f:id:canard0328:20170618135301j:plain
精密ドライバーを使って滑り止めのゴムを外していきます.奥まで差し込んでほじくるようにするとボロンと外れます.ゴムの裏面は糊のようなものでベタついているので,元に戻せば再びくっついてくれそうです.
f:id:canard0328:20170618135411j:plain
四隅ではなく中央部のゴムも同様に外します.
f:id:canard0328:20170618135626j:plain
5箇所の滑り止めのゴムを外したら,次は3箇所ある目隠しシールを剥がします.ここはマイナスドライバーではなく,カッターナイフを使うのが良いと思います.隙間にカッターの歯を少し潜らせると切れに剥がすことができると思います.
この目隠しシールもはがした後も粘着性が残っているので,綺麗に戻すことができました.
f:id:canard0328:20170618135722j:plain
f:id:canard0328:20170618135749j:plain
すべてのネジ穴が見えている状態です.
f:id:canard0328:20170618135906j:plain
プラスドライバーでネジを外していきます.100円ショップで購入した精密ドライバーでは上手く外すことができず,ネジ山をバカにしてしまいそうだったため,手持ちのもう少し良さそうなプラスドライバーを利用しました.
f:id:canard0328:20170618140416j:plain
すべてのネジを外したら,手前側から隙間にマイナスドライバーを差し込んで,少しずつ蓋を外していきます.何箇所かあるフックを外していく感じです.
f:id:canard0328:20170618140703j:plain
ヒンジ側はこのように開きます.
f:id:canard0328:20170618140814j:plain
蓋が開きました.中央部に空いているメモリスロットが見えます.
f:id:canard0328:20170618140925j:plain
メモリスロットに拡大図です.
f:id:canard0328:20170618140938j:plain
ここに,このメモリを増設します.
f:id:canard0328:20170618141019j:plain
しました.切り欠きがあるので,特に悩むことはないと思います.
f:id:canard0328:20170618141429j:plain
蓋を閉じるまでに起動してメモリが増えていることを確認します.
4GB+8GB=12GBになっていることが確認できました.
f:id:canard0328:20170618141722j:plain

LG gramの13インチモデル,840gと軽量でメモリを増設しても10万円以下とコストパフォーマンス高いと思います.
皆様の参考になれば幸いです.

機械学習によるデータ分析まわりのお話

機械学習によるデータ分析について,できるだけ広く浅くまとめてみました.

アルゴリズムの詳細やツールの使い方などにはほとんど触れず,
実際にデータ分析を行う際の注意点などに重きをおいています.