NVIDIA NIMs
langchain-nvidia-ai-endpoints 包含用于通过NVIDIA NIM推理微服务构建模型应用程序的LangChain集成。NIM支持来自社区和NVIDIA的跨领域模型,如聊天、嵌入和重排序模型。这些模型由NVIDIA优化,以在NVIDIA加速基础设施上提供最佳性能,并作为NIM部署,这是一种易于使用的预构建容器,可以通过在NVIDIA加速基础设施上使用单一命令部署到任何地方。
NVIDIA 托管的 NIM 部署可以在 NVIDIA API 目录 中进行测试。测试完成后, 可以使用 NVIDIA AI Enterprise 许可证从 NVIDIA 的 API 目录中导出 NIM,并在本地或云端运行, 使企业拥有对其知识产权和 AI 应用程序的完全控制权。
NIM 按模型打包为容器镜像,并通过 NVIDIA NGC 目录以 NGC 容器镜像的形式分发。 其核心是,NIM 提供了简单、一致且熟悉的 API,用于在 AI 模型上运行推理。
此示例介绍了如何使用 LangChain 与支持的 NVIDIA 检索 QA 嵌入模型 进行交互,通过 NVIDIAEmbeddings 类实现 检索增强生成。
有关通过此API访问聊天模型的更多信息,请查看ChatNVIDIA文档。
安装
%pip install --upgrade --quiet langchain-nvidia-ai-endpoints
设置
开始使用:
-
使用NVIDIA创建一个免费账户,该账户托管NVIDIA AI基础模型。
-
选择
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目录上使用NIMs
在初始化嵌入模型时,可以通过传递模型来选择模型,例如下面的 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 NIMs 一起工作
当准备部署时,您可以使用 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="http://localhost:8080/v1")
相似性
以下是对这些数据点的相似性进行的快速测试:
Queries:
-
科米塔卡的天气怎么样?
-
意大利以哪些食物闻名?
-
我的名字是什么?我打赌你记不起来了……
-
人生的意义究竟是什么?
-
生活的意义在于享受乐趣。:D
Documents:
-
科姆恰卡的天气寒冷,冬季漫长且严酷。
-
意大利以意大利面、披萨、冰淇淋和浓缩咖啡而闻名。
-
我无法记住个人姓名,只能提供信息。
-
生命的意义各不相同,通常被视为个人的满足感。
-
享受生活中的每一刻确实是一种美好的方式。
嵌入运行时
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()
作为提醒,发送到我们系统的查询和文档是:
Queries:
-
科米塔卡的天气怎么样?
-
意大利以哪些食物闻名?
-
我的名字是什么?我打赌你记不起来了……
-
人生的意义究竟是什么?
-
生活的意义在于享受乐趣。:D
Documents:
-
科姆恰卡的天气寒冷,冬季漫长且严酷。
-
意大利以意大利面、披萨、冰淇淋和浓缩咖啡而闻名。
-
我无法记住个人姓名,只能提供信息。
-
生命的意义各不相同,通常被视为个人的满足感。
-
享受生活中的每一刻确实是一种美好的方式。
截断
嵌入模型通常有一个固定的上下文窗口,这决定了可以嵌入的最大输入标记数。这个限制可能是一个硬性限制,等于模型的最大输入标记长度,或者是一个有效限制,在此之后嵌入的准确性会下降。
由于模型以标记(tokens)为单位运行,而应用程序通常处理文本,因此应用程序很难确保其输入保持在模型的标记限制之内。默认情况下,如果输入过大,会抛出异常。
为了提供帮助,NVIDIA 的 NIMs(API 目录或本地)提供了一个 truncate 参数,如果输入过大,服务器端会对其进行截断。
参数truncate有三个选项:
- "NONE":默认选项。如果输入过大,将抛出异常。
- "START": 服务器从开头(左侧)截断输入,根据需要丢弃标记。
- "END":服务器从输入的末尾(右侧)截断,必要时丢弃标记。
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 表达式检索示例条目 示例的重新利用,但使用了 AI 基础模型中的 Mixtral 8x7B 指令 和 NVIDIA 检索问答嵌入 模型,这些模型可在它们的游乐场环境中使用。示例中的后续示例也按预期运行,我们鼓励您使用这些选项进行探索。
提示: 我们建议使用 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"})