FC2ブログ

頭と尻尾はくれてやる!

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


SceneKitのHitTestにオプション付けたらなぜかうまくいった

macOS用アプリでSceneKit使ってエディターっぽいものを作っているんだけど、オブジェクトのHit Testがたまにうまくいかない場合があってなんだこりゃ?と悩んでいたんだけど、
{
    NSDictionary *option = [NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:SCNHitTestBoundingBoxOnlyKey];
    NSArray *results = [scnView hitTest:point options:option];
}
てな感じでbounding box only のオプションをセットすると意図通りに動いた!
ということはややこしい形状のオブジェクトだとHit Testの精度がイマイチなのだろうか?

カメラオブジェクト

↑ややこしいと言ってもこの程度なんだけどなあ?


MacのPythonからIFTTTを使ってiPhoneに通知

機械学習で株価予測やってみたが
↑これに関連して、Pythonで日経平均採用銘柄225種の株価のスクレイピングをやってる。
時々インターネットの回線の具合が悪いのか、データを取りに行ったもののうまくいかずエラーで止まってることがある。そこで例外拾って再度動かす、というのもありだけど、スクレイピングなので意図しない不具合でアホみたいにリクエストしてサイトにブロックされても困るし、、、ってことでここは通知してもらいたい。

そんなわけで、エラー発生時にiPhoneにメールか通知でもできんかなあと調べてみた。
するとなにやらapnsなるものを使ってどうのこうのとすればいい、という記事はあるんだけど俺には難易度が高い。もっと簡単なのはないかのうと試したのがIFTTTを使ってみる方法。

1)エラー発生時など通知したいタイミングでMacのDropboxフォルダーにテキストファイルを保存する。

{
    req = urllib.request.Request(url)

    try :
        response = urllib.request.urlopen(req)
    except urllib.error.URLError as e:
        print(e.reason)
        MyUtility.sendErrorNotify(stockName)

    html = response.read()
}
↑呼び出す部分はこんな感じ。例外のとこで通知するようにしてる。
Pythonでの「通知」処理とは言っても単にDropboxの任意のフォルダー(ここではiftttとしてる)に適当なテキストファイルを保存するだけなのでコードは省略。

2)iPhoneのIFTTTアプリで「Dropboxのあるフォルダーにファイルが保存されたらiPhoneに通知」というAppletを作成

IFTTTのApplet

これでOK。
最初はGoogle Driveでやってみたけど試した時にGoogle Driveの調子が悪いのかうまくいかなかった。IFTTTで扱えるサービスで自分好みのを使えばよろしいかと。

IFTTTによる通知

↑実際のiPhoneの通知画面だとこんな感じ。文字もいろいろとカスタマイズできる。すごいな、IFTTT!
ただDropbox経由なのでホントに回線が切れっぱなしだと通知は来ないんだけどね。
機械学習の途中経過報告とかにも使えそう。


機械学習で株価予測やってみたが

今更かよってネタだけどTensorFlow使って株価予測をやってる。
やってみたけどうまくいかないって内容だけど、、、現時点での備忘録。

1)データセット
スクレイピングなどのデータ収集方法は置いといて、、、
学習データには日付、日経平均、JASDAQ指数、ドル、ユーロと予想の対象となる株価(今回はトヨタ 7203)を使ってる。売買の手数料については無視(俺の場合だと信用取引で100万円betしても税込388円だし)。

ユーロが1991年1月から導入されたとかなんとかで、自分が収集した方法ではこのユーロが一番データ数が少ない。1991年1月から2017年6月末までを学習データ、7月から12月末までをテストデータとしてデータセットを作成。
寄りで買うか売り、引けでその反対の売買をする。信用取引で売りからもあり。宵越しの株は持たない。

1つのデータは以下のようなデータを持つ。
横幅はn日分、縦は項目(上記の日付、日経平均など)、奥行きが4つで始値、高値、安値、終値の4つ。悩んだけど、出来高は考慮外。

