Pythonで回帰モデルを作って(使って)みる
機械学習のためのPythonのお勉強として、回帰モデルを作ってみます
基礎の基礎ですが理解したつもりでも、使わないと覚えてないですからね( ゚Д゚)
pythonでの実装お勉強重視なので、細かいお作法の説明などは飛ばします
回帰モデルとは
教師あり学習に分類され、回帰分析を使って今までに取得したデータから未来の数値を予測しちゃいます
回帰分析には、線形と非線形回帰がありまして線形回帰モデル以外は非線形回帰モデルになります(そのまんま)
計算式で表すと
・線形回帰
y = Ax + B
・多項式回帰(非線形回帰)
y = Ax + Bx**2 + Cx***3 + D(3次元の場合)
※多項式回帰は、表現力があがってトレーニングデータにフィットしやすいが、トレーニングデータへの過学習しやすいので注意が必要です
線形モデルではこの式でいうところのAとかBとかCとかの係数と切片の最適な数値を求めていくのです
係数とか切片とか高校生ぶり聞いたよ
そして、xとyは
x=説明変数:目的変数を求めるための数値データ
y=目的変数:予測したい数値
となります
文字ばっかりはしんどいのでちょっと絵にしてみました
Pythonで回帰モデル練習してみよう
データセット
よく使われる機械学習入門でよく使われてる住宅価格予測データセットを使います
こんな感じのデータが入ってます
- id
- price 価格
- bedrooms ベットルームの数
- bathrooms お風呂の数
- sqft_living 家の面積
- sqft_lot 敷地面積
- floors 家の中に何フロアあるか
- waterfront 海岸[湖岸,川岸]の土地か否か
- view 眺めの良さ.
- condition どれくらい状態がよいか
- grade 「King County grading system」に基づいて、住宅部門に与えられた格付け
- y_built 建築された年
- yr_renovated 家が改装された年
csvの読み込み
import pandas as pd df = pd.read_csv("house_train.csv") df.shape (2000, 11)
訓練(学習)用データとテスト用データに分割
from sklearn.model_selection import train_test_split train_df, test_df = train_test_split(df) #75%を学習用データ、25%を検証用 train_df.shape (1500, 11)
練習用説明変数と目的変数の設定
予測したい目的変数をprice(価格)とした時によさげな説明変数をさがしてみます
import seaborn as sns import matplotlib.pyplot as plt train_df_corr = train_df.corr() sns.heatmap(train_df_corr, vmax=1, vmin=-1, center=0, annot=True) plt.tight_layout() plt.show()
sqft_living(家の面積)が良さげなので、とりあえずこれを使って実験です
線形回帰の練習
まずは線形回帰から、正則化にLasso回帰を使います
#線形回帰と非線形回帰 from sklearn.linear_model import Lasso from sklearn.preprocessing import PolynomialFeatures rgs1 = Lasso() #線形回帰 X_test = df["sqft_living"].values.reshape(-1,1) #説明変数を格納 y_test = df["price"] #目的変数を格納 rgs1.fit(X_test, y_test)#家の大きさ, 売却価格の関係を学習. y = rgs1.predict(np.arange(0,10000).reshape(10000,1)) plt.plot(y, label = "First-order equation") plt.xlabel("sqft_living" ,size = 15)#x軸のタイトル plt.ylabel("price", size=15)#y軸のタイトル plt.scatter(X_test, y_test, s=1, c ="r", label = "price_sqftliving") #実際の価格分布 plt.xlim(0, 10000) plt.legend() plt.show()#画面に出力 plt.clf()
赤い点が実際の価格で、青い直線が回帰分析した結果です
なんとなくできてそうですが、価格高くて面積が広いとなんかうまくいってないぽいです
多項式回帰の練習
では次は多項式回帰も追加してみます
#2次式の多項式回帰モデルの作成. quadratic_test = PolynomialFeatures(degree = 2) X_quadratic_test = quadratic_test.fit_transform(X_test) rgs2.fit(X_quadratic_test, y_test) a1, a2 = rgs2.coef_[1:]#係数を取得 b = rgs2.intercept_#切片を取得 X = np.arange(10000) y_quadratic = a1*X + a2*X**2 + b #3次式の多項式回帰モデルの作成. cubic_test = PolynomialFeatures(degree = 3) X_cubic_test = cubic_test.fit_transform(X_test) rgs3.fit(X_cubic_test, y_test) a1, a2, a3= rgs3.coef_[1:]#係数を取得 b = rgs3.intercept_#切片を取得 X = np.arange(10000) y_cubic = a1*X + a2*X**2 + a3*X**3 + b
なんだかサクッとできちゃいました
次数が上がった方がよりフィットしてきていますね
ポイントはここかな
quadratic_test = PolynomialFeatures(degree = 2) #多項式基底を作成
Pythonで回帰モデルの実装
では、ここから本番の複数の説明変数から目的変数を予測する回帰モデルを実装してみます
前処理
欠損処理、文字列データのダミー変数化、いらないデータの削除などを行って処理しやすいカタチへ変換します
まずは、欠損データの確認
df.isnull().sum() date 0 price 0 bedrooms 0 bathrooms 0 sqft_living 0 sqft_lot 0 floors 0 condition 0 grade 0 yr_built 0 yr_renovated 0 dtype: int64
欠損データは無しですね
他のデータを見直してみますIDはいらないので削除
del(df["id"])
次は、yr_renovated(家が改装された年)に注目です
df["yr_renovated"].value_counts() 0 14326 2014 71 2003 30 2005 28 2013 27 ... Name: yr_renovated, Length: 70, dtype: int64
この部分を最近改装された方が点数が高くなるように変換(格付け)してやります
max_renovated = np.max(df["yr_renovated"])#最新年数は2014年 # 5年ごとに格付け for i in range(1, 11): renovated_value = max_renovated - 5*i df["yr_renovated"] [df["yr_renovated"] > renovated_value ] = 10-i df["yr_renovated"] [df["yr_renovated"] > 1900] = 0
GridSearchCVをつかってパラメータを設定する
Lasso回帰におけるパラメータαを"0", "0.0001", "0.001", "0.01"で比較
from sklearn.model_selection import GridSearchCV #目的変数を正規化 def normalize(x): xmean = x.mean() xstd = np.std(x) print("平均値:{}、 標準偏差:{}".format(xmean, xstd)) zscore = (x-xmean)/xstd return zscore rgs = Lasso(normalize=True)#ハイパーパラメータ設定 X_house = df.iloc[:,1:]#price以外の説明変数を格納 y_house = normalize(df.iloc[:, 0])#目的変数を格納 params = {"alpha":[0, 0.0001, 0.001, 0.01],"random_state":[0] } gs_house = GridSearchCV(rgs, params, cv = 20 , n_jobs =-1,scoring= "neg_mean_squared_error") gs_house.fit(X_house, y_house)
計算結果
gs_house.cv_results_ {'mean_fit_time': array([0.58394495, 0.02234361, 0.01938235, 0.01740695]), 'std_fit_time': array([0.08013057, 0.0067094 , 0.00324884, 0.00612001]), 'mean_score_time': array([0.00149595, 0.00159466, 0.00197146, 0.00282371]), 'std_score_time': array([0.0007394 , 0.00050879, 0.00056194, 0.0033067 ]), 'param_alpha': masked_array(data=[0, 0.0001, 0.001, 0.01], mask=[False, False, False, False], fill_value='?', dtype=object), 'param_random_state': masked_array(data=[0, 0, 0, 0], mask=[False, False, False, False], fill_value='?', dtype=object), 'params': [{'alpha': 0, 'random_state': 0}, {'alpha': 0.0001, 'random_state': 0}, {'alpha': 0.001, 'random_state': 0}, {'alpha': 0.01, 'random_state': 0}], 'split0_test_score': array([-0.3156082 , -0.31196537, -0.36593591, -0.92801374]), 'split1_test_score': array([-0.50691827, -0.51413918, -0.62991457, -1.39517021]), 'split2_test_score': array([-0.28130773, -0.28014487, -0.35091699, -0.87565584]), 'split3_test_score': array([-0.33970167, -0.34486073, -0.4418372 , -1.10316337]), 'split4_test_score': array([-0.30199193, -0.30286138, -0.36128771, -0.87046335]), 'split5_test_score': array([-0.64322954, -0.65581046, -0.81226534, -1.78765605]), 'split6_test_score': array([-0.25634842, -0.25441057, -0.29564304, -0.6534209 ]), 'split7_test_score': array([-0.31992551, -0.3188683 , -0.35752385, -0.87117022]), 'split8_test_score': array([-0.26997994, -0.26910238, -0.33268867, -0.88780084]), 'split9_test_score': array([-0.53974845, -0.55336569, -0.70870277, -1.58779144]), 'split10_test_score': array([-0.31747036, -0.32618177, -0.43372206, -1.13224736]), 'split11_test_score': array([-0.34028025, -0.33408583, -0.3355078 , -0.69151974]), 'split12_test_score': array([-0.42783977, -0.43251042, -0.50674055, -1.12393257]), 'split13_test_score': array([-0.26840543, -0.27115054, -0.33471494, -0.76616733]), 'split14_test_score': array([-0.22736611, -0.22323425, -0.2451692 , -0.63893988]), 'split15_test_score': array([-0.35909256, -0.36233365, -0.43499646, -1.11426097]), 'split16_test_score': array([-0.32840737, -0.32984466, -0.38241267, -0.8789923 ]), 'split17_test_score': array([-0.27980876, -0.27917634, -0.31589882, -0.82329218]), 'split18_test_score': array([-0.38421611, -0.38266937, -0.45139289, -1.07005405]), 'split19_test_score': array([-0.24348397, -0.24084829, -0.28444973, -0.80385683]), 'mean_test_score': array([-0.34755652, -0.3493782 , -0.41908606, -1.00017846]), 'std_test_score': array([0.10435111, 0.10851461, 0.14219775, 0.29451512]), 'rank_test_score': array([1, 2, 3, 4])}
gs_house.best_params_ {'alpha': 0, 'random_state': 0}
どうやら、パラメータαは0が一番良いらしい
学習済みモデルを用いてテストデータを予測してみる
#学習時の平均値と標準偏差を指定 def normalize_test(x): xmean = 532513.975 xstd = 366713.522 zscore = (x - xmean) / xstd return zscore #テスト用でcsvの読み込み df_test = pd.read_csv("house_test.csv") #前処理は学習の時と同様に del(df_test["id"]) max_renovated = 2014 for i in range(1, 11): renovated_value = max_renovated - 5*i df_test["yr_renovated"] [df_test["yr_renovated"] > renovated_value ] = 10-i df_test["yr_renovated"] [df_test["yr_renovated"] > 1900] = 0 df_test = df_test.iloc[:,1:] #テスト用データの説明変数を格納 df_ans = normalize_test(df_test.iloc[:, 0]) #テスト用データの目的変数を格納 #学習済みモデル(線形モデル)を用いて値を予測. house_pred = gs_house.predict(X_test) #Xmeanとxstdを用いて価格を復元する. xmean = 532513.975 xstd = 366713.522 #復元しつつみにくいので両方10000で割る. house_pred1_acutual = (xstd * house_pred + xmean) /10000 house_ans_acutual= (xstd * y_house_ans+ xmean)/10000 #線形回帰の予測値と実際の値の比較. plt.scatter(house_pred1_acutual, house_ans_acutual, c = "b", s = 1) plt.plot(range(300), range(300), c = "r") plt.xlabel("predict value (10**4)", size = 15) plt.ylabel("actual value (10**4)", size = 15) plt.title("Linear regression") plt.xlim(-10, 400) plt.tight_layout() plt.show() plt.clf()
とりあえず線形回帰だとこんなところかな
多項式回帰にすればもっとフィットしそうな気がします