Act 26. Pythonで主成分分析を試す
はじめに
Act 01. AIで外国為替を自動売買するまでの道のりをベースに学習を進めて行く。
前回は主成分分析の概要について学習した。
なので今回は実際にpythonで主成分分析を使ったデータの次元削減を行う。
また、最後に次元削減後のデータを使って予測しているので、もしよかったら見てね。
データセットが必要になるため、お馴染みのpythonで提供されているデータセットを使用する。
今回は特徴量の数が多いワインのデータセット(load_wine
)を用いてコードを書いていく。
主成分分析(PCA)
結論
まずはいつも通りコードを載せておく。
from sklearn.datasets import load_wine
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
import pandas as pd
import matplotlib.pyplot as plt
# データセットの読み込み
wine = load_wine()
data = wine.data # 特徴量(13次元)
target = wine.target # クラスラベル
# 特徴量の標準化
scaler = StandardScaler()
data_scaled = scaler.fit_transform(data)
# PCAの適用
pca = PCA(n_components=3) # 主成分を3つに削減
data_pca = pca.fit_transform(data_scaled)
# 主成分の寄与率
explained_variance = pca.explained_variance_ratio_
print("各主成分の寄与率:", explained_variance)
print("累積寄与率:", explained_variance.cumsum())
# 主成分をデータフレームに変換(クラスラベル付き)
pca_df = pd.DataFrame(data_pca, columns=['PC1', 'PC2', 'PC3'])
pca_df['target'] = target
# 主成分1と2の散布図を可視化
plt.figure(figsize=(8, 6))
for t, label in zip([0, 1, 2], wine.target_names):
plt.scatter(
pca_df.loc[pca_df['target'] == t, 'PC1'],
pca_df.loc[pca_df['target'] == t, 'PC2'],
label=label
)
plt.xlabel('Principal Component 1')
plt.ylabel('Principal Component 2')
plt.title('PCA of Wine Dataset (PC1 vs PC2)')
plt.legend()
plt.show()
fig = plt.figure(figsize=(10, 7))
ax = fig.add_subplot(111, projection='3d')
for t, label in zip([0, 1, 2], wine.target_names):
ax.scatter(
pca_df.loc[pca_df['target'] == t, 'PC1'],
pca_df.loc[pca_df['target'] == t, 'PC2'],
pca_df.loc[pca_df['target'] == t, 'PC3'],
label=label
)
ax.set_xlabel('Principal Component 1')
ax.set_ylabel('Principal Component 2')
ax.set_zlabel('Principal Component 3')
ax.set_title('PCA of Wine Dataset (3D Plot)')
plt.legend()
plt.show()
出力は以下の通り。
まずはコンソールに以下が出力される。
各主成分の寄与率: [0.36198848 0.1920749 0.11123631]
累積寄与率: [0.36198848 0.55406338 0.66529969]
その後にプロットが表示される。
×でウィンドウを閉じると、さらに別のプロットが表示される。
コードの説明
ということでコードについて説明していく。
毎回説明している個所については割愛する。
データセット
まずはwine
データセットについて。
このデータセットでは、以下の特徴量が存在している。
-
Alcohol
: ワインに含まれるアルコールの量(%) -
Malic Acid
: りんご酸の量 -
Ash
: 灰分量(ワインを燃焼させた後に残るミネラル含有量) -
Alcalinity of Ash
: 灰分のアルカリ度 -
Magnesium
: マグネシウム含有量 -
Total Phenols
: 総フェノール量 -
Flavanoids
: フラボノイドの量 -
Nonflavanoid Phenols
: 非フラボノイドフェノールの量 -
Proanthocyanins
: プロアントシアニジン(色素化合物) -
Colour Intensity
: 色の濃度 -
Hue
: 色相(色の質) -
OD280/OD315 of diluted wines
: 280nmおよび315nmでの光学密度の比率 -
Proline
: プロリン(アミノ酸の一種)の含有量
何がどうとかは正直どうでも良いけど、一応こんな感じの特徴量があるよってことで。
続いてターゲットについて。
このデータセットでは、
確認してみたが、['class_0' 'class_1' 'class_2']
の
具体的に何かは分からない…。
まあ、とりあえずターゲットは
モデルの学習
まず初めに、特徴量それぞれの単位やサイズが異なるため、データの標準化を行う。
標準化はfrom sklearn.preprocessing import StandardScaler
でインポートしている通り、StandardScaler
クラスのfit_transform()
メソッドを使用する。
from sklearn.datasets import load_wine
from sklearn.preprocessing import StandardScaler
import pandas as pd
# データセットの読み込み
wine = load_wine()
data = wine.data # 特徴量(13次元)
target = wine.target # クラスラベル
# 特徴量の標準化
scaler = StandardScaler()
data_scaled = scaler.fit_transform(data)
# 標準化前のデータ
df = pd.DataFrame(data, columns=wine.feature_names)
print(df.head(10))
# 標準化後のデータ
df_scaled = pd.DataFrame(data_scaled, columns=wine.feature_names)
print(df_scaled.head(10))
出力は以下の通りとなる。
桁の異なる様々なデータが標準化により分析しやすい値となった。
alcohol malic_acid ash alcalinity_of_ash magnesium total_phenols flavanoids nonflavanoid_phenols proanthocyanins color_intensity hue od280/od315_of_diluted_wines proline
0 14.23 1.71 2.43 15.6 127.0 2.80 3.06 0.28 2.29 5.64 1.04 3.92 1065.0
1 13.20 1.78 2.14 11.2 100.0 2.65 2.76 0.26 1.28 4.38 1.05 3.40 1050.0
2 13.16 2.36 2.67 18.6 101.0 2.80 3.24 0.30 2.81 5.68 1.03 3.17 1185.0
3 14.37 1.95 2.50 16.8 113.0 3.85 3.49 0.24 2.18 7.80 0.86 3.45 1480.0
4 13.24 2.59 2.87 21.0 118.0 2.80 2.69 0.39 1.82 4.32 1.04 2.93 735.0
5 14.20 1.76 2.45 15.2 112.0 3.27 3.39 0.34 1.97 6.75 1.05 2.85 1450.0
6 14.39 1.87 2.45 14.6 96.0 2.50 2.52 0.30 1.98 5.25 1.02 3.58 1290.0
7 14.06 2.15 2.61 17.6 121.0 2.60 2.51 0.31 1.25 5.05 1.06 3.58 1295.0
8 14.83 1.64 2.17 14.0 97.0 2.80 2.98 0.29 1.98 5.20 1.08 2.85 1045.0
9 13.86 1.35 2.27 16.0 98.0 2.98 3.15 0.22 1.85 7.22 1.01 3.55 1045.0
alcohol malic_acid ash alcalinity_of_ash magnesium total_phenols ... nonflavanoid_phenols proanthocyanins color_intensity hue od280/od315_of_diluted_wines proline
0 1.518613 -0.562250 0.232053 -1.169593 1.913905 0.808997 ... -0.659563 1.224884 0.251717 0.362177 1.847920 1.013009
1 0.246290 -0.499413 -0.827996 -2.490847 0.018145 0.568648 ... -0.820719 -0.544721 -0.293321 0.406051 1.113449 0.965242
2 0.196879 0.021231 1.109334 -0.268738 0.088358 0.808997 ... -0.498407 2.135968 0.269020 0.318304 0.788587 1.395148
3 1.691550 -0.346811 0.487926 -0.809251 0.930918 2.491446 ... -0.981875 1.032155 1.186068 -0.427544 1.184071 2.334574
4 0.295700 0.227694 1.840403 0.451946 1.281985 0.808997 ... 0.226796 0.401404 -0.319276 0.362177 0.449601 -0.037874
5 1.481555 -0.517367 0.305159 -1.289707 0.860705 1.562093 ... -0.176095 0.664217 0.731870 0.406051 0.336606 2.239039
6 1.716255 -0.418624 0.305159 -1.469878 -0.262708 0.328298 ... -0.498407 0.681738 0.083015 0.274431 1.367689 1.729520
7 1.308617 -0.167278 0.890014 -0.569023 1.492625 0.488531 ... -0.417829 -0.597284 -0.003499 0.449924 1.367689 1.745442
8 2.259772 -0.625086 -0.718336 -1.650049 -0.192495 0.808997 ... -0.578985 0.681738 0.061386 0.537671 0.336606 0.949319
9 1.061565 -0.885409 -0.352802 -1.049479 -0.122282 1.097417 ... -1.143031 0.453967 0.935177 0.230557 1.325316 0.949319
余談だがStandardScaler
クラスにはfit()
メソッドとfit_transform()
メソッドが存在する。
fit()
メソッドは計算のみ(平均値や標準偏差など)を行い、fit_transform()
メソッドは、その計算結果をもとにデータの標準化を行うメソッドになる。
データセットを標準化したため、次に主成分分析(PCA)を行う。
前回の説明であんなに苦戦したのに、コードだとめちゃくちゃ簡単にできちゃうの凄いね…。
from sklearn.datasets import load_wine
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
import pandas as pd
# データセットの読み込み
wine = load_wine()
data = wine.data # 特徴量(13次元)
target = wine.target # クラスラベル
# 特徴量の標準化
scaler = StandardScaler()
data_scaled = scaler.fit_transform(data)
# PCAの適用
pca = PCA(n_components=3) # 主成分を3つに削減
data_pca = pca.fit_transform(data_scaled)
df = pd.DataFrame(data_pca)
print(df.head(10))
出力は以下の通りとなる。
PCA(n_components=3)
で主成分を
0 1 2
0 3.316751 1.443463 -0.165739
1 2.209465 -0.333393 -2.026457
2 2.516740 1.031151 0.982819
3 3.757066 2.756372 -0.176192
4 1.008908 0.869831 2.026688
5 3.050254 2.122401 -0.629396
6 2.449090 1.174850 -0.977095
7 2.059437 1.608963 0.146282
8 2.510874 0.918071 -1.770969
9 2.753628 0.789438 -0.984247
主成分分析による次元削減はこんな感じ。
今までの学習もあってか、そんなに難しくはない。
ということで次に進んでみる。
寄与率について
コードの内容を見ていると寄与率という言葉が出現する。
寄与率とは簡単に言うと、各主成分が全体の中でどれだけの変動の割合を占めるかを示すもので、値が大きいほど相対的に説明力が高い主成分であることを表す。
from sklearn.datasets import load_wine
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
import pandas as pd
import matplotlib.pyplot as plt
# データセットの読み込み
wine = load_wine()
data = wine.data # 特徴量(13次元)
target = wine.target # クラスラベル
# 特徴量の標準化
scaler = StandardScaler()
data_scaled = scaler.fit_transform(data)
# PCAの適用
pca = PCA(n_components=3) # 主成分を3つに削減
data_pca = pca.fit_transform(data_scaled)
# 主成分の寄与率
explained_variance = pca.explained_variance_ratio_
print("各主成分の寄与率:", explained_variance)
print("累積寄与率:", explained_variance.cumsum())
出力は以下の通り。
各主成分の寄与率: [0.36198848 0.1920749 0.11123631]
累積寄与率: [0.36198848 0.55406338 0.66529969]
-
各主成分の寄与率
配列の左( 番目)から第一主成分、第二主成分、第三主成分となる。0
第一主成分とは、PCA前のデータの情報を最も多く説明する成分 -
累積寄与率
各主成分の寄与率を累積していったもの。
つまり、
ちなみに主成分を
各主成分の寄与率: [0.36198848 0.1920749 0.11123631 0.0706903 0.06563294]
累積寄与率: [0.36198848 0.55406338 0.66529969 0.73598999 0.80162293]
一旦コードについての説明は以上。
matplotlib
については主成分分析と大きく関わるわけではないため割愛。
分析・予測
元のデータセットとPCAで次元削減した後のデータを使ってロジスティック回帰分析を行おうと思う。
以下のようなコードを作ってみた。
やっていることは単純で、元のデータ、3次元まで削減したデータ、5次元まで削減したデータを使ってロジスティック回帰分析を行っている。
from sklearn.datasets import load_wine
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report
from sklearn.model_selection import train_test_split
import pandas as pd
import numpy as np
# データセットの読み込み
wine = load_wine()
data = wine.data # 特徴量(13次元)
target = wine.target # クラスラベル
# 特徴量の標準化
scaler = StandardScaler()
data_scaled = scaler.fit_transform(data)
# PCAの適用(3次元)
pca_3 = PCA(n_components=3) # 主成分を3つに削減
data_pca_3 = pca_3.fit_transform(data_scaled)
# PCAの適用(5次元)
pca_5 = PCA(n_components=5) # 主成分を5つに削減
data_pca_5 = pca_5.fit_transform(data_scaled)
# 元のデータで分析・予測
print("元のデータ")
model = LogisticRegression()
X_train, X_test, y_train, y_test = train_test_split(data, target, test_size=0.2, random_state=2)
model.fit(X_train, y_train)
y_pred = model.predict(X_test)
report = classification_report(y_test, y_pred)
print(report)
# PCAで3次元まで削減したデータで分析・予測
print("3次元に削減したデータ")
model_3 = LogisticRegression()
X_train_3, X_test_3, y_train_3, y_test_3 = train_test_split(data_pca_3, target, test_size=0.2, random_state=2)
model_3.fit(X_train_3, y_train_3)
y_pred_3 = model_3.predict(X_test_3)
report_3 = classification_report(y_test_3, y_pred_3)
print(report_3)
# PCAで5次元まで削減したデータで分析・予測
print("5次元に削減したデータ")
model_5 = LogisticRegression()
X_train_5, X_test_5, y_train_5, y_test_5 = train_test_split(data_pca_5, target, test_size=0.2, random_state=2)
model_5.fit(X_train_5, y_train_5)
y_pred_5 = model_5.predict(X_test_5)
report_5 = classification_report(y_test_5, y_pred_5)
print(report_5)
出力は以下の通り。
元のデータ
/home/onishi/.pyenv/versions/3.12.7/lib/python3.12/site-packages/sklearn/linear_model/_logistic.py:469: ConvergenceWarning: lbfgs failed to converge (status=1):
STOP: TOTAL NO. of ITERATIONS REACHED LIMIT.
Increase the number of iterations (max_iter) or scale the data as shown in:
https://scikit-learn.org/stable/modules/preprocessing.html
Please also refer to the documentation for alternative solver options:
https://scikit-learn.org/stable/modules/linear_model.html#logistic-regression
n_iter_i = _check_optimize_result(
precision recall f1-score support
0 0.94 0.89 0.91 18
1 0.73 0.89 0.80 9
2 1.00 0.89 0.94 9
accuracy 0.89 36
macro avg 0.89 0.89 0.89 36
weighted avg 0.90 0.89 0.89 36
3次元に削減したデータ
precision recall f1-score support
0 1.00 0.83 0.91 18
1 0.75 1.00 0.86 9
2 1.00 1.00 1.00 9
accuracy 0.92 36
macro avg 0.92 0.94 0.92 36
weighted avg 0.94 0.92 0.92 36
5次元に削減したデータ
precision recall f1-score support
0 1.00 1.00 1.00 18
1 1.00 1.00 1.00 9
2 1.00 1.00 1.00 9
accuracy 1.00 36
macro avg 1.00 1.00 1.00 36
weighted avg 1.00 1.00 1.00 36
気になる点は3つ。
-
元データの時によく分からないログが出力されている。
こちらは最適化アルゴリズムの最大反復回数に引っかかった際に出るメッセージ。
デフォルトでは 回になっているため、それ以上に試行が必要だということ。100 ちなみに、最適化アルゴリズムとは、損失関数で求めた予測した値と実際の値の誤差を最小にするためにパラメータを調整することだったね。
-
次元に削減したデータでは、最大反復回数のログが出力されていない。3
特徴量が減ったからか試行回数が減り、デフォルトの 回でも問題なく学習が出来ている。100 -
次元に削減したデータの場合は正解率が5 になっている。100\%
これは驚いた。特徴量が多い方が情報がたくさんあるため、正解率が上がるのかなーと思っていたが、実際は必要な情報だけあれば正解率が上がるってことかな?(一概には言えないと思うけど…。)
この結果を得て、主成分分析による次元削減が如何に大事なのかということが分かった気がする。
さいごに
主成分分析、いい内容だったね。
特に最後の正解率が
学習の時間は短くなり、予測の精度も上がるなんて知ったからには、積極的に次元削減していきたくなる。
次回から強化学習について学ぶ。
正直ここからが本番みたいな感じがするから、気を抜かずにしっかりと学んでいこう!
ただ、追求しすぎて挫折しないようにだけは気を付けたい。
あー、めっちゃ楽しみ!
ではまた!
Discussion