4.1、ipex-llm(原bigdl-llm)构建聊天机器人

Source

ipex-llm环境配置及模型下载

1、加载模型

from ipex_llm.transformers import AutoModelForCausalLM
from transformers import AutoTokenizer

model_path = 'THUDM/chatglm3-6b'
model = AutoModelForCausalLM.from_pretrained(model_path,
                                  load_in_4bit=True,
                                  trust_remote_code=True)
# model_in_8bit = AutoModelForCausalLM.from_pretrained(pretrained_model_name_or_path=model_path,
#                                                      load_in_low_bit="sym_int8",
#                                                      trust_remote_code=True)
tokenizer = AutoTokenizer.from_pretrained(model_path,
                                          trust_remote_code=True)

2、保存并加载低精度模型

# 保存低精度模型
save_directory='./chatglm3-6b-ipex-llm-4-bit'
model_in_4bit.save_low_bit(save_directory)
del(model_in_4bit)

# 建议将 tokenizer 保存在与优化后的模型相同的路径中,以简化后续的加载过程:
tokenizer.save_pretrained(save_directory)
# 加载低精度模型
model_in_4bit = AutoModelForCausalLM.load_low_bit(save_directory)
tokenizer = LlamaTokenizer.from_pretrained(save_directory)

3、运行模型

3.1 对话

大语言模型的一个常见应用是聊天机器人 (Chatbot), LLM 可以参与进其中的互动对话。聊天机器人的互动并没有什么魔法——它依然依赖于 LLM 预测以及生成下一个 token. 为了让 LLM 对话,我们需要将 prompt 适当的格式化为对话格式。
首先,定义对话上下文格式,以便模型完成对话:

SYSTEM_PROMPT = "You are a helpful, respectful and honest assistant, who always answers as helpfully as possible, while being safe."

def format_prompt(input_str, chat_history):
    prompt = [f'<s>[INST] <<SYS>>\n{
      
        SYSTEM_PROMPT}\n<</SYS>>\n\n']
    do_strip = False
    for history_input, history_response in chat_history:
        history_input = history_input.strip() if do_strip else history_input
        do_strip = True
        prompt.append(f'{
      
        history_input} [/INST] {
      
        history_response.strip()} </s><s>[INST] ')
    input_str = input_str.strip() if do_strip else input_str
    prompt.append(f'{
      
        input_str} [/INST]')
    return ''.join(prompt)

接下来,定义 chat 函数,将模型输出持续添加到聊天记录中。这样可以确保对话上下文正确的被格式化从而便于下一次回复的生成:

def chat(model, tokenizer, input_str, chat_history):
    # 通过聊天记录将对话上下文格式化为 prompt
    prompt = format_prompt(input_str, chat_history)
    input_ids = tokenizer.encode(prompt, return_tensors="pt")

    # 预测接下来的 token,同时施加停止的标准
    output_ids = model.generate(input_ids,
                                max_new_tokens=32)

    output_str = tokenizer.decode(output_ids[0][len(input_ids[0]):], # 在生成的 token 中跳过 prompt
                                  skip_special_tokens=True)
    print(f"Response: {
      
        output_str.strip()}")

    # 将模型的输出添加至聊天记录中
    chat_history.append((input_str, output_str))

IPEX-LLM 优化的低精度模型与所有 Hugging Face transformers API 兼容。因此,除了使用 generate
函数来预测 token,您也可以使用其他的方法,例如 TextGenerationPipeline。

与 LLM 进行互动式多轮对话:

import torch

chat_history = []

while True:
    with torch.inference_mode():
        user_input = input("Input:")
        if user_input == "stop": # 当用户输入 "stop" 时停止对话
          print("Chat with chatglm3-6b stopped.")
          break
        chat(model=model_in_4bit,
             tokenizer=tokenizer,
             input_str=user_input,
             chat_history=chat_history)

Input: What is CPU?
Response: Hello! I’m glad you asked! CPU stands for Central Processing Unit. It’s the part of a computer that performs calculations and executes instructions
Input: What is its difference between GPU?
Response: Great question! GPU stands for Graphics Processing Unit. It’s a specialized type of computer chip that’s designed specifically for handling complex graphical
Input: stop
Chat with chatglm3-6b stopped.

3.2 流式对话

流式对话可以被视作是聊天机器人的进阶功能,其中响应是逐字生成的。在这里,我们通过 transformers.TextIteratorStreamer 定义了 stream_chat 函数:

from transformers import TextIteratorStreamer

def stream_chat(model, tokenizer, input_str, chat_history):
    # 通过聊天记录将对话上下文格式化为 prompt
    prompt = format_prompt(input_str, chat_history)
    input_ids = tokenizer([prompt], return_tensors='pt')

    streamer = TextIteratorStreamer(tokenizer,
                                    skip_prompt=True, # 在生成的 token 中跳过 prompt
                                    skip_special_tokens=True)

    generate_kwargs = dict(
        input_ids,
        streamer=streamer,
        max_new_tokens=128
    )
    
    # 为了确保对生成文本的非阻塞访问,生成过程应在单独的线程中运行
    from threading import Thread
    
    thread = Thread(target=model.generate, kwargs=generate_kwargs)
    thread.start()

    output_str = []
    print("Response: ", end="")
    for stream_output in streamer:
        output_str.append(stream_output)
        print(stream_output, end="")

    # 将模型的输出添加至聊天记录中
    chat_history.append((input_str, ''.join(output_str)))

为了成功观察标准输出中的文本流行为,我们需要设置环境变量 PYTHONUNBUFFERED=1 以确保标准输出流直接发送到终端而不是先进行缓冲。
Hugging Face transformers streamer classes 目前还在开发中,未来可能会发生变化。

可以通过像之前一样允许连续的用户输入来实现人类和机器人之间的互动式、多轮流式对话:

chat_history = []

while True:
    with torch.inference_mode():
        user_input = input("Input:")
        if user_input == "stop": # 当用户输入 "stop" 时停止对话
          print("Stream Chat with Llama 2 (7B) stopped.")
          break
        stream_chat(model=model_in_4bit,
                    tokenizer=tokenizer,
                    input_str=user_input,
                    chat_history=chat_history)