0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Python で代数的データ型の設定ファイルを使う

0
Posted at

はじめに

Deep Learning 関連の実験をしていて、常々、python で代数データ型での設定ファイルの型チェックをしたいという欲求がありました。もう結構長いので自分で書けよという話ではあったのですが、先日ずいぶん前からあった pyserde というライブラリでできるということを知ったので紹介します。

公式サイト

Qiitaにも作者の記事がありました。

ただし、悲しいことに作者がすでに敗北宣言しています。

私の要望

私がなぜ代数データ型で設定ファイルを検証したいかを説明します。

Optimizer のパラメータの指定を設定ファイルで行えるようにするというのはよくあることです。このときに、SGDとAdamでは必要なパラメータが違います。

SGDの主要なパラメータは以下の通りです。

  1. 学習率(lr)
  2. 慣性(momentum)

Adamの主要パラメータは以下の通りです。

  1. 学習率(alpha)
  2. beta1
  3. 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 もそういう挙動だったので問題ないかと

以上です。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?