大语言模型集成工具 LangChain

Posted kebijuelun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了大语言模型集成工具 LangChain相关的知识,希望对你有一定的参考价值。

大语言模型集成工具 LangChain

LangChain 介绍

  • 介绍:
    • 通过可组合性使用大型语言模型构建应用程序
    • 【背景】大型语言模型 (LLM) 正在成为一种变革性技术,使开发人员能够构建他们以前无法构建的应用程序,但是单独使用这些 LLM 往往不足以创建一个真正强大的应用程序,当可以将它们与其他计算或知识来源相结合时,就有真的价值了。LangChain 旨在协助开发这些类型的应用程序
  • 使用文档:https://langchain.readthedocs.io/en/latest/index.html
  • 代码:https://github.com/hwchase17/langchain

安装介绍

  • 库安装
pip install langchain
pip install openai
  • OPENAI KEY 获取:
    • 淘宝上花几块钱购买一个是最方便的途径
    • 或到 https://openai.com/blog/openai-api/ 网站申请账号,申请周期有可能会比较长
  • 添加环境变量
    • 在终端运行:export OPENAI_API_KEY="..."
    • 或在 python 脚本中添加:import os; os.environ["OPENAI_API_KEY"] = "..."

LangChain 应用(基于0.0.64 版本测试)

获取 LLM 的预测 (QA 任务)

  • 获取 LLM 的预测是最直接的应用方式,测试样例如下
text = "What would be a good company name a company that makes colorful socks?"
print(llm(text)) # 返回 Socktastic!

简单数学问题:

from langchain.llms import OpenAI # 导入 LLM wrapper
llm = OpenAI(temperature=0.9) # 大的 temperature 会让输出有更多的随机性
text = "what is the results of 5+6?"
print(llm(text)) # 返回 11
text = "what is the results of 55+66?"
print(llm(text)) # 返回 121
text = "what is the results of 55555+66666?"
print(llm(text)) # 返回 122221
text = "what is the results of 512311+89749878?"
print(llm(text)) # 返回 89,876,189,终于错了...

另一个例子,这里返回的是同义词,如果要返回同音词则需要修改输入的 prompt(另外一个解决方式是基于以下章节中的 Memory 模式):

text = "what word is similar to good?"
print(llm(text)) # 返回 Excellent
text = "what word is homophone of good?"
print(llm(text)) # 返回 Goo

输入 prompts 模板设置

  • 在上面根据公司生产的产品生成公司名字的应用中,一种让用户输入更简单的方式是仅让客户输入公司生产的产品即可,不需要输入整个语句,这需要对 prompts 设置模板:
from langchain.prompts import PromptTemplate
prompt = PromptTemplate(
    input_variables=["product"],
    template="What is a good name for a company that makes product?",
)
print(prompt.format(product="colorful socks")) # 返回 What is a good name for a company that makes colorful socks?
text = prompt.format(product="colorful socks")
print(llm(text)) # 返回 Socktastic!
text = prompt.format(product="chocolates")
print(llm(text)) # 返回 ChocoDelightz!

Memory 功能: 在 LLM 交互中记录交互的历史状态,并基于历史状态修正模型预测

  • 该实现基于论文: MemPrompt

    • 即当模型出错了之后,用户可以反馈模型错误的地方,然后这些反馈会被添加到 memory 中,以后遇到类似问题时模型会提前找到用户的反馈,从而避免犯同样的错
  • 对话任务中的 ConversationChain 示例(ConversationBufferMemory 模式),verbose=True 会输出对话任务中的 prompt,可以看到之前聊天会作为短期 memory 加在 prompt 中,从而让模型能有短时间的记忆能力:

from langchain import OpenAI, ConversationChain
llm = OpenAI(temperature=0)
conversation = ConversationChain(llm=llm, verbose=True)
conversation.predict(input="Hi there!") # 返回如下
#> Entering new ConversationChain chain...
#Prompt after formatting:
#The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific #details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.

#Current conversation:

#Human: Hi there!
#AI:

#> Finished chain.
# Out[53]: " Hi there! It's nice to meet you. How can I help you today?"
conversation.predict(input="I'm doing well! Just having a conversation with an AI.") # 返回如下
#Prompt after formatting:
#The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific #details from its context. If the AI does not know the answer to a question, it truthfully says it does not know.

#Current conversation:

