【深層学習入門】Kerasによる回帰ニューラルネットワークの実装

機械学習

はじめに

この記事では簡単なニューラルネットワークを作成して、数値データの予測値を出力する推論モデルを作成します。

「回帰」は、複数の特徴データをもとに、連続値などの「数値」を予測するタスクです。 今回は、データセット「Boston house-prices」を使って、住宅の情報から価格を予測します。

この記事で使用されているソースコードは以下のGitHubや、Google Colabで試すことができます。

GitHub - Joichiro433/Blog-regression-nn
Contribute to Joichiro433/Blog-regression-nn development by creating an account on GitHub.
Google Colaboratory

\begin{align*}
\newcommand{\mat}[1]{\begin{pmatrix} #1 \end{pmatrix}}
\newcommand{\f}[2]{\frac{#1}{#2}}
\newcommand{\pd}[2]{\frac{\partial #1}{\partial #2}}
\newcommand{\d}[2]{\frac{{\rm d}#1}{{\rm d}#2}}
\newcommand{\e}{{\rm e}}
\newcommand{\T}{\mathsf{T}}
\newcommand{\(}{\left(}
\newcommand{\)}{\right)}
\newcommand{\{}{\left\{}
\newcommand{\}}{\right\}}
\newcommand{\[}{\left[}
\newcommand{\]}{\right]}
\newcommand{\dis}{\displaystyle}
\newcommand{\eq}[1]{{\rm Eq}(\ref{#1})}
\newcommand{\n}{\notag\\}
\newcommand{\t}{\ \ \ \ }
\newcommand{\tt}{\t\t\t\t}
\newcommand{\argmax}{\mathop{\rm arg\, max}\limits}
\newcommand{\argmin}{\mathop{\rm arg\, min}\limits}
\def\l<#1>{\left\langle #1 \right\rangle}
\def\us#1_#2{\underset{#2}{#1}}
\def\os#1^#2{\overset{#2}{#1}}
\newcommand{\case}[1]{\{ \begin{array}{ll} #1 \end{array} \right.}
\newcommand{\s}[1]{{\scriptstyle #1}}
\definecolor{myblack}{rgb}{0.27,0.27,0.27}
\definecolor{myred}{rgb}{0.78,0.24,0.18}
\definecolor{myblue}{rgb}{0.0,0.443,0.737}
\definecolor{myyellow}{rgb}{1.0,0.82,0.165}
\definecolor{mygreen}{rgb}{0.24,0.47,0.44}
\newcommand{\c}[2]{\textcolor{#1}{#2}}
\newcommand{\ub}[2]{\underbrace{#1}_{#2}}
\end{align*}

住宅価格のデータセット「Boston house-prices」

「Boston house-prices」は、ボストン市の住宅の特徴と正解ラベルとなる価格を集めたデータセットです。訓練データ 404 件、テストデータ 102 件が含まれています。ボストン市の住宅の特徴としては、以下の 13 の項目を保持しています。

特徴説明
CRIM人口 1 人当たりの犯罪発生数
ZN25,000 平方フィート以上の住居区画の占める割合
INDUS小売業以外の商業が占める面積の割合
CHASチャールズ川によるダミー変数(1:川の周辺、0:それ以外)
NOXNOxの濃度
RM住居の平均部屋数
AGE1940 年より前に建てられた物件の割合
DIS5つのボストン市の雇用施設からの距離(重み付け済み)
RAD環状高速道路へのアクセスしやすさ
TAX10,000ドルあたりの不動産税率の総計
PTRATIO町ごとの児童と教師の比率
Bある計算式で計算した町ごとの黒人(Bk)の比率
LSTAT給与の低い職業に従事する人口の割合(%)
▲ Boston house-prices の特徴量

TensorFlow には、このデータセットを読み込む機能が用意されています。

from tensorflow.keras.datasets import boston_housing

(train_data, train_labels), (test_data, test_labels) = boston_housing.load_data()

print(train_data.shape)
print(train_labels.shape)
print(test_data.shape)
print(test_labels.shape)
## 出力
(404, 13)
(404,)
(102, 13)
(102,)

訓練データと訓練ラベルは 404 件、テストデータとテストラベルは 102 件であることがわかります。特徴量の数は 13 件です。

▲ Boston house-prices の訓練データの内容

Kerasによるニューラルネットワークの実装

この記事では以下のプロジェクト構成で実装を進めていきます。

.
├── requirements.txt
└── app/
    ├── logs/
    ├── model.py
    ├── params.py
    ├── predict.py
    ├── preprocessing.py
    ├── train.py
    └── utils.py

app/ ディレクトリ配下にソースコードを配置します。それぞれの説明は下記の通りです。

  • logs/ : 機械学習モデルの学習過程の情報が出力されるディレクトリです
  • model.py : 機械学習モデルの定義を記載します
  • params.py : モデルのパラメータ等を記載します
  • predict.py : 学習済みモデルを用いた推論用コードを記載します
  • preprocessing.py : 前処理用の関数を記載します
  • train.py : モデルの学習用コードを記載します
  • utils.py : データを読み書きするコードを記載します

また、requirements.txt の内容は次の通りです。

numpy==12.2.4
pandas==1.4.2
matplotlib==3.5.2
seaborn==0.11.2
scikit-learn==1.1.1
nptyping==2.1.1
tensorflow==2.9.1
# tensorflow-macos==2.9.2  # M1macの場合はこちらを使用する
pydot==1.4.2
graphviz==0.20
rich==12.5.1

今回作成するニューラルネットワークのアーキテクチャは下図のようなシンプルなものとします。

以上を踏まえて、それぞれのソースコードをみてみましょう。

params.py

params.py ではモデルのアーキテクチャを決めるパラメータ等を記載します。

from datetime import datetime
from pathlib import Path

INPUT_SIZE = 13
HIDDEN1_SIZE = 64
HIDDEN2_SIZE = 64
OUTPUT_SIZE = 1

EPOCHS = 500
VALIDATION_SPLIT = 0.2

LOG_DIR = Path('logs/fit') / datetime.now().strftime("%Y%m%d-%H%M%S")
MODEL_FILE_PATH = Path('model.h5')

model.py

model.py ではニューラルネットワークモデルの定義を記載します。ここでは Keras の Functional API を使用しています。

import tensorflow as tf
from keras.api._v2 import keras
from keras.layers import Input, Dense
from keras.models import Model

import params


class DNNModel:
    def __init__(
            self,
            input_size: int = params.INPUT_SIZE,
            hidden1_size: int = params.HIDDEN1_SIZE,
            hidden2_size: int = params.HIDDEN2_SIZE,
            output_size: int = params.OUTPUT_SIZE) -> None:
        self.input: Input = Input(shape=(input_size,), name='input')
        self.hidden1: Dense = Dense(hidden1_size, activation='relu', name='hidden1')
        self.hidden2: Dense = Dense(hidden2_size, activation='relu', name='hidden2')
        self.output: Dense = Dense(output_size, name='output')

    def build(self) -> Model:
        input = self.input
        x = self.hidden1(input)
        x = self.hidden2(x)
        output = self.output(x)
        return Model(inputs=input, outputs=output)

preprocessing.py

preprocessing.py では画像の前処理用関数を記載します。

処理内容は、すべての特徴量を「平均 0、分散 1」に標準化しています。

import numpy as np
from nptyping import NDArray, Shape, Float, Int
from sklearn.preprocessing import StandardScaler

from utils import save_pkl, read_pkl


Features = NDArray[Shape['Sample, Feature'], Float]

def preprocess_dataset(dataset: Features, is_training: bool) -> Features:
    if is_training:
        sc = StandardScaler()
        dataset = sc.fit_transform(dataset)
        save_pkl(sc, path='scaler.pkl')
        return dataset

    sc = read_pkl(path='scaler.pkl')
    dataset = sc.transform(dataset)
    return dataset

ここでは型情報と numpy arrey の次元を明確にするために、nptyping ライブラリを使用しています。

また、 sklearnStandardScaler を用いて標準化をおこなっています。テストデータを標準化する際は学習データを標準化する際に計算した平均、標準偏差を使って変換をおこないます。そのため、学習時に StandardScaler オブジェクトを pickle 形式で保存しています。

utils.py

utils.py ではオブジェクトを pickle 形式で保存、読み込みする関数を定義します。

import pickle


def save_pkl(obj, path):
    with open(path, 'wb') as f:
        pickle.dump(obj, f)

def read_pkl(path):
    with open(path, 'rb') as f:
        return pickle.load(f)
        

train.py

train.py はモデルの学習を行います。

import tensorflow as tf
from keras.api._v2 import keras
from keras.models import Model
from keras.datasets import boston_housing
from keras.callbacks import EarlyStopping, ModelCheckpoint, TensorBoard
from keras.utils import plot_model

import params
from preprocessing import preprocess_dataset
from model import DNNModel


def main():
    (train_data, train_labels), (test_data, test_labels) = boston_housing.load_data()
    train_data, train_labels = preprocess_dataset(dataset=train_data, labels=train_labels)

    model: Model = DNNModel().build()

    model.compile(
        optimizer='adam',
        loss='mse',
        metrics=['mae'])
    model.summary()
    plot_model(model, to_file='model.pdf', show_shapes=True)

    callbacks = [
        EarlyStopping(patience=20),
        ModelCheckpoint(filepath=params.MODEL_FILE_PATH, save_best_only=True),
        TensorBoard(log_dir=params.LOG_DIR)]

    model.fit(
        x=train_data,
        y=train_labels,
        epochs=params.EPOCHS,
        validation_split=params.VALIDATION_SPLIT,
        callbacks=callbacks)
    
if __name__ == '__main__':
    main()

ここでは、Keras に用意されたコールバックを利用することで、学習中のバッチやエポックの開始 / 終了時にメゾットを呼び出しています。使用しているコールバックは以下の通りです。

  • EarlyStopping : 過学習を避けるためのコールバックで、一定のエポック数、学習指標に改善が見られなくなった時に自動的に学習を停止します。
  • ModelCheckpoint : エポック終了時にモデルを保存するコールバックです。学習中にモデル保存が自動的にされ、もし学習が中断された場合、中断された時点から学習を再開することができます。
  • TensorBoard : 学習指標やモデルの構造を可視化できるツールです。引数 log_dir に指定したディレクトリに学習過程のさまざまな指標が自動的に格納されます。

また今回、ニューラルネットワークの評価関数は「MSE (Mean Squared Error)」、評価関数は「MAE (Mean Absolute Error)」を用いています。

\begin{align*}
MSE &= \f{1}{n} \sum_{i=1}^n \(y^{(i)} – \hat{y}^{(i)}\)^2 \n
MAE &= \f{1}{n} \sum_{i=1}^n \left|y^{(i)} – \hat{y}^{(i)}\right|
\end{align*}

($y^{(i)}$: $i$ 番目の正解データ、$\hat{y}^{(i)}$: $i$ 番目の予測値、$n$: データ数)

predict.py

predict.py は、train.py で学習したモデルを活用して推論を行います。

import numpy as np
import matplotlib.pyplot as plt
import tensorflow as tf
from keras.api._v2 import keras
from keras.datasets import boston_housing
from rich import print

import params
from preprocessing import preprocess_dataset


def predict(dataset):
    model = keras.models.load_model(params.MODEL_FILE_PATH)
    pred = model.predict(dataset).flatten()
    return pred


if __name__ == '__main__':
    (train_data, train_labels), (test_data, test_labels) = boston_housing.load_data()
    test_data, test_labels = test_data[:10], test_labels[:10]  # 10データ分だけ推論をおこなう
    test_data = preprocess_dataset(dataset=test_data, is_training=False)
    pred = predict(dataset=test_data)
    print(f'prediction: {np.round(pred, 1)}')
    print(f'labels: {test_labels}')

ここでは、テストデータの 10 データ分だけ推論をおこないます。

ニューラルネットワークの実行

モデルの学習

train.py を実行してニューラルネットワークモデルの学習をおこないます。

$ python train.py
## 出力
Model: "model"
_________________________________________________________________
 Layer (type)                Output Shape              Param #   
=================================================================
 input (InputLayer)          [(None, 13)]              0         
                                                                 
 hidden1 (Dense)             (None, 64)                896       
                                                                 
 hidden2 (Dense)             (None, 64)                4160      
                                                                 
 output (Dense)              (None, 1)                 65        
                                                                 
=================================================================
Total params: 5,121
Trainable params: 5,121
Non-trainable params: 0
_________________________________________________________________
Epoch 1/500
11/11 [==============================] - 0s 10ms/step - loss: 556.1224 - mae: 21.7004 - val_loss: 638.2590 - val_mae: 23.3441
Epoch 2/500
11/11 [==============================] - 0s 2ms/step - loss: 513.5609 - mae: 20.6846 - val_loss: 587.2802 - val_mae: 22.2735
.
.
.
Epoch 121/500
11/11 [==============================] - 0s 5ms/step - loss: 6.7154 - mae: 1.8254 - val_loss: 12.1797 - val_mae: 2.5738
Epoch 122/500
11/11 [==============================] - 0s 5ms/step - loss: 6.7295 - mae: 1.8319 - val_loss: 12.2754 - val_mae: 2.5554

学習が終了すると、app/ ディレクトリ内に学習済モデル model.h5 とモデルの構造が記載された model.pdf が生成されます。model.pdf 内容は下図の通りです。

▲ model.pdf

TensorBordをつかってみる

モデルの学習過程を確認するために、TensorBord を利用してみます。

コマンドラインで下記のコマンドを実行します。

$ tensorboard --logdir ./logs/fit
## 出力
Serving TensorBoard on localhost; to expose to the network, use a proxy or pass --bind_all
TensorBoard 2.9.1 at http://localhost:6006/ (Press CTRL+C to quit)

http://localhost:6006/ にアクセスすることで、下図のように、学習過程における各指標の推移を確認することができます。

学習済モデルによる推論

学習したモデルを用いて、Boston house-pricesデータセットのテストデータを推論してみます。コマンドラインで次のコマンドを実行します。

$ python predict.py
## 出力
prediction: [ 8.3 18.  21.1 34.6 26.  19.1 26.  21.6 19.6 22.3]
labels:     [ 7.2 18.8 19.  27.  22.2 24.5 31.2 22.9 20.5 23.2]

以上のように、ニューラルネットワークによる、回帰モデルを作成することができました。

分類問題はこちら ▼

タイトルとURLをコピーしました