ギャン・バギャム・ソルドン

一打粉砕に怒喝の心力を込め、万物を叩き割る剛剣の刃を生み出さん

今日の頑張り

・今日の頑張り見て

SIGNATEの練習問題をやってたら一日経ってた。下のリンクから見れる。

【練習問題】民泊サービスの宿泊価格予測 | SIGNATE - Data Science Competition

1. MINPAKU.py

データの確認やクレンジング(欠損値の保管や、データのダミー変数化)、変数の返還方針の決定、変換したデータの吐き出し、変数同士の相関をヒートマップで見てる。

import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np

# CSV の読み込み
data = pd.read_csv('C:\\work\\SIGNATE\\TRAIN_MINPAKU\\train.csv')

# 欠損地が何個あるか確認
# print(data.isnull().sum())
'''''
・欠損のあるデータと補完の仕方
bathrooms:風呂の数 平均
bedrooms:ベッドルーム数  平均
beds:ベッド数   平均
first_review:最初のレビュー日 x
host_has_profile_pic: ホストの写真があるかどうか    f埋め
host_identity_verified:ホストの身元確認が取れているか  f埋め
host_response_rate:ホストの返信率 平均
host_since:ホストの登録日 2020/1/1
last_review:最後のレビュー日 2020/1/1
neighbourhood:近隣情報 x
review_scores_rating:レビュースコア  平均
thumbnail_url:サムネイル画像リンク x
zipcode:郵便番号 x

・特徴量の選択、加工
1. 削除カラム
id ,description ,name ,neiborhood , thumbnail_url ,zipcode
latitude ,longitude

2. 加工カラム
amenities ⇒ 文字データからアメニティの個数に加工、その後amenities drop
bed_type ⇒ ダミー変数へ加工しdrop
cancellation_policy ⇒ ダミー変数へ加工しdrop
city ⇒ ダミー変数へ加工しdrop
cleaning_fee ⇒ダミー変数へ加工しdrop

last_review - host_since ⇒ 営業日数として扱う
その後 first_review,last_review,host_since drop

host_has_profile_pic ⇒ダミー変数へ加工しdrop
host_identity_verified ⇒ダミー変数へ加工しdrop
instant_bookable ⇒ダミー変数にしてdrop
property_type ⇒ダミー変数にしてdrop
room_type ⇒ ダミー変数にしてdrop

'''
# 欠損値を f で置換
data = data.fillna({'host_has_profile_pic':'f' ,
'host_identity_verified':'f' })
# ホストからの返信率は文字置換して数字型へ
data['host_response_rate'] = data['host_response_rate'].str.replace('%','')
data['host_response_rate'] = data['host_response_rate'].astype(float)
# 欠損値を 平均値 で置換
data = data.fillna({'bathrooms':data['bathrooms'].mean()
,'bedrooms':data['bedrooms'].mean()
,'beds':data['beds'].mean()
,'host_response_rate':data['host_response_rate'].mean()
,'review_scores_rating':data['review_scores_rating'].mean()})

# last_review - host_since を計算
# 日付のデータを - で分割
df_last = data['last_review'].str.split('-',expand=True)
df_since = data['host_since'].str.split('-',expand=True)
# 欠損部分を平均で置換
df_last = df_last.fillna(df_last.astype(float).mean())
df_since = df_since.fillna(df_since.astype(float).mean())
# period を営業している期間の長さとして定義
data = data.assign(period=365*(df_last[0].astype(float)-df_since[0].astype(float))
+ 30*(df_last[1].astype(float)-df_since[1].astype(float))
+ (df_last[2].astype(float)-df_since[2].astype(float)) )
# アメニティの文字データをアメニティの個数データに変換
df_amenities = data['amenities'].str.split(',',expand=True)
data = data.assign(amenities=len(df_amenities.columns) - df_amenities.isnull().sum(axis=1))
# 文字型の変数をダミー変数へ
data = pd.get_dummies(data,drop_first=True, columns=['bed_type','cancellation_policy','city','cleaning_fee'
,'host_has_profile_pic','host_identity_verified','instant_bookable','room_type'])
# 不要なカラムをdrop
data = data.drop(['property_type','last_review','host_since','id','description' , 'first_review','name', 'neighbourhood', 'thumbnail_url' ,'zipcode','latitude' ,'longitude'] ,axis=1)
# 全て1のカラムを追加
data = data.assign(seppen=1)

# 欠損地があるか再確認
# print(data.isnull().sum())

