Skip to content

Configuration

Each model has a statically typed configuration model. Each configuration has default settings that will be instantiated when the model is instantiated. To create a default preprocessing configuration for example you would:

from everyvoice.config.preprocessing_config import PreprocessingConfig

preprocessing_config = PreprocessingConfig()

Static typing means that misconfiguration errors should occur as soon as the configuration is instantiated instead of producing downstream runtime errors. It also means that intellisense is available in your code editor when working with a configuration class.

Sharing Configurations

The Text and Preprocessing configurations should only be defined once per dataset and shared between your models to ensure each model makes the same assumptions about your data. To achieve that, each model configuration can also be defined as a path to a configuration file. So, a configuration for an aligner that uses separately defined text and audio preprocessing configurations might look like this:

model:
    lstm_dim: 512
    conv_dim: 512
    ...
training:
    batch_size: 32
    ...
preprocessing: "./config/default/everyvoice-shared-data.yaml"
text: "./config/default/everyvoice-shared-text.yaml"

Serialization

By default configuration objects are serialized as dictionaries, which works as expected with integers, floats, lists, booleans, dicts etc. But there are some cases where you need to specify a Callable in your configuration. For example the {ref}TextConfig has a cleaners field that takes a list of Callables to apply in order to raw text. By default, these functions turn raw text to lowercase, collapse whitespace, and normalize using Unicode NFC normalization. In Python, we could instantiate this by passing the callables directly like so:

from everyvoice.config.text_config import TextConfig
from everyvoice.utils import collapse_whitespace, lower, nfc_normalize

text_config = TextConfig(cleaners=[lower, collapse_whitespace, nfc_normalize])

But, for yaml or json configuration, we need to serialize these functions. To do so, EveryVoice will turn each callable into module dot-notation. That is, your configuration will look like this in yaml:

cleaners:
    - everyvoice.utils.lower
    - everyvoice.utils.collapse_whitespace
    - everyvoice.utils.nfc_normalize

This will then be de-serialized upon instantiation of your configuration.

Text Configuration

The TextConfig is where you define the symbol set for your data and any cleaners used to clean your raw text into the text needed for your data. You can share the TextConfig with any models that need it and only need one text configuration per dataset (and possibly only per language).

TextConfig

everyvoice.config.text_config.TextConfig

Bases: ConfigModel

Source code in everyvoice/config/text_config.py
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
class TextConfig(ConfigModel):
    symbols: Symbols = Field(default_factory=Symbols)
    to_replace: Dict[str, str] = {}  # Happens before cleaners
    cleaners: list[PossiblySerializedCallable] = [collapse_whitespace, strip_text]
    g2p_engines: G2P_Engines = Field(
        {},
        title="External G2P",
        description="User defined or external G2P engines.\nSee https://github.com/EveryVoiceTTS/everyvoice_g2p_template_plugin to implement your own G2P.",
        examples=["""{"fr": "everyvoice_plugin_g2p4example.g2p"}"""],
    )

    @model_validator(mode="after")
    def clean_symbols(self) -> Self:
        """We should apply all cleaners to the symbols

        Returns:
            TextConfig: a text config with cleaned symbols
        """
        for k, v in self.symbols:
            if k not in ["punctuation", "silence"]:
                normalized = [
                    normalize_text_helper(x, self.to_replace, self.cleaners) for x in v
                ]
                if "" in normalized or len(normalized) != len(set(normalized)):
                    logger.warning(
                        f"Normalization created a duplicate or inserted '' in {k}={normalized}. "
                        "Please check your shared-text config for problems."
                    )
                setattr(self.symbols, k, normalized)
        return self

    @model_validator(mode="after")
    def load_g2p_engines(self) -> Self:
        """
        Given `g2p_engines`, populate the global list `AVAILABLE_G2P_ENGINES`.
        """
        import importlib

        from everyvoice.text.phonemizer import AVAILABLE_G2P_ENGINES

        for lang_id, name in self.g2p_engines.items():
            # Load the user provided G2P Engine.
            try:
                module_name, _, function_name = name.rpartition(".")
                module = importlib.import_module(module_name)
            except ModuleNotFoundError:
                error_message = (
                    f"Invalid G2P engine module `{module_name}` for `{lang_id}`"
                )
                logger.error(error_message)
                raise ValueError(error_message)

            g2p_func = _validate_g2p_engine_signature(getattr(module, function_name))

            if lang_id in AVAILABLE_G2P_ENGINES:
                logger.warning(
                    f"Overriding g2p for `{lang_id}` with user provided g2p plugin `{name}`"
                )

            AVAILABLE_G2P_ENGINES[lang_id] = g2p_func
            logger.info(f"Adding G2P engine from `{name}` for `{lang_id}`")

        return self
