Skip to content

Allow subgroup argument selection via Enum #37

@janvainer

Description

@janvainer

Is your feature request related to a problem? Please describe.

Hi, first of all, this project has a great potential for ML project configuration! Well done <3!!!
There is one usecase that is quite common in ML. It is when you have two different sub-configurations and you want to easily switch between them and then also specify certain sub-options. For example, consider a ML training script where you want to be able to select different optimizers:

class Optimizer(BaseModel):
    lr: float = 0.001
    eps: float = 1e-7

class Adam(Optimizer):
    lr: float = 0.003


class SGD(Optimizer):
    lr: float = 0.002


class Config(BaseModel):
    optimizer: Adam | SGD = Adam()

Now it would be awesome to somehow specify which optimizer to initialize in the config and also be able to set some of its parameters.

Describe the solution you'd like
How the CLI should look:

python train.py --optimizer adam --optimizer.lr 0.01  # specify the optimizer type and then also specify some of its params

There may be an issue regarding naming of the arguments based on the Union type. Instead, perhaps enums could be used:

class Optimizers(Optimizer, Enum):
    adam = Adam()
    sgd = SGD()
    sgd_custom = SGD(lr=1.0)


class Config(BaseModel):  # this gets parsed by argdantic into a cli later
    optimizer: Optimizers = Optimizers.adam

The CLI would have to check that the enum value itself is a BaseModel and treat it as a nested configuration node to be displayed and available in terminal.

Describe alternatives you've considered
Hydra allows this kind of sub-grouping via subfolders. Simple-parsing solves it via subgroups type. Unfortunately, none of them are built on pydantic, so the user has to take care of validation themselves.

WDYT about the feature? It would allow quite complex configurations, for example:

python train.py \
    --optimizer adam | sgd \
    --optimizer.lr 0.1 \
    --encoder lstm | conv \
    --encoder.channels 512

Edit: after a bit of thought, perhaps the Annotated type could be better suited 🤔 It would be something like

class Config(BaseModel):  # this gets parsed by argdantic into a cli later
    optimizer: Annotated[Optimizer, argdantic.Subgroups(adam=Adam(), sgd=SGD), sgd_custom=SGD(lr=1.0))] = Adam()

The advantage is that this can be used outside of CLI world without issues - one would be able to initialize Config without the need to import the enum class. It would be simply Config(optimizer=SGD(0.1)) instead of Config(optimizer=Optimizers.SGD). Another advantage is that it would be possible to pass optimizer config that is not pre-defined in the Enum.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions