Open o-jill opened 1 year ago
学習時間の例
real 109m48.141s
user 107m11.096s
sys 2m29.153s
完全にシングルスレッド。 ファイルI/Oはこれ以上減らせない気がするので何かを別スレッドに移す必要あり。
案5をやってみた。 1.5s/batchが2.15s/batchになった印象。 問題はStringのコピーか、mpscのやり取りか。 (rfen, score)の配列をGlobalで確保しておけばちょっとは速くなる? 学習中にシャッフルするつもりだったけどそうなっていない可能性あり。。。 → 学習スレッドからの応答を待って対象RFENを送るべし。 それでも大して変わらない気がするので、RFEN配列のGlobal化+mpscはインデックスを配列で渡すのが良いと思われる。 →それでも大して変わらなかった。結構良さげで1.5s/batch : 1.55s/batch。global化でどうか?
repeat200で20回ずつ計測してみた。ウェルチのt検定。
unit | before | after | p |
---|---|---|---|
sec/200repeat | 13.3±0.56 | 12.8±0.40 | 0.3% < 5% |
msec/batch | 69.55±1.87 | 66.22±1.32 | 0.0% < 5% |
x[idx].as_ptr()
ではなくx.as_ptr().add(idx)
ならチェックが発生しない。backwardは分割できそうな気がしてきたけど、forwardは今まで通り計算しないといけないんじゃないか疑惑。 思ったより高速化しないかも。
分割するには、
trainer::learn()
-> weight::training()
-> weight::learnbb()
x2 -> weight::forwardv3bb()
, weight::backwardv3bb()
を分断する必要があるようだ。。。
とりあえずlearnbb()
はweight::forwardv3bb()
とweight::backwardv3bb()
を呼んでるだけなので要らないかもしれない。
スレ分けするならこんな感じかな?コスト(データ渡しなどの時間)に見合わない気がしている。backwardの処理時間が伸びれば効果も出そうだが。。。
backwardv3bbを二分するのは #82 でやって失敗している模様。
学習(--learn)と評価(--duel)で、duelを2個同時に実行するのはあまり効果が無さそうに見えている。 学習率ごとにスクリプトを分けて実行するのはどうか? スクリプトAではeta=0.1, 0.05, スクリプトBでは0.01と0.001など。 評価は順番に今まで通りやる。 学習時の並列化があまり効いていない(ほぼシングルスレッド)ので、時間的にはこっちのほうがいいかもしれない。 スクリプトAとBでフォルダを分ける必要があるので注意。途中経過のファイルが上書きされちゃう。 今までのやり方では1,000min(sys60min)ぐらいだった。
2フォルダ作戦をやってみた。 ついでに仮想環境のCPUの割当がEコアだったので設定を変えてPコアで動くようにした。 400min弱 x 2で終わるようになった。Pコアにした影響もありそうな気がするので一概に2フォルダ分にした影響で半分になったわけではないと思う。 ともかく効果はありそう。
- 案9 最後12手分ぐらいを読み切れるので終盤の学習はいらないのではないか? 学習する局面数が減るのは良いことなのか悪いことなのか。
絶対に最終局面は学習しなくてもいい。 昔その判定を入れたけどバグっているようだ。。。 現在midはゼロで運用されていて、すなわち最終局面も学習している模様。
@@ -2281,19 +2281,19 @@ impl Weight {
/// - `rfen` : RFEN
/// - `winner` : winner or # of stones.
/// - `eta` : learning ratio.
/// - `mid` : last (mid) moves will not be used.
///
/// # Returns
/// - OK(()) if succeeded.
/// - Err(String) if some error happened.
- pub fn train(&mut self, rfen : &str, winner : i8, eta : f32, mid : i8)
+ pub fn train(&mut self, rfen : &str, winner : i8, eta : f32, mid : u32)
-> Result<(), String> {
if cfg!(feature="bitboard") {
let ban = match bitboard::BitBoard::from(rfen) {
Ok(b) => {b},
Err(e) => {return Err(e)}
};
- if ban.count() > 64 - mid {return Ok(());}
+ if ban.nblank() < mid {return Ok(());}
self.learnbb(&ban, winner, eta);
最終10手分は学習しないみたいだけど多分これはあまり良くない。最後の局面は学習不要。< 10
じゃなくて< 1
かな?
pub fn learn_stones_para_rfengrp(&mut self) {
...
// 最後数手は読み切れるので学習しなくて良い
if bitboard::count_emptycells(&rfen).unwrap() < 10 {
continue;
}
unsafe {RFENCACHE.push((rfen, score));}
ミニバッチを取り入れますか? 今はオンライン学習状態だと思っている。(1rfenで1回更新) 例えば、10rfenごとに差分→平均→更新とやってみるとどうなるか? ミニバッチの中では同じ重みを使えば良いのでスレ分けができるのかな?(速くなる?) 10個のバックプロパゲーションした結果(差分でも重みでも)を足して10で割ったものが1ミニバッチの結果?
多スレ版ミニバッチ案 実は1スレでもLockしないだけと言う説。
lock時に一度にlockしてN個の局面分を書いた方が速そう。10回lockしてると遅そう。そこまで評価関数は大きくないのでスレごとに結果領域を確保して全部終わったら結果をlockして出力が良さそう。
ミニバッチ学習についてはこちらへ #130
学習関連はすべて :octocat: o-jill/tigerdenversi へ移行。
ある局面を学習するためには、forwarding(順伝搬)とbackwarding(逆伝搬)をする必要がある。 これをなんとかして高速に実行する方法を考えたい。
例: w1で順伝搬 逆伝搬でw1を更新、w2になる w2で順伝搬 逆伝搬でw2を更新、w3になる 。。。