data.to_csv('train_trance.csv')
# ---------------------- 変換ここまで ------------------------------------------------
# ---------------------- 可視化 ------------------------------------------------------
# ダミー変数を除く変数でヒートマップを描いてみる
cols = ['accommodates','amenities','bathrooms','bedrooms','beds','host_response_rate',
'number_of_reviews','review_scores_rating','y','period']
cm = np.corrcoef(data[cols].values.T)
heat_map = sns.heatmap(cm
,cbar=True
,annot=True
,square=True
,fmt='.2f'
,yticklabels=cols
,xticklabels=cols)
plt.tight_layout()
plt.show()

>>出力

f:id:kiui_4:20200711182357p:plain

ヒートマップ

 

2. MINPAKU_LinerRegiression.py

回帰に使う変数や関数をクラスを定義して、線形単回帰と重回帰でパラメータを推定。テストデータで平均偏差を求めて、モデルの評価としてる。最後にSIGNATEのモデル評価用のデータを加工して、予測値を算出してcsvに吐いてる

import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt

# 加工したcsv を読み込み
df = pd.read_csv('train_trance.csv')

# ----------------------- 回帰の為のクラスを定義 ----------------------
class liner_Regression(object):
# 学習率とパラメータの更新回数を定義
def __init__(self,eta=0.00000005 ,n_iter=5000):
self.eta = eta
self.n_iter = n_iter

# パラメータを推定する fit メソッドを定義
def fit(self,X,y):
# パラメータ w を初期化
self.w = np.random.random(len(X.columns))
# 更新規則に則ってパラメータを更新
for i in range(self.n_iter):
# 推定値と実測値の誤差を計算
self.error = y - self.predict(X)
# j 番目のパラメータを推定
for j in range(len(X.columns)):
self.w[j] += self.eta * np.dot(X.iloc[:,j], self.error)
return self
# 入力に対して予測値を計算する関数を定義
def predict(self, X):
return np.dot(X, self.w)

# 平均2乗誤差を求める関数を定義
def error_ave_test(self,X,y):
self.errors = y - self.predict(X)
return np.sqrt(0.5 * (self.errors*self.errors).sum()/len(X))
# ----------------------- クラスの定義ここまで ------------------------------------------
# 学習データとテストデータへ分割
df_train, df_test = train_test_split(df,test_size=0.4)

# ----------------------------------------- 単回帰モデルの実装 ---------------------------------------
# 単回帰なので accommodates を説明変数にし、切片を求めるためにseppen を抽出
data_train = df_train[['accommodates','seppen']]
# 目的変数に y を設定
target_train = df_train['y']

# 回帰クラスをインスタンス
linerReg = liner_Regression()
# fit メソッドを呼び出して、学習データにfitするパラメータを推定。
linerReg.fit(data_train,target_train)

# モデルの性能を評価
error = linerReg.error_ave_test(data_train,target_train)
print('単回帰のトレーニン2乗誤差:',error)

# テストデータで2乗誤差を推定
data_test = df_test[['accommodates','seppen']]
target_test = df_test['y']

test_error = linerReg.error_ave_test(data_test,target_test)
print('単回帰のテスト2乗誤差   :',test_error)

# ------------------------------ 重回帰モデルの推定 ----------------------------------------------------
# カラムを指定して説明変数を用意
colmuns = ['accommodates','amenities','bathrooms','bedrooms','beds','bed_type_Couch','bed_type_Futon','bed_type_Pull-out Sofa','bed_type_Real Bed','cancellation_policy_moderate','cancellation_policy_strict','cancellation_policy_super_strict_30','cancellation_policy_super_strict_60','city_Chicago','city_DC','city_LA','city_NYC','city_SF','cleaning_fee_t','host_has_profile_pic_t','host_identity_verified_t','instant_bookable_t','room_type_Private room','room_type_Shared room','seppen']
data_train = df_train[colmuns]
# 目的変数に y を設定
target_train = df_train['y']

# 重回帰クラスをインスタンス
Multiple_regression = liner_Regression()
# fit メソッドを呼び出して、学習データにfitするパラメータを推定。
Multiple_regression.fit(data_train,target_train)
# モデルの性能を評価
error = Multiple_regression.error_ave_test(data_train,target_train)
print('重回帰のトレーニン2乗誤差:',error)

# テストデータで2乗誤差を推定
data_test = df_test[colmuns]
target_test = df_test['y']

test_error = Multiple_regression.error_ave_test(data_test,target_test)
print('重回帰のテスト2乗誤差   :',test_error)

