<実践>HomeCredit作業記録(6) - 進捗とメモ

f:id:kurupical:20180615203929p:plain:w500

いいところまで来ました。
淡々と、定石通りにやってるだけのつもりなんですが・・・

ただ、手法のPrivate shareが禁止ということで、規約にひっかかりそうなので過去の記事を非公開にしました。

いま使っているLightGBMがブラックボックスすぎるので、そこの勉強をしたいです。

ここまで作業時間40時間くらい、思ったことややったことメモ

Kaggleだと限界がわかる

自分が1位じゃないかぎり、スコアの伸びしろがわかるっていうのは大きいですね。
実務だと一人でやるので、どこまでできるのかが不透明です。

しばらく誰も100m10秒切れなかったのに切ったとたん、他の人も10秒切れるようになった、みたいな感じですね
1位じゃないからまだまだやれるんだなというのがわかるのは大きいです。

Discussionが参考になる

今私は特徴量作成をやっているのですが、それすらも自動化する「Featuretools」というツールが出てるみたいですね。
Kaggleのディスカッションは以下です。
https://www.kaggle.com/willkoehrsen/intro-to-tuning-automated-feature-engineering

まだ使っていないのですが、いつかは使ってみたいです。

仕事でもプライベートでも作れるEDAツールにする

多分次の分析でもやるだろうな〜と思ったデータ加工のプログラムは、再利用可能+テストを書いて多少の品質を確保しています。

データサイエンティストの人を片っ端からフォローした

議論を追ったりするとめっちゃ勉強になります!

<調査>Kaggle過去コンペ上位入賞者のSolutionから学ぶ(1)

blog.kaggle.com
この記事に、「コンペに優勝するためには過去のコンペから学ぶことが大事だ」と書いてあったので、過去のコンペを調べることにしました。
過去のコンペのDiscussionを見ると、上位入賞者が解法を公開してくれています。

HomeCreditと同じ「分類」タスクのコンペを探しました。

以下、自分のメモ書きです。

Porto Seguro’s Safe Driver Prediction 1st solution

Porto Seguro’s Safe Driver Prediction | Kaggle

1ファイルのコンペ(主キー1に対して、レコードがN行みたいなことがない)

手法

  • Representation learning
    • DAE(Deep Auto Encoder)による特徴抽出。
    • Feature Engineeringが嫌い!それを自動化してこそAIだ!ということだそうです。
  • Normalizationには"RankGauss"を使った
  • inputSwapNoize

    • 0.15 means 15% of features replaced by values from another row.
    • これって以下の認識であってるんでしょうか?
      • DAEはノイズ除去の役割を果たす
      • 人工的にノイズを作るため、各特徴の15%は別の行とスワップする
        f:id:kurupical:20180605205648p:plain:w500
  • GANはやってみたけど失敗

    • 連続値とカテゴリ変数が混在する場合のGANは難しい
    • 疑問:GANを何に使おうとしたのでしょうか?
  • NN5個とLGBM1個をstacking(重みはすべて同じ)

感想

  • DAEによる特徴抽出が強い!今のコンペでもやってみたいです。ただ、主キー1に対してN行データがある場合は入力が動的になるから難しいですね。この問題が解決できたら、いよいよFeature Engineeringが要らなくなる?
  • inputSwapNoiseの考え方が個人的にとても面白かったです。

<実践>HomeCredit作業記録(5) - FeatureEngineering続き

Kaggle挑戦中です!
債務不履行になる人を予想するタスクです。
Home Credit Default Risk | Kaggle

やったこと

  • FeatureEngineering

FeatureEngineering

前回の記事では、一つ一つの特徴量をじっくり見ていました
kurupical.hatenablog.com

ですが、やっぱりキリがないので、機械的に全部のデータをいろいろな形で集計しました。
参考にしたKernelは以下などです。
Good_fun_with_LigthGBM | Kaggle
LightGBM - all tables included [0.778] | Kaggle

結果

score: 0.735 -> 0.782
順位: 214位/1642 (上位15%)

今後やっていきたいこと

いったんFeatureEngineeringはやめて他のタスクを。

  • パラメタチューニング
    RandomisedSearch, GridSearch以外もいろいろ試したいです。

  • 別のアルゴリズムも使う
    今はLightGBMを使っているのですが、XGBoostやDeepLearningも試してみたいです。

  • アンサンブル学習
    複数の学習器でStackingをやりたいです。

