DeepSeek R1重磅开源!一文读懂训练方法与RAG应用搭建

DeepSeek R1学习方法概述

DeepSeek R1的特点在于使用强化学习(RL)进行后期训练。一般来说,大规模语言模型的开发要经过以下几个步骤:

  • 预训练:利用大规模语料库创建一个 “预测下一个单词” 的模型。
  • 监督微调(SFT):使用高质量的、人工创建的指令-响应配对数据,针对特定任务对模型进行微调。
  • 基于人类反馈的强化学习(RLHF):由人类评估模型的输出,并将评分作为奖励来更新模型。

据说,DeepSeek R1在大规模地进行强化学习,尤其是上述的第三步。此外,他们还探索了一种新方法,即直接应用强化学习(有一个版本叫DeepSeek-R1-Zero),不经过传统的SFT,之后再加入SFT完成DeepSeek R1的训练。这种方法据说能够促进模型的内部思维过程(思维链)和自我验证。

通常情况下,要有效处理思维链需要一些SFT数据和人工标注。然而,DeepSeek-R1-Zero宣称仅通过强化学习就实现了推理能力,无需经过SFT。这种方法有以下优点:

  • 降低大规模数据收集的成本。
  • 能够在未知任务和复杂情况下进行自我修正。

不过,完全没有经过SFT的模型仍然存在一些问题,比如生成的文本可读性欠佳,还可能出现一些意外的多语言内容。

构建应用程序

现在,让我们一步步探索如何创建RAG应用程序。首先,要安装支持模型的库,在命令行输入:

pip install -r requirements.txt

接下来就是常规操作了,导入相关库,随着后续的操作,这些库的作用就会显现出来。

import os
import streamlit as st
from langchain_community.document_loaders import PDFPlumberLoader
from langchain_experimental.text_splitter import SemanticChunker
from langchain_huggingface import HuggingFaceEmbeddings
import os
from langchain_community.vectorstores import FAISS
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.chains.history_aware_retriever import create_history_aware_retriever
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain.chains.retrieval import create_retrieval_chain
from langchain_core.messages import AIMessage, HumanMessage
import tempfile
from langchain_openai.chat_models.base import BaseChatOpenAI

PDFPlumberLoader是一个功能强大的工具,可用于处理PDF文件中的表格等复杂结构化数据,它能提取文本、图像、表格、字段等内容。SemanticChunker则基于语义相似度将文本分割成块,确保相关内容都在同一个块中。

我创建了一个函数,用于处理上传的文件。这个函数会使用临时文件存储文件内容,接着用PDFPlumberLoader处理PDF文件中的复杂结构化数据,处理完后还会清理临时文件,以保持系统整洁,最后返回提取的数据供后续使用。

def process_uploaded_file(uploaded_file):
    with tempfile.NamedTemporaryFile(delete=False, suffix='.pdf') as tmp_file:
        tmp_file.write(uploaded_file.getvalue())
        loader = PDFPlumberLoader(tmp_file.name)
        documents = loader.load()
        os.unlink(tmp_file.name)
        return documents

我还创建了一个函数,用于处理文档列表并构建一个基于相似度搜索的检索器。首先,使用带有OpenAIEmbeddings的SemanticChunker将文档分割成较小的块,同时保留语义关系,确保相关内容聚集在一起。在确认分割过程无误后,使用FAISS开发一个向量存储,它会对文本块进行组织,以实现高效的相似度搜索。最后,返回一个检索器,它能为给定的查询找到最相似的前3个文本块,让文档检索过程既准确又高效。

def get_vs_retriever_from_docs(doc_list):
    text_splitter = SemanticChunker(HuggingFaceEmbeddings())
    documents = text_splitter.split_documents(doc_list)
    st.write('Document splitting done')
    embedder = HuggingFaceEmbeddings()
    vector = FAISS.from_documents(documents, embedder)
    return vector.as_retriever(search_type="similarity", search_kwargs={"k": 3})

init_ui()函数的作用是使用Streamlit搭建一个便于用户上传和分析PDF文档的界面。首先,配置页面标题和描述性的头部信息。然后初始化会话状态变量,用于管理检索器、聊天历史记录和上传状态。接着提供一个文件上传小部件,方便用户上传PDF文件。一旦有文件上传,就处理文件提取内容,用get_vs_retriever_from_docs创建一个检索器,并将其存储在会话状态中供后续使用。最后,用一条成功消息通知用户文档处理成功。

