IT業務効率化

機械学習済みのモデルを比較するためのコード

本記事はMLOpsのための追求の過程であり、ベストプラクティスを保証するものではございません。また、気になる箇所があればコメントをいただけると誠に幸いでございます。

複数の学習済みモデルの性能をどうやって比較するべきだろうか?

どのモデルを使うと最もスコアが良いのかすぐに判断できるクラスを用意したい。

データの加工について比較するクラスも作りたいが、それは後回しにしよう。

最初は2つのモデルのデータを比較したい。そしてそこに新しく3つ目のデータが加わった時にすぐに動かせる環境にしたい。そのために必要だと思ったことは以下の2つだ。

1. wrapperが必要

2. 新しいモデルを追加するlistもしくは関数

またデータのプロットも行いたい。可視化することによりそれぞれのデータの動向を確認したい。

3. プロットする仕組みも加える

学習データを与える必要も出てくる。学習データと同時に予測したいデータを与えることができるといい。

4. 学習データを与えられる

また、時系列データを与える時には予測値よりも過去のデータのみを学習させるようにしたい。

5. 学習データが時系列データなら、学習データは予測値より過去に限定する

また

6. モデル自体のチューニングは別の関数で行い、その機能をクラス内には持たせない

7. 予測したい期間を与え、それに対する予測値を比較する

これら7つの仕様を満たすコードを考える。

  1. wrapperが必要
  2. 新しいモデルを追加するlistもしくは関数
  3. プロットする仕組みも加える
  4. 学習データを与えられる
  5. 学習データが時系列データなら、学習データは予測値より過去に限定する
  6. モデル自体のチューニングは別の関数で行い、その機能をクラス内には持たせない
  7. 予測したい期間を与え、それに対する予測値を比較する

環境はjupyter notebookを使用し、技術者が利用する前提の元、バリデーション等は考えない。

予測したい期間を与え、それに対する予測値を比較する

class compareModels:
    def __init__(self):
        return

    def _model1_predict(self):
        return

    def _model2_predict(self):
        return

    def compare(self, term: list):
        predict1 = self._model1_predict(term)
        predict2 = self._model2_predict(term)
        actuals = actual_data(term)
        
        scores = self.create_scores(actuals, predictions=[predict1, predict2])
        return

 

すごくざっくりと上のようなイメージです。(当然ですが、動くコードではないです)

wrapperが必要

データの与え方は一律に管理できるようにしたいです。そのため、wrapperを用意する必要があります。

class compareModels:
    def __init__(self):
        return

    def _model1_train(self, train_data):
        return model

    def _model2_train(self, train_data):
        return model

    def _model1_predict(self):
        model = self._model1_train(train_data)
        return model.predict()

    def _model2_predict(self):
        model = self._model2_train(train_data)
        return model.predict()

    def compare(self, term: list, train_data):
        predict1 = self._model1_predict(term, train_data)
        predict2 = self._model2_predict(term, train_data)
        actuals = actual_data(term)

        scores = self.create_scores(actuals, predictions=[predict1, predict2])
        return

ここで思いつきました。train_dataはクラス作成時に与えて、期間はcompareを実行する時に与えることにします。

class compareModels:
    def __init__(self, train_data):
        self.train_data = train_data
        return

    def _model1_train(self):
        model1.train(self.train_data)
        return model1

    def _model2_train(self):
        model2.train(self.train_data)
        return model

    def _model1_predict(self, term):
        model = self._model1_train()
        return model.predict(term)

    def _model2_predict(self, term):
        model = self._model2_train()
        return model.predict(term)

    def compare(self, term: list):
        predict1 = self._model1_predict(term)
        predict2 = self._model2_predict(term)
        actuals = actual_data(term)
        
        scores = self.create_scores(actuals, predictions=[predict1, predict2])
        return

これでwrapperも実装できました。変数を与えるタイミングも今は問題ないように思います。

新しいモデルを追加するlistもしくは関数

新しいモデルを追加するときは_modelX_trainと_modelX_predictを追加すればよさそうです。それぞれ固有のデータの与え方があると思うので、関数内に記載します。

プロットする仕組みも加える

予測値のデータと実績値のデータを元にデータにプロットするので、それらの値が出た後にプロットの関数を実行することにします。

class compareModels:
    def __init__(self, train_data):
        self.train_data = train_data
        return

    def _model1_train(self):
        model1.train(self.train_data)
        return model1

    def _model2_train(self):
        model2.train(self.train_data)
        return model

    def _model1_predict(self, term):
        model = self._model1_train()
        return model.predict(term)

    def _model2_predict(self, term):
        model = self._model2_train()
        return model.predict(term)

    def compare(self, term: list):
        predict1 = self._model1_predict(term)
        predict2 = self._model2_predict(term)
        actuals = actual_data(term)
        
        scores = self.create_scores(actuals, predictions=[predict1, predict2])
        self.plot(actuals, predictions=[predict1, predict2])
        return

学習データを与えられる

これはすでに実装しました。

学習データが時系列データなら、学習データは予測値より過去に限定する

この機能を一度クラスの中に持たせました。

こうするとテストしたい期間を与えるだけで、できる限り多くのデータを利用して学習してくれるので、最適な結果を出し続けたい時に有効でした。(データが多いほど必ず精度が上がるわけではないですが、傾向として)

class compareModels:
    def __init__(self, train_data):
        self.train_data_origin = train_data
        return

    def _model1_train(self):
        model1.train(self.train_data)
        return model1

    def _model2_train(self):
        model2.train(self.train_data)
        return model

    def _model1_predict(self, term):
        model = self._model1_train()
        return model.predict(term)

    def _model2_predict(self, term):
        model = self._model2_train()
        return model.predict(term)

    def adjust_train_date(self, term):
        self.train_data = self.train_data_origin[:term[0]] # term = [start, end]

    def compare(self, term: list):
        self.adjust_train_date(term)
        predict1 = self._model1_predict(term)
        predict2 = self._model2_predict(term)
        actuals = actual_data(term)
        
        scores = self.create_scores(actuals, predictions=[predict1, predict2])
        self.plot(actuals, predictions=[predict1, predict2])
        return

モデル自体のチューニングは別の関数で行い、その機能をクラス内には持たせない

これは一度クラスの中に持たせようとしましたが、非常に冗長で比較昨日から外れているために、別で用意しました。

ABOUT ME
hirayuki
今年で社会人3年目になります。 日々体当たりで仕事を覚えています。 テーマはIT・教育です。 少しでも技術に親しんでもらえるよう、noteで4コマ漫画も書いています。 https://note.mu/hirayuki