konchangakita

KPSを一番楽しんでいたブログ 会社の看板を背負いません 転載はご自由にどうぞ

Pythonで回帰モデルを作って(使って)みる

機械学習のためのPythonのお勉強として、回帰モデルを作ってみます
基礎の基礎ですが理解したつもりでも、使わないと覚えてないですからね( ゚Д゚)

pythonでの実装お勉強重視なので、細かいお作法の説明などは飛ばします

ゴール

機械学習モデルとしてPythonで回帰モデルとにかく作ってみる(使ってみる)

回帰モデルとは

教師あり学習に分類され、回帰分析を使って今までに取得したデータから未来の数値を予測しちゃいます
回帰分析には、線形と非線形回帰がありまして線形回帰モデル以外は非線形回帰モデルになります(そのまんま)

計算式で表すと
・線形回帰
y = Ax + B

多項式回帰非線形回帰)
y = Ax + Bx**2 + Cx***3 + D(3次元の場合)
多項式回帰は、表現力があがってトレーニングデータにフィットしやすいが、トレーニングデータへの過学習しやすいので注意が必要です

線形モデルではこの式でいうところのAとかBとかCとかの係数と切片の最適な数値を求めていくのです
係数とか切片とか高校生ぶり聞いたよ
そして、xとyは
x=説明変数:目的変数を求めるための数値データ
y=目的変数:予測したい数値
となります

文字ばっかりはしんどいのでちょっと絵にしてみました

f:id:konchangakita:20200105210731p:plain
図のセンスがほしい


まずお勉強のため、説明変数1コ目的変数1コで線形回帰と多項式回帰(非線形)を実装してみます

Pythonで回帰モデル練習してみよう

データセット

よく使われる機械学習入門でよく使われてる住宅価格予測データセットを使います

こんな感じのデータが入ってます

  1. id
  2. price 価格
  3. bedrooms ベットルームの数
  4. bathrooms お風呂の数
  5. sqft_living 家の面積
  6. sqft_lot 敷地面積
  7. floors 家の中に何フロアあるか
  8. waterfront 海岸[湖岸,川岸]の土地か否か
  9. view 眺めの良さ.
  10. condition どれくらい状態がよいか
  11. grade 「King County grading system」に基づいて、住宅部門に与えられた格付け
  12. y_built 建築された年
  13. yr_renovated 家が改装された年
csvの読み込み
import pandas as pd
df = pd.read_csv("house_train.csv")
df.shape
(2000, 11)

f:id:konchangakita:20200105004242p:plain
house_train.csvの中身

訓練(学習)用データとテスト用データに分割

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()

f:id:konchangakita:20200105022347p:plain
相関関係ヒートマップ
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()

f:id:konchangakita:20200105024356p:plain
線形回帰
赤い点が実際の価格で、青い直線が回帰分析した結果です
なんとなくできてそうですが、価格高くて面積が広いとなんかうまくいってないぽいです

多項式回帰の練習

では次は多項式回帰も追加してみます

#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

f:id:konchangakita:20200105030436p:plain
多項式回帰
なんだかサクッとできちゃいました
次数が上がった方がよりフィットしてきていますね

ポイントはここかな

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

欠損データは無しですね

他のデータを見直してみます

f:id:konchangakita:20200105004242p:plain
データの見直し
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()

f:id:konchangakita:20200105043517p:plain
線形回帰の予測(赤い線)実際の価格(赤い点)

とりあえず線形回帰だとこんなところかな
多項式回帰にすればもっとフィットしそうな気がします

感想

とりあえずコピペ合戦でここまできたけれども、このpythonで回帰問題って一回書いておけば前処理とパラメータ以外はほとんど触ることがなさそうなので、一回理解しておけば記憶してなくてもコピペ済ますことがでキガスル
(記憶力に自信がない)

さぁもっと他の機械学習を学ぼうか