# ----------------------- 予測した様子を可視化 ----------------------------------------------
plt.scatter(target_test, Multiple_regression.predict(data_test))
plt.xlabel('correct')
plt.ylabel('predict')
plt.show()

# ------------------------------- テストデータから予測値を計算 -----------------------------------------------
# CSV の読み込み
data = pd.read_csv('C:\\work\\SIGNATE\\TRAIN_MINPAKU\\test.csv')

# 欠損値を f で置換
data = data.fillna({'host_has_profile_pic':'f' ,
'host_identity_verified':'f' })
# ホストからの返信率は文字置換して数字型へ
data['host_response_rate'] = data['host_response_rate'].str.replace('%','')
data['host_response_rate'] = data['host_response_rate'].astype(float)
# 欠損値を 平均値 で置換
data = data.fillna({'bathrooms':data['bathrooms'].mean()
,'bedrooms':data['bedrooms'].mean()
,'beds':data['beds'].mean()
,'host_response_rate':data['host_response_rate'].mean()
,'review_scores_rating':data['review_scores_rating'].mean()})

# last_review - host_since を計算
# 日付のデータを - で分割
df_last = data['last_review'].str.split('-',expand=True)
df_since = data['host_since'].str.split('-',expand=True)
# 欠損部分を平均で置換
df_last = df_last.fillna(df_last.astype(float).mean())
df_since = df_since.fillna(df_since.astype(float).mean())
# period を営業している期間の長さとして定義
data = data.assign(period=365*(df_last[0].astype(float)-df_since[0].astype(float))
+ 30*(df_last[1].astype(float)-df_since[1].astype(float))
+ (df_last[2].astype(float)-df_since[2].astype(float)) )
# アメニティの文字データをアメニティの個数データに変換
df_amenities = data['amenities'].str.split(',',expand=True)
data = data.assign(amenities=len(df_amenities.columns) - df_amenities.isnull().sum(axis=1))
# 文字型の変数をダミー変数へ
data = pd.get_dummies(data,drop_first=True, columns=['bed_type','cancellation_policy','city','cleaning_fee'
,'host_has_profile_pic','host_identity_verified','instant_bookable','property_type','room_type'])
# 不要なカラムをdrop
data = data.drop(['last_review','host_since','id','description' , 'first_review','name', 'neighbourhood', 'thumbnail_url' ,'zipcode','latitude' ,'longitude'] ,axis=1)
# 全て1のカラムを追加
data = data.assign(seppen=1)

# データから予測値を計算
data = data[colmuns]
predicts = Multiple_regression.predict(data)
predicts = pd.DataFrame(predicts)
predicts.to_csv('predicts.csv',header=None)


# 参考 -----------------------正規方程式を使った回帰パラメータの推定 ------------------------------
def normal_equation(X,y):
param = np.linalg.pinv(X.T.dot(X)).dot(X.T).dot(y)
return param
W = normal_equation(data_train,target_train)

# --------------------------------------- ここまで ----------------------------------------------------

>>出力

単回帰のトレーニング2乗誤差: 101.35950150166111
単回帰のテスト2乗誤差   : 101.34383018796446
重回帰のトレーニング2乗誤差: 92.66598989721335
重回帰のテスト2乗誤差   : 92.4839200158771

プロセスは終了コード 0 で完了しました

まぁ重回帰の方が予測性能が良い、過学習してる様子も見られないし、線形回帰の限界はこの辺りにありそう。まぁデータから有益な特徴量を作れれば話は別なんだけど、どうやって作るんだろ...。今度は非線形の回帰も勉強してみるか...。因みに自分でクラスを定義したのは初めてだったんだけど、使ってみると便利さが分かるね...。

 

3. Nural_network_regression.py

線形回帰の限界をみたので、気になっているニューラルネットワーク回帰に手を出してみた。中身はブラックボックスだけど、やっぱり予測の精度は重回帰より全然よかった。ただ使う分にはこれで問題ないのだけど、やっぱり使うからには中身で何やってるか理解しないと何か気持ち悪いな...。何も分かってなくても回帰より良い結果が得られちゃうの、ちょっと怖い。

import numpy as np
import pandas as pd
from sklearn.neural_network import MLPRegressor
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt

# 加工したcsv を読み込み
df = pd.read_csv('train_trance.csv')

# 学習データとテストデータへ分割
df_train, df_test = train_test_split(df,test_size=0.4)

