読んでいる本の中に、主成分分析 (Principal Component Analysis; PCA) はデータを重み付き和に分解していると解釈することもできる、という記述があった。 なるほどーと思ったので、今回はそれについて試してみた。
使った環境は次のとおり。
$ sw_vers ProductName: Mac OS X ProductVersion: 10.15.7 BuildVersion: 19H15 $ python -V Python 3.8.6
下準備
下準備として、あらかじめ必要なパッケージをインストールしておく。
$ pip install scikit-learn matplotlib
使うデータセットを確認する
今回は Labeled Faces in the Wild データセットを用いる。 これは、著名人の顔画像を切り抜いたデータセットになっている。
以下のサンプルコードでは、データセットの先頭 10 件の画像をプロットしている。
#!/usr/bin/env python3 # -*- coding: utf-8 -*- from sklearn.datasets import fetch_lfw_people from matplotlib import pyplot as plt def main(): # Labeled Faces in the Wild データセット people = fetch_lfw_people(min_faces_per_person=20) # データセットの情報 print(f'dataset shape: {people.images.shape}') print(f'number of classes: {len(people.target_names)}') # 先頭の 10 件をグレイスケールで表示してみる fig, axes = plt.subplots(2, 5, figsize=(12, 6), subplot_kw={'xticks': (), 'yticks': ()}) mappings = zip(people.target, people.images, axes.ravel()) for target, image, ax in mappings: ax.imshow(image, cmap='gray') ax.set_title(people.target_names[target]) plt.show() if __name__ == '__main__': main()
上記を実行してみよう。
$ python lfw.py
すると、次のようなプロットが得られる。
主成分分析したときの主軸 (Principal Axes) を可視化する
一般的に、主成分分析では得られた主成分得点 (Principal Component Score) に着目することが多い。 一方で、データを主成分得点に変換するときに用いる主軸 (Principal Axes) の情報も得られる。 試しに、主軸を主成分ごとに可視化してみよう。
以下のサンプルコードでは、先頭の 10 主成分について主軸をプロットしている。
#!/usr/bin/env python3 # -*- coding: utf-8 -*- from sklearn.datasets import fetch_lfw_people from sklearn.decomposition import PCA from matplotlib import pyplot as plt def main(): people = fetch_lfw_people(min_faces_per_person=20) print(f'dataset shape: {people.images.shape}') print(f'number of classes: {len(people.target_names)}') x, y = people.data, people.target image_shape = people.images.shape[1:] # PCA で先頭の 100 成分を取り出す pca = PCA(n_components=100, random_state=42) x_pca = pca.fit_transform(x) print(f'transformed shape: {x_pca.shape}') print(f'principal axes shape: {pca.components_.shape}') # 主軸 (Principal Axes) を可視化する fig, axes = plt.subplots(2, 5, figsize=(12, 6), subplot_kw={'xticks': (), 'yticks': ()}) mappings = zip(pca.components_, axes.ravel()) for i, (component, ax) in enumerate(mappings): ax.imshow(component.reshape(image_shape)) ax.set_title(f'components: {i + 1}') plt.show() if __name__ == '__main__': main()
上記を実行してみよう。
$ python plotax.py dataset shape: (3023, 62, 47) number of classes: 62 transformed shape: (3023, 100) principal axes shape: (100, 2914)
すると、以下のようなプロットが得られる。
主成分得点と主軸から画像を再構成する
元のデータは、先ほど得られた主軸と主成分得点という二つの要素から再構成できる。 ここから、主成分分析はデータを主軸と主成分得点に分解していると解釈できる。
以下のサンプルコードでは使う主成分の数を変化させながら主成分得点と主軸を使って画像を再構成している。
#!/usr/bin/env python3 # -*- coding: utf-8 -*- from sklearn.datasets import fetch_lfw_people from sklearn.decomposition import PCA from matplotlib import pyplot as plt def main(): people = fetch_lfw_people(min_faces_per_person=20) print(f'dataset shape: {people.images.shape}') print(f'number of classes: {len(people.target_names)}') x, y = people.data, people.target image_shape = people.images.shape[1:] fig, axes = plt.subplots(5, 5, figsize=(12, 6), subplot_kw={'xticks': (), 'yticks': ()}) # 元画像をプロットする axes[0, 0].set_title('original image') for i in range(5): axes[i, 0].imshow(people.images[i], cmap='gray') # 利用する主成分の次元ごとに処理する for i, n_components in enumerate([10, 50, 100, 500], start=1): # 主成分得点に変換する pca = PCA(n_components=n_components, random_state=42) x_pca = pca.fit_transform(x) # 主成分得点を元の画像に逆変換する x_pca_reversed = pca.inverse_transform(x_pca) # 逆変換した画像をプロットする axes[0, i].set_title(f'{n_components} components') for j in range(5): ax = axes[j, i] ax.imshow(x_pca_reversed[j].reshape(image_shape), cmap='gray') plt.show() if __name__ == '__main__': main()
上記を実行してみよう。
$ python pcarev.py dataset shape: (3023, 62, 47) number of classes: 62
すると、以下のようなプロットが得られる。
再構成に使う主成分の数が増えるほど、だんだんと鮮明な画像が得られていることがわかる。 ちなみに、今回使ったデータセットでは 500 主成分を使った場合に累積寄与率が 99% を越えていた。
とはいえ、再構成するのに scikit-learn の API を使うだけだと面白くない。 なので、以下のサンプルコードでは scikit-learn の API を使わずに画像を再構成している。 具体的には主成分得点と主軸のドット積を取った上で、元データの平均を足せば良い。
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import numpy as np from sklearn.datasets import fetch_lfw_people from sklearn.decomposition import PCA from matplotlib import pyplot as plt def main(): people = fetch_lfw_people(min_faces_per_person=20) print(f'dataset shape: {people.images.shape}') print(f'number of classes: {len(people.target_names)}') x, y = people.data, people.target image_shape = people.images.shape[1:] fig, axes = plt.subplots(5, 5, figsize=(12, 6), subplot_kw={'xticks': (), 'yticks': ()}) axes[0, 0].set_title('original image') for i in range(5): axes[i, 0].imshow(people.images[i], cmap='gray') for i, n_components in enumerate([10, 50, 100, 500], start=1): pca = PCA(n_components=n_components, random_state=42) x_pca = pca.fit_transform(x) # inverse_transform() を使わずに逆変換してみる x_pca_reversed = np.dot(x_pca, pca.components_) + pca.mean_ # 逆変換した画像をプロットする axes[0, i].set_title(f'{n_components} components') for j in range(5): ax = axes[j, i] ax.imshow(x_pca_reversed[j].reshape(image_shape), cmap='gray') plt.show() if __name__ == '__main__': main()
上記を実行してみよう。
$ python pcarev2.py dataset shape: (3023, 62, 47) number of classes: 62
先ほどと同様のプロットが得られる。
いじょう。
Pythonではじめる機械学習 ―scikit-learnで学ぶ特徴量エンジニアリングと機械学習の基礎
- 作者:Andreas C. Muller,Sarah Guido
- 発売日: 2017/05/25
- メディア: 単行本(ソフトカバー)
- 作者:もみじあめ
- 発売日: 2020/02/29
- メディア: Kindle版