強化学習では、戦略を重視するか(Policyベース)、価値を重視するか(Valueベース)が重要なポイントとなります。
この2つをシミュレーションするサンプルがありましたので実行してみました。
参考
Pythonで学ぶ強化学習 -入門から実践まで- サンプルコード
強化学習では、戦略を重視するか(Policyベース)、価値を重視するか(Valueベース)が重要なポイントとなります。
この2つをシミュレーションするサンプルがありましたので実行してみました。
参考
Pythonで学ぶ強化学習 -入門から実践まで- サンプルコード
前回作成した環境(環境構築に関する記事)を使ってマルコフ決定過程に従う環境を作成します。
構成要素は次の4つとなります。
コードにコメントをいっぱい書いてみましたので参考にしてください
1 | from enum import Enum |
上記で実装した環境を実行するためのコードは下記の通りです。
1 | import random |
environment_demo.pyを実行すると下記のような結果となりました。
(乱数を使ってるので実行するたびに結果は異なります。)
最初よくわからなったのですがエージェントの戦略で決めた行動が必ず実行されるわけではなく、その行動をもとに遷移確率を算出し、その確率に応じて実際の動作(Action)が決まるということです。
(Policyで決めた行動と違う方向に進んでいて「なぜなんだ?!」と悩んでました。。。)
今回の例では、まず戦略で決めた行動がUPだとしたらそれが実行される確率が80%で、残り20%の半分10%がRIGHTかLEFTが実行される確率に割り当てられるということになります。(反対行動のDOWNは0%)
機械学習の中で一番興味があった強化学習ですが、難しいのと時間がかかるので敬遠していたのですが少しづつ記事に書いていこうかと思います。
今回は環境構築編になります。
前準備としてWindows10環境にAnaconda3-2019.07-Windows-x86_64.exeとgitをインストールしておきます。
それが終わったら強化学習用の実行環境を構築します。
1 | conda create -n env3.6 python=3.6 |
全て問題なくインストールできたら下記のpyファイルを作成し実行します。
1 | import numpy as np |
ウィンドウが現れて、次のようなボールキャッチゲームが動いたら環境構築は完了です。
強化学習用の実行環境を終了する場合は次のコマンドを実行します。
1 | conda deactivate |
強化学習(環境構築編)は以上になります。お疲れ様でした。
CIFAR10とういうデータを使って一般物の認識をしてみます。
まずは必要なモジュールをインポートします。
1 | import numpy as np |
共通関数を定義します。
1 | # -------------- # |
一般物体認識のデータセットを読み込みます。
1 | # 一般物体認識のデータセットの読み込み |
どんなデータがあるかいくつか画像を出力します。
1 | # 試しに画像を出力 |
画像識別の問題に有効な畳み込みニューラルネットワークを設定します。
1 | # 2層畳み込みニューラルネットワークの設定 |
プーリング層を導入した関数を定義します。
1 | # プーリング層の導入 |
GPUを設定します。
1 | # GPUの設定 |
最適化手法を設定します。
1 | # 最適化手法の設定 |
学習の経過を保存する変数を宣言します。
1 | # 学習データ保存エリア |
一度に全部実行するとメモリオーバーになってしまうのでデータセットを分割して実行します。
CIFARのデータは50,000個の訓練データがあるので5,000個ずつを10回に分けて実行します。
1 | # シリアルイテレータの呼び出し |
いつくかのバッチと呼ばれる小さな塊に分けて学習を進める方法をバッチ学習といいます。
特にランダムにデータを選んでニューラルネットワークの最適化を進める方法を確率勾配法といいます。
確率勾配法による最適化を行います。
1 | # 確率勾配法による最適化 |
結果を表示します。
1 | show_graph(result[0], result[1], 'loss function', 'step', 'loss function', 0.0, 4.0) |
正解率は6割に届かない程度となりました。
ここから正解率を上げるために4層のニューラルネットワークにしていきます。
畳み込みニューラルネットワークのクラスを定義します。
1 | # 畳み込みニューラルネットワークのクラス作成 |
多段階の畳み込みニューラルネットワークと関数を定義します。
1 | # 多段階の畳み込みニューラルネットワーク |
GPUの設定を行います。
1 | # GPUの設定 |
結果を一旦初期化します。
1 | train_loss = [] |
最適化手法にAdamを設定します。
1 | # 最適化手法の設定 |
関数を定義します。
1 | def shift_labeled(labeled_data): |
確率勾配法で最適化し、結果をグラフ化します。
1 | from tqdm import tqdm |
2、3時間実行しているのですが、進捗が48%でまだ時間がかかるので一旦ここまでの結果を確認しておきます。
なんとか6割の正解率を超えてきているでしょうか。ただここまで時間がかかるのと正解率が微妙にしか上昇していないのでなんらかの改善の余地はあると思います。
5時間20分かかってようやく終了しました。
最終的な正解率は73%ですか。。。まー実用性があるといってもいいような気がします。
(Google Colaboratoryで動作確認しています。)
ニューラルネットワークで株価予測をしてみます。
まずは必要なモジュールをインポートします。
1 | import numpy as np |
データ振り分け処理、グラフ表示、回帰分析用の関数を定義します。
1 | # -------------- # |
株価予測の前に適当な関数で変換したデータを回帰分析してみます。
データを作成します。
1 | # 時系列データ作成 |
直前2回分データを入力データとして、入力データとラベルデータを作成します。
1 | # 直前2回分のデータを入力にする |
4層のニューラルネットワークを作成し、関数化します。
1 | # 4層ニューラルネットワークを作成し、関数化 |
最適化手法を設定します。
1 | # 最適化手法の設定 |
入力データとラベルデータをそれぞれ訓練データとテストデータに振り分けます。
ここでは先頭3分の1のデータを訓練データとします。
1 | # データ分割(3分の1を訓練データ、3分の2をテストデータとする) |
回帰分析を行います。200回学習します。
1 | train_loss, test_loss = learning_regression(model, optNN, data, 200) |
学習結果と予測結果をグラフ表示します。
1 | ytrain = model(xtrain).data |
予測がうまくいってない箇所もありますが、十分な結果がでているようです。
(実行するごとに微妙に結果が変わります。ニューラルネットワークの個性ということでしょうか。)
次に予測用の株価データを準備します。
(株価の読み込み期間を変えるニューラルネットワークの学習処理でエラーになることがあります。
異常値エラーとのことですが、理由がよくわからないので今回は何回か試して問題のなかった[2005/01/01-2007/12/31]を分析期間としました。)
1 | # 株価読み込み用モジュール |
読み込んだ株価データをグラフ化します。
1 | plt.figure(figsize=(12, 8)) |
株価データを入力データとラベルデータに振り分けます。
今回は直前5回分のデータを入力データとします。
1 | value_data = web_data['Close'] |
4層ニューラルネットワークを作成し、関数化します。
1 | # 4層ニューラルネットワークを作成し、関数化 |
最適化手法を設定し、訓練データとテストデータに振り分けます。
訓練データとテストデータは半分ずつに分けます。
1 | # 最適化手法の設定 |
回帰分析を行います。今回は学習回数を1000回に設定しました。
1 | train_loss, test_loss = learning_regression(model, optNN, data, 1000) |
誤差と予測結果を表示します。
1 | # 誤差の表示 |
いまいちな結果です。やはり株価を予測するのは無理なのでしょうか。
(Google Colaboratoryで動作確認しています。)
ニューラルネットワークを構築する際に、どの非線形関数を選択するかは大事なポイントとなります。
そこでどんな非線形関数があるかそれぞれグラフ化して確認します。
まずは必要なモジュールのインストールとx軸用に等間隔の数値を用意します。
1 | # モジュールのインポート |
非線形関数をグラフ化するコードは下記の通りです。
1行目のF.absoluteの箇所をそれぞれの非線形関数に置き換えて実行していきます。
1 | ydata = F.absolute(ndata).data # 非線形関数を変えながら実行する。 |
非線形関数一覧 | ||
---|---|---|
(Google Colaboratoryで動作確認しています。)
ニューラルネットワークで回帰分析をします。
まずは必要なモジュールのインポートと共通関数の定義をします。
1 | # モジュールのインポート |
データを準備します。
x軸を-5から5までの等間隔100個分準備し、その後適当な関数でy軸に変換します。
1 | # 等間隔の数値を用意する(x軸用) |
作成したデータを視覚化します。
1 | # 作成したデータをグラフ表示 |
ニューラルネットワークを作成し関数化します。
非線形関数はreluを使用します。
1 | # 回帰のニューラルネットワークを作成し、関数化 |
参考までに学習前のニューラルネットワークの状態を確認します。
まずは重さです。
1 | # ニューラルネットワークの状態調査 |
初期値としてでたらめな重さが設定されています。
次に勾配を表示してみます。
1 | # ニューラルネットワークの勾配表示 |
こちらはnan(Not a number)が設定されています。
最適化手法を設定し学習を行います。
1 | # 最適化手法の設定 |
学習結果の誤差は下記のようになります。
次第に減少してはいますが0.2から0.3付近で改善しなくなっています。
回帰の結果を比較します。
1 | # 回帰の結果を比較する |
結果はぜんぜん合っていないようです。なんらかの改善策をとる必要がありそうです。
非線形関数調整してみます。
今回使ったreluの形は下記のようになっており、今回学習するデータには合わないようです。
1 | # F.reluの形を調べる |
別の非線形関数sigmoidの形を調べると、reluよりは今回学習するデータに近いようにみえます。
1 | # F.sigmoidの形を調べる |
非線形関数をreluからsigmoidに変更し、同じように学習・結果表示を行います。
1 | # 回帰のニューラルネットワークを作成し、関数化 |
誤差に関しては2つの非線形関数でそれほど結果が変わりませんでした。
回帰の結果を確認してみます。
1 | # 回帰の結果を比較する |
reluよりはsigmoidを使った方がだいぶましになったように思われます。
ただまだまだ納得いく結果ではありません。
ほかの非線形関数を調べたところsin関数というのがありました。
1 | # F.sinの形を調べる |
この関数はかなり今回回帰分析を行いたいグラフと形が似ているようです。
非線形関数をsinに変更してまたまた回帰分析を行います。
1 | # 回帰のニューラルネットワークを作成し、関数化 |
誤差は十分に下がることが確認できました。
回帰の結果も確認してみます。
1 | # 回帰の結果を比較する |
完璧な結果がでました。
ただ今回はグラフの見た目でそれに近い非線形関数を使うという少しずるい手順を踏んでしましました。
relu関数のまま結果を改善することはできないのでしょうか。
試しにニューラルネットワークの階層を増やしてみました。
1 | # 4層のニューラルネットワークを作成し関数化 |
結果は以下の通りです。
誤差は十分に下がっています。
回帰の結果もなかなか良いものがでました。回帰分析の結果を改善するためには非線形関数を変更する方法とは別に、ニューラルネットワークの階層を深くするのも有効な手段だと言えそうです。
(Google Colaboratoryで動作確認しています。)
Google ColaboratoryではGPUを簡単に使うことができます。
メニューから[ランタイム]->[ランタイムのタイプを変更]で表示されるダイアログから[ハードウェアアクセラレータ]でGPUを選択するだけです。
またソースもGPU用にいくつか変更しなくてはなりません。
コメントに # GPU対応対応 を追加しておきましたので参考にしてください。
まずは必要なモジュールをインポートし、共通関数(グラフ表示、学習)を定義します。
1 | # モジュールのインポート |
GPU利用の設定を行い、ニューラルネットワークの作成と関数化を行います。
1 | # GPUの利用 |
ファッションMNISTのデータセットを読み込み訓練データとテストデータに振り分けます。
1 | # fashion_mnistのデータセット読込んでデータ分割 |
読み込んだデータを2つほど視覚化してみます。
1 | # データの視覚化 |
最適化手法を設定し、学習させてから、誤差と正解率のグラフを表示します。
1 | # 最適化手法の設定 |
正解率は8割程度となりました。悪くない数字かもしれませんがまだまだ改善の余地はありそうです。
(Google Colaboratoryで動作確認しています。)
ニューラルネットワークが複雑になり学習がうまくいかない場合にはバッチ規格化を行うといいです。
バッチ規格化を使うと途中の結果を整理しながら学習をすることができます。
またバッチ再規格化というものがあり、バッチ規格よりもよい性能を示すとのことなのでこの2つを比較してみます。
まずchainerよりMNISTデータを読み込みます。
1 | import chainer.datasets as ds |
次に読み込んだデータを訓練データとテストデータに分けます。
chainerにはconverくらすにconcat_examplesというメソッドがあり容易にデータの振り分けを行うことができます。
1 | # 入力データとラベルデータの設定 |
振り分けたデータの1つをmatplotlibを使って表示してみます。
1 | import matplotlib.pyplot as plt |
本題のバッチ規格化を設定します。
まずニューラルネットワークを作成する際にbnorm1=L.BatchNormalization(400)(9行目)を引数に渡し、またニューラルネットワークの関数でNN.bnorm1関数(15行目)をはさみます。
バッチ再規格をする場合は、BatchNormalizationの代わりにBatchRenormalization(10行目)を指定します。
1 | import chainer.optimizers as Opt |
次に学習用関数を定義します。
1 | # 学習用関数 |
グラフ表示用関数を定義します。
1 | # グラフ表示関数 |
最適化手法を設定し、学習を実行したのち結果をグラフ表示します。
1 | # 最適化手法の設定 |
バッチ規格化とバッチ再規格化をそれぞれ実行した結果は下記の通りです。
バッチ規格化 | バッチ再規格化 |
---|---|
ほぼ同じ結果で明確な違いがないような気がします。。。
(Google Colaboratoryで動作確認しています。)
ニューラルネットワークで手書き文字の認識をします。有名なMNIST(エムニスト)です。
まず必要なモジュールをインポートします。
1 | # モジュール読み込み |
MNISTデータを読み込み、確認のために画像として表示してみます。
1 | # MNISTデータの読み込み |
データ分割関数を定義し、実行します。今回、訓練データと学習データはちょうど半分ずつにしています。
1 | # データ分割関数 |
chainerの宣言をします。
1 | # chainerの宣言 |
ニューラルネットワークを作成し、ニューラルネットワークの関数を定義します。
また誤差と正解率の遷移を記録する変数を用意します。
1 | # 2層のニューラルネットワークを作成 |
最適化を行います。今回は200回学習を行います。
1 | # 最適化 |
グラフ表示用の関数を定義します。
1 | # グラフ表示関数 |
誤差と正解率の遷移をグラフ表示します。
1 | # 誤差と正解率をグラフ表示 |
順調に誤差が減少し、正解率が上昇していることが見てとれます。
ただ正解率が9割そこそこなのが少々不満です。
非線形関数や最適化手法を変えて改善する余地はありそうです。
(Google Colaboratoryで動作確認しています。)