100次元のセンサデータを目の前にして、「とりあえず散布図でデータの傾向を見たい」と思ったことはないでしょうか。あるいは、機械学習モデルが高次元データに対して過学習してしまい、「効いている軸だけを残してノイズの軸を捨てたい」と感じた経験はないでしょうか。
このような場面で長年使われ続けているのが 主成分分析(Principal Component Analysis, PCA) です。PCA は、データを「最も情報量を残せる方向」に座標変換し直し、次元を減らしても元の散らばり方をできる限り保つ、という古典かつ強力な手法です。100年以上前(Pearson 1901, Hotelling 1933)に提案されたにもかかわらず、現代の機械学習・統計学の最前線でも前処理・可視化・特徴抽出として日常的に使われています。
PCA を理解すると、その応用範囲の広さに驚かされます。たとえば、顔認識における Eigenfaces、遺伝子発現データの可視化、株価リターンの主成分でリスク要因を抽出するファクターモデル、画像圧縮、ノイズ除去、推薦システムの行列分解の出発点として。さらに、現代的な手法(オートエンコーダ、Variational Autoencoder、Diffusion モデル)も「データ多様体の本質的な次元を捉える」という意味で PCA と同じ問いから派生しています。
本記事では、PCA を「分散を最大化する直交方向を探す」という最適化問題から導出し、共分散行列の固有値分解と SVD の関係、寄与率による次元決定、カーネル PCA への非線形拡張、そして Python での実装と Iris / digits データへの応用までを通しで解説します。線形代数と最適化の知識があれば、なぜ固有ベクトルが主成分になるのかが「定理」ではなく「自然な帰結」として腑に落ちる構成にしました。
本記事の内容
- PCA の直感(多次元データを少ない軸に集約する)
- 分散・共分散行列の復習と幾何学的意味
- 「分散最大化」を制約付き最適化として定式化し、ラグランジュ未定乗数法で解く
- 共分散行列の固有値分解と特異値分解(SVD)の対応
- 寄与率・累積寄与率による次元の決め方
- 主成分の解釈と Iris データへの応用
- カーネル PCA — 同心円のような非線形データへの拡張
- Python でスクラッチ実装 + scikit-learn 利用法、画像復元、t-SNE / UMAP との比較
- PCA の限界と、現代の表現学習へのつながり

前提知識
この記事を読む前に、以下の概念について把握しておくと理解が一気に進みます。
- ベクトル・行列の基本演算(内積、転置、行列積)
- 分散・共分散の定義と意味
- 固有値・固有ベクトルの計算と直交行列の性質
- ラグランジュ未定乗数法(制約付き最適化)
- 特異値分解(SVD)の概念
行列・ベクトルの演算に慣れている方は、後半の数学的な導出から読み始めて問題ありません。一方で、これらに自信のない方は、まず「## PCA の直感 — 多次元データを少ない軸に集約する」だけを読んで全体像を掴み、そのあと数学的議論に戻ってくる読み方をおすすめします。
PCA の直感 — 多次元データを少ない軸に集約する
「重要な軸」と「捨ててよい軸」
PCA を一言で表すと、「データが大きく広がっている方向を新しい軸として選び直す」 手法です。なぜ広がっている方向(=分散が大きい方向)が重要なのか。それは、分散が大きいほどデータ点を見分けやすい、つまり情報量が多いからです。逆に、分散がほとんどゼロの軸(つまり全データ点が同じような値を取る軸)は、削除してもデータの個性をほとんど失いません。
身長と体重のデータを例に取りましょう。普通の人間集団では身長と体重には強い正の相関があるため、データ点は右上がりの楕円状に分布します。

この図の中で、データが最も広がっているのは「右上 — 左下」の方向です。この方向に新しい座標軸を引くと、その軸上の値は「体格の大きさ」を表現する一次元の指標になります。これと直交する「右下 — 左上」の方向は分散が小さく、おおまかには「身長の割に体重が重いか軽いか(≒肥満度)」を表します。元の (身長, 体重) という 2 つの軸を捨て、「体格」「肥満度」という新しい 2 つの軸に取り直す——これが PCA の幾何学的なエッセンスです。

上図は、共分散行列を等高線(マハラノビス距離が同じ点を結んだ線)で可視化したものです。一番左の「等方」な分布では、どの方向にも同じだけ広がっているため主成分の方向は一意に決まりません。中央の「無相関だが異分散」のケースでは、座標軸方向そのものが主成分になります。一番右の「正の相関」がある分布では、楕円が斜めに傾き、主成分も斜めの方向に伸びます。共分散行列の固有ベクトルが、この楕円の長軸・短軸を与えている——これが、PCA の幾何学の核心です。
次元削減として使う
身長と体重の例では 2 次元のままなので、まだ「次元削減」にはなっていません。しかし、「体格」軸の上にデータを射影し(つまり「肥満度」軸の情報を捨て)て1次元データとして扱えば、データの本質的な散らばりを大きく失わずに次元を半分にできます。

