第7回:教師なし学習・次元削減,機械学習の実践的アプローチ

7.1 教師なし学習・次元削減【中村】

次元削減 (dimensionality reduction) とは

4_unsupervised_learning.png
図1:教師あり学習と教師なし学習のイメージ(再掲)

なぜ次元を削減するのか?

次元削減の手法の例

【例題】5科目のテスト結果の分析

準備

  1. Google Colabを開き,新規ノートブックを作成
  2. ノートブックの名前 Untitled.ipynb を exam-pca.ipynb に変更する
#準備(すべてに共通)
# PandasとNumpyをインポート
import pandas as pd
import numpy as np

# 日本語化Matplotlibもインポート
import matplotlib.pyplot as plt
#↓の1行は提出時にはコメントアウトしてください
!pip install japanize-matplotlib
import japanize_matplotlib

# Seabornもインポート
import seaborn as sns

# pickleをインポート(モデルの保存用)
import pickle

#データフレームをきれいに表示するメソッド
from IPython.display import display

#標準化はよく使うのでインポート
from sklearn.preprocessing import StandardScaler
#データの取り込み
data = pd.read_csv("https://www2.cmds.kobe-u.ac.jp/~masa-n/dshandson/exam-pca.csv", index_col="受験番号")

データを眺める

display(data)
df = data.copy()

#要約統計量
display(df.describe())

#ヴァイオリン・プロット
sns.violinplot(df)
plt.show()

#相関係数
sns.heatmap(df.corr(), annot=True)

課題

#5科目の合計を計算
total = pd.DataFrame(df.sum(axis=1), columns=["合計"])
display(total)
#横棒グラフに可視化する
total.sort_values("合計").plot.barh(title="5科目の合計", figsize=(8,16))
#積み上げ棒グラフで表示する(オプショナル)
#df.loc[total.sort_values("合計").index].plot.barh(stacked=True, figsize=(8,16))
7_exam_total.png
図2: 5科目の得点を合計点で比較・説明する

情報量の減少

#66番と19番の得点を表示
display(df.loc[[66,19],:])
display(total.loc[[66,19]])

主成分分析 (PCA)

新しい軸をデータがなるべく散らばるように決める

新しい軸 = w1*国語 + w2*数学 + w3*英語 + w4*物理 + w5*化学
7_exam_variance.png
図3: 新しい軸でデータを比較する

PCAの考え方

用語

sklearn.decomposition.PCA

from sklearn.decomposition import PCA
pca = PCA(ハイパーパラメータ)
 
#標準化されたデータフレームで学習させる
pca.fit(df_sc)

#主成分のラベル (データフレームの列・行に名前を付ける際に使う)
labels = [f"第{i+1}主成分" for i in range(pca.n_components_)]

#主成分得点に変換.データフレームに入れなおす
df_pca = pd.DataFrame(pca.transform(df_sc), index=df_sc, columns=labels)
#主成分得点を表示
print("【主成分得点】")
display(df_pca)
 
#主成分負荷量
df_comp = pd.DataFrame(pca.components_, index=labels, columns=df_sc.columns)
#主成分負荷量を表示
print("【主成分負荷量】")
display(df_comp)

#分散,寄与率
df_var = pd.DataFrame(pca.explained_variance_, index=labels, columns=["分散"])
df_var["寄与率"] = pca.explained_variance_ratio_
df_var["累積寄与率"] = pca.explained_variance_ratio_.cumsum()
print("【分散・寄与率】")
display(df_var)

5科目テストのデータにPCAを適用

#まずは標準化
sc = StandardScaler()
sc.fit(df)
df_sc = pd.DataFrame(sc.transform(df), index=df.index, columns=df.columns)
#主成分分析を行う.次元数はデフォルトで元データの次元数(5)になる
from sklearn.decomposition import PCA
pca = PCA()
pca.fit(df_sc)

#主成分のラベル (データフレームの列・行に名前を付ける際に使う)
labels = [f"第{i+1}主成分" for i in range(pca.n_components_)]

#主成分得点に変換.データフレームに入れなおす
df_pca = pd.DataFrame(pca.transform(df_sc), index=df_sc.index, columns=labels)
#主成分得点を表示
print("【主成分得点】")
display(df_pca)

#主成分負荷量
df_comp = pd.DataFrame(pca.components_, index=labels, columns=df_sc.columns)
#主成分負荷量を表示
print("【主成分負荷量】")
display(df_comp) 

#分散,寄与率
df_var = pd.DataFrame(pca.explained_variance_, index=labels, columns=["分散"])
df_var["寄与率"] = pca.explained_variance_ratio_
df_var["累積寄与率"] = pca.explained_variance_ratio_.cumsum()
print("【分散・寄与率】")
display(df_var)
7_exam_pca_results.png
図4: 5科目テストのPCA適用結果
#第1,第2主成分のみを取り出す → 次元を2に削減
df_dim = df_pca.iloc[:,[0,1]]
display(df_dim)

#散布図を描く
ax = sns.scatterplot(df_dim, x="第1主成分", y="第2主成分")
#データフレームの各行を取り出し,各ポイントに受験番号(インデクス)をつける
for idx, row in df_dim.iterrows():
  ax.text(row["第1主成分"], row["第2主成分"], idx)

#細かい装飾
ax.grid()
ax.axvline(x=0, c="red")
ax.axhline(y=0, c="blue")
7_exam_pca_scatter.png~
図5: 5科目テストの成績を2次元で可視化する

7.2 特徴量エンジニアリング【中村】

特徴量を開発する (feature engineering)

アプローチ

【例題】製品の売上予測

【売上データ】

7_yogurt_data.png
図6:特徴量が少ないデータセット

【問題】

