一步一步教你利用DeepSeek构建一个强大的中文新闻RAG AI应用
推荐超级课程:
@TOC
我将教你利用DeepSeek和Qdrant构建一个中文新闻AI。
尽管LLM的规模和复杂性日益庞大,但在构建一个精准有效的语言AI(不仅限于英语)方面仍然存在诸多挑战。
为此,DeepSeek LLM启动了一个长期项目,旨在克服语言应用中的不准确性。DeepSeek LLM在中文方面表现出色,我们将在这里看看它在从中文新闻数据集中获取新闻时的表现。
我们将利用LlamaIndex、Qdrant的FastEmbed和Qdrant Vector Store开发一个应用程序,该应用程序可以通过强大的RAG理解中文新闻。
首先简单画一下我们的处理流程架构:
前提条件
硬件要求:
- 内存: 16GB+
- CPU: 4+
- Disk: 100GB+
软件要求:
- Python: 3.11
DeepSeek LLM开源语言模型
DeepSeek LLM 是一个先进的语言模型,已开发出包括 Base 和 Chat 在内的多种模型。它基于包含 2 万亿个中英文词条的数据集从零开始训练。就规模而言,DeepSeek LLM 模型有两种:一种包含 70 亿个参数,另一种包含 670 亿个参数。
7B 模型使用多头注意力机制,而 67B 模型使用分组查询注意力机制。这些变体与 Llama 2 模型采用相同的架构,后者是一个自回归 Transformer 解码器模型。
DeepSeek LLM 是一个致力于长远推进开源大型语言模型的项目。然而,DeepSeek LLM 67B 在推理、数学、编码和理解等多个领域均优于 Llama 2 70B。与包括 GPT 3.5 在内的其他模型相比,DeepSeek LLM 在中文能力方面更胜一筹。
DeepSeek LLM 的对齐流程由两个阶段组成:
- 监督微调: 7B 模型微调 4 个 epoch,而 67B 模型微调 2 个 epoch。在监督微调期间,7B 模型的学习率为 1e-5,而 67B 模型的学习率为 5e-6。尽管数学 SFT 数据在推理中包含类似的模式,但模型的重复率往往会随着数学 SFT 数据量的增加而增加。
- **直接偏好优化:**为了解决重复问题,我们通过 DPO 训练增强了模型的能力,这被证明是一种有效的 LLM 对齐方法。DPO 训练的偏好数据是根据有用性和无害性构建的。
该模型及其所有变体均可在Hugging Face 上获取。 如需了解更多关于 DeepSeek LLM 的信息,请访问其论文 和Github 仓库 。
Qdrant:高性能矢量数据库
Qdrant 是一个用 Rust 编写的开源向量数据库和向量相似性搜索引擎,旨在通过先进且高性能的向量相似性搜索技术赋能下一代 AI 应用。其主要功能包括多语言支持(可灵活处理各种数据类型)以及适用于各种应用程序的过滤器。
Qdrant 通过定制修改的近似最近邻搜索 (HNSW) 算法,实现了速度和准确性,从而确保了其在保持精确结果的同时,拥有一流的搜索能力。此外,它还支持与向量相关的额外负载,允许根据负载值筛选结果。Qdrant 支持丰富的数据类型和查询条件,包括字符串匹配、数值范围和地理位置,从而提供灵活的数据管理功能。
Qdrant 是一个云原生且可水平扩展的平台,它通过动态查询规划和有效负载数据索引,高效利用计算资源,从而高效地处理数据扩展需求。其应用范围包括语义文本搜索、推荐、用户行为分析等,它提供可立即投入生产的服务,并提供便捷的 API 用于存储、搜索和管理向量以及其他有效负载。更多详细信息,请参阅Qdrant 文档, 其中包含安装、使用、教程和示例的全面指南。
利用 FastEmbed 进行轻量级嵌入生成
FastEmbed 是一个轻量级、快速且准确的 Python 库,专为嵌入生成而构建,由Qdrant 负责维护。它通过利用量化模型权重和 ONNX Runtime 来实现效率和速度,从而避免了对 PyTorch 的依赖。
FastEmbed 支持数据并行,可高效编码海量数据集,并采用以 CPU 为中心的设计方法。此外,FastEmbed 的准确率和召回率指标均优于 OpenAI Ada-002,其默认模型为 Flag Embedding,在 MTEB 排行榜上名列前茅。此外,它还支持 Jina Embedding 和 Text Embedding。FastEmbed 支持多种常用的文本模型。如需了解更多关于所支持模型的信息,请访问此处 。
强大的 RAG 的 LlamaIndex 框架
此外,LlamaIndex 优先优化性能,提供一系列策略来有效增强 RAG 流程。它旨在通过减少幻觉来提高复杂数据集的检索和生成准确性。LlamaIndex 支持多种嵌入模型,并可与大型语言模型集成。此外,它还能与 LangChain、Flask 和 Docker 等成熟的技术平台无缝集成,并提供自定义选项,例如使用自定义摘要提示构建种子树。
使用 DeepSeek 理解新闻系统
由于 DeepSeek LLM 在中文能力方面表现出色,让我们使用检索增强生成来构建中文新闻 AI。
首先,让我们安装所有依赖项。
!pip install -q llama-index==0.12.31
!pip install -q transformers==4.51.3
!pip install -q datasets==3.5.0
!pip install -q llama-cpp-python==0.3.8
!pip install -q qdrant-client==1.13.3
!pip install -q llama_hub==0.0.79
!pip install -q llama-index-embeddings-fastembed==0.3.1
!pip install -q fastembed==0.6.1
!pip install -q llama-index-llms-huggingface==0.5.0
!pip install -q llama-index-vector-stores-qdrant==0.6.0
!pip install -q llama-index-readers-file==0.4.7
!pip install -q torch==2.6.0
这里我使用了此数据集 ;它是一个多语言新闻数据集。我选择了中文。加载数据集并将其保存到您的目录中。我们将使用 LlamaIndex 和 SimpleDirectoryReader 读取数据。
# 原始加载可能会很大,之后需要很大16GB左右内存才可以,所以,在此我只选择1000个样本,
# 来减小内存体积。
from datasets import load_dataset
# 只加载zh中文部分训练数据集
dataset = load_dataset("intfloat/multilingual_cc_news", languages=["zh"], split="train")
# 选取前 10 个样本
n_samples = 10
small_dataset = dataset.select(range(n_samples))
print(f"原始数据集样本数: {len(dataset)}")
print(f"选取后数据集样本数: {len(small_dataset)}")
# 你现在可以使用 small_dataset 进行操作或保存
small_dataset.save_to_disk("Notebooks/dataset_small")
现在,使用 LlamaIndex 从我们保存数据集的目录加载数据。
from llama_index.core import SimpleDirectoryReader
documents = SimpleDirectoryReader("Notebooks/dataset_small").load_data()
数据集目录:
之后,使用 SentenceSplitter 将文档拆分成小块。在这里,我们需要维护文档与源文档索引之间的关系,以便于注入文档元数据。
from llama_index.core.node_parser.text import SentenceSplitter
text_parser = SentenceSplitter(chunk_size=1024,)
text_chunks = []
doc_idxs = []
for doc_idx, doc in enumerate(documents):
cur_text_chunks = text_parser.split_text(doc.text)
text_chunks.extend(cur_text_chunks)
doc_idxs.extend([doc_idx] * len(cur_text_chunks))
然后,我们将手动从文本块构建节点。
from llama_index.core.schema import TextNode
nodes = []
for idx, text_chunk in enumerate(text_chunks):
node = TextNode(text=text_chunk,)
src_doc = documents[doc_idxs[idx]]
node.metadata = src_doc.metadata
nodes.append(node)
对于每个节点,我们将使用 FastEmbed 嵌入模型生成嵌入。
from llama_index.embeddings.fastembed import FastEmbedEmbedding
embed_model = FastEmbedEmbedding(model_name="BAAI/bge-small-en-v1.5")
for node in nodes:
node_embedding = embed_model.get_text_embedding(node.get_content(metadata_mode="all"))
node.embedding = node_embedding
现在,是时候使用 LlamaIndex 的 HuggingFaceLLM 加载 DeepSeek LLM 了。这里我使用了聊天模型
from llama_index.llms.huggingface import HuggingFaceLLM
import torch
offload_directory = "offloads"
llm = HuggingFaceLLM(
context_window=4096,
max_new_tokens=256,
generate_kwargs={"temperature": 0.7, "do_sample": False},
tokenizer_name="deepseek-ai/deepseek-llm-7b-chat",
model_name="deepseek-ai/deepseek-llm-7b-chat",
device_map="auto",
stopping_ids=[50278, 50279, 50277, 1, 0],
tokenizer_kwargs={"max_length": 4096},
model_kwargs={"torch_dtype": torch.float16, "offload_folder": offload_directory}
)
这步会很占用内存和Disk,如果内存低于16GB会很容易崩溃哦!!! 你可能需要重复执行多次,才能完成成功!!
然后,我们将llm和嵌入模型设置到llama设置中。
from llama_index.core import Settings
Settings.llm = llm
Settings.embed_model = embed_model
之后,我们将使用 Qdrant 向量数据库创建一个向量存储集合,并使用该向量存储集合创建一个存储上下文。
import qdrant_client
from llama_index.vector_stores.qdrant import QdrantVectorStore
client = qdrant_client.QdrantClient(location=":memory:")
from llama_index.core.storage.storage_context import StorageContext
vector_store = QdrantVectorStore(client=client, collection_name="my_collection")
vector_store.add(nodes)
storage_context = StorageContext.from_defaults(vector_store=vector_store)
我们将把文档传递到 VectorStoreIndex。
from llama_index.core import VectorStoreIndex
index = VectorStoreIndex.from_documents(
documents, embed_model=embed_model, storage_context=storage_context,
)
我们将使用查询字符串生成查询嵌入来构建检索管道。
query_str = "Can you give me news around Money?"
query_embedding = embed_model.get_query_embedding(query_str)
然后,我们将构建一个 Vector Store 查询并查询向量数据库。
from llama_index.core.vector_stores import VectorStoreQuery
query_mode = "default"
vector_store_query = VectorStoreQuery(query_embedding=query_embedding, similarity_top_k=2, mode=query_mode)
query_result = vector_store.query(vector_store_query)
print(query_result.nodes[0].get_content())
结果如下:
而且,由於華裔購買偏好,在洛杉磯華人聚居的區域的漲幅明顯高於在洛杉磯的平均地區。
據全美房地產經紀人協會(NAR)資料顯示,在截至2015年3月的一年時間裏,約20.9萬美國境外人士在美國買房。總價值在1040億美元,中國買家在其中占比最大,為286億美元,平均購房價格約為83.2萬美元。華人買家占了美國房地產所有國際買家的三分之一還多。而集中在洛杉磯的就占相當比例。
據洛杉磯縣經濟發展局凱瑟中心(Los Angeles County Economic Development Corp.'s Kyser Center)今年5月10日公布的房市報告顯示,因就業率提高、利率持平、房源庫存適中等因素,從2015年起華裔聚居的聖蓋博穀地區房價呈現高於洛杉磯地區的增長態勢,2016年度也繼續上揚。 凱瑟中心報告顯示,聖蓋博穀地區房價從2015年起呈現穩健上升,個別地區漲幅達到10%以上。
洛杉磯地區的一些華裔地產經紀人今年預計,至少未來兩年內,聖穀華人區房價將健康穩健增長。凱瑟中心報告舉例顯示,西柯汶納市獨立屋的中值價位(Median Price)在2015年3月達到50.6萬美元,同比增長17.7%。
而同樣的華人眾多的帕薩迪納市,該市今年的獨立屋中值價位也近120萬美元,比去年同期增長14%,同樣增長幅度還出現在蒙特利公園市、惠提爾市及皮科維拉(Pico Rivera)等地區。
根據報告分析,華人聚居的聖蓋博穀地區房價回升不僅有外來買家因素,更有當地經濟回升、就業率的穩步提高以及房源精益庫存適中等多個因素。
然后我们将结果解析为一组节点。
from llama_index.core.schema import NodeWithScore
from typing import Optional
nodes_with_scores = []
for index, node in enumerate(query_result.nodes):
score: Optional[float] = None
if query_result.similarities is not None:
score = query_result.similarities[index]
nodes_with_scores.append(NodeWithScore(node=node, score=score))
现在,使用上述内容,我们将创建一个检索器类。
from llama_index.core import QueryBundle
from llama_index.core.retrievers import BaseRetriever
from typing import Any, List
class VectorDBRetriever(BaseRetriever):
"""Retriever over a qdrant vector store."""
def __init__(self,
vector_store: QdrantVectorStore,
embed_model: Any,
query_mode: str = "default",
similarity_top_k: int = 2) -> None:
"""Init params."""
self._vector_store = vector_store
self._embed_model = embed_model
self._query_mode = query_mode
self._similarity_top_k = similarity_top_k
super().__init__()
def _retrieve(self, query_bundle: QueryBundle) -> List[NodeWithScore]:
"""Retrieve."""
query_embedding = embed_model.get_query_embedding(
query_bundle.query_str
)
vector_store_query = VectorStoreQuery(
query_embedding=query_embedding,
similarity_top_k=self._similarity_top_k,
mode=self._query_mode,
)
query_result = vector_store.query(vector_store_query)
nodes_with_scores = []
for index, node in enumerate(query_result.nodes):
score: Optional[float] = None
if query_result.similarities is not None:
score = query_result.similarities[index]
nodes_with_scores.append(NodeWithScore(node=node, score=score))
return nodes_with_scores
retriever = VectorDBRetriever(
vector_store, embed_model, query_mode="default", similarity_top_k=2
)
然后,创建一个检索器查询引擎。
from llama_index.query_engine import RetrieverQueryEngine
query_engine = RetrieverQueryEngine.from_args(
retriever
)
# 下面是新的Engine启动方法,请查阅: https://docs.llamaindex.ai/en/stable/module_guides/supporting_modules/service_context_migration/
query_engine = index.as_query_engine(llm=llm)
最后,我们的检索器已准备好进行查询和聊天。让我们传递一个查询。
query_str = "告诉我你知道的新闻消息,分条简单列举出来。"
response = query_engine.query(query_str)
print(str(response))
以下是响应:
以下是根据提供的信息提炼出的新闻要点:
* 海南地產參展規模空前盛大,成為旅遊地產投資的勝地。
* 北京房展會組委會秘書長鄭向東表示,北京人選擇在外地購買養老度假房產。
* 海外置業新政頻出,海外項目仍是半壁江山。
* 臺當局債務不斷增長,民進黨當局決定“公私兼顧”,先拿軍公教開刀。
* 大陸乃經濟源頭活水,臺灣經濟發展需要大陸市場。
* 民進黨蔡英文反對馬英九“開放的兩岸政策”,阻撓兩岸經貿交流步伐。
结论
DeepSeek LLM 在回答问题方面表现非常出色。它的架构使其区别于其他模型,最令人印象深刻的是它利用直接偏好优化 (Direct Preference Optimization) 来增强模型能力。这是一个针对中文和英文进行微调和优化的模型,我们从结果中观察到了这样一个经过微调和优化的模型的出色表现。
我们利用 FastEmbed 和 Qdrant 进行向量嵌入生成和向量相似性搜索。使用 Qdrant 检索速度很快。Qdrant 最令人印象深刻的功能之一是它可以通过 Docker 安装、在云端访问,并且支持内存存储。