この「捨ててよい軸を選んで削る」ことが、$d$ 次元データを $r$ 次元 ($r < d$) に変換するときの基本戦略です。$d = 4$ の Iris データから 2 次元の可視化を作るのも、$d = 784$ の MNIST 画像を 50 次元の特徴量に圧縮するのも、考え方は同じです。
PCA の用途
PCA が活躍する場面は大きく分けて 3 つあります。
- 可視化 — 4 次元以上のデータを 2 〜 3 次元に落とし、人間が散布図で眺められるようにする。Iris、遺伝子発現、市場リターンなどでよく使われます。
- 次元削減(前処理) — 機械学習モデルに入れる前に冗長な軸を削り、「次元の呪い」を緩和する。少ないデータ点でも安定して学習できるようになります。
- 特徴抽出 / 圧縮 / ノイズ除去 — 上位 $k$ 主成分だけで再構成すると、低分散ノイズが落ちて主要な構造だけが残ります。Eigenfaces や画像圧縮はこの応用です。
直感が掴めたところで、PCA がどのような最適化問題なのかを定式化し、その解が共分散行列の固有ベクトルに帰着することを示しましょう。
PCA を最適化問題として定式化する
設定と記号
$n$ 個のサンプルがあり、それぞれが $d$ 次元の観測値であるとします。$i$ 番目のサンプルを $\boldsymbol{x}_i = (x_{i1}, x_{i2}, \dots, x_{id})^{\top} \in \mathbb{R}^d$ と書き、これを行に並べたデータ行列を
$$ \boldsymbol{X} = \begin{pmatrix} \boldsymbol{x}_1^{\top} \\ \boldsymbol{x}_2^{\top} \\ \vdots \\ \boldsymbol{x}_n^{\top} \end{pmatrix} \in \mathbb{R}^{n \times d} $$
とします。データの平均ベクトルを $\bar{\boldsymbol{x}} = \frac{1}{n}\sum_{i=1}^{n} \boldsymbol{x}_i$ とし、平均を引いた中心化済みデータ行列を $\bar{\boldsymbol{X}} = \boldsymbol{X} – \mathbf{1}\bar{\boldsymbol{x}}^{\top}$ と表します。以降の議論ではデータは中心化済みであることを仮定します(中心化していないと、後の分散の式が成り立ちません)。
PCA で求めたい第 1 主成分の方向を、長さ 1 の単位ベクトル $\boldsymbol{u} \in \mathbb{R}^d$ で表します。$\boldsymbol{u}$ にデータを正射影した値
$$ y_i = \boldsymbol{u}^{\top} \boldsymbol{x}_i $$
の分散が最大になるような $\boldsymbol{u}$ を見つけることがゴールです。
射影した値の分散を書き下す
中心化済みデータ ($\bar{\boldsymbol{x}} = 0$) を仮定すると、射影 $y_i = \boldsymbol{u}^{\top}\boldsymbol{x}_i$ の平均もゼロです。したがって分散は
$$ \mathrm{Var}(y) = \frac{1}{n}\sum_{i=1}^{n} y_i^2 = \frac{1}{n}\sum_{i=1}^{n} (\boldsymbol{u}^{\top}\boldsymbol{x}_i)^2 $$
と書けます。$(\boldsymbol{u}^{\top}\boldsymbol{x}_i)^2 = (\boldsymbol{u}^{\top}\boldsymbol{x}_i)(\boldsymbol{x}_i^{\top}\boldsymbol{u}) = \boldsymbol{u}^{\top}(\boldsymbol{x}_i\boldsymbol{x}_i^{\top})\boldsymbol{u}$ と分解できることに注目しましょう。これは「スカラーを 2 つに割って、間に行列をはさんだ二次形式」になっています。和の中の $\boldsymbol{u}, \boldsymbol{u}^{\top}$ は $i$ に依存しないので外に出して、
$$ \mathrm{Var}(y) = \boldsymbol{u}^{\top}\left(\frac{1}{n}\sum_{i=1}^{n}\boldsymbol{x}_i\boldsymbol{x}_i^{\top}\right)\boldsymbol{u} = \boldsymbol{u}^{\top} \boldsymbol{S} \boldsymbol{u} $$
を得ます。ここで $\boldsymbol{S} = \frac{1}{n}\sum_i \boldsymbol{x}_i\boldsymbol{x}_i^{\top} = \frac{1}{n}\bar{\boldsymbol{X}}^{\top}\bar{\boldsymbol{X}}$ は 標本共分散行列 であり、$d \times d$ の対称行列です。観測データから一意に決まる既知量です。
制約付き最大化問題
ナイーブに $\boldsymbol{u}^{\top}\boldsymbol{S}\boldsymbol{u}$ を大きくしようとすると、$\boldsymbol{u}$ をいくらでも長くすれば値はいくらでも大きくなってしまいます。私たちが知りたいのは「方向」だけなので、長さを 1 に固定する制約 $\boldsymbol{u}^{\top}\boldsymbol{u} = 1$ を加えます。すると次の制約付き最大化問題になります。
$$ \boxed{\quad \max_{\boldsymbol{u}} \quad \boldsymbol{u}^{\top}\boldsymbol{S}\boldsymbol{u}, \quad \text{s.t.} \quad \boldsymbol{u}^{\top}\boldsymbol{u} = 1 \quad} $$
これが PCA の基本問題です。形は非常にシンプルで、$\boldsymbol{S}$ という二次形式を単位球の上で最大化せよ、というだけ。次にこれをラグランジュ未定乗数法で解きます。
ラグランジュ未定乗数法で解く
制約 $\boldsymbol{u}^{\top}\boldsymbol{u} = 1$ を組み込んだラグランジュ関数を
$$ \mathcal{L}(\boldsymbol{u}, \lambda) = \boldsymbol{u}^{\top}\boldsymbol{S}\boldsymbol{u} – \lambda(\boldsymbol{u}^{\top}\boldsymbol{u} – 1) $$
と置きます。$\boldsymbol{u}$ で微分してゼロと置くと、
$$ \frac{\partial \mathcal{L}}{\partial \boldsymbol{u}} = 2\boldsymbol{S}\boldsymbol{u} – 2\lambda\boldsymbol{u} = \boldsymbol{0} $$
となり、整理すると
$$ \boldsymbol{S}\boldsymbol{u} = \lambda \boldsymbol{u} $$
を得ます。これは $\boldsymbol{u}$ が共分散行列 $\boldsymbol{S}$ の固有ベクトル、$\lambda$ がそれに対応する固有値であることを意味します。つまり、PCA の最適化問題は固有値問題そのものなのです。
さらに、最適解での目的関数値を計算すると、$\boldsymbol{u}^{\top}\boldsymbol{S}\boldsymbol{u} = \boldsymbol{u}^{\top}(\lambda \boldsymbol{u}) = \lambda \boldsymbol{u}^{\top}\boldsymbol{u} = \lambda$ となります。つまり目的関数(分散)の値は固有値 $\lambda$ そのもの。最大の分散を得るには、$\boldsymbol{S}$ の固有値のうち最も大きいものに対応する固有ベクトルを選べばよい、という結論になります。
第 2 主成分以降
第 1 主成分 $\boldsymbol{u}_1$(最大固有値 $\lambda_1$ に対応)は決まりました。次に第 2 主成分 $\boldsymbol{u}_2$ を、$\boldsymbol{u}_1$ と直交する単位ベクトル ($\boldsymbol{u}_2^{\top}\boldsymbol{u}_1 = 0$, $\boldsymbol{u}_2^{\top}\boldsymbol{u}_2 = 1$) という条件のもとで、再び $\boldsymbol{u}_2^{\top}\boldsymbol{S}\boldsymbol{u}_2$ を最大化して求めます。同様にラグランジュ未定乗数法を使うと、第 2 主成分は 2 番目に大きい固有値 $\lambda_2$ に対応する固有ベクトルであることが導けます。
一般に第 $k$ 主成分は $\boldsymbol{S}$ の $k$ 番目に大きい固有値 $\lambda_k$ に対応する固有ベクトル $\boldsymbol{u}_k$ です。共分散行列は対称行列なので、固有ベクトルどうしは必ず直交します(実対称行列のスペクトル定理)。そして固有値の総和 $\sum_k \lambda_k = \mathrm{tr}(\boldsymbol{S})$ は元データの総分散に等しく、$\lambda_k$ は「第 $k$ 主成分が説明する分散の量」そのものを表します。
直感に戻ると、これはとても自然な話です。共分散行列の固有ベクトルがデータの広がりの「軸」を与え、固有値がその軸方向の広がりの大きさを与える——共分散の楕円を思い出せば、当たり前のように見えてきます。次のセクションで、この対応関係を可視化と数式の両面から固めましょう。
固有値分解と SVD — 同じものを 2 通りに見る
共分散行列の固有値分解
実対称行列であるサンプル共分散行列 $\boldsymbol{S}$ は、スペクトル定理により
$$ \boldsymbol{S} = \boldsymbol{V} \boldsymbol{\Lambda} \boldsymbol{V}^{\top} $$
と分解できます。ここで $\boldsymbol{V} = (\boldsymbol{u}_1, \boldsymbol{u}_2, \dots, \boldsymbol{u}_d)$ は固有ベクトルを列に並べた直交行列、$\boldsymbol{\Lambda} = \mathrm{diag}(\lambda_1, \lambda_2, \dots, \lambda_d)$ は固有値を対角に並べた対角行列です($\lambda_1 \geq \lambda_2 \geq \cdots \geq \lambda_d \geq 0$)。

