Kaggleメダル獲得への挑戦 Vol.3 ~主成分分析
はじめに
この記事は30歳未経験からエンジニアに転身したド文系の僕が、機械学習エンジニアになるためにKaggleに挑戦しながらスキルを身につけていく過程をリアルタイムで発信していく連載記事となります。
毎週末、その週に僕自身が学んだ内容を発信していきます。
学びながら発信していきますので、場合によっては間違った情報や、誤って理解してしまっているものもあるかもしれません。
その際はぜひ温かくご指摘をいただけるとうれしいです!
ド文系でも本気で取り組めば結果を出せるんだという姿を見せられるように、日々頑張っていきます!
2025年5月12日〜5月18日までの学び
注)Kaggleは英語で構成されているため、その用語に慣れるためにも、主要な用語は英語のまま学ぶこととしています。
なので以下の記事でもところどころ英語のまま記載していますが、それは上記の意図があって敢えてそう表記しているものなので、その点だけご理解ください。
今週は「主成分分析」にかなり苦戦したので、今回は「主成分分析」について調べたことをまとめました。
PCA(主成分分析)とは何か?
主成分分析(PCA)は、高次元のデータを低次元に圧縮するための次元削減手法。
簡単に言うと:
・相関のある特徴量を組み合わせて、新しい軸(=主成分)を作る
・情報の損失を最小限に抑えつつ、データの次元数を減らす
例)
100人の身長と体重のデータがあるとします。
この2つは相関があると言える(身長が大きい人の方が基本的には体重も重くなりがち)。
主成分分析は「身長と体重の情報をなるべく保ちながら、それを1つの軸で表現できないか?」と考える手法のこと。
→この「新しい軸」が主成分
第一主成分(PC1)は、データの「ばらつき(分散)」が一番大きくなる方向に取られる。
第二主成分(PC2)は、PC1と直交(90度)で、次に大きな分散を持つ方向となる。
データの「ばらつき(分散)」が一番大きくなる方向に取るとは?
例えば、以下のようなデータを考える
・X軸:身長(cm)
・Y軸:体重(kg)
このとき、身長と体重には強い正の相関がある(身長が高い人ほど体重も重くなる傾向)。
この データを散布図で描くと、こんな感じになる
このデータの点群を「どの方向に広がっているか?」と考えると、右上方向(斜め)に一番ばらついているとわかる。
PCAでは、この「一番ばらつきのある方向」に軸を取りたい
・このばらつきこそが「情報量」と見なされる
・よって「ばらつきが最大になる方向」が第一主成分(PC1)となる
では「ばらつきが大きい」とは具体的に言うと?
数学的には、ばらつきとは分散(variance)のこと。
・ある方向に沿って、データがどれくらい広がっているか?
・PCAでは、あらゆる方向を試してみて、その中で「最も分散が大きい」方向を選ぶ
具体的には:
・各方向ベクトル(単位ベクトル)にデータを投影してみて、
・投影された点の分散を計算
・一番分散が大きくなる方向が第一主成分
この処理は、線形代数的には共分散行列の固有ベクトル・固有値分解で求める
PC1と直交で次に大きな分散を持つ方向とは?
「直交」とは「90度の角度で交わること」
つまりPC2はPC1と全く別の情報を持つ軸になる(直交=PC1とPC2は相関が全くないの意)
これがなぜ必要かというと、
・PC1と似た方向だと、同じ情報を二重に見ることになる
・PCAは「できるだけ少ない主成分で、できるだけ多くの情報(分散)を保持したい」ので、すでに選んだ軸とは直交する方向を選ぶルールがある
その中で「次にばらつきが大きい」ものを選ぶ
つまり、PC1との直交条件を満たしたうえで、「データがまだ大きくばらついている方向」をPC2とする。
→これが「PC1に直交していて、かつ次に分散が大きい方向」という意味
どのようなケースにPCAを使用するか?
・高次元で特徴量が多すぎる
→計算効率、可視化、過学習対策
・特徴量間に相関がある
→主成分に集約することで冗長性を減らせる
・データを2次元や3次元で可視化したい
→データの構造やクラスタ傾向が見える
・ノイズの多いデータをスムーズにしたい
→情報の本質部分だけを抽出可能
PCAを使うべきでないケース
・特徴量に意味があり、それを保持したいとき
→PCAは特徴量の「意味」を失う(主成分は解釈しにくい)
・スケールがバラバラで、標準化しないまま使う場合
→分散ベースなので、スケールが大きい特徴量が支配してしまう
・非線形な構造を捉えたいとき
→PCAは線形手法なので、非線形構造(曲線的な分離など)は苦手
どの特徴量とどの特徴量を組み合わせるか?の判断はどうするか?
PCAは手動で組み合わせを決める必要はない
PCAは、すべての数値特徴量の共分散行列から、自動で「ばらつきを最大化する軸(=主成分)」を見つけてくれる
ただし使用する前に以下の点を考慮すべき
・使用する特徴量を自薦に選ぶ必要はある
→目的に関係ない特徴量(例:IDやカテゴリ変数)は除外
→数値変数に限定(カテゴリはOneHot化するか除外)
・特徴量を標準化(Zスコア化)する
→(x - 平均) / 標準偏差
→分散ベースの手法なのでスケール調整が必須
・主成分の意味合いはloadings(寄与係数)で確認
→どの特徴量が主成分にどの程度貢献しているかを可視化可能
→これにより「どの特徴量の組み合わせでできた軸か」が見えてくる
loadingsとは?
PCAで得られる主成分(PC1、PC2…)は元の特徴量の線形結合でできている
このとき「各特徴量がどれだけ主成分に効いているか?」 を示す係数が loadings(寄与係数)
数式で書くと以下の通り(後半パートで使用しているIris(アヤメ)データのPC1の場合)
PC1 = a₁ × sepal length + a₂ × sepal width + a₃ × petal length + a₄ × petal width
この a₁, a₂, a₃, a₄ が loadings
・正の値 → 同じ方向に影響
・負の値 → 逆の方向に影響
・絶対値が大きい → PC1を構成する上で重要な特徴量
pythonコードでloadingsを可視化してみる
# 主成分軸の方向(各特徴量が主成分にどれだけ効いてるか)
loadings = pd.DataFrame(
pca.components_.T,
columns=['PC1', 'PC2'],
index=feature_names
)
print("主成分ごとの特徴量の寄与(loadings):\n", loadings)
結果がこんな感じになったとする
PC1 PC2
sepal length (cm) 0.36 -0.08
sepal width (cm) -0.08 0.55
petal length (cm) 0.86 0.28
petal width (cm) 0.36 0.78
PC1の構成を見るとsepal width はほとんど効いてない(-0.08)
→ PC1は「花びらのサイズに関するばらつき」を最も反映している主成分だと考えられる
PC2はpetal width と sepal width の値が大きい
→ PC2は、「花びらとガクの幅に関する軸」と解釈できる
どのくらいの主成分を使えばいいか?
「累積寄与率」を見るのが一般的。
累積寄与率80%〜95%を目安に上位の主成分を選ぶ。
例えば「元の10次元のうち、PC1~PC3で90%説明できる」なら、その3つだけ使えばよい。
まとめ
・主成分分析とは?
→多次元データを、情報をなるべく保ったまま圧縮する手法
・いつ使うか?
→特徴量が多いとき、相関があるとき、可視化したいとき
・逆に使うべきでないときは?
→特徴量の意味を重視したいとき、非線形な関係があるとき
・どの特徴量を使うか?
→数値変数を選び、スケーリングし、PCAで自動で抽出
・主成分の意味づけはどのようにするか?
→pca.components_ でどの特徴量が効いてるかを見る
PythonでPCAを実践してみる
##ワインの成分データ
以下のようなワインの成分データがあったとする
このデータをもとに
・「成分が似ているワイン」を見つけたい
・可視化して、クラスタ傾向を知りたい
・でも、次元が多くなると視覚的に見にくいので「2次元に圧縮」したい
→ここでPCAの出番
具体的コードは以下
import pandas as pd
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
import matplotlib.pyplot as plt
# データを作成
data = {
'Alcohol': [12.5, 13.0, 11.8, 13.2, 12.0, 12.8, 13.1, 11.9, 12.2, 12.6],
'Sugar': [6.1, 5.8, 7.0, 5.5, 6.9, 6.0, 5.6, 7.2, 6.5, 6.3],
'Acidity': [3.5, 3.2, 3.8, 3.1, 3.6, 3.4, 3.3, 3.9, 3.7, 3.5],
}
df = pd.DataFrame(data)
# ---------- Step 1: 標準化(Zスコア化) ----------
scaler = StandardScaler()
X_scaled = scaler.fit_transform(df)
# ---------- Step 2: PCA実行 ----------
pca = PCA()
X_pca = pca.fit_transform(X_scaled)
# ---------- Step 3: 結果の確認 ----------
# 寄与率(分散の割合)
print("各主成分の寄与率:", pca.explained_variance_ratio_)
print("累積寄与率:", np.cumsum(pca.explained_variance_ratio_))
# 主成分軸の方向(成分への貢献度)
loadings = pd.DataFrame(
pca.components_.T,
columns=['PC1', 'PC2', 'PC3'],
index=df.columns
)
print("各特徴量の主成分への寄与:\n", loadings)
# ---------- Step 4: 2次元に削減して可視化 ----------
pca2 = PCA(n_components=2)
X_2d = pca2.fit_transform(X_scaled)
plt.figure(figsize=(8,6))
plt.scatter(X_2d[:, 0], X_2d[:, 1], color='orange')
for i, (x, y) in enumerate(X_2d):
plt.text(x+0.05, y+0.05, str(i+1)) # サンプル番号
plt.xlabel("PC1(主成分1)")
plt.ylabel("PC2(主成分2)")
plt.title("PCAによるワインの2次元可視化")
plt.grid()
plt.show()
結果は以下の通り
各主成分の寄与率: [0.97217348 0.02080731 0.0070192 ]
累積寄与率: [0.97217348 0.9929808 1. ]
各特徴量の主成分への寄与:
PC1 PC2 PC3
Alcohol 0.580184 0.322026 0.748121
Sugar -0.578580 -0.483537 0.656838
Acidity -0.573263 0.813936 0.094223
コードからわかる主成分分析の考え方
分析の読み取りポイント
・PC1の寄与率が高ければ、それだけで十分情報を保持できている
・loadings(寄与係数) を見ると「どの特徴量が主成分を構成しているか」がわかる(例:PC1に対して Alcohol が大きい → PC1は「アルコール強さの軸」かも?)
Iris(アヤメ)データセット
Irisデータセットとは
・150件の花データ(3種類×50件)
・各花には以下の4つの数値特徴量がある:
1. sepal length(がく片の長さ)
2. sepal width(がく片の幅)
3. petal length(花びらの長さ)
4. petal width(花びらの幅)
・ラベル(花の品種)付き:setosa, versicolor, virginica
→ PCAによって、この4次元データを2次元に圧縮して可視化してみる
Pythonコード
from sklearn.datasets import load_iris
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
import matplotlib
# 日本語フォント設定(Mac標準)
matplotlib.rcParams['font.family'] = 'AppleGothic'
matplotlib.rcParams['axes.unicode_minus'] = False
# ---------- Step 1: データ読み込み ----------
iris = load_iris()
X = iris.data
y = iris.target
feature_names = iris.feature_names
target_names = iris.target_names
# ---------- Step 2: 標準化 ----------
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)
# ---------- Step 3: PCA適用 ----------
pca = PCA(n_components=2)
X_pca = pca.fit_transform(X_scaled)
# ---------- Step 4: 結果の確認 ----------
# 寄与率(分散の割合)
print("各主成分の寄与率:", pca.explained_variance_ratio_)
print("累積寄与率:", np.cumsum(pca.explained_variance_ratio_))
# 主成分軸の方向(成分への貢献度)
loadings = pd.DataFrame(
pca.components_.T,
# 今回は主成分2つに指定しているので、columnsも2つ
columns=['PC1', 'PC2'],
index=feature_names
)
print("各特徴量の主成分への寄与:\n", loadings)
# ---------- Step 5: 可視化 ----------
plt.figure(figsize=(8, 6))
for i, target_name in enumerate(target_names):
plt.scatter(
X_pca[y == i, 0], X_pca[y == i, 1],
label=target_name, alpha=0.7
)
plt.xlabel(f"PC1 ({pca.explained_variance_ratio_[0]*100:.1f}%)")
plt.ylabel(f"PC2 ({pca.explained_variance_ratio_[1]*100:.1f}%)")
plt.title("PCAによるIrisの次元削減・可視化")
plt.legend()
plt.grid(True)
plt.show()
結果
各主成分の寄与率: [0.72962445 0.22850762]
累積寄与率: [0.72962445 0.95813207]
各特徴量の主成分への寄与:
PC1 PC2
sepal length (cm) 0.521066 0.377418
sepal width (cm) -0.269347 0.923296
petal length (cm) 0.580413 0.024492
petal width (cm) 0.564857 0.066942
改めてまとめ
・PCAは既存の特徴量の次元(つまり特徴量のカラム数)を減らすための仕組み
・PCAを実施する際は、次元を減らしたい特徴量を指定する(たとえば全体で20カラムあって、そのうちPCAの対象としたいカラムが10個ならその10個を指定する
・算出した各PCの寄与率を確認し、高い方から使用するPCを決める(合計累積寄与率が90〜95%以上の寄与率になる主成分のみ使用すれば筋の良い分析ができる→ここで次元が減る)
主成分分析の理解に苦しんだ今週でしたが、このようにまとめることでなんとなくやっていることが理解できたような気がします
今後いろんなコンペに挑戦していく中できっともっと理解が深まっていくと思うので、今の時点ではその未来の自分に期待します!
今週の学びは以上です。
次週も頑張って学びを積み上げていきます!
未経験からエンジニア転職を目指すあなたへ
僕は今「未経験から本気でエンジニア転職を目指す人」のための新しいサポートサービスを準備中です。
もしあなたが…
- 業務改善を通じて価値を出せるエンジニアを目指したい
- 数字や仕組みで現場を変える力を、キャリアに活かしたい
- これから学ぶべきステップを明確にしたい
そんな想いを少しでも持っているなら、ぜひ僕の公式LINEに登録しておいてください。
サービスに関する先行案内や最新情報を優先的にお届けします。
👉公式LINEはこちら
Discussion