本地部署开源大模型教程:LangChain + Streamlit + Llama
如何在本地消费级硬件上部署开源大语言模型 LLaMA,结合 LangChain 框架与 Streamlit 构建文档问答应用。内容涵盖环境配置、GGML 量化模型下载、Python 绑定 llama-cpp-python 安装、LangChain 链式调用集成、向量数据库 Chroma 的嵌入检索流程,以及最终 Web 界面的搭建。通过该方案,开发者可实现离线运行的私有化大模型应用,无需依赖云端 API。

如何在本地消费级硬件上部署开源大语言模型 LLaMA,结合 LangChain 框架与 Streamlit 构建文档问答应用。内容涵盖环境配置、GGML 量化模型下载、Python 绑定 llama-cpp-python 安装、LangChain 链式调用集成、向量数据库 Chroma 的嵌入检索流程,以及最终 Web 界面的搭建。通过该方案,开发者可实现离线运行的私有化大模型应用,无需依赖云端 API。

在过去的几个月里,大型语言模型(LLM)获得了极大的关注。这些模型创造了令人兴奋的前景,特别是对于从事聊天机器人、个人助理和内容创作的开发人员。
![图:LLM 全景示意图]
大型语言模型(LLM)是指能够生成与人类语言非常相似的文本并以自然方式理解提示的机器学习模型。这些模型使用广泛的数据集进行训练,包括书籍、文章、网站和其他来源。通过分析数据中的统计模式,LLM 可以预测给定输入后最可能出现的单词或短语。
在本文中,我将演示如何利用 LLaMA 7B 和 LangChain 从头开始创建自己的文档助手。
在这篇文章中,我将展示从头开始创建自己的文档助手的过程,利用 LLaMA 7B 和 LangChain。LangChain 是一个专门为与 LLM 无缝集成而开发的开源库。
以下是该教程的结构概述:
LangChain 是一个令人印象深刻且免费的框架,它彻底改变了广泛应用的开发过程,包括聊天机器人、生成式问答(GQA)和摘要。通过将来自多个模块的组件无缝链接,LangChain 能够使用大部分的 LLM 来创建应用程序。
LLaMA 是由 Meta AI 设计的一个新的大型语言模型。LLaMA 拥有 70 亿到 650 亿个参数的模型集合,是目前最全面的语言模型之一。考虑到 LLaMA 的卓越能力,我们选择利用这个强大的语言模型来达到我们的目的。具体来说,我们将采用最小的 LLaMA 版本,称为 LLaMA 7B。即使在这个缩小的版本中,LLaMA 7B 也提供了重要的语言处理能力,使我们能够有效地实现我们的预期结果。
官方研究论文:LLaMA: Open and Efficient Foundation Language Models
为了在本地 CPU 上执行 LLM,我们需要一个 GGML 格式的本地模型。有几种方法可以实现这一点,但最简单的方法是直接从 Hugging Face Models 资源库下载 bin 文件。在我们的案例中,我们将下载 Llama 7B 模型。这些模型是开源的,可以免费下载。
GGML 是一个用于机器学习的张量库,它只是一个 C++ 库,允许你在 CPU 或 CPU + GPU 上运行 LLM。它定义了用于分发大型语言模型(LLM)的二进制格式。GGML 使用了一种称为量化的技术,该技术允许大型语言模型在消费者硬件上运行。
我们都知道,模型的权重是浮点数。就像表示大整数需要更多的空间一样,表示高精度浮点数比表示低精度浮点数需要更多的空间。量化大型语言模型的过程涉及降低表示权重的精度,以减少使用模型所需的资源。GGML 支持许多不同的量化策略(例如 4 位、5 位和 8 位量化),每种策略在效率和性能之间提供不同的权衡。
下面是量化后模型大小的对比参考:
![图:量化前后模型大小对比]
Streamlit 是一个用于构建数据科学和机器学习应用程序的开源 Python 库。它旨在使开发人员能够以简单快速的方式构建交互式应用程序,无需繁琐的前端开发。Streamlit 提供了一组简单的 API,可用于创建具有数据探索、可视化和交互功能的应用程序。只需要通过简单的 Python 脚本就可以创建一个 Web 应用程序。
在模型的文件夹中,将存储下载的 LLM,而 pip 文件将位于根目录。
设置虚拟环境为运行应用程序提供了一个受控和隔离的环境,确保其依赖关系与其他系统范围的包分离。这种方法简化了依赖关系的管理,并有助于维护不同环境之间的一致性。
然后就是创建我们的项目,一个好的结构会加速我们的开发:
![图:项目文件结构示意]
models 的文件夹中,我们要存储下载的 LLM。setup_env.bat 将从 pipfile 中安装所有依赖项。run_app.bat 则是直接运行我们的 App。(以上 2 个文件都是 Windows 环境下的脚本)建议使用 Python 3.9 或更高版本。创建虚拟环境命令如下:
python -m venv venv
# Windows 激活
venv\Scripts\activate
# Linux/Mac 激活
source venv/bin/activate
创建 requirements.txt 文件,包含以下依赖:
langchain==0.0.330
llama-cpp-python==0.1.83
streamlit==1.28.0
chromadb==0.4.15
sentence-transformers==2.2.2
安装依赖:
pip install -r requirements.txt
为了有效地使用模型,必须考虑内存和磁盘。由于模型需要完全加载到内存中,因此不仅需要有足够的磁盘空间来存储它们,还需要足够的 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 在本地是可用的:
![图:本地 LLaMA 测试输出]
看样子没有任何问题,并且程序是完全脱机并以完全随机的方式(可以使用温度超参数)运行的。
现在我们可以利用 LangChain 框架来开发使用 LLM 的应用程序。
为了提供与 LLM 的无缝交互,LangChain 提供了几个类和函数,可以使用提示模板轻松构建和使用提示。它包含一个文本字符串模板,可以接受来自最终用户的一组参数并生成提示符。
下面我们可以使用 LangChain 进行集成了:
![图:PromptTemplate 示例]
继续对 LLM 进行提示:
![图:LLM 响应示例]
目前我们使用了单独的组件,通过提示模板对其进行格式化,然后使用 LLM,在 LLM 中传递这些参数以生成答案。对于简单的应用程序,单独使用 LLM 是可以的,但是更复杂的应用程序需要将 LLM 链接起来——要么相互链接,要么与其他组件链接。
LangChain 为这种链接应用程序提供了 Chain 接口。我们可以将 Chain 定义为对组件的调用序列,其中可以包含其他 Chain。Chain 允许我们将多个组件组合在一起,以创建一个单一的、一致的应用程序。
创建一个非常简单的 Chain,它将接受用户输入,用它格式化提示符,然后使用我们已经创建的上述各个组件将其发送到 LLM。
![图:Chain 结构示意]
在许多 LLM 应用程序中,需要特定于用户的数据,这些数据不包括在模型的训练集中。LangChain 提供了加载、转换、存储和查询数据的基本组件,我们这里可以直接使用。
上图包含了 5 个组件:
我们将实现这五个步骤,流程图如所提供的下图所示:
![图:RAG 流程示意图]
我们这里使用维基百科上复制的一段关于一些 DC 超级英雄的文本作为开发测试使用。
使用文本加载器创建一个文档对象(LangChain 提供了对多个文档的支持,可以根据文档使用不同的加载器),使用 load 方法检索数据,并将其作为文档从预配置的源加载。
加载文档之后,通过将其分解为更小的块来继续转换过程。使用 TextSplitter(默认情况下,拆分器以 '\n\n' 分隔符分隔文档)。如果将分隔符设置为 null 并定义特定的块大小,则每个块将具有指定的长度。
词嵌入只是一个词的向量表示,向量包含实数。词嵌入通过在低维向量空间中提供词的密集表示来解决简单的二进制单词向量由于维度高的问题。
LangChain 中的基 Embeddings 类公开了两个方法:一个用于嵌入文档,另一个用于嵌入查询。前者接受多个文本作为输入,后者接受单个文本作为输入。
因为后面的检索也是检索嵌入在相同潜在空间中最相似的向量,所以词向量必须使用相同的方法(模型)生成。
矢量存储有效地管理嵌入数据的存储,并加速矢量搜索操作。我们将使用 Chroma,一个专门用于简化包含嵌入的人工智能应用程序的开发的矢量数据库。它提供了一套全面的内置工具和函数,我们只需要使用 pip install chromadb 命令将它安装在本地。
现在我们可以存储和检索向量了,下面就是与 LLM 来整合了:
![图:Chroma 集成示意]
到这一步,已经可以使用本地运行的 LLM 构建问答机器人了,这个结果还不错,但是我们还有更好的要求,就是一个 GUI 界面。
如果你只喜欢命令行的方式运行,则这一节是完全可选的。因为在这里我们将创建一个允许用户上传任何文本文档的 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?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=[, ])
llm = LlamaCpp(model_path=)
embeddings = LlamaCppEmbeddings(model_path=)
llm_chain = LLMChain(llm=llm, prompt=prompt)
st.title()
uploaded_file = st.file_uploader(, =)
uploaded_file :
content = uploaded_file.read().decode()
file_path =
write_text_file(content, file_path)
loader = TextLoader(file_path)
docs = loader.load()
text_splitter = CharacterTextSplitter(chunk_size=, chunk_overlap=)
texts = text_splitter.split_documents(docs)
db = Chroma.from_documents(texts, embeddings)
st.success()
question = st.text_input(, placeholder=, disabled= uploaded_file,)
question:
similar_doc = db.similarity_search(question, k=)
context = similar_doc[].page_content
query_llm = LLMChain(llm=llm, prompt=prompt)
response = query_llm.run({: context, : question})
st.write(response)
看看我们的界面:
![图:Streamlit 应用界面]
这样一个简单的并且可以使用的程序就完成了。
在实际部署过程中,可能会遇到以下问题:
n_threads) 或使用 GPU 加速(如果显卡支持 CUDA)。chunk_size。通过 LangChain 和 Streamlit 我们可以方便地整合任何的 LLM 模型,并且通过 GGML 我们可以将大模型运行在消费级的硬件中,这对我们个人研究来说是非常有帮助的。该方案实现了完全的本地化部署,保护了数据隐私,同时降低了云端 API 的成本。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online