直交行列 $\boldsymbol{V}$ は $\boldsymbol{V}^{\top}\boldsymbol{V} = \boldsymbol{I}$ を満たすので、変換 $\boldsymbol{x} \mapsto \boldsymbol{V}^{\top}\boldsymbol{x}$ は「主成分の方向に座標を回転する」操作に対応します。回転後の座標で見ると共分散行列が対角化され、各軸の分散が $\lambda_k$ となる——これが PCA の幾何学的な姿です。
データ行列の SVD
実は、共分散行列をわざわざ計算しなくても、データ行列 $\bar{\boldsymbol{X}}$ そのものに 特異値分解(Singular Value Decomposition, SVD) を適用すれば PCA は完了します。SVD は
$$ \bar{\boldsymbol{X}} = \boldsymbol{U} \boldsymbol{\Sigma} \boldsymbol{V}^{\top} $$
という分解で、$\boldsymbol{U} \in \mathbb{R}^{n \times r}$、$\boldsymbol{V} \in \mathbb{R}^{d \times r}$ は直交行列(列が直交する)、$\boldsymbol{\Sigma} = \mathrm{diag}(\sigma_1, \dots, \sigma_r)$ は非負の特異値を並べた対角行列です($r = \min(n, d)$)。
ここで $\bar{\boldsymbol{X}}^{\top}\bar{\boldsymbol{X}} = \boldsymbol{V}\boldsymbol{\Sigma}^{\top}\boldsymbol{U}^{\top}\boldsymbol{U}\boldsymbol{\Sigma}\boldsymbol{V}^{\top} = \boldsymbol{V}\boldsymbol{\Sigma}^2\boldsymbol{V}^{\top}$ となるので、共分散行列との対応は
$$ \boldsymbol{S} = \frac{1}{n}\bar{\boldsymbol{X}}^{\top}\bar{\boldsymbol{X}} = \boldsymbol{V}\left(\frac{\boldsymbol{\Sigma}^2}{n}\right)\boldsymbol{V}^{\top} $$
となり、$\boldsymbol{S}$ の固有ベクトルは $\bar{\boldsymbol{X}}$ の右特異ベクトル $\boldsymbol{V}$ と一致し、固有値は $\lambda_k = \sigma_k^2 / n$ という関係になります。$\boldsymbol{V}$ の列が主成分方向、$\boldsymbol{U}\boldsymbol{\Sigma}$ の列が主成分スコア(射影された値)を与えます。
SVD の幾何学
SVD には美しい幾何学的解釈があります。任意の線形写像 $\boldsymbol{A} = \boldsymbol{U}\boldsymbol{\Sigma}\boldsymbol{V}^{\top}$ は、
- $\boldsymbol{V}^{\top}$: 入力空間を回転(主成分方向に揃える)
- $\boldsymbol{\Sigma}$: 各軸ごとに伸縮(特異値倍する)
- $\boldsymbol{U}$: 出力空間を回転(出力の主軸に揃える)
の 3 段階に分解できる、というのが SVD の主張です。次の図で、単位円がこの 3 段階でどう変形されるかを見てください。

