RでProject Euler(随時更新)
Project Eulerはプログラムで解く数学の問題集です。
https://projecteuler.net/about
(日本語訳→http://odz.sakura.ne.jp/projecteuler/)
頭の体操のためにR言語でProject Eulerを解いていきます。
縛りは、基本のbaseパッケージのみとします。
低頻度ですが、随時更新していきます。
pandasのSeriesよりDataFrameの方が保存サイズが小さくなる
1列のpandasのデータをpickleで保存する場合。
そのとき、なぜかSeriesで保存するよりDataFrameで保存した方がサイズが小さくなることを確認した。
ちょっと条件いろいろ試して実験しメモ。
続きを読む新しい活性化関数「Mish」を使ってみた。
kerasのoptimizerの状態を保存する
背景
新規データに対して逐次的に毎日学習するkerasのモデルを考える。
シンプルなモデルであればmodel.save()
とload_model()
を使えばなんの問題もない。
参考: Kerasのノウハウ覚え書き
しかし、Lambda層が入るとmodel.save()
ができなくなる。
参考: KerasのLambda層でreshapeしたとき、保存に失敗する(場合がある)話
追加の学習はせず予測のためだけに使うモデルなら、save_weights()
とかで重みだけ保存しておけばよいが、毎日新規データを学習したいため微妙。
というのは、optimizerがmomentumなどの状態を持っている場合、それも保存しておかなければならない。
以下の記事を参考に、optimizerの状態の保存と読み込みを行って、各種実験をしてみる。
参考: python - Save and load model optimizer state - Stack Overflow
実験
準備
必要なライブラリのインポート
from sklearn import datasets from sklearn import preprocessing from keras.models import Model from keras.layers import Input, Dense from keras.utils import np_utils from keras.optimizers import SGD from keras import backend as K
データの準備
iris = datasets.load_iris() X = iris.data Y = iris.target X = preprocessing.scale(X) Y = np_utils.to_categorical(Y)
モデルを作ってコンパイルする関数
def make_model(momentum=0.0, decay=0.0, nesterov=False): inputs = Input(shape=(4,)) outputs = Dense(3, activation="softmax", kernel_initializer="ones", bias_initializer="ones", name="output_layer")(inputs) model = Model(inputs=inputs, outputs=outputs) model.compile(optimizer=SGD(lr=0.01, momentum=momentum, decay=decay, nesterov=nesterov), loss='categorical_crossentropy') return model
実験1
optimizerの状態がない場合の学習。
model = make_model() model.fit(X, Y, epochs=10, verbose=2, batch_size=200)
- 0s - loss: 1.0986 Epoch 2/10 - 0s - loss: 1.0890 Epoch 3/10 - 0s - loss: 1.0796 Epoch 4/10 - 0s - loss: 1.0704 Epoch 5/10 - 0s - loss: 1.0613 Epoch 6/10 - 0s - loss: 1.0524 Epoch 7/10 - 0s - loss: 1.0437 Epoch 8/10 - 0s - loss: 1.0351 Epoch 9/10 - 0s - loss: 1.0267 Epoch 10/10 - 0s - loss: 1.0185
実験2
optimizerの状態がない場合の学習。
epochs=1
の学習をfor文で回す。
実験1と同じ結果。
model = make_model() for i in range(10): model.fit(X, Y, epochs=1, verbose=2, batch_size=200)
Epoch 1/1 - 0s - loss: 1.0986 Epoch 1/1 - 0s - loss: 1.0890 Epoch 1/1 - 0s - loss: 1.0796 Epoch 1/1 - 0s - loss: 1.0704 Epoch 1/1 - 0s - loss: 1.0613 Epoch 1/1 - 0s - loss: 1.0524 Epoch 1/1 - 0s - loss: 1.0437 Epoch 1/1 - 0s - loss: 1.0351 Epoch 1/1 - 0s - loss: 1.0267 Epoch 1/1 - 0s - loss: 1.0185
実験3
optimizerの状態がある場合の学習。
model = make_model(momentum=1, decay=1, nesterov=True) model.fit(X, Y, epochs=10, verbose=2, batch_size=200)
Epoch 1/10 - 0s - loss: 1.0986 Epoch 2/10 - 0s - loss: 1.0795 Epoch 3/10 - 0s - loss: 1.0610 Epoch 4/10 - 0s - loss: 1.0414 Epoch 5/10 - 0s - loss: 1.0209 Epoch 6/10 - 0s - loss: 0.9997 Epoch 7/10 - 0s - loss: 0.9781 Epoch 8/10 - 0s - loss: 0.9563 Epoch 9/10 - 0s - loss: 0.9343 Epoch 10/10 - 0s - loss: 0.9125
実験4
optimizerの状態がある場合の学習。
epochs=1
の学習をfor文で回す。
実験3と同じ結果。
model
がoptimizerの状態を保持している。
model = make_model(momentum=1, decay=1, nesterov=True) for i in range(10): model.fit(X, Y, epochs=1, verbose=2, batch_size=200)
Epoch 1/1 - 0s - loss: 1.0986 Epoch 1/1 - 0s - loss: 1.0795 Epoch 1/1 - 0s - loss: 1.0610 Epoch 1/1 - 0s - loss: 1.0414 Epoch 1/1 - 0s - loss: 1.0209 Epoch 1/1 - 0s - loss: 0.9997 Epoch 1/1 - 0s - loss: 0.9781 Epoch 1/1 - 0s - loss: 0.9563 Epoch 1/1 - 0s - loss: 0.9343 Epoch 1/1 - 0s - loss: 0.9125
実験5
optimizerの状態がない場合の学習。
モデルの作成と学習を繰り返す。
lossが変化しない。
for i in range(10): model = make_model() model.fit(X, Y, epochs=1, verbose=2, batch_size=200)
Epoch 1/1 - 0s - loss: 1.0986 Epoch 1/1 - 0s - loss: 1.0986 Epoch 1/1 - 0s - loss: 1.0986 Epoch 1/1 - 0s - loss: 1.0986 Epoch 1/1 - 0s - loss: 1.0986 Epoch 1/1 - 0s - loss: 1.0986 Epoch 1/1 - 1s - loss: 1.0986 Epoch 1/1 - 1s - loss: 1.0986 Epoch 1/1 - 1s - loss: 1.0986 Epoch 1/1 - 1s - loss: 1.0986
実験6
optimizerの状態がある場合の学習。
モデルの作成と学習を繰り返す。
実験5と同様にlossが変化しない。
for i in range(10): model = make_model(momentum=1, decay=1, nesterov=True) model.fit(X, Y, epochs=1, verbose=2, batch_size=200)
Epoch 1/1 - 1s - loss: 1.0986 Epoch 1/1 - 1s - loss: 1.0986 Epoch 1/1 - 1s - loss: 1.0986 Epoch 1/1 - 1s - loss: 1.0986 Epoch 1/1 - 1s - loss: 1.0986 Epoch 1/1 - 1s - loss: 1.0986 Epoch 1/1 - 1s - loss: 1.0986 Epoch 1/1 - 1s - loss: 1.0986 Epoch 1/1 - 1s - loss: 1.0986 Epoch 1/1 - 1s - loss: 1.0986
実験7
optimizerの状態がない場合の学習。
モデルの作成と学習を繰り返す。
モデル作成後に重みを読み込む。
実験1や実験2と同じ結果。
for i in range(10): model = make_model() if i != 0: model.get_layer("output_layer").set_weights(weights_dense) model.fit(X, Y, epochs=1, verbose=2, batch_size=200) weights_dense = model.get_layer("output_layer").get_weights()
Epoch 1/1 - 1s - loss: 1.0986 Epoch 1/1 - 1s - loss: 1.0890 Epoch 1/1 - 1s - loss: 1.0796 Epoch 1/1 - 1s - loss: 1.0704 Epoch 1/1 - 1s - loss: 1.0613 Epoch 1/1 - 1s - loss: 1.0524 Epoch 1/1 - 1s - loss: 1.0437 Epoch 1/1 - 1s - loss: 1.0351 Epoch 1/1 - 2s - loss: 1.0267 Epoch 1/1 - 2s - loss: 1.0185
実験8
optimizerの状態がある場合の学習。
モデルの作成と学習を繰り返す。
モデル作成後に重みを読み込む。
optimizerの状態の違いのため、実験3や実験4と異なる結果。
for i in range(10): model = make_model(momentum=1, decay=1, nesterov=True) if i != 0: model.get_layer("output_layer").set_weights(weights_dense) model.fit(X, Y, epochs=1, verbose=2, batch_size=200) weights_dense = model.get_layer("output_layer").get_weights()
Epoch 1/1 - 2s - loss: 1.0986 Epoch 1/1 - 2s - loss: 1.0795 Epoch 1/1 - 2s - loss: 1.0703 Epoch 1/1 - 2s - loss: 1.0612 Epoch 1/1 - 2s - loss: 1.0523 Epoch 1/1 - 2s - loss: 1.0350 Epoch 1/1 - 2s - loss: 1.0266 Epoch 1/1 - 2s - loss: 1.0183 Epoch 1/1 - 2s - loss: 1.0102 Epoch 1/1 - 2s - loss: 1.0023
実験9
optimizerの状態がある場合の学習。
モデルの作成と学習を繰り返す。
モデル作成後に重みとoptimizerの状態を読み込む。
実験3や実験4と同じ結果。
for i in range(10): model = make_model(momentum=1, decay=1, nesterov=True) model._make_train_function() if i != 0: model.get_layer("output_layer").set_weights(weights_dense) model.optimizer.set_weights(weights_optimizer) model.fit(X, Y, epochs=1, verbose=2, batch_size=200) weights_dense = model.get_layer("output_layer").get_weights() weights_optimizer = model.optimizer.get_weights()
Epoch 1/1 - 2s - loss: 1.0986 Epoch 1/1 - 0s - loss: 1.0795 Epoch 1/1 - 0s - loss: 1.0610 Epoch 1/1 - 0s - loss: 1.0414 Epoch 1/1 - 0s - loss: 1.0209 Epoch 1/1 - 0s - loss: 0.9997 Epoch 1/1 - 0s - loss: 0.9781 Epoch 1/1 - 0s - loss: 0.9563 Epoch 1/1 - 0s - loss: 0.9343 Epoch 1/1 - 0s - loss: 0.9125
まとめ
model.save()
が使える場合
->model.save()
を使えば良い。model.save()
が使えない場合- 再学習しない場合
-> モデルの重みのみ保存すれば良い。 - 再学習する必要がある場合
-> モデルの重みとoptimizerの状態を保存すれば良い。
- 再学習しない場合
ちなみに、weights_dense
とweights_optimizer
の中身は以下。
weights_dense
[array([[0.9180371 , 1.00887 , 1.0730928 ], [1.0696087 , 0.94541067, 0.9849805 ], [0.8941715 , 1.0231149 , 1.0827136 ], [0.8982664 , 1.0132247 , 1.0885084 ]], dtype=float32), array([0.9999076, 1.000105 , 0.9999874], dtype=float32)]
weights_optimizer
[10, array([[-0.00934379, 0.00099744, 0.00834635], [ 0.00797938, -0.0062994 , -0.00167998], [-0.01209093, 0.00263835, 0.00945257], [-0.01162081, 0.00148986, 0.01013095]], dtype=float32), array([-2.1416694e-05, 2.4464378e-05, -3.0475137e-06], dtype=float32)]
なので、pickleとかで保存すれば良い。
model._make_train_function()
は何をしているかわからない。
けど、それがないとmodel.optimizer.set_weights(weights_optimizer)
でエラーになる。
競艇や競馬の買い目最適化(ケリー基準の一般化)
問題設定
特定のレースの特定の券種で、確定オッズ(以下オッズ)と的中確率は所与とします。
そのうち、期待値がを超える券が枚あるとします。
枚の券のうち、番目の券の、オッズを、的中確率をとします。
それぞれの券の期待値がを超えるので、です。
さて、このときそれぞれの券にいくらずつかけるのが最適ですか?という問題です。
ただし、何レースでも購入でき、オッズは低下せず、少数金額も購入できるとします。
(他にも条件あるかもしれませんが、このくらいで。)
最適化
番目の券を所持金の倍購入するとします。
いま、枚の券はすべて同じ券種であるため、互いに排反な事象となります。なので、的中してもどれか1枚です。
(※券種は複勝や拡連複ではないとして、さらに同着や返還等も考えません。)
なので、レースが終わったときの結果の事象としては、
枚の券のうちどれか1枚が的中するか、全部外れるかです。
したがって、ケリー基準を導出する際と同様に考えて、
この関数を最大化します。
ただし、です。
また、のは、あらゆるの組み合わせ的な感じです。
なぜこの関数を最大化するのかは、上記参考サイトに任せますが、
イメージとしては、
- 枚全部外れる確率はで、そのとき所持金は倍になる。
- 番目の券が的中する確率はで、そのとき所持金は倍になる。
それらの期待値のような感じです。
掛け算や指数があれなので、最適化の常套手段である対数変換をします。
ベクトルで偏微分とか苦手なので、地道にいきます。
上式を、番目の券の購入割合で偏微分してイコールとおくことで、
となります。
ここで、第1項と第2項はによらず一定です。
なので、別の番目の券の購入割合で偏微分した場合は、
第3項だけが変わるので、
が成り立ちます。
変形して、
となります。
この式により、偏微分した式の第2項の総和の中身のインデックスを
オッズの比をかけることで、変換できるようになります。
第2項の総和の中身のインデックスをに変換します。
とおいて、さらに変形をつづけます。
上式はすべてのについて成り立ちます。
ここまでくるともう少しですね。
いま求めたいのは、ですけど、
線形になっているので、解けますね。
具体的には、
次元縦ベクトル
、
の行列
、
次元縦ベクトル
とすると、
が成り立ちますので、最適なは、
と求まりました。
の場合について考えて、ケリー基準と一致するか確認してみます。
のとき、
なので、
となります。
ここで、
なので、
となり、普通のケリー基準の式と一致します。
(参考サイトや一般的に紹介されているケリー基準と違うのはオッズの置き方によるもの。本質的には全く同じ。)
数値シミュレーション
求めた解が正しいか、数値シミュレーションで確認します。
現実ではありえないかもしれませんがで、
という状況を考えます。
期待値はそれぞれ順にとなります。
番目の券が一番期待値高いです。番目と番目の券は期待値同じです。
最適なを求めると、となりました。
番目と番目の券を比較すると期待値は同じですが、
確率は番目の券の方が高いため、購入金額の割合は大きいです。
番目の券が一番期待値高いですが、一番購入金額は低くなりました。
的中確率が小さいためですね。
では、この設定で5000レースシミュレーションしてみます。
また、最適なに、
定数を掛けた場合も同時に示します。
横軸はレース数、縦軸は所持金で対数グラフです。初期所持金はとしました。
どうやら、求めたものは正しかったようですね。
最適なより小さいと、資産の増え方は下がりばらつきが抑えられます。
一方最適なより大きいと、資産の増え方は下がりばらつきが増えます。
レース数が少ないとき、一時的に資産が最適なのときの資産を上回る可能性はありますが、長期的にみるといいことないですね。
なので、ハーフケリーとかの方法があるんですかね。
おわりに
今回は、的中確率とオッズが所与のときの買い目を最適化しました。
今後の課題としては、
- 複数種類の券種がある場合どうなるか
- 的中確率やオッズが確立変数として与えられたときどうなるか
です。気になります。
のちのち考えていきます。
Rコード
library(ggplot2) p=c(0.1,0.2,0.3) o=c(15,6,4) N=length(p) p0=1-sum(p) o0=1-sum(1/o) f=solve(matrix(p0-p*o*o0,ncol=N,nrow=N)-diag(N)*p0*o)%*%(p0-p*o*o0) p*o f R=5000 flg=sample(1:(N+1),R,prob=c(p,p0),replace=TRUE) ff=f data=data.frame(R=1:R,w=cumprod(c(1+ff*o-sum(ff),1-sum(ff))[flg])) g=ggplot()+geom_line(data=data,aes(x=R,y=w),color=2)+scale_y_log10() for(i in seq(0.2,0.8,by=0.2)){ ff=f*i data=data.frame(R=1:R,w=cumprod(c(1+ff*o-sum(ff),1-sum(ff))[flg])) g=g+geom_line(data=data,aes(x=R,y=w)) } g ff=f data=data.frame(R=1:R,w=cumprod(c(1+ff*o-sum(ff),1-sum(ff))[flg])) g=ggplot()+geom_line(data=data,aes(x=R,y=w),color=2)+scale_y_log10() for(i in seq(1.2,1.8,by=0.2)){ ff=f*i data=data.frame(R=1:R,w=cumprod(c(1+ff*o-sum(ff),1-sum(ff))[flg])) g=g+geom_line(data=data,aes(x=R,y=w)) } g
変なユニバーサル基板を作ってみた。
ブログを1年以上放置していました。
何かしら発信しようと思い、雑な記事ですけど投稿します。
これからちょっとずつ投稿しようと思います。
作ったもの
3方向に挿すことができる2.54mmピッチのユニバーサル基板です。
作ったといっても2年くらい前なのですが、画像が出てきましたので。
作ってみたものの、使ってみると使いにくくどうしようもなかったです。