colmuns = ['accommodates','amenities','bathrooms','bedrooms','beds','bed_type_Couch','bed_type_Futon','bed_type_Pull-out Sofa','bed_type_Real Bed','cancellation_policy_moderate','cancellation_policy_strict','cancellation_policy_super_strict_30','cancellation_policy_super_strict_60','city_Chicago','city_DC','city_LA','city_NYC','city_SF','cleaning_fee_t','host_has_profile_pic_t','host_identity_verified_t','instant_bookable_t','room_type_Private room','room_type_Shared room','seppen']
data_train = df_train[colmuns]
# 目的変数に y を設定
target_train = df_train['y']

# ニューラル回帰をインスタンス
mlp = MLPRegressor(hidden_layer_sizes=(100,), max_iter=500)
mlp.fit(data_train, target_train)

# テストデータの生成
data_test = df_test[colmuns]
target_test = df_test['y']

# predict関数で予測値を計算
pred = mlp.predict(data_test)
# 平均2乗誤差を計算
errors = np.sqrt(0.5*((target_test - pred)*(target_test - pred)).sum()/len(pred))
print('ニューラルネットワーク回帰の2乗誤差:',errors)

plt.scatter(target_test, pred)
plt.xlabel('correct')
plt.ylabel('predict')
# plt.show()

# -------------------------- テストデータに対して予測値を計算 -----------------------------------
# CSV の読み込み
data = pd.read_csv('C:\\work\\SIGNATE\\TRAIN_MINPAKU\\test.csv')

# 欠損値を f で置換
data = data.fillna({'host_has_profile_pic':'f' ,
'host_identity_verified':'f' })
# ホストからの返信率は文字置換して数字型へ
data['host_response_rate'] = data['host_response_rate'].str.replace('%','')
data['host_response_rate'] = data['host_response_rate'].astype(float)
# 欠損値を 平均値 で置換
data = data.fillna({'bathrooms':data['bathrooms'].mean()
,'bedrooms':data['bedrooms'].mean()
,'beds':data['beds'].mean()
,'host_response_rate':data['host_response_rate'].mean()
,'review_scores_rating':data['review_scores_rating'].mean()})

# last_review - host_since を計算
# 日付のデータを - で分割
df_last = data['last_review'].str.split('-',expand=True)
df_since = data['host_since'].str.split('-',expand=True)
# 欠損部分を平均で置換
df_last = df_last.fillna(df_last.astype(float).mean())
df_since = df_since.fillna(df_since.astype(float).mean())
# period を営業している期間の長さとして定義
data = data.assign(period=365*(df_last[0].astype(float)-df_since[0].astype(float))
+ 30*(df_last[1].astype(float)-df_since[1].astype(float))
+ (df_last[2].astype(float)-df_since[2].astype(float)) )
# アメニティの文字データをアメニティの個数データに変換
df_amenities = data['amenities'].str.split(',',expand=True)
data = data.assign(amenities=len(df_amenities.columns) - df_amenities.isnull().sum(axis=1))
# 文字型の変数をダミー変数へ
data = pd.get_dummies(data,drop_first=True, columns=['bed_type','cancellation_policy','city','cleaning_fee'
,'host_has_profile_pic','host_identity_verified','instant_bookable','property_type','room_type'])
# 不要なカラムをdrop
data = data.drop(['last_review','host_since','id','description' , 'first_review','name', 'neighbourhood', 'thumbnail_url' ,'zipcode','latitude' ,'longitude'] ,axis=1)
# 全て1のカラムを追加
data = data.assign(seppen=1)

colmuns = ['accommodates','amenities','bathrooms','bedrooms','beds','bed_type_Couch','bed_type_Futon','bed_type_Pull-out Sofa','bed_type_Real Bed','cancellation_policy_moderate','cancellation_policy_strict','cancellation_policy_super_strict_30','cancellation_policy_super_strict_60','city_Chicago','city_DC','city_LA','city_NYC','city_SF','cleaning_fee_t','host_has_profile_pic_t','host_identity_verified_t','instant_bookable_t','room_type_Private room','room_type_Shared room','seppen']
data = data[colmuns]

# predict関数で予測値を計算してcsvに吐き出す
pred = mlp.predict(data)
predicts = pd.DataFrame(pred)
predicts.to_csv('NN_predicts.csv',header=None)

>>出力

ConvergenceWarning: Stochastic Optimizer: Maximum iterations (500) reached and the optimization hasn't converged yet.
% self.max_iter, ConvergenceWarning)
ニューラルネットワーク回帰の2乗誤差: 86.69826189432175

ちょっとエラーは言えるけど原因分から無し、でも結果は出てるから無視...。

以上。