正解は 終値/始値 の値をベースに9種類とした。ざっくり下のような感じ。5から8はマイナス側。
0 +6%超
1 +4%〜+6%
2 +2%〜+4%
3 0%〜+2%
4 変化なし

確率分布図1
↑理想的には確率分布を求めるイメージ。
あと、学習には使わないけど実際にbetした場合の利率(一番知りたい値だよね)を計算するために 終値/始値 の値も入れてる。


2)NNの構成
上記のようなデータにしたのはCNN(畳み込み)で解いてみようとしたため。MNISTとかだと画像が白黒だったのでチャンネル(画像の奥行き)が1だったが、今回は4。Cifar10はカラー画像なので参考になるかも。
なお、今回のデータを4チャンネルの画像としてデータ化すると1つの値が8ビットしかないので無次元化すると高値も安値も同じ値になるかもしれないので注意(いや、俺が実際画像を作って気付いたんだけどね)。32bitの浮動小数点数で扱ってる。
畳み込みなのでCifar10みたいな畳み込み層2つにLRN層、ドロップアウト、全結合層、出力層ってよくある感じ。


3)損失関数、期待値
損失関数にはソフトマックス関数を使っているので各ラベルの確率を得ることができる。
画像認識なんかでは単に一番確率の高いのを選ぶのが一般的だけど、今回は確率分布を得ているつもりなので、期待値の高い方にbetすることにする。

確率分布図2
↑極端な例だけどこういう場合、確率が一番高いのはマイナス側にあるので売りがよさそうに見えるけど、買いと売りだと買いの方が期待値(面積だよね)が高くなるので買いにbetする。この期待値うんぬんは学習時には関係なくて、テストデータの検証でのみ使う。


4)結果

TensorBoadによる損失関数の値変化
↑学習するやん!と喜んだのだが、、、学習が進むめば進むほど損失関数の値が学習用のデータに対する値と、テスト用のデータに対する値でどんどん乖離していった。
的中率や肝心の利率も同様で、正解率はせいぜい43%程度、半年での利率は0から-3%という結果に(パラメータをいじってもたいしてよくならなかった)。

ちなみに学習データに対してはツイートもしたけど、、、



こういう値がテストデータで出るのを期待してたんだが。
データ数少ないよねえ、、、1991年からで5,400個程度だしなあ。


5)雑感
こういうわけのわからん株価データに対してもちゃんと学習が進むんだなあ、というのには感心した。NNの表現力というか。機械学習やTensorFlowの研究者、開発者にほんとリスペクトである。

それと相場ってのはやっぱり掴み所がないんだねえ。26年間のデータに対し90%正解率あるモデルでも、いざ新しいデータに対しては50%いかないんだもの。


動くようになったらNNの構成やパラーメータをいじったりしないと、と思ってたけどそれ以前に圧倒的にデータ数が足りないみたい。ここをなんとかしないとねえ、、、


SceneKitで子ボーンの回転

ここのところ3DエディターっぽいmacOSアプリを作ってて、SceneKitでボーンを自在に回転させようとしたらえらい苦労した話。

Blenderで作成したオブジェクト

↑Blenderで作成したボーンを持つオブジェクト。これをdaeで出力。

daeファイルを表示

↑そのdaeファイルをSceneKitでインポートして表示(画像は作成中のアプリの一部、Xcodeではない)。ここまではええねん。

任意の子ボーンを任意の軸まわりで回転させたいわけだが、、、話を簡単にするためにz軸周りに回転させる、とする。
この「z軸周り」というのは世界座標系でのz軸ね。
一番の親ボーンの回転はできる。問題はその子ボーン、さらにその子ボーン、、、などを意図通り回転させられないことだ。

あるボーンを回転させても、それ以下の子ボーンオブジェクト(SCNNode)の持つ姿勢(orientationプロパティ)を表すクォータニオンは変わらない。
なので、ターゲットの子ボーンオブジェクトを回転させようとすれば、その親ボーンの姿勢を考慮して、さらにその親の、、、と一番の親まで考慮しなければならない、、、?なんか面倒だよなあ?
と思ってたらリファレンスに

