名人博客
程序學到昏 個人主頁 (csdn.net)

從零開始:Windows系統下Qwen2.5大模型的實踐教程(一)
本文將基于Windows系統和CPU環境,使用Qwen2.5系列模型,詳細實踐從大型語言模型的下載、部署到微調的全過程。
程序學到昏 ?·? 2024-11-10 11:41:51 發布
本文將基于Windows系統和CPU環境,使用Qwen2.5系列模型,詳細實踐從大型語言模型的下載、部署到微調的全過程。
1.1、部署開發環境
本地開發環境:windows
(1)python開發環境
①pycharm 安裝pycharm(community版本):www.jetbrains.com/pycharm/dow…
②anaconda 安裝anaconda:清華鏡像源,選擇合適的版本,例如:Anaconda3-2024.06-1-Windows-x86_64.exe 下載anaconda完成后,windows系統下點擊exe文件一路nex即可安裝完成。
③配置anaconda環境變量(非必須): 假設你的anaconda安裝地址為:D:\soft\anaconda;進入系統高級配置,添加系統變量:

然后點擊Path,添加如下變量:
%ANACONDA_HOME%
%ANACONDA_HOME%\Scripts
%ANACONDA_HOME%\Library\mingw-w64\bin%ANACONDA_HOME%\Library\usr\bin
%ANACONDA_HOME%\Library\bin
配置完成后,使用conda
可以看到anaconda已經安裝完成:

④anaconda使用:
一些簡單的命令,幫助我們使用它:
# 安裝一個新的anaconda環境,名為qwen1.5-4b,python版本為3.10conda create -n qwen1.5-4b python=3.10# 查詢安裝的anaconda環境conda env list# 手動切換anaconda環境conda activate qwen1.5-4b# 關閉anaconda環境conda deactivate# 檢查python的版本(當前conda環境下的)python --version
在我們新建完名為qianwen的conda虛擬環境后,去pycharm的setting->Python Interpreter中導入創建好的conda環境即可:

1.2、大模型下載
huggingface:略
modelscape,魔搭社區提供了相應的組件來供使用者下載:
# 安裝ModelScopepip install modelscope# 下載完整模型repomodelscope download --model qwen/Qwen2.5-1.5B# 下載單個文件(以README.md為例)modelscope download --model qwen/Qwen2.5-1.5B README.md
示例如下:
我在base的conda環境下進行安裝相應組件,然后調用modelscope命令進行下載,且該組件具備斷點續傳的功能,如果當前網絡不佳,可以殺死命令行,重新執行命令,已下載的文件內容不會丟失,可以繼續在進度條附近開始下載任務。

2.1、概述
使用:
import torchfrom flask import Flaskfrom flask import requestfrom transformers import (AutoTokenizer, AutoModelForCausalLM, AutoModel,
Qwen2ForCausalLM, Qwen2Tokenizer)# 參數max_new_tokens: int = 512 # 生成響應時最大的新token數
system_prompt = "你是一個專門分類新聞標題的分析模型。你的任務是判斷給定新聞短文本標題的分類。"user_template_prompt = ("請評估以下網購評論的情感,不要返回0或1以外的任何信息,不要返回你的思考過程。"
"輸入正面評論輸出1,輸入負面評論輸出0。輸入如下:{}\n請填寫你的輸出")
eos_token_id = [151645, 151643]
app = Flask(__name__)
model_path = "D:\project\llm\Qwen2.5-1.5B"# tokenizer = AutoTokenizer.from_pretrained(model_path)tokenizer = Qwen2Tokenizer.from_pretrained(model_path)# model = AutoModelForCausalLM.from_pretrained(model_path, device_map='cpu').eval()model = Qwen2ForCausalLM.from_pretrained(model_path, device_map='cpu').eval()# 非流式請求@app.route('/chat', methods=["POST"])
def chat():
# 系統設定和prompt
req_json = request.json
content = user_template_prompt.format(req_json['message'])
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": content}
] print("input: " + content)
input_ids = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
inputs = tokenizer([input_ids], return_tensors="pt").to(model.device)
generated_ids = model.generate(
inputs.input_ids, max_new_tokens=max_new_tokens, eos_token_id=eos_token_id, # 結束令牌,模型生成這個token時,停止生成
)
generated_ids = [
output_ids[len(inputs):] for inputs, output_ids in zip(inputs.input_ids, generated_ids)
] print(f"generated_ids=\n{generated_ids}")
response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0] print(response)
# 使用占位符拼接字符串
return responseif __name__ == '__main__':
app.run(port=8080, host="0.0.0.0")
其中用到的Qwen2ForCausalLM替換了AutoModelForCausalLM,是一個基于Transformer結構的decoder-only模型,它是Qwen大模型的第二代架構。
2.2、參數
通常來說參數越大的大模型。其中需要關注到的一些問題以及使用上需要注意的地方:
①apply_chat_template
用在會話中,告訴大模型接下來應該生成新的東西,并且包含了整個會話的上下文信息。使用如下:
input_ids = tokenizer.apply_chat_template(messages, # 輸入的純文本信息
tokenize=False, # 告訴大模型暫時不需要分詞
add_generation_prompt=True) # 添加一個特殊的標記,告訴大模型接下來應該生成新的文本內容。
輸出如下:

