Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
openvino.py352 linesDownload Raw Back to embeddings
1from pathlib import Path2from typing import Any, Dict, List3 4from langchain_core.embeddings import Embeddings5from pydantic import BaseModel, ConfigDict, Field6 7DEFAULT_QUERY_INSTRUCTION = (8    "Represent the question for retrieving supporting documents: "9)10DEFAULT_QUERY_BGE_INSTRUCTION_EN = (11    "Represent this question for searching relevant passages: "12)13DEFAULT_QUERY_BGE_INSTRUCTION_ZH = "为这个句子生成表示以用于检索相关文章:"14 15 16class OpenVINOEmbeddings(BaseModel, Embeddings):17    """OpenVINO embedding models.18 19    Example:20        .. code-block:: python21 22            from langchain_community.embeddings import OpenVINOEmbeddings23 24            model_name = "sentence-transformers/all-mpnet-base-v2"25            model_kwargs = {'device': 'CPU'}26            encode_kwargs = {'normalize_embeddings': True}27            ov = OpenVINOEmbeddings(28                model_name_or_path=model_name,29                model_kwargs=model_kwargs,30                encode_kwargs=encode_kwargs31            )32    """33 34    ov_model: Any = None35    """OpenVINO model object."""36    tokenizer: Any = None37    """Tokenizer for embedding model."""38    model_name_or_path: str39    """HuggingFace model id."""40    model_kwargs: Dict[str, Any] = Field(default_factory=dict)41    """Keyword arguments to pass to the model."""42    encode_kwargs: Dict[str, Any] = Field(default_factory=dict)43    """Keyword arguments to pass when calling the `encode` method of the model."""44    show_progress: bool = False45    """Whether to show a progress bar."""46 47    def __init__(self, **kwargs: Any):48        """Initialize the sentence_transformer."""49        super().__init__(**kwargs)50 51        try:52            from optimum.intel.openvino import OVModelForFeatureExtraction53        except ImportError as e:54            raise ImportError(55                "Could not import optimum-intel python package. "56                "Please install it with: "57                "pip install -U 'optimum[openvino,nncf]'"58            ) from e59 60        try:61            from huggingface_hub import HfApi62        except ImportError as e:63            raise ImportError(64                "Could not import huggingface_hub python package. "65                "Please install it with: "66                "`pip install -U huggingface_hub`."67            ) from e68 69        def require_model_export(70            model_id: str, revision: Any = None, subfolder: Any = None71        ) -> bool:72            model_dir = Path(model_id)73            if subfolder is not None:74                model_dir = model_dir / subfolder75            if model_dir.is_dir():76                return (77                    not (model_dir / "openvino_model.xml").exists()78                    or not (model_dir / "openvino_model.bin").exists()79                )80            hf_api = HfApi()81            try:82                model_info = hf_api.model_info(model_id, revision=revision or "main")83                normalized_subfolder = (84                    None if subfolder is None else Path(subfolder).as_posix()85                )86                model_files = [87                    file.rfilename88                    for file in model_info.siblings89                    if normalized_subfolder is None90                    or file.rfilename.startswith(normalized_subfolder)91                ]92                ov_model_path = (93                    "openvino_model.xml"94                    if subfolder is None95                    else f"{normalized_subfolder}/openvino_model.xml"96                )97                return (98                    ov_model_path not in model_files99                    or ov_model_path.replace(".xml", ".bin") not in model_files100                )101            except Exception:102                return True103 104        if require_model_export(self.model_name_or_path):105            # use remote model106            self.ov_model = OVModelForFeatureExtraction.from_pretrained(107                self.model_name_or_path, export=True, **self.model_kwargs108            )109        else:110            # use local model111            self.ov_model = OVModelForFeatureExtraction.from_pretrained(112                self.model_name_or_path, **self.model_kwargs113            )114 115        try:116            from transformers import AutoTokenizer117        except ImportError as e:118            raise ImportError(119                "Unable to import transformers, please install with "120                "`pip install -U transformers`."121            ) from e122        self.tokenizer = AutoTokenizer.from_pretrained(self.model_name_or_path)123 124    def _text_length(self, text: Any) -> int:125        """126        Help function to get the length for the input text. Text can be either127        a list of ints (which means a single text as input), or a tuple of list of ints128        (representing several text inputs to the model).129        """130 131        if isinstance(text, dict):  # {key: value} case132            return len(next(iter(text.values())))133        elif not hasattr(text, "__len__"):  # Object has no len() method134            return 1135        # Empty string or list of ints136        elif len(text) == 0 or isinstance(text[0], int):137            return len(text)138        else:139            # Sum of length of individual strings140            return sum([len(t) for t in text])141 142    def encode(143        self,144        sentences: Any,145        batch_size: int = 4,146        show_progress_bar: bool = False,147        convert_to_numpy: bool = True,148        convert_to_tensor: bool = False,149        mean_pooling: bool = False,150        normalize_embeddings: bool = True,151    ) -> Any:152        """153        Computes sentence embeddings.154 155        :param sentences: the sentences to embed.156        :param batch_size: the batch size used for the computation.157        :param show_progress_bar: Whether to output a progress bar.158        :param convert_to_numpy: Whether the output should be a list of numpy vectors.159        :param convert_to_tensor: Whether the output should be one large tensor.160        :param mean_pooling: Whether to pool returned vectors.161        :param normalize_embeddings: Whether to normalize returned vectors.162 163        :return: By default, a 2d numpy array with shape [num_inputs, output_dimension].164        """165        try:166            import numpy as np167        except ImportError as e:168            raise ImportError(169                "Unable to import numpy, please install with `pip install -U numpy`."170            ) from e171        try:172            from tqdm import trange173        except ImportError as e:174            raise ImportError(175                "Unable to import tqdm, please install with `pip install -U tqdm`."176            ) from e177        try:178            import torch179        except ImportError as e:180            raise ImportError(181                "Unable to import torch, please install with `pip install -U torch`."182            ) from e183 184        def run_mean_pooling(model_output: Any, attention_mask: Any) -> Any:185            token_embeddings = model_output[186                0187            ]  # First element of model_output contains all token embeddings188            input_mask_expanded = (189                attention_mask.unsqueeze(-1).expand(token_embeddings.size()).float()190            )191            return torch.sum(token_embeddings * input_mask_expanded, 1) / torch.clamp(192                input_mask_expanded.sum(1), min=1e-9193            )194 195        if convert_to_tensor:196            convert_to_numpy = False197 198        input_was_string = False199        if isinstance(sentences, str) or not hasattr(200            sentences, "__len__"201        ):  # Cast an individual sentence to a list with length 1202            sentences = [sentences]203            input_was_string = True204 205        all_embeddings: Any = []206        length_sorted_idx = np.argsort([-self._text_length(sen) for sen in sentences])207        sentences_sorted = [sentences[idx] for idx in length_sorted_idx]208 209        for start_index in trange(210            0, len(sentences), batch_size, desc="Batches", disable=not show_progress_bar211        ):212            sentences_batch = sentences_sorted[start_index : start_index + batch_size]213 214            length = self.ov_model.request.inputs[0].get_partial_shape()[1]215            if length.is_dynamic:216                features = self.tokenizer(217                    sentences_batch, padding=True, truncation=True, return_tensors="pt"218                )219            else:220                features = self.tokenizer(221                    sentences_batch,222                    padding="max_length",223                    max_length=length.get_length(),224                    truncation=True,225                    return_tensors="pt",226                )227 228            out_features = self.ov_model(**features)229            if mean_pooling:230                embeddings = run_mean_pooling(out_features, features["attention_mask"])231            else:232                embeddings = out_features[0][:, 0]233            if normalize_embeddings:234                embeddings = torch.nn.functional.normalize(embeddings, p=2, dim=1)235 236            # fixes for #522 and #487 to avoid oom problems on gpu with large datasets237            if convert_to_numpy:238                embeddings = embeddings.cpu()239 240            all_embeddings.extend(embeddings)241 242        all_embeddings = [all_embeddings[idx] for idx in np.argsort(length_sorted_idx)]243 244        if convert_to_tensor:245            if len(all_embeddings):246                all_embeddings = torch.stack(all_embeddings)247            else:248                all_embeddings = torch.Tensor()249        elif convert_to_numpy:250            all_embeddings = np.asarray([emb.numpy() for emb in all_embeddings])251 252        if input_was_string:253            all_embeddings = all_embeddings[0]254 255        return all_embeddings256 257    model_config = ConfigDict(extra="forbid", protected_namespaces=())258 259    def embed_documents(self, texts: List[str]) -> List[List[float]]:260        """Compute doc embeddings using a HuggingFace transformer model.261 262        Args:263            texts: The list of texts to embed.264 265        Returns:266            List of embeddings, one for each text.267        """268 269        texts = list(map(lambda x: x.replace("\n", " "), texts))270        embeddings = self.encode(271            texts, show_progress_bar=self.show_progress, **self.encode_kwargs272        )273 274        return embeddings.tolist()275 276    def embed_query(self, text: str) -> List[float]:277        """Compute query embeddings using a HuggingFace transformer model.278 279        Args:280            text: The text to embed.281 282        Returns:283            Embeddings for the text.284        """285        return self.embed_documents([text])[0]286 287    def save_model(288        self,289        model_path: str,290    ) -> bool:291        self.ov_model.half()292        self.ov_model.save_pretrained(model_path)293        self.tokenizer.save_pretrained(model_path)294        return True295 296 297class OpenVINOBgeEmbeddings(OpenVINOEmbeddings):298    """OpenVNO BGE embedding models.299 300    Bge Example:301        .. code-block:: python302 303            from langchain_community.embeddings import OpenVINOBgeEmbeddings304 305            model_name = "BAAI/bge-large-en-v1.5"306            model_kwargs = {'device': 'CPU'}307            encode_kwargs = {'normalize_embeddings': True}308            ov = OpenVINOBgeEmbeddings(309                model_name_or_path=model_name,310                model_kwargs=model_kwargs,311                encode_kwargs=encode_kwargs312            )313    """314 315    query_instruction: str = DEFAULT_QUERY_BGE_INSTRUCTION_EN316    """Instruction to use for embedding query."""317    embed_instruction: str = ""318    """Instruction to use for embedding document."""319 320    def __init__(self, **kwargs: Any):321        """Initialize the sentence_transformer."""322        super().__init__(**kwargs)323 324        if "-zh" in self.model_name_or_path:325            self.query_instruction = DEFAULT_QUERY_BGE_INSTRUCTION_ZH326 327    def embed_documents(self, texts: List[str]) -> List[List[float]]:328        """Compute doc embeddings using a HuggingFace transformer model.329 330        Args:331            texts: The list of texts to embed.332 333        Returns:334            List of embeddings, one for each text.335        """336        texts = [self.embed_instruction + t.replace("\n", " ") for t in texts]337        embeddings = self.encode(texts, **self.encode_kwargs)338        return embeddings.tolist()339 340    def embed_query(self, text: str) -> List[float]:341        """Compute query embeddings using a HuggingFace transformer model.342 343        Args:344            text: The text to embed.345 346        Returns:347            Embeddings for the text.348        """349        text = text.replace("\n", " ")350        embedding = self.encode(self.query_instruction + text, **self.encode_kwargs)351        return embedding.tolist()352 
codekingpro/portable-devtools · Team Ai