Reference:LangChain 实战课
之前的内容讲了提示工程的原理以及 LangChain 中的具体使用方式。今天,我们来着重讨论 Model I/O 中的第二个子模块,LLM。
让我们带着下面的问题来开始这一节课的学习。大语言模型,不止 ChatGPT 一种。调用 OpenAI 的 API,当然方便且高效,不过,如果我就是想用其他的模型(比如说开源的 Llama2 或者 ChatGLM),该怎么做?再进一步,如果我就是想在本机上从头训练出来一个新模型,然后在 LangChain 中使用自己的模型,又该怎么做?
关于大模型的微调(或称精调)、预训练、重新训练、乃至从头训练,这是一个相当大的话题,不仅仅需要足够的知识和经验,还需要大量的语料数据、GPU 硬件和强大的工程能力。别说一节课了,我想两三个专栏也不一定能讲全讲透。不过,我可以提纲挈领地把大模型的训练流程和使用方法给你缕一缕。这样你就能体验到,在 LangChain 中使用自己微调的模型是完全没问题的。
说到语言模型,我们不妨先从其发展史中去了解一些关键信息。
Google 2018 年的论文名篇 Attention is all you need,提出了 Transformer 架构,也给这一次 AI 的腾飞点了火。Transformer 是几乎所有预训练模型的核心底层架构。基于 Transformer 预训练所得的大规模语言模型也被叫做“基础模型”(Foundation Model 或 Base Model)。
在这个过程中,模型学习了词汇、语法、句子结构以及上下文信息等丰富的语言知识。这种在大量数据上学到的知识,为后续的下游任务(如情感分析、文本分类、命名实体识别、问答系统等)提供了一个通用的、丰富的语言表示基础,为解决许多复杂的 NLP 问题提供了可能。
在预训练模型出现的早期,BERT 毫无疑问是最具代表性的,也是影响力最大的模型。BERT 通过同时学习文本的前向和后向上下文信息,实现对句子结构的深入理解。BERT 之后,各种大型预训练模型如雨后春笋般地涌现,自然语言处理(NLP)领域进入了一个新时代。这些模型推动了 NLP 技术的快速发展,解决了许多以前难以应对的问题,比如翻译、文本总结、聊天对话等等,提供了强大的工具。
当然,现今的预训练模型的趋势是参数越来越多,模型也越来越大,训练一次的费用可达几百万美元。这样大的开销和资源的耗费,只有世界顶级大厂才能够负担得起,普通的学术组织和高等院校很难在这个领域继续引领科技突破,这种现象开始被普通研究人员所诟病。
不过,话虽如此,大型预训练模型的确是工程师的福音。因为,经过预训练的大模型中所习得的语义信息和所蕴含的语言知识,能够非常容易地向下游任务迁移。NLP 应用人员可以对模型的头部或者部分参数根据自己的需要进行适应性的调整,这通常涉及在相对较小的有标注数据集上进行有监督学习,让模型适应特定任务的需求。
这就是对预训练模型的微调(Fine-tuning)。微调过程相比于从头训练一个模型要快得多,且需要的数据量也要少得多,这使得作为工程师的我们能够更高效地开发和部署各种 NLP 解决方案。
图中的“具体任务”,其实也可以更换为“具体领域”。那么总结来说:
这种预训练 + 微调的大模型应用模式优势明显。首先,预训练模型能够将大量的通用语言知识迁移到各种下游任务上,作为应用人员,我们不需要自己寻找语料库,从头开始训练大模型,这减少了训练时间和数据需求;其次,微调过程可以快速地根据特定任务进行优化,简化了模型部署的难度;最后,预训练 + 微调的架构具有很强的可扩展性,可以方便地应用于各种自然语言处理任务,大大提高了 NLP 技术在实际应用中的可用性和普及程度,给我们带来了巨大的便利。
第二步,用pip install transformers
安装 HuggingFace Library。详见这里。
当然,也可以在程序中设置你的 API Token,但是这不如在命令行中设置来得安全。
# 导入HuggingFace API Token
import os
os.environ['HUGGINGFACEHUB_API_TOKEN'] = '你的HuggingFace API Token'
在 HuggingFace 的 Model 中,找到 meta-llama/Llama-2-7b。注意,各种各样版本的 Llama2 模型多如牛毛,我们这里用的是最小的 7B 版。此外,还有 13b\70b\chat 版以及各种各样的非 Meta 官方版。
好,万事俱备,现在我们可以使用 HuggingFace 的 Transformers 库来调用 Llama 啦!
# 导入必要的库
from transformers import AutoTokenizer, AutoModelForCausalLM
# 加载预训练模型的分词器
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-2-7b-chat-hf")
# 加载预训练的模型
# 使用 device_map 参数将模型自动加载到可用的硬件设备上,例如GPU
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-2-7b-chat-hf",
device_map = 'auto')
# 定义一个提示,希望模型基于此提示生成故事
prompt = "请给我讲个玫瑰的爱情故事?"
# 使用分词器将提示转化为模型可以理解的格式,并将其移动到GPU上
inputs = tokenizer(prompt, return_tensors="pt").to("cuda")
# 使用模型生成文本,设置最大生成令牌数为2000
outputs = model.generate(inputs["input_ids"], max_new_tokens=2000)
# 将生成的令牌解码成文本,并跳过任何特殊的令牌,例如[CLS], [SEP]等
response = tokenizer.decode(outputs[0], skip_special_tokens=True)
# 打印生成的响应
print(response)
这段程序是一个很典型的 HuggingFace 的 Transformers 库的用例,该库提供了大量预训练的模型和相关的工具。
from_pretrained
方法来加载预训练的分词器和模型。其中,device_map = 'auto'
是为了自动地将模型加载到可用的设备上,例如 GPU。"请给我讲个玫瑰的爱情故事?"
,并使用分词器将该提示转换为模型可以接受的格式,return_tensors="pt"
表示返回 PyTorch 张量。语句中的 .to("cuda")
是 GPU 设备格式转换,因为我在 GPU 上跑程序,不用这个的话会报错,如果你使用 CPU,可以试一下删掉它。.generate()
方法生成响应。max_new_tokens=2000
限制生成的文本的长度。使用分词器的.decode()
方法将输出的数字转化回文本,并且跳过任何特殊的标记。如何把 HuggingFace 里面的模型接入 LangChain。
第一种集成方式,是通过 HuggingFace Hub。HuggingFace Hub 是一个开源模型中心化存储库,主要用于分享、协作和存储预训练模型、数据集以及相关组件。
我们给出一个 HuggingFace Hub 和 LangChain 集成的代码示例。
# 导入HuggingFace API Token
import os
os.environ['HUGGINGFACEHUB_API_TOKEN'] = '你的HuggingFace API Token'
# 导入必要的库
from langchain import PromptTemplate, HuggingFaceHub, LLMChain
# 初始化HF LLM
llm = HuggingFaceHub(
repo_id="google/flan-t5-small",
#repo_id="meta-llama/Llama-2-7b-chat-hf",
)
# 创建简单的question-answering提示模板
template = """Question: {question}
Answer: """
# 创建Prompt
prompt = PromptTemplate(template=template, input_variables=["question"])
# 调用LLM Chain --- 我们以后会详细讲LLM Chain
llm_chain = LLMChain(
prompt=prompt,
llm=llm
)
# 准备问题
question = "Rose is which type of flower?"
# 调用模型并返回结果
print(llm_chain.run(question))
可以看出,这个集成过程非常简单,只需要在 HuggingFaceHub 类的 repo_id 中指定模型名称,就可以直接下载并使用模型,模型会自动下载到 HuggingFace 的 Cache 目录,并不需要手工下载。
初始化 LLM,创建提示模板,生成提示的过程,你已经很熟悉了。这段代码中有一个新内容是我通过 llm_chain 来调用了 LLM。这段代码也不难理解,有关 Chain 的概念我们以后还会详述。
不过,我尝试使用 meta-llama/Llama-2-7b-chat-hf 这个模型时,出现了错误,因此我只好用比较旧的模型做测试。我随便选择了 google/flan-t5-small,问了它一个很简单的问题,想看看它是否知道玫瑰是哪一种花。