「回転 → 軸伸縮 → 回転」の 3 ステップで任意の線形写像が表現できる、という事実は線形代数の中でも特に直観に訴える結果です。PCA は本質的に「分散最大の伸縮方向(特異値が大きい方向)を見つけている」ことになります。
なぜ SVD を使うのか — 数値計算上の利点
理論的には固有値分解と SVD は等価ですが、実装上は データ行列の SVD を直接計算する方が安定です。共分散行列を作るために $\bar{\boldsymbol{X}}^{\top}\bar{\boldsymbol{X}}$ を計算すると、条件数が二乗されてしまい、特異値が小さい成分の精度が劣化します。scikit-learn の PCA も内部で SVD を使っており、$n < d$(サンプル数 $<$ 次元数)の場合でも問題なく動作します。
ここまでで PCA の「軸」が決まりました。残る問題は、$d$ 個ある主成分のうち何本を残すか——次元削減の度合いをどう決めるか、という実践的な問いです。
寄与率と累積寄与率による次元決定
寄与率の定義
第 $k$ 主成分が説明する分散の総分散に対する割合を 寄与率 (proportion of variance explained) といい、
$$ \text{寄与率}_k = \frac{\lambda_k}{\sum_{j=1}^{d}\lambda_j} $$
で定義します。$\sum_k \lambda_k = \mathrm{tr}(\boldsymbol{S})$ は元データの総分散なので、寄与率を全主成分で足すと 1 になります。第 1 主成分から第 $K$ 主成分までを残して残りを捨てたときの「保たれる情報量」は、累積寄与率
$$ \text{累積寄与率}_K = \frac{\sum_{k=1}^{K}\lambda_k}{\sum_{j=1}^{d}\lambda_j} $$
で測れます。

