<実践>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
    書き込んでみました。笑 質問した後、自分の凡ミスに気づくという・・・

<実践>HomeCredit作業記録(4) - FeatureEngineeringのためのツール作成

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

やったこと

FeatureEngineeringのつづき

  • 可視化ツールの作成
  • 特徴量の作成

可視化ツールの作成

f:id:kurupical:20180601223524j:plain:w500

def visualize_bar(df,
                  target_name="TARGET",
                  output_path=None):
    """
    分析対象のcolumnとTARGETの関係を棒グラフにする

    :param df:
        columns=[分析対象column, TARGET]
    :return:
    """

    column = list(df.drop([target_name], axis=1).columns)
    df_count = df.groupby(column).count().sort_index()
    print("columns_unique_num:{}".format(len(df_count.values)))

    if len(df_count.values) > 200:
        print("columns is too big to output bar")
        return

    yticks = []
    for index, count in zip(df_count.index, df_count.values):
        tmp = "{} (N={})".format(index, count[0])
        yticks.append(tmp)

    plt.figure(figsize=(8, len(df_count)*0.6))
    sns.barplot(x=target_name, y=column[0], data=df, orient="h")

    plt.axvline(df[target_name].mean(), ls=":", label="all_mean")
    for i in range(len(df_count.values)):
        plt.text(0, i, "N={}".format(df_count.values[i]))
    plt.legend()
    plt.savefig(output_path)
    plt.close()

このツールを作ったことで、自分が作った特徴量の評価がしやすくなりました。

特徴量の作成

データから、「顧客IDごとの、現在返済中借金の件数」という特徴量を作りました。
棒グラフがカラフルなのはデフォルト設定で、僕のセンスじゃないです。

f:id:kurupical:20180601223822j:plain:w500

現在返済中借金の件数が多いほど、債務不履行になる確率が高くなるのがわかります!

以下は学習器が出力する「分類に使った大事な特徴ランキング」なのですが、僕が作った項目(*ACTIVE_COUNT)が上位にランクインしています。(全部で200項目くらいあるうちの30位) f:id:kurupical:20180601224432p:plain:w500

まとめ

  • 特徴量作成が捗るツールを作った。
  • いい特徴量が1個つくれた。

こんな感じで、全体データを眺めては特徴量を作成する、というのをしばらくはもくもくと続けることになりそうです。

<実践>HomeCredit作業記録(3) - FeatureEngineeringのアプローチ

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

最近は特徴量エンジニアリング(FeatureEngineering)をやっています。

やっている中で気づいたことがあるのでブログにします

特徴量エンジニアリングのアプローチ

特徴量エンジニアリングを、ボトムアップでやっていたのですが、どうやら最初は「トップダウン」でやるほうがよさそうです。

※業務経験2ヶ月の新米データサイエンティストのいち考察として読んでいただければ幸いです。。

ボトムアップ

https://www.kaggle.com/c/home-credit-default-risk/data データの特徴量(=項目)を1つずつ吟味して、特徴量を生成する
以下は、「クレジットカードの返済期限を過ぎてしまった回数」という特徴量を作成しました。
(返済期限を過ぎた人が多いほど債務不履行になるのでは?という私の推測から)

def count_credit_day_overdue(df):
    """
    credit_day_overdue>0の件数を、ユーザーIDごとに数える
    :param df:
    :return:
    """
    def _count_over_zero(array):
        count = 0
        for val in array["CREDIT_DAY_OVERDUE"].values.flatten():
            if val > 0:
                count += 1
        return count

    df_tmp = df[["SK_ID_CURR", "CREDIT_DAY_OVERDUE"]]

    series_count_by_id = df_tmp.groupby("SK_ID_CURR").apply(_count_over_zero)
    series_count_by_id.name = "*DAY_OVERDUE_COUNT"

    df_count_by_id = pd.DataFrame(series_count_by_id).reset_index()
    return df_count_by_id

・・・こんな感じで2,3項目を作ってみたのですが、これじゃ日がくれそうです。
KaggleのKernelを見ると・・・トップダウンアプローチで作っている人が多いです。

トップダウン

    output_dir = "../../data/edited/all_mean"
    label_enc = LabelEncoder()

    def labelencode_cat(df):
        df = df.copy()
        cat_var = df.select_dtypes(['object']).columns.tolist()
        for col in cat_var:
            df["category_{}".format(col)] = label_enc.fit_transform(df[col].astype('str'))
            df = df.drop(col, axis=1)
        return df

    original_dir = "../../data/original"

    def output_mean(df, filename):
        df_avg = df.groupby("SK_ID_CURR").mean().reset_index()
        output_csv(df=df_avg,
                   output_path="{}/{}.csv".format(output_dir, filename))

    print("bureau")
    fname = "bureau"
    df_bureau = pd.read_csv("{}/{}.csv".format(original_dir, fname)).pipe(labelencode_cat)
    df_bureau = df_bureau.drop("SK_ID_BUREAU", axis=1)
    output_mean(df=df_bureau,
                filename=fname)
    del df_bureau

