読者です 読者をやめる 読者になる 読者になる

Obey Your MATHEMATICS.

機械学習関連の純粋数学や実験など

つくってあそぼう!ヒッグス粒子発見器 -開発編-

こんにちは。

今回は、前回の記事

mathetake.hatenablog.com

で前処理して得られたデータセットを元に、実際に分類機を作っていこうと思います。

§1. データの読み込みと正規化

モデルを構築する前に、とりあえずデータの読み込みと正規化をしましょう。

#ファイルの読み込み
train_df = pd.read_csv("outlier_removed_high_feature_train.csv", index_col=0, header=0)
test_df = pd.read_csv("high_feature_test.csv", header=0, index_col=0, names=['is_Higgs','m_jj', 'm_jjj', 'm_lv', 'm_jlv', 'm_bb', 'm_wbb', 'm_wwbb'])


まず、目的変数について整理します。目的変数(is_Higgs)を

0 --->ノイズ
1 ---->Higgs粒子

と言う値にしたいので、"is_Higgs"の列の値を振り直し、Y_train & Y_test に格納します:

#トレーニングデータの目的変数をまず-1を0に変換してから抽出
train_df.loc[(train_df.is_Higgs == -1 ),"is_Higgs"] = 0
Y_train = train_df.values[0::,0]
Y_train = np.reshape(Y_train,(-1,1))

#テストデータのの目的変数もまず-1を0に変換してから抽出
test_df.loc[(test_df.is_Higgs == -1 ),"is_Higgs"] = 0
Y_test = test_df.values[0::,0]
Y_test = np.reshape(Y_test,(1,-1))

これで目的変数については準備OKです。


次に特徴量について考えます。今回は

各特徴量の平均=0&[-1, 1 ]の区間に値を持つ

ように正規化します。(後ほど、平均を引いて標準偏差で割るやつでも実験します。)

##[-1,1]区間に正規化ver.#####
#トレーニングデータの特徴量の正規化
train_df_means = train_df.mean().values
train_df_max = train_df.max().values
train_df_min = train_df.min().values
X_train = train_df.apply(lambda x: (x-x.mean())/(x.max()-x.min()), axis=0).fillna(0).values[0::,1::]

#テストデータの説明変数を上の正規化変換を使って正規化
X_test = test_df.values[0::,1::]
for i in range(X_test.shape[1]):
	X_test[0::,i] = (X_test[0::,i] - train_df_means[i+1])/(train_df_max[i+1]-train_df_min[i+1] )


これでモデル構築の準備完了です。

§2. ベンチマークについて

モデル構築&実験、の前にベンチマークを確認しておきましょう。

[1402.4735] Searching for Exotic Particles in High-Energy Physics with Deep Learning

データセットの提供者の論文には次のようなベンチマークが示されています:

f:id:mathetake:20170114210325p:plain

これらの値はROC曲線下のAUCで算出されています。それがなにを意味するのかは

はじめてのパターン認識

はじめてのパターン認識

この本とかに書いてあります。密度関数が被ってるときの分類器の評価に使われる指標です。


上の図の説明ですが、各列がそれぞれ使用する特徴量によって分けられます。

左からlow-leve特徴量のみ & high-level特徴量 & 全ての特徴量、となっていてそれぞれの意味については前回説明しました。

今扱っているデータはhigh-level特徴量のみなので、真ん中の列のみに注目します。

また、各行は上からBoosted Decision Trees & 3層NN & DNN、となっていてそれぞれのROC_AUCスコアが示されています。

今は実際の設定よりもトレーニングデータが少ないですが、せめて0.8弱ぐらい目指したいなあって事で実験しました。


今回の話題と関係はないですが、

◯low-levelの場合のDEEPとSHALLOWの差

◯high-levelの場合のDEEPとSHALLOWの差

に注目してみたください。後者の場合が明らかに差が縮まっているかと思います。

ディープなネットワークが上手く学習出来る関数は、深い階層構造を持った関数である、と数学的に証明されています*1から、その裏付けになっていると言えるでしょう。

(high-level特徴はlow-level特徴の値の関数の値だそうで、その意味で階層的に上な特徴になっていると見れるのでは?と考えています。)

§3. 多項式特徴+ロジスティック回帰

まず簡単なモデルから行きましょう。

多項式特徴&ロジスティック回帰でモデルを作っていきます。scikit-learnでささっと実装しちゃいましょう。

とりあえず3次の多項式のコードはこんな感じ。

#polynomial_logistic回帰
poly_3 = Pipeline([('poly', PolynomialFeatures(degree=3)),('logistic', linear_model.LogisticRegression())])
poly_3.fit(X_train, Y_train)
poly_3_predict = np.reshape(poly_3.predict(X_test),(1,-1))

