ML Boot Camp IIIのオープニング





Machine Learning Boot Camp IIIは、Mail.Ru Groupによる3回目の機械学習とデータ分析のコンテストである2月15日に始まります。 今日は過去のコンテストについて話し、新しいコンテストの秘密を発見します! そのため、今後のコンテスト中に、参加者がオンラインゲームに残るか、退場するかを推測する必要があります。 タスクのサンプルは、25,000ユーザーの12のゲーム属性に基づいています。 当然、すべてのデータは匿名化されます。



サイン自体:





テストサンプルを40/60の比率でランダムに分割します。 最初の40%の結果により、コンテスト全体の評価表における参加者の位置が決まります。 残りの60%の結果は、競技終了後に判明し、参加者の最終的な配置を決定するのは彼です。



MacBook Airに最高のソリューションの勝者を与えます。 2位と3位はApple iPadになります。 4番目、5番目、6番目はApple iPod Nanoです。 伝統的に、最高の50人の参加者には、チャンピオンシップのシンボルが記された記念Tシャツが贈られます。 さらに、データ分析に関連する職務についてのインタビューのために、最高の最高のものをMail.Ru Groupに招待します。 ここで選手権に登録してください



機械学習Boot Camp II



参加者が何をしなければならないかをよりよく理解するために、最後のチャンピオンシップのタスクと勝者からの最良の解決策を提示します。



チャレンジ。 2回目のコンテストの参加者は、タスク「パフォーマンス評価」に直面しました。 テストコンピューティングシステムでサイズmxk



kxn



の2つの行列の乗算時間を予測するようにコンピューターに教えることを提案しました。 参加者は、このタスクが他のシステム、マトリックスサイズ、およびシステムパラメーターでどの程度解決されるかを知っていました。



問題を解決するための品質基準として、1秒よりも長く実行する実装には、最小平均相対誤差(一部のソースではMREと呼ばれるMAPE )を使用しました。



MAPE= frac1N sumNi=1 frac|yigi|yiyi>1



ここで、Nはサンプル内のオブジェクトの数、y iはi番目の実験の実際の動作時間、g iは予測時間です。 コンテストの最初の場所は、ミハイル・カラチュンによって、彼の決定方法論について、さらに彼の言葉からとられました。



受賞ストーリー



そのような問題を解決するための一般的な方法論は、競争に添付された文書に記載されています 。 主なアイデアは、時間の計算時間ではなく、 時間/(m * n * k)の値を予測することです。 さらに、1つのシステムのフレームワーク内でのこの値はわずかに異なります。複数のシステムでモデルをトレーニングする場合は、 ランダムフォレストなどの非線形手法を使用することをお勧めします。



ステップ0-共通



ソリューションにはPythonを使用し、5つのパーティションでkfoldメソッドを使用してローカルで結果を確認しました。 データを準備する際、すべてのカテゴリフィーチャに「特性の1つの値-1つの列」( 1つのホット )の変換が行われました。



ステップ1-モデルの選択