- (SCNMatrix4)convertTransform:(SCNMatrix4)transform toNode:(SCNNode *)node;
- (SCNMatrix4)convertTransform:(SCNMatrix4)transform fromNode:(SCNNode *)node;

などのメソッドがあってnodeにnilを使えば世界座標系に変換できるから、ああ、これを使えばいいんだな、と思って取り組むもうまくいかなかった。

結局試行錯誤の結果、、、処理の流れとしては自分の姿勢を世界座標で表してそこでz軸周りの回転を与え、それを元にもどしてやる、という流れだとうまくいった。世界座標に変換するのに上記のメソッドでいけそうなのもんだけど、うまくいかず結局自作(GetQuaternionToWorldForBone)した。
↓コードはこんな感じで。MySceneKitUtilityなどところどころ自作のUtilityメソッドがあるけど、GLKitベースのメソッドをSceneKitで使えるようにしてるだけなのでここでは省略。

{
    SCNQuaternion(^GetQuaternionToWorldForBone)(SCNNode *) = ^(SCNNode *targetNode) {
            //世界座標に変換のためのquaternionを得る(自分自身は含まない)
            
            SCNQuaternion quat = QuaternionIdentity;
            
            while (1) {
                if ([targetNode.name isEqualToString:TOP_BONE_NAME]) {
                    break;
                }

                SCNNode *pNode = [targetNode parentNode];
                quat = [MySceneKitUtility quaternionMultiplyLeft:pNode.orientation right:quat];
                targetNode = pNode;
            }
            return quat;
    };


        SCNQuaternion toWorldQuat = GetQuaternionToWorldForBone(bone);
        SCNQuaternion toLocalQuat = [MySceneKitUtility scnQuaternionInvert:toWorldQuat];
        
        SCNQuaternion localBoneQuat = bone.orientation;
        SCNQuaternion worldBoneQuat = [MySceneKitUtility quaternionMultiplyLeft:toWorldQuat right:localBoneQuat];
        
        SCNQuaternion newWorldBoneQuat = [MySceneKitUtility quaternionMultiplyLeft:dQ right:worldBoneQuat];
        SCNQuaternion newLocalBoneQuat = [MySceneKitUtility quaternionMultiplyLeft:toLocalQuat right:newWorldBoneQuat];

        bone.orientation = newLocalBoneQuat;
}
z軸周りに少し回転させるクォータニオンがdQ。
一番大元のボーンには TOP_BONE_NAME という名前を付けてる。
while(1) なんてやる場合には無限ループにならないようにしましょう(省略してるけど)。

実際に動かすと以下のような感じ。z軸周りに30度回転させてる。

全体を回転

↑一番大元のボーンを対象にすると全体が回転する。

子ボーンを回転(1)

↑途中のボーンを回転。

子ボーンを回転(2)

↑下側のボーンとか。

この方法だとBlenderから持って来たファイルを使う時のY-upとかも気にしなくていい。どちらにせよとにかくz軸周りに回転する。あー、すっきりした。


100均とANKERの車内用バッテリーチャージャー

セリアの車内用バッテリーチャージャー

↑100均(セリア)で車内用のバッテリーチャージャーを買ったんだ(以前使ってたのが壊れたので)。
ところが、これが充電してくれない、、、いや、わずかに増えるから壊れてるわけじゃないんだ。でもアプリとか起動してると少しずつ減って行くほどなんだよ。まあ、100均だから仕方ないか、とあきらめてAmazonで別なのを買ってみた。



↑これを使ってみたら全然違う!アプリ使っててもちゃんと充電されていく!100均のが800mAって書いてて、ANKERのは1ポート最大2.4Aとあるんだけど、この違いなのかな?比較できる値なのかよくわからんのだけど。
ともかく、100均よりはだいぶ高い(といっても1000円もしないのだが)けど期待通りに動いてくれるのでめでたし、めでたし。



  TopPage  



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

FC2Ad