マジで駄文。ボウリングのスコア計算
ボウリングのスコア計算ってどういうルールなんだろう?
と思って調べてみた。
なるほどそういうルールだったのか…興味ないから今まで知らんかった(汗
- ストライク = 10 点+次の 2 投分
- スペア = 10 点 + 次の 1 投分
ストライクは次の 2 投分 + 10 するということは、「ストライク → 1, 3」となったとき、10+1+3, 1, 3 という計算になるのだろう。
仮に9 フレーム目まで All Strikeで、10フレーム目で「1, 0」だったと仮定すると
9 フレーム目にフィードバックで 11, 8 フレーム目= 10 + 10(9フレーム目) + 1(10フレーム目) みたいな計算になるはず。
てことは、実質最後から計算した方が計算しやすいのかもしれない。
全フレームストライクと仮定すると、最初の 2 投まで1投目のスコアはつかず、3投目決まった時点で 30 点になる。
で、その後もストライクが続くと 1 フレームあたりマックススコア 30 が 10 フレーム続くので、 300 点マックスと。
やっぱりこれは後ろから計算した方が楽そうだ。
python にゃ配列の処理が豊富なので、色々楽にできる。
# 各フレームのスコアを列挙してみた score = [ [9, 1], [8, 2], [10], [5, 0], [3, 6], [4, 2], [7, 3], [6, 3], [10], [9, 1, 9] ] score.reverse() score
[[9, 1, 9], [10], [6, 3], [7, 3], [4, 2], [3, 6], [5, 0], [10], [8, 2], [9, 1]]
逆順ソートできるのでイイネ
そしたら 10 フレーム目だけ単純 sum して残りをスライスでループしたらいい
total=0 next1, next2 = 0, 0 # 最終フレームは特別扱い total += sum(score[0]) if len(score[0]) == 3: next1, next2, _ = score[0] else: next1, next2 = score[0] print(f'Total: {total}') # 他のフレームを計算 for frame in score[1:]: print(f'frame {frame}') frame_sum = sum(frame) if len(frame) == 1: print(f'strike! 10 + {next1} + {next2}') total += 10 + next1 + next2 next1, next2 = 10, next1 elif frame_sum == 10: print(f'spere! 10 + {next1}') total += 10 + next1 next1, next2 = frame else: print(f'append {frame_sum}') total += frame_sum next1, next2 = frame print(f'Total: {total}') total
結果: イインジャネ?
Total: 19 frame [10] strike! 10 + 9 + 1 Total: 39 frame [6, 3] append 9 Total: 48 frame [7, 3] spere! 10 + 6 Total: 64 frame [4, 2] append 6 Total: 70 frame [3, 6] append 9 Total: 79 frame [5, 0] append 5 Total: 84 frame [10] strike! 10 + 5 + 0 Total: 99 frame [8, 2] spere! 10 + 10 Total: 119 frame [9, 1] spere! 10 + 8 Total: 137 137
WSL に Salesforce 開発環境を作る
「汚さず」というのはWindowsのPATHとか常駐とか増やしたくないのです。
で、どういう事かというと WSL2 に Ubuntu 突っ込んで、そこに sfdx をセットアップするという方向。
なぜセットアップかって?メインマシンに SFDX 入れてなかったからさ!
WSL2 インスコ
まずは、Windows subsystem for linux をインストールしよう。
やるならWSL2からだね。
当たり前だけど窓 10 限定。
- Powershell を管理者で起動して
- Powershell に脳死で
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
と突っ込む。
- 次のコマンドで
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart
を叩く。
- カーネルパッチを当てよう
Windows Subsystem for Linux (WSL) を Windows 10 にインストールする | Microsoft Docs - 最後にPowerShell で
wsl --set-default-version 2
を叩けばおk
因みに、最後のコマンド実行時に BIOS の仮想化が Off になってる場合は指摘されます。
BIOS の設定はマシンによるので、環境ごとにやって下さいな。
尚、ASUS の Intel マザボの場合は、「詳細」「CPU」の中に 「Intel VT」があるのでこいつを有効にしよう。
Ubuntu のインストール…っても Windows ストアで「Ubuntu」って突っ込めば手に入ります。
初回起動時にアカウントとパスワードを登録する。
因みに、初回は以下をお忘れなく
$ sudo apt update $ sudo apt upgrade -y
nodejs と sfdx インストール
なぜ nodejs からだって?sfdx単体でなんて入れたくないからだ
sudo apt install nodejs npm -y
でとりあえず二つをインストール。sudo npm install n -g
でn
というコマンドをインストールする。こいつはパッケージ管理ツールみたいなもんだ。sudo n stable
安定版 nodejs いれませう
$ node --version v14.16.1
- で、インストール後に apt で入れた node は消しとく。
sudo apt purge -y nodejs npm
- npm コマンドをアップデートしよう
npm update npm
sfdx
をインストールして完了。sudo npm install sfdx-cli --global
畳み込み層を実装してみる
この辺の続き。
畳み込み層とプーリング層、ひいては CNN をしてみる。
前回の im2col
連中を、バッチサイズ/チャンネルとか踏まえて再実装。
超長いので、今回はここから続きを読むリンク…。
画像を行列で処理する前準備
CNN で特徴的な動作に畳み込みっちゅう処理がある。
画像には局所性(隣接するピクセルの影響を受ける事)があるので、それを利用して画像の特徴を強調したりできる。
やってることはフィルタを用意して、画像に畳み込み計算を行う事。
以下の例みたいに、左上からフィルタを適用して 1 セル生成。したら、右に 1 セルずらして…と繰り返す。
見ての通り画像自体は小さくなる。
因みにこのずらす間隔をストライドと呼ぶ。
ストライド幅が大きいほど画像サイズは小さくなり、特徴が見逃されやすくなる。
で、この計算するとしたときに普通の画像に対して処理するのがしんどいなんで整形するのが一般的なのだそうだ。
im2col
画像に対して畳み込みを行うには形状がめんどくさいので以下みたいに配置しなおす。
これを im2col と呼ぶっぽい。
例えば 2x2 のフィルタで 3x2 のデータに適用するとき、まずは im2col でこんな風にしてしまう
こう変換すれば、フィルタをドット積で一括計算できる。
で、実際に変換と逆変換をやってみた。
続きを読む過学習対策あれこれ
前回わざと過学習させてみたわけだが、そもそも学習の打ち切りはやたらと難しいハズだ。
あの時はデータセットが 200 位しかないし、ニューロンの数も限られていたといえる。
しかし、現実には大量のデータセットがあるだろうし、学習時間はそこそこかかるだろうと思われる。
例えば1回の学習だけで数時間かかるとする。
それを見て、じゃぁどの位で打ち切るか?2週目開始までにどれだけ待つ必要があるだろう?
要するに学習の打ち切りを判断するのはちょとどころでは難しいと予測できる。
ということで、今回はやり方を変えてみる。
まずは前回の誤差を見てみよう。
Epoch: 0 / 1000, Err_train: 1.1043634751935245, Err_test: 1.0623824378086812 Epoch: 100 / 1000, Err_train: 0.04227735559583767, Err_test: 0.13396243782658682 Epoch: 200 / 1000, Err_train: 0.018747407955545834, Err_test: 0.12865441586002588 Epoch: 300 / 1000, Err_train: 0.004991569690202153, Err_test: 0.218183645593179 Epoch: 400 / 1000, Err_train: 0.002959841289188, Err_test: 0.25887483170314346 Epoch: 500 / 1000, Err_train: 0.001493562533847086, Err_test: 0.3049230574592406 Epoch: 600 / 1000, Err_train: 0.0010474962034239849, Err_test: 0.3333645685971381 Epoch: 700 / 1000, Err_train: 0.0007757803305818819, Err_test: 0.3506614397003805 Epoch: 800 / 1000, Err_train: 0.0006128411251375161, Err_test: 0.36719589756963056 Epoch: 900 / 1000, Err_train: 0.0004995183637277335, Err_test: 0.37831813699595335
最終的に過学習のせいで 0.4 目指してエラーが広がっている。
モノは試しに RMSProp を導入してみた。
式は過去記事のものを利用。
roh = 0.95 class RMSNeuron(Neuron): def __init__(self, n_upper, n, activation_function, differential_function): super().__init__(n_upper, n, activation_function, differential_function) self.h_w = np.zeros((n_upper, n)) + 1e-8 self.h_b = np.zeros((n)) + 1e-8 def update(self): # ここだけ更新 self.h_w = (self.h_w * roh) + (1 - roh) * (self.grad_w * self.grad_w) self.h_b = (self.h_b * roh) + (1 - roh) * (self.grad_b * self.grad_b) self.w -= eta / np.sqrt(self.h_w) * self.grad_w self.b -= eta / np.sqrt(self.h_b) * self.grad_b class Output(RMSNeuron): pass class Middle(RMSNeuron): def backword(self, grad_y): delta = self.differential_function(grad_y, self.y) self.grad_w = self.x.T.dot(delta) self.grad_b = delta.sum(axis = 0) self.grad_x = delta.dot(self.w.T) return self.grad_x # ニューロン初期化 mid_layer1 = Middle(n_in, n_mid, relu_func, relu_func_dash) mid_layer2 = Middle(n_mid, n_mid, relu_func, relu_func_dash) outputs = Output(n_mid, n_out, soft_max, soft_max_dash) exec()
結果が
Epoch: 0 / 1000, Err_train: 1.1000068284857787, Err_test: 1.105102282860116 Epoch: 100 / 1000, Err_train: 0.0004723302009342129, Err_test: 0.25285731779944576 Epoch: 200 / 1000, Err_train: 2.435419747198503e-05, Err_test: 0.41570733582516095 Epoch: 300 / 1000, Err_train: 1.6498331127020632e-07, Err_test: 0.42415464268234687 Epoch: 400 / 1000, Err_train: 3.2775216891847647e-06, Err_test: 0.424167001320326 Epoch: 500 / 1000, Err_train: 1.7252434605417651e-06, Err_test: 0.42416032411453786 Epoch: 600 / 1000, Err_train: -9.588821783186093e-08, Err_test: 0.42416031798981263 Epoch: 700 / 1000, Err_train: 1.9298299962666904e-07, Err_test: 0.424160335489329 Epoch: 800 / 1000, Err_train: -2.7639660886741467e-08, Err_test: 0.42416031479869326 Epoch: 900 / 1000, Err_train: -9.780188673424984e-08, Err_test: 0.4241603145452869
ちょwwwww
過学習広がったしwwww
アカンw これ学習係数は一方通行で減らさないとダメだコレw
初期段階で一気に学習進めると、一時的に学習係数に圧がかかって、緩やかになるのだけど、それ以降で物忘れによって学習係数が跳ね上がる。 そこに過学習が入ってヒャッハーされたことが見て取れる。
続きを読む多層ニューラルネットで過学習させてみる
実際過学習がどの程度影響するのかを検証してみる。
ニューロンのひな型は このへん で作ったものを流用。
import numpy as np wb_width = 0.1 # 重みとバイアスの広がり方 eta = 0.01 # 学習係数 class Neuron: def __init__(self, n_upper, n, activation_function, differential_function): self.w = wb_width * np.random.randn(n_upper, n) # ランダムなのが確立勾配法 self.b = wb_width * np.random.randn(n) self.grad_w = np.zeros((n_upper, n)) self.grad_b = np.zeros((n)) self.activation_function = activation_function self.differential_function = differential_function def update(self): self.w -= eta * self.grad_w self.b -= eta * self.grad_b def forward(self, x): self.x = x u = x.dot(self.w) + self.b self.y = self.activation_function(u) return self.y def backword(self, t): delta = self.differential_function(self.y, t) self.grad_w = self.x.T.dot(delta) self.grad_b = np.sum(delta, axis=0) self.grad_x = delta.dot(self.w.T) return self.grad_x def show(self): print(json.dumps(self.w.tolist())) print(json.dumps(self.b.tolist())) class Output(Neuron): pass class Middle(Neuron): def backword(self, grad_y): delta = self.differential_function(grad_y, self.y) self.grad_w = self.x.T.dot(delta) self.grad_b = delta.sum(axis = 0) self.grad_x = delta.dot(self.w.T) return self.grad_x
活性関数は ReLU で、出力層にはソフトマックス関数を利用。
損失関数には交差エントロピーを使う。(最適化には確率的勾配降下法)
前回は2層(中間層8+出力層3)で過学習らしい話はなかったので、今回はわざと中間層ニューロンを 25 個とかにする。
def relu_func(x): return np.where(x <= 0, 0, x) def relu_func_dash(y, t): return y * np.where(t <= 0, 0, 1) def soft_max(u): return np.exp(u) / np.sum(np.exp(u), axis=1, keepdims = True) def soft_max_dash(y, t): return y - t n_in = 4 n_mid = 25 n_out = 3 mid_layer1 = Middle(n_in, n_mid, relu_func, relu_func_dash) mid_layer2 = Middle(n_mid, n_mid, relu_func, relu_func_dash) outputs = Output(n_mid, n_out, soft_max, soft_max_dash) def forward_propagation(x): return outputs.forward(mid_layer2.forward(mid_layer1.forward(x))) def back_propagation(t): mid_layer1.backword(mid_layer2.backword(outputs.backword(t))) def update(): mid_layer1.update() mid_layer2.update() outputs.update() def get_err(result, t, batch_size): """交差エントロピー誤差""" return -np.sum(t * np.log(result + 1e-7)) / batch_size続きを読む
ニューラルネットの弱点
過学習
現在のデータセットに過度に最適化されすぎて、未知のデータに対処できなくなる問題。
これはニューラルネットに限った話でもない。
同一のデータセットを学習させ続けることでも起きるが、ニューラルネットの場合以下のケースがある。
- 少ないデータを学習に使い続ける
ニューロンの学習は1回で決まるものでもないので、エポック(データセット1週分)内の確率的というか平均というか…そんな特徴を捉えていく。
データ数が少ない場合、特徴を取り違える可能性がある。 - ニューロン数を増やしすぎる
データセットにピッタリ合いすぎて、未知のデータに対応できなくなる。
ニューロン数が増える=表現力が上がる…が上がった表現力が過剰にマッチする状態。 - 階層を増やしすぎる
これも同じようなもの。
対策
- ハイパーパラメータ(ニューロン数や学習係数なんかの定数のこと)を最適化していく。
まぁ要するにやりすぎになってそうならニューロン数とか減らす。 - 学習の打ち切り
学習しすぎる前に処理を打ち切る。
検証用データセットの誤差を監視して、適当なところで学習を打ち切る。 - 十分なデータを用意しよう。
いや、これが地味にきついんだけどね…。 - データ拡張
データの水増し。例えば画像データなら、回転や反転した画像を使ってみるとか。 - データ前処理
データを 0-1 に抑える(正規化)。統計値を使って標準偏差 1 になるように調整する(標準化)
import numpy as np import matplotlib as mpl import matplotlib.pyplot as plt %matplotlib inline def normalize(x): """正規化""" xmax = np.max(x) xmin = np.min(x) return (x - xmin) / (xmax - xmin) def standardize(x): """標準化""" av = np.average(x) std = np.std(x) return (x - av) / std
局所最適解
局所的な最適解にハマって学習が停止し、全体最適解にたどり着けなくなる問題。
対策
- 最適化アルゴリズムで回避する。
記事は このへん 。 - 正則化する。
重みの値に制限を与える。(最大値/最小値など)
最大値制限(※1)とか制限したり重みを減衰させる場合に(※2)などする。 - 重みやバイアスの初期値をランダムにしとく。
勾配喪失
3層以上でやってると、表層への伝播が弱くなりすぎてしまう問題。
微分を繰り返すので、それによって値が小さくなり、最終的にほとんど学習できなくなってしまう。
例えばシグモイド関数だと で MAX 0.25 そりゃ勾配少ないわけだ。
def sigmoid(u): return 1 / (1 + np.exp(-u)) def fdash(y): d = sigmoid(y) return (1 - d) * d X = np.linspace(-5, 5) Y = fdash(X) plt.plot(X, Y) plt.show()
対策
なんで、ReLU とかを使うらしい。