'<|im_start|>system你是一個專門評估網購評論情感的分析模型。你的任務是判斷給定評論是正面還是負面。<|im_end|>
<|im_start|>user請評估以下網購評論的情感,不要返回0或1以外的任何信息,不要返回你的思考過程。如果是正面評論返回1,如果是負面評論返回0:不錯!挺合適<|im_end|>
<|im_start|>assistant'
②eos_token_id
設定了大模型生成文本的分割符,其中eos_token_id
= [151645, 151643],兩個id的含義分別是:
tokenizer.convert_ids_to_tokens(151645) # <|im_end|>
tokenizer.convert_ids_to_tokens(151643) # <|endoftext|>
tokenizer.convert_ids_to_tokens(1773) # 。
這兩個標記與輸入中的標記保持一致。若不設置該值,在未達到最大返回token數之前,對話將不會自動終止,大模型可能會進行不必要的自問自答。

為了控制大模型可能產生的不穩定輸出,設置停用詞是一種有效手段。除了使用 eos_token_id
外,還可以采用以下方法:
generation_config = GenerationConfig(use_cache=True, repetition_penalty=repetition_penalty, do_sample=False, # 取消采樣,使用貪心策略,輸出是確定的
stop_strings="}")generated_ids = model.generate(input_ids, tokenizer=tokenizer, generation_config=generation_config)
對于模型的推理參數,可以統一放置在 GenerationConfig
中。通過 stop_strings
參數(其值可為字符串或字符串列表)來設置停用詞。在上例中,將 }
設為停用詞,這在要求大模型返回JSON數據時尤為有效,能夠有效避免大模型在輸出完整JSON數據后繼續進行不必要的推理。
③repetition_penalty
使用如下:
repetition_penalty float = 1.2 # 用于懲罰重復生成相同token的參數generated_ids = model.generate(
inputs.input_ids,
max_new_tokens=max_new_tokens,
repetition_penalty=repetition_penalty, # 解決問題后面有過多重復問答)
某些時候,大模型也會持續重復之前的對話,直到生成的token數等于max_new_tokens
為止。情況如下:

