Kaggle House Prices ② ～ モデル作成 ～

以下で作成した特徴量を使用してモデルを作成します。
[Kaggle House Prices ① ～ 特徴量エンジニアリング ～](https://qiita.com/jun-16/items/bfb15b7b5ccfed4156e8)

## ライブラリの読み込み
```py
import numpy as np
import pandas as pd
import lightgbm as lgb
from sklearn.model_selection import KFold
from sklearn.metrics import mean_squared_error
from sklearn.externals import joblib
```

## データの読み込み
```py
def load_x_train() -> pd.DataFrame:
    """事前に作成した学習データの特徴量を読み込む

    :return: 学習データの特徴量
    """
    return joblib.load('train_x.pkl')

def load_y_train() -> pd.Series:
    """事前に作成した学習データの目的変数を読み込む

    :return: 学習データの目的変数
    """
    # 目的変数の読込を行う
    train_y = joblib.load('train_y.pkl')
    # 目的変数を対数変換
    train_y = np.log1p(train_y)
    return train_y
```

## クロスバリデーション
```py
def load_index_fold(i_fold: int) -> np.array:
    """クロスバリデーションでのfoldを指定して対応するレコードのインデックスを返す

    :param i_fold: foldの番号
    :return: foldに対応するレコードのインデックス
    """
    # 学習データ・バリデーションデータを分けるインデックスを返す
    # ここでは乱数を固定して毎回作成しているが、ファイルに保存する方法もある
    train_y = load_y_train()
    kf = KFold(n_splits=4, random_state=6, shuffle=True)
    return list(kf.split(train_y))[i_fold]

def train_fold(i_fold):
    """クロスバリデーションでのfoldを指定して学習・評価を行う

    他のメソッドから呼び出すほか、単体でも確認やパラメータ調整に用いる

    :param i_fold: foldの番号
    :return: （モデルのインスタンス、レコードのインデックス、予測値、評価によるスコア）のタプル
    """
    # 学習データの読込
    train_x = load_x_train()
    print(train_x.shape)
    train_y = load_y_train()

    # 学習データ・バリデーションデータをセットする
    tr_idx, va_idx = load_index_fold(i_fold)
    print(tr_idx.shape)
    print(va_idx.shape)
    tr_x, tr_y = train_x.iloc[tr_idx], train_y.iloc[tr_idx]
    va_x, va_y = train_x.iloc[va_idx], train_y.iloc[va_idx]

    # 学習を行う
    params_lgbm = {
        "boosting_type": "gbdt",
        "objective": "regression",
        "metric": "rmse",
        "learning_rate": 0.05,
        "max_depth": 4,
        "colsample_bytree": 0.9,
        "subsample": 0.9,
        "reg_alpha": 0.1,
        "reg_lambda": 0.0,
        "min_child_weight": 1,
        "num_leaves": 31
    }
    lgb_train = lgb.Dataset(tr_x, tr_y)
    lgb_eval = lgb.Dataset(va_x, va_y, reference=lgb_train)

    model = lgb.train(
        params_lgbm, lgb_train,
        # モデルの評価用データを渡す
        valid_sets=lgb_eval,
        # 最大で 1000 ラウンドまで学習する
        num_boost_round=1000,
        # 10 ラウンド経過しても性能が向上しないときは学習を打ち切る
        early_stopping_rounds=10
    )

    # バリデーションデータへの予測・評価を行う
    va_pred = model.predict(va_x)
    score = np.sqrt(mean_squared_error(va_y, va_pred))

    # モデル、インデックス、予測値、評価を返す
    return model, va_idx, va_pred, score
```

## 学習とモデル作成を実行
```py
# クロスバリデーションでの学習・評価を行う
scores = []
va_idxes = []
preds = []
n_fold = 4

# 各foldで学習を行う
for i_fold in range(n_fold):
    # 学習を行う
    print(f'fold {i_fold} - start training')
    model, va_idx, va_pred, score = train_fold(i_fold)
    print(f'fold {i_fold} - end training - score {score}')

    # モデルを保存する
    # model.save_model()
    joblib.dump(model, f'model-{i_fold}.pkl')

    # 結果を保持する
    va_idxes.append(va_idx)
    scores.append(score)
    preds.append(va_pred)

# 各foldの結果をまとめる
va_idxes = np.concatenate(va_idxes)
order = np.argsort(va_idxes)
preds = np.concatenate(preds, axis=0)
preds = preds[order]

print(f'end training cv - score {np.mean(scores)}')

# 予測結果の保存
joblib.dump(preds, 'pred-train.pkl')

# 評価結果
print('result_scores', scores)
```