#Human: Hi there!
#AI:  Hi there! It's nice to meet you. How can I help you today?
#Human: I'm doing well! Just having a conversation with an AI.
#AI:

#> Finished chain.
#Out[54]: " That's great! It's always nice to have a conversation with someone new. What would you like to talk about?"
  • LangChain 这里看起来没有直接在 QA 任务中集成 memprompt,不过可以基于对话任务来测试之前 QA 任务中出错的问题,可以看到基于 memprompt 确实可以利用用户的反馈来修正模型预测结果:
conversation.predict(input="what word is similar to good?") # 返回 ' Synonyms for "good" include excellent, great, fine, and superb.'
conversation.predict(input="similar to means with similar pronunciation") # 返回 ' Ah, I see. Synonyms for "good" with similar pronunciation include wood, hood, and should.'

这里的实现看起来和 memprompt 非常类似,每个问题不会直接回答答案,而是回答 understating+answer,从而让用户可以基于对 understating 的理解来判断模型反馈是否符合用户的预期,而不用直接判断 answer 的正确性

  • 对话任务中的其他几种 memory 添加模式

    • ConversationSummaryMemory:与 ConversationBufferMemory 类似,不过之前的对话会被总结为一个 summary 加在 prompt 中
    • ConversationBufferWindowMemory:在 ConversationBufferMemory 模式基础上加个滑窗,即只加入最近几次对话的记录,避免 memory buffer 过大
    • ConversationSummaryBufferMemory:结合以上两种方式,将之前的对话总结为一个 summary 加在 prompt 中,同时会设置一个 prompt 最大词汇数量,超过该词汇数量的时候会抛弃更早的对话来使 prompt 的词汇数量符合要求
  • 更高级的 memory 使用方式

    • Adding Memory to a Multi-Input Chain:主要用于 QA 任务,用一个语料库作为 memory,对于输入的 prompt,找到与该 prompt 类似的信息加在 prompt 中,从而能利用上语料库中的信息
    • Adding Memory to an Agent:对于具备 google 搜索功能的 Agent,可以将对话历史记录到 memory 中,从而能让 Agent 对某些与之前历史结合的对话理解更准确

总结

  • LangChain 基于 OPENAI 的 GPT3 等大语言模型设计一系列便于集成到实际应用中的接口,降低了在实际场景中部署大语言模型的难度

自然语言处理大模型大语言模型BLOOM推理工具测试

相关博客
【自然语言处理】【大模型】大语言模型BLOOM推理工具测试
【自然语言处理】【大模型】GLM-130B:一个开源双语预训练语言模型
【自然语言处理】【大模型】用于大型Transformer的8-bit矩阵乘法介绍
【自然语言处理】【大模型】BLOOM:一个176B参数且可开放获取的多语言模型
【自然语言处理】【大模型】PaLM:基于Pathways的大语言模型
【自然语言处理】【chatGPT系列】大语言模型可以自我改进
【自然语言处理】【ChatGPT系列】WebGPT:基于人类反馈的浏览器辅助问答
【自然语言处理】【ChatGPT系列】FLAN:微调语言模型是Zero-Shot学习器
【自然语言处理】【ChatGPT系列】ChatGPT的智能来自哪里?
【自然语言处理】【ChatGPT系列】大模型的涌现能力

​ BLOOM的原理见【自然语言处理】【大模型】BLOOM:一个176B参数且可开放获取的多语言模型

​ BLOOM是由HuggingFace推出的大模型,其参数量达到176B(GPT-3是175B)。目前超过100B参数量且能够支持中文的开源大模型只有BLOOM和GLM-130B。由于HuggingFace是著名开源工具Transformers的开发公司,很多推理工具都会支持Transformers中的模型。

​ LLM(大语言模型)推理的两个问题:(1) 单张显卡无法容纳整个模型;(2) 推理速度太慢。本文初步整理了一些推理大模型的工具和代码,并简单测试了推理速度。下面是本文测试的一些背景:

  • 目前是2023年2月

  • 使用7B模型bloom-7b1-mt

  • 4张3090(但在实际推理中仅使用2张3090)

  • 依赖包的版本

    transformers==4.26.0
    tensor-parallel==1.0.24
    deepspeed==0.7.7
    bminf==2.0.1
    

零、辅助函数

# utils.py
import numpy as np
  
from time import perf_counter

