【このページは現在作成中です】
データにおける入力X(特徴量)と出力y(正解データ)の関係f (y=f(X))を学習する。機械学習の最も代表的なアプローチ。回帰と分類の2タイプに大別される。
N個のデータとラベル (x, y) = { (x_1, y_1), (x_2, y_2), ..., (x_N, y_N) } = { (x_n, y_n) }_{n=1}^N とする。このとき、y_n = f(x_n ,w) + ε(ノイズ) として、N個のデータに対して、二乗誤差が最小となるf(x,w)とw*を求めたい(最小二乗法)。
但し、
ここで、f(x,w):モデル、w:パラメータ、f(x_n,w):モデル出力、y_n:目標値、正解データ
学習データセットX = { (x_n, y_n) }_{n=1}^Nに対し、以下の平均二乗誤差 (Mean Square Error, MSE)を最小化してモデルパラメータwを最適化
これはパラメータwの関数
テストデータセット tilde{X} = { (tilde{x}_n, tilde{y}_n) }_{n=1}^{tilde{N}}に対し、パラメータwを固定してモデルを評価
これはテストデータセットtilde{X}の関数 モデルの評価は、学習途中でも行うし、学習終了後にも行う
# PandasとNumPyをインポート import pandas as pd import numpy as np # 日本語化MatplotLib import matplotlib.pyplot as plt !pip install japanize-matplotlib import japanize_matplotlib # Seabornをインポート import seaborn as sns # Pickleをインポート import pickle
# 不動産データの取得 data = pd.read_csv("https://www2.cmds.kobe-u.ac.jp/~masa-n/dshandson/realestate-sample.csv")
data
# 表の形を表示する data.shape
# 各列の型を確認する data.dtypes
data.info()
# 生データをコピーしておく(いつでもやり直せるように) df = data.copy()
# 箱ひげ図 sns.boxplot(df[["坪単価"]])
# ヒストグラム sns.histplot(df["坪単価"])
# 相関行列 sns.heatmap(df.corr(), annot=True)
# 散布図 sns.scatterplot(df, x="駅距離", y="坪単価", hue="コンビニ数")
# 欠損値の確認 df.isnull().sum()
# 外れ値を見つける:各変数の要約統計量 df.describe()
# 外れ値を見つける:各変数の箱ひげ図 fig, axes= plt.subplots(nrows=2, ncols=4, tight_layout=True, squeeze=False) for i, col in enumerate(["取引日", "築年数", "駅距離", "コンビニ数", "緯度", "経度", "坪単価"]): sns.boxplot(df[[col]], ax=axes[i//4, i%4])
外れ値に対する処理は省略
# 重複行を見つける df[df.duplicated(keep=False)]
# 特徴量に指定する列名リスト features = df.columns[0:6] # 正解データに指定する列名 target = df.columns[6:7] # 特徴量 X = df[features] # 正解データ y = df[target] # X, yのそれぞれを訓練データとテストデータに分ける (訓練:テスト=8:2) from sklearn.model_selection import train_test_split X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=1234)
from sklearn.preprocessing import StandardScaler # 標準化のためのスケーラー sc = StandardScaler() # 訓練データにフィットさせる sc.fit(X_train) # 訓練データをスケール変換して、データフレームに入れなおす X_train_sc = pd.DataFrame(sc.transform(X_train), index=X_train.index,columns=X_train.columns) # テストデータもスケール変換する X_test_sc = pd.DataFrame(sc.transform(X_test), index=X_test.index,columns=X_test.columns)
教師あり学習の場合は、訓練データとテストデータを分割した後にスケーリングを行う
- データ分割前にスケーラーをfitさせてはならない(訓練データがテストデータに依存してしまう)
- 訓練データにスケーラーをfit、transformさせ、同じスケーラーでテストデータをtransformさせること
- 訓練データとテストデータで別々のスケーラを使ってはいけない
# 線形回帰モデルの選択 from sklearn import linear_model model_lr = linear_model.LinearRegression() # 線形回帰モデルの学習(標準化した訓練データを使う) model_lr.fit(X_train_sc, y_train)
# 線形回帰モデルのパラメータ m_exp = pd.DataFrame() m_exp.index = ["切片"] + model_lr.feature_names_in_.tolist() m_exp["重み"] = [model_lr.intercept_[0]] + model_lr.coef_[0].tolist() m_exp
# 実際にあっているかどうかを確認してみる y_eval = pd.DataFrame() y_eval["正解"] = y_test[target] y_eval["予測"] = model_lr.predict(X_test_sc) y_eval["誤差"] = (y_eval["正解"] - y_eval["予測"]) from sklearn.metrics import PredictionErrorDisplay, r2_score, mean_absolute_error, mean_squared_error disp = PredictionErrorDisplay(y_true = y_eval["正解"], y_pred= y_eval["予測"])
# 正解値と予測値を散布図にプロット disp.plot(kind="actual_vs_predicted")
# 残差と予測値を散布図にプロット disp.plot()
# 決定係数 r2 = r2_score(y_true = y_eval['正解'], y_pred= y_eval['予測']) print(f"決定係数:{r2}") # MAE(平均絶対誤差) mae = mean_absolute_error(y_true = y_eval['正解'], y_pred= y_eval['予測']) print(f"MAE(平均絶対誤差):{mae}") # MSE(平均二乗誤差) mse = mean_squared_error(y_true = y_eval['正解'], y_pred= y_eval['予測']) print(f"MSE(平均二乗誤差):{mse}") # RMSE(二乗平均平方根誤差) rmse = np.sqrt(mse) print(f"RMSE(二乗平均平方根誤差):{rmse}")
# 訓練データに対しても評価してみる y_eval_train = pd.DataFrame() y_eval_train["正解"] = y_train[target] y_eval_train["予測"] = model_lr.predict(X_train_sc) y_eval_train["誤差"] = (y_eval_train["正解"] - y_eval_train["予測"])
# 正解値と予測値を散布図にプロット(訓練データ、テストデータ両方とも) plt.xlabel("Predicted values") plt.ylabel("Actual values") plt.xlim(0.0, 120.0) plt.ylim(0.0, 120.0) plt.grid(True) plt.gca().set_aspect('equal', adjustable='box') plt.scatter(y_eval["予測"], y_eval["正解"], label="test", s=10, alpha=0.5, linewidths=1) plt.scatter(y_eval_train["予測"], y_eval_train["正解"], label="train", s=10, alpha=0.5, linewidths=1) plt.plot([0, y_test.max()], [0, y_test.max()], 'k--', lw=1) # y=x plt.legend() plt.show()
# 残差と予測値を散布図にプロット(訓練データ、テストデータ両方とも) plt.xlabel("Predicted values") plt.ylabel("Residuals (Actual - Predicted)") plt.xlim(0.0, 100.0) plt.ylim(-100.0, 100.0) plt.grid(True) plt.scatter(y_eval["予測"], y_eval["誤差"], label="test", s=10, alpha=0.5, linewidths=1) plt.scatter(y_eval_train["予測"], y_eval_train["誤差"], label="train", s=10, alpha=0.5, linewidths=1) plt.legend() plt.show()
# 学習誤差とテスト誤差の比較 # 決定係数(テストデータ) r2 = r2_score(y_true = y_eval['正解'], y_pred= y_eval['予測']) print(f"決定係数(テストデータ):{r2}") # 決定係数(訓練データ) r2_train = r2_score(y_true = y_eval_train['正解'], y_pred = y_eval_train['予測']) print(f"決定係数(訓練データ) :{r2_train}") # MAE(テストデータ) mae = mean_absolute_error(y_true = y_eval['正解'], y_pred= y_eval['予測']) print(f"MAE(テストデータ):{mae}") # MAE(訓練データ) mae_train = mean_absolute_error(y_true = y_eval_train['正解'], y_pred = y_eval_train['予測']) print(f"MAE(訓練データ) :{mae_train}")
# 学習曲線を作成する from sklearn.model_selection import learning_curve train_sizes, train_scores, test_scores = learning_curve( estimator=model_lr, X=X_train_sc, y=y_train, cv=10, scoring='r2', train_sizes=np.linspace(0.1, 1.0, 10), random_state=1234) train_scores_mean = np.mean(train_scores, axis=1) train_scores_std = np.std(train_scores, axis=1) test_scores_mean = np.mean(test_scores, axis=1) test_scores_std = np.std(test_scores, axis=1) plt.plot(train_sizes, train_scores_mean, 'o-', color='r', label='Training score') plt.plot(train_sizes, test_scores_mean, 'o-', color='g', label='Validation score') plt.fill_between(train_sizes, train_scores_mean - train_scores_std, train_scores_mean + train_scores_std, alpha=0.1, color='r') plt.fill_between(train_sizes, test_scores_mean - test_scores_std, test_scores_mean + test_scores_std, alpha=0.1, color='g') plt.grid() plt.title('Learning curve') plt.xlabel('Number of training examples') plt.ylabel('R2') plt.ylim(0.0, 1.0) plt.legend(loc='best') plt.show()
# ラッソ回帰モデル # モデルの選択 model_lasso = linear_model.Lasso(alpha=0.1) # モデルの学習(標準化した訓練データを使う) model_lasso.fit(X_train_sc, y_train) # 決定係数 # テストデータ r2_lasso = model_lasso.score(X_test_sc, y_test) print(f"決定係数(テストデータ)(ラッソ回帰):{r2_lasso}") # 訓練データ r2_train_lasso = model_lasso.score(X_train_sc, y_train) print(f"決定係数(訓練データ)(ラッソ回帰) :{r2_train_lasso}")
# リッジ回帰モデル # モデルの選択 model_ridge = linear_model.Ridge(alpha=0.1) # モデルの学習(標準化した訓練データを使う) model_ridge.fit(X_train_sc, y_train) # 決定係数 # テストデータ r2_ridge = model_ridge.score(X_test_sc, y_test) print(f"決定係数(テストデータ)(リッジ回帰):{r2_ridge}") # 訓練データ r2_train_ridge = model_ridge.score(X_train_sc, y_train) print(f"決定係数(訓練データ)(リッジ回帰) :{r2_train_ridge}")
# ElasticNetモデル # モデルの選択 model_elastic = linear_model.ElasticNet(alpha=0.1, l1_ratio=0.5) # モデルの学習(標準化した訓練データを使う) model_elastic.fit(X_train_sc, y_train) # 決定係数 # テストデータ r2_elastic = model_elastic.score(X_test_sc, y_test) print(f"決定係数(テストデータ)(ElasticNet):{r2_elastic}") # 訓練データ r2_train_elastic = model_elastic.score(X_train_sc, y_train) print(f"決定係数(訓練データ)(ElasticNet) :{r2_train_elastic}")
# 回帰木モデルの選択 from sklearn import tree model_dt = tree.DecisionTreeRegressor(max_depth=10, random_state=1234) #max_depthは木の深さの最大値 # 回帰木モデルの学習 model_dt.fit(X_train, y_train)
# 得られた木を可視化する(木が深いと描画に時間がかかる) plt.figure(figsize=(40, 20)) _ = tree.plot_tree(model_dt, fontsize=10, feature_names=X.columns)
# 誤差を確認する y_eval = pd.DataFrame() y_eval["正解"] = y_test[target] y_eval["予測"] = model_dt.predict(X_test) y_eval["誤差"] = (y_eval["正解"] - y_eval["予測"]) from sklearn.metrics import PredictionErrorDisplay, r2_score, mean_absolute_error, mean_squared_error disp = PredictionErrorDisplay(y_true = y_eval["正解"], y_pred= y_eval["予測"])
# 正解値と予測値を散布図にプロット disp.plot(kind="actual_vs_predicted")
# 残差と予測値を散布図にプロット disp.plot()
# 決定係数 r2 = r2_score(y_true = y_eval['正解'], y_pred= y_eval['予測']) print(f"決定係数:{r2}") # MAE(平均絶対誤差) mae = mean_absolute_error(y_true = y_eval['正解'], y_pred= y_eval['予測']) print(f"MAE(平均絶対誤差):{mae}") # MSE(平均二乗誤差) mse = mean_squared_error(y_true = y_eval['正解'], y_pred= y_eval['予測']) print(f"MSE(平均二乗誤差):{mse}") # RMSE(二乗平均平方根誤差) rmse = np.sqrt(mse) print(f"RMSE(二乗平均平方根誤差):{rmse}")
# 訓練データに対しても評価してみる y_eval_train = pd.DataFrame() y_eval_train["正解"] = y_train[target] y_eval_train["予測"] = model_dt.predict(X_train) y_eval_train["誤差"] = (y_eval_train["正解"] - y_eval_train["予測"])
# 正解値と予測値を散布図にプロット(訓練データ、テストデータ両方とも) plt.xlabel("Predicted values") plt.ylabel("Actual values") plt.xlim(0.0, 120.0) plt.ylim(0.0, 120.0) plt.grid(True) plt.gca().set_aspect('equal', adjustable='box') plt.scatter(y_eval["予測"], y_eval["正解"], label="test", s=10, alpha=0.5, linewidths=1) plt.scatter(y_eval_train["予測"], y_eval_train["正解"], label="train", s=10, alpha=0.5, linewidths=1) plt.plot([0, y_test.max()], [0, y_test.max()], 'k--', lw=1) # y=x plt.legend() plt.show()
# 残差と予測値を散布図にプロット(訓練データ、テストデータ両方とも) plt.xlabel("Predicted values") plt.ylabel("Residuals (Actual - Predicted)") plt.xlim(0.0, 100.0) plt.ylim(-100.0, 100.0) plt.grid(True) plt.scatter(y_eval["予測"], y_eval["誤差"], label="test", s=10, alpha=0.5, linewidths=1) plt.scatter(y_eval_train["予測"], y_eval_train["誤差"], label="train", s=10, alpha=0.5, linewidths=1) plt.legend() plt.show()
# 学習誤差とテスト誤差の比較 # 決定係数(テストデータ) r2 = r2_score(y_true = y_eval['正解'], y_pred= y_eval['予測']) print(f"決定係数(テストデータ):{r2}") # 決定係数(訓練データ) r2_train = r2_score(y_true = y_eval_train['正解'], y_pred = y_eval_train['予測']) print(f"決定係数(訓練データ) :{r2_train}") # MAE(テストデータ) mae = mean_absolute_error(y_true = y_eval['正解'], y_pred= y_eval['予測']) print(f"MAE(テストデータ):{mae}") # MAE(訓練データ) mae_train = mean_absolute_error(y_true = y_eval_train['正解'], y_pred = y_eval_train['予測']) print(f"MAE(訓練データ) :{mae_train}")
# 学習曲線を作成する from sklearn.model_selection import learning_curve train_sizes, train_scores, test_scores = learning_curve( estimator=model_dt, X=X_train, y=y_train, cv=10, scoring='r2', train_sizes=np.linspace(0.1, 1.0, 10), random_state=1234) train_scores_mean = np.mean(train_scores, axis=1) train_scores_std = np.std(train_scores, axis=1) test_scores_mean = np.mean(test_scores, axis=1) test_scores_std = np.std(test_scores, axis=1) plt.plot(train_sizes, train_scores_mean, 'o-', color='r', label='Training score') plt.plot(train_sizes, test_scores_mean, 'o-', color='g', label='Validation score') plt.fill_between(train_sizes, train_scores_mean - train_scores_std, train_scores_mean + train_scores_std, alpha=0.1, color='r') plt.fill_between(train_sizes, test_scores_mean - test_scores_std, test_scores_mean + test_scores_std, alpha=0.1, color='g') plt.grid() plt.title('Learning curve') plt.xlabel('Number of training examples') plt.ylabel('R2') plt.ylim(0.0, 1.0) plt.legend(loc='best') plt.show()
# 検証曲線を作成する(横軸はmax_depth) from sklearn.model_selection import validation_curve param_range = np.arange(1, 16) train_scores, test_scores = validation_curve( estimator=model_dt, X=X_train, y=y_train, cv=10, scoring='r2', param_name='max_depth', param_range=param_range) train_scores_mean = np.mean(train_scores, axis=1) train_scores_std = np.std(train_scores, axis=1) test_scores_mean = np.mean(test_scores, axis=1) test_scores_std = np.std(test_scores, axis=1) plt.plot(param_range, train_scores_mean, 'o-', color='r', label='Training score') plt.plot(param_range, test_scores_mean, 'o-', color='g', label='Validation score') plt.fill_between(param_range, train_scores_mean - train_scores_std, train_scores_mean + train_scores_std, alpha=0.1, color='r') plt.fill_between(param_range, test_scores_mean - test_scores_std, test_scores_mean + test_scores_std, alpha=0.1, color='g') plt.grid() plt.title('Validation curve') plt.xlabel('Parameter max_depth') plt.ylabel('R2') plt.ylim(0.0, 1.0) plt.legend(loc='best') plt.show()
# 特徴量重要度の確認 imp_value = pd.DataFrame({'feature':features, 'importance':model_dt.feature_importances_}) print(imp_value)
# 特徴量重要度のグラフ化 imp = model_dt.feature_importances_ label = X.columns indices = np.argsort(imp) fig=plt.figure(figsize=(4,4)) plt.barh(range(len(imp)), imp[indices]) plt.yticks(range(len(imp)), label[indices], fontsize=10) plt.xticks(fontsize=10) plt.ylabel("Feature", fontsize=12) plt.xlabel("Feature importance", fontsize=12)
# 上位2つの特徴量以外を削除 X_train_dropped = X_train.drop(columns=["コンビニ数", "取引日", "緯度", "築年数"]) X_test_dropped = X_test.drop(columns=["コンビニ数", "取引日", "緯度", "築年数"]) # 確認 X_train_dropped.info() X_test_dropped.info()
# 再評価 # モデルの学習(特徴量を選別した訓練データを使う) model_dt.fit(X_train_dropped, y_train) # 決定係数 # テストデータ r2 = model_dt.score(X_test_dropped, y_test) print(f"決定係数(テストデータ)(回帰木):{r2}") # 訓練データ r2_train = model_dt.score(X_train_dropped, y_train) print(f"決定係数(訓練データ)(回帰木) :{r2_train}")