基于 ChatGLM2 和 OpenVINO™ 打造中文聊天助手

openlab_4276841a 更新于 8月前

作者:武卓博士 英特尔 OpenVINO 布道师

          杨亦诚  英特尔 AI 软件工程师

Llama 2是Meta发布了其最新的大型语言模型,Llama2是基于Transformer的人工神经网络,以一系列单词作为输入,递归地预测下一个单词来生成文本。这是一款开源且免费的人工智能模型。此前,由于开源协议问题,Llama 1虽然功能强大,但并不可免费商用。然而,这一次Meta终于推出了免费商用版本Llama 2,借这一机会,我们分享一下如何基于Llama2和OpenVINO工具套件来打造一款聊天机器人。

项目仓库地址:https://github.com/OpenVINO-dev-contest/llama2.openvino

注1:由于Llama2对在模型转换和运行过程中对内存的占用较高,推荐使用支持64Gb以上内存的的服务器终端作为测试平台。

2:本文仅分享部署Llama2原始预训练模型的方法,如需获得自定义知识的能力,需要对原始模型进行Fine-tune;如需获得更好的推理性能,可以使用量化后的模型版本。

模型导出

第一步,我们需要下载Llama2模型,并将其导出为OpenVINO™所支持的IR格式模型进行部署,这里我们使用Optimum-Intel所提供的接口,直接从Hugging Face仓库中下载并生成IR模型。

    ov_model = OVModelForCausalLM.from_pretrained(arg***odel_id,

                                                  compile=False,

                                                  from_transformers=True)

    ov_model.save_pretrained(model_path)

不过在这之前,我们首先需要向Meta申请模型下载的许可,方可开始下载,具体如何发送申请可以参考Llama2仓库中的说明和引导:https://huggingface.co/meta-llama/Llama-2-7b-hf

在运行项目仓库中的export_ir.py脚本后,会在本地指定路径中生成openvino_model.bin和openvino_model.xml,前者为模型参数文件,后者为模型结构文件。

模型部署(方案一)

由于目前Hugging Face的Transformer以及Optimum库都已经支持了Llama2系列模型的部署,一种比较简便和快捷的做法是,直接使用Optimum-Intel来运行整个Llama2 pipeline,由于Optimum中已经预置了完整的问答类模型pipeline: ModelForCausalLM,并进行了深度的集成, 所以我们只需要调用少量接口,并可以轻松调用OpenVINO™推理后端,实现一个简单问答任务的部署。

ov_model = OVModelForCausalLM.from_pretrained(model_path,

                                                  compile=False,

                                                  device=args.device)

ov_model.compile()

generate_ids = ov_model.generate(inputs.input_ids,

                                 max_length=arg***ax_sequence_length)

output_text = tokenizer.batch_decode(generate_ids,

                                     skip_special_tokens=True,

                                     clean_up_tokenization_spaces=False)[0]

这里再简单介绍下什么是Optimum, Optimum库是Hugging Face为了方便开发者在不同的硬件平台部署来自Transformer和Diffuser库的模型,所打造的部署工具,其中的Optimum-Intel库则支持在Intel平台部署模型时,调用OpenVINO™工具套件作为模型的推理后端,提升任务性能。

最终效果如下:

Response: what is openvino ?

OpenVINO is an open-source software framework for deep learning inference that is designed to run on a variety of platforms, including CPUs, GPUs, and FPGAs. It is developed by the OpenVINO Project, which is a collaboration between Intel and the Linux Foundation.

OpenVINO provides a set of tools and libraries for developers to build, optimize, and deploy deep learning models for inference. It supports popular deep learning frameworks such as TensorFlow, PyTorch, and Caffe, and provides a number of features to improve the performance

模型部署(方案二)

由于Optimum仍属于“黑箱”模式,开发者无法充分自定义内在的运行逻辑,所以这里使用的第二种方式则是在脱离Optimum库的情况,仅用OpenVINO™的原生接口部署Llama2模型,并重构pipeline。

整个重构后pipeline如下图所示,Prompt提示会送入Tokenizer进行分词和词向量编码,然后有OpenVINO™推理获得结果(蓝色部分),来到后处理部分,我们会把推理结果进行进一步的采样和解码,最后生成常规的文本信息。这里为了简化流程,仅使用了Top-K作为筛选方法。

图:Llama2问答任务流程

整个pipeline的大部分代码都可以套用文本生成任务的常规流程,其中比较复杂一些的是OpenVINO™推理部分的工作,由于Llama2文本生成任务需要完成多次递归迭代,并且每次迭代会存在cache缓存,因此我们需要为不同的迭代轮次分别准备合适的输入数据。接下来我们详细解构一下模型的运行逻辑:

图:Llama2模型输入输出原理

Llama2模型的输入主要由三部分组成:

· input_id****>是向量化后的提示输入

· attention_mask用来描述输入数据的长度,input_ids需要被计算的数据所在对应位置的attention_mask值用1表示,需要在计算时被丢弃数据用0表示

· past_key_values.x是由一连串数据构成的**,用来保存每次迭代过程中可以被共享的cache.

