俺人〜OREGIN〜俺、バカだから人工知能に代わりに頑張ってもらうまでのお話

俺って、おバカさんなので、とっても優秀な人工知能を作って代わりに頑張ってもらうことにしました。世界の端っこでおバカな俺が夢の達成に向けてチマチマ頑張る、そんな小さなお話です。現在はG検定、E資格に合格し、KaggleやProbSpaceのコンペに参画しながら、Pythonや機械学習、統計学、Dockerなどの勉強中です。学習したことをブログにアウトプットすることで、自分の身に着けていきたいと思います。まだまだ道半ばですが、お時間がありましたら見て行ってください。

【10位解法】ProbSpace開催「スパムメール判別」コンペの振り返り。

データ分析プラットフォームProbspace「スパムメール判別」コンペに参加し、10位に入賞しました!

今回は、初めて本格的に自然言語処理に挑戦したコンペとなりました。

セキュリティに関するドメイン知識はあったのですが、様子が違って、なかなか活かすことができず、苦戦しました。

参加中のコンペで、今年最後の締切となるコンペだったので、なんとか金トロフィー圏内に入れてよかったです。

f:id:kanriyou_h004:20201221200738j:image

では、振り返って参りたいと思います。

1.全体構成

今回のコンペは、自然言語処理の分類課題でした。これまで、自然言語処理をじっくり取り組んだことがなかったので、とても勉強になりました。

オライリー社の、入門自然言語処理を片手に、勉強しながら取り組みました。

f:id:kanriyou_h004:20201216192807j:plain

全体構成としては、以下の図とおりです。

前処理として、テキスト文書のクレンジング処理を共通的に実施して、不均衡データ対策にアンダーサンプリングを実施したあと、特徴量として扱えるようベクトル化を実施しました。

モデルは、最終的には、2層のStackingモデルの出力を加重平均するモデルとなりました。

f:id:kanriyou_h004:20201220174845p:plain


前半はSolafuneの「衛星画像から空港利用者数を予測」に注力していたので、あまり精度があがりませんでしたが、最終週以外は、とにかくシングルモデルでの精度向上に注力しました。

最終週は、いつもどおり物量での試行錯誤になってしまい、自然言語処理の部分でスコアが向上できなかったので、もっとじっくり自然言語処理についても学びたいと思いました。

2.前処理

前処理については、以下の通りで実施いたしました。

【共通処理】 この処理は、cha_kabuさんのベースモデルを参考にしました。    

前処理

処理内容

ストップワードの削除

'i','me','at','by','is'など、どんな文章にも一定量出現し、特徴量として不要な情報となりうる「ストップワード」に該当する単語を削除.

句読点の削除

',', '.', ';',などの句読点や括弧などの記号を削除。

HTMLの分解

HTMLのタグで囲まれた部分については、要素に分解。

URLの削除

URLを削除。

ブラックリスト等との突き合わせができれば、活用できたかも。今回は外部データの申請をしなかったので、利用しませんでした。)

[ ]で囲まれた文の削除

[ ]で囲まれた注釈などの文章は割り切って削除。

 【不均衡データ対策】

前処理

処理内容

アンダーサンプリング

今回のデータは、スパムメールの数に対して、非スパムメールが非常に少ないとても偏った不均衡データだったため、適切に学習できるようスパムメールと非スパムメールのデータ数を均衡化する必要がありました。

均衡化には、RandomUnderSamplerを使用しました。

from imblearn.under_sampling import RandomUnderSampler

# アンダーサンプリングしながら訓練用データの作成
sampler = RandomUnderSampler(random_state=5)
X_resampled, y_resampled = sampler.fit_resample(X,y)

【ベクトル化】

前処理

処理内容

CountVectorizer

出現する単語をカウントすることで、単語の出現数を特徴量にする手法です。出現した単語を純粋にカウントします。

from sklearn.feature_extraction.text import CountVectorizer

# 単語の出現回数取得
vec_count = CountVectorizer(min_df=3)
vec_count.fit(X['contents'])
    
# 訓練データをベクトル化
X_resampled_vec = vec_count.transform(X_resampled['contents'])
TfidfVectorizer

TF-IDF(索引語頻度逆文書頻度)という手法で特徴量を作成します。
TF(単語の出現頻度)とIDF(単語のレア度)を掛けたものになります。

TFは、【文書内の該当する単語の出現回数】÷【文書内の全単語の出現回数】となります。

IDFは、【全ての文書数】÷【該当する単語を含む文書数】のLogを取ったもので表されて、値が大きければ大きいほどレアな単語であることになります。

このTFとIDFをかけ合わせた値を単語の特徴量とします。  

from sklearn.feature_extraction.text import TfidfVectorizer

# 単語のTFI-IDを算出
tfi_count = TfidfVectorizer()
tfi_count.fit(X['contents'])
    
# 訓練データをベクトル化
X_resampled_vec = tfi_count.transform(X_resampled['contents'])

3.モデル構築

 今回のモデル構築は、最初、LightGBM、XGboost、RandomForestとMLPRegressorの単体のモデルを大量に作成して、LBの成績が良かったモデルを1層目とする2階層のStackingモデルとしました。

その後、2層目も複数作成して、最終的には、1層目、2層目の出力の加重平均を試行錯誤しました。

f:id:kanriyou_h004:20201220174845p:plain

BERTなどの自然言語処理の学習済みモデルは、今回は、勉強不足のため、採用できませんでした。

終盤は、自然言語処理とは関係なく、アンサンブルの試行錯誤になってしまったので、次回の機会に向けて、自然言語処理も学んでいきたいと思いました。

4.感想

今回のコンペは、初めて、本格的に、自然言語処理に挑戦したコンペとなりました。本格的にとはいったものの、テキストデータのクレンジングと、ベクトル化のみしかできませんでした。

実際は系列データの学習など、もっと深いところまで取り組まないと、太刀打ちできないと思いますが、今回は2値分類ということで、なんとかこれまでの手法も活用してそれなりのスコアを残すことができました。

今回はコンペなので、これで良かったのですが、実業務に活かしていくことを考えると、もっと勉強しないとならないと考えています。

引き続き精進していきたいと思います。

5.謝辞

最後となってしまいましたが、本コンペを運営してくださいました、Probspace の運営の皆様、一緒にコンペに取り組んでいらっしゃった皆様、挫けそうになったときにTwitter上でお声がけくださった皆様に心より感謝申し上げます。

引き続き、いろいろなコンペに参加させていただきたいと思いますので、今後ともよろしくお願いいたします。

 

【過去記事】

G検定、E資格の取得から、これまでに取り組んできた道のりは以下にまとめております。何かのご参考にしていただければ幸いです。

oregin-ai.hatenablog.com

oregin-ai.hatenablog.com

oregin-ai.hatenablog.com