cleaners = [collapse_whitespace, strip_text] class-attribute instance-attribute
symbols = Field(default_factory=Symbols) class-attribute instance-attribute
to_replace = {} class-attribute instance-attribute

Symbols

Your symbol set is created by taking the union of all values defined. For example:

symbols:
    dataset_0_characters: ['a', 'b', 'c']
    dataset_1_characters: ['b', 'c', 'd']

Will create a symbol set equal to {'a', 'b', 'c', 'd'} (i.e. the union of both key/values). This allows you to train models with data from different languages, for example.

Important

You should always manually inspect your configuration here to make sure it makes sense with respect to your data. Is there a symbol that shouldn't be there? Is there a symbol that's defined as 'punctuation' but is used as non-punctuation in your language? Please inspect these and update the configuration accordingly.

everyvoice.config.text_config.Symbols

Bases: BaseModel

Source code in everyvoice/config/text_config.py
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
class Symbols(BaseModel):
    silence: list[str] = Field(
        ["<SIL>"], description="The symbol(s) used to indicate silence."
    )
    punctuation: Punctuation = Field(
        default_factory=Punctuation,
        description="EveryVoice will combine punctuation and normalize it into a set of five permissible types of punctuation to help tractable training.",
    )
    model_config = ConfigDict(extra="allow")

    @property
    def all_except_punctuation(self) -> set[str]:
        """Returns the set containing all characters."""
        return set(w for _, v in self if not isinstance(v, Punctuation) for w in v)

    @model_validator(mode="after")
    def cannot_have_punctuation_in_symbol_set(self) -> "Symbols":
        """You cannot have the same symbol defined in punctuation as elsewhere.

        Raises:
            ValueError: raised if a symbol from punctuation is found elsewhere

        Returns:
            Symbols: The validated symbol set
        """
        for punctuation in self.punctuation.all:
            if punctuation in self.all_except_punctuation:
                raise ValueError(
                    f"Sorry, the symbol '{punctuation}' occurs in both your declared punctuation and in your other symbol set. Please inspect your text configuration and either remove the symbol from the punctuation or other symbol set."
                )
        return self

    @model_validator(mode="after")
    def member_must_be_list_of_strings(self) -> "Symbols":
        """Except for `punctuation` & `pad`, all user defined member variables
        have to be a list of strings.
        """
        for k, v in self:
            if isinstance(v, Punctuation):
                continue
            if k == "pad":
                continue
            if not isinstance(v, list) or not all(isinstance(e, str) for e in v):
                raise ValueError(f"{k} must be a list")

        return self
all_except_punctuation property

Returns the set containing all characters.

cannot_have_punctuation_in_symbol_set()

You cannot have the same symbol defined in punctuation as elsewhere.

Raises:

Type Description
ValueError

raised if a symbol from punctuation is found elsewhere

Returns:

Name Type Description
Symbols Symbols

The validated symbol set

Source code in everyvoice/config/text_config.py
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
@model_validator(mode="after")
def cannot_have_punctuation_in_symbol_set(self) -> "Symbols":
    """You cannot have the same symbol defined in punctuation as elsewhere.

    Raises:
        ValueError: raised if a symbol from punctuation is found elsewhere

    Returns:
        Symbols: The validated symbol set
    """
    for punctuation in self.punctuation.all:
        if punctuation in self.all_except_punctuation:
            raise ValueError(
                f"Sorry, the symbol '{punctuation}' occurs in both your declared punctuation and in your other symbol set. Please inspect your text configuration and either remove the symbol from the punctuation or other symbol set."
            )
    return self
member_must_be_list_of_strings()

Except for punctuation & pad, all user defined member variables have to be a list of strings.

Source code in everyvoice/config/text_config.py
85
86
87
88
89
90
91
92
93
94
95
96
97
98
@model_validator(mode="after")
def member_must_be_list_of_strings(self) -> "Symbols":
    """Except for `punctuation` & `pad`, all user defined member variables
    have to be a list of strings.
    """
    for k, v in self:
        if isinstance(v, Punctuation):
            continue
        if k == "pad":
            continue
        if not isinstance(v, list) or not all(isinstance(e, str) for e in v):
            raise ValueError(f"{k} must be a list")

    return self