左の棒グラフ(scree plot)からは、固有値が指数的に急減することがわかります。これはデータに「効いている軸」と「効いていない軸」がはっきり分かれていることを示します。右のグラフでは累積寄与率が階段状に上がっていき、$k$ 本残せば 90 % / 95 % の分散が保たれる、という対応が読み取れます。
残す次元数の決め方
実用上、何本残すかを決める基準はいくつかあります。
- 累積寄与率がしきい値を超えるまで残す: 「90 %」「95 %」が定番。
PCA(n_components=0.95)のように指定すると、scikit-learn が自動でこのルールを適用してくれます。 - エルボー(肘)の位置で切る: scree plot で固有値の減衰が急に緩やかになる「肘」を目視で探す方法。
- Kaiser 基準: 標準化済みデータの場合、固有値が 1 以上の主成分だけ残す(平均的な変数の分散 = 1 より大きい寄与を持つ成分だけ残す、という発想)。
- クロスバリデーション: 後段の機械学習モデルの性能で次元数を決める。最も信頼できるが計算コストがかかります。
どれを使うかは目的次第です。可視化目的なら 2 〜 3 本に固定、機械学習の前処理なら 95 % 累積寄与率 or CV、という使い分けがよくあります。
寄与率の基準で「軸を選んだ」あとは、主成分そのものが何を意味しているのかを読み解く段階に進みます。次にその「解釈」を見ていきましょう。
主成分の解釈と Iris データへの応用
主成分の負荷(loading)
主成分ベクトル $\boldsymbol{u}_k = (u_{k1}, u_{k2}, \dots, u_{kd})^{\top}$ の各成分 $u_{kj}$ は、「第 $k$ 主成分が元の変数 $j$ をどれくらいの重みで足し合わせて作られているか」を表します。これを 負荷(loading) または 重み と呼びます。
たとえば、Iris データセットでは特徴量は (sepal length, sepal width, petal length, petal width) の 4 種類です。標準化したデータに PCA を適用すると、第 1 主成分の負荷は (0.52, -0.27, 0.58, 0.56) のような値を取ります。符号と大きさを読むと、第 1 主成分は「花弁が長く幅も広く、がく片も長いが、がく片の幅は逆に小さい花」を高くスコアリングする軸であることがわかります。これがアヤメの種類を分ける主要な軸になっているわけです。
Iris データセットへの適用
具体例として、Iris データ(150 サンプル × 4 変数)を PCA で 2 次元に落とすとどうなるかを見ましょう。Iris は 3 種類のアヤメ(setosa, versicolor, virginica)の花弁とがく片の長さ・幅という 4 変数からなるデータセットです。

実際にデータを読み込んで先頭を確認すると、次のような表形式になっています。

これを PCA で 2 次元化したのが次の散布図です。

scikit-learn による Iris の 2 次元 PCA の典型的な結果はこちらにもまとめています。

左の散布図は、4 次元データを上位 2 主成分の張る平面に射影した結果です。3 種類のアヤメ(setosa, versicolor, virginica)が、ほぼ重なりなく分離できていることがわかります。とくに setosa は他 2 種から完全に離れて固まっており、versicolor と virginica は連続的に並んでいて少しだけ重なります。右の寄与率のグラフを見ると、上位 2 主成分だけで全分散の約 96 % を説明できており、4 次元から 2 次元への削減は実質的に「ほぼ情報を失っていない」ことが読み取れます。
主成分の解釈は「向き」に意味がある
注意点として、主成分の 符号は不定 です。$\boldsymbol{u}_k$ も $-\boldsymbol{u}_k$ も同じ固有値に対応する固有ベクトルなので、軸の向きはアルゴリズムやライブラリのバージョンで反転することがあります。第 1 主成分のスコアが「左右逆」になっているからといって、結果が間違っているわけではありません。
身長と体重の例で実際に PCA を行うと、次のように回転された散布図が得られます。元データ(外れ値を除外した Davis の身体測定データ)はこのような分布で、

これに PCA を適用すると、次のように主成分軸方向に回転された散布図が得られます。

横軸が「体格の大きさ」、縦軸が「肥満度」に相当する軸になっています。データ点の散らばりは横方向が大きく、縦方向は狭くなっていることが目で見えるはずです。
2 次元 PCA は実質的に回転である
2 次元 → 2 次元 の PCA は次元削減になっていません。にもかかわらず「PCA を適用した」と言えるのは、それが直交基底の取り直し(回転)になっているからです。直交行列 $\boldsymbol{V}$ は
$$ \boldsymbol{V} = \begin{pmatrix} \cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{pmatrix} $$
の形を取り(反転を許せば $\det\boldsymbol{V} = \pm 1$)、座標変換 $\boldsymbol{y} = \boldsymbol{V}^{\top}\boldsymbol{x}$ は単に座標系を $\theta$ だけ回転して見直すことに対応します。身長・体重の例での $\boldsymbol{V}$ は約 $\theta = -34°$ に対応し、これがちょうど「楕円の長軸を水平軸に揃える」回転角に一致します。
Iris のようなクリーンなデータでは線形 PCA が極めて有効に働きますが、現実のデータはもっとひねくれた構造を持つことがあります。たとえばクラスがリング状や螺旋状に分布する場合、直線的な軸でいくら回転しても分離できません。そこで線形を超える拡張が必要になります。
カーネル PCA — 非線形な軸を扱う
線形 PCA の限界
PCA は本質的に「線形変換 $\boldsymbol{y} = \boldsymbol{V}^{\top}\boldsymbol{x}$ による次元削減」です。したがって、データが直線(超平面)でうまく分離できないような構造を持つ場合、PCA はうまく機能しません。典型的な失敗例が「同心円データ」です。