Llama2模型的输出则由两部分组成:

· Logit****>为模型对于下一个词的预测,或者叫next token

· present.x则可以被看作cache,直接作为下一次迭代的past_key_values.x值

整个pipeline在运行时会对Llama2模型进行多次迭代,每次迭代会递归生成对答案中下一个词的预测,直到最终答案长度超过预设值max_sequence_length,或者预测的下一个词为终止符eos_token_id。

· 第一次迭代

如图所示在一次迭代时(N=1)input_ids为提示语句,此时我们还需要利用Tokenizer分词器将原始文本转化为输入向量,而由于此时无法利用cache进行加速,past_key_values.x系列向量均为空值。

· 第N次迭代

当第一次迭代完成后,会输出对于答案中第一个词的预测Logits,以及cache数据,我们可以将这个Logits作为下一次迭代的input_ids再输入到模型中进行下一次推理(N=2), 此时我们可以利用到上次迭代中的cache数据也就是present.x,而无需将完整的“提示+预测词”一并送入模型,从而减少一些部分重复的计算量。这样周而复始,将当前的预测词所谓一次迭代的输入,就可以逐步生成所有的答案。

聊天机器人

除了Llama 2基础版本,Meta还发布了LLaMA-2-chat ,使用来自人类反馈的强化学习来确保安全性和帮助性, 专门用于构建聊天机器人。相较于问答模型模式中一问一答的形式,聊天模式则需要构建更为完整的对话,此时模型在生成答案的过程中还需要考虑到之前对话中的信息,并将其作为cache数据往返于每次迭代过程中,因此这里我们需要额外设计一个模板,用于构建每一次的输入数据,让模型能够给更充分理解哪些是历史对话,哪些是新的对话问题。

图:Llama2聊天任务流程

这里的text模板是由“引导词+历史记录+当前问题(提示)”三部分构成:

· 引导词:描述当前的任务,引导模型做出合适的反馈

· 历史记录:记录聊天的历史数据,包含每一组问题和答案

· 当前问题:类似问答模式中的问题

def build_inputs(history: list[tuple[str, str]],

                 query: str,

                 system_prompt=DEFAULT_SYSTEM_PROMPT) -> str:

    texts = [f'[INST] <<SYS>>\n{system_prompt}\n<</SYS>>\n\n']

    for user_input, response in history:

        texts.append(

            f'{user_input.strip()} [/INST] {response.strip()} </s><s> [INST] ')

    texts.append(f'{query.strip()} [/INST]')

    return ''.join(texts)

我们采用streamlit框架构建构建聊天机器人的web UI和后台处理逻辑,同时希望该聊天机器人可以做到实时交互,实时交互意味着我们不希望聊天机器人在生成完整的文本后再将其输出在可视化界面中,因为这个需要用户等待比较长的时间来获取结果,我们希望在用户在使用过程中可以逐步看到模型所预测的每一个词,并依次呈现。因此需要利用Hugging Face的TextIteratorStreamer组件,基于其构建一个流式的数据处理pipeline,此处的streamer为一个可以被迭代的对象,我可以可以依次获取模型迭代过程中每一次的预测结果,并将其依次添加到最终答案中,并逐步呈现。

        streamer = TextIteratorStreamer(self.tokenizer,

                                        skip_prompt=True,

                                        skip_special_tokens=True)

        generate_kwargs = dict(model_inputs,

                               streamer=streamer,

                               max_new_token***ax_generated_tokens,

                               do_sample=True,

                               top_p=top_p,

                               temperature=float(temperature),

                               top_k=top_k,

                               eos_token_id=self.tokenizer.eos_token_id)

        t = Thread(target=self.ov_model.generate, kwargs=generate_kwargs)

        t.start()

        # Pull the generated text from the streamer, and update the model output.

        model_output = ""

        for new_text in streamer:

            model_output += new_text

            yield model_output

        return model_output

当完成任务构建后,我们可以通过streamlit run chat_streamlit.py命令启动聊天机器,并访问本地地址进行测试。这里选择了几个常用配置参数,方便开发者根据机器人的回答准确性进行调整:

· max_tokens: 生成句子的最大长度。

· top-k: 从置信度对最高的k个答案中随机进行挑选,值越高生成答案的随机性也越高。

· top-p: 从概率加起来为p的答案中随机进行挑选, 值越高生成答案的随机性也越高,一般情况下,top-p会在top-k之后使用。

· Temperature: 从生成模型中抽样包含随机性, 高温意味着更多的随机性,这可以帮助模型给出更有创意的输出。如果模型开始偏离主题或给出无意义的输出,则表明温度过高。

3:由于Llama2模型比较大,首次硬件加载和编译的时间会相对比较久

总结

作为当前最火爆的开源大语言模型,Llama2凭借在各大基准测试中出色的成绩,以及支持微调等特性被越来越多开发者所认可和使用。利用Optimum-Intel和OpenVINO™构建Llama2系列任务可以进一步提升其模型在英特尔平台上的性能,并降低部署门槛。

0个评论