FC2ブログ

頭と尻尾はくれてやる!

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


何年もだめだったradiko.jpが復活してた

最近気が付いたんだけど、iPhoneの4G回線でradiko.jpが再び聞けるようになってた。

以前は普通にiPhoneのradikoアプリは外でも使えていた。
ところが、(記憶に間違いがなければ)あのタイムフリー機能を追加した大幅リニューアル以降、外では使えなくなった。一度取得した数十秒の内容を繰り返すばかり(ただしWi-Fiだと問題なし)。

もっともアプリの問題かiOSの問題か使っている回線(いわゆる格安SIMだし)の問題かよくわからなかった。

いつの間にか外ではradikoを使わなくなり何年か経っていたんだけど、先日何気なく起動してみたら、、、ちゃんと聞けるやん!
何やったんやろ?


倒立振子にDueling Networkを適用してみた

強化学習の倒立振子問題でDueling Networkなるものを使いようやく動かすことができた。

強化学習(DQN)で倒立振子で入力を変更など
↑この辺りでもやっていたんだけど、再度ざっくり概要を。

学習はKeras(バックエンドはTensorFlow)を使う(Python)。
振子を揺らす(データ取得)とかはmacOSアプリのSceneKitの物理エンジンを使い、この時には予測する必要があるのでそこはMetal Performance Shaderを使う。
Python側とmacOS側は共通の状態ファイルをみて必要な処理を交互に行う。

今回はDueling Networkを使ってみたというお話。
詳細は参考リンク(※)を。

悩んだのが出力層でlambdaを使っている部分。
{
    outputlayer = Lambda(lambda a: K.expand_dims(a[:, 0], -1) + a[:, 1:] - 0.0*K.mean(a[:, 1:], keepdims=True),
                             output_shape=(action_size,))(y)
}
↑参考リンク先にあるコードなんだけど、この出力層をMPSでどうやって記述するんだ?ってなった。係数はどうやって渡すんだ?とか悩んだんだが、、、

Kerasのサマリー表示

↑Kerasで表示してくれるsummaryによるとlambda層に係数がなかった。
あー、そういうことか、とここで気付いたよ。1つ手前の層とは全結合で繋がってるのかと思ってた。違ったのね。

ってことはMPSのために保存する必要もない。MPS側では単に出力層の1つ手前まで計算させて、最後は単純に足せば(従来の)出力層であるQ値が得られた。
{
    //mpsResult.q0 = self->qResults[0];
    //mpsResult.q1 = self->qResults[1];

    // Dueling Networkの場合
    mpsResult.q0 = self->qResults[0] + self->qResults[1];
    mpsResult.q1 = self->qResults[0] + self->qResults[2];
}
↑V値とA値を足してQ値を得てるところ。Keras側とMPS側で同じ入力値を与え、同じ出力値になることで確認した。

DQN→Dueling Networkへのコードの変更が案外少なくて、ホントこんなんで動くのか?と思いつつ動かしたらあっさり学習した。しかも従来より早く。

学習結果のグラフ

↑ある程度学習が進んだ状態。

いや、本来なら得た報酬が大きくなっていくグラフの比較があるべきなんだが、、、そこまでは作っていないのでした(このあたりはmacOS側の処理なのでTensorBoardとか使えない)。
それでも「あれ?もう結構振子が振れてる?!」と思う程度にすんなり学習していた。
考えた人、すごいわ。


(※)参考リンク
【強化学習中級者向け】実装例から学ぶDueling Network DQN 【CartPoleで棒立て:1ファイルで完結】


NHK筋トレ動画を頭出しするアプリを作った

少し前に話題になったNHKの筋トレ番組「みんなで筋肉体操」。
1回やるのに5分もかからないので、日替わりで順次1つずつだけど毎日やってる!

公式より、腕立て、腹筋、背筋、スクワットの4種類のビデオがYouTubeに公開されてる。
いざやる時にはiPhoneのYouTubeアプリを起動、NHK 筋トレ で検索して、今日はどれだったかなー?と考え1つのビデオを選んで、やり方はもうわかっているので最初の方はスキップしてようやく、武田真治さんらと一緒に筋トレを始めるわけだ。

