Skip to content

Commit

Permalink
community[minor]: Add OpenVINO rerank model support (#19791)
Browse files Browse the repository at this point in the history
@eaidova @AlexKoff88 Could you help to review, thanks

---------

Co-authored-by: Bagatur <baskaryan@gmail.com>
  • Loading branch information
2 people authored and hinthornw committed Apr 26, 2024
1 parent 36683f2 commit df4f22e
Show file tree
Hide file tree
Showing 6 changed files with 827 additions and 5 deletions.
622 changes: 622 additions & 0 deletions docs/docs/integrations/document_transformers/openvino_rerank.ipynb

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions docs/docs/integrations/llms/openvino.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"id": "959300d4",
"metadata": {},
"source": [
"# OpenVINO Local Pipelines\n",
"# OpenVINO\n",
"\n",
"[OpenVINO™](https://github.com/openvinotoolkit/openvino) is an open-source toolkit for optimizing and deploying AI inference. OpenVINO™ Runtime can enable running the same model optimized across various hardware [devices](https://github.com/openvinotoolkit/openvino?tab=readme-ov-file#supported-hardware-matrix). Accelerate your deep learning performance across use cases like: language + LLMs, computer vision, automatic speech recognition, and more.\n",
"\n",
Expand Down Expand Up @@ -229,7 +229,7 @@
"\n",
"* [OpenVINO Get Started Guide](https://www.intel.com/content/www/us/en/content-details/819067/openvino-get-started-guide.html).\n",
" \n",
"* [RAG Notebook with LangChain](https://github.com/openvinotoolkit/openvino_notebooks/tree/master/notebooks/llm-chatbot)."
"* [RAG Notebook with LangChain](https://github.com/openvinotoolkit/openvino_notebooks/tree/latest/notebooks/llm-chatbot)."
]
}
],
Expand Down
48 changes: 47 additions & 1 deletion docs/docs/integrations/text_embedding/openvino.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"id": "ed47bb62",
"metadata": {},
"source": [
"# OpenVINO Local Pipelines\n",
"# OpenVINO\n",
"[OpenVINO™](https://github.com/openvinotoolkit/openvino) is an open-source toolkit for optimizing and deploying AI inference. The OpenVINO™ Runtime supports various hardware [devices](https://github.com/openvinotoolkit/openvino?tab=readme-ov-file#supported-hardware-matrix) including x86 and ARM CPUs, and Intel GPUs. It can help to boost deep learning performance in Computer Vision, Automatic Speech Recognition, Natural Language Processing and other common tasks.\n",
"\n",
"Hugging Face embedding model can be supported by OpenVINO through ``OpenVINOEmbeddings`` class. If you have an Intel GPU, you can specify `model_kwargs={\"device\": \"GPU\"}` to run inference on it."
Expand Down Expand Up @@ -139,6 +139,52 @@
"doc_result = ov_embeddings.embed_documents([text])"
]
},
{
"cell_type": "markdown",
"id": "9a6da5ba",
"metadata": {},
"source": [
"## Export IR model\n",
"It is possible to export your embedding model to the OpenVINO IR format with ``OVModelForFeatureExtraction``, and load the model from local folder."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a6544a65",
"metadata": {},
"outputs": [],
"source": [
"from pathlib import Path\n",
"\n",
"ov_model_dir = \"all-mpnet-base-v2-ov\"\n",
"if not Path(ov_model_dir).exists():\n",
" from optimum.intel.openvino import OVModelForFeatureExtraction\n",
" from transformers import AutoTokenizer\n",
"\n",
" ov_model = OVModelForFeatureExtraction.from_pretrained(\n",
" model_name, compile=False, export=True\n",
" )\n",
" tokenizer = AutoTokenizer.from_pretrained(model_name)\n",
" ov_model.half()\n",
" ov_model.save_pretrained(ov_model_dir)\n",
" tokenizer.save_pretrained(ov_model_dir)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "162004c4",
"metadata": {},
"outputs": [],
"source": [
"ov_embeddings = OpenVINOEmbeddings(\n",
" model_name_or_path=ov_model_dir,\n",
" model_kwargs=model_kwargs,\n",
" encode_kwargs=encode_kwargs,\n",
")"
]
},
{
"cell_type": "markdown",
"id": "92019ef1-5d30-4985-b4e6-c0d98bdfe265",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

_module_lookup = {
"LLMLinguaCompressor": "langchain_community.document_compressors.llmlingua_filter",
"OpenVINOReranker": "langchain_community.document_compressors.openvino_rerank",
}


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
from pathlib import Path
from typing import Any, Dict, Optional, Sequence

import numpy as np
from langchain_core.callbacks import Callbacks
from langchain_core.documents import Document
from langchain_core.documents.compressor import BaseDocumentCompressor
from langchain_core.pydantic_v1 import Field


class RerankRequest:
def __init__(self, query: Any = None, passages: Any = None):
self.query = query
self.passages = passages if passages is not None else []


class OpenVINOReranker(BaseDocumentCompressor):
"""
OpenVINO rerank models.
"""

ov_model: Any
"""OpenVINO model object."""
tokenizer: Any
"""Tokenizer for embedding model."""
model_name_or_path: str
"""HuggingFace model id."""
model_kwargs: Dict[str, Any] = Field(default_factory=dict)
"""Keyword arguments passed to the model."""
top_n: int = 4
"""return Top n texts."""

def __init__(self, **kwargs: Any):
super().__init__(**kwargs)

try:
from optimum.intel.openvino import OVModelForSequenceClassification
except ImportError as e:
raise ValueError(
"Could not import optimum-intel python package. "
"Please install it with: "
"pip install -U 'optimum[openvino,nncf]'"
) from e

try:
from huggingface_hub import HfApi
except ImportError as e:
raise ValueError(
"Could not import huggingface_hub python package. "
"Please install it with: "
"`pip install -U huggingface_hub`."
) from e

def require_model_export(
model_id: str, revision: Any = None, subfolder: Any = None
) -> bool:
model_dir = Path(model_id)
if subfolder is not None:
model_dir = model_dir / subfolder
if model_dir.is_dir():
return (
not (model_dir / "openvino_model.xml").exists()
or not (model_dir / "openvino_model.bin").exists()
)
hf_api = HfApi()
try:
model_info = hf_api.model_info(model_id, revision=revision or "main")
normalized_subfolder = (
None if subfolder is None else Path(subfolder).as_posix()
)
model_files = [
file.rfilename
for file in model_info.siblings
if normalized_subfolder is None
or file.rfilename.startswith(normalized_subfolder)
]
ov_model_path = (
"openvino_model.xml"
if subfolder is None
else f"{normalized_subfolder}/openvino_model.xml"
)
return (
ov_model_path not in model_files
or ov_model_path.replace(".xml", ".bin") not in model_files
)
except Exception:
return True

if require_model_export(self.model_name_or_path):
# use remote model
self.ov_model = OVModelForSequenceClassification.from_pretrained(
self.model_name_or_path, export=True, **self.model_kwargs
)
else:
# use local model
self.ov_model = OVModelForSequenceClassification.from_pretrained(
self.model_name_or_path, **self.model_kwargs
)

try:
from transformers import AutoTokenizer
except ImportError as e:
raise ImportError(
"Unable to import transformers, please install with "
"`pip install -U transformers`."
) from e

self.tokenizer = AutoTokenizer.from_pretrained(self.model_name_or_path)

def rerank(self, request: Any) -> Any:
query = request.query
passages = request.passages

query_passage_pairs = [[query, passage["text"]] for passage in passages]
input_tensors = self.tokenizer(
query_passage_pairs, padding=True, truncation=True, return_tensors="pt"
)

outputs = self.ov_model(**input_tensors, return_dict=True)
if outputs[0].shape[1] > 1:
scores = outputs[0][:, 1]
else:
scores = outputs[0].flatten()

scores = list(1 / (1 + np.exp(-scores)))

# Combine scores with passages, including metadata
for score, passage in zip(scores, passages):
passage["score"] = score

# Sort passages based on scores
passages.sort(key=lambda x: x["score"], reverse=True)

return passages

def compress_documents(
self,
documents: Sequence[Document],
query: str,
callbacks: Optional[Callbacks] = None,
) -> Sequence[Document]:
passages = [
{"id": i, "text": doc.page_content} for i, doc in enumerate(documents)
]

rerank_request = RerankRequest(query=query, passages=passages)
rerank_response = self.rerank(rerank_request)[: self.top_n]
final_results = []
for r in rerank_response:
doc = Document(
page_content=r["text"],
metadata={"id": r["id"], "relevance_score": r["score"]},
)
final_results.append(doc)
return final_results
2 changes: 0 additions & 2 deletions libs/community/langchain_community/embeddings/openvino.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
class OpenVINOEmbeddings(BaseModel, Embeddings):
"""OpenVINO embedding models.
To use, you should have the ``sentence_transformers`` python package installed.
Example:
.. code-block:: python
Expand Down

0 comments on commit df4f22e

Please sign in to comment.