左の元データは内側のリングと外側のリングという 2 つのクラスからなり、直線ではどう引いても 2 群を分離できません。中央の通常 PCA の結果を見ると、回転しかしないので構造はそのままです。一方、右のカーネル PCA(RBF カーネル)では 2 群が完全に分離されて 1 次元軸でクラスを判別できるようになっています。
カーネルトリック
カーネル PCA のアイデアは、データを 無限次元の特徴空間 $\mathcal{H}$ に写像 $\phi: \mathbb{R}^d \to \mathcal{H}$ してから PCA を行うというものです。直接 $\phi(\boldsymbol{x})$ を扱うとコストが膨大ですが、カーネル関数
$$ k(\boldsymbol{x}, \boldsymbol{x}’) = \phi(\boldsymbol{x})^{\top}\phi(\boldsymbol{x}’) $$
を介して「内積だけ」で計算できるなら、$\phi$ を明示的に計算する必要はありません(カーネルトリック)。代表的なカーネルは
- ガウス(RBF)カーネル: $k(\boldsymbol{x}, \boldsymbol{x}’) = \exp(-\gamma \|\boldsymbol{x} – \boldsymbol{x}’\|^2)$
- 多項式カーネル: $k(\boldsymbol{x}, \boldsymbol{x}’) = (\boldsymbol{x}^{\top}\boldsymbol{x}’ + c)^p$
- シグモイドカーネル: $k(\boldsymbol{x}, \boldsymbol{x}’) = \tanh(\alpha\, \boldsymbol{x}^{\top}\boldsymbol{x}’ + c)$
などです。カーネル PCA では、共分散行列の代わりに グラム行列 $\boldsymbol{K} \in \mathbb{R}^{n \times n}$ ($K_{ij} = k(\boldsymbol{x}_i, \boldsymbol{x}_j)$) の固有値問題を解きます。
カーネル PCA のアルゴリズム
- 全データ点に対してグラム行列 $\boldsymbol{K}$ を計算する。
- 中心化: $\tilde{\boldsymbol{K}} = \boldsymbol{K} – \boldsymbol{1}_n\boldsymbol{K} – \boldsymbol{K}\boldsymbol{1}_n + \boldsymbol{1}_n\boldsymbol{K}\boldsymbol{1}_n$ (ここで $\boldsymbol{1}_n$ は全要素 $1/n$ の $n \times n$ 行列)。
- $\tilde{\boldsymbol{K}}$ の固有値分解 $\tilde{\boldsymbol{K}} \boldsymbol{\alpha}_k = n\lambda_k \boldsymbol{\alpha}_k$ を計算する。
- 第 $k$ 主成分への射影は $z_{ik} = \sum_j \alpha_{kj} k(\boldsymbol{x}_j, \boldsymbol{x}_i)$ で得る。
カーネル PCA は、特徴空間が高次元(または無限次元)でも有限の $n \times n$ 行列の固有値問題に帰着するため、計算可能です。ただし、サンプル数 $n$ が大きいと $\boldsymbol{K}$ が巨大になるので、近似手法(Nyström 近似など)が使われます。
カーネル PCA を使えば PCA を非線形に拡張できますが、それでも線形 PCA の基本構造が出発点です。次に、PCA を実際に Python で動かして、これまでの議論を実感に変えましょう。
Python でのスクラッチ実装と scikit-learn 利用
スクラッチ実装で「やっていること」を確認
まずは NumPy だけを使って、PCA を 1 から実装します。共分散行列の固有値分解、データの中心化、寄与率の計算という基本要素を踏まえます。
import numpy as np
class MyPCA:
def __init__(self, n_components):
self.n_components = n_components
def fit(self, X):
# 1) 平均ベクトルを保存し、データを中心化する
self.mean_ = X.mean(axis=0)
Xc = X - self.mean_
# 2) 標本共分散行列を計算 (d x d)
n = Xc.shape[0]
S = (Xc.T @ Xc) / n
# 3) 固有値分解 (実対称行列なので eigh が安定)
eigval, eigvec = np.linalg.eigh(S)
# 4) 固有値の大きい順にソート
order = np.argsort(eigval)[::-1]
self.eigenvalues_ = eigval[order]
self.components_ = eigvec[:, order].T # 行ベクトルとして保持
# 5) 寄与率を計算
total = self.eigenvalues_.sum()
self.explained_variance_ratio_ = self.eigenvalues_ / total
return self
def transform(self, X):
Xc = X - self.mean_
# n_components 個の主成分軸に射影
return Xc @ self.components_[: self.n_components].T
def fit_transform(self, X):
return self.fit(X).transform(X)
def inverse_transform(self, Z):
# 低次元表現から元の空間へ復元
return Z @ self.components_[: self.n_components] + self.mean_
このクラスでやっていることは、これまで導出してきた内容そのものです。fit で共分散行列 $\boldsymbol{S}$ の固有値分解を行い、上位 n_components 個の固有ベクトルを components_ に保存します。transform でデータを主成分の軸に正射影し、inverse_transform で元の次元に戻します(情報が失われた分は再現できないので、復元誤差が残ります)。
np.linalg.eigh を使っている点に注目してください。eigh は実対称行列に特化したルーチンで、eig よりも数値的に安定し、固有値が必ず実数として返ります。
scikit-learn と結果が一致するか確認
スクラッチ実装が正しいか、scikit-learn の PCA クラスと結果を比較します。
import numpy as np
from sklearn.decomposition import PCA
from sklearn.datasets import load_iris
iris = load_iris()
X = iris.data # (150, 4)
# 自作PCA
my_pca = MyPCA(n_components=2)
Z_my = my_pca.fit_transform(X)
# sklearn PCA
sk_pca = PCA(n_components=2)
Z_sk = sk_pca.fit_transform(X)
print("自作 寄与率:", my_pca.explained_variance_ratio_[:2])
print("sklearn 寄与率:", sk_pca.explained_variance_ratio_)
# 符号反転の可能性に注意して比較
print("射影スコアの一致 (符号差を許容):",
np.allclose(np.abs(Z_my), np.abs(Z_sk), atol=1e-6))
このコードを実行すると、寄与率は両者で完全に一致します。射影スコアも軸の符号反転を許容すれば一致します。sklearn 側は内部で SVD ベースの実装を使っているため、$n < d$ のケースでも自作版より安定しますが、結果は同じです。
scikit-learn での実用的な使い方
実務では scikit-learn の PCA を使うのが標準的です。最低限の使い方は次の通り。
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.datasets import load_iris
from sklearn.preprocessing import StandardScaler
from sklearn.decomposition import PCA
iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df["label"] = iris.target
# 標準化(PCAでは原則として必須)
X_std = StandardScaler().fit_transform(df[iris.feature_names].values)
# 累積寄与率 95% を超える成分数で自動決定
pca = PCA(n_components=0.95)
Z = pca.fit_transform(X_std)
print(f"選ばれた成分数: {pca.n_components_}")
print(f"各成分の寄与率: {pca.explained_variance_ratio_}")
print(f"累積寄与率: {pca.explained_variance_ratio_.cumsum()}")
PCA(n_components=0.95) のように小数を渡すと、累積寄与率がそのしきい値を超える最小の成分数が自動的に選ばれます。可視化目的なら整数(典型的には 2 または 3)を渡します。
標準化の効果を確認する
PCA は変数のスケールに敏感です。たとえば「身長 (m)」「体重 (kg)」「年収 (円)」を混ぜると、年収の桁が桁違いに大きいので分散もそれが圧倒的になり、PCA は事実上「年収軸」を第 1 主成分として選んでしまいます。

