指導 |?英特爾?OpenVINO?布道師 武卓博士
排版 |?李擎
基于 Llama2 和 OpenVINO 打造聊天機器人
Llama 2是 Meta 發(fā)布了其最新的大型語言模型,Llama2 是基于 Transformer 的人工神經(jīng)網(wǎng)絡,以一系列單詞作為輸入,遞歸地預測下一個單詞來生成文本。
這是一款開源且免費的人工智能模型。此前,由于開源協(xié)議問題,Llama 1 雖然功能強大,但并不可免費商用。然而,這一次 Meta 終于推出了免費商用版本 Llama 2,借這一機會,我們分享一下如何基于 Llama2 和 OpenVINO 工具套件來打造一款聊天機器人。
?
注1:由于 Llama2 對在模型轉換和運行過程中對內(nèi)存的占用較高,推薦使用支持 128Gb 以上內(nèi)存的的服務器終端作為測試平臺。
注2:本文僅分享部署 Llama2 原始預訓練模型的方法,如需獲得自定義知識的能力,需要對原始模型進行 Fine-tune;如需獲得更好的推理性能,可以使用量化后的模型版本。
模型導出
第一步,我們需要下載 Llama2? 模型,并將其導出為OpenVINO 所支持的 IR 格式模型進行部署,這里我們使用 Optimum-Intel 所提供的接口,直接從 Hugging Face 倉庫中下載并生成 IR 模型。
?
ov_model = OVModelForCausalLM.from_pretrained(args.model_id, compile=False, from_transformers=True) ?ov_model.save_pretrained(model_path)
?
不過在這之前,我們首先需要向 Meta 申請模型下載的許可,方可開始下載,具體如何發(fā)送申請可以參考 Llama2 倉庫中的說明和引導:https://huggingface.co/meta-llama/Llama-2-7b-hf
在運行項目倉庫中的 export_ir.py 腳本后,會在本地指定路徑中生成openvino_model.bin和 openvino_model.xml ,前者為模型參數(shù)文件,后者為模型結構文件。
?
模型部署(方案一)
由于目前 Hugging Face 的 Transformer 以及 Optimum 庫都已經(jīng)支持了 Llama2 系列模型的部署,一種比較簡便和快捷的做法是,直接使用 Optimum-Intel 來運行整個 Llama2 pipeline,由于 Optimum 中已經(jīng)預置了完整的問答類模型 pipeline:? ModelForCausalLM,并進行了深度的集成, 所以我們只需要調用少量接口,并可以輕松調用 OpenVINO 推理后端,實現(xiàn)一個簡單問答任務的部署。
?
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=args.max_sequence_length) output_text = tokenizer.batch_decode(generate_ids, skip_special_tokens=True, ?????????????????????????????????????clean_up_tokenization_spaces=False)[0]
?
這里再簡單介紹下什么是 Optimum。Optimum 庫是 Hugging Face 為了方便開發(fā)者在不同的硬件平臺部署來自 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 仍屬于“黑箱”模式,開發(fā)者無法充分自定義內(nèi)在的運行邏輯,所以這里使用的第二種方式則是在脫離 Optimum 庫的情況,僅用 OpenVINO 的原生接口部署 Llama2 模型,并重構 pipeline。
整個重構后 pipeline 如下圖所示,Prompt 提示會送入 Tokenizer 進行分詞和詞向量編碼,然后有 OpenVINO 推理獲得結果(藍色部分),來到后處理部分,我們會把推理結果進行進一步的采樣和解碼,最后生成常規(guī)的文本信息。這里為了簡化流程,僅使用了 Top-K 作為篩選方法。
圖:Llama2問答任務流程
整個 pipeline 的大部分代碼都可以套用文本生成任務的常規(guī)流程,其中比較復雜一些的是 OpenVINO 推理部分的工作,由于 Llama2 文本生成任務需要完成多次遞歸迭代,并且每次迭代會存在 cache 緩存,因此我們需要為不同的迭代輪次分別準備合適的輸入數(shù)據(jù)。接下來我們詳細解構一下模型的運行邏輯:
圖:Llama2模型輸入輸出原理
Llama2 模型的輸入主要由三部分組成:
· input_ids?是向量化后的提示輸入
·attention_mask?用來描述輸入數(shù)據(jù)的長度, input_ids 需要被計算的數(shù)據(jù)所在對應位置的 attention_mask 值用1表示,需要在計算時被丟棄數(shù)據(jù)用0表示
· past_key_values.x?是由一連串數(shù)據(jù)構成的集合,用來保存每次迭代過程中可以被共享的cache.
Llama2 模型的輸出則由兩部分組成:
· Logits 為模型對于下一個詞的預測,或者叫 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次迭代
當?shù)谝淮蔚瓿珊螅瑫敵鰧τ诖鸢钢械谝粋€詞的預測 Logits,以及 cache 數(shù)據(jù),我們可以將這個 Logits 作為下一次迭代的 input_ids 再輸入到模型中進行下一次推理(N=2), 此時我們可以利用到上次迭代中的 cache 數(shù)據(jù)也就是 present.x,而無需將完整的“提示+預測詞”一并送入模型,從而減少一些部分重復的計算量。這樣周而復始,將當前的預測詞所謂一次迭代的輸入,就可以逐步生成所有的答案。
聊天機器人
除了 Llama 2 基礎版本,Meta 還發(fā)布了 LLaMA-2-chat ,使用來自人類反饋的強化學習來確保安全性和幫助性, 專門用于構建聊天機器人。相較于問答模型模式中一問一答的形式,聊天模式則需要構建更為完整的對話,此時模型在生成答案的過程中還需要考慮到之前對話中的信息,并將其作為 cache 數(shù)據(jù)往返于每次迭代過程中,因此這里我們需要額外設計一個模板,用于構建每一次的輸入數(shù)據(jù),讓模型能夠給更充分理解哪些是歷史對話,哪些是新的對話問題。
圖:Llama2聊天任務流程
這里的 text 模板是由“引導詞+歷史記錄+當前問題(提示)”三部分構成:
· 引導詞:描述當前的任務,引導模型做出合適的反饋
· 歷史記錄:記錄聊天的歷史數(shù)據(jù),包含每一組問題和答案
· 當前問題:類似問答模式中的問題
?
def build_inputs(history: list[tuple[str, str]], query: str, system_prompt=DEFAULT_SYSTEM_PROMPT) -> str: texts = [f'[INST] <> {system_prompt} < > '] for user_input, response in history: texts.append( f'{user_input.strip()} [/INST] {response.strip()}[INST] ') texts.append(f'{query.strip()} [/INST]') ????return?''.join(texts)
?
我們采用 streamlit 框架構建構建聊天機器人的 web ?UI 和后臺處理邏輯,同時希望該聊天機器人可以做到實時交互,實時交互意味著我們不希望聊天機器人在生成完整的文本后再將其輸出在可視化界面中,因為這個需要用戶等待比較長的時間來獲取結果,我們希望在用戶在使用過程中可以逐步看到模型所預測的每一個詞,并依次呈現(xiàn)。因此需要利用 Hugging Face 的 TextIteratorStreamer 組件,基于其構建一個流式的數(shù)據(jù)處理 pipeline,此處的 streamer 為一個可以被迭代的對象,我可以依次獲取模型迭代過程中每一次的預測結果,并將其依次添加到最終答案中,并逐步呈現(xiàn)。
?
streamer = TextIteratorStreamer(self.tokenizer, skip_prompt=True, skip_special_tokens=True) generate_kwargs = dict(model_inputs, streamer=streamer, max_new_tokens=max_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 命令啟動聊天機器,并訪問本地地址進行測試。這里選擇了幾個常用配置參數(shù),方便開發(fā)者根據(jù)機器人的回答準確性進行調整:
· max_tokens: 生成句子的最大長度。
· top-k: 從置信度對最高的k個答案中隨機進行挑選,值越高生成答案的隨機性也越高。
· top-p: 從概率加起來為p的答案中隨機進行挑選, 值越高生成答案的隨機性也越高,一般情況下,top-p會在top-k之后使用。
· Temperature: 從生成模型中抽樣包含隨機性, 高溫意味著更多的隨機性,這可以幫助模型給出更有創(chuàng)意的輸出。如果模型開始偏離主題或給出無意義的輸出,則表明溫度過高。
注3:由于Llama2模型比較大,首次硬件加載和編譯的時間會相對比較久
總結
作為當前最火爆的開源大語言模型,Llama2 憑借在各大基準測試中出色的成績,以及支持微調等特性被越來越多開發(fā)者所認可和使用。利用 Optimum-Intel 和 OpenVINO 構建 Llama2 系列任務可以進一步提升其模型在英特爾平臺上的性能,并降低部署門檻。
評論
查看更多