乐趣区

关于深度学习:本地部署开源大模型的完整教程LangChain-Streamlit-Llama

在过来的几个月里,大型语言模型 (llm) 取得了极大的关注,这些模型发明了令人兴奋的前景,特地是对于从事聊天机器人、集体助理和内容创作的开发人员。

大型语言模型 (llm) 是指可能生成与人类语言十分类似的文本并以天然形式了解提醒的机器学习模型。这些模型应用宽泛的数据集进行训练,这些数据集包含书籍、文章、网站和其余起源。通过剖析数据中的统计模式,LLM 能够预测给定输出后最可能呈现的单词或短语。

以上是目前的 LLM 的一个全景图。

在本文中,我将演示如何利用 LLaMA 7b 和 Langchain 从头开始创立本人的 Document Assistant。

背景常识

1、LangChain 🔗

LangChain 是一个令人印象粗浅且收费的框架,它彻底改变了广泛应用的开发过程,包含聊天机器人、生成式问答 (GQA) 和摘要。通过将来自多个模块的组件无缝链接,LangChain 可能应用大部分的 llm 来创立应用程序。

2、LLaMA 🦙

LLaMA 是由 Facebook 的母公司 Meta AI 设计的一个新的大型语言模型。LLaMA 领有 70 亿到 650 亿个参数的模型汇合,是目前最全面的语言模型之一。2023 年 2 月 24 日,Meta 向公众公布了 LLaMA 模型,展现了他们对凋谢迷信的奉献精神(尽管咱们当初用的都是泄露版)。

3、什么是 GGML

GGML 是一个用于机器学习的张量库,它只是一个 c ++ 库,容许你在 CPU 或 CPU + GPU 上运行 llm。它定义了用于散发大型语言模型 (llm) 的二进制格局。GGML 应用了一种称为量化的技术,该技术容许大型语言模型在消费者硬件上运行。

4、量化

咱们都晓得,模型的权重是浮点数。就像示意大整数 (例如 1000) 比示意小整数 (例如 1) 须要更多的空间一样,示意高精度浮点数 (例如 0.0001) 比示意低精度浮点数 (例如 0.1) 须要更多的空间。量化大型语言模型的过程波及升高示意权重的精度,以缩小应用模型所需的资源。GGML 反对许多不同的量化策略(例如 4 位、5 位和 8 位量化),每种策略在效率和性能之间提供不同的衡量。

上面是量化后模型大小的比照:

5、Streamlit🔥

Streamlit 是一个用于构建数据迷信和机器学习应用程序的开源 Python 库。它旨在使开发人员可能以简略疾速的形式构建交互式应用程序,无需繁琐的前端开发。Streamlit 提供了一组简略的 API,可用于创立具备数据摸索、可视化和交互性能的应用程序。只须要通过简略的 Python 脚本就能够创立一个 Web 应用程序。能够利用 Streamlit 的丰盛组件库来构建用户界面,例如文本框、滑块、下拉菜单和按钮,以及可视化组件,例如图表和地图。

1、建设虚拟环境和我的项目构造

设置虚拟环境为运行应用程序提供了一个受控和隔离的环境,确保其依赖关系与其余零碎范畴的包拆散。这种办法简化了依赖关系的治理,并有助于保护不同环境之间的一致性。

而后就是创立咱们的我的项目,一个好的构造会减速咱们的开发,如下图所示

在 models 的文件夹中,咱们要存储下载的 llm,setup_env.bat 将从 pipfile 中装置所有依赖项。而 run_app.bat 则是间接运行咱们的 app。(以上 2 个文件都是 windows 环境下的脚本)

2、在本地机器上装置 LLaMA

为了无效地应用模型,必须思考内存和磁盘。因为模型须要齐全加载到内存中,因而不仅须要有足够的磁盘空间来存储它们,还须要足够的 RAM 在执行期间加载它们。比方 65B 模型,即便在量化之后,也须要 40gb 的 RAM。

所以为了在本地运行,咱们将应用最小版本的 LLaMA,也就是 LLaMA 7B。尽管它是最小的版本,然而 LLaMA 7B 也提供了很好的语言解决能力,咱们可能高效地实现预期的后果。

为了在本地 CPU 上执行 LLM,咱们应用 GGML 格局的本地模型。这里间接从 Hugging Face Models 存储库间接下载 bin 文件,而后将文件挪动到根目录下的 models 目录中。