def measure_latency(model, tokenizer, payload, device, generation_args=):
    input_ids = tokenizer(payload, return_tensors="pt").input_ids.to(device)
    latencies = []
    # 预热
    for _ in range(2):
        _ =  model.generate(input_ids, **generation_args)
    # 统计时间
    for _ in range(10):
        start_time = perf_counter()
        _ = model.generate(input_ids, **generation_args)
        latency = perf_counter() - start_time
        latencies.append(latency)
    # 计算统计量
    time_avg_ms = 1000 * np.mean(latencies) # 延时均值
    time_std_ms = 1000 * np.std(latencies) # 延时方差
    time_p95_ms = 1000 * np.percentile(latencies,95) # 延时的95分位数
    return f"P95延时 (ms) - time_p95_ms; 平均延时 (ms) - time_avg_ms:.2f +\\- time_std_ms:.2f;"

def infer(model, tokenizer, payload, device):
    input_ids = tokenizer(payload, return_tensors="pt").input_ids.to(device)
    logits = model.generate(input_ids, num_beams=1, max_length=512)
    out = tokenizer.decode(logits[0].tolist())
    return out

一、层并行

​ BLOOM是Huggingface开发的,所以在transformers库中提供了支持。具体来说,在使用from_pretrained加载模型时,指定参数devce_map即可。其通过将模型的不同层放置在不同的显卡上,从而将单个大模型分拆至多张卡上(流水线并行也会将层分拆,然后采用流水线的方式训练模型)。下面是调用的示例代码:

# layer_parallel_test.py
import os
import transformers

from utils import measure_latency, infer
from transformers import AutoTokenizer, AutoModelForCausalLM

transformers.logging.set_verbosity_error()
os.environ['CUDA_VISIBLE_DEVICES'] = "0,1"

def run():
    model_name = "bigscience/bloomz-7b1-mt"
    payload = """
    参考下面的文章,然后用与文章相同的语言回答问题: 段落:当细菌突破免疫系统的防御而开始增生时,疾病会由结核菌感染进展到症状明显的结核病。在原发型结核病 (占 1-5% 的比例),这种现象会在感染刚开始的时候很快的发生。然而>多数人感染模式为潜伏结核感染,通常没有明显症状。在5-10%潜伏结合感染的案例中,这些休眠的细菌经常会在感染后数年的时间制造出活动的结核。 问题:What is the next stage after TB infection?
    """
    tokenizer = AutoTokenizer.from_pretrained(model_name)
    model = AutoModelForCausalLM.from_pretrained(model_name, device_map="auto")
    model = model.eval()
    out = infer(model, tokenizer, payload, model.device)
    print("="*70+" 模型输入输出 "+"="*70)
    print(f"模型输入: payload")
    print(f"模型输出: out")
    print("\\n\\n"+"="*70+" 模型延时测试 "+"="*70)
    print(measure_latency(model, tokenizer, payload, model.device))
    print("\\n\\n"+"="*70+" 显存占用 "+"="*70)
    print(os.system("nvidia-smi"))

if __name__ == "__main__":
    run()
    pass

模型的时延结果:

P95延时 (ms) - 118.402308691293; 平均延时 (ms) - 117.72 +- 0.58;

显存占用:

二、张量并行

​ 张量并行是将矩阵乘法进行分块,从而将大矩阵拆分为更小的矩阵,这样就能把不同的矩阵放置在不同的显卡上。(具体原理会在后续的文章中介绍)

​ 这里使用开源工具包tensor_parallel来实现。

# tensor_parallel_test.py
import os
import transformers
import tensor_parallel as tp

from utils import measure_latency, infer
from transformers import AutoTokenizer, AutoModelForCausalLM

transformers.logging.set_verbosity_error()
os.environ['CUDA_VISIBLE_DEVICES'] = "0,1"

