LangChain实战课-7.调用模型:使用OpenAPI还是微调开源Llama2/ChatGLM?

569 阅读11分钟

我正在参加「豆包MarsCode AI练中学体验活动」

1.用HuggingFace跑开源模型

1.1注册安装HuggingFace

第一步,还是要登录 HuggingFace 网站,并拿到专属于你的Token。(如果你做了前面几节课的实战案例,那么你应该已经有这个API Token了)

第二步,用 pip install transformers ,pip install accelerate>=0.26.0安装HuggingFace Library。详见这里

第三步,在命令行中运行 huggingface-cli login,设置你的API Token。

image.png

当然,也可以在程序中设置你的API Token

# 导入HuggingFace API Token
import os
os.environ['HUGGINGFACEHUB_API_TOKEN'] = '你的HuggingFace API Token'

1.2申请使用Meta的Llama模型

在HuggingFace的Model中,找到 meta-llama/Llama-2-7b。注意,各种各样版本的Llama2模型多如牛毛,我们这里用的是最小的7B版。此外,还有13b\70b\chat版以及各种各样的非Meta官方版。

image.png 选择meta-llama/Llama-2-7b这个模型后,你能够看到这个模型的基本信息。如果你是第一次用Llama,你需要申请Access,因为我已经申请过了,所以屏幕中间有句话:“You have been granted access to this model”。从申请到批准,大概是69分钟的事儿。应该会有邮件通知。

image.png

1.3通过HuggingFace调用Llama

好,万事俱备,现在我们可以使用HuggingFace的Transformers库来调用Llama啦!

由于网络限制,Huggingface.co无法正常访问,可能需要科学上网

TPM,代表着一家企业客户在单位时间内能够使用的token限额;RPM,代表着一家企业客户在单位时间内最多能用多少次大模型

  • 模型下载可能有点久,我们可以进行之后的步骤再回来看看

import os
os.environ["HUGGINGFACEHUB_API_TOKEN"] = "" #"Your HuggingFace API Token"

# 导入必要的库
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库的用例,该库提供了大量预训练的模型和相关的工具。

  • 导入AutoTokenizer:这是一个用于自动加载预训练模型的相关分词器的工具。分词器负责将文本转化为模型可以理解的数字格式。
  • 导入AutoModelForCausalLM:这是用于加载因果语言模型(用于文本生成)的工具。
  • 使用from_pretrained方法来加载预训练的分词器和模型。其中,device_map = 'auto' 是为了自动地将模型加载到可用的设备上,例如GPU。
  • 然后,给定一个提示(prompt):"请给我讲个玫瑰的爱情故事?",并使用分词器将该提示转换为模型可以接受的格式,return_tensors="pt" 表示返回PyTorch张量。语句中的 .to("cuda") 是GPU设备格式转换,因为我在GPU上跑程序,不用这个的话会报错,如果你使用CPU,可以试一下删掉它。
  • 最后使用模型的 .generate() 方法生成响应。max_new_tokens=2000 限制生成的文本的长度。使用分词器的 .decode() 方法将输出的数字转化回文本,并且跳过任何特殊的标记。

因为是在本地进行推理,耗时时间比较久。在我的机器上,大概需要30s~2min产生结果。

2.LangChain和HuggingFace接口

2.1通过HuggingFace Hub集成LangChain

HuggingFace Hub 是一个开源模型中心化存储库,主要用于分享、协作和存储预训练模型、数据集以及相关组件。



import os

