頭と尻尾はくれてやる!

パソコンおやじのiPhoneアプリ・サイト作成・運営日記


強化学習(DQN)に強引にSceneKitの物理エンジンを使った結果

強化学習(DQN)に強引にSceneKitの物理エンジンを使う
↑SceneKitの物理エンジンを強引に強化学習(DQN)に使う、という手法で倒立振子を学習させてみたが、、、



↑こんな感じでなんとなくそれっぽくなってる。正直それっぽい場合を動画にしてるだけで、毎回倒立できるわけではない。
それでもまあ学習してんだな、ということはわかった。

あと、制御の時間ごと(今回は0.2秒ごと)に逐次前の状態を使って計算していく、しかも状態を得られるタイミングが SCNSceneRendererDelegate がコールされるタイミングであり、そこで近似する、という方法なので、ランダムなアクションを与えなくても微妙にずれが生じていた。

与えるトルクや制御の時間間隔、1エピソードを何点にするか?何エピソードを学習にまわすか?などなど粘り強くやって行けば、完全に倒立キープできるようになるのかもしれないけど、、、そこまではええか。


アプリでMacの輝度調整

機械学習で株価予測は断念したので最近は夜中に強化学習を動かしてる。
株価予測の場合は消費電力減らすためにMacのディスプレイをオフにしてた。
ところがSceneKitを使う強化学習だとディスプレイをオフにすると計算が止まってしまう。SceneKitとはそういうものなのか?
どういうわけかキーボードの太陽マークみたいなのでも輝度は変わらないし、システム環境設定のディスプレイにも輝度調整スライダはない。

どうしたもんかなあと調べてたら↓こんな記事を見つけた。

Macディスプレイの明るさを自由に調整したいなら、専用アプリ「Brightness Slider」がおすすめ! | 男子TRENDY

さっそくMac App Storeでダウンロード(無料)。
使ってみたらいいやん!ありがとう、開発者の方!!

なお、マウスで画面を真っ暗にした後に再び輝度を上げようとする時に画面が真っ暗なのでショートカットキーを設定しておかないと難儀する。


semaphoreでコードがスッキリする

強化学習(DQN)に強引にSceneKitの物理エンジンを使う
↑ここでSCNSceneRendererDelegateメソッドの
- (void)renderer:(id)renderer updateAtTime:(NSTimeInterval)time;
内でオブジェクトの位置・姿勢・速度・角速度の4つを取得してるとあったけど、呼び出し元で、
nextStatus = [sceneManager getNextStatusFor:currentStatus action:action];
てな感じでコードを書けるとすっきりする。

- (void)renderer:(id)renderer updateAtTime:(NSTimeInterval)time;
実際は↑このメソッドは適当な時間にコールされて何回目かにようやく欲しい値を得られる、ということになる。
なので
[sceneManager getStatusFor:currentStatus action:action];
で計算をスタートさせて、実際に欲しい値を取得できた段階でdelegate使うなりして呼び出し元で値を入手する、、、
とか書くともう処理をたどるのが大変になってくる。
なので、今回も

Metal Performance Shaderで同期処理っぽく記述する
↑これと同じ方法を使ってる。このsemaphoreを使う記述方法がすっごいお気に入りなんだわ。
-(Status)getNextStatusFor:(Status)status action:(Action)action
{
    currentStatus = status;
    applyTorque = [self getTorqueForAction:action];
    
    semaphore = dispatch_semaphore_create(0);
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        self->calculationMode = CM_START;
        
    });
    
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    
    return currentStatus;
}
↑呼び出し元からはこれを呼び出して、ここでは値を得るまでwaitさせておいて、SCNSceneRendererDelegateメソッド内で値を得た時点で
dispatch_semaphore_signal(semaphore);
してやると値をreturnする。
この方法、ホント重宝するわ。


強化学習(DQN)に強引にSceneKitの物理エンジンを使う

強化学習で物理エンジンを使いたいが
↑ここにも書いたんだけど、強化学習(DQN)やる時に物理エンジンを使うと次の時刻の状態や報酬は1通りしか取れないのでだめじゃん?と理解してる。

【強化学習中級者向け】実装例から学ぶDueling Network DQN 【CartPoleで棒立て:1ファイルで完結】
↑Dueling Networkとかあるみたいなんだけど、まだよくわからないのでとりあえずもっとベタな方法を試してみた。

物理エンジンにある状態を与えてdT後の状態を得るというのを繰り返す、というもの。

物理エンジンに下手に初期値を与えると瞬間的に移動するので慣性ですっとんで行ってしまったり期待通りの位置、姿勢になるのに時間がかかるんじゃないの?とか思っていたので、ちゃんとSceneKitで指定の状態を与えることができるのか?というところから確かめた。

与える状態は
位置 position
姿勢 orientation
速度 velocity
角速度 angularVelocity
の4つ。
これをループの最初に振り子(SCNNodeオブジェクト)に与える。
SCNSceneRendererDelegateメソッドのおなじみの
- (void)renderer:(id)renderer updateAtTime:(NSTimeInterval)time;
内で上記の状態を取得する。
このメソッドは適当な時間間隔でコールされるので、きっちりと指定したタイミングでの状態を得ることはできないので仕方なく近似してる。
これでなんとかそれらしい値を取得することができそうだと確認できた。



↑学習時にはこんな感じに見える。ある状態から2種類のアクションをやった場合にどういう状態になるかを得るのでちらついて見える。
負荷を減らすつもりで照明もなし、振り子以外のオブジェクトもなし。
scnscene.physicsWorld.speed
でスピードを早くすることもできなくもないんだけど、2.0にしただけで取得する値がばらつきやすくなったので結局デフォの1.0のままでやってる。
なので計算に時間がかかる(やっぱDueling Networkとか勉強するか?)。

なんとも面倒なことをやってるけど、これがうまくいけば物理エンジンを使わないとできないようなややこしい動き(二足歩行とか)に対しても強化学習ができる可能性が出てくるんだよな。






SceneKitでオブジェクトの速度・角速度が取得できない

SCNNodeオブジェクトの位置・姿勢が取得できない
↑ここで、SceneKitにおいてオブジェクトの位置、姿勢(クォータニオン )を得るのにpresentationNodeを使わないとダメ、という話をしてるけど、今回は速度、角速度を得ようとしてはまったというお話。もちろん物理エンジンを使ってる。

振り子

オブジェクトはいつもの(?)振り子だ。棒の上部をSCNPhysicsHingeJoint使って”世界”に固定してる(支持してる板は飾り)。

このSCNNodeオブジェクトの速度、角速度なので
node.physicsBody.velocity
node.physicsBody.angularVelocity
で取得できると思うやん?

もう半分罠にかかってるで。

結論から言うと
- (void)renderer:(id)renderer updateAtTime:(NSTimeInterval)time;
などのSCNSceneRendererDelegateメソッド内でないと意図する値を得ることができなかった。

例えば、
・画面をクリックした時にコールされるメソッド
・タイマーでコールされるメソッド
なんかでは値がどれもゼロになってしまう。
なんでやあ〜?って叫ぶ前によくリファレンス読んだら書いてた。
さっきの
- (void)renderer:(id)renderer updateAtTime:(NSTimeInterval)time;
などのSCNSceneRendererDelegateのメソッド内はOK。
でもその他のメソッドだと前にsetされた値を返すよって書いてる。

えええ?なにそのトラップ。
確かにセットした後に調べると値はゼロじゃないなあ、、、謎仕様や、、、
まあいいや。深く考えるのはよそう、だってSceneKitだもの(みつを)。




Copyright ©頭と尻尾はくれてやる!. Powered by FC2 Blog. Template by eriraha.

FC2Ad