hyperoptモジュールは、モデルパラメーターの選択に使用されました。 パラメータを反復するだけでなく、パラメータを最適化しようとするため、単純なグリッド検索よりもうまく機能します。



 # -*- coding: utf-8 -*- import pandas as pd import numpy as np from sklearn.ensemble import GradientBoostingRegressor from sklearn.cross_validation import KFold from hyperopt import fmin, tpe, hp, STATUS_OK, Trials import random random.seed(1) def mean_absolute_percentage_error(y_true, y_pred): ind = y_true > -1 return np.mean(np.abs((y_true[ind] - y_pred[ind]) / y_true[ind])) def loss_func(y_true, y_pred): return mean_absolute_percentage_error(y_true,y_pred) all_train = pd.read_csv('~/Projects/DataMining/Bimbo/data/train1.csv') all_target = pd.read_csv('~/Projects/DataMining/Bimbo/data/y_train.csv') all_train['TARGET'] = all_target['time'] cols_to_drop = ['ID','TARGET'] cols = list(set(all_train.columns)-set(cols_to_drop)) print(len(cols)) def hyperopt_train_test(hpparams): all_results = [] kf = KFold(len(all_train['TARGET'].values),n_folds=5,random_state=1, shuffle=True) for train_index, test_index in kf: train = all_train.ix[train_index,:] test = all_train.ix[test_index,:] X_train = train[cols].values y_train_c = train['n'].values*train['m'].values*train['k'].values y_train = train['TARGET'].values X_test = test[cols].values y_test_c = test['n'].values*test['m'].values*test['k'].values y_test = test['TARGET'].values params_est = {'n_estimators':int(hpparams['n_estimators']), 'learning_rate':hpparams['eta'], 'max_depth':hpparams['max_depth'], 'min_samples_split':hpparams['min_samples_split'], 'min_samples_leaf':hpparams['min_samples_leaf'], 'loss':hpparams['loss'], 'alpha':hpparams['alpha'], 'subsample':hpparams['subsample'], 'random_state':1} bst = GradientBoostingRegressor(**params_est) bst.fit(X_train, np.log(y_train/y_train_c)) y_test_pred = np.exp(bst.predict(X_test))*y_test_c current_res = loss_func(y_test, y_test_pred) all_results.append(current_res) return np.mean(all_results) space4dt = { 'min_samples_split': hp.quniform('min_samples_split', 3, 14, 1), 'min_samples_leaf': hp.quniform('min_samples_leaf', 1, 7, 1), 'subsample': hp.quniform('subsample', 0.6, 0.99, 0.001), 'eta': hp.quniform('eta', 0.07,0.2, 0.001), 'n_estimators': hp.quniform('n_estimators', 10, 1000, 10), 'max_depth': hp.choice('max_depth', (4,5,6,7,8,9,10)), 'alpha': hp.quniform('alpha', 0.01, 0.99, 0.01), 'loss':hp.choice('loss', ('ls', 'lad', 'huber', 'quantile')), } def f(params): acc = hyperopt_train_test(params) print(acc) print(params) return {'loss': acc, 'status': STATUS_OK} trials = Trials() best = fmin(f, space4dt, algo=tpe.suggest, max_evals=2000, trials=trials) print 'best:' print best
      
      





いくつかのツリーで最初に実行した後、GradientBoostingRegressor(loss = 'lad')が最高のパフォーマンスを発揮しました。



ステップ2-機能エンジニアリング



第2段階では、すべて1〜100個あるため、タスクは余分な機能を除外するように設定されました。このため、再帰的な選択方法が使用されました。 これは、相互検証評価に従ってN個の機能を順次除外することで構成されます。 出力では、ranking_パラメータのアルゴリズムは、属性が削除された段階を保存します。1-それは最後まで残ったことを意味し、多ければ多いほど悪化します。 support_パラメーターには、選択した特性(つまり、ranking_内の特性の単位)のマスクが格納されます。 最後のオプションが常に最良であるとは限らないことに注意してください。場合によっては、選択の終わり近くに削除されたサインを使用して決定することもできます。 この手順は長時間実行されました。たとえば、平均的なパフォーマンスのラップトップでは、12時間以上かかりました。



 # -*- coding: utf-8 -*- from sklearn.feature_selection.rfe import RFECV from sklearn.ensemble import GradientBoostingRegressor import numpy as np bst = GradientBoostingRegressor(**params_est) selector = RFECV(bst, step=50, cv=5) selector.fit(all_train[cols], target) print(list(selector.ranking_ )) print(np.asarray(cols)[selector.support_ ])
      
      





