defprotocol Bumblebee.HuggingFace.Transformers.Model do @moduledoc false # This protocol defines details related to loading Bumblebee model # from huggingface/transformers model. @type params_mapping :: %{layer_name() => params_source()} @type params_source :: layer_name() | list(layer_name()) | param_builders() @type param_builders :: %{param_name() => param_builder()} @type param_builder :: {list(param_source()), (list(Nx.tensor()) -> Nx.Tensor.t() | Nx.Container.t())} @type param_source :: param_ref() | list(param_ref()) @type param_ref :: {layer_name(), param_name()} @type layer_name :: String.t() @type param_name :: String.t() @doc """ Returns a map describing layers/parameters relationship between an Axon model and a corresponding huggingface/transformers model. ## Mapping format The basic mapping format is a map with Axon layer names (target) as keys and PyTorch layer names (source) as values. For example: %{ "embedder.token_embedding" => "bert.embeddings.word_embeddings", ... } The mapping should always use the longest names, that is, depending on the architecture, the PyTorch layer name could be either `"bert.embeddings.word_embeddings"` or `"embeddings.word_embeddings"`. The longer version should generally be used. Prefixes are removed/added as necessary, so loading partial models is supported automatically. The layer names may include simple substitutions, useful for lists of layers: %{ "encoder.blocks.{n}.self_attention.query" => "bert.encoder.layer.{n}.attention.self.query", ... } Both param names and values for corresponding layers may not match exactly, so they require further transformations. For example, the convolution `"kernel"` in Axon corresponds to a transposed `"weight"` from PyTorch. For most common layers such conversions are handled automatically. In some cases, particularly with model-specific layers/parameters, we may need more control over the parameter mapping. In such cases, instead of source layer name, a map with parameter-level transformations may be specified: %{ "embedder.class_embedding" => %{ "embedding" => { [{"vit.embeddings", "cls_token"}], fn [value] -> Nx.squeeze(value, axes: [0, 1]) end } }, ... } For each parameter, we specify a list of source parameters in the form of `{source_layer_name, source_param_name}`, then a function to build our parameter value. Instead of a single tuple, we can specify a list of those to try one by one. With the explicit transformation we can handle arbitrary parameter name and value transformations. """ @spec params_mapping(t()) :: params_mapping() def params_mapping(spec) end