はじめに
VarianceThresholdは名前の通り、分散がしきい値以下の特徴量を捨てます。
sklearn.feature_selection.VarianceThreshold — scikit-learn 0.20.2 documentation
これといってすごいところはありませんが、気楽に使えそうなので試してみました。
目次
スポンサーリンク
とりあえず試す
しきい値の設定でどれだけ特徴量のshapeが減るか見てみました。
データは20newsgroupsです。
Pipelineにしてあるのは、あとでこれを使って分類のチューニングをしてみるためです。
from sklearn.datasets import fetch_20newsgroups from sklearn.feature_extraction.text import CountVectorizer from sklearn.feature_selection import VarianceThreshold from sklearn.pipeline import Pipeline def test_shape(): news20 = fetch_20newsgroups() cv = CountVectorizer(min_df=0.005, max_df=0.5, stop_words="english") vth = VarianceThreshold() pl = Pipeline([("cv", cv), ("vth", vth)]) for v in [0.0,0.05,0.1,0.15]: pl.set_params(vth__threshold=v) print(pl.fit_transform(news20.data).shape) if __name__ == "__main__": test_shape()
関連記事:
- 【python】sklearnのfetch_20newsgroupsで文書分類を試す(1) - 静かなる名辞
- 【python】sklearnのPipelineを使うとできること - 静かなる名辞
- 【python】sklearnのCountVectorizerの使い方 - 静かなる名辞
結果は、
(11314, 3705) (11314, 1476) (11314, 859) (11314, 573)
なるほど。
(実際にはいろいろ試してちょうど良いshapeの減り具合になる値を探しています。これを使うならそういう作業が必要になると思います)
分類を試してみる
これをうまく設定すると、分類精度が上がったりするのでしょうか?
import pandas as pd from sklearn.datasets import fetch_20newsgroups from sklearn.feature_extraction.text import CountVectorizer from sklearn.preprocessing import FunctionTransformer from sklearn.feature_selection import VarianceThreshold from sklearn.naive_bayes import GaussianNB from sklearn.pipeline import Pipeline from sklearn.model_selection import GridSearchCV def convert(x): return x.toarray() def test_best_v(): news20 = fetch_20newsgroups() cv = CountVectorizer(min_df=0.005, max_df=0.5, stop_words="english") vth = VarianceThreshold() sparse_to_dense = FunctionTransformer(func=convert, accept_sparse=True) gnb = GaussianNB() pl = Pipeline([("cv", cv), ("vth", vth), ("s2d", sparse_to_dense), ("gnb", gnb)]) params = {"vth__threshold":[0.0,0.05,0.1,0.15]} clf = GridSearchCV(pl, params, return_train_score=False, n_jobs=-1) clf.fit(news20.data, news20.target) cv_result_df = pd.DataFrame(clf.cv_results_) df = cv_result_df[["param_vth__threshold", "mean_score_time", "mean_test_score"]] print(df) if __name__ == "__main__": test_best_v()
ただ単にナイーブベイズに入れて性能を見ているだけですが、かなり色々なテクニックを使っているコードなので、初見だと読みづらいと思います。
- FunctionTransformer:
ナイーブベイズが疎行列を受け付けてくれないので変換している。こんな関数ラムダ式で良いじゃんと思う向きもあるかもしれませんが、GridSearchCVでn_jobs=-1を指定するためにはトップレベル関数として定義してあげる必要があります(中でpickleを使うので)
- GridSearchCV:
return_train_score=Falseにすると速くなります。
- pd.DataFrame
GridSearchCV.cv_results_はそのままpandas.DataFrameに変換できるとドキュメントに書いてあるので、それを使ってpandasで取り扱っています。
走らせた結果は、
param_vth__threshold mean_score_time mean_test_score 0 0 9.646958 0.656178 1 0.05 4.504278 0.587149 2 0.1 2.804257 0.512551 3 0.15 1.909767 0.453244
改善する訳ではない。CountVectorizerのmin_dfで予めゴミ変数を削っていること、スパースな空間なので分散が低くてもそれはそれで構わず、ナイーブベイズが意外とスパースに強いのも相まって優秀に働いていることが原因でしょう。
それより注目すべきはmean_score_timeで、今回のデータで変数を削っていくと、しきい値を0.05上げるたびに、0.07ポイントくらいの性能低下と引き換えに半減するような傾向です。性能と時間のトレードオフになったときは、これをいじって調整する手はあるのかも。
まとめ
微妙といえば微妙だし、データによっては効くのかもしれない気もします。とりあえず確実に速くはなります。
性能はあまり重視していないとき、気楽に変数を捨てて速くするのに使えそうです。