その他

参考になったDiscussion


  1. この特徴量生成は正直引きました。え、そこまでやるの?みたいな
    Hybrid Jeepy and LGB | Kaggle
    いろんな特徴量を足したり引いたりミニマム取ったりtanh取ったり・・・
    そこまでやるならDeepLearningでやればよいのでは?なんて思ったり。
    でも、こういうアプローチもあるのかと勉強になりました。


  2. Home Credit Default Risk | Kaggle
    書き込んでみました。笑 質問した後、自分の凡ミスに気づくという・・・

<実践>「過去の閲覧履歴から最適な街コンを勧める」システムのコンペに参加してみた

AIエンジニア(データサイエンティスト?)として働き出して1ヶ月。
なんだかんだ勉強して1年。
いろんなアルゴリズムも触ったし、いちど自分の実力を試してみたいなと思い、データサイエンスのコンペを探していました。

探したのは

そして、signateでいいコンペを見つけたので参加することにしました。
できるだけ、AI・機械学習界隈の人でなくてもわかるように書いていきます。

目次

  1. コンペの概要
  2. 取組み
    • 2-1. 取組みの流れ(ざっくり)
    • 2-2. 使用した機械学習アルゴリズム
    • 2-3. 前処理(preprocession)
    • 2-4. パラメタチューニング
  3. 結果
  4. 感想
  5. 告知

1. コンペの概要

コレに参加しました。
signate.jp

概要を箇条書きで書きます。

  • 目的

    • ユーザーに最適な街コンを勧める。以下のようなイメージです。 f:id:kurupical:20180522190400p:plain
  • 評価基準

    • 概要

      • 2017/9/24までのデータを基に、9/25〜9/31に開催される街コンをおすすめする
      • ユーザごとに「おすすめ街コンランキング」を20位まで提案する
    • 評価

      • nDCG(具体的な計算式は上記サイト参照)
        • 提案した街コンについて、9/25〜9/31に実際に「参加した=7点、ブックマークした=3点、見た=1点」として、自分が提案したランキングの上から計算
        • ランキングの上位のほうが多く得点が与えられる。 例:(1位=参加、2位=不参加、3位=見た) > (1位=不参加、2位=参加、3位=見た)
        • 理想の提案の得点を1としたときの自分の提案の得点がスコアになる。 詳しく知りたい方は  レコメンドつれづれ ~第3回 レコメンド精度の評価方法を学ぶ~ - Platinum Data Blog by BrainPad
  • 与えられるデータ

    • ユーザーの属性
      • 年齢
      • 住所
    • 過去開催されたイベントの詳細
      • 都道府県
      • 開催日時
      • 女性/男性の参加可能年齢範囲
      • イベントジャンル
    • ユーザーの閲覧ログ
      • どのユーザーが
      • どのイベントに
      • (参加した/ブックマークした/見た)
    • 評価対象のユーザー一覧(2886件)
      • ユーザーID
  • 提出データ

    • ユーザーごとのおすすめイベント
      • ユーザー数(2886)×20イベント

2. 取組み

2-1. 取組みの流れ

今回、以下のような流れで取り組みました。 * データの前処理 * パラメタチューニング

2-2. 使用した機械学習アルゴリズム

教師なし学習であるNearest Neighborを使いました。
ざっくり言うと、

  • データを与えると、
  • そのデータと似たものを出力する

アルゴリズムです。

簡単に例を挙げます。

f:id:kurupical:20180522215526p:plain:w500

やや雑ですが、食べ物を「甘いー辛い」属性、「洋食ー和食」属性の2軸にとりました。

ここで、「エビフライが好きな人が好きな食べ物は何か?」をNearestNeighborを使って解決します

1.データ「エビフライ」を与え、座標上に点を描きます。
f:id:kurupical:20180522220455p:plain:w500

2.エビフライの近くにあるものが答えです。(つまり、ここでは「オムライス」と「グラタン」。)
f:id:kurupical:20180522220535p:plain:w500

