AIって結局何なのかよく分からないので、とりあえず100日間勉強してみた Day29
経緯についてはこちらをご参照ください。
■本日の進捗
●単変量統計を理解
■はじめに
引き続き「Pythonではじめる機械学習(オライリー・ジャパン)」で学んでいきます。
モデルベース特徴量選択に続いて、自動特徴量選択の仲間である単変量統計を学んでいきます。
■単変量統計
単変量統計(univariate statics)とは、各特徴量がターゲットとの間にどのような関係があるのかを計算する統計的な手法で、各特徴量が予測においてどれだけ重要かを数値化することができます。
クラス分類タスクにおいては、データ全体の分散を、グループ間の分散とグループ内の分散に分けて考え、グループ間の分散の方が大きい場合にそのグループには有意な差があると評価する分散分析(analysis of variance : ANOVA)が知られています。
前回のすべての特徴量を同時に考慮して交互作用を捉えた上でそれぞれの特徴量ごとに重要度を算出するモデルベース特徴量選択(詳しくは下記参照)との最大の違いは、目的のためにモデルを作ることなく自動的に特徴量を選択することにあります。この手法の最大のメリットは情報量のない特徴量を検出して取り除く速さにあります。
ただし、その性質上、他の特徴量との組み合わせで意味を成すような特徴量は重要でないと判断されて捨てられてしまうので注意が必要です。
前回同様あやめデータセットに大量のノイズ特徴量を付加してロジスティック回帰で学習させてみます。
import numpy as np import pandas as pd from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegression iris = load_iris() X = pd.DataFrame(iris.data, columns=iris.feature_names) y = iris.target np.random.seed(8) for i in range(88): X[f'noise_feature_{i+1}'] = np.random.rand(X.shape[0]) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=8) model = LogisticRegression(max_iter=1000, random_state=8) model.fit(X_train, y_train) score = model.score(X_test, y_test) print("score is ", score)
score is 0.8444444444444444
それなりの精度ですが、元のあやめデータセットのみで学習させるとスコアは0.933であることを考えると、やはりデータセットにノイズが乗ってしまっています。
それでは単変量統計を用いてノイズを持った特徴量を捨てて(そうなることを期待しています)からロジスティック回帰で学習させてみます。
import numpy as np import pandas as pd from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegression from sklearn.feature_selection import SelectPercentile iris = load_iris() X = pd.DataFrame(iris.data, columns=iris.feature_names) y = iris.target np.random.seed(8) for i in range(88): X[f'noise_feature_{i+1}'] = np.random.rand(X.shape[0]) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=8) selector = SelectPercentile(percentile=50) selector.fit(X_train, y_train) X_train_selected = selector.transform(X_train) X_test_selected = selector.transform(X_test) model = LogisticRegression(max_iter=1000, random_state=8) model.fit(X_train_selected, y_train) score = model.score(X_test_selected, y_test) print("score is ", score)
score is 0.8444444444444444
あれ、スコアに変化がありません。それもそのはず意味のある4個の特徴量に88個のノイズを乗せているので、50%の特徴量を選択するにはノイズが多すぎます。仮に20%のみの特徴量を選択するように変えてみます。
import numpy as np import pandas as pd from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegression from sklearn.feature_selection import SelectPercentile iris = load_iris() X = pd.DataFrame(iris.data, columns=iris.feature_names) y = iris.target np.random.seed(8) for i in range(88): X[f'noise_feature_{i+1}'] = np.random.rand(X.shape[0]) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=8) selector = SelectPercentile(percentile=20) selector.fit(X_train, y_train) X_train_selected = selector.transform(X_train) X_test_selected = selector.transform(X_test) model = LogisticRegression(max_iter=1000, random_state=8) model.fit(X_train_selected, y_train) score = model.score(X_test_selected, y_test) print("score is ", score)
score is 0.9555555555555556
恐らくノイズの量が減ってモデルの予測精度が大きく向上しました。
実際にはどの特徴量が選択されたのかを見てみます。
import numpy as np import pandas as pd from sklearn.datasets import load_iris from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegression from sklearn.feature_selection import SelectPercentile iris = load_iris() X = pd.DataFrame(iris.data, columns=iris.feature_names) y = iris.target np.random.seed(8) for i in range(88): X[f'noise_feature_{i+1}'] = np.random.rand(X.shape[0]) X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=8) selector = SelectPercentile(percentile=20) selector.fit(X_train, y_train) selected_features_mask = selector.get_support() selected_feature_names = X_train.columns[selected_features_mask] print("Selected features are", selected_feature_names)
Selected features are Index(['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)',
'petal width (cm)', 'noise_feature_5', 'noise_feature_11',
'noise_feature_15', 'noise_feature_19', 'noise_feature_23',
'noise_feature_26', 'noise_feature_33', 'noise_feature_34',
'noise_feature_39', 'noise_feature_40', 'noise_feature_42',
'noise_feature_55', 'noise_feature_60', 'noise_feature_70',
'noise_feature_85'],
dtype='object')
見事に元の4つの特徴量が生き残っていて、作為的に追加された特徴量のみが80%捨てられています。最初に意図していた目的を自動で満たしてくれたと言えます。
■おわりに
正直にお話すると、前回までの数回は帯同のため大変お疲れでうとうとしながら書いたので単変量統計とモデルベース特徴量選択の順番を間違えた挙句、内容もめちゃくちゃだったのでしれっと改定を加えています笑
前回のモデルベース特徴量選択の方が若干高級な手法(機械学習モデルを使って選択しているので)に思えますが、この程度はっきりとしたノイズが乗っている場合には単変量統計で十分対応できるのみならず、やはり処理が爆速です。
実運用では複雑なデータセットが多いと思うので相互に関係を持つ特徴量を捨ててしまう単変量統計は使い方が難しいですが、学習をさせないで特徴量選択をできるスピード感は大きな魅力ではないでしょうか。
■参考文献
- Andreas C. Muller, Sarah Guido. Pythonではじめる機械学習. 中田 秀基 訳. オライリー・ジャパン. 2017. 392p.
- ChatGPT. 4o mini. OpenAI. 2024. https://chatgpt.com/
- API Reference. scikit-learn.org. https://scikit-learn.org/stable/api/index.html