#roc_auc_score
roc_score = roc_auc_score(np.reshape(Y_test,(-1,)), np.reshape(poly_3_predict,(-1,)))
print roc_score

#まとめ
result_array = np.concatenate((Y_test, poly_3_predict), axis=0)
result_df = pd.DataFrame(result_array.T, columns=['Actual_class','poly_3_predict'])
result_df['is_correct'] = result_df['Actual_class']
result_df.loc[(result_df.Actual_class == result_df.poly_3_predict), 'is_correct'] = 1
result_df.loc[(result_df.Actual_class != result_df.poly_3_predict), 'is_correct'] = 0
summary = result_df.describe()

同様に2次、1次(線形)も実装して得られたAUCスコア&テストデータに対する正解率は

◯1次AUC
f:id:mathetake:20170114213025p:plain
◯2次
f:id:mathetake:20170114213310p:plain
◯3次AUC
f:id:mathetake:20170114213317p:plain

◯1次正解率
f:id:mathetake:20170114213430p:plain
◯2次正解率
f:id:mathetake:20170114213452p:plain
◯3次正解率
f:id:mathetake:20170114213519p:plain


どちらも0.6.5前後が関の山、と言う感じでしょうか。

もちろんもっと次数を増やせば精度は上がると思いますが…マシンパワーの関係上これ以上はやめました。*2

§4. 3層ニューラルネット

次に、3層ニューラルネットワークで実験します。
例のごとくTFLearn で実装していきます。

def sigmoid(z):
	return 1/(1+np.exp(-z))

with tf.Graph().as_default():
	three = tflearn.input_data(shape=[None,7])
	three = tflearn.fully_connected(three, 20, activation='tanh')
	three = tflearn.fully_connected(three, 1, activation='linear')
	three = tflearn.regression(three, optimizer='adam', learning_rate=0.01, loss='binary_crossentropy')
	three_model = tflearn.DNN(three, tensorboard_verbose=0)
	three_model.fit(X_train, Y_train, validation_set=0.1, batch_size=10000, n_epoch=50,show_metric=True) 
	three_dnn_output = np.array(three_model.predict(X_test))
	three_dnn_output = sigmoid(three_dnn_output.T)

TFlearnでloss='binary_crossentropy'と設定すると、損失関数の部分でシグモイドが噛まされるので、ネットワークそのもののアウトプットは'linear'にしておきます。
その後.predictの出力にsigmoidを噛ませてヒッグス粒子である確率の値を得ます。

これによって得られた結果*3

◯ROC_AUCスコア
f:id:mathetake:20170114214953p:plain

◯正解率
f:id:mathetake:20170114215116p:plain

ROC_AUCスコアが多項式モデルの場合と比べてかなり改善されました。ベンチマークにもかなり近い値です。

§5. 深層ニューラルネット

最後にディープなやつで実験しましょう。

with tf.Graph().as_default():
	three = tflearn.input_data(shape=[None,7])
	three = tflearn.fully_connected(three, 20, activation='tanh')
	three = tflearn.fully_connected(three, 15, activation='tanh')
	three = tflearn.fully_connected(three, 8, activation='tanh')
	three = tflearn.fully_connected(three, 4, activation='tanh')
	three = tflearn.fully_connected(three, 1, activation='linear')
	three = tflearn.regression(three, optimizer='adam', learning_rate=0.01, loss='binary_crossentropy')
	three_model = tflearn.DNN(three, tensorboard_verbose=0)
	three_model.fit(X_train, Y_train, validation_set=0.1, batch_size=10000, n_epoch=50, show_metric=True) 
	three_dnn_output = np.array(three_model.predict(X_test))
	three_dnn_output = sigmoid(three_dnn_output.T)

この設定により得られた結果を確認します。

◯ROC_AUCスコア
f:id:mathetake:20170114215945p:plain
◯正解率
f:id:mathetake:20170114215809p:plain


ROC_AUCスコアも0.783と、ベンチマークに肉薄していますし、正解率も70%を超えてきました。


§6. おわりに

今回はただ実験結果を貼り付けただけの記事になってしまいました。すみません。

もう少し頑張ってベンチマークを超えていきたいと思ってますが、疲れたのでこの辺にしておきます。

様々なハイパーパラメータのネットワークの学習状況を見ていた感じ、どの場合も正解率70%前後のローカルミニマに落ち込んでいる雰囲気がしました。

また気力があるときに、ベイジアンニューラルネットを使ってそのローカルミニマを克服するような実験をしたいなあ、と考えています。


それでは。

*1:論文リストhttp://mathetake.hatenablog.com/entry/2016/12/20/005632の2-4-1,2-4-2辺り参照。

*2:200万のサンプルから4次以上の多項式特徴作るなんてメモリが足りません。

*3:ハイパーパラメータは色々実験していじりました。