qdrant / quaterion

Blazing fast framework for fine-tuning similarity learning models
https://quaterion.qdrant.tech/
Apache License 2.0
640 stars 45 forks source link

error while serving the model #193

Closed nizamsp closed 1 year ago

nizamsp commented 1 year ago

I am getting the following error.

 File "/betterapp.ai/clustering/trainer/quaterion/serving.py", line 4, in get_model
    model = SimilarityModel.load(path)
  File "/usr/local/lib/python3.10/dist-packages/quaterion_models/model.py", line 233, in load
    encoders[encoder_key] = encoder_class.load(
  File "/betterapp.ai/clustering/trainer/quaterion/encoders.py", line 44, in load
    return BetterAppEncoder(input_path)
  File "/betterapp.ai/clustering/trainer/quaterion/encoders.py", line 11, in __init__
    self.encoder = SentenceTransformer(pretrained_name)
  File "/usr/local/lib/python3.10/dist-packages/sentence_transformers/SentenceTransformer.py", line 97, in __init__
    modules = self._load_auto_model(model_path)
  File "/usr/local/lib/python3.10/dist-packages/sentence_transformers/SentenceTransformer.py", line 806, in _load_auto_model
    transformer_model = Transformer(model_name_or_path)
  File "/usr/local/lib/python3.10/dist-packages/sentence_transformers/models/Transformer.py", line 28, in __init__
    config = AutoConfig.from_pretrained(model_name_or_path, **model_args, cache_dir=cache_dir)
  File "/usr/local/lib/python3.10/dist-packages/transformers/models/auto/configuration_auto.py", line 852, in from_pretrained
    config_dict, unused_kwargs = PretrainedConfig.get_config_dict(pretrained_model_name_or_path, **kwargs)
  File "/usr/local/lib/python3.10/dist-packages/transformers/configuration_utils.py", line 565, in get_config_dict
    config_dict, kwargs = cls._get_config_dict(pretrained_model_name_or_path, **kwargs)
  File "/usr/local/lib/python3.10/dist-packages/transformers/configuration_utils.py", line 620, in _get_config_dict
    resolved_config_file = cached_file(
  File "/usr/local/lib/python3.10/dist-packages/transformers/utils/hub.py", line 380, in cached_file
    raise EnvironmentError(
OSError: /data/betterapp.ai/output/cluster_trainer/quaterion/betterapp/encoders/default does not appear to have a file named config.json. Checkout 'https://huggingface.co//data/betterapp.ai/output/cluster_trainer/quaterion/betterapp/encoders/default/None' for available files.

My files are as follows:

models.py

from typing import Any, Dict, List, Union
from quaterion import TrainableModel
from quaterion_models.encoders import Encoder
from quaterion_models.heads import EncoderHead, GatedHead
from quaterion.loss import SimilarityLoss, SoftmaxLoss
from .encoders import BetterAppEncoder
import torch

class Model(TrainableModel):
    def __init__(
        self,
        pretrained_name: str = "paraphrase-multilingual-MiniLM-L12-v2",
        num_groups: int = 27,
        lr: float = 3e-5,
    ):
        self._pretrained_name = pretrained_name
        self._num_groups = num_groups
        self._lr = lr
        super().__init__()

    def configure_encoders(self) -> Union[Encoder, Dict[str, Encoder]]:
        return BetterAppEncoder(self._pretrained_name)

    def configure_head(self, input_embedding_size) -> EncoderHead:
        return GatedHead(input_embedding_size)

    def configure_loss(self) -> SimilarityLoss:
        return SoftmaxLoss(self.model.head.output_size, self._num_groups)

    def configure_optimizers(self):
        optimizer = torch.optim.Adam(
            [
                {"params": self.model.parameters(), "lr": self._lr},
                {"params": self.loss.parameters(), "lr": self._lr * 10.0},
            ]
        )

        return optimizer

serving.py

from quaterion_models import SimilarityModel

def get_model(path):
    model = SimilarityModel.load(path)
    return model

encoders.py

import os
from quaterion_models.encoders import Encoder
from quaterion_models.types import CollateFnType
from typing import Any, Dict, List, Union
from sentence_transformers import SentenceTransformer

class BetterAppEncoder(Encoder):
    def __init__(self, pretrained_name: str):
        super().__init__()
        print(pretrained_name)
        self.encoder = SentenceTransformer(pretrained_name)

        self._pretrained_name = pretrained_name

    @property
    def trainable(self) -> bool:
        return False

    @property
    def embedding_size(self) -> int:
        return self.encoder.get_sentence_embedding_dimension()

    def get_collate_fn(self) -> CollateFnType:
        return self.extract_texts

    def extract_texts(self, batch: List[Union[str, Dict[str, Any]]]):
        if isinstance(batch[0], str):
            return batch
        elif isinstance(batch[0], Dict):
            return [item["description"] for item in batch]
        else:
            raise TypeError("Expecting list of strings or dicts as inputs")

    def forward(self, inputs):
        return self.encoder.encode(
            inputs, convert_to_numpy=False, convert_to_tensor=True
        )

    def save(self, output_path: str):
        self.encoder.save(os.path.join(output_path, self._pretrained_name))

    @classmethod
    def load(cls, input_path: str) -> "Encoder":
        return BetterAppEncoder(input_path)

Can you please help me with this since I am stuck here for a while?

monatis commented 1 year ago

Hi @nizamsp, from the error output, I understand that you are using a model from the Transformers package. In this case, you need to save it with their method .save_pretrained() in your custom Encoder implementation and re-load it with .from_pretrained(). If your encoder is not trainable, i.e., if you are training only EncoderHead, then you don't need to save it at all, you can do pass in the .save() method and load the pretrained Transformers model as usual in .load() method of your encoder.

nizamsp commented 1 year ago

Thanks for the quick reply. I saved the model using save_servable and trying to serve it to get embedding via model = SimilarityModel.load(path) and model.encode


    model = Model(num_groups=dataset.get_num_industries(), lr=3e-5)

    train_dataloader = GroupSimilarityDataLoader(dataset, batch_size=64, shuffle=True)

    trainer = pl.Trainer(accelerator="auto", devices=1, num_nodes=1, max_epochs=30)

    Quaterion.fit(
        trainable_model=model,
        trainer=trainer,
        train_dataloader=train_dataloader,
    )

    model.save_servable("/data/betterapp.ai/output/cluster_trainer/quaterion/betterapp")

I am trying to make this code example given here https://github.com/qdrant/quaterion/blob/master/examples/train_startup_search.py to get embeddings after fine tuning.

nizamsp commented 1 year ago

I had to do the following to get the encoding. In this file https://github.com/qdrant/quaterion/blob/master/examples/train_startup_search.py the save and load are inconsistent


    def save(self, output_path: str):
        self.encoder.save(os.path.join(output_path, self._pretrained_name))

    @classmethod
    def load(cls, input_path: str) -> "Encoder":
        return StartupEncoder(input_path)

I changed

        self.encoder.save(os.path.join(output_path, self._pretrained_name))

to

        self.encoder.save(output_path)

Hope I am doing the right fix.

monatis commented 1 year ago

Oh, you're right. This example is from early stages of Quaterion and definitely not the best possible example out there. I'll fix it.

monatis commented 1 year ago

Completed in #194