文章目录

  • 论文概述
  • RAG工作流程
  • 核心代码解读
  • 软件架构
  • 查询引擎构建
  • 数据加载与索引创建
  • 微调嵌入模型
  • 项目应用
  • 结论



在人工智能领域,大型语言模型(LLMs)因其强大的文本生成能力而备受关注。然而,这些模型在生成信息时可能会产生过时的信息或编造事实。为了解决这一问题,检索增强生成(Retrieval-Augmented Generation, RAG)技术应运而生。本文将对论文《Searching for Best Practices in Retrieval-Augmented Generation》进行解读,并结合其核心代码实现进行分析。

论文概述

论文《Searching for Best Practices in Retrieval-Augmented Generation》由复旦大学的研究人员撰写,旨在探索RAG技术的最佳实践。RAG通过结合预训练模型和基于检索的模型,提供了一个增强模型性能的稳健框架。论文通过广泛的实验,提出了几种策略,旨在在性能和效率之间取得平衡。

【大模型-RAG】RAG最佳实践论文及项目解读_RAG

RAG工作流程

论文将RAG过程划分为如下阶段,并通过实验说明每个阶段模块选择最佳实践方案。

  • Query Classification:并非所有查询都需要检索增强。
  • Chunking:块大小显著影响性能。更大的块提供了更多的上下文,增强了理解,但增加了处理时间。较小的分块提高了检索的召回率,减少了时间,但可能缺乏足够的上下文。使用sliding window技术更加有效,即将文本按照句子进行划分,每个块包含窗口大小个句子。
  • Embedding:嵌入模型选择LLM-Embedder,其与BAAI/big-large-en的效果相当,但前者的大小比后者小三倍。
  • Vector Database:Milvus支持多种索引类型、十亿规模的向量支持、混合搜索和云原生能力。
  • Retrieval:HyDE(pseudoDoc+query)+Hybrid Search(=0.3*BM25+Original embedding)。
  • Reranking:monoT5模型参数量小且准确率相对较高,RankLLaMA绝对准确率更高。
  • Repacking:reverse方式最好。
  • Summarization:Recomp | Fangyuan Xu,Weijia Shi, and Eunsol Choi. Recomp: Improving retrieval-augmented lms with compression and selective augmentation. arXiv preprint arXiv:2310.04408, 2023.
  • Generator Fine-tuning:混合相关和随机上下文可以增强生成器对无关信息的鲁棒性,同时保证相关信息的有效利用。用一个相关文档和一个随机选择的文档来训练。

核心代码解读

博主对论文进行实现,并将代码发布至github,地址为:rag-best-practices,如果对您的工作有帮助,请给一个star※,感谢!也欢迎大家fork项目,以此为baseline构建自己的RAG应用。

软件架构

项目基于 LlamaIndex RAG框架实现,向量数据库选择Qdrant。 大模型选择基于Ollama本地调用qwen2-1.5b模型,嵌入模型选择BAAI/bge-large-zh-v1.5。 选择原因:

  • LlamaIndex框架对当前较为常用的技术进行了模块化封装,个人认为相较于langchain框架来说,其抽象层级更高,把更多的时间用于高层次的思考,而不是陷入编程的细节。
  • Qdrant数据库比Milvus更容易部署,且文档较为详细直观。

查询引擎构建

query.py文件中,定义了一个函数build_query_engine,用于构建查询引擎。这个函数根据是否使用混合搜索、重排等参数,构建不同的查询引擎。

rag_query_engine = index.as_query_engine(similarity_top_k=top_k,
                                         text_qa_template=qa_prompt_tmpl,
                                         node_postprocessors=[reranker, MetadataReplacementPostProcessor(
                                         target_metadata_key="window")],
                                         sparse_top_k=12,
                                         vector_store_query_mode="hybrid",
                                         response_synthesizer=get_response_synthesizer(
                                         response_mode=response_mode),                                         
                                         )

数据加载与索引创建

utils.py文件中,定义了load_txt_data函数,用于加载混合数据并创建索引。这个函数使用了滑动窗口分块技术,并利用Qdrant作为向量存储。

def load_txt_data(input_file, persist_dir, with_sliding_window: bool, chunk_size=512, chunk_overlap=128):
    documents = SimpleDirectoryReader(input_files=input_file).load_data()
    if with_sliding_window:
        # Sliding windows chunking & Extract nodes from documents
        node_parser = SentenceWindowNodeParser.from_defaults(
            # how many sentences on either side to capture
            window_size=3,
            # the metadata key that holds the window of surrounding sentences
            window_metadata_key="window",
            # the metadata key that holds the original sentence
            original_text_metadata_key="original_sentence",
        )
    else:
        node_parser = SentenceSplitter(chunk_size=512, chunk_overlap=128)

    nodes = node_parser.get_nodes_from_documents(documents, show_progress=True)

    # indexing & storing
    try:
        storage_context = StorageContext.from_defaults(persist_dir=persist_dir)
        index = load_index_from_storage(storage_context, show_progress=True)
    except:
        index = VectorStoreIndex(nodes=nodes, show_progress=True)
        index.storage_context.persist(persist_dir=persist_dir)
    return index, nodes

微调嵌入模型

FTEmbed.py文件中,提供了微调嵌入模型的相关函数。这些函数包括数据准备、微调和评估。

def finetuning_data_preparation(...):
    docs_nodes = load_corpus(all_data, verbose=verbose)
    train_nodes = docs_nodes[:int(2 * len(docs_nodes) / 3)]
    val_nodes = docs_nodes[int(2 * len(docs_nodes) / 3 + 1):]
    # 自带保存
    train_dataset = generate_qa_embedding_pairs(llm=llm, nodes=train_nodes, verbose=verbose,
output_path="ft_data/qa_finetune_train_dataset.json")
    val_dataset = generate_qa_embedding_pairs(llm=llm, nodes=val_nodes, verbose=verbose,output_path="ft_data/qa_finetune_val_dataset.json")
    train_dataset.save_json(train_dataset_dir)
    val_dataset.save_json(val_dataset_dir)

def finetuning_embedding(...):
    train_dataset = EmbeddingQAFinetuneDataset.from_json(train_dataset_dir)
    val_dataset = EmbeddingQAFinetuneDataset.from_json(val_dataset_dir)
    finetune_engine = SentenceTransformersFinetuneEngine(
        train_dataset,
        model_id=model_name,
        model_output_path=model_output_path,
        val_dataset=val_dataset,
    )
    if not os.path.exists(model_output_path):  # 如果已经存在微调好的模型就不用重新微调了
        finetune_engine.finetune()
    embed_model = finetune_engine.get_finetuned_model()
    print(embed_model)
    return embed_model

项目应用

论文中提出的RAG最佳实践,可以应用于多种场景,如问答系统、内容生成等。通过结合检索和生成,RAG能够提供更准确、更丰富的信息。

结论

RAG技术通过检索增强生成,有效地提高了大型语言模型的准确性和可靠性。论文《Searching for Best Practices in Retrieval-Augmented Generation》通过广泛的实验,为RAG的实际应用提供了宝贵的指导。结合论文的核心代码实现,我们可以更深入地理解RAG的工作机制和最佳实践。