第十章 大語言模型安裝與應用
第十章簡介
本章介紹大語言模型(Large Language Model)的本地安裝、推理分析,包括國內主流開源大模型,Qwen系列、ChatGLM系列、Baichuan系列和Yi。同時為了輔助大家瞭解LLM的能力邊界和應用,分析一個54K stars的LLM應用工具——GPT Academic(GPT 學術優化)。
通過本章可以瞭解到四個國產開源大模型的:
- 安裝部署環境準備
- 模型代碼結構設計方案
- LLM推理流程步驟
- 多輪對話prompt組裝方式
- 顯存佔用與上下文長度關係趨勢
- LLM內置角色system、user、assistant、observation的作用與區別
10.1 Qwen部署與分析
前言
隨著大語言模型的出圈及廣泛應用落地,LLM技術已成為新一代人工智慧、深度學習的必備技能,本節將介紹當下較為熱門的開源大語言模型Qwen的安裝及代碼、流程、顯存消耗分析。
本節內容有3個內容,有助於理解LLM模型,包括:
- Qwen模型代碼結構組織與介面分析:可瞭解LLM模型的代碼是如何設計的
- LLM模型多輪對話機制:逐步剖析LLM的推理機制,多輪對話時如何組裝歷史聊天記錄的
- LLM模型顯存佔用:瞭解顯存隨著上下文長度的增加的變化趨勢
Qwen簡介
通義千問是阿裡巴巴集團Qwen小組研發的大語言模型,自2023年4月開放測試8月開源7B模型,12月開源72B;到2024年3月開放了Qwen1.5-32B,可最大限度兼顧性能、效率和記憶體佔用的平衡,Qwen系列不斷更新,為開源社區做出了貢獻。
Qwen系列產品
通義千問僅是Qwen團隊產品之一,屬於對話模型,能理解人類語言、生成內容,作為使用者生活和工作的智慧助手。
- 通義,取自《漢書》中的“天地之常經,古今之通義也”,有“普遍適用的道理與法則”之意。官網展示,通義是致力於實現類人智慧的通用智慧
- 千問,千萬次的問,千萬的學問。
除了通義千問,Qwen團隊還有其他大模型,共計10個(截止至2024年4月16日),分別是:
通義千問、通義萬相、通義聽悟、通義點金、通義靈碼、通義法睿、通義星塵、通義仁心、通義曉蜜和通義智文。
Qwen目前開源Qwen1.5系列,屬於2.0的pre版,相較於1.0在更方面指標均有所提升,並且開放了0.5B, 1.8B, 4B, 7B, 14B, 32B, 72B多個尺寸,同時有base和chat版,也有多種精度fp16, int8, int4。開發者可根據不同的場景、硬體平臺、任務複雜度來選擇模型。
為了更全面瞭解Qwen系列產品,推薦以下連結:
- 官方產品主頁:https://tongyi.aliyun.com/
- Qwen團隊github主頁:https://github.com/QwenLM
- Qwen開來源文件:https://qwen.readthedocs.io/en/latest/
- Qwen技術報告:https://arxiv.org/abs/2309.16609
- Qwen的HuggingFace主頁:https://huggingface.co/Qwen
本地部署安裝
Qwen代碼提供了命令交互案例,運行成功後,如下所示:
User: 你是誰 Qwen-Chat: 我是來自阿裡雲的大規模語言模型,我叫通義千問。我可以回答各種問題、創作文字,還能表達觀點、撰寫代碼。 User: 你還有什麼能力? Qwen-Chat: 我還可以將文本從一種語言翻譯成另一種語言,總結文本,生成文本,寫故事,分析情緒,提供建議,開發演算法,編寫代碼以及任何其他基於語言的任務。 User>
Copy
為了成功安裝Qwen,建議嚴格閱讀github的readme,下面說明本次部署的環境、版本配置。
Qwen官方github建議的版本要求如下:
- python 3.8 and above
- pytorch 1.12 and above, 2.0 and above are recommended
- transformers 4.32 and above
- CUDA 11.4 and above are recommended (this is for GPU users, flash-attention users, etc.)
代碼和模型,採用的事Qwen,不是Qwen1.5,這兩個是不同的代碼倉庫,一定要注意。
筆者的安裝環境如下:
- win11
- python 3.10.14
- pytorch 2.2.0
- transformers 4.39.0
- CUDA 12.1
- RTX 4060 Laptop 8GB
- RAM 32GB
安裝步驟
第一步,下載Qwen代碼
git clone https://github.com/QwenLM/Qwen.git
Copy
第二步,安裝基礎python包
pip install -r requirements.txt
pip install -r requirements_web_demo.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
Copy
如果需要量化版,則需要安裝對應的autogptq等;
pip install auto-gptq optimum
Copy
第三步 下載模型權重
根據顯存大小,選擇合適的模型,這裡選擇1.8B進行使用,通過github資訊,可大約知道不同尺寸模型顯存需求。
Model |
Release Date |
Max Length |
System Prompt Enhancement |
# of Pretrained Tokens |
Minimum GPU Memory Usage of Finetuning (Q-Lora) |
Minimum GPU Usage of Generating 2048 Tokens (Int4) |
Tool Usage |
---|---|---|---|---|---|---|---|
Qwen-1.8B |
23.11.30 |
32K |
✅ |
2.2T |
5.8GB |
2.9GB |
✅ |
Qwen-7B |
23.08.03 |
32K |
❎ |
2.4T |
11.5GB |
8.2GB |
✅ |
Qwen-14B |
23.09.25 |
8K |
❎ |
3.0T |
18.7GB |
13.0GB |
✅ |
Qwen-72B |
23.11.30 |
32K |
✅ |
3.0T |
61.4GB |
48.9GB |
✅ |
Qwen-1.8B-chat下載
下載有兩個地方,分別是HF和國內的ModelScope社區
- HuggingFace下載:https://huggingface.co/Qwen/Qwen-1_8B-Chat/tree/main
- ModelScope: https://www.modelscope.cn/models/qwen/Qwen-1_8B-Chat/files
模型除了權重檔,還有一系列設定檔等資訊,因此需要完整的檔下載才可使用,鑒於網路問題,這裡介紹兩種下載方式:
方式一:純命令列(看網速)
git clone https://www.modelscope.cn/qwen/Qwen-1_8B-Chat.git
Copy
方式二:命令列 + 手動下載權重文件
git clone https://www.modelscope.cn/qwen/Qwen-1_8B-Chat.git
查看資料夾下設定檔是否下載完畢,下載完畢後,ctrl + c停止。
可手動下載兩個.safetensors文件,放到資料夾中,手動下載.safetensors檔,速度高達10+MB/s
Copy
第四步:配置模型路徑
修改 Qwen/cli_demo.py 中權重路徑
DEFAULT_CKPT_PATH = r"G:\04-model-weights\qwen\Qwen-1_8B-Chat"
Copy
第五步,運行cli_demo.py,看到用戶端運行成功(通過日誌看出,這裡採用流式輸出):
C:\Users\yts32\anaconda3\envs\pt220\python.exe D:\github_desktop\Qwen\cli_demo.py
The model is automatically converting to bf16 for faster inference. If you want to disable the automatic precision, please manually add bf16/fp16/fp32=True to "AutoModelForCausalLM.from_pretrained".
Try importing flash-attention for faster inference...
Warning: import flash_attn rotary fail, please install FlashAttention rotary to get higher efficiency https://github.com/Dao-AILab/flash-attention/tree/main/csrc/rotary
Warning: import flash_attn rms_norm fail, please install FlashAttention layer_norm to get higher efficiency https://github.com/Dao-AILab/flash-attention/tree/main/csrc/layer_norm
Warning: import flash_attn fail, please install FlashAttention to get higher efficiency https://github.com/Dao-AILab/flash-attention
Loading checkpoint shards: 100%|██████████| 2/2 [00:02<00:00, 1.04s/it]
Welcome to use Qwen-Chat model, type text to start chat, type :h to show command help.
(歡迎使用 Qwen-Chat 模型,輸入內容即可進行對話,:h 顯示命令説明。)
Note: This demo is governed by the original license of Qwen.
We strongly advise users not to knowingly generate or allow others to knowingly generate harmful content, including hate speech, violence, pornography, deception, etc.
(注:本演示受Qwen的授權合約限制。我們強烈建議,用戶不應傳播及不應允許他人傳播以下內容,包括但不限於仇恨言論、暴力、色情、欺詐相關的有害資訊。)
User> 你好,你是誰?
C:\Users\yts32\.cache\huggingface\modules\transformers_modules\Qwen-1_8B-Chat\modeling_qwen.py:528: UserWarning: 1Torch was not compiled with flash attention. (Triggered internally at C:\cb\pytorch_1000000000000\work\aten\src\ATen\native\transformers\cuda\sdp_utils.cpp:263.)
attn_output = F.scaled_dot_product_attention(
User: 你好,你是誰?
Qwen-Chat: 我是
User: 你好,你是誰?
Qwen-Chat: 我是來自
User: 你好,你是誰?
Copy
需要web ui介面,也可以修改web_demo.py的路徑,然後運行代碼即可
DEFAULT_CKPT_PATH = r"G:\04-model-weights\qwen\Qwen-1_8B-Chat"
Copy
模型代碼結構分析
為了詳細瞭解Qwen模型代碼的設計結構,瞭解大語言模型推理邏輯,在這裡對Qwen的代碼結構進行剖析。
首先關心的是,模型在哪裡創建的,是什麼形式,這個可從cli_demo.py的55行代碼看到
model = AutoModelForCausalLM.from_pretrained(
args.checkpoint_path,
device_map=device_map,
trust_remote_code=True,
resume_download=True,
).eval()
Copy
其中AutoModelForCausalLM是transformers庫的類,那麼它和Qwen的關係又是怎麼樣的呢?下面通過UML類圖來分析兩者關係。
UML類圖分兩部分.
- 左邊的_BaseAutoModelClass和AutoModelForCausalLM是transformers庫的標準設計,基於transformers推理的LLM需要遵循這套規則。
- 右邊則是Qwen的代碼結構設計,包括:
- QWenLMHeadModel:最外層,使用者調用的模型,通過HeadModel就知道這個模型是加入下游任務的head,可被用戶使用的。
- QWenPreTrainedModel:定義為所有預訓練模型類
- QWenModel:定義為基模型,可以看到在QWenLMHeadModel中被初始化為它的一個屬性,用於基座推理,然後加上lm_head: nn.Linear進行具體的token分類任務。
- 再往上,就是transformers庫的基礎類了,包括PreTrainedModel、nn.Module, ModuleUtilsMixin, GenerationMixin, PushToHubMixin, PeftAdapterMixin。這些都是基礎的必備模組。可以看到熟悉的pytorch的nn.Module,以及一系列Mixin類。
補充知識:Mixin類是一種設計模式,表示一些通用的功能放到這裡,其他需要此功能的模組,通過繼承的方式將這些共同行為混入到其他類中。
流式推理流程分析
通過cli_demo.py代碼可看到,使用者輸入的字串是通過這個chat_stream函數實現返回的,下面分析該函數實現的推理流程
def chat_stream(
self,
tokenizer: PreTrainedTokenizer,
query: str,
history: Optional[HistoryType],
system: str = "You are a helpful assistant.",
stop_words_ids: Optional[List[List[int]]] = None,
logits_processor: Optional[LogitsProcessorList] = None,
generation_config: Optional[GenerationConfig] = None,
**kwargs,
) -> Generator[str, Any, None]:
Copy
chat_stream原始程式碼位於C:\Users\yts32.cache\huggingface\modules\transformers_modules\Qwen-7B-Chat-Int4\modeling_qwen.py:
它不在本地倉庫,也不在transformers庫,這是因為HuggingFace提供的通用規則,允許使用者自訂上傳模型代碼進行推理,因此基於本規則,Qwen的代碼會變到.cache資料夾下進行運行。
核心參數說明:
- query:使用者本輪對話所輸入的資訊
- history: 歷史對話資訊,以list存儲,包括使用者輸入的資訊及使用者收到的資訊。(使用者收到的資訊不等價於模型輸出資訊)
- system:用於模型角色設定
- generation_config: 是transformers庫中一個模組,該類中存儲大量與生成相關的配置,例如停止詞、最大輸出長度等
函數核心步驟:
第一步:生成本輪輸入給模型的資訊及tokens。
通過make_context函數,實現需要輸入給模型的上下文資訊組裝,其核心功能包括:
- 設定system角色信息:例如,<|im_start|>system \n You are a helpful assistant.<|im_end|>
- 拼接歷史對話資訊:例如,<|im_start|>user 你是誰<|im_end|> <|im_start|>assistant 我是來自阿裡雲的大規模語言模型,我叫通義千問。我可以回答問題、創作文字,還能表達觀點、撰寫代碼。有什麼我可以幫助你的嗎?<|im_end|> <|im_start|>user 1+1=<|im_end|> <|im_start|>assistant 1+1=2。這是數學中的基本算數運算之一。如果您有任何其他問題或需要任何説明,請隨時告訴我。<|im_end|>
- 將string轉token
說明:模型輸入的資訊中,包含多個段落結構,主要包括三個角色的內容描述。各角色內容之間採用特殊分隔符號來區分。Qwen採用的是<|im_start|>、<|im_end|>。
raw_text, context_tokens = make_context(
tokenizer,
query,
history=history,
system=system,
max_window_size=max_window_size,
chat_format=generation_config.chat_format,
)
raw_text = '<|im_start|>system
You are a helpful assistant.<|im_end|>
<|im_start|>user
你好<|im_end|>
<|im_start|>assistant
'
context_tokens = [151644, 8948, 198, 2610, 525, 264, 10950, 17847, 13, 151645, 198, 151644, 872, 198, 108386, 151645, 198, 151644, 77091, 198]
Copy
第二步,獲取停止詞。
LLM的生成是自回歸的,通常設定一些停止詞,當模型輸出停止詞時,程式會停止推理,返回本輪結果。
stop_words_ids = [[151645], [151644]]
第三步,進入生成器 stream_generator,開始逐token輸出。
for token in self.generate_stream(
input_ids,
return_dict_in_generate=False,
generation_config=stream_config,
logits_processor=logits_processor,
seed=-1,
**kwargs):
outputs.append(token.item())
yield tokenizer.decode(outputs, skip_special_tokens=True, errors='ignore')
Copy
self.generate_stream方法會進入NewGenerationMixin(GenerationMixin)類中的方法進行生成,實際生成token函數是NewGenerationMixin.sample_stream(),這部分屬於transformers_stream_generator庫中功能。
注:NewGenerationMixin.sample_stream()位於site-packages\transformers_stream_generator\main.py
第四步:停止判斷
通過判斷上一個詞是否為停止詞,決定是否跳出while迴圈。
代碼位於transformers_stream_generator\main.py 中NewGenerationMixin.sample_stream()
# if eos_token was found in one sentence, set sentence to finished
if eos_token_id is not None:
unfinished_sequences = unfinished_sequences.mul(
(sum(next_tokens != i for i in eos_token_id)).long()
)
# stop when each sentence is finished, or if we exceed the maximum length
if unfinished_sequences.max() == 0 or stopping_criteria(input_ids, scores):
if not synced_gpus:
break
else:
this_peer_finished = True
Copy
總結一下,chat_stream()內部主要的步驟包括:
- 第一步:生成本輪輸入給模型的資訊及tokens。
- 第二步,獲取停止詞。
- 第三步,進入生成器 stream_generator,開始逐token輸出。
- 第四步:停止條件判斷。
多輪對話Prompt分析
通過代碼分析,可瞭解到LLM模型每次推理都需要把上文資訊全部輸入,計算開銷相當大,並且隨著上下文長度增加,計算量呈平方級增長。
為了更好理解LLM實際的推理過程,下面分析模型的多輪對話過程。
第一輪對話
第一輪對話,使用者輸入“你是誰”,對於LLM而言,實際的輸入資訊包括角色描述、特殊分隔token、分行符號。如下圖所示:
LLM模型執行第一次推理,獲得“我”,然後將“我”拼接到第一次推理的輸入,繼續讓LLM輸出下一個token,直到第N次推理時,模型輸出的是停止符號token,第一輪對話結束。
第二輪對話
第二輪對話,使用者輸入“1+1=”,對於LLM而言,它是無狀態的,因此需要把前面對話資訊一併帶入,作為輸入給模型的資訊,如下圖所示:
第一次推理,給模型輸入的資訊需要包括前序對話輪次的歷史資訊,加上本輪使用者輸入資訊,經過N次LLM模型推理後,模型輸出停止token,本輪對話結束。
通過多輪對話流程分析,可以知道:
- LLM是無狀態的,多輪對話完全依賴於輸入資訊的拼接;
- LLM輸入資訊通常有三個部分。第一個部分,所有推理的開頭是system的描述;第二個部分,使用者的輸入;第三個部分LLM回答的資訊,稱之為assistant。多輪對話時,把歷史對話,迴圈的通過user、assistant進行拼接。
顯存使用與文本長度分析
為了瞭解模型顯存使用與文本長度的關係,這裡做了文本長度與顯存的統計,觀察在transformers庫進行LLM模型推理部署時,顯存使用情況。(修改了的cli_demo.py在github上)
這裡需要特別說明:LLM的推理部署針對顯存使用有大量的優化策略和現成框架,這裡的分析是針對transformers庫,針對其他後端推理框架的適用性需要仔細考量。
LLM模型推理顯存佔用可分兩個部分,模型參數和中間變數。模型參數載入後就佔用固定空間,中間變數則會隨著輸入文本的長度增加和增加。(transformers是沒有KV-Cache機制的,因此沒有額外的顯存消耗)
這裡手動進行對話,並且記錄每一輪對話結束後,輸入+輸出的字串長度,顯存使用情況,繪製折線圖如下所示:
通過上圖分析,可發現:
- 1.8B - fp16模型載入需要3.6GB顯存;
- 3千字元之後,顯存需求呈指數級增長,3千之前還可呈現線性增長趨勢;
- Qwen測試代碼有截斷功能,即將超出顯存上限8GB時,上下文被截斷,以保障模型能運行。
為了進一步瞭解顯存與上下文長度的關係,還進行了7B-int4模型的分析,結果如下圖:
同樣可得出上述的2和3兩點結論。
在此處用的是文本長度,而不是tokens長度,是因為代碼介面返回的只有文本,沒有tokens_ids,但不影響分析結果的趨勢。
詳細代碼參見
小結
本小節對Qwen-1.8B-chat-fp16模型進行了本地安裝部署,以及Qwen系列LLM模型代碼結構的剖析,同時還分析了LLM模型進行多輪對話時的處理邏輯。通過本節的學習,可以瞭解到:
- 通義千問是阿裡Qwen團隊的產品之一,還有其餘9款基於大模型的產品。
- Qwen最新的1.5版支持多尺寸、多精度版模型。
- Qwen系列LLM模型支援三種角色,system, user, assistant。
- LLM模型的多輪對話機制是將歷史資訊拼接起來,類似於微信的聊天記錄。
- LLM一次推理只輸出一個token。
- Qwen模型推理顯存佔用與上下文長度關係。
下一小節,將進行ChatGLM模型的部署及分析。