はじめに
Deep Learning 関連の実験をしていて、常々、python で代数データ型での設定ファイルの型チェックをしたいという欲求がありました。もう結構長いので自分で書けよという話ではあったのですが、先日ずいぶん前からあった pyserde というライブラリでできるということを知ったので紹介します。
公式サイト
Qiitaにも作者の記事がありました。
ただし、悲しいことに作者がすでに敗北宣言しています。
私の要望
私がなぜ代数データ型で設定ファイルを検証したいかを説明します。
Optimizer のパラメータの指定を設定ファイルで行えるようにするというのはよくあることです。このときに、SGDとAdamでは必要なパラメータが違います。
SGDの主要なパラメータは以下の通りです。
- 学習率(lr)
- 慣性(momentum)
Adamの主要パラメータは以下の通りです。
- 学習率(alpha)
- beta1
- beta2
私が期待する正しく書けている設定ファイルは以下の通りです。
SGDの場合
optimizer:
SGD:
lr: 0.1
momentum: 0.9
Adamの場合
optimizer:
Adam:
alpha: 1e-3
beta1: 0.9
beta2: 0.999
その上で、以下のように SGD の場合に beta2 を書くとエラーになってほしいのです。
optimizer:
SGD:
lr: 0.1
momentum: 0.9
beta2: 0.95 # SGDにbeta2は存在しない
SGDとAdamの場合、Adamのパラメータが単純にSGDのパラメータのスーパーセットになっているので例として微妙かもしれませんが、実際に実験用のプログラムを書くときには、片方にしか存在しないパラメータがそれぞれに存在したりします。
pyserde での実装方法
これを pyserde でどう実現するかといいますと単純に dataclass のように @serde をつけたクラスを作り、その Union を使えばいいだけです。
from serde import serde
from serde.yaml import from_yaml
@serde
class SGD:
lr: float
momentum: float
@serde
class Adam:
alpha: float
beta1: float
beta2: float
@serde
class Config:
optimizer: SGD | Adam
with open("config.yaml") as f:
from_yaml(f.read())
本当にこれだけなので、実はこれ以上言うことはありません。
ひとまず、私が知る限り Union が簡単に使えるライブラリはこれだけなので、みなさんも使ってみてはいかがでしょうか
補足1: pydantic でできないか
一応、有名どころの pydantic 等でできないかという話をしますと、面倒だが可能となります。
以下のように型の識別用に Literal のフィールドを用意しいます。
from pydantic import BaseModel
from typing import Literal
import yaml
class SGD(BaseModel):
type: Literal["sgd"] = "sgd"
lr: float
momentum: float
class Adam(BaseModel):
type: Literal["adam"] = "adam"
alpha: float
beta1: float
beta2: float
class Config(BaseModel):
optimizer: SGD | Adam = Field(discriminator='type')
typeフィールドへのデフォルト値は記載しなくても動作しますが、テストなどソースコード中でこれらのクラスのインスタンスを作成する際に、きちんと type="adam"のように記載する必要がでてきます。
また、optimizer フィールドに対して Field(discriminator='type') を指定してしますが、なくても動作はします。ただし、共有体の構成要素として type フィールドがないクラスを指定してもエラーにならない等の少々曖昧な挙動になります。
なお、設定を記載する際には pyserde の場合と異なり、型を記載したフィールド名(この場合 type)で指定する形になります。
optimizer:
type: adam
alpha: 1e-3
beta1: 0.9
beta2: 0.999
私としては型名を書いているのに、リテラルの型名とデフォルト値の3回も同じことを書くのには耐えられませんでした。デコレータで自動定義できるようにしようとしたのですが、そうすると mypy のチェックでエラーがでるようになり、どうしたものかと悩んでいました。
補足2
私の利用方法は設定ファイルなのですが、浮動小数点数フィールドに「1e-3」と記載さいところ、エラーがでました。どうも pyyaml の問題のようで、「1.0e-3」のように小数点を書かないと pyyaml 側では文字列として処理されるようです。
リンク先のような設定を記載すればいいのでしょうが、面倒なので私は ruamel.yaml で読み込んだ辞書を pyserde に渡しています。
また、整数を浮動小数点フィールドに書いた際にもエラーとなるため、type_checking=coerce を設定したりしています。
この場合、文字列型として"1e-3"を渡した場合なども、浮動小数点数に変換されるのですが、pydantic もそういう挙動だったので問題ないかと
以上です。