NVIDIA NIMs
langchain-nvidia-ai-endpoints
套件包含 LangChain 整合,可使用 NVIDIA NIM 推論微服務上的模型建置應用程式。 NIM 支援來自社群以及 NVIDIA 的聊天、嵌入和重新排序等領域的模型。 這些模型經過 NVIDIA 最佳化,可在 NVIDIA 加速基礎架構上提供最佳效能,並部署為 NIM,這是一種易於使用、預先建置的容器,可使用單一命令在 NVIDIA 加速基礎架構上隨處部署。
NVIDIA 託管的 NIM 部署可用於在 NVIDIA API 目錄上進行測試。 測試後,可以使用 NVIDIA AI Enterprise 授權從 NVIDIA 的 API 目錄匯出 NIM,並在內部部署或雲端中執行,讓企業擁有其 IP 和 AI 應用程式的所有權和完全控制權。
NIM 以每個模型為基礎封裝為容器映像,並作為 NGC 容器映像透過 NVIDIA NGC 目錄分發。 NIM 的核心是為在 AI 模型上執行推論提供簡單、一致且熟悉的 API。
此範例說明如何使用 LangChain 與支援的 NVIDIA Retrieval QA 嵌入模型,透過 NVIDIAEmbeddings
類別進行 檢索增強生成 (retrieval-augmented generation) 。
有關透過此 API 存取聊天模型的更多資訊,請查看 ChatNVIDIA 文件。
安裝
%pip install --upgrade --quiet langchain-nvidia-ai-endpoints
設定
開始使用
-
在 NVIDIA 建立一個免費帳戶,該帳戶託管 NVIDIA AI Foundation 模型。
-
選擇
Retrieval
標籤,然後選擇您選擇的模型。 -
在
Input
下選擇Python
標籤,然後按一下Get API Key
。 然後按一下Generate Key
。 -
複製並儲存產生的金鑰作為
NVIDIA_API_KEY
。 從那裡,您應該可以存取端點。
import getpass
import os
# del os.environ['NVIDIA_API_KEY'] ## delete key and reset
if os.environ.get("NVIDIA_API_KEY", "").startswith("nvapi-"):
print("Valid NVIDIA_API_KEY already in environment. Delete to reset")
else:
nvapi_key = getpass.getpass("NVAPI Key (starts with nvapi-): ")
assert nvapi_key.startswith("nvapi-"), f"{nvapi_key[:5]}... is not a valid key"
os.environ["NVIDIA_API_KEY"] = nvapi_key
我們應該能夠在列表中看到一個嵌入模型,該模型可以與 LLM 結合使用,以實現有效的 RAG 解決方案。 我們可以透過 NVIDIAEmbeddings
類別與此模型以及 NIM 支援的其他嵌入模型進行互動。
在 NVIDIA API 目錄中使用 NIM
初始化嵌入模型時,您可以透過傳遞模型來選擇模型,例如下面的 NV-Embed-QA
,或者透過不傳遞任何參數來使用預設模型。
from langchain_nvidia_ai_endpoints import NVIDIAEmbeddings
embedder = NVIDIAEmbeddings(model="NV-Embed-QA")
此模型是一個微調的 E5-large 模型,支援預期的 Embeddings
方法,包括
-
embed_query
:為查詢樣本產生查詢嵌入。 -
embed_documents
:為您想要搜尋的文件清單產生段落嵌入。 -
aembed_query
/aembed_documents
:上述項目的非同步版本。
使用自行託管的 NVIDIA NIM
準備好部署時,您可以使用 NVIDIA NIM(包含在 NVIDIA AI Enterprise 軟體授權中)自行託管模型,並在任何地方執行它們,讓您擁有自訂的所有權,並完全控制您的智慧財產權 (IP) 和 AI 應用程式。
from langchain_nvidia_ai_endpoints import NVIDIAEmbeddings
# connect to an embedding NIM running at localhost:8080
embedder = NVIDIAEmbeddings(base_url="https://127.0.0.1:8080/v1")
相似度
以下是這些資料點的相似度快速測試
查詢
-
堪察加半島的天氣如何?
-
義大利以哪些食物聞名?
-
我叫什麼名字? 我打賭你不記得了...
-
人生的意義到底是什麼?
-
人生的意義是享受樂趣:D
文件
-
堪察加半島的天氣寒冷,冬季漫長而嚴峻。
-
義大利以義大利麵、披薩、義式冰淇淋和義式濃縮咖啡而聞名。
-
我不記得個人姓名,只提供資訊。
-
人生的目的各不相同,通常被視為個人成就。
-
享受人生的時刻確實是一種美好的方法。
嵌入執行階段
print("\nSequential Embedding: ")
q_embeddings = [
embedder.embed_query("What's the weather like in Komchatka?"),
embedder.embed_query("What kinds of food is Italy known for?"),
embedder.embed_query("What's my name? I bet you don't remember..."),
embedder.embed_query("What's the point of life anyways?"),
embedder.embed_query("The point of life is to have fun :D"),
]
print("Shape:", (len(q_embeddings), len(q_embeddings[0])))
文件嵌入
print("\nBatch Document Embedding: ")
d_embeddings = embedder.embed_documents(
[
"Komchatka's weather is cold, with long, severe winters.",
"Italy is famous for pasta, pizza, gelato, and espresso.",
"I can't recall personal names, only provide information.",
"Life's purpose varies, often seen as personal fulfillment.",
"Enjoying life's moments is indeed a wonderful approach.",
]
)
print("Shape:", (len(q_embeddings), len(q_embeddings[0])))
現在我們已經產生了嵌入,我們可以在結果上執行一個簡單的相似度檢查,以查看哪些文件會在檢索任務中觸發為合理的答案
%pip install --upgrade --quiet matplotlib scikit-learn
import matplotlib.pyplot as plt
import numpy as np
from sklearn.metrics.pairwise import cosine_similarity
# Compute the similarity matrix between q_embeddings and d_embeddings
cross_similarity_matrix = cosine_similarity(
np.array(q_embeddings),
np.array(d_embeddings),
)
# Plotting the cross-similarity matrix
plt.figure(figsize=(8, 6))
plt.imshow(cross_similarity_matrix, cmap="Greens", interpolation="nearest")
plt.colorbar()
plt.title("Cross-Similarity Matrix")
plt.xlabel("Query Embeddings")
plt.ylabel("Document Embeddings")
plt.grid(True)
plt.show()
提醒一下,發送到我們系統的查詢和文件如下
查詢
-
堪察加半島的天氣如何?
-
義大利以哪些食物聞名?
-
我叫什麼名字? 我打賭你不記得了...
-
人生的意義到底是什麼?
-
人生的意義是享受樂趣:D
文件
-
堪察加半島的天氣寒冷,冬季漫長而嚴峻。
-
義大利以義大利麵、披薩、義式冰淇淋和義式濃縮咖啡而聞名。
-
我不記得個人姓名,只提供資訊。
-
人生的目的各不相同,通常被視為個人成就。
-
享受人生的時刻確實是一種美好的方法。
截斷
嵌入模型通常有一個固定的上下文視窗,該視窗決定了可以嵌入的最大輸入 token 數量。 此限制可能是一個硬性限制,等於模型的最大輸入 token 長度,或者是一個有效限制,超過此限制,嵌入的準確性會降低。
由於模型基於 token 運作,而應用程式通常處理文字,因此應用程式很難確保其輸入保持在模型的 token 限制內。 預設情況下,如果輸入太大,則會擲回異常。
為了協助解決此問題,NVIDIA 的 NIM(API 目錄或本地)提供了一個 truncate
參數,如果輸入太大,則會在伺服器端截斷輸入。
truncate
參數有三個選項
- "NONE": 預設選項。 如果輸入太大,則會擲回異常。
- "START": 伺服器從開始 (左側) 截斷輸入,根據需要捨棄 token。
- "END": 伺服器從結尾 (右側) 截斷輸入,根據需要捨棄 token。
long_text = "AI is amazing, amazing is " * 100
strict_embedder = NVIDIAEmbeddings()
try:
strict_embedder.embed_query(long_text)
except Exception as e:
print("Error:", e)
truncating_embedder = NVIDIAEmbeddings(truncate="END")
truncating_embedder.embed_query(long_text)[:5]
RAG 檢索:
以下是 LangChain Expression Language Retrieval Cookbook entry 初始範例的重新利用,但使用 AI Foundation Models 的 Mixtral 8x7B Instruct 和 NVIDIA Retrieval QA Embedding 模型在其 playground 環境中執行。 cookbook 中的後續範例也會如預期般執行,我們鼓勵您探索這些選項。
提示: 我們建議使用 Mixtral 進行內部推理 (即,用於資料提取、工具選擇等的指令遵循),並使用 Llama-Chat 做出一個簡單的最終「根據歷史記錄和上下文為該使用者製作適用的回應」的回應。
%pip install --upgrade --quiet langchain faiss-cpu tiktoken langchain_community
from operator import itemgetter
from langchain_community.vectorstores import FAISS
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_nvidia_ai_endpoints import ChatNVIDIA
vectorstore = FAISS.from_texts(
["harrison worked at kensho"],
embedding=NVIDIAEmbeddings(model="NV-Embed-QA"),
)
retriever = vectorstore.as_retriever()
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"Answer solely based on the following context:\n<Documents>\n{context}\n</Documents>",
),
("user", "{question}"),
]
)
model = ChatNVIDIA(model="ai-mixtral-8x7b-instruct")
chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| model
| StrOutputParser()
)
chain.invoke("where did harrison work?")
prompt = ChatPromptTemplate.from_messages(
[
(
"system",
"Answer using information solely based on the following context:\n<Documents>\n{context}\n</Documents>"
"\nSpeak only in the following language: {language}",
),
("user", "{question}"),
]
)
chain = (
{
"context": itemgetter("question") | retriever,
"question": itemgetter("question"),
"language": itemgetter("language"),
}
| prompt
| model
| StrOutputParser()
)
chain.invoke({"question": "where did harrison work", "language": "italian"})