左のヒートマップは標準化なしのケース。PC1 は年収 $x_3$ に重みが集中し、他の変数の構造が捨てられています。右の標準化ありでは、3 変数が公平に各主成分に寄与しているのがわかります。実務では、変数の単位や桁が異なる場合は必ず StandardScaler で平均 0・分散 1 に揃えてから PCA を適用してください。
画像の主成分による復元
PCA は次元削減手段としてだけでなく、圧縮として使うこともできます。$d = 64$ 次元の digits データ(8×8 画像)に対し、上位 $k$ 主成分だけで画像を復元してみると、$k$ を増やすほど元画像に近づいていく様子が観察できます。

import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import load_digits
from sklearn.decomposition import PCA
digits = load_digits()
X = digits.data # (1797, 64)
pca_full = PCA(n_components=64).fit(X)
# 上位 k 主成分で復元した画像
ks = [1, 4, 8, 16, 32, 64]
fig, axes = plt.subplots(1, len(ks) + 1, figsize=(2 * (len(ks) + 1), 2))
ii = 7 # 適当な1枚
# 元画像
axes[0].imshow(X[ii].reshape(8, 8), cmap="gray_r")
axes[0].set_title("原画像"); axes[0].axis("off")
for ax, k in zip(axes[1:], ks):
pca = PCA(n_components=k).fit(X)
Z = pca.transform(X[ii:ii+1])
rec = pca.inverse_transform(Z)
cum = pca.explained_variance_ratio_.sum()
ax.imshow(rec.reshape(8, 8), cmap="gray_r")
ax.set_title(f"k={k}\n({cum*100:.0f}%)"); ax.axis("off")
plt.tight_layout(); plt.show()
$k = 1$ では「平均的な数字の影」のような画像にしかならず、$k = 4$ ではぼんやりと形が見え、$k = 16$ あたりでほとんど元画像と区別がつかなくなります。これは データの本質的な次元が 64 よりずっと小さい(たとえば 16 〜 32)ことを意味し、PCA で次元削減してもほとんど情報を失わないという事実を物語っています。
PCA と非線形手法(t-SNE / UMAP)の比較
PCA は線形なので、複雑な多様体構造を持つデータの可視化では限界があります。非線形な可視化手法 t-SNE(局所的近傍を保つ)や UMAP(位相的な構造を保つ)と比較すると違いが見えてきます。

