大模型全链路开发最佳实践:从数据到模型评测
- 1. 环境准备
- 1.1 创建conda环境
- 1.2 安装依赖
- 2. 数据集准备
- 2.1 下载数据集
- 2.2 数据示例
- 3. 使用Data-Juicer进行数据清洗
- 3.1 数据清洗流程
- 3.1.1 编写YAML配置文件
- 3.1.2 数据清洗
- 3.2 划分训练集和测试集
- 4. 使用MS-Swift训练模型
- 4.1 模型训练
- 4.1.1 编写训练脚本
- 4.1.2 启动训练
- 5. 使用EvalScope评测模型
- 5.1 自定义数据集评测
- 5.1.1 编写评测配置文件
- 5.1.2 评测脚本
- 5.1.3 输出结果
- 5.2 模型推理人工评测
- 6. 模型上传
- 总结
随着人工智能技术的飞速发展,大型语言模型(LLMs)已经成为自然语言处理领域的核心驱动力。本文将详细介绍如何使用ModelScope生态进行LLM训练的全链路最佳实践,涵盖数据下载、数据预处理、模型训练、模型评测等完整流程。我们将以知乎评论数据集为例,使用LoRA微调模型,减少生成文本的“AI味”,并分享整个开发过程中的关键步骤和工具。
1. 环境准备
在开始之前,我们需要准备好开发环境。推荐使用conda
进行环境管理,并使用pip
安装相关依赖。
1.1 创建conda环境
conda create -n zhihu python=3.10
conda activate zhihu
1.2 安装依赖
安装modelscope
、data-juicer
、swift
、evalscope
等工具:
pip install modelscope[framework] # 模型库
pip install py-data-juicer[sci] # 数据处理库
pip install ms-swift[llm] # 训练库
pip install ms-swift[eval] # 评测库
2. 数据集准备
2.1 下载数据集
使用modelscope
下载知乎评论数据集,并进行初步处理:
from modelscope.msdatasets import MsDataset
import json
import pandas as pd# 下载数据
ds = MsDataset.load('OmniData/Zhihu-KOL', subset_name='default', split='train')# 处理 metadata
metadata = list(map(lambda x: json.loads(x), ds['METADATA']))# 处理 upvotes
vote_list = []
for item in metadata:try:upvotes = item['upvotes'][3:]if not upvotes:votes = 0elif '万' in upvotes:votes = int(float(upvotes[:-2]) * 10000)else:votes = int(upvotes)except Exception as e:votes = 0vote_list.append(votes)# 写入 jsonl 文件
df = pd.DataFrame.from_dict({'query': ds['INSTRUCTION'],'response': ds['RESPONSE'],'upvotes': vote_list
})df.to_json("data/zhihu.jsonl", orient="records", lines=True, force_ascii=False)
2.2 数据示例
原始数据示例:
{"INSTRUCTION": "怎么说服男朋友买烤箱?","METADATA": "{\"question_id\": 357137111.0, \"answer_id\": 914332816.0, \"url\": \"https://www.zhihu.com/question/357137111/answer/914332816\", \"upvotes\": \"赞同 15\", \"answer_creation_time\": \"2019-11-28T12:01:22.000Z\"}","RESPONSE": "emmmmm,首先想说的是,我买厨房用品一般是不用「说服」的,只是在厨房堆的满满当当的情况下会象征性的问一下我老公,他就会回答我说:你看看你还有地方放吗。然后我会思考一下,如果是特别想买的,就不会问他了。自己决定就好。 比如,前几天我又买了两个盘子~~~~他还不知道。 可以给题主看看我有多少的锅具:自家炒菜用什么锅好?各有什么优缺点? 说回烤箱的问题,买的时候处于热恋期,我告诉他我有一个买烤箱的计划。虽然他基本不吃点心,也不喜欢烘焙,但那个时期的他欣然同意并热情洋溢的给我选烤箱。可能是他有憧憬我会给他做什么好吃的吧。又因为我是一个不怎么吃甜食的湖南人,烤箱在我家烘焙的使用率很低。 但是!!你还是可以告诉他烤箱的作用是可以烤制各种肉类!!!我不相信有不喜欢吃肉的男生!!烤箱真的是可以烤一切的肉类,熟悉之后会觉得非常简单。 我很久以前用烤箱做的最多的就是烤羊排和烤鸡翅,我老公不怎么吃羊肉和鸡翅。这个烤箱因为厨房放不下,被放在了餐厅,也就闲置了下来…… 要说的事是,烤箱真的能给你做出很多不一样的美食,尤其是来了客人,在你两个灶台忙不过来的时候,烤箱特别适合准备一个荤素搭配的豪华大菜。在烹饪其他需要爆炒的菜肴的空档去处理一下就可以了。 总结来说理由如下: 1、如果你家是你做饭多,那么为什么有这么多话说, 也不是他用,等着吃就好了。 2、工欲善其事,必先利其器。没有好的工具怎么能吃到更好的美食。 3、我要我喜欢,不要你喜欢。我还不能有个爱好吗?","SOURCE": "Zhihu"
}
预处理后数据示例:
3. 使用Data-Juicer进行数据清洗
3.1 数据清洗流程
Data-Juicer 是一个一站式多模态数据处理系统,旨在为大语言模型提供更高质量、更丰富、更易“消化”的数据。它提供了多种数据处理算子,包括格式化、映射、过滤、去重和选择等。
3.1.1 编写YAML配置文件
# global parameters
project_name: 'zhihu-process'
dataset_path: 'data/zhihu.jsonl'
np: 16
text_keys: 'response'
export_path: 'data/zhihu_refine.jsonl'# process schedule
process:- specified_numeric_field_filter:field_key: 'upvotes'min_value: 500- text_length_filter:min_len: 100max_len: 2000- clean_email_mapper:- clean_html_mapper:- clean_ip_mapper:- clean_links_mapper:- clean_copyright_mapper:- language_id_score_filter:lang: zhmin_score: 0.9- alphanumeric_filter:tokenization: falsemin_ratio: 0.72- flagged_words_filter:lang: zhtokenization: falsemax_ratio: 0.0005- perplexity_filter:lang: zhmax_ppl: 4000- special_characters_filter:max_ratio: 0.4- document_simhash_deduplicator:tokenization: characterwindow_size: 5lowercase: falseignore_pattern: '\p{P}'num_blocks: 10hamming_distance: 6- topk_specified_field_selector:field_key: 'upvotes'topk: 50000reverse: True
3.1.2 数据清洗
dj-process --config zhihu-bot.yaml
3.2 划分训练集和测试集
import pandas as pddata = pd.read_json("data/zhihu_refine.jsonl", lines=True)def split_data(data, save=False, suffix=''):train_data = data.sample(frac=0.9, random_state=42)test_data = data.drop(train_data.index)if suffix:suffix = '_' + suffixif save:train_data.to_json(f"data/zhihu_train{suffix}.jsonl", orient='records', lines=True, force_ascii=False)test_data.to_json(f"data/zhihu_test{suffix}.jsonl", orient='records', lines=True, force_ascii=False)return train_data, test_datatrain_data, test_data = split_data(data, save=True)
4. 使用MS-Swift训练模型
4.1 模型训练
MS-Swift 支持300+ LLM和50+ MLLM的训练、推理、评测和部署。我们可以使用LoRA微调模型,减少生成文本的“AI味”。
4.1.1 编写训练脚本
CUDA_VISIBLE_DEVICES=0 swift sft \--sft_type lora \--model_type qwen2-7b-instruct \--model_id_or_path qwen/Qwen2-7B-Instruct \--dataset data/zhihu_train.jsonl#4000 alpaca-zh#2000 \--system "你是一个真实的人类。回答用户的问题,并在聊天回复中展现你的情绪,包括开心,幸福,愤怒,伤心,阴阳怪气等" \--dataset_test_ratio 0.01 \--output_dir output \--lora_target_modules ALL \--lora_rank 8 \--dtype bf16 \--seed 42 \--learning_rate 1e-4 \--warmup_ratio 0.05 \--max_length 2048 \--batch_size 4 \--eval_batch_size 4 \--num_train_epochs 1 \--gradient_accumulation_steps 4 \--save_total_limit 10 \--eval_steps 100 \--save_steps 100
4.1.2 启动训练
./train.sh
5. 使用EvalScope评测模型
5.1 自定义数据集评测
使用general_qa
模板自定义评测数据集,评测指标包括BLEU和ROUGE。
5.1.1 编写评测配置文件
[{"name": "custom_general_qa","pattern": "general_qa","dataset": "data","subset_list": ["zhihu_test"]}
]
5.1.2 评测脚本
CUDA_VISIBLE_DEVICES=0 swift eval \--ckpt_dir output/qwen2-7b-instruct/v1-20240819-150005/checkpoint-371 \--eval_dataset no \--infer_backend pt \--eval_backend Native \--eval_limit 10 \--seed 42 \--eval_batch_size 8 \--custom_eval_config custom_eval_config.json \--temperature 0.7 \--top_k 20 \--top_p 0.9
5.1.3 输出结果
{"result": {"data": {"rouge-1-r": 0.1366327464084804,"rouge-1-p": 0.3397212949722054,"rouge-1-f": 0.1453481684882953,"rouge-2-r": 0.03827942419095308,"rouge-2-p": 0.11396557995638323,"rouge-2-f": 0.03626899512109694,"rouge-l-r": 0.1234295688857564,"rouge-l-p": 0.15583028795014991,"rouge-l-f": 0.08378730853798907,"bleu-1": 0.055066495373721956,"bleu-2": 0.01267421096081624,"bleu-3": 0.0009279523752259867,"bleu-4": 1.1801272718452154e-308}},"model": "qwen2-7b-instruct","time": "20240819_153042"
}
5.2 模型推理人工评测
由于自动评测缺少语义维度的评测,我们可以通过脚本进行人工评测。
import os
os.environ['CUDA_VISIBLE_DEVICES'] = '0'
import pandas as pdfrom swift.llm import (get_model_tokenizer, get_template, inference, ModelType, get_default_template_type,
)
from swift.utils import seed_everything
from swift.tuners import Swift
import torchseed_everything(42)def infer_querys(model, template, querys):if type(querys) == str:querys = [querys]responses = []for query in querys:response, history = inference(model, template, query)response = response.replace("\n", "\t")responses.append(response)print(f'response: {response}')return responsesdef load_model(ckpt_dir):model_type = ModelType.qwen2_7b_instructtemplate_type = get_default_template_type(model_type)model, tokenizer = get_model_tokenizer(model_type, model_kwargs={'device_map': 'auto'})model.generation_config.max_new_tokens = 500model.generation_config.temperature = 0.7model.generation_config.top_p = 0.9model.generation_config.top_k = 20if ckpt_dir:model = Swift.from_pretrained(model, ckpt_dir, inference_mode=True)system_prompt = "你是一个真实的人类。回答用户的问题,并在聊天回复中展现你的情绪,包括开心,幸福,愤怒,伤心,阴阳怪气等"template = get_template(template_type, tokenizer, default_system=system_prompt)return model, templatequerys = pd.read_json("data/zhihu_test.jsonl", lines=True)["query"].sample(10, random_state=42).tolist()
querys = ["你是谁?"] + querysprint(querys)ckpt_dict = {
'origin': None,
'lora': 'output/qwen2-7b-instruct/v1-20240819-150005/checkpoint-371',
}
model = None
model_responses = {}
for ckpt_name, ckpt_dir in ckpt_dict.items():if model:del modeltorch.cuda.empty_cache()model, template = load_model(ckpt_dir)model_responses[ckpt_name] = infer_querys(model, template, querys)df = pd.DataFrame.from_dict(model_responses)
df.index = querys
df.to_markdown("output.md")
6. 模型上传
训练完成后,可以将模型上传到ModelScope平台:
from modelscope.hub.api import HubApiYOUR_ACCESS_TOKEN = '请从ModelScope个人中心->访问令牌获取'api = HubApi()
api.login(YOUR_ACCESS_TOKEN)
api.push_model(model_id="AlexEz/zhihu_bot_lora", # 用户名/模型仓库名称model_dir="output/qwen2-7b-instruct/v1-20240819-150005/checkpoint-371" # 本地模型目录
)
总结
本文详细介绍了如何使用ModelScope生态进行大模型全链路开发,涵盖数据准备、数据清洗、模型训练、模型评测等关键步骤。通过LoRA微调,我们成功减少了生成文本的“AI味”,并通过自动评测和人工评测验证了模型的效果。希望本文能为大模型开发者提供有价值的参考。