機械学習において特徴量の間のスケールを揃えることを特徴量スケーリングといいます。
特徴量スケーリングを行うことで、モデルの学習時間短縮や予測精度向上を図ることができます。
このページでは、特徴量のスケールを揃える方法を紹介します。
目次
特徴量スケーリングの必要性
特徴量スケーリング(Feature Scaling)とは、複数の特徴量を用いた機械学習において特徴量間の数値のスケールを揃えることで学習にかかる時間短縮や予測精度向上を図る前処理のことです。
ユークリッド空間上の距離を使用して予測値を求めるモデルでは、予測値の値は特徴量のスケールに影響を受けます。
説明変数の中にスケールが異なる特徴量が含まれていると、スケールの大きな特徴量の影響を強く受けたり、逆にスケールの小さな特徴量の変動をモデルに十分に反映できなかったりして学習に時間を要したり最適な予測値を求められなくなります。
そこで、特徴量間のスケールを揃えることで、モデルのチューニングを行うのが特徴量スケーリングです。
特徴量のスケールの影響を受ける手法の例として、$ k $ -means クラスタリング、$ kNN $、重回帰分析などがあり、いずれもユークリッド距離を使用するモデルです
一方、決定木のように論理的に条件分岐を繰り返すようなモデルはスケールの影響を受けないため特徴量スケーリングは不要です。
線形回帰であっても正規方程式のように解析的に方程式を解く場合もスケーリングが不要になります。
以下では、特徴量のスケールを揃えるために使われる様々な手法について簡単にまとめます。
特徴量スケーリングでは標準化を行う場合が多いですが、その他の数値データのスケールを変換する方法についてもふれます。
正規化
正規化(Normalization)は特徴量スケーリングの一種であり、最小値を0、最大値を1と固定して値のスケールを振りなおす(リスケールする)処理です。
scikit-learnで正規化を行う際には sklearn.preprocessing.MinMaxScaler を使用します(最小値:0、最大値:1)。
from sklearn.preprocessing import MinMaxScaler # 正規化
scaler = MinMaxScaler() # インスタンス生成
scaler.fit(X) # 正規化に必要な統計量(最大値・最小値)を計算
X_scaled = scaler.transform(X) # 実際に正規化を実行
pd.DataFrame(X_scaled, columns=X.columns) # 正規化したdfを作成
正規化の処理は次の式で表されます。
$$ x_{ rescale } =
\frac{ x - x_{ min } }{ x_{ max } - x_{ min } }
$$
ここで $ x $ は変換前、 $ x_{ rescale } $ は変換後の値、$ x_{ min } $ は最小値、$ x_{ max } $ は最大値です。
後述する標準化とまぎらわしいですが、正規化では最大値と最小値が特定の値になります。
そのため、最小値と最大値が一定範囲内におさまることがわかっているデータに対して有効です(例:五段階評価と10点満点の評価のリスケール)。
また、スパースなデータに対しては sklearn.preprocessing.MaxAbsScaler を使用して「最大値の絶対値」が1となるようにリスケールできます(最小値:-1、最大値:1)
$$ x_{ rescale } =
\frac{ x }{ \vert x_{ max } \vert }
$$
正規化はデータセットの最大値と最小値によってスケールが変わります。
そのため、小売店の日次売上のように最大値や最小値に上限がないデータには向いていません。
データに極端な最大値や最小値がデータに含まれているとスケールが大きく変わってしまいます。
標準化
標準化(Standardization)は特徴量スケーリングの一種であり、平均値が0、標準偏差が1になるように値のスケールを振りなおす(リスケールする)処理です。
scikit-learnで標準化を行う際には sklearn.preprocessing.StandardScaler を使用します(平均:0、標準偏差:1)。
from sklearn.preprocessing import StandardScaler # 標準化
scaler = StandardScaler() # インスタンス生成
scaler.fit(X) # 標準化に必要な平均値等の統計量を計算
X_scaled = scaler.transform(X) # 実際に標準化を実行
pd.DataFrame(X_scaled, columns=X.columns) # 標準化したdfを作成
標準化の処理は以下のような式で表されます。
$$ x_{ rescale } =
\frac{ x - x_{ mean } }{ s }
$$
ここで、$ x_{ mean } $ は $ x $ の平均値、$ s $ は標準偏差です。
標準化はデータが正規分布に従っている場合は特に効果的であり、最大値と最小値の影響をうける正規化と比較して外れ値の影響を受けづらい方法です。
そのため、特徴量スケーリングにおいて標準化は正規化よりも好まれ、しばしば使われます。
それでも、極端な外れ値があるとその影響をうけてしまうことがあります。
そのような場合には sklearn.preprocessing.RobustScaler を使用することで、四分位点を基準にしてスケールすることができます。
デフォルトでは中央値をゼロ、四分位範囲(第一四分位点と第三四分位点)を使ってスケールします。
$$ x_{ rescale } =
\frac{ x - x_{ median } }{ x_{ 75\% } - x_{ 25\% } }
$$
ここで、$ x_{ median } $ は中央値、$ x_{ 75\% } $ は第三四分位数、$ x_{ 25\% } $ は第一四分位数です。
の範囲におさまるようにスケールできます。
離散化
離散化とは、連続値(0.9, 0.99, 0.99, … )を離散値(0.90, 1.00, 1.00, …)に変換することです。
スケールのことなる数値データを扱う際には、離散化により連続値をいくつかの階級(カテゴリ)に分けて質的変数のように扱うことで、スケールの違いを調整することができます。
特徴量スケーリングに関わらず、単純に説明変数の数値が2倍になったからといって目的変数へ2倍の影響があるとは限らない場合には、離散化の閾値を工夫することで対処できます。
たとえば、動画サイトで視聴回数からユーザーへのレコメンドを行う動画を決めるモデルを作る場合を考えます。
この場合、視聴回数20回の人は視聴回数10回の人の2倍好きだとは言えません。
一定以上の視聴回数の場合は一律で評価する方が現実に適合しています。
このような場合は、一定値ごとに閾値を設け、その閾値間で階級を作る離散化を行うのが効果的です。
たとえば、視聴回数0回、1回、2回、3-4回、5回以上といった具合です。
閾値の決め方
離散化の閾値を恣意的に決めてしまうと、離散化によりデータの分布が元のデータから乖離してしまう可能性があります。
そこで、離散化の際の閾値の決め方について紹介します。
1.固定幅による離散化
離散化の階級幅を決める際に、各階級の幅を固定幅に決めてしまう方法があります。
一例として、年齢を年代(10代、20代、・・・)に離散化する際は固定幅(10歳刻みなど)で離散化を行うのが一般的です。
これは離散化した後の各階級のデータ数に極端な偏りが出ない場合に効果的です(例:30-39歳と40-49歳の人数に極端な差がない)。
固定幅で区切ると該当するデータ数が極端に少ない階級が出る場合は、1つの階級にまとめます(例:10代未満、80代以上)。
2.分位数による離散化
次にデータの分布に基づいて分位数(quantile)を閾値として階級を決める方法について紹介します。
データの分布に極端なギャップがあり、固定幅で階級を区切ってしまうと該当データがない階級が出てしまう場合には使われます。
中央値や第1四分位数などの分位数に基づいて階級を決めることで、各階級に入るデータ数の偏りが無くなります。
その代わり、各階級の幅(上限と下限の閾値の差)は階級ごとにばらばらになります。
3.二値化
二値化とは、数値データに対して条件を決めてその条件に当てはまるか否かで2つに分類してしまう方法です。
二値化を行うことで、数値データを質的変数のように扱うことができます。
この手法は分布に極端な偏りがある場合に使います。
一例として、「2023年の海外渡航回数」を「2023年に1回以上海外渡航したか否か」の2択に変換する場合です。
日本人の多くは1年間の海外渡航回数が0回であるのに対し、年間で数十回海外渡航している人も少数存在します。
そのような裾が長い分布をするデータでは、二値化を行うことで本質的な情報を保持したままスケールを調整することができます。
対数変換・べき変換
データによってスケールが指数関数的に変化する場合(10, 100, 1000, …)には、対数変換を行うことでスケールの影響の違いを緩和できます。
対数変換が有効なのは、非常に大きな値をとる確率が高い分布(正規分布よりも裾が重い分布)です(例:商品ごとの販売数やWebページごとのアクセス数)。
注意点としては、対数変換を行うためにはデータは正の値をとる必要があります。
0が値として含まれる場合はそのままでは対数を取ることができないため、$ log(x+1) $ による変換がよく使われます。
べき変換
べき変換(power transform)は対数変換を一般化してより汎用的にした手法です。
具体的なべき変換の手法としてBox-Cox変換とYeo-Johnson変換があります。
Box-Cox変換は正規分布に従わない分布に対して、正規分布に近づけるような変換です。
ポアソン分布を仮定した次のような式で定義されます。
$$ \begin{eqnarray}
\ \widetilde{ x } (λ)
=
\begin{cases}
\frac{ x^λ - 1 }{ λ } \quad ( λ \neq 0 ) \\
\ln (x) \quad ( λ = 0 )
\end{cases}
\end{eqnarray} $$
ここで、$ λ = 0 $ のときは対数変換、$ λ = 0.5 $ のときは平方根変換という変換になります。
$ λ < 1 $ のときは対数変換同様に大きな値を縮小するような返還となり、$ λ > 1 $ のときは逆に大きな値を拡大する変換になります。
Box-Cox変換の注意点としては、対数変換同様に全てのデータが正の値の場合のみに適用できる点です。
負の値を含む場合は全ての値が正になるように定数を加えたり、Yeo-Johnson変換を使用します。
Yeo-Johnson変換では、Box-Cox変換の定義式を拡張してゼロや負の値を含む場合でも適用できるようにしたものです。
Box-Cox変換よりも汎用性が高いため、べき変換を行う場合には通常Yeo-Johnson変換を行います。
Yeo-Johnson変換は以下の式で表されます。
$$ \begin{eqnarray}
\ \widetilde{ x } (λ)
=
\begin{cases}
\frac{ ( x + 1 )^λ - 1 }{ λ } \quad ( λ \neq 0, x \geq 0 ) \\
\log (x + 1) \quad ( λ = 0, x \geq 0 ) \\
\frac{ -[( -x + 1 )^{ 2-λ } - 1 ]}{ 2-λ } \quad ( λ \neq 2, x \leq 0 ) \\
- \log (-x + 1) \quad ( λ = 2, x \leq 0 ) \\
\end{cases}
\end{eqnarray} $$
L2正規化
L2正規化(L2スケーリング)では、データをL2ノルム(ユークリッドノルム )$ || x ||_2 $ で割ることで特徴量を正規化します(L2正則化(Ridge)とは異なる手法です)。
$$ x_{ rescale } =
\frac{ x }{ || x ||_2 }
$$
L2ノルムはベクトル空間におけるベクトルの長さを表します。
ピタゴラスの定理より、以下のように表されます。
$$ || x ||_2 = \sqrt{ x_1^2 + x_2^2 + \cdots + x_n^2 } $$
L2正規化を行うことで特徴量のノルムは1になります。
参考文献
Alice Zheng and Amanda Casari 「機械学習のための特徴量エンジニアリングーーその原理とPythonによる実践」オライリー・ジャパン(2019)
スケーリング 農学情報科学 2024/1/28閲覧
門脇 大輔 他「Kaggleで勝つデータ分析の技術」技術評論社 (2019)
6.3. Preprocessing data, scikit-learn 2024/1/28閲覧
機械学習 – 特徴量のスケーリングについて pystyle 2024/1/29閲覧
scikit-learn数値系特徴量の前処理まとめ(Feature Scaling) Qiita 2024/1/29閲覧
Box-Cox変換を理解してみる Qiita 2024/1/28閲覧
Power transform, Wikipedia 2024/1/28閲覧
Box and Cox "An Analysis of Transformations" J. R. Stat. Soc., Ser. B, Methodol., 26(2) p211-252 (1964)
Box-cox変換を用いて正規分布に従わないデータを解析をしてみよう! スタビジ 2024/1/28閲覧
吉田 拓倫、松井 藤五郎「血液検査データに対する包括的な分類分析のためのデータ変換法」 人工知能学会全国大会論文集 セッションID: 2P5-J-2-02(2019)
PythonでベクトルをL2正規化(normalization)する方法一覧 Qiita 2024/1/29閲覧