【Kaggle】VSB Power competitionに参加:使用手法と反省点
初めてKaggleのcompetitionに参加しました。
参加したcompetitonはVSB Power competitionです。
今回は反省と今後のための備忘録記事を。
Competitionの概要
架空送電線の電圧値とその送電線の部分放電の有無(0/1)を判定するcompetitionでした。
【背景】
送電線の故障は部分放電と呼ばれる破壊的な現象を引き起こす可能性があり、
そのままにしておくと、機器が損傷し、停電や火災が発生してしまう。
そのためそのような災害が発生する前に修理ができるように
部分放電を検出することが求められている。
【説明変数】架空送電線の電圧値
・三相交流と呼ばれる3系統の単相交流を組み合わせた交流の電圧値
・三相同時に測定されるため、各相で時間のずれはない
・データ数80万点(20ミリ秒毎のデータ)で50Hzで動作してるらしい
・センサデータ(信号データ)
【目的変数】部分放電の有無
・部分放電が生じたか否かのフラグ値
・三相交流(3Phase)の各相にフラグがある
・部分放電無のほうが有りより圧倒的に多い(不均衡データ)
Competition Results
今回の評価指標はMCC(Matthews correlation coefficient)でした。
Publicで0.693で568位
Privateで0.606で870位
ちなみに提出してなかった他のKernelでは
0.634で235位あたりとなり、
Bronzeまで残り0.008だった模様
Kaggle初めてで知らなかったのですが、
Publicのデータに対して過学習してしまい
Privateのデータで大ゴケするのを"Shake down"というみたいですね。
なぜこんなことになったのかを考えつつ振り返ります。
使用した手法
以下時系列で使用した順に記載
Random Forest
とりあえず簡単なモデルを使用してみた。
特徴量は各信号の平均・分散・最大・最小といった簡単な統計量を使用。
結果として0.219とかなり低い値。
LightGBM(他のKernel参照)
次にLightGBMを使ってみた。 結果は0.169とかなり低い。
LSTM+Attention(他のKernelを参照)
おそらく他の参加者の多くも参考にしたであろうKernel
私もこれをベースにしました。
ただし下でも述べますが、
説明変数である信号データ3つ分と目的変数であるフラグ値との紐づけの際、
各相で説明変数と目的変数を紐づけるのではなく
Phase==0となるときに目的変数を格納するリストに
Phase0のフラグを入れるという処理をしており
違和感を覚えてました。
Adabound&Swish
まずは最適化手法のところで最近性能が良いといわれている Adaboundを使用しました。 Report:https://openreview.net/pdf?id=Bkg3g2R9FX
実は最終的にはこのAdaboundを使用したKernelが
一番Private Scoreが良かった模様。
ただし、この手法の特徴とする
SGD+Adamの性能がうまく出るためには
ある程度のEpoch数を設定する必要があり、
今回50Epochs程度ではダメでした。
※問題にもよりますが、よかったKernelのときは
1000Epochsにしてました。
またSwishとよばれるActivation Functionを使用してみました。
Report:https://arxiv.org/pdf/1710.05941.pdf
こちらもReLUと同等かそれ以上の性能が出るみたい(問題による)
なので試しに使用。
ただし、AdaBound並の性能向上は見込めませんでした。
ここでの反省点は学習過程をちゃんと可視化しなかった点です。
説明変数と目的変数の紐づけ方変更
ここで先ほど述べた説明変数と目的変数の紐づけ方を変えました。
今回、訓練データでは各Phaseの組み合わせごとの
部分放電の有無を調べると、3つとも部分放電有りが多かった。
そのため、3つとも部分放電有りの場合、目的変数1というように
紐づけ方法を変更しました。
Public Scoreはこれで上がるのですが、
問題は
・Public DataとPrivate Dataでおそらく目的変数の分布が異なる
・そもそも1つのPhaseで部分放電が起きている場合を放置
となってしまい、ここでまず過学習を加速させてしまいました。
ドメイン知識の獲得
関連論文調査
最も参考になったのは
Report:Classfication of multiple PD Sources by Signal Features and LSTM Networks
この中では、4つの部分放電モードに対して
Random ForestとLSTMが使用されている。
Random Forestでは、簡単な統計量に加え
信号をFast Fourier Transform(FFT)した後、Bin5つに分けて
それぞれのBinに入っているデータの個数を特徴量に加えている。
Random Forestで予測した際、FFTの重要度が上位3つで全体の40%以上を占めている。
一方、LSTMの入力はセンサデータを移動平均法を使い
1000ポイント程度にリサンプリングし、入力にしている。
Randam Forestで98.25%, LSTMで97.38%のAccuracyを出しており、
今回のcompetitionの解法の方向性としては間違ってないと認識した。
(MCCはAccuracyとは異なるが)
なお部分放電は'Corona', 'Needle-Plane', 'Surface Discharge', 'Void'の4種があるみたいだ。
ドメイン知識を入れたり、関連論文調査の知見を入れることはある程度できていて
今後も行っていきたい。
FFTによる周波数解析
上記のReportでも言及されていたFFTをとりあえず1センサデータあたり
160dimになるように5000サンプル毎に図示してみた。
例えばidmeasurement=0でPhase0の最初のサンプル5000個(0~4999)の
センサデータならびににFFT結果をを図示した場合、
左上:元信号
左下:振幅スペクトル(横軸:周波数、縦軸:振幅)
右上:元信号に対し、窓関数(Hamming Window)使用
右下:窓関数使用した信号の振幅スペクトル
このデータは部分放電していないセンサデータの一部であり、
他の部分放電していないセンサデータも大体同じであった。
一方、idmeasurement=3のセンサデータの中で
例えばPhase0, サンプル
といった端で大きな振幅スペクトルが見られる。
※ただしここで上と下の縦軸の縮尺があってないので、
詳細に詰めるべきだった。
これは他の部分放電なしのデータでもある程度20Hzのところで見られるのだが
振幅スペクトルの値が部分放電ありに比べて低い。
これをもとにした特徴量を作ろうとしたが、
FFTの処理に大体1ms1かかり、
1つのセンサデータで
1ms×160=160ms、
センサデータ29049個(train:8712個+test:20337個)なので
大体4648秒かかり、この処理だけcommit出来なくなってしまいます。
そこで下に記載するSpectrogramで行いました。
Spectrogram
Scipyのsignal.periodogramはなぜか上記FFTより速いため
今回こちらを用いた
例えば先ほどのセンサデータ(部分放電なしとあり)では
縮尺のせいで若干わかりにくいですが、
部分放電ありのほうが、なしに比べ、
10Hz~20Hzのベースラインが高くなっている(logをとると顕著)
あと以下の参考資料をもとに特徴量を作成した。
参考資料:https://www.denso-ten.com/jp/gihou/jp_pdf/46/46-5.pdf
この参考資料はセンサデータ(音)の異音検査に関するものである。
生波形を二乗平均し、その大きさがある閾値以上であれば異音としている。
反省点
色々と試してみましたが、以下の反省点がありました。
* 準備&考察不足:2週間前から始めたこと
* 独自性の欠如:人のKernelに依存しすぎたこと
* 過学習:PublicデータとPrivateデータはあまり変わらないだろうという間違った認識
* 整理整頓不足:モデル・Kernelの管理不足
次はメダル圏内目指します。