PCA はデータの「大局構造」を線形に保ちますが、digits のような多クラスを綺麗に分離するのは苦手です。一方 t-SNE は局所的なクラスタを強く分離し、UMAP は大局構造と局所構造のバランスをとります。「次元削減して機械学習に渡す」場面では PCA、「可視化で集団構造を見たい」場面では t-SNE / UMAP という使い分けが定石です。ただし t-SNE / UMAP は距離の絶対的な意味を保たないので、得られたプロット上の距離・密度を量的に解釈することは避けるべきです。
実装で動かして PCA の威力を体感できたところで、最後に PCA の限界と「その先」について整理します。
PCA の限界と発展
PCA は強力で汎用性の高い手法ですが、万能ではありません。次のような制約があります。
- 線形性の仮定: PCA は線形変換だけを使うため、データが曲がった多様体に乗っている場合は本質的な構造を捉えられない。→ カーネル PCA、オートエンコーダ、t-SNE、UMAP などの非線形手法が必要。
- 分散 = 重要性の仮定: 「分散が大きい方向 = 情報量が多い方向」という仮定は、外れ値やノイズが大きい場合に崩れる。ノイズが偶然大きな分散を作り出し、PC1 に乗ってしまうリスクがある。→ ロバスト PCA、確率的 PCA。
- 解釈の難しさ: 主成分は元の変数の線形結合なので、「PC1 とは何か」を人間が解釈しづらいことがある。→ Sparse PCA、Factor Analysis でスパースな負荷を学習する。
- 教師なし: ラベル情報を一切使わないので、識別に有用な軸が必ずしも PC1 になるとは限らない。→ Fisher の線形判別分析(LDA)、PLS(部分最小二乗法)。
- 計算コスト: 単純な実装は $O(d^3)$ で固有値分解を行うため、$d$ が大きい(数千以上)と重い。→ Randomized PCA、Truncated SVD、Incremental PCA。
これらの拡張・代替手法は、それぞれが「PCA のどの仮定を緩めるか」という観点で派生しています。理論を深く理解するには、まず「PCA は何を仮定しているか」を押さえることが第一歩です。
そして現代的な視点では、オートエンコーダや Variational Autoencoder、Diffusion モデルが「データ多様体の本質的な次元を捉える」という同じ問いに取り組んでいると見ることができます。実は、線形オートエンコーダ(活性化関数なし)の最適解は PCA と一致することが知られています。深層学習の文脈で表現学習を学ぶときも、PCA を理解しているとずいぶん見通しがよくなります。
まとめ
本記事では、主成分分析を直感・線形代数・最適化・実装の 4 つの視点から解説しました。
- 直感: データが大きく広がっている方向を新しい軸に取り直す。広がりが小さい軸を捨てれば次元削減になる。
- 最適化: 「単位ベクトル方向への射影の分散を最大化せよ」というラグランジュ問題を解くと、$\boldsymbol{S}\boldsymbol{u} = \lambda\boldsymbol{u}$ という固有値方程式に帰着する。
- 線形代数: 主成分は共分散行列の固有ベクトル、固有値はその方向の分散。実装上は SVD が安定。
- 次元決定: 累積寄与率 90 % / 95 % を目安に、または scree plot のエルボーで選ぶ。
- 解釈: 主成分の負荷を読むと「どの変数が効いているか」がわかる。符号は不定。
- 拡張: カーネル PCA で非線形に拡張できる。t-SNE / UMAP は可視化、PCA は次元削減、と使い分ける。
- 実装: NumPy のスクラッチ実装も
scikit-learnのPCAも同じ結果を返す。標準化は基本的に必須。
PCA を理解すると、線形代数(固有値・特異値分解)、最適化(ラグランジュ未定乗数法)、統計学(共分散)、そして機械学習(次元削減・特徴抽出)がすべて 1 つの手法の中で交差していることが見えてきます。次のステップとして、以下の関連トピックも参考にしてください。
- カーネル法・カーネルトリックの解説
- scikit-learnでIrisデータセットを手軽に用意する
- k-means クラスタリング(PCA で次元を減らしたあとの後続処理に)
- オートエンコーダによる非線形次元削減

参考文献
本記事における参考文献・引用
- Jolliffe, I. T. (2002). Principal Component Analysis (2nd ed.). New York: Springer-Verlag.
- Bishop, C. M. (2006). Pattern Recognition and Machine Learning. Springer. — Chapter 12 (Continuous Latent Variables).
- Schölkopf, B., Smola, A., & Müller, K. R. (1998). Nonlinear Component Analysis as a Kernel Eigenvalue Problem. Neural Computation, 10(5).
- Hastie, T., Tibshirani, R., & Friedman, J. (2009). The Elements of Statistical Learning (2nd ed.). Springer.