一、前置准备
1. 下载 LLaMA Factory
https://github.com/hiyouga/LLaMA-Factory.git
搭建过程详见:https://blog.csdn.net/CSBLOG/article/details/144584581
2. 选择 预训练模型 和 prompt指令模型
预训练阶段在实际工作中,一般是用不上的,但我们可以学习了解。为了方便节省资源,我们可以先选个小点的模型试一试。
这里预训练模型选择 通义千问2.5-0.5B-预训练 ,指令模型选择 Qwen2.5-0.5B-Instruct
3. 下载 预训练模型 和 指令模型
下载预训练模型:
git clone https://www.modelscope.cn/Qwen/Qwen2.5-0.5B.git
下载指令模型:
git clone https://www.modelscope.cn/Qwen/Qwen2.5-0.5B-Instruct.git
4. 打开 LLaMA Factory 的webui,修改参数
(1)查看并确保 模型名称 和 模型路径 是否正确
(2)选择推理引擎,实际工作中一般选择 vllm
(3)按需决定是否还需要调整其他参数
5. 对话测试
二、数据处理
1. 数据集构建
通过查看 LLaMA-Factory/data/README_zh.md 文件可知,它需要什么样的数据。
先找一小段文本尝试一下
import json
texts = """
1.流行性感冒:流行性感冒潜伏期多为1~7天,临床表现主要以发热(体温可达39~40℃)、头痛、肌痛和全身不适起病。除了全身症状,常有咽喉痛、干咳,可有鼻塞、流涕、胸骨后不适等。部分有呕吐、腹痛、腹泻等消化道症状。流感病原学检测阳性。
2.过敏性鼻炎:有过敏史,常年打喷嚏和流涕,鼻黏膜苍白伴有瘙痒感,鼻分泌物内嗜酸粒细胞增加等。
3.萎缩性鼻炎:大多是鼻腔通畅,鼻和鼻咽部干燥,鼻分泌物为块状、管筒状脓痂,伴有呼气恶臭、嗅觉减退等症状。
4.血管舒缩性鼻炎:无过敏史,常出现鼻黏膜间歇性血管充盈、打喷嚏和流清涕,吸入干燥空气后症状加重。
5.上呼吸道感染性疾病:如细菌性咽-扁桃体炎,疱疹性咽峡炎等均有其病变部位的特异性体征。前者有咽部充血,扁桃体肿大,表面有脓性分泌物等;后者软腭、咽和扁桃体表面有灰白色疱疹和浅表溃疡伴周围红晕。
"""
samples = []
for text in texts.split("\n"):
if text: # 检查行是否非空
sample = dict(text=text) # 创建一个字典,键为 'text',值为当前行的文本
samples.append(sample) # 将字典添加到 samples 列表中
运行结果:
保存数据
with open(file="samples.json", mode="w", encoding="utf8") as f:
json.dump(obj=samples, fp=f, ensure_ascii=False)
运行后,会在当前目录下生成 samples.json 文件。
想要在 LLaMA-Factory webui 的数据集中显示,还需要参照 README_zh.md 文件中的方式进行注册,因为它是通过dataset_info.json文件去找相关的数据。
再回到 LLaMA Board,即可看到我们新增的数据集
按需添加数据集,然后就可以开始训练了
数据太少,训练没啥效果,接下来,我们用之前酒店评论情感分析的数据集作为示例
2. 数据切分
import pandas as pd
data = pd.read_csv(filepath_or_buffer="samples.tsv", sep="\t", encoding="utf8")
data = data.sample(frac=1)
data.reset_index(drop=True, inplace=True)
# 包含 stop [start, stop]
train = data.loc[1500:, :]
test = data.loc[:1499, :]
print(train.shape)
print(test.shape)
train.to_csv(path_or_buf="train.tsv", sep="\t", index=None)
test.to_csv(path_or_buf="test.tsv", sep="\t", index=None)
3. 数据格式转换
import pandas as pd
import json
from pprint import pp
data = pd.read_csv(filepath_or_buffer="train.tsv", sep="\t", encoding="utf8")
num_samples, num_features = data.shape
results = []
sys_prompt = """
你是一个情感识别专家!请对用户输入的酒店评论做情感分析!
如果是正面的评论,请输出:
{
"sentiment": "正面"
},
如果是负面的评论,请输出:
{
"sentiment": "负面"
}
"""
user_instruction = '{input}'
for idx in range(num_samples):
comment, sentiment = data.loc[idx, :]
# print(comment, sentiment)
result = {
"instruction": comment,
"input": "",
"output": '{"sentiment": "' + sentiment + '"}',
"system": sys_prompt}
# pp(result)
results.append(result)
print(results)
type()
print(results[1000]["system"])
print(results[1000]["instruction"])
print(results[1000]["input"])
print(results[1000]["output"])
with open(file="sentiments.json", mode="w", encoding="utf8") as f:
json.dump(obj=results, fp=f, ensure_ascii=False)
运行成功后,会自动生成 sentiments.json 文件。
(1)将 sentiments.json 文件移动到 LLaMA-Factory/data目录下;
(2)在 LLaMA-Factory/data/dataset_info.json文件里进行注册;
三、微调
1. 修改模型的自我认知
修改 LLaMA-Factory/data/identity.json 文件,将 {{name}} 和 {{author}}分别替换成你需要的名字。
2. 回到 LLaMA Board 页面,选择数据集,微调时,一般选择三类数据。
(1)自我认知(每个项目是必改的,修改identity)
(2)私有数据(针对你的具体任务,需要设计指定格式,转换自己的数据)
(3)公开数据(框架自带)
所以这里的数据集可以选择 identity, sentiment , alpaca_zh_demo,alpaca_en_demo 。
3. 确保模型的路径正确。
4. 小模型,算力足,可以选择 full 全参。再调一下训练轮数、批处理大小、保存间隔可以调大一些, 日志可以改小一点,不需要打那么多日志,其他基本不怎么需要修改,即可开始炼丹啦。
四、多机多卡
1. 打开算力平台,选择多台机器
https://www.autodl.com/login
选了3台机器
2. 下载相关模型文件,操作步骤跟前面差不多。
(1)下载 LLaMA Factory 、下载Qwen模型。
(2)将前面修改好的 identity.json, sentiments.json, dataset_info.json 文件上传过来替换掉。
3. 监控显存
刚开始不要弄的太复杂。打开终端,切换到LLaMA Factory根目录下,输入以下命令:
# 监控显存
watch -n 1 'nvidia-smi'
可以看到目前3张卡都是空的。
4. 修改相关配置文件
参考 LLaMA Factory 说明文档
https://llamafactory.readthedocs.io/zh-cn/latest/advanced/distributed.html
FORCE_TORCHRUN=1,是True的意思,也就是直接用torch来启动。
train examples/train_full/llama3_full_sft_ds3.yaml 配置文件
(1)找到 LLaMA-Factory/examples/train_full 目录
编辑
(2)将该目录下的 llama3_full_sft.yaml 配置文件复制到 autodl 平台的 LLaMA-Factory 目录下,再重命名为: Qwen2.5_0.5B_Instruct_full_sft.yaml
(3)修改Qwen2.5_0.5B_Instruct_full_sft.yaml 文件内容
(4)autodl 平台终端,安装 deepspeed
pip install deepspeed
(5)autodl 平台终端,切换到 LLaMA-Factory 根目录下,输入命令
# 并行训练
FORCE_TORCHRUN=1 llamafactory-cli train QWen2.5_0.5B_Instruct_full_sft.yaml
可以看到,3张卡开始使用了 。
接下来,查看训练结果。
5. 模型微调
(1)启动 LLaMA-Factory(LLaMA Board 可视化微调),终端输入以下命令
llamafactory-cli webui
(2)在 LLaMA Board 可视化微调界面,将模型路径替换为刚才训练成果的路径。
(3)加载模型
(4)检验成果
先粗糙的试一下,看自我认知是否已经修改好;再进行公共知识和私有知识的提问,看回答问题的质量如何。
(5)考试,来测试训练后的模型效果
modelscope 和 antodl 两个平台的终端分别都切换到LLaMA-Factory的根目录下,输入以下命令:
# 模型测评
CUDA_VISIBLE_DEVICES=0 llamafactory-cli eval \
--model_name_or_path /root/autodl-tmp/outputs/Qwen2.5-0.5B-Instruct \
--template qwen \
--task cmmlu_test \
--lang zh \
--n_shot 5 \
--batch_size 4 \
--trust_remote_code True
其中,model_name_or_path 是之前训练后生成的模型路径
modelscope考试结果:
Average只有32,说明模型微调后,综合能力降低了。
training loss :
autodl 平台考试情况:
6. autodl 平台操作
(1)打开autodl 终端,输入以下命令,安装vllm
pip install vllm
(2)减配卡(训练的时候尽量多张卡,训练完成后可以只用一张卡)
(3)将之前写的情感识别任务的代码上传到autodl平台上,修改模型路径;
(4)autodl 终端,进入LLaMA-Factory的根目录下,输入以下命令:
CUDA_VISIBLE_DEVICES=0 llamafactory-cli eval \
--model_name_or_path /root/autodl-tmp/outputs/Qwen2.5-0.5B-Instruct \
--template qwen \
--task cmmlu_test \
--lang zh \
--n_shot 5 \
--batch_size 4 \
--trust_remote_code True
运行第一轮:
运行第二轮:
7. 部署API服务
部署神器 vllm ,参考文档:
https://blog.vllm.ai/2023/06/20/vllm.html
# 模型部署
python -m vllm.entrypoints.openai.api_server \
--host 0.0.0.0 \
--port 6006 \
--model Qwen2.5-0.5B-Instruct \
--api-key abc123 \
--enable-auto-tool-choice \
--tool-call-parser hermes
五、大模型上层开发
上层开发在本地操作即可,接下来使用本地 jupyter 操作。
上层开发有3件事:
- prompt 开发
- RAG 系统
- Agent
from langchain_openai import ChatOpenAI
model = ChatOpenAI(base_url=base_url,
api_key=api_key,
model=model,
max_tokens=512,
temperature=0.1,
top_p=0.3)
1. 大模型调用方法
"""
call
invoke
"""
response = model.invoke(input="你是谁?")
# 文本内容
response.content
response.response_metadata["token_usage"]
response.response_metadata["finish_reason"]
2. stream
response = model.stream(input="你好")
for chunk in response:
print(chunk.content)
3. 批量推理
queries = ["你好", "你吃饭了吗?", "一起去打球吧"]
responses = model.batch(inputs=queries)
len(responses)
responses
4. 输入部分
# system
from langchain_core.messages import SystemMessage
# user
from langchain_core.messages import HumanMessage
# assistant
from langchain_core.messages import AIMessage
messages = [
SystemMessage(content="""
你是一个情感识别专家!请对用户输入的酒店评论做情感分析!
如果是正面的评论,请输出:
{
"sentiment": "正面"
},
如果是负面的评论,请输出:
{
"sentiment": "负面"
}
"""),
HumanMessage(content="刚从无锡回来,住了该酒店二号楼328YUAN的,总体感觉如下;房间干净,配制齐全还有液镜电脑;饭菜可口且不是很贵;最值得称赞的是服务水准,老公粗心大意把手机掉了,工作人员捡后马上送回,赞了!饭店的员工几乎都是笑脸相迎,很难得,单从这点就间接提升了入住该饭店的理由!")
]
messages
response = model.invoke(input=messages)
response.content
import json
json.loads(s=response.content)
LangChain
- 输入预处理
- 大模型
- 后处理
from langchain_core.prompts import SystemMessagePromptTemplate
from langchain_core.prompts import HumanMessagePromptTemplate
from langchain_core.prompts import ChatPromptTemplate
sys_prompt = SystemMessagePromptTemplate.from_template(template="""
你是一个情感识别专家!请对用户输入的酒店评论做情感分析!
如果是正面的评论,请输出:
{{
"sentiment": "正面"
}},
如果是负面的评论,请输出:
{{
"sentiment": "负面"
}}
""")
user_prompt = HumanMessagePromptTemplate.from_template(template="{text}")
prompt = ChatPromptTemplate.from_messages(messages=[sys_prompt, user_prompt])
prompt
model
from langchain_core.output_parsers import StrOutputParser
output_parser = StrOutputParser()
chain = prompt | model | output_parser
text = "刚从无锡回来,住了该酒店二号楼328YUAN的,总体感觉如下;房间干净,配制齐全还有液镜电脑;饭菜可口且不是很贵;最值得称赞的是服务水准,老公粗心大意把手机掉了,工作人员捡后马上送回,赞了!饭店的员工几乎都是笑脸相迎,很难得,单从这点就间接提升了入住该饭店的理由!"
chain.invoke(input={"text": text})
import pandas as pd
data = pd.read_csv(filepath_or_buffer="test.tsv", sep="\t", encoding="utf8")
num_samples, num_features = data.shape
from tqdm import tqdm
results = []
for idx in tqdm(range(num_samples)):
comment, sentiment = data.loc[idx, :]
result = chain.invoke(input={"text": comment})
try:
result = json.loads(s=result)
except Exception as ex:
print(result)
results.append(result["sentiment"] == sentiment)
sum(results) / len(results)