その結果、兆候の数は約10に減少しました。さらに、試行錯誤により、さらに成功した兆候がいくつか見つかりました。



 df.ix[:, 'cpuExtra1'] = 0 df.ix[df['cpuFull'] == 'Intel(R) Core(TM) i3-2310M CPU @ 2.10GHz', 'cpuExtra1'] = 1 df.ix[:, 'cpuExtra2'] = 0 df.ix[df['cpuFull'] == 'Intel(R) Atom(TM) CPU N550 @ 1.50GHz', 'cpuExtra2'] = 1 #     df.ix[:, 'm_div_n'] = df['m'] / df['n'] df.ix[:, 'magic'] = df['k'] * df['m'] * df['n'] / (df['cpuCount'] * df['cpuCount']) cols = [ 'n', 'Sequential_read_128B_by128', 'k', 'Random_write_3MB_by128', 'cpuCount', 'Sequential_write_32kB_by128', 'Random_read_9MB_by128', 'm', 'SeqRead_20kB_by256', 'cpuCores', 'Sequential_read_48MB_by128', 'Random_read_4MB_by32', 'Random_write_32MB_by128', 'Random_read_2MB_by32', 'SeqCopy10MB_by128', 'BMI', 'm_div_n', 'magic', 'cpuExtra1', 'cpuExtra2', 'Random_write_bypassing_cache_6kB_by128', 'Sequential_read_192kB_by32', ]
      
      





合計20個の兆候が得られ、サンプルから取得され、2個が生成されました。 1つ目は、行列の次元のすべての可能な関数を列挙することによって取得されました。 2番目の症状は、明らかなロジックの欠如にもかかわらず、結果の改善に寄与するため、特別な注意に値します。



ステップ3-組み立て



アンサンブル。 特徴の異なるサブセットでトレーニングされた決定木を組み合わせると、結果は単一の木よりも効率が優れています。これがランダムフォレストの仕組みです。 ただし、複数の異なるアンサンブルを使用してそれらのソリューションを組み合わせると、これも役立ちます。 一般的なプラクティスと私の個人的な経験が示すように、おおよそ同じ結果のモデルが複数ある場合、それらの平均はほぼ常に優れています。 そして、これらのモデルの構築ロジックが異なるほど、より優れています。 たとえば、同じパラメータを使用し、ツリーの数が異なる2つのランダムフォレストを使用する場合、これは役に立ちそうにありません。 そして、ランダムフォレストと勾配ブースター回帰子を使用する場合、ほとんど常により良く機能します。これは、小数点以下2桁または3桁について話している場合にまさに必要なものです。



この問題を解決するために、パラメーターの超最適化によって得られた上位モデルを取り上げ、それらの組み合わせを調べました。 特に良いのは、同等の良好な平均交差検証結果を生成するモデルであり、最良と最悪のフォールドは異なります。 その結果、3つのモデルが残り、平均値が予測値として使用されました。



行および/または列のサブセットで異なるモデルをトレーニングしても、迅速な結果は得られませんでした。



 #     params_est = {'n_estimators': 370, 'subsample': 0.961, 'learning_rate': 0.076, 'min_samples_split': 18.0, 'max_depth': 6, 'min_samples_leaf': 8.0, 'random_state':1, 'loss':'lad',} bst1 = GradientBoostingRegressor(**params_est) bst1.fit(X_train, y_train/y_train_c1) params_est = {'n_estimators': 680, 'subsample': 0.902, 'learning_rate': 0.076, 'min_samples_split': 14.0, 'alpha': 0.29, 'max_depth': 9, 'min_samples_leaf': 5.0, 'loss':'quantile', 'random_state':1} bst2 = GradientBoostingRegressor(**params_est) bst2.fit(X_train, y_train/y_train_c1) params_est = {'n_estimators': 430, 'subsample': 0.978, 'learning_rate': 0.086, 'min_samples_split': 19.0, 'max_depth': 6, 'min_samples_leaf': 10.0, 'loss':'lad', 'random_state':1} bst3 = GradientBoostingRegressor(**params_est) bst3.fit(X_train, y_train/y_train_c1)
      
      





ステップ4-もっと深くする必要がある



その後、コンピューティングシステムのさまざまなパラメーターによって予測誤差がどの程度異なるかを確認することにしました。 単純な検索では、相互検証中のエラーの平均値が〜0.05である場合、オペレーティングシステムの1つでこのエラーは〜0.30であることが示されました。