この過程が面倒なので、今日の筋トレメニュー動画がすっと出てくる自分専用のiOSアプリを作った。

噂には聞いていたけどUIWebViewは使うべからず、ってことらしいのでWebKitのWKWebViewを使う。
意味わからないんだが、どうもこいつはStoryboardでオブジェクト生成ができないらしい(試したが確かに挙動不審であった)。
なのでコードで書く必要がある。
それに付随してレイアウト指定もコードで書かないとだめっぽいのだが、、、謎仕様だ。

ともかく。
@import WebKit;

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    // (1)
    WKWebViewConfiguration *configuration = [WKWebViewConfiguration new];
    WKWebView *webView = [[WKWebView alloc] initWithFrame:self.view.bounds configuration:configuration];
    [self.view addSubview:webView];
    
    // (2)
    [webView setTranslatesAutoresizingMaskIntoConstraints:NO];
    [self.view addConstraints:[self makeConstraintsForView:webView]];

    // (3)
    NSURL *targetUrl = [self getTodaysURL];
    NSURLRequest* request = [NSURLRequest requestWithURL:targetUrl];
    [webView loadRequest:request];
}
↑ざっとした流れだけど。
(1)あたりでWKWebViewオブジェクトを生成。
(2)はレイアウトの指定。でもなぜか動画を再生するとワーニング出まくりになってしまう。まあ俺専用だからスルーだ。それにしてもコードで書くのって面倒なのね、、、
(3)で欲しい動画のURLをWKWebViewオブジェクトに渡すんだけど、このあたりはUIWebViewと同じだな。

ちなみに動画選択部分はこんなの↓
-(NSURL *)getTodaysURL
{
    // (4)
    NSString *urlStr1 = @"https://www.youtube.com/watch?v=WndOChZSjTk&t=65s";//腕立て
    NSString *urlStr2 = @"https://www.youtube.com/watch?v=1GanGLmDt2I&t=97s";//腹筋
    NSString *urlStr3 = @"https://www.youtube.com/watch?v=IHYOhDe4FB8&t=81s";//背筋
    NSString *urlStr4 = @"https://www.youtube.com/watch?v=PyJOEt2nsGQ&t=81s";//スクワット
    
    // (5)
    NSTimeInterval timeInterval = NSDate.timeIntervalSinceReferenceDate;
    NSUInteger days = timeInterval/3600.0/24.0;
    
    NSString *urlStr = nil;
    NSUInteger amari = days % 4;
    if (amari == 0) {
        urlStr = urlStr1;
    } else if (amari == 1) {
        urlStr = urlStr2;
    } else if (amari == 2) {
        urlStr = urlStr3;
    } else if(amari == 3) {
        urlStr = urlStr4;
    } else {
        NSLog(@"error! wrong amari %lu",(unsigned long)amari);
        exit(0);
    }
    
    NSURL *url = [NSURL URLWithString:urlStr];
    
    return url;
}
アプリ開始時のスクショ

↑アプリスタート時はサムネ表示になる。
残念ながらここで動画再生ボタンを押す必要がある。勝手に再生して欲しいがさすがにこれはどうしようもないだろう。

(4)にあるようにYouTubeへのリンクでt=65sなどとするとビデオの開始時間を指定できる。これだと65秒から開始するので

開始画面のスクショ

↑動画再生ボタンをタップするとここから始められる。4本ともここからスタートするのですぐに筋トレできる。

(5)現在時刻の'タイムスタンプ'から今が何日目かを得ている。4で割った余りでビデオの種類を変えてるわけだが、これだと日本時間じゃないので、ビデオ切り替わりが朝9時くらいになる。まあいつも夜に筋トレするから問題ないが。


筋肉は裏切らない!


IFTTTでボールルームへようこそのツイート更新を通知で得る

Amazonプライムビデオのボールルームへようこそ

↑Amazonプライムビデオで見たアニメの中で一二を争うくらいよかったのが「ボールルームへようこそ」。

漫画ボールルームへようこそ

