Pythonのsignatureをつかって機械学習モデルのfactoryクラスを作るTips - 4月 04, 2019 こんにちは、ぐぐりら(@guglilac)です。 研究とか実験する際に、複数のmodelをパラメータでとっかえひっかえして実験したい、みたいな状況はよく起こると思います。 比較実験とかをする場合には使用するmodelの部分だけ変更するわけで、この辺りを綺麗に書けると取り回しが良くなって実験がはかどります。 自分なりにこの辺りをうまく書けるように工夫してみているので、そのtipsをまとめようと思います。 ## 辞書オブジェクトによる実装 デザインパターンの中に、factoryパターンというものがあります。 (デザインパターンは、アプリなどのクラス設計のお手本となる典型パターン、みたいなもので自分もしっかり勉強できているわけではないのですが...) factoryパターンでは、機械学習モデルを作成するFactoryクラスを用意します。 Facotryクラスは、modelを表す文字列を受け取って指定されたmodelを返すように実装します。 一番簡単なのは辞書オブジェクトに則ってインスタンスを作成して返す、みたいな実装だと思います。 ```python from lib import ModelA,ModelB class ModelFactory:: def __init__(self): pass def run(self,str_for_model): str2model = { "model_a": ModelA(), "model_b": ModelB() } if str_for_model not in str2model.keys(): raise Exception("Invalid string for model.") return str2model[str_for_model] ``` ですが、機械学習モデルにはハイパーパラメータを持つものがあります。持たないものもありますが持つほうが普通です。 ハイパーパラメータは基本的にmodelをインスタンス化するときに最初に渡す必要があります。 しかも機械学習モデルによって使用するハイパーパラメータの種類や個数はもちろん異なります。 今回でいうと、ModelA,ModelBがそれぞれこんな感じ。 ```python class ModelA: def __init__(self,a1,a2): self.a1=a1 self.a2=a2 def fit(self,x,y): pass def predict(self,x): pass class ModelB: def __init__(self,b1,b2,b3): self.b1=b1 self.b2=b2 self.b3=b3 def fit(self,x,y): pass def predict(self,x): pass ``` そのため、辞書オブジェクトをそのまま使って Factoryクラスを作るのは難しそうです。 ## signatureを使って改良 pythonの関数の引数の渡し方に、辞書オブジェクトを展開して渡すという方法があります。 今回はこれを使って上で書いた実装を改良します。 こんな渡し方ができます。 ```python def hoge(a,b,c,d): return a+b+c+d d = {"a": 1, "b": 2, "c": 100, "d": 200} print(hoge(**d)) #=> 303 ``` これを使えば、modelのinitに必要なパラメータの辞書(param)さえ作れれば、factoryクラスのrunの中で ```python return ModelA(**params) ``` みたいにできそうです。 あとは、modelのinitに適切なパラメータの辞書を作れればよくて、これをsignatureを使って書いてみます。 やってることは、signatureを使って必要な引数の列を取得して、対応するparamsを作成しています。 ```python """モデルを作成するfactoryクラス.""" from lib import ModelA,ModelB from inspect import signature class ModelFactory: def __init__(self, params): self.params = params def run(self, str_for_model): str2model = { "model_a": ModelA, "model_b": ModelB } if str_for_model not in str2model.keys(): raise Exception("Invalid string for model.") # modelごとにinitに必要なparamsを取得する. sig = signature(str2model[str_for_model].__init__) init_params = {} for param_name in sig.parameters.keys(): if param_name is not "self": init_params[param_name] = self.params[param_name] return str2model[str_for_model](**init_params) if __name__=="__main__": x,y=load_dataset() params={"a1":0 "a2":True,"b1":False,"b2":"hoge","b3":2} f=ModelFactory(params) model=f.run("model_a") model.run(x,y) ``` signatureの部分だけ、解説してみると ```python sig = signature(ModelA.__init__) for params in sig.parameters.keys(): print(params) ``` とすると、`ModelA`の`__init__`関数についての情報をまとめたインスタンス`sig`が作成され、`parameters.keys()`で引数の文字列をイテレートできます。 なので、上記のプログラムを実行すると ``` self a1 a2 ``` と出力されます。 あとはこれをつかって必要な引数をとってきてparamsに詰めてmodelのinitに渡せばおっけーです。 ```python if __name__=="__main__": ``` 以下の部分のように使います。 ハイパーパラメータやmodelを指定する文字列の部分をargparseなどで切り替えてあげれば、簡単にmodelを取り替えることができて実験しやすくなりますね。 ## まとめ signatureをつかってpythonの機械学習モデルのFactoryクラスを実装してみました。 完全に我流なのでもっといい実装あるよ、というかたはぜひ教えていただければと思います! ありがとうございました。 この記事をシェアする Twitter Facebook Google+ B!はてブ Pocket Feedly コメント
コメント
コメントを投稿