最初のアイデアは、トレーニング中にオブジェクトの重みを調整し、特定のスズメバチの重量を増やすことでしたが、結果は悪化するだけでした。 このスズメバチのデータは非常に小さい(<100)ため、別のモデルを構成することもできません(再トレーニング)。 暫定的な解決策が役立ちました。 私は別のモデルを取り、サンプル全体で訓練しましたが、このスズメバチを優先する重みがありました。 最終結果を計算するとき、このモデルは1つのハチだけに使用されました。 つまり メインモデルは重みなしでサンプル全体でトレーニングされ、1つを除くすべてのハチの結果を予測しました。 1つのモデルがサンプル全体で重み付きでトレーニングされ、1つのハチだけが予測されました。 ここで、初めて、相互検証に困難が生じました-地方の改善は、公共の評価によって必ずしも確認されませんでした。 これは、1つのスズメバチの例の数が非常に少ないためであり、検証のためにまだ複数の部分に分割されている場合、安定した結果を待つ必要はありません。



 #      all_train['w'] = 1 all_train['w'][all_train['os'] == 15] = 4 #      os = 15 params_est = {'n_estimators': 480, 'subsample': 0.881, 'learning_rate': 0.197, 'min_samples_split': 3.0, 'max_depth': 7, 'min_samples_leaf': 2.0, 'loss':'lad', 'random_state':1} bst4 = GradientBoostingRegressor(**params_est) bst4.fit(X_train, np.log(y_train/y_train_c), sample_weight=train['w'])
      
      





ステップ5-最後のステップ



かなり良いモデルのセットがあるため、別の仮定が行われ、かなり良い結果がもたらされました。 相対偏差を最小化する必要がある場合は、予測しないでください。 ここで、より詳細に試します。 参考例としてモデルを提供するためのいくつかのオプションがあります。



  1. モデルをトレーニングして、出力として直接時間 -計算時間を与えます。 これは、冒頭に引用した記事の著者のアドバイスを聞いた後、すぐに拒否したものです。
  2. 時間/(m * n * k)を出力に適用することでモデルをトレーニングします。つまり、各コンピューターシステムに対して計算された角度係数です。 これが私たちがこれまでやってきたことです。
  3. time / reg_k *(m * n * k)を入力してモデルをトレーニングします。 つまり 各コンピューターシステムについて、時間のm * n * kへの依存性は角度係数reg_kで線形であると仮定し 、このモデルからの相対偏差を予測するようにモデルをトレーニングします。


中央値時間/(m * n * k)reg_kとして取得され、os + cpuFullの符号はコンピューティングシステムの識別子として取得されました。中央値を持つ線形モデルが最良の結果を与えるのはこの組み合わせであるためです。



 all_train.ix[:, 'c1'] = all_train['TARGET'] / (all_train['m'] * all_train['n'] * all_train['k']) all_train_median = all_train[['c1', 'os', 'cpuFull']].groupby(['os', 'cpuFull'], as_index=False).median() def preprocess_data(df): #    df = pd.merge(df, all_train_median, on=['os', 'cpuFull'], how='left', suffixes=('', '_med')) df.ix[:, 'test_mdeian'] = df['c1_med']*df['m']*df['n']*df['k'] return df
      
      





ステップ6-ルールルール



ちなみに、最終的な結果に大きな影響を与えた小さなハックについても説明します。 評価式を注意深く見ると、1秒未満の測定値が除外されていることは明らかです。 つまり、1未満のすべての予測値を安全に切り上げることができます。実際に小さい場合は結果が含まれず、そうでない場合はエラーが減少するためです。



一般的な印象



記事に記載されているすべての改善の過程で、ローカル決定スコア、パブリックスコアのスコア、およびプライベートスコアで後に判明したように、常に改善の方向に変化したことに注意してください。 ここで、 最終的な作業スクリプトを見ることができます



試してみてください!



いつものように、私たちはポータルの初心者向けのトレーニング記事と専門家向けの真剣なタスクを提供しています。 ところで、ポータルでは、以前のコンテストの解決を練習できます。すべてのコンテストはサンドボックスモードで開かれています。 登録時にご参加ください!



All Articles