def run():
    model_name = "bigscience/bloomz-7b1-mt"
    payload = """
    参考下面的文章,然后用与文章相同的语言回答问题: 段落:当细菌突破免疫系统的防御而开始增生时,疾病会由结核菌感染进展到症状明显的结核病。在原发型结核病 (占 1-5% 的比例),这种现象会在感染刚开始的时候很快的发生。然而>多数人感染模式为潜伏结核感染,通常没有明显症状。在5-10%潜伏结合感染的案例中,这些休眠的细菌经常会在感染后数年的时间制造出活动的结核。 问题:What is the next stage after TB infection?
    """
    tokenizer = AutoTokenizer.from_pretrained(model_name)
    model = AutoModelForCausalLM.from_pretrained(model_name, low_cpu_mem_usage=True)
    model = tp.tensor_parallel(model, ["cuda:0", "cuda:1"])
    model = model.eval()
    out = infer(model, tokenizer, payload, model.device)
    print("="*70+" 模型输入输出 "+"="*70)
    print(f"模型输入: payload")
    print(f"模型输出: out")
    print("\\n\\n"+"="*70+" 模型延时测试 "+"="*70)
    print(measure_latency(model, tokenizer, payload, model.device))
    print("\\n\\n"+"="*70+" 显存占用 "+"="*70)
    print(os.system("nvidia-smi"))

if __name__ == "__main__":
    run()
    pass

模型的时延结果:

P95延时 (ms) - 91.34029923006892; 平均延时 (ms) - 90.66 +- 0.46;

显存占用:

三、模型量化

​ 原理见【自然语言处理】【大模型】用于大型Transformer的8-bit矩阵乘法介绍

​ 量化是一种常见的模型压缩技术,核心思想是将模型参数从高精度转换为低精度。在BLOOM上使用8-bit量化只需要在调用from_pretrained时,设置参数load_in_8bit=True, device_map="auto"

​ (注:bloom在实现量化时,会按照是否超越阈值来分拆矩阵,然后对低于阈值的模型参数进行量化,这会拖慢推理速度)

# int8_test.py
import os
import transformers

from utils import measure_latency, infer
from transformers import AutoTokenizer, AutoModelForCausalLM

transformers.logging.set_verbosity_error()
os.environ['CUDA_VISIBLE_DEVICES'] = "0,1"

def run():
    model_name = "bigscience/bloomz-7b1-mt"
    payload = """
    参考下面的文章,然后用与文章相同的语言回答问题: 段落:当细菌突破免疫系统的防御而开始增生时,疾病会由结核菌感染进展到症状明显的结核病。在原发型结核病 (占 1-5% 的比例),这种现象会在感染刚开始的时候很快的发生。然而>多数人感染模式为潜伏结核感染,通常没有明显症状。在5-10%潜伏结合感染的案例中,这些休眠的细菌经常会在感染后数年的时间制造出活动的结核。 问题:What is the next stage after TB infection?
    """
    max_memory_mapping = 0: "24GB", 1: "0GB"
    tokenizer = AutoTokenizer.from_pretrained(model_name)
    model = AutoModelForCausalLM.from_pretrained(model_name, load_in_8bit=True, device_map="auto", max_memory=max_memory_mapping)
    model = model.eval()
    out = infer(model, tokenizer, payload, model.device)
    print("="*70+" 模型输入输出 "+"="*70)
    print(f"模型输入: payload")
    print(f"模型输出: out")
    print("\\n\\n"+"="*70+" 模型延时测试 "+"="*70)
    print(measure_latency(model, tokenizer, payload, model.device))
    print("\\n\\n"+"="*70+" 显存占用 "+"="*70)
    print(os.system("nvidia-smi"))

if __name__ == "__main__":
    run()
    pass

模型的时延结果:

P95延时 (ms) - 147.89210632443428; 平均延时 (ms) - 143.30 +- 3.02;

显存占用:

四、DeepSpeed-Inference

​ DeepSpeed-Inference是分布式训练工具DeepSpeed中用户模型推理的功能。

# deepspeed_test.py
import os
import torch
import deepspeed
import transformers

from utils import measure_latency, infer
from transformers import AutoTokenizer, AutoModelForCausalLM

transformers.logging.set_verbosity_error()
os.environ['CUDA_VISIBLE_DEVICES'] = "0,1"