下面咱们曾经是说了,GGML 是 c ++ 库,所以还须要应用 Python 调用 C ++ 的接口,好在这一步很简略,咱们将应用llama-cpp-python,这是 LLaMA .cpp 的 Python 绑定,它在纯 C / c++ 中充当 LLaMA 模型的推理。cpp 的次要指标是应用 4 位整数量化来运行 LLaMA 模型。这样能够能够无效地利用 LLaMA 模型,充分利用 C / c++ 的速度劣势和 4 位整数量化🚀的劣势。

llama.cpp 还反对很多其余模型,下图是列表:

筹备好 GGML 模型和所有依赖项之后,就能够开始 LangChain 进行集成了。然而在开始之前,咱们还须要做一下测试,保障咱们的 LLaMA 在本地使可用的:

看样子没有任何问题,并且程序是齐全脱机并以齐全随机的形式 (能够应用温度超参数) 运行的。

3、LangChain 集成 LLM

当初咱们能够利用 LangChain 框架来开发应用 llm 的应用程序。

为了提供与 llm 的无缝交互,LangChain 提供了几个类和函数,能够应用提醒模板轻松构建和应用提醒。它蕴含一个文本字符串模板,能够承受来自最终用户的一组参数并生成提示符。让咱们先看几个例子。

没有输出参数的模板

多个参数的模板

上面咱们能够应用 LangChain 进行集成了

目前咱们应用了独自的组件,通过提醒模板对其进行格式化,而后应用 llm,在 llm 中传递这些参数以生成答案。对于简略的应用程序,独自应用 LLM 是能够的,然而更简单的应用程序须要将 LLM 链接起来——要么互相链接,要么与其余组件链接。

LangChain 为这种链接🔗应用程序提供了 Chain 接口。咱们能够将 Chain 定义为对组件的调用序列,其中能够蕴含其余 Chain。Chain 容许咱们将多个组件组合在一起,以创立一个繁多的、统一的应用程序。例如,能够创立一个 Chain,它承受用户输出,应用 Prompt Template 对其进行格式化,而后将格式化后的响应传递给 LLM。咱们能够通过将多个 Chain 组合在一起,或者与其余组件组合在一起,来构建更简单的 Chain。这其实就和咱们个别数据处理中的 pipeline 是相似的。

创立一个非常简单的 Chain🔗,它将承受用户输出,用它格式化提示符,而后应用咱们曾经创立的上述各个组件将其发送到 LLM。

4、生成嵌入和向量库

在许多 LLM 应用程序中,须要特定于用户的数据,这些数据不包含在模型的训练集中。LangChain 提供了加载、转换、存储和查问数据的根本组件,咱们这里能够间接应用

上图蕴含了 5 个组件:

  • 文档加载器: 它用于将数据加载为文档。
  • 文档转换器: 它将文档分成更小的块。
  • 嵌入: 它将块转换为向量示意,即嵌入。
  • 嵌入向量存储: 用于将上述块向量存储在矢量数据库中。
  • 检索器: 它用于检索一组向量,这些向量以嵌入在雷同 Latent 空间中的向量的模式与查问最类似。

咱们将实现这五个步骤,流程图如所提供的下图所示。

咱们这里应用维基百科上复制的一段对于一些 DC 超级英雄的文本作为开发测试应用。原文如下:

a. 加载和转换文档

应用文本加载器创立一个文档对象(Lang chain 提供了对多个文档的反对,能够依据文档应用不同的加载器),应用 load 办法检索数据,并将其作为文档从预配置的源加载。

加载文档之后,通过将其合成为更小的块来持续转换过程。应用 TextSplitter(默认状况下,拆分器以 ’ \n\n ‘ 分隔符分隔文档)。如果将分隔符设置为 null 并定义特定的块大小,则每个块将具备指定的长度。这样就失去了列表长度将等于文档的长度除以块大小的一个块列表。

b.Embeddings

词嵌入只是一个词的向量示意,向量蕴含实数。词嵌入通过在低维向量空间中提供词的密集示意来解决简略的二进制单词向量因为维数高的问题。

LangChain 中的基 Embeddings 类公开了两个办法: 一个用于嵌入文档,另一个用于嵌入查问。前者承受多个文本作为输出,后者承受单个文本作为输出。

因为前面的检索也是检索嵌入在雷同潜在空间中最类似的向量,所以词向量必须应用雷同的办法(模型)生成。

