引言随着大型语言模型(LLMs)的兴起,我们见证了一种新的工具类别的诞生。然而,LLMs 也存在局限性,尤其是当面对需要最新信息或专有数据的商业用例时。本文将介绍如何通过微调和 RAG 来解决这些问题。 ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 1.1em;font-weight: bold;margin-top: 2em;margin-right: 8px;margin-bottom: 0.75em;padding-left: 8px;border-left: 3px solid rgb(250, 81, 81);color: rgb(63, 63, 63);">LLMs 的局限性ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">传统 LLMs 训练成本高昂,且只能访问公共信息。对于商业用途,需要模型能够提供基于内部知识的最新回应。文章介绍了两种解决这一问题的方法:微调和 RAG。ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-weight: bold;margin: 2em 8px 0.5em;color: rgb(250, 81, 81);">微调ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">微调是针对特定数据集进一步训练预训练模型的过程,使其适应特定任务或领域。这类似于给一个通才型助手提供额外的、针对性的训练,使其成为某个特定领域的专家。ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-weight: bold;margin: 2em 8px 0.5em;color: rgb(250, 81, 81);">RAGingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">RAG 是一种模型从外部来源检索相关信息以生成更准确、更有信息量回应的方法。与传统依赖预训练知识的模型不同,RAG 通过数据库或搜索引擎查找额外数据,并结合这些数据生成回应。ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 1.1em;font-weight: bold;margin-top: 2em;margin-right: 8px;margin-bottom: 0.75em;padding-left: 8px;border-left: 3px solid rgb(250, 81, 81);color: rgb(63, 63, 63);">开源解决方案ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;margin: 1.5em 8px;letter-spacing: 0.1em;color: rgb(63, 63, 63);">面对 LLMs 的法律和安全问题,开源社区提供了解决方案。自 Meta 发布了首个 Llama 模型以来,开源社区迅速响应,为本地实验提供了机会。ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;font-size: 1.1em;font-weight: bold;margin-top: 2em;margin-right: 8px;margin-bottom: 0.75em;padding-left: 8px;border-left: 3px solid rgb(250, 81, 81);color: rgb(63, 63, 63);">RAG 工作流程文章通过图解介绍了 RAG 的核心工作流程,包括文本分块、向量化存储、语义搜索和组合提示。 
ingFang SC", "Hiragino Sans GB", "Microsoft YaHei UI", "Microsoft YaHei", Arial, sans-serif;padding-left: 1em;color: rgb(63, 63, 63);" class="list-paddingleft-1">1.文本分块:将内容分割成文本块,以便更好地检索相关内容。 2.向量化存储:将文本转换为向量并存储在向量数据库中。 3.语义搜索:使用数值表示进行内容搜索,返回相关内容。 4.组合提示:将问题与相关内容结合,生成更准确的提示。 构建 RAG 应用程序文章通过示例代码,展示了如何使用 Langchain、ChromaDB、Ollama 和 Streamlit 构建 RAG 应用程序。 1.Langchain是一个构建大型语言模型(LLM)驱动应用程序的框架。它通过将链、代理和检索策略整合在一起,简化了从概念到实际应用的整个开发过程。Langchain 的核心是其链的概念,这些链构成了应用程序的认知架构。 2.ChromaDB是一款开源的轻量级矢量数据库,非常适合小规模服务和用例。它在任何基于 LLM 的应用程序中都扮演着重要角色,因为它以矢量格式存储文本数据,这是 AI 和 ML 模型原生使用的数据格式,可以视为 AI 的内存。 3.Ollama是一个工具,允许用户轻松在本地运行开源模型。它简化了将这些模型集成到应用程序中的复杂性,使得开发者可以快速利用最新的模型,如 Meta 的 Llama3,进行本地开发和测试。 4.Streamlit是一个开源框架,用于快速且容易地在机器学习和数据科学应用程序之上构建 Web 界面。它允许开发者使用纯 Python 代码将数据脚本转换为可共享的 Web 应用程序,无需前端开发经验,非常适合快速原型开发和应用部署。
初始化项目使用 Poetry 进行依赖管理,并安装了必要的依赖项。 poetryaddlangchainchromadbstreamlit 创建文档上传 UI使用 Streamlit 创建简单的界面,允许用户上传 PDF 文档。 definit_ui(): """init_uiInitializestheUI""" st.set_page_config(page_title="LangchainRAGBot",layout="wide") st.title("LangchainRAGBot")
#Initialisesessionstate if"chat_history"notinst.session_state: st.session_state.chat_history=[ AIMessage(content="Hello,I'mheretohelp.Askmeanything!") ]
withst.sidebar: st.header("DocumentCapture") st.write("Pleaseselectasingledocumenttouseascontext") st.markdown("**Pleasefillthebelowform:**") withst.form(key="Form",clear_on_submit=True): uploaded_file=st.file_uploader("Upload",type=["pdf"],key="pdf_upload") submit=st.form_submit_button(label="Upload")
ifsubmit: persist_file(uploaded_file)
Ollama确保本地运行 Ollama,以便创建正确格式的嵌入。 初始化向量存储将文档内容转换为向量并存储在 ChromaDB 中。 definit_vector_store(): """ InitializesandreturnsChromaDBvectorstorefromdocumentchunks
Returns: ChromaDB:Initializedvectorstore """ #Getthefirstfile-inrealitythiswouldbemorerobust files=[fforfinDATA_DIR.iterdir()iff.is_file] ifnotfiles: st.error("Nofilesuploaded") returnNone
#Getthepathtothefirstfileinthedirectory first_file=files[0].resolve() #UsethePDFloaderinLangchaintofetchthedocumenttext loader=PyPDFLoader(first_file) document=loader.load_and_split()
#Nowweinitialisethetextsplitterwewilluseonthedocument text_splitter=RecursiveCharacterTextSplitter() document_chunks=text_splitter.split_documents(document)
#Lastly,weinitialisethevectorstoreusingthesplitdocument vector_store=Chroma.from_documents( documents=document_chunks, embedding=OllamaEmbeddings(), persist_directory=str(DB_DIR), collection_name="pdf_v_db"#ImportantifyouwanttoreferencetheDBlater )
returnvector_store
创建检索链构建检索链,以便根据用户查询检索相关内容。 defget_related_context(vector_store:Chroma)->RetrieverOutputLike: """ Willretrievetherelevantcontextbasedontheuser'squery usingApproximateNearestNeighborsearch(ANN)
Args: vector_store(Chroma):Theinitializedvectorstorewithcontext
Returns: RetrieverOutputLike:ThechaincomponenttobeusedwiththeLLM """
#Specifythemodeltouse llm=Ollama(model="llama3")
#Hereweareusingthevectorstoreasthesource retriever=vector_store.as_retriever()
#Createapromptthatwillbeusedtoquerythevectorstoreforrelatedcontent prompt=ChatPromptTemplate.from_messages([ MessagesPlaceholder(variable_name="chat_history"), ("user","{input}"), ("user","Giventheaboveconversation,generateasearchquerytolookuptogetinformationrelevanttotheconversation") ])
#CreatethechainelementwhichwillfetchtherelevantcontentfromChromaDB chain_element=create_history_aware_retriever(llm,retriever,prompt) returnchain_element
defget_context_aware_prompt(context_chain:RetrieverOutputLike)->Runnable: """ Combinedthechainelementtofetchcontentwithonethatthencreatesthe promptusedtointeractwiththeLLM
Args: context_chain(RetrieverOutputLike):Theretrieverchainthatcan fetchrelatedcontentfromChromaDB
Returns: Runnable:Thefullrunnablechainthatcanbeexecuted """
#Specifythemodeltouse llm=Ollama(model="llama3")
#Astandardprompttemplatewhichcombinedchathistorywithuserquery #NOTE:YouMUSTpassthecontextintothesystemmessage prompt=ChatPromptTemplate.from_messages([ ("system","Youareahelpfulassistantthatcananswertheusersquestions.Useprovidedcontexttoanswerthequestionasaccuratelyaspossible:\n\n{context}"), MessagesPlaceholder(variable_name="chat_history"), ("user","{input}") ])
#ThismethodcreatesachainforpassingdocumentstoaLLM docs_chain=create_stuff_documents_chain(llm,prompt)
#Nowwemergethecontextchain&docschaintoformthefullprompt rag_chain=create_retrieval_chain(context_chain,docs_chain) returnrag_chain
聊天界面使用 Streamlit 的内置聊天界面与 RAG LLM 进行交互。 defget_response(user_query:str)->str: """ Willusethequerytofetchcontext&formaquerytosendtoanLLM. Respondswiththeresultofthequery
Args: user_query(str) ueryinputbutuser
Returns: str:AnswerfromtheLLM """ context_chain=get_related_context(st.session_state.vector_store) rag_chain=get_context_aware_prompt(context_chain)
res=rag_chain.invoke({ "chat_history":st.session_state.chat_history, "input":user_query }) returnres["answer"]
definit_chat_interface(): """ Initializesachatinterfacewhichwillleverageourragchain&alocalLLM toanswerquestionsaboutthecontextprovided """
user_query=st.chat_input("Askaquestion....") ifuser_queryisnotNoneanduser_query!="": response=get_response(user_query)
#Addthecurrentchattothechathistory st.session_state.chat_history.append(HumanMessage(content=user_query)) st.session_state.chat_history.append(AIMessage(content=response))
#Printthechathistory formessageinst.session_state.chat_history: ifisinstance(message,HumanMessage): withst.chat_message("Human"): st.write(message.content) ifisinstance(message,AIMessage): withst.chat_message("AI"): st.write(message.content)
结论尽管 LLMs 功能强大,但它们并非没有缺点。通过一些创造性思维和正确的工具,可以将这些挑战转化为机遇。结合微调和 RAG,以及 Langchain、ChromaDB、Ollama 和 Streamlit 等开源模型和框架,可以为 LLMs 的实际应用提供强大的解决方案。
|