Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add OpenVINO rerank model support #19791

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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