os.environ["HUGGINGFACEHUB_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,问了它一个很简单的问题,想看看它是否知道玫瑰是哪一种花。

image.png

模型告诉我,玫瑰是花。对,答案只有一个字,flower。这...不得不说,2023年之前的模型,和2023年之后的模型,水平没得比。以前的模型能说话就不错了。

2.2通过HuggingFace Pipeline调用

既然HuggingFace Hub还不能完成Llama-2的测试,让我们来尝试另外一种方法,HuggingFace Pipeline。HuggingFace 的 Pipeline 是一种高级工具,它简化了多种常见自然语言处理(NLP)任务的使用流程,使得用户不需要深入了解模型细节,也能够很容易地利用预训练模型来做任务。


# 指定预训练模型的名称
model = "meta-llama/Llama-2-7b-chat-hf"

# 从预训练模型中加载词汇器
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained(model)

# 创建一个文本生成的管道
import transformers
import torch

pipeline = transformers.pipeline(
    "text-generation",
    model=model,
    torch_dtype=torch.float16,
    device_map="auto",
    max_length=1000,
)

# 创建HuggingFacePipeline实例
from langchain import HuggingFacePipeline

llm = HuggingFacePipeline(pipeline=pipeline, model_kwargs={"temperature": 0})

# 定义输入模板,该模板用于生成花束的描述
template = """
              为以下的花束生成一个详细且吸引人的描述:
              花束的详细信息:
              ```{flower_details}```
           """

# 使用模板创建提示
from langchain import PromptTemplate, LLMChain

prompt = PromptTemplate(template=template, input_variables=["flower_details"])

# 创建LLMChain实例
from langchain import PromptTemplate

llm_chain = LLMChain(prompt=prompt, llm=llm)

# 需要生成描述的花束的详细信息
flower_details = "12支红玫瑰,搭配白色满天星和绿叶,包装在浪漫的红色纸中。"

# 打印生成的花束描述
print(llm_chain.run(flower_details))

这里简单介绍一下代码中使用到的transformers pipeline的配置参数。

image.png

至此,通过HuggingFace接口调用各种开源模型的尝试成功结束。下面,我们进行最后一个测试,看看LangChain到底能否直接调用本地模型。

image.png

3.用LangChain调用自定义语言模型

假设你就是想训练属于自己的模型。而且出于商业秘密的原因,不想开源它,不想上传到HuggingFace,就是要在本机运行模型。此时应该如何利用LangChain的功能?

我们可以创建一个LLM的衍生类,自己定义模型。而LLM这个基类,则位于langchain.llms.base中,通过from langchain.llms.base import LLM语句导入。 这个自定义的LLM类只需要实现一个方法:

  • _call方法:用于接收输入字符串并返回响应字符串。

以及一个可选方法:

  • _identifying_params方法:用于帮助打印此类的属性。

下面,让我们先从HuggingFace的这里,下载一个llama-2-7b-chat.ggmlv3.q4_K_S.bin模型,并保存在本地。

image.png 你可能会质疑我,不是说自己训练,自己微调,不再用HuggingFace了吗?

不好意思,容许我解释一下。自己训练一个能用的模型没那么容易。这个模型,它并不是原始的Llama模型,而是TheBloke这位老兄用他的手段为我们量化过的新模型,你也可以理解成,他已经为我们压缩或者说微调了Llama模型。

量化是AI模型大小和性能优化的常用技术,它将模型的权重简化到较少的位数,以减少模型的大小和计算需求,让大模型甚至能够在CPU上面运行。当你看到模型的后缀有GGML或者GPTQ,就说明模型已经被量化过,其中GPTQ 是一种仅适用于 GPU 的特定格式。GGML 专为 CPU 和 Apple M 系列设计,但也可以加速 GPU 上的某些层。llama-cpp-python这个包就是为了实现GGML而制作的。

所以,这里你就假设,咱们下载下来的llama-2-7b-chat.ggmlv3.q4_K_S.bin这个模型,就是你自己微调过的。将来你真的微调了Llama2、ChatGLM、百川或者千问的开源版,甚至是自己从头训练了一个mini-ChatGPT,你也可以保存为you_own_model.bin的格式,就按照下面的方式加载到LangChain之中。

然后,为了使用llama-2-7b-chat.ggmlv3.q4_K_S.bin这个模型,你需要安装 pip install llama-cpp-python 这个包。

linux下使用这个命令下载,注意科学上网问题。

curl -O https://huggingface.co/TheBloke/Llama-2-7B-Chat-GGML/tree/main/llama-2-7b-chat.ggmlv3.q4_.bin

windows下直接点击下载,3.83G!,原文件大概9.98G。

image.png


# 导入需要的库
from llama_cpp import Llama
from typing import Optional, List, Mapping, Any
from langchain.llms.base import LLM

# 模型的名称和路径常量
MODEL_NAME = "llama-2-7b-chat.ggmlv3.q4_K_S.bin"
MODEL_PATH = "./cache/llama2/"


# 自定义的LLM类,继承自基础LLM类
class CustomLLM(LLM):
    model_name = MODEL_NAME

    # 该方法使用Llama库调用模型生成回复
    def _call(self, prompt: str, stop: Optional[List[str]] = None) -> str:
        prompt_length = len(prompt) + 5
        # 初始化Llama模型,指定模型路径和线程数
        llm = Llama(model_path=MODEL_PATH + MODEL_NAME, n_threads=4)
        # 使用Llama模型生成回复
        response = llm(f"Q: {prompt} A: ", max_tokens=256)

        # 从返回的回复中提取文本部分
        output = response["choices"][0]["text"].replace("A: ", "").strip()

        # 返回生成的回复,同时剔除了问题部分和额外字符
        return output[prompt_length:]

    # 返回模型的标识参数,这里只是返回模型的名称
    @property
    def _identifying_params(self) -> Mapping[str, Any]:
        return {"name_of_model": self.model_name}

    # 返回模型的类型,这里是"custom"
    @property
    def _llm_type(self) -> str:
        return "custom"


# 初始化自定义LLM类
llm = CustomLLM()

# 使用自定义LLM生成一个回复
result = llm(
    "昨天有一个客户抱怨他买了花给女朋友之后,两天花就枯了,你说作为客服我应该怎么解释?"
)

# 打印生成的回复
print(result)

代码中需要解释的内容不多,基本上就是CustomLLM类的构建和使用,类内部通过Llama类来实现大模型的推理功能,然后直接返回模型的回答。 评价:对中文能力不够强大,但是英文表现良好,和gpt3.5差不多,重点在于完全开源的免费模型,且是最小版本,可以理解。

  • 跑不起来

4.总结

现在,你已经知道大模型训练涉及在大量数据上使用深度学习算法,通常需要大量计算资源和时间。训练后,模型可能不完全适合特定任务,因此需要微调,即在特定数据集上继续训练,以使模型更适应该任务。为了减小部署模型的大小和加快推理速度,模型还会经过量化,即将模型参数从高精度格式减少到较低精度。

如果你想继续深入学习大模型,那么有几个工具你不得不接着研究。

  • PyTorch是一个流行的深度学习框架,常用于模型的训练和微调。
  • HuggingFace是一个开源社区,提供了大量预训练模型和微调工具,尤其是NLP任务。
  • LangChain则擅长于利用大语言模型的推理功能,开发新的工具或应用,完成特定的任务。

这些工具和库在AI模型的全生命周期中起到关键作用,使研究者和开发者更容易开发和部署高效的AI系统。

5.延伸阅读

  1. Llama2,开源的可商用类ChatGPT模型,Facebook链接GitHub链接
  2. HuggingFace Transformer 文档
  3. PyTorch 官方教程文档
  4. AutoGPTQ 基于GPTQ算法的大模型量化工具包
  5. Llama CPP 支持 GGML,目标是在MacBook(或类似的非GPU的普通家用硬件环境)上使用4位整数量化运行Llama模型