def init_ui():
    st.set_page_config(page_title='Document uploader')
    st.markdown('#### :books:🧙Gao Dalie (高達烈): Your Document Summarizer')
    st.markdown("<h8 style='text-align: right; color: green;'>*Share the pdf of the book you want to read*</h8>", unsafe_allow_html=True)
    if "vector_store" not in st.session_state:
        st.session_state.vector_store = None
    if "chat_history" not in st.session_state:
        st.session_state.chat_history = []
    if "doc_upload" not in st.session_state:
        st.session_state.doc_upload = False
    uploaded_file = st.file_uploader("Upload PDF", type=["pdf"])
    if uploaded_file:
        docs = process_uploaded_file(uploaded_file)
        if docs:
            retriever = get_vs_retriever_from_docs(docs)
            st.session_state.vector_store = retriever
            st.session_state.doc_upload = True
            st.success("Document processed successfully")

get_related_context()函数用于在对话系统中优化相关信息的检索。首先,初始化Ollama模型(deepseek-r1)来处理用户输入。然后定义一个提示模板,将聊天历史和用户输入结合起来,指示模型根据当前对话生成一个搜索查询。最后,使用create_history_aware_retriever增强智能体将整个对话历史融入检索过程的能力。

def get_related_context(vector_store):
    # llm = Ollama(model="deepseek-r1")
    llm = BaseChatOpenAI(
        model='deepseek-reasoner',
        openai_api_key='sk-68f459660c7b4d179e074cbedce962c0',
        openai_api_base='https://api.deepseek.com',
        max_tokens=1024
    )
    prompt = ChatPromptTemplate.from_messages([
        ("system", "Generate a search query based on the conversation."),
        ("user", "{input}")
    ])
    return create_history_aware_retriever(llm, vector_store, prompt)

在这个函数中,我设计了一个整合了两个核心链的函数:

  • context_chain:负责查找外部输入数据。
  • docs_chain:负责查找对话记录。
    最后,使用retrieval_chain = create_retrieval_chain(context_chain, docs_chain)创建一个能够检索对话和外部输入数据的链,从而完成一个支持自然对话的链。对话记录需要自行创建一个列表存储,也就是chat_history这部分。

    def get_context_aware_prompt(context_chain):
      # llm_! = Ollama(model="deepseek-r1")
      llm = BaseChatOpenAI(
          model='deepseek-reasoner',
          openai_api_key='sk-68f459660c7b4d179e074cbedce962c0',
          openai_api_base='https://api.deepseek.com',
          max_tokens=1024
      )
      prompt = ChatPromptTemplate.from_messages([
          ("system", "Answer questions using the provided context:\n\n{context}"),
          ("user", "{input}")
      ])
      # st.write(docs_chain)
      return create_retrieval_chain(context_chain, docs_chain)

    在这个函数中,我处理了针对给定查询生成上下文感知响应的过程。首先检查是否上传了必要的文档,如果没有上传,则返回错误信息。确认文档已上传后,检索上下文链以从上传的文档中获取相关信息,如果检索上下文失败,同样返回错误信息。接着创建一个RAG链,将上下文、对话历史和用户查询结合起来生成回复。最后调用RAG链,处理回复并返回生成的答案。

    def get_response(query: str) -> str:
      if not st.session_state.vector_store:
          return "Error: Please upload documents first"
      try:
          context_chain = get_related_context(st.session_state.vector_store)
          # st.write(context_chain)
          if not context_chain:
              return "Error: Failed to process context"
          rag_chain = get_context_aware_prompt(context_chain)
          # st.write(rag_chain)
          current_history = st.session_state.chat_history[-2:] if len(st.session_state.chat_history) > 1 else []
          return rag_chain.invoke({
              "chat_history": current_history,
              "input": query
          })["answer"].do
      except Exception as e:
          return f"Error: {str(e)}"

    在这个函数中,我设置并展示了一个供用户交互的聊天界面。首先,遍历存储的聊天历史记录,在聊天窗口中展示用户和AI的消息。然后提供一个输入框,用户可以在其中输入新问题。如果用户提交查询,就调用get_response函数生成答案,之后将新消息和回复更新到聊天历史记录中。

    def init_chat_interface():
      for message in st.session_state.chat_history:
          with st.chat_message("user" if isinstance(message, HumanMessage) else "assistant"):
              st.write(message.content)
      if prompt := st.chat_input("Ask a question...", disabled=not st.session_state.doc_upload):
          st.session_state.chat_history.append(HumanMessage(content=prompt))
          response = get_response(prompt)
          st.session_state.chat_history.append(AIMessage(content=response))
    if __name__ == "__main__":
      init_ui()
      if st.session_state.vector_store:
          init_chat_interface()

参考链接

https://github.com/deepseek-ai/DeepSeek-R1

近日热文:全网最全的神经网络数学原理(代码和公式)直观解释
欢迎关注知乎和公众号的专栏内容
LLM架构专栏
知乎LLM专栏
知乎【柏企
公众号【柏企科技说】【柏企阅文

本文由mdnice多平台发布


柏企科技圈
1 声望0 粉丝

时间差不多了,快上车!~