c. 创立存储和检索文档

矢量存储无效地治理嵌入数据的存储,并减速矢量搜寻操作。咱们将应用 Chroma,一个专门用于简化蕴含嵌入的人工智能应用程序的开发的矢量数据库。它提供了一套全面的内置工具和函数,咱们只须要应用 pip install chromadb 命令将它装置在本地。

当初咱们能够存储和检索向量了,上面就是与 LLM 来整合了。

到这一步,曾经能够应用本地运行的 LLM 构建问答机器人了,这个后果还不错,然而咱们还有更好的要求,就是一个 GUI 界面。

5、Streamlit

如果你只喜爱命令行的形式运行,则这一节是齐全可选的。因为在这里咱们将创立一个容许用户上传任何文本文档的 WEB 程序。能够通过文本输出提出问题,来对文档进行剖析。

因为波及到文件上传,所以为了避免潜在的内存不足谬误,这里只将简略地读取文档并将其写入长期文件夹中并重命名为 raw.txt。这样无论文档的原始名称是什么,Textloader 都将在未来无缝地解决它(咱们这里假如:单用户同时只解决一个文件)。

咱们也只解决 txt 文件,代码如下:

 import streamlit as st 
 from langchain.llms import LlamaCpp
 from langchain.embeddings import LlamaCppEmbeddings
 from langchain.prompts import PromptTemplate
 from langchain.chains import LLMChain
 from langchain.document_loaders import TextLoader
 from langchain.text_splitter import CharacterTextSplitter
 from langchain.vectorstores import Chroma
 
 
 # Customize the layout
 st.set_page_config(page_title="DOCAI", page_icon="🤖", layout="wide",)     
 st.markdown(f"""
             <style>
             .stApp {{background-image: url("https://images.unsplash.com/photo-1509537257950-20f875b03669?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1469&q=80"); 
                      background-attachment: fixed;
                      background-size: cover}}
          </style>
          """, unsafe_allow_html=True)
 
 # function for writing uploaded file in temp
 def write_text_file(content, file_path):
     try:
         with open(file_path, 'w') as file:
             file.write(content)
         return True
     except Exception as e:
         print(f"Error occurred while writing the file: {e}")
         return False
 
 # set prompt template
 prompt_template = """Use the following pieces of context to answer the question at the end. If you don't know the answer, just say that you don't know, don't try to make up an answer.
 {context}
 Question: {question}
 Answer:"""prompt = PromptTemplate(template=prompt_template, input_variables=["context","question"])
 
 # initialize hte LLM & Embeddings
 llm = LlamaCpp(model_path="./models/llama-7b.ggmlv3.q4_0.bin")
 embeddings = LlamaCppEmbeddings(model_path="models/llama-7b.ggmlv3.q4_0.bin")
 llm_chain = LLMChain(llm=llm, prompt=prompt)
 
 st.title("📄 Document Conversation 🤖")
 uploaded_file = st.file_uploader("Upload an article", type="txt")
 
 if uploaded_file is not None:
     content = uploaded_file.read().decode('utf-8')
     # st.write(content)
     file_path = "temp/file.txt"
     write_text_file(content, file_path)   
     
     loader = TextLoader(file_path)
     docs = loader.load()    
     text_splitter = CharacterTextSplitter(chunk_size=100, chunk_overlap=0)
     texts = text_splitter.split_documents(docs)
     db = Chroma.from_documents(texts, embeddings)    
     st.success("File Loaded Successfully!!")
     
     # Query through LLM    
     question = st.text_input("Ask something from the file", placeholder="Find something similar to: ....this.... in the text?", disabled=not uploaded_file,)    
     if question:
         similar_doc = db.similarity_search(question, k=1)
         context = similar_doc[0].page_content
         query_llm = LLMChain(llm=llm, prompt=prompt)
         response = query_llm.run({"context": context, "question": question})        
         st.write(response)

看看咱们的界面:

这样一个简略的并且能够应用的程序就实现了。

总结

通过 LangChain 和 Streamlit 咱们能够不便的整合任何的 LLM 模型,并且通过 GGML 咱们能够将大模型运行在生产级的硬件中,这对咱们集体钻研来说使十分有帮忙的。

如果你对本文感兴趣,这里是本文的全副源代码,能够间接下载应用:

https://avoid.overfit.cn/post/f6d24e1b178540379b762708bb2dfa37

作者:Afaque Umer

退出移动版