2023年11月3日金曜日

Keras 超入門

Keras 超入門

概要

前回 RNN のサンプルを動かしましたがそもそも Keras の仕組みがさっぱりだったので簡単なデータからモデルの訓練までできる簡単なサンプルを動かしてみました
流れがわかりやすいようにクラス化もしています

環境

  • macOS 14.0
  • Python 3.11.6
    • keras 2.14.0
    • tensorflow 2.14.0

サンプルコード

from dataclasses import dataclass
from typing import Any

import numpy as np
from tensorflow.keras.layers import Dense  # type: ignore
from tensorflow.keras.models import Sequential  # type: ignore


# 説明変数を管理するクラス
class IndependentVariable:
    NUM_TRAIN_COL = 2

    # 128行2列の行列データ
    data: np.ndarray[Any, np.dtype[np.float64]]

    def __init__(self, row: int) -> None:
        self.data = np.random.rand(row, self.NUM_TRAIN_COL)


# 目的変数を管理するクラス
class DependentVariable:
    NUM_TRAIN_COL = 1

    # 128行1列の行列データ、説明変数に対する答えのラベルを管理
    labels: Any

    def __init__(self, data: np.ndarray[Any, np.dtype[np.float64]], row: int) -> None:
        # np.sum(data, axis=1) で各行ごとの合計を計算
        # それが 1.0 以上なら True で True(1) or False(0) * 1 をするので最終的には128行の 0 or 1 の配列になる
        self.raw_labels = (np.sum(data, axis=1) > 1.0) * 1
        self.labels = self.raw_labels.reshape(row, self.NUM_TRAIN_COL)


# テストデータを管理するクラス
class TestData:
    NUM_TRAIN_ROW = 128

    def __init__(self) -> None:
        self.independent = IndependentVariable(self.NUM_TRAIN_ROW)
        self.dependent = DependentVariable(self.independent.data, self.NUM_TRAIN_ROW)


# モデルを管理するクラス
class Model:
    model: Sequential

    def __init__(self) -> None:
        self.model = Sequential()

    def compile(self) -> None:
        # 結合層(2層->4層)
        self.model.add(Dense(4, input_dim=2, activation="tanh"))
        # 結合層(4層->1層):入力次元を省略すると自動的に前の層の出力次元数を引き継ぐ
        self.model.add(Dense(1, activation="sigmoid"))
        self.model.compile(
            loss="binary_crossentropy", optimizer="sgd", metrics=["accuracy"]
        )

    def show(self):
        self.model.summary()

    def train(
        self,
        independent: IndependentVariable,
        dependent: DependentVariable,
        epochs=300,
        validation_split=0.2,
    ):
        self.model.fit(
            independent.data,
            dependent.labels,
            epochs=epochs,
            validation_split=validation_split,
        )


# 実際にモデルを評価するためのクラス
@dataclass
class Result:
    accuracy: float


class Test:
    NUM_TEST_ROW = 50

    def __init__(self, model: Model) -> None:
        self.model = model
        # テストデータは Independent, DependentVariable を使える
        self.independent = IndependentVariable(self.NUM_TEST_ROW)
        self.dependent = DependentVariable(self.independent.data, self.NUM_TEST_ROW)

    def run(self) -> Result:
        predict = ((self.model.model.predict(self.independent.data) > 0.5) * 1).reshape(
            self.NUM_TEST_ROW
        )
        # 50個中何個正解だったか
        accuracy = sum(predict == self.dependent.raw_labels) / self.NUM_TEST_ROW
        return Result(accuracy=accuracy)


if __name__ == "__main__":
    # テストデータ作成
    test_data = TestData()
    # モデル生成
    model = Model()
    model.compile()
    # モデルの訓練
    model.train(test_data.independent, test_data.dependent)
    # モデルの評価
    test = Test(model)
    result = test.run()
    print(result)

ちょっと解説

大きく「テストデータ」「モデル」「評価」というクラスに分割しました
その中で更に説明変数や目的変数といったクラスを委譲しています
main を見ると全体流れが簡単に把握できるかなと思います
基本はデータ作成 -> モデル生成 -> 評価という流れになっています

リファクタすることで学習したデータと評価するためのデータが同じクラスから作成できるのでコードの重複を排除できました

最後に

keras の既存コードをリファクタしつつ動かすだけでもかなり理解が深まる気がしました

参考サイト

0 件のコメント:

コメントを投稿