↑漫画も発売されてる全て(9巻まで)を揃えた。これを書いててもUNISON SQUARE GARDENの曲が脳内再生される。あ、でもダンス教室には通い出してはいないけど。

現在も話は続いてるが作者の竹内友氏が病気療養中のため連載はストップしている。
早く続きが読みたいわけで、新しい情報を求め毎日竹内友氏とボールルームへようこそ公式のツイートをチェックしている。



↑こちらは作者竹内氏の最新のツイート。今年2018年3月1日付だ。



↑こちらは公式。これも最新ツイートの日付が今年の4月5日だ。
こんな具合でどちらももう半年近く更新がない状態。


新しいツイートがあるか調べるのって案外めんどくさくて、いちいち調べなくてもツイートがあった時に教えてくれればいいのに、、、
と思いIFTTTを使ってみた。


Discover IFTTT and Applets - IFTTT
↑IFTTTのサイト。iOSアプリもある。
アカウントを持っていない場合は作成しましょう。
iOSアプリで設定したけどどっちでもいいと思う。

My Appletsタブで新しいのを作るので画面右上の十字ボタンをタップする。

IFTTTのapplet作成開始画面

↑するとこんなのがでるので、thisってところをタップするとトリガーにできるサービスがいっぱい出てくるので検索などでTwitterを選ぶ。

トリガーをTwitterにする

↑Twitterでも何がどうなったら、という条件がいっぱいあるので、今回の目的に合致した
New tweet by a specific user
というのを選ぶ。

アカウント名入力画面

↑こんな画面が出るので、チェックしたいユーザー名を入れる。
問題なければ(※)次は ‘that’ に相当する部分、つまり新しいツイートがあった場合にどうするのかを指定する。

アクション指定画面

↑これもいろいろ選べるみたいだけど今回はNotifications(iPhoneに通知)を選んだ。

今回は2つのアカウントをチェックしたいので2回同じ作業をした。

これであとは「来月から連載再開します!」とかそういうツイートが通知されるのを待つだけだ!
早く竹内先生の病状が回復されますように。



※こんなエラーが出た場合

could not verify Twitter username. If this persists, try reconnecting your Twitter Channel.

エラーメッセージ

↑最初、こういうエラーがでてなんのこっちゃと思ったらIFTTTとTwitterの連携ができていなかったようで、IFTTTのサイトでTwitterの Edit connectionで許可したらいけた。


KerasにしたらTensorFlowと同じ結果にならない

TensorFlowで記述していたところをKerasに変えた。
同じような結果を期待してたんだけど、なぜか違うような結果になる。

倒立振子(強化学習、DQN)をやってるんだが、TensorFlowだとそこそこ学習が進んで振子がどんどん高い位置へ行くのに、Kerasだとうまくいかず、振子が初期地点からもじもじして対して動かなかったりしてた。

どういうわけかKerasだといくら学習させても損失関数の値が下がりきらない。
それほどのデータ数があるわけでもないし、NNの層の数や大きさが不足しているとは到底思えない。というかTensorFlowだといけてたし。
残る原因になりそうなのは、、、ドロップアウト?

Kerasでは出力層の手前にドロップアウトを
layerD = Dropout(0.5)
↑このように入れていた。0.5というのは0.5の割合だけないものにして過学習を防ぐってやつだよな。
#設定
keep_prob = tf.placeholder(tf.float32 , name='keep_prob')
fc_drop = tf.nn.dropout(hidden4, keep_prob )

#学習時
sess.run(train_step, feed_dict={x_ph: xData, y_ph: yData , keep_prob : 0.5})
↑TensorFlowでも同じように出力層の前に入れ、値を0.5で指定してた。

試しにKerasでこの0.5を0.1(数値に意味はなく単に0.5より小さい値)にしてみたらなぜかいい感じで学習が進んだ。

Kerasのバグかもしれないし、たまたまかもしれない。
なんせ、学習させるfit関数をwhileでぶん回して、、、とか変な実装してるからクリアされるべきところがされてない、とかそういう類のものかもしれない。




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

FC2Ad