実際の食べ物の趣向は、2軸では表現できません。重いー軽い、高いー安いなど、人によって何個も判断基準があると思います。
実際、NearestNeighborでは、2次元に限らず、3次元以上のデータも扱えます。

このNearestNeighborを、婚活データに適用してみようと思いました。

2-3. 前処理

NearestNeighborを婚活データに適用します。
大きな方針は、「ユーザーの属性」を過去の参加履歴から計算し、開催されるイベントと属性が近いもの順にリコメンドしていくこととしました。

工夫点は以下の通りです。

  • ユーザーの属性を平均化する。

    • 例:参加費4000円のイベントに1回、参加費8000円のイベントに1回参加→「参加費」属性は、6000円
  • 数値化する

    • 「距離を計算する」アルゴリズムのため、全ての項目は数字である必要があります。
      以下項目は数字項目ではないため、数字にしました。
      変換の際、「その項目の本質的な情報を抜く」ことが大事です。
      • 「日付」
        • 本質的な情報は、「参加時間帯」。(平日休みの人は平日昼間のイベントを見るはず・・・)
        • 「平日昼間」「平日夜(19:00〜)」「休日昼」「休日夜」の4属性に変換しました。
      • 都道府県」
        • 本質的な情報は、「位置」。(東京にいる人に大阪のイベントは勧めないが、神奈川のイベントは勧めるかも)
        • 「緯度」「経度」に変換しました。
  • 重み付け

    • 時系列重み付け
      • 例:参加費4000円のイベントに昨日、参加費8000円のイベントに1年前に参加→「参加費」属性は4500円(直近参加の4000円に寄せる)
      • 具体的に言うと、ログをさかのぼるたびに0.99を掛けることで、昔のイベントの「重み」を軽くしました。
    • ログ重み付け
      • 「参加したイベント」のログは大きい重み付け、「見ただけ」のログは小さい重み付けをそれぞれ行い、ユーザーの属性に反映しました。

2-4. パラメータチューニング

ユーザーの属性を計算したら、次は「どの項目を重要視するか?」を考慮します。(パラメータチューニング)
例えば、ユーザーの属性が(参加費4000円付近が多い、平日昼間に参加が多い、東京付近が多い)だとしたとき、次のどのイベントを勧めるのが一番見込みがありそうですか?

  1. 参加費4000円、平日夜間、大阪 (「参加費」を重要視)
  2. 参加費5000円、平日昼間、大阪 (「時間帯」を重要視)
  3. 参加費5000円、平日夜間、東京 (「場所」を重要視)

多くの人は3.を選択するでしょう。つまり、数ある項目のなかで「場所」を重要視しているということになります。(=「場所」に大きな重みをかけている

パラメータの決め方は以下の流れで行いました。

  1. ドメイン知識(=常識:場所は大事でしょ、等)を考慮
  2. 以下を繰り返す
  3. 常識を考慮してランダムにパラメータを設定
  4. 予想、性能評価を行う
  5. 手順2の結果を検証し、パラメータに当たりをつける
  6. 3.により「常識」をアップデートし、2.に戻る

3. 結果

29位/158でした。
https://signate.jp/competitions/67/leaderboard

4.感想

「仮説」と「検証」の粒度が荒い
ここでいう仮説と検証は、

  • 仮説とは、AすればBになる
  • 検証とは、Bになったかを確かめる

ですが、仮説を立てるための材料が揃えられませんでした。

  • 結果を評価するための材料が「スコア」という非常に荒い材料しかなかった
    ex. 年齢別、都道府県別など、項目ごとにスコアがどうなっているのかを確かめる、など
  • データの分布を見きれなかった(機械学習というブラックボックスに投げ過ぎた)
    ex. 20代の人は比較的参加費が安いものに参加している→何か予想に反映できないか、など

複数のアルゴリズムを試したかった
xgboostのpairwiseを少し試したのですが、使い方がわからずNearestNeighborの半分くらいのスコアしか出ませんでした。
いろいろ試してみたいものはありました

  • xgboost(pairwise)
  • catboost(pairwise)
  • DeepLearning(RankNet)
    qiita.com
  • DeepLearning(ListNet)
    qiita.com

次回同じ課題が来た時はリベンジします。

これは機械学習なのか・・・?
「AIというブラックボックスが全て判断してくれる」という考え方は駄目、だけど、今回はゴリゴリやり過ぎたような気がしなくもないです・・・。

10日という短納期にしては悪くない出来だったなと思っていますが、時間があればもっとこうしたかった!!というのが多いです。。。

5. 告知

誰かコレ一緒にやりませんか?
Competitions | Kaggle

kaggleでメダル取りたいです。週5時間程度の作業で大丈夫です。
興味ある方はkurupicalあっとGmailかブログコメントによろしくお願いしますm( )m
プログラムの心得と意欲があれば初心者でもOKです

<基礎学習>機械学習、DeepLearningを使わずにFXの分析をしてみた(2)

以下前回記事の続きです。
kurupical.hatenablog.com

概要

  • 未知のデータに対して予想できるようにした
  • 「次の日上がるか下がるか」ではなく、「N%上がるが先か下がるが先か」を当てるようにした
  • いろんなパラメータをグリッドサーチ(総当たり)した

目次

  1. 前回の反省
  2. 予想の対象を変更
  3. 与えるパラメータ
  4. 結果、考察
  5. 今後

1.前回の反省

前回の記事の「2-2.セグメントの組み合わせ」で正解率を出し、このトレードアルゴリズムは使えそうだ・・・と思ったのですが、初歩的なミスがありました。
「未知のデータに対する検証をしていない」ことです。

このアルゴリズムは未来においても有効なのか?を示す必要があります。

f:id:kurupical:20180418193502p:plain

前回の考え方は、集計に使うデータと性能評価に使うデータが同じでした。
そのため、その期間の値動きに特化した予想になってしまいます。たとえ、性能評価をして勝率が高かったからといって、未来(未知)の値動きに対応できるかは保証できません。

ですので、このような考え方にしました

f:id:kurupical:20180418193944p:plain

上記の図は、
(1) 2016/1〜2016/6のデータで集計をし、2016/7〜2016/9の値動きで性能評価を行う
(2) 2016/4〜2016/9のデータで集計をし、2016/10〜2016/12の値動きで性能評価を行う



と最新のデータまで続け、それぞれで行った性能評価を集計し、アルゴリズムの性能評価とします。
これで、「未知のデータに対する性能評価」を行うことができます。
・・・いろいろDeepLearningとか機械学習をやってるのに我ながら信じられないミスでした。笑

2. 予想の対象を変更

2-1. 前回までの予想対象とその難点

前回は、「過去の1時間足のチャートの各種データをクロス集計して、次の1時間で上がるか下がるか」を予想するプログラムでした。
このアルゴリズムには難点があります。

1時間足なので利幅が少ない→手数料負けしがち
以下を仮定します。

  • ドル円で1時間で動く値幅の平均が5pips(0.05円)だとします。
  • 取引にかかるコストは一定で、0.4pipsとスリップ幅0.6pipsみて1pips(0.01円)。

この場合、勝てば5 - 1 = +4pips、負ければ -5 -1 = -6pipsとなります。
期待値プラスになるための勝率を { \displaystyle p }と置くと、

{ \displaystyle
4*p + (-6) * (1-p) > 0
} を満たす必要があります。

これを解くと、{ \displaystyle p > 0.6 }が求まります。
60%の勝率を保ってイーブンというのはかなり厳しいですね。

2-2. あたらしい予想対象

なので、次の1時間であがるか下がるかではなく、「N%上がるかN%上がるかどっちが先か」を予想することにしました。
そうすることで、利幅を自分で決めることができます。
たとえば、

  • N=0.5%(ドル円だとおおよそ50pips(0.5円)

この場合、先ほどの計算を考えると、勝てば 50-1=+49pips、負ければ-50-1=-51pipsとなります。
同様に期待値がプラスになるための勝率を以下式により求めます。

{ \displaystyle
49*p + (-51) * (1-p) > 0
}

これを解くと、{ \displaystyle p > 0.51 }が求まります。
51%の勝率であればなんとかなりそうですね。

3.与えるパラメータ

あとは、パラメータを試行錯誤するだけです。
以下のパラメータを調整しました。

  • 前回記事2-1.優位性のありそうなセグメントの抽出
    • 必要なデータ量(前回記事のa)
    • クロス集計の偏り具合(前回記事のb) (e.g. MA25=0.9975/MA75=1.0025の場合60%の確率で「N%下がるが先」)
    • 組み合わせ数(前回記事のc)
  • 予想する値動き幅N%
  • 値動き集計、性能評価に使うデータの期間
  • チャートの足(15分足、30分足、1時間足、2時間足)

4. 結果と考察

4-1. 結果

1時間足で5年分検証した結果が下図です。
f:id:kurupical:20180418202101p:plain:w500
縦軸の黄色で塗りつぶしているのが「勝率」、その右隣が「取引件数」です。

4-2. 考察

4-2-1. 数字だけを見る

勝率が1(=100%)になっているものもありますが、取引件数が少なすぎるので信用できません。
大数の法則からも、1000件以上あるデータで上から探していくと...
赤文字で塗っている行が、なんと1420回取引して勝率79%。
む、無敵だ・・・

早速採用!!と思いましたが、11月に作ったDeepLearningのアルゴリズムをろくすっぽ検証もせず取引したらアルゴリズムの考え方がバグっていて、5000円を失ったことを思い出しました。。
今回は注意深く。
念のため、月ごとの取引件数を勝率を見てみました。

f:id:kurupical:20180418202817p:plain:w500

なんと。。2016年から全く取引してないです。
これじゃ使えませんね。(過学習とはいえ、2014/4〜2014/6で200回トレードの勝率100%なのは驚異的です。なんかバグってそう・・・)

他の勝率高いデータも確認してみましたが、特定月に取引が偏っているものが多く、実運用はできそうにないものが多かったです。
全部のデータについて取引の分布をしらみつぶしに見ていくのはしんどいので、集計に工夫をしました。

4-2-2. 取引件数のばらつきを考慮してアルゴリズムを選定する

集計を以下のようにしました。

f:id:kurupical:20180418204307p:plain:w500

右側に、月ごとの取引件数の平均と標準偏差を取りました。
標準偏差が低い=月ごとの取引件数にばらつきが少ないということになります。
このデータで、「勝率60%以上、取引件数1000件以上」で標準偏差が低い順に並べてみました。

上2つのデータについて、月ごとの取引回数を確認してみます。
f:id:kurupical:20180418204004p:plain:w500
[f:id:kurupical:20180418210021p:plain:w500
] 2個目は特になかなか安定してそうです。
1個目は利幅0.5%、2個目は利幅0.1%に設定しているので、2.で説明した「必要な勝率」が違うことに注意が必要です。
勝率やグラフは2個目のグラフがキレイですが、実際どちらが利益を生むかは別問題です。

5. 今後

いろいろなパラメータでチューニングして、「利幅」と「勝率/取引のばらつき」が一番良いものを選びたいと思います。
仮想通貨にも同じアルゴリズムを適用してどうなるかもあわせて検証しています。

あとは、「資金管理」についても考えていかなくては・・・。

<基礎学習>機械学習、DeepLearningを使わずにFXの分析をしてみた

機械学習のライブラリや考え方などの勉強もさながら、「そもそもデータ分析ってどうやるの?どうやって結果を説明すりゃーいーの?」とふと思いまして、データ分析のいろはを勉強しようと以下の本を手に取りました。(厳密にいうと、この本がインストールされたKindleを手に取った)

とても勉強になりました。特に1章。

  • 「データ分析の設計」が必要
    • 問題領域を決める
    • 問題を解決するための評価軸を決める
    • 要因を列挙する

上記プロセスを踏んでいろいろやった結果、なかなか有意義な結果が出たんでブログにしました。
まだ途中ですが・・・

※このブログに書いてあることは投資およびデータ分析の素人が書いたものです。
 投資は自己責任でお願いします。
 

目次

  • 概要
  • データ分析
    • データの前処理
    • 集計
  • 今後
  • 感想
  • 参考サイト

概要

  • 過去10年分の1時間足について、以下テクニカル指標を使って、「次の1時間足で上がるか下がるか」を予想する

データ分析

1.データの前処理

1−1.データのダウンロード、時間足へ加工

ここに貼ってあるリンクから、1分足18年分USDJPYをダウンロードしました。
www.forextester.jp

1分足を60足刻みに加工することで、1時間足のチャートを作成しました。

1−2.各種テクニカル指標の加工

集計しやすくするため、移動平均線ボリンジャーバンド、前日の価格を「当日比」としました。
データ分布はこんな感じ(以下例は移動平均線です)

f:id:kurupical:20180329212446p:plain:w600

また、クロス集計がしやすいように0.25%刻みでデータをまとめました。
データまとめ後はこんな感じ

f:id:kurupical:20180329212548p:plain:w600

2.集計

2−1.優位性のありそうなセグメントの抽出

移動平均線ボリンジャーバンド、前日の価格、すべての組み合わせに対してクロス集計して偏りがあるところを調べます

f:id:kurupical:20180329214504p:plain:w600

上記のエクセルは、25日ボリバンと25日移動平均線のクロス集計です。
これの見方ですが、たとえば緑で塗りつぶされているところの意味は以下の通りです。

  • 25日ボリバンが当日比98.5%
  • 25日移動平均線が当日比99.25%
  • 上2つを満たすデータが7件ある
  • 7件のうち85.7%は、1時間後下がっている

すべての指標の組み合わせでクロス集計を行い、以下を満たすデータを抽出していきます

  • データ量(count)が十分であること ( count > a)
  • 1時間後の値動きがどちらかに偏っていること( abs(0.5-b) < mean)

今回は、a=500、b = 0.07としました。
(セグメントのデータが500件以上あり、値下げ確率が43%以下もしくは57%以上)

抽出の結果は以下のとおりです
f:id:kurupical:20180329215635p:plain:w500
※MA=移動平均線、BB=ボリバン

このデータからわかることは、例えば1行目だと、「MA25=0.995、MA75=0.9975の場合、57.5%の確率で次の1時間足は上がる」ということです。
つまりエントリーのシグナルを出力してることになります!

2−2.セグメントの組み合わせ

上記クロス集計で抽出した条件を満たせばトレード」で、少し勝ちトレードに近づけるかもしれないと思います。
ここでもう少し考えてみました。
ある時間足について「上記クロス集計で抽出した条件をc個以上満たせばトレード」にしたらどうなるのか?を検証しました。

結果
n=81243
c=1 : トレード回数=6963回、正解率=57.4%
c=2 : トレード回数=1415回、正解率=59.6%
c=3 : トレード回数=267回、正解率=63.2%

f:id:kurupical:20180329221730p:plain:w500

Cの数を上げると、正解率は上がりますがトレード回数が少なくなります。
トレード回数が多いほうが分散が下がりリスクが少ないです。
なので、トレード回数と正解率のトレードオフを考える必要があります。

3.今後

  • やってみたいこと

    • パラメータチューニング。a,b,cの値を変えればどうなるかを試す。
    • クロス集計するテクニカル指標を増やす。MACD加重移動平均ストキャスティクスなど・・・
    • up/downではなく、値動きを見てみる
  • 課題

    • 1時間足のだと手数料負けして多分儲からない。バイナリオプションでの運用にならいけるかも?

4.感想

次の派遣先で「データ分析の結果を報告することもお願いする」と言われたので練習がてらやってみましたが、時間がかかるわりにまとめるの下手・・・。これだけ書くのに2時間かかりました。

あとは単純に、データ分析のプログラムの書き方がむずかしい。
データ加工のやり方に四苦八苦してます。numpy、pandasむずいです。。

今は、以下の方針でやってます。

  • やりたいことを書き出してから始める(いきあたりばったりにやらない)
  • 書いたプログラムは消さない(関数に改良を重ねるときはコピペして、pivot_table2とか命名する。)
  • コードの綺麗さは2の次。コピペ上等。
  • 処理速度は大事にする。
  • データ処理するとき、numpy/pandasですでに実装されている関数がないか調べる
    • 意外と何でもある。今回画像で貼ってるエクセルは、pythonで出力したものをほぼそのまま貼ってるだけです。pivot_tableで、セルの結合なんかもやってくれて最強。。

5.参考サイト

以下ブログで最近はじまったシリーズ。マジ神です。
we.love-profit.com

<基礎学習>機械学習(ランダムフォレスト)でFX - 為替データの特徴量抽出

もくじ

  • 近況報告
  • ランダムフォレストでFX分析してみた
  • 感想など

近況報告

あけましておめでとうございますm(_ _)m
今年初のブログです。

去年12月に新しい会社に入り、現在はAI関係ではない仕事をやっています。
4月から念願だった機械学習関係のプロジェクトに入ることになりました。

DeepLearningは勉強していたのですが機械学習やscikit-learnは全然勉強していなかったので
昔買った本を読んで勉強中です。。


現在は、これの第1版を読んでいます。

このなかで、「ランダムフォレスト」という手法が面白かったので、
チュートリアルがてらFXの分析を実装してみました。
今日は、それを記事にしてみました。

ランダムフォレストでFX分析してみた

目標

  • numpyのお作法をちゃんと守る(for文を使わず行列計算)
  • ランダムフォレストの理論を理解する

ランダムフォレストとは?

弱い決定木をたくさん学習させて多数決する、的なやつです
説明は省略します。。

何をするの?

<翌日の騰落を当てる>にあたり、FXでよくきく「移動平均線」「ボリンジャーバンド」といったテクニカル指標はどの程度重要な指標となるのか?を分析してみました。

具体的には、移動平均線(25日,75日,200日)とボリバン(25日,75日,200日それぞれの±2σ、3σ)と前日終値の合計16変数を調べました。
データは過去10年分のUSDJPYの日足です。

ソースコード

総作成時間:4時間
参考書籍:上で紹介した書籍

import pandas as pd
import numpy as np
from sklearn.preprocessing import OneHotEncoder
from copy import copy
from sklearn.ensemble import RandomForestClassifier
import matplotlib as plt

IDX_HAZIMENE = 1

feature_name = []

def diviveData(term, in_data):
    out_data = np.array([[]])
    for i in range(len(in_data) - term):
        if i == 0:
            out_data = [in_data[i:i+term]]
        else:
            out_data = np.append(out_data, [in_data[i:i+term]],axis=0)

    return out_data

def movingaverage(in_data, isAppend=False):
    '''
    移動平均線のデータを作成
    :param in_data: 作成に使うデータ
    :param isAppend: append対象ならtrue
    :return: MAのデータ np.array([len(in_data)])
    '''
    out_data = np.sum(in_data, axis=1) / len(in_data[0])
    if isAppend:
        feature_name.append("movingaverage{}".format(len(in_data[0])))
    return out_data

def bollingerband(in_data, sigma):
    '''
    ボリバンのデータを作成
    :param in_data: 作成に使うデータ
    :param sigma: ボリバンのシグマ
    :return: ボリバンのデータ np.array([len(in_data)]
    '''
    variance = np.sqrt(np.sum((in_data - movingaverage(in_data).reshape(-1,1)).astype(np.float64) ** 2, axis=1) / len(in_data[0]))
    # in_dataの最終行が最新の終値になっているので,in_data[:,-1]
    out_data = in_data[:, -1] + variance * sigma
    feature_name.append("bolingerband_date{}/{}sigma".format(len(in_data[0]),sigma))
    return out_data

def make_y_data(in_data):
    tomorrow_data = copy(in_data[1:])
    out_data = in_data[:-1] - tomorrow_data
    out_data = np.where(out_data<0, 0, 1)

    # one-hotにencodeするための準備
    out_data = out_data.reshape(-1,1)
    encoder = OneHotEncoder(n_values=2)

    out_data = encoder.fit_transform(out_data).toarray()
    return out_data

data = (pd.read_csv("data/USDJPY.csv")).values

data = data[:, 1]

div25 = diviveData(25, data)
div75 = diviveData(75, data)
div200 = diviveData(200, data)

pre_term = 0

#=================
# ラベル作成
#=================
y_data = make_y_data(data)

#=================
# データ作成
#=================
# 2次元にしないとnp.appendがとおらないので整形
x_data = data.reshape(1, -1)

for divdata, term in zip([div25, div75, div200], [25, 75, 200]):
    # データは少ない方にあわせる
    term_dif = term - pre_term
    x_data = x_data[:,term_dif:]
    pre_term = term

    # MA,BollingerBandをデータに加える
    x_data = np.append(x_data, np.array([bollingerband(divdata, sigma=2)]), axis=0)
    x_data = np.append(x_data, np.array([bollingerband(divdata, sigma=3)]), axis=0)
    x_data = np.append(x_data, np.array([bollingerband(divdata, sigma=-2)]), axis=0)
    x_data = np.append(x_data, np.array([bollingerband(divdata, sigma=-3)]), axis=0)
    x_data = np.append(x_data, np.array([movingaverage(divdata, isAppend=True)]), axis=0)

# 前日の価格
x_data = np.append(x_data, [data[200-1:-1]], axis=0)
feature_name.append("yesterday")

# 当日の価格を1とした相対的な値に変換
x_data = x_data / x_data[0,:]

# 当日の価格を教師データからのぞく
x_data = x_data[1:,]

#=================
# 機械学習にかける
#=================

# 前処理
x_data = x_data.transpose()

# x_dataは最新日のデータは除く(正解がわからないため)
x_data = x_data[:-1]
# y_dataをx_dataにあわせる
y_data = y_data[-len(x_data):]

clf = RandomForestClassifier(criterion='entropy', # 不純度
                             n_estimators=1000,  # 決定木の数
                             random_state=1,    # 乱数を固定
                             n_jobs=-1,         # 使うコア数(-1:ぜんぶ)
                             max_depth=10,      # 決定木の深さ
                             max_features='auto')   # 決定木で使う特徴量("auto":sqrt(特徴量数))

clf.fit(x_data, y_data)

importances = clf.feature_importances_
indices = np.argsort(importances)[::-1]

for i in range(x_data.shape[1]):
    print("{} : {:.6f}".format(feature_name[indices[i]], importances[indices[i]]))


print(clf.feature_importances_)

結果

movingaverage25 : 0.073916
bolingerband_date25/2sigma : 0.073495
yesterday : 0.070974
movingaverage200 : 0.069467
movingaverage75 : 0.069117
bolingerband_date25/3sigma : 0.065644
bolingerband_date25/-2sigma : 0.064432
bolingerband_date25/-3sigma : 0.062237
bolingerband_date75/3sigma : 0.059920
bolingerband_date75/2sigma : 0.059884
bolingerband_date200/3sigma : 0.056606
bolingerband_date75/-2sigma : 0.056600
bolingerband_date75/-3sigma : 0.056141
bolingerband_date200/-3sigma : 0.054896
bolingerband_date200/-2sigma : 0.053710
bolingerband_date200/2sigma : 0.052961

25日移動平均線、25日の2sigmaボリバンが相対的に大事な指標みたいです。

感想

ディープラーニングとの違いって?

「(ディープラーニング以外の)機械学習は特徴量を絞らないと次元の呪いが〜〜〜」ってずっと思っていたのですが、ランダムフォレストは特徴量を増やしても大丈夫なんでしょうか。
そうなると、ディープラーニングと比べてメリットデメリットは何なんでしょう。
感覚的には

  • ディープラーニング > ランダムフォレスト

    • 表現幅が広い(ディープラーニングだと、ボリバンや移動平均線ではなく普通に200日分の為替データを入力としそうです。ランダムフォレストでそれをすると学習うまくいかなさそうです。なんとなく。)
  • ランダムフォレスト > ディープラーニング

    • 「なぜこの結論に至ったか」の説明がしやすいです。ランダムフォレストは上のように特徴量の重要度が出せるので・・・
    • 学習に必要なデータが少なくてすむ?(一般論)

こんな感じでしょうか。。

特徴量の重要度は、1変数でしかできない?

説明変数の組み合わせで重要度を出したいです。たとえば、25日移動平均線と25日+2sigmaボリバンを組み合わせるとどれだけ重要な特徴量になるのか?など調べられると嬉しいです。

パラメータ

パラメータチューニング全くしてないので、してみるとどうなるのか。とくに、木の深さってどれくらいが推奨されるのだろう。。とか思ったり。。

numpy

for文使わず綺麗にかけた!ような気がします。笑
ただ、ゴリ押ししている部分も多いので汚いですね
3ヶ月後に読んだらなんのこっちゃってなりそうです・・・

その他

  • 久々なので気合入れて書こうと思ったのですが最後の方投げやりです。。すいません。
  • 今週土曜日3/17に某所でDeepLearningのハンズオンをやります。
    定員30名に対して倍くらい人が来ているということもあり緊張しますが、がんばります!