這個值不宜過低或過高:過低不生效;過高會導致大模型不敢生成正確答案,因為輸入的prompt中攜帶了正確答案。目前看1.2是一個比較合適的值。
④skip_special_tokens=True
這個告訴分詞器在解碼時跳過任何特殊的標記,如結束標記end-of-sequence token
或其他模型特定的標記。
由于我們在上面調用model時設置了停用詞,在大模型推理到停用詞就會返回輸出。如果不設置該參數,則效果如下:
1<|endoftext|>
優化參數之后,效果如下:
|
| gpt-4o | qwen2.5-1.5b | qwen2.5-1.5b_修改后 | qwen2.5-1.5b_微調后 | qwen2.5-3b | qwen2.5-3b_修改后 |
---|
二元分類 |
| 0.96 | 幾乎無法輸出規定格式的結果 | 0.93 | <暫未微調> | 0.62 | 0.91 |
多元分類 | 樣本100條 | 0.93 |
| 0.67 | 0.9 | 0.12 | 0.72 |
| 樣本1000條 | 0.785 |
| 0.579 | 0.796 |
|
|
2.3、總結
本章節主要關注qwen2.5-1.5b_修改后的結果,有兩個主要成果:
1.1、修改了上述等啟動參數之后,大模型能夠正常輸出預期的結果
1.2、對于相對簡單的人呢無
下一章節將進行qwen2.5-1.5b模型的微調
2.3.1、最終代碼
最終代碼如下:
import torchfrom flask import Flaskfrom flask import requestfrom transformers import (AutoTokenizer, AutoModelForCausalLM, AutoModel,
Qwen2ForCausalLM, Qwen2Tokenizer)from peft import PeftModel# 參數max_new_tokens: int = 64 # 生成響應時最大的新token數temperature: float = 0.6 # 控制生成文本的隨機性top_p: float = 0.9 # 用于概率限制的參數,有助于控制生成文本的多樣性top_k: int = 32 # 控制生成過程中每一步考慮的最可能token的數量repetition_penalty: float = 1.2 # 用于懲罰重復生成相同token的參數system_template_prompt = "你是一個專門評估網購評論情感的分析模型。你的任務是判斷給定評論是正面還是負面。"system_prompt = "你是一個專門分類新聞標題的分析模型。你的任務是判斷給定新聞短文本標題的分類。"user_template_prompt = ("請評估以下評論,不要返回0或1以外的任何信息,不要返回你的思考過程。"
"如果是正面評論輸出1,是反面評論輸出0。輸入如下:{}\n請填寫你的輸出:")
user_prompt = ("請將以下新聞短文本標題分類到以下類別之一:故事、文化、娛樂、體育、財經、房產、汽車、教育、"
"科技、軍事、旅游、國際、股票、農業、游戲。輸入如下:\n{}\n請填寫你的輸出:")
eos_token_id = [151645, 151643]
app = Flask(__name__)
lora_model_path = "./output/Qwen2.5-1.5b/checkpoint-100"model_path = "D:\project\llm\Qwen2.5-1.5B"# 從指定路徑加載大模型的分詞器(tokenizer),用于加載預訓練的文本處理模型(Tokenizer),以便將文本數據轉換為模型可以接受的輸入格式。tokenizer = Qwen2Tokenizer.from_pretrained(model_path)# AutoModelForCausalLM更適合語言大模型model = Qwen2ForCausalLM.from_pretrained(model_path, device_map='cpu').eval()# 非流式請求@app.route('/chat_old', methods=["POST"])def chat_old(): # 系統設定和prompt
req_json = request.json
content = user_template_prompt.format(req_json['message'])
messages = [
{"role": "system", "content": system_template_prompt},
{"role": "user", "content": content}
] # 使用tokenizer將整個會話轉換成模型可以理解的input_ids,并將這些input_ids輸入到模型
# tokenize=False 表示不要立即分詞,只是使用apply_chat_template將會話進行格式化
input_ids = tokenizer.apply_chat_template(messages,
tokenize=False,
add_generation_prompt=True) print(f"input:{input_ids}")
inputs = tokenizer([input_ids], return_tensors="pt").to(model.device)
generated_ids = model.generate(
inputs.input_ids,
max_new_tokens=max_new_tokens,
repetition_penalty=repetition_penalty, # 解決問題后面有過多重復問答(對重復token的懲罰)
eos_token_id=eos_token_id, # 結束令牌,模型生成這個token時,停止生成
)
generated_ids = [
output_ids[len(inputs):] for inputs, output_ids in zip(inputs.input_ids, generated_ids)
] print(f"generated_ids=\n{generated_ids}")
response = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)[0]
response = response.encode('utf-8', errors='ignore').decode('utf-8') print(response) # 使用占位符拼接字符串
return response@app.route('/chat', methods=["POST"])def chat(): # 系統設定和prompt
req_json = request.json
prompt = user_prompt.format(req_json['message']) print(prompt) # 非會話的輸入方式,將單句話進行分詞成token ids
inputs = tokenizer(prompt, return_tensors="pt")
input_ids = inputs.input_ids # Generate
generate_ids = model.generate(input_ids=input_ids,
bos_token_id=151645, # 開始令牌(在生成文本時,模型會在輸入序列的末尾添加這個令牌,以指示新生成的文本的開始。)
max_new_tokens=len(input_ids) + 1,
repetition_penalty=repetition_penalty) print(generate_ids)
response = tokenizer.batch_decode(generate_ids, skip_special_tokens=True)[0] print(response) # # 去掉response中的包含prompt的文本
response = response[len(prompt):] return response.strip()
if __name__ == '__main__':
app.run(port=8080, host="0.0.0.0")
2.3.2、數據集
數據集如下:
二元分類數據集:電商平臺評論數據集
多元分類數據集:今日頭條文本分類數據集 / 數據集 / HyperAI超神經
其他:天池數據集、ChineseNlpCorpus
聲明:本網站所提供的信息僅供參考之用,并不代表本網站贊同其觀點,也不代表本網站對其真實性負責。