ざっくりと全部の項目についてユーザーごとに値の平均を取って、特徴量としてしまう。
なんと大雑把なー!!と思ったのですがこっちのほうが良さそうです。

なぜトップダウンがいいのか?

ローコスト

とにかく楽です。30行くらいのコードで、全ファイルの特徴量ごとの平均が取れちゃいました。

大事な項目を絞り込める

上記のような形で全部のデータをLightGBMやXGBoostなどの学習器で学習させると、feature_importances_を出力してくれます。

f:id:kurupical:20180530213313p:plain:w500

この図でわかることは、「予測にあたり学習器が重要視した項目」です。
つまり、(平均という大雑把なやり方とはいえ)重要な項目がなんとなくつかめます。
ここから、各項目について「ボトムアップ」で、特徴量作成するのがいいような気がします。

トップダウンのデメリット

カテゴリ変数(=文字列項目)の平均?

機械学習にかけるときは数値変換をする必要があります。
動物であれば、うさぎ=1、象=2、犬=3、・・・など。1
カテゴリ変数の「平均」を取ると、うさぎと犬の平均は象!?みたいな変なことになります。
トップダウン的なアプローチは、「数値」にのみ有効なんだと思います。

まとめ

最初からボトムアップで泥臭くやるより、トップダウン→特徴見てボトムアップのほうが当たりがつきやすく、素人にはよいのかもしれません。

途中経過

f:id:kurupical:20180530214633p:plain:w500

score: 0.706 -> 0.735
順位: 936位/1197 (上位80%)


  1. LightGBMはカテゴリ変数に対応しており、OneHotEncodeしなくてもよい

<実践>HomeCredit作業記録(2) - LightGBMを使って全特徴量を突っ込んだ

Kaggle挑戦中です!
Home Credit Default Risk | Kaggle

今日やったこと

LightGBMで、application_{train/test}.csvの全パラメータを突っ込んで

  1. 特徴の重要さ(feature importance)を見る
  2. LightGBMの予想を投稿する

そのために、

  • LightBGMの仕様調査
  • データの予想を投稿
    • データの前処理
    • train/predict

をやりました。

LightBGMの仕様調査

勾配ブースティングのLightGBMのパラメタについて調べました。
www.analyticsvidhya.com
rautaku.hatenablog.com

ここで書いてるパラメータを指定しても、「そないなパラメータあらへんで!」といわれてしまいます。
もう少し調査が必要そうです。

データの前処理

HomeCreditRisk : Extensive EDA + Baseline Model JP | Kaggle 和訳していただいた神Kernelをパクって実装。
このカーネルにはなかったのですが、「カテゴリ変数」をわかるようにカラム名をつけてみました。

train/predict

ハイパーパラメータはこんな感じ(チューニングはしてない)

params = {'task': 'train',
          'boosting_type': 'gbdt',
          'objective': 'binary',
          'metric': 'auc',
          'learning_rate': 0.01,
          'n_estimators': 50,
          'max_depth': 8,
          'subsample': 0.8,
          'verbose': 1,
          'num_iteration': 3000}

あとは、カテゴリ変数のカラム名を取得する関数を書いて

def _get_categorical_features(df):
    feats = [col for col in list(df.columns) if col[:9] == "category_"]
    return feats
data_cat = _get_categorical_features(X)

訓練時に、カテゴリ変数はこれだよーって教えてあげます。
OneHotEncodeとかしなくてよいから便利!(実際どういう仕組みなのか全くわからないし、どこに載ってるんだろう)

model = lgb.train(params,
                  lgb_train,
                  categorical_feature=data_cat,
                  valid_sets=lgb_test,
                  early_stopping_rounds=150,
                  verbose_eval=100)

feature_importanceも出してみました。
判定にあたり、100以上の特徴量の中でどれが大事だったか。 f:id:kurupical:20180528215509p:plain

この図を見ると、以下の特徴量が大事だということがわかります。

  • ORGANAIZATION_TYPE: どういった組織で働いてるか(ex. school/goverment...)
  • EXT_SOURCE: 別審査機関のクレジットスコア
  • DAYS_BIRCH: 年齢(生まれてから経過した日)

こういった分析は、すでにKaggleのDiscussion・Kernelで行われています。
しかし、そこでは「ORGANAIZATION_TYPE」が大事だとは書かれていませんでした。
自分の手で動かしてやってみるのは大事ですね!

score: 0.629→0.706
上位90%→上位85%(859位/1006)

signateは最初の投稿で結構いい順位についたけど、Kaggleはまだまだ全然駄目ですね・・・。

今回は1ファイルだけを使って予想しましたが、実はあと4ファイルくらいあります。
次はそのファイルも使って学習させてみたいと思います。

<基礎学習>機械学習、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名に対して倍くらい人が来ているということもあり緊張しますが、がんばります!