def run():
    model_name = "bigscience/bloomz-7b1-mt"
    payload = """
    参考下面的文章,然后用与文章相同的语言回答问题: 段落:当细菌突破免疫系统的防御而开始增生时,疾病会由结核菌感染进展到症状明显的结核病。在原发型结核病 (占 1-5% 的比例),这种现象会在感染刚开始的时候很快的发生。然而>多数人感染模式为潜伏结核感染,通常没有明显症状。在5-10%潜伏结合感染的案例中,这些休眠的细菌经常会在感染后数年的时间制造出活动的结核。 问题:What is the next stage after TB infection?
    """
    tokenizer = AutoTokenizer.from_pretrained(model_name)
    model = AutoModelForCausalLM.from_pretrained(model_name, torch_dtype=torch.float16)
    model = deepspeed.init_inference(
            model=model,      # Transformers模型
            mp_size=2,        # 模型并行数量
            dtype=torch.float16, # 权重类型(fp16)
            replace_method="auto", # 让DS自动替换层
            replace_with_kernel_inject=True, # 使用kernel injector替换
            )
    out = infer(model, tokenizer, payload, model.module.device)
    print("="*70+" 模型输入输出 "+"="*70)
    print(f"模型输入: payload")
    print(f"模型输出: out")
    print("\\n\\n"+"="*70+" 模型延时测试 "+"="*70)
    print(measure_latency(model, tokenizer, payload, model.module.device))
    print("\\n\\n"+"="*70+" 显存占用 "+"="*70)
    print(os.system("nvidia-smi"))


if __name__ == "__main__":
    run()
    pass

这里不能使用python来自动脚本,需要使用下面的命令:

deepspeed --num_gpus 2 --master_port 60000 deepspeed_test.py

模型的时延结果:

P95延时 (ms) - 31.88958093523979; 平均延时 (ms) - 30.75 +- 0.64;

显存占用:

五、BMInf

​ BMInf能够在单张显卡下加载完整的模型,但是推理速度非常慢(应该是利用了Offload技术)。

import os
import bminf
import transformers

from utils import measure_latency, infer
from transformers import AutoTokenizer, AutoModelForCausalLM

transformers.logging.set_verbosity_error()
os.environ['CUDA_VISIBLE_DEVICES'] = "0,1"

def run():
    model_name = "bigscience/bloomz-7b1-mt"
    payload = """
    参考下面的文章,然后用与文章相同的语言回答问题: 段落:当细菌突破免疫系统的防御而开始增生时,疾病会由结核菌感染进展到症状明显的结核病。在原发型结核病 (占 1-5% 的比例),这种现象会在感染刚开始的时候很快的发生。然而>多数人感染模式为潜伏结核感染,通常没有明显症状。在5-10%潜伏结合感染的案例中,这些休眠的细菌经常会在感染后数年的时间制造出活动的结核。 问题:What is the next stage after TB infection?
    """
    tokenizer = AutoTokenizer.from_pretrained(model_name)
    model = AutoModelForCausalLM.from_pretrained(model_name, low_cpu_mem_usage=True)
    model = model.eval()
    model = bminf.wrapper(model, quantization=False, memory_limit=8 << 30)
    out = infer(model, tokenizer, payload, model.device)
    print("="*70+" 模型输入输出 "+"="*70)
    print(f"模型输入: payload")
    print(f"模型输出: out")
    print("\\n\\n"+"="*70+" 模型延时测试 "+"="*70)
    print(measure_latency(model, tokenizer, payload, model.device))
    print("\\n\\n"+"="*70+" 显存占用 "+"="*70)
    print(os.system("nvidia-smi"))

if __name__ == "__main__":
    run()
    pass

模型的时延结果:

P95延时 (ms) - 719.2403690889478; 平均延时 (ms) - 719.05 +- 0.14;

显存占用:

六、结论

  • DeepSpeed-Inference的速度是最快的;
  • 张量并行比自带的层并行快一些;
  • 8 bit量化虽然速度慢一些,但是能够实现单卡推理;
  • BMInf虽然速度最慢,但是其可能在不损失模型精度的情况下,单卡推理;

说明

  • 本文并不是这些推理工具的最佳实践,仅是罗列和展示这些工具如何使用;
  • 这些工具从不同的角度来优化模型推理,对于希望进一步了解具体如何实现的人来说,可以阅读源代码;

以上是关于大语言模型集成工具 LangChain的主要内容,如果未能解决你的问题,请参考以下文章

LangChain vs Semantic Kernel

自然语言处理大模型大语言模型BLOOM推理工具测试

吴恩达教授AI课程:LangChain扩散模型,用ChatGPT API搭建系统

REALM:将检索集成到语言表征模型,搞定知识密集型任务!

《安富莱嵌入式周报》第310期:集成大语言模型的开源调试器ChatDBG, 多功能开源计算器,M7内核航空航天芯片评估板, Zigbee PRO规范

如何创建高效的Prompt和ChatGPT等大语言模型AI对话