→ 特徴量エンジニアリングによって,新しい特徴量を作る

準備

#準備(すべてに共通)
# PandasとNumpyをインポート
import pandas as pd
import numpy as np

# 日本語化Matplotlibもインポート
import matplotlib.pyplot as plt
#↓の1行は提出時にはコメントアウトしてください
!pip install japanize-matplotlib
import japanize_matplotlib

# Seabornもインポート
import seaborn as sns

# pickleをインポート(モデルの保存用)
import pickle

# pandasのデータフレームを表示する関数
from IPython.display import display

#データをロードする(エクセルデータの読み込み)
data = pd.read_excel("https://www2.cmds.kobe-u.ac.jp/~masa-n/dshandson/yogurt.xlsx")
data

データを眺める

#型チェック
data.dtypes
#整形
df = data.copy()
#売上日付をインデクスに
df = df.set_index("売上日付")
df
#売上個数を可視化
#箱ひげ図
df["売上数"].plot.box()
#時系列
df["売上数"].plot()
#月別に可視化
for y in [2021, 2022]: 
  for i in range(1,13):
    df[(df.index.year==y)&(df.index.month==i)]["売上数"].plot.bar(title=f"{y}年{i}月", figsize=(8,6))
    plt.show()

ドメイン知識による特徴量開発

ヨーグルトAの売上数に関係ありそうなものは何か?

df["曜日"] = df.index.day_of_week
df["日"] = df.index.day
df["月"] = df.index.month
df["単価"] = df["売上額"] / df["売上数"]
n=3
for i in range(1, n+1):
   #df["列"].shift(i)で,列を下にi桁ずらすことができる
   df[f"{i}日前売上数"] = df["売上数"].shift(i)

df
7_yogurt_features.png
図7: 追加された特徴量
#天気のデータを拾ってくる.インデクスを日付に
data_weather = pd.read_csv("どこかの天気のデータ.csv")
df_w = data_weather.set_index("日付")
#売上データとマージする (参考:Python基礎演習6.2)
df_merged = pd.merge(df, df_w, left_index=True, right_index=True)

機械的に作る

sklearn.preprocessing.PolynomialFeatures

多項式を使って,特徴量を作成するライブラリ

from sklearn.preprocessing import PolynomialFeatures
poly = PolynomialFeatures(ハイパーパラメータ)

#データフレームをフィットさせる
poly.fit(df)  #dfは特徴量作成の元になる列を含んだデータフレーム

#データフレームを変換して,データフレームに入れなおす
df_poly = pd.DataFrame(poly.transform(df), index=df.index, columns=poly_features_names_out())

#確認する
df_poly
#データフレームを適当に作る
df_sample = pd.DataFrame(data={"a":[1,2,3,4,5], "b":[60,70,80,90,100]})
df_sample
#多項式特徴量を作成する
from sklearn.preprocessing import PolynomialFeatures

poly=PolynomialFeatures(degree=3,include_bias=False)
poly.fit(df_sample)
df_poly = pd.DataFrame(poly.transform(df_sample), index=df_sample.index,
                       columns=poly.get_feature_names_out())
df_poly
7_polynomial_features.png
図8: 多項式特徴量の作成

7.3 モデルのチューニング【伊藤】

訓練データ,検証データ,テストデータの分割

# X, yのそれぞれを訓練データ、検証データ、テストデータに分ける
from sklearn.model_selection import train_test_split
# まず、訓練・検証データとテストデータに分ける (訓練・検証:テスト=8:2)
X_train_val, X_test, y_train_val, y_test = train_test_split(X, y, test_size=0.2, random_state=1234)
# さらに、訓練データと検証データに分ける (訓練:検証=7:3)
X_train, X_val, y_train, y_val = train_test_split(X_train_val, y_train_val, test_size=0.3, random_state=1234)

クロスバリデーション

予測性能評価

K分割交差検証

# KFoldの処理で分割時の条件を指定
from sklearn.model_selection import KFold
kf = KFold(n_splits = 3, shuffle = True, random_state = 0)
# cross_validate関数で交差検証を行う
from sklearn.model_selection import cross_validate
# 線形回帰モデルの選択
from sklearn import linear_model
model = linear_model.LinearRegression()
result = cross_validate(model, X, y, cv = kf, scoring = 'r2', return_train_score = True)
print(result)
#平均値を計算する
sum(result['test_score']) / len(result['test_score'])

チューニングのためのK分割交差検証

グリッドサーチ

# GridSearchCVクラスのインポート
from sklearn.model_selection import GridSearchCV
# 学習に使用するアルゴリズムの定義
estimator = tree.DecisionTreeRegressor(random_state=0)
# 探索するハイパーパラメータと範囲の定義
param_grid = [{
   'max_depth': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
   'min_samples_split': [2, 10, 20]
}]
# データセット分割数を定義
cv = 5
# GridSearchCVクラスを用いたモデルの定義
tuned_dt = GridSearchCV(estimator=estimator,
                          param_grid=param_grid,
                          cv=cv, return_train_score=False)
# モデルの学習・検証
tuned_dt.fit(X_train_val, y_train_val)
# 検証結果の確認
pd.DataFrame(tuned_dt.cv_results_).T
# 最も予測精度の高かったハイパーパラメータの確認
tuned_dt.best_params_
# 最も予測精度の高かったモデルの引き継ぎ
best_dt = tuned_dt.best_estimator_
# モデルの検証
print('train_val score : ', best_dt.score(X_train_val, y_train_val))
print('test score : ', best_dt.score(X_test, y_test))

最適化問題

最適化問題と機械学習

7.4 アプリケーション【陳】

作成したモデルをAPI化する

訓練済みモデルの利用


トップ   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS