掘金 人工智能 19小时前
【ERNIEKit】基于ERNIE4.5-0.3B大模型微调的心理咨询师大模型全流程
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文档详述了如何使用ERNIE框架对SFT数据集进行微调,并提供了基于FastDeploy的便捷部署方案。通过数据处理、环境搭建、模型训练、权重合并及FastDeploy服务启动等一系列步骤,演示了从数据准备到模型上线服务的完整流程。特别关注了LoRA微调技术,以及在AISTUDIO等平台上的高效实践。旨在帮助开发者快速掌握AI模型开发与部署的实用技能。

✨ **数据集处理与划分**:首先,对原始JSONL格式的数据集进行预处理,包括删除不必要的ID字段,并将prompt和response字段分别重命名为src和tgt,以适应PaddleNLP的SFT数据格式要求。随后,将8775条数据按照8:2的比例划分为训练集(psy_train.jsonl)和测试集(psy_test.jsonl),为后续模型训练奠定基础。

🛠️ **环境搭建与检查**:详细列出了进行模型训练和部署所需的关键环境依赖,包括PaddlePaddle-GPU(3.1版本)、ERNIE和FastDeploy。文中通过代码示例验证了PaddlePaddle的安装和GPU可用性,并展示了如何安装ERNIE及其依赖,为模型训练和部署提供了可靠的环境保障。

🚀 **SFT模型微调与权重合并**:介绍了使用ERNIE框架进行SFT(Supervised Fine-Tuning)模型微调的流程,重点讲解了LoRA(Low-Rank Adaptation)技术的应用。配置了模型下载、模型配置(包括数据集路径、模型路径、训练轮次等)以及模型训练的详细步骤。微调完成后,通过权重合并操作将LoRA适配器权重融入基础模型,生成可独立部署的模型。

🌐 **FastDeploy高效部署**:展示了如何利用FastDeploy框架将微调后的模型快速部署为API服务。通过启动FastDeploy的OpenAI兼容API服务器,指定模型路径、端口以及其他关键参数,实现了模型的在线推理能力。这一步骤极大地简化了模型上线流程,使得开发者能够高效地将AI模型投入实际应用。

一、数据处理

1.原始数据情况

!head -n1 /home/aistudio/data/data351965/train.jsonl
{"id": "fs7sWG4EJmLR", "prompt": "我晚上难以入睡,我认为这是因为我对工作感到压力", "response": "你的情况是很多职场人都会遇到的困扰,工作压力导致的失眠往往是一个恶性循环:压力影响睡眠质量,睡眠不足又导致次日工作效率降低,进一步加重压力。以下是一些针对性建议,希望能帮助你逐步打破这个循环:\n\n**第一步:建立压力档案(当晚即可操作)**\n1. 准备笔记本记录:\n- 具体压力事件(如\"明早要向董事会汇报\"\n- 伴随的生理反应(手心出汗/心跳加速)\n- 脑海中的具体担忧(\"忘词怎么办?数据记错怎么办?\"\n- 实际发生概率(用百分比标注,如\"忘词概率约20%\"\n\n**第二步:90分钟睡眠周期调整法(3天内见效)**\n- 计算就寝时间:设定起床时间后倒推1.5小时的整数倍\n- 例:若需7点起床,可尝试11:30或1:00入睡\n- 使用SleepCycle等APP监测睡眠质量\n\n**第三歩:办公室微放松(工作中随时可用)**\n- 4-7-8呼吸法:吸气4秒→屏息7秒→呼气8秒(\n- 桌面减压神器:备握力球/指尖陀螺,在接电话时使用\n- 视觉放松:将电脑桌面换成动态自然景观(推荐Rainy Mood网站)\n\n**进阶方案:压力转化训练(需1-2周练习)**\n1. 压力具象化练习:\n- 每天花5分钟将压力想象成具体物体(如不断膨胀的气球)\n- 在脑海中为这个\"压力球\"设置安全阀,想象缓慢释放过程\n\n2. 焦虑时段专用笔记本:\n- 设定\"焦虑时间\"(如晚8-8:30)\n- 其他时间出现担忧时快速记录关键词,留到专用时段处理\n\n**重要提醒:**\n若出现以下情况建议立即就医:\n- 连续3天睡眠不足4小时\n- 出现心悸/手抖等躯体症状\n- 对原本爱好完全失去兴趣\n\n建议从今晚开始尝试\"压力档案\"记录,很多时候当我们把模糊的焦虑具体化后,它的压迫感就会明显减弱。这个过程可能需要持续记录3-5天才能看到明显效果,请给自己足够的耐心。记住,你不需要完美解决所有压力源,重点在于重建对压力的掌控感。"}
!wc -l /home/aistudio/data/data351965/train.jsonl
8775 /home/aistudio/data/data351965/train.jsonl

2.数据集比对

PaddleNLP 采用的 SFT 数据集(以 JSONL 格式存储)与常见的 SFT 数据集组织形式上稍有不同。PaddleNLP 支持的数据格式是每行包含一个字典,每个字典包含以下字段:

目前只需要将格式转换过来即可。

3.数据处理

3.1字段处理

删除不必要的字段id,并更改字段prompt、response为src、tgt

import jsondef read_jsonl(file_path):    """读取JSONL文件并返回数据列表    Args:        file_path (str): JSONL文件路径    Returns:        list: 包含JSON对象的列表    """    data = []    try:        with open(file_path, 'r', encoding='utf-8') as f:            for line_num, line in enumerate(f, 1):                line = line.strip()                if not line:                    continue                try:                    json_obj = json.loads(line)                    data.append(json_obj)                except json.JSONDecodeError as e:                    print(f"警告:第{line_num}行JSON解析失败: {e}")        print(f"成功读取{len(data)}条记录")        return data    except FileNotFoundError:        print(f"错误:文件'{file_path}'不存在")        return []    except Exception as e:        print(f"读取文件时发生错误: {e}")        return []if __name__ == "__main__":    # 读取当前目录下的train.jsonl文件    jsonl_data = read_jsonl('/home/aistudio/data/data351965/train.jsonl')        # 处理数据:删除id字段,重命名prompt为src,response为tgt    processed_data = []    for item in jsonl_data:        # 删除id字段(如果存在)        if 'id' in item:            del item['id']                # 重命名prompt为src        if 'prompt' in item:            item['src'] = item.pop('prompt')                # 重命名response为tgt        if 'response' in item:            item['tgt'] = item.pop('response')                processed_data.append(item)        # 将处理后的数据保存为all.jsonl    output_file = 'all.jsonl'    try:        with open(output_file, 'w', encoding='utf-8') as f:            for item in processed_data:                f.write(json.dumps(item, ensure_ascii=False) + '\n')        print(f"成功将{len(processed_data)}条处理后的数据保存到{output_file}")    except Exception as e:        print(f"保存文件时发生错误: {e}")        # 示例:打印前3条处理后的记录    for i, item in enumerate(processed_data[:3], 1):        print(f"第{i}条处理后记录: {item}")
成功读取8775条记录成功将8775条处理后的数据保存到all.jsonl第1条处理后记录: {'src': '我晚上难以入睡,我认为这是因为我对工作感到压力', 'tgt': '你的情况是很多职场人都会遇到的困扰,工作压力导致的失眠往往是一个恶性循环:压力影响睡眠质量,睡眠不足又导致次日工作效率降低,进一步加重压力。以下是一些针对性建议,希望能帮助你逐步打破这个循环:\n\n**第一步:建立压力档案(当晚即可操作)**\n1. 准备笔记本记录:\n- 具体压力事件(如"明早要向董事会汇报")\n- 伴随的生理反应(手心出汗/心跳加速)\n- 脑海中的具体担忧("忘词怎么办?数据记错怎么办?")\n- 实际发生概率(用百分比标注,如"忘词概率约20%")\n\n**第二步:90分钟睡眠周期调整法(3天内见效)**\n- 计算就寝时间:设定起床时间后倒推1.5小时的整数倍\n- 例:若需7点起床,可尝试11:301:00入睡\n- 使用SleepCycle等APP监测睡眠质量\n\n**第三歩:办公室微放松(工作中随时可用)**\n- 4-7-8呼吸法:吸气4秒→屏息7秒→呼气8秒(\n- 桌面减压神器:备握力球/指尖陀螺,在接电话时使用\n- 视觉放松:将电脑桌面换成动态自然景观(推荐Rainy Mood网站)\n\n**进阶方案:压力转化训练(需1-2周练习)**\n1. 压力具象化练习:\n- 每天花5分钟将压力想象成具体物体(如不断膨胀的气球)\n- 在脑海中为这个"压力球"设置安全阀,想象缓慢释放过程\n\n2. 焦虑时段专用笔记本:\n- 设定"焦虑时间"(如晚8-8:30)\n- 其他时间出现担忧时快速记录关键词,留到专用时段处理\n\n**重要提醒:**\n若出现以下情况建议立即就医:\n- 连续3天睡眠不足4小时\n- 出现心悸/手抖等躯体症状\n- 对原本爱好完全失去兴趣\n\n建议从今晚开始尝试"压力档案"记录,很多时候当我们把模糊的焦虑具体化后,它的压迫感就会明显减弱。这个过程可能需要持续记录3-5天才能看到明显效果,请给自己足够的耐心。记住,你不需要完美解决所有压力源,重点在于重建对压力的掌控感。'}

。。。。。。

3.2划分训练集、测试集

import jsonimport randomdef read_jsonl(file_path):    """读取JSONL文件并返回数据列表"""    data = []    try:        with open(file_path, 'r', encoding='utf-8') as f:            for line_num, line in enumerate(f, 1):                line = line.strip()                if not line:                    continue                try:                    json_obj = json.loads(line)                    data.append(json_obj)                except json.JSONDecodeError as e:                    print(f"警告:第{line_num}行JSON解析失败: {e}")        print(f"成功读取{len(data)}条记录")        return data    except FileNotFoundError:        print(f"错误:文件'{file_path}'不存在")        return []    except Exception as e:        print(f"读取文件时发生错误: {e}")        return []def save_jsonl(data, file_path):    """将数据列表保存为JSONL文件"""    try:        with open(file_path, 'w', encoding='utf-8') as f:            for item in data:                f.write(json.dumps(item, ensure_ascii=False) + '\n')        print(f"成功将{len(data)}条数据保存到{file_path}")    except Exception as e:        print(f"保存文件时发生错误: {e}")def split_data(data, train_ratio=0.8, random_seed=42):    """    将数据按比例分割为训练集和测试集        Args:        data: 要分割的数据列表        train_ratio: 训练集占比,默认为0.8        random_seed: 随机种子,确保结果可复现            Returns:        训练集列表和测试集列表    """    # 设置随机种子,确保每次运行结果一致    random.seed(random_seed)        # 打乱数据顺序    shuffled_data = data.copy()    random.shuffle(shuffled_data)        # 计算分割点    split_index = int(len(shuffled_data) * train_ratio)        # 分割数据    train_data = shuffled_data[:split_index]    test_data = shuffled_data[split_index:]        return train_data, test_dataif __name__ == "__main__":    # 读取all.jsonl文件    input_file = 'all.jsonl'    all_data = read_jsonl(input_file)        if not all_data:        print("没有数据可处理,程序退出")    else:        # 按8:2比例分割数据        train_data, test_data = split_data(all_data, train_ratio=0.8)                # 保存训练集和测试集        save_jsonl(train_data, 'psy_train.jsonl')        save_jsonl(test_data, 'psy_test.jsonl')                # 打印分割统计信息        print(f"数据分割完成:")        print(f"训练集: {len(train_data)}条记录 (80%)")        print(f"测试集: {len(test_data)}条记录 (20%)")
成功读取8775条记录成功将7020条数据保存到psy_train.jsonl成功将1755条数据保存到psy_test.jsonl数据分割完成:训练集: 7020条记录 (80%)测试集: 1755条记录 (20%)

4.数据集分析

import osimport jsondef read_jsonl(file_path):    if not os.path.exists(file_path):        print(file_path, "不存在")        return []    data = []    with open(file_path, "r", encoding="utf-8") as f:        for line in f.readlines():            data.append(json.loads(line))    return data# 虽然文件扩展名是 json,但其实是 jsonl,需要按行读入后再解析。train = read_jsonl("psy_train.jsonl")dev = read_jsonl("psy_test.jsonl")for i in range(1):    print(train[i])
{'src': '我不知道该如何应对失去亲人的痛苦。', 'tgt': '失去至亲的痛苦是人类最深刻的情感体验之一,这种经历就像在内心撕裂一个永远无法完全愈合的伤口。在这个时刻,你可能感觉自己被困在暴风雨中的孤舟里,但请记住,这样的感受恰恰证明了你拥有爱与被爱的能力。以下这些建议不是要消除痛苦,而是陪伴你渡过这段艰难航程的浮木:\n\n**1. 允许自己成为「不完美」的幸存者**\n- 清晨突然爆发的哭泣,深夜翻看旧照片的恍惚,听到某个旋律时的心悸——这些都是心灵在进行自我修复的生理反应。不必为「为什么三个月了还走不出来」而自责,神经科学发现,悲伤会在脑部形成真实的物理印记,需要时间去重组神经通路\n\n**2. 建立「过渡性仪式」**\n- 在厨房保留Ta常用的那个缺口茶杯\n- 继续购买Ta喜欢的杂志放在床头\n- 每周给那个不会再接听的号码发条消息\n这些看似「非理性」的行为,其实是心理学中的过渡性客体(Transitional Object),能帮助我们在现实与回忆之间搭建缓冲带\n\n**3. 重构「存在」的定义**\n尝试在笔记本写下:\n「如果爱可以具象化,Ta留下的痕迹是什么?」\n可能是你烘焙时无意识模仿的揉面手法\n是某个总让你想起Ta笑颜的日落时分\n是逐渐内化成你性格一部分的某个习惯\n这种存在形式的转化,是量子物理中的能量守恒在情感世界的映射\n\n**4. 创建双向对话通道**\n在手机备忘录里开辟一个专属空间:\n「今天看到梧桐叶黄了,你曾说这是秋天写的信」\n「公司新来的实习生有和你一样的虎牙」\n这种单向对话看似徒劳,但镜像神经元会因此激活,让你在书写时产生被聆听的神经反馈\n\n**5. 寻找「创伤后成长」的可能性**\n研究显示,88%的丧亲者最终能找到积极的心理转变:\n- 更敏锐的共情能力\n- 对生命优先级的重新排序\n- 建立更深刻的人际联结\n这不是对痛苦的背叛,而是让失去成为生命韧性的锻造之火\n\n**重要提醒**:如果出现持续的身体疼痛(非器质性)、对日常事物完全丧失兴趣、或产生跟随离世者而去的念头,请立即寻求专业心理干预。这就像心灵骨折需要骨科医生一样正常且必要。\n\n在量子纠缠理论中,曾经紧密相连的粒子即使相隔光年也会保持感应。那些共同经历过的晨昏与四季,早已在原子层面让你们永恒联结。允许自己带着这种「柔软

4.1 数据集大小统计

# 数据集大小统计print(f"训练集样本数: {len(train)}")print(f"验证集样本数: {len(dev)}")print(f"总样本数: {len(train) + len(dev)}")
训练集样本数: 7020验证集样本数: 1755总样本数: 8775

4.2 字符分布统计

# 文本长度统计(按字符计算)def analyze_data(data, name):    src_lens = [len(d['src']) for d in data]    tgt_lens = [len(d['tgt']) for d in data]        print(f"\n{name}数据集统计:")    print(f"• src平均长度: {sum(src_lens)/len(src_lens):.1f} 字符")    print(f"• tgt平均长度: {sum(tgt_lens)/len(tgt_lens):.1f} 字符")    print(f"• src最大长度: {max(src_lens)} 字符")    print(f"• tgt最大长度: {max(tgt_lens)} 字符")    print(f"• src最小长度: {min(src_lens)} 字符")    print(f"• tgt最小长度: {min(tgt_lens)} 字符")
# 执行统计分析analyze_data(train, "训练集")analyze_data(dev, "验证集")
训练集数据集统计: src平均长度: 23.9 字符 tgt平均长度: 1125.0 字符 src最大长度: 63 字符 tgt最大长度: 2295 字符 src最小长度: 5 字符 tgt最小长度: 0 字符验证集数据集统计: src平均长度: 23.7 字符 tgt平均长度: 1113.3 字符 src最大长度: 57 字符 tgt最大长度: 2323 字符 src最小长度: 7 字符 tgt最小长度: 247 字符

二、环境搭建

1.环境检查

1.1 PaddlePaddle版本检查

此次使用paddlepaddle-gpu==3.1版本

import warningswarnings.filterwarnings('ignore')# 验证安装!python -c "import paddle;paddle.utils.run_check()"
/opt/conda/envs/python35-paddle120-env/lib/python3.10/site-packages/paddle/utils/cpp_extension/extension_utils.py:715: UserWarning: No ccache found. Please be aware that recompiling all source files may be required. You can download and install ccache from: https://github.com/ccache/ccache/blob/master/doc/INSTALL.md  warnings.warn(warning_message)Running verify PaddlePaddle program ... I0803 14:11:42.519806  2223 pir_interpreter.cc:1524] New Executor is Running ...W0803 14:11:42.521167  2223 gpu_resources.cc:114] Please NOTE: device: 0, GPU Compute Capability: 8.0, Driver API Version: 12.8, Runtime API Version: 12.6I0803 14:11:42.521916  2223 pir_interpreter.cc:1547] pir interpreter is running by multi-thread mode ...PaddlePaddle works well on 1 GPU.PaddlePaddle is installed successfully! Let's start deep learning with PaddlePaddle now.

1.2 ERNIE版本检查

pip list|grep ernie
[33mWARNING: Ignoring invalid distribution -astapi (/opt/conda/envs/python35-paddle120-env/lib/python3.10/site-packages)[0m[33m[0m[33mWARNING: Ignoring invalid distribution -okenizers (/opt/conda/envs/python35-paddle120-env/lib/python3.10/site-packages)[0m[33m[0m[33mWARNING: Ignoring invalid distribution -radio (/opt/conda/envs/python35-paddle120-env/lib/python3.10/site-packages)[0m[33m[0m[33mWARNING: Ignoring invalid distribution -tarlette (/opt/conda/envs/python35-paddle120-env/lib/python3.10/site-packages)[0m[33m[0merniebot                   0.5.0erniebot_agent             0.5.0erniekit                   0.0.1        /home/ERNIE-developNote: you may need to restart the kernel to use updated packages.

1.3 fastdeploy版本检查

pip list|grep fastdeploy
[33mWARNING: Ignoring invalid distribution -astapi (/opt/conda/envs/python35-paddle120-env/lib/python3.10/site-packages)[0m[33m[0m[33mWARNING: Ignoring invalid distribution -okenizers (/opt/conda/envs/python35-paddle120-env/lib/python3.10/site-packages)[0m[33m[0m[33mWARNING: Ignoring invalid distribution -radio (/opt/conda/envs/python35-paddle120-env/lib/python3.10/site-packages)[0m[33m[0m[33mWARNING: Ignoring invalid distribution -tarlette (/opt/conda/envs/python35-paddle120-env/lib/python3.10/site-packages)[0m[33m[0mfastdeploy-gpu             2.0.0Note: you may need to restart the kernel to use updated packages.

以上环境AISTUDIO默认均已OK,如其他环境需要自己安装

2. 环境安装

2.1 PaddlePaddle-GPU安装

# 源码安装!python -m pip install paddlepaddle-gpu==3.1.0 -i https://www.paddlepaddle.org.cn/packages/stable/cu126/

2.2 ERNIE安装

# !git clone https://gitclone.com/github.com/PaddlePaddle/ERNIE%cd ERNIE!python -m pip install -r requirements/gpu/requirements.txt!python -m pip install -e . # 推荐使用可编辑模式安装

2.3 FastDeploy安装

!python -m pip install fastdeploy-gpu -i https://www.paddlepaddle.org.cn/packages/stable/fastdeploy-gpu-80_90/ --extra-index-url https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple

三、SFT训练

1.模型下载

在单台80G A/H GPU机器上训练,需先下载模型

# 首先请先安装aistudio-sdk库!pip install --upgrade aistudio-sdk# 使用aistudio cli下载模型(推荐)!aistudio download --model PaddlePaddle/ERNIE-4.5-0.3B-Paddle --local_dir baidu/ERNIE-4.5-0.3B-Paddle
Downloading [model.safetensors]: 100%|████████| 688M/688M [00:01<00:00, 431MB/s][A[A[A[AProcessing 9 items: 100%|████████████████████| 9.00/9.00 [00:03<00:00, 2.90it/s]Download model 'PaddlePaddle/ERNIE-4.5-0.3B-Paddle' successfully.Target directory already exists, skipping creation.

2.模型配置

/home/aistudio/run_sft_lora_8k.yaml

### datatrain_dataset_type: "erniekit"eval_dataset_type: "erniekit"train_dataset_path: "/home/aistudio/psy_train.jsonl"train_dataset_prob: "1.0"eval_dataset_path: "/home/aistudio/psy_test.jsonl"eval_dataset_prob: "1.0"max_seq_len: 8192num_samples_each_epoch: 6000000### modelmodel_name_or_path: /home/aistudio/baidu/ERNIE-4.5-0.3B-Paddlefine_tuning: LoRAlora_rank: 32fuse_rope: Trueuse_sparse_head_and_loss_fn: True### finetuning# basestage: SFTseed: 23do_train: Truedo_eval: Truedistributed_dataloader: Falsedataloader_num_workers: 1batch_size: 1num_train_epochs: 10max_steps: 100max_evaluate_steps: 10000eval_steps: 10000evaluation_strategy: stepssave_steps: 10000000save_total_limit: 5save_strategy: stepslogging_steps: 1release_grads: Truegradient_accumulation_steps: 8logging_dir: ./vdl_logoutput_dir: ./outputdisable_tqdm: True# trainwarmup_steps: 20learning_rate: 3.0e-4lr_scheduler_type: cosinemin_lr: 0layerwise_lr_decay_bound: 1.0# optimizerweight_decay: 0.01adam_epsilon: 1.0e-8adam_beta1: 0.9adam_beta2: 0.95offload_optim: True# performancetensor_parallel_degree: 1pipeline_parallel_degree: 1sharding_parallel_degree: 1sharding: stage1sequence_parallel: Truepipeline_parallel_config: disable_partial_send_recv enable_clear_every_step_cacherecompute: Falserecompute_use_reentrant: Truecompute_type: bf16fp16_opt_level: O2disable_ckpt_quant: Trueamp_master_grad: Trueamp_custom_white_list:  - lookup_table  - lookup_table_v2  - flash_attn  - matmul  - matmul_v2  - fused_gemm_epilogueamp_custom_black_list:  - reduce_sum  - softmax_with_cross_entropy  - c_softmax_with_cross_entropy  - elementwise_div  - sin  - cosunified_checkpoint: Trueunified_checkpoint_config: async_save

3.模型训练

# 8K序列长度SFT-LoRA训练%cd ~/ERNIE!erniekit train /home/aistudio/run_sft_lora_8k.yaml

4.权重合并

LoRA微调完成后,需要将LoRA权重与主模型权重合并。多机训练场景下需要注意:⚠️每台机器存储部分模型参数(检查点)⚠️必须同步所有机器的参数文件 后再进行LoRA权重合并或部署

训练完成后合并LoRA参数到基座模型:

注:需要将output中LoRA产生的权重新建checkpoint-1文件夹全部移入,方可运行下述命令。

%cd ~/ERNIE!mkdir output/checkpoint-1!mv output/*.* output/checkpoint-1
/home/aistudio/ERNIE

修改run_export.yaml

### modelmodel_name_or_path: /home/aistudio/baidu/ERNIE-4.5-0.3B-Paddlefine_tuning: LoRA### splitmax_shard_size: 5hf_hub_id: nulloutput_dir: ./output### performancetensor_parallel_degree: 1pipeline_parallel_degree: 1sharding_parallel_degree: 1sharding: stage1pipeline_parallel_config: disable_partial_send_recv enable_clear_every_step_cachesequence_parallel: Truecompute_type: bf16fp16_opt_level: O2
%cd ~/ERNIE!erniekit export /home/aistudio/run_export.yaml lora=True
Merging tensor:   0%|          | 0/110 [00:00<?, ?it/s]W0803 14:28:23.709003 29982 gpu_resources.cc:114] Please NOTE: device: 0, GPU Compute Capability: 8.0, Driver API Version: 12.8, Runtime API Version: 12.6Merging tensor:   1%|          | 1/110 [00:00<01:22,  1.32it/s]Merging tensor:  55%|█████▌    | 61/110 [00:00<00:00, 96.32it/s]Merging tensor: 100%|██████████| 110/110 [00:00<00:00, 125.79it/s][32m[2025-08-03 14:28:24,221] [    INFO][0m - Merge tensors successfully.[0m[32m[2025-08-03 14:28:24,974] [    INFO][0m - Model weights saved in ./output/export/model-00001-of-00001.safetensors.[0m[32m[2025-08-03 14:28:24,984] [    INFO][0m - Model index file saved in ./output/export/model.safetensors.index.json.[0m[32m[2025-08-03 14:28:24,985] [    INFO][0m - Merge config file saved in ./output/export/merge_config.json.[0m[32m[2025-08-03 14:28:24,987] [    INFO][0m - ***** Successfully finished merging LoRA model. Time cost: 2.3652708530426025 s *****[0mLAUNCH INFO 2025-08-03 14:28:26,390 Pod completedLAUNCH INFO 2025-08-03 14:28:26,390 Exit code 0

四、FastDeploy部署

import subprocessimport timeimport requestsimport threadingdef start_fastdeploy():    cmd = [        "python", "-m", "fastdeploy.entrypoints.openai.api_server",        "--model", "output/export",        "--port", "8180",        "--metrics-port", "8181",         "--engine-worker-queue-port", "8182",        "--max-model-len", "32768",        "--max-num-seqs", "32"    ]        print("🚀 启动FastDeploy服务...")    print("-" * 50)        process = subprocess.Popen(        cmd,        stdout=subprocess.PIPE,        stderr=subprocess.STDOUT,        universal_newlines=True,        bufsize=1    )        print(f"📝 PID: {process.pid}")        service_ready = False        def monitor_logs():        nonlocal service_ready        try:            while True:                output = process.stdout.readline()                if output == '' and process.poll() is not None:                    break                if output:                    line = output.strip()                    print(f"[日志] {line}")                                        if "Loading Weights:" in line and "100%" in line:                        print("✅ 权重加载完成")                    elif "Loading Layers:" in line and "100%" in line:                        print("✅ 层加载完成")                    elif "Worker processes are launched" in line:                        print("✅ 工作进程启动")                    elif "Uvicorn running on" in line:                        print("🎉 服务启动完成!")                        service_ready = True                        break        except Exception as e:            print(f"日志监控错误: {e}")        log_thread = threading.Thread(target=monitor_logs, daemon=True)    log_thread.start()        start_time = time.time()    while time.time() - start_time < 120:        if service_ready:            break        if process.poll() is not None:            print("❌ 进程退出")            return None        time.sleep(1)        if not service_ready:        print("❌ 启动超时")        process.terminate()        return None        print("-" * 50)    return processdef test_model():    try:        import openai                print("🔌 测试模型连接...")                client = openai.Client(base_url="http://localhost:8180/v1", api_key="null")                response = client.chat.completions.create(            model="null",            messages=[                {"role": "system", "content": "你是一个有用的AI助手。"},                {"role": "user", "content": "你好"}            ],            max_tokens=50,            stream=False        )                print("✅ 模型测试成功!")        print(f"🤖 回复: {response.choices[0].message.content}")        return True            except Exception as e:        print(f"❌ 测试失败: {e}")        return Falsedef check_service():    try:        response = requests.get("http://localhost:8180/v1/models", timeout=3)        return response.status_code == 200    except:        return Falsedef setup_service():    print("=== ERNIE-4.5-0.3B-Paddle 服务启动 ===")        if check_service():        print("✅ 发现运行中的服务")        if test_model():            print("🎉 服务已就绪!")            return True        print("⚠️ 服务异常,重新启动")        process = start_fastdeploy()        if process is None:        print("❌ 启动失败")        return False        if test_model():        print("🎊 启动成功!现在可以运行知识图谱代码")        return True    else:        print("❌ 启动但连接失败")        return Falseif __name__ == "__main__" or True:    setup_service()
=== ERNIE-4.5-0.3B-Paddle 服务启动 ===🚀 启动FastDeploy服务...--------------------------------------------------📝 PID: 30523[日志] [日志] Loading Weights:   0%|          | 0/100 [00:00<?, ?it/s][日志] Loading Weights:   0%|          | 0/100 [00:00<?, ?it/s][日志] Loading Weights:   0%|          | 0/100 [00:00<?, ?it/s][日志] Loading Weights:   0%|          | 0/100 [00:00<?, ?it/s][日志] Loading Weights:   0%|          | 0/100 [00:01<?, ?it/s][日志] Loading Weights:   0%|          | 0/100 [00:01<?, ?it/s][日志] Loading Weights:   0%|          | 0/100 [00:01<?, ?it/s][日志] Loading Weights:   0%|          | 0/100 [00:01<?, ?it/s][日志] Loading Weights:   0%|          | 0/100 [00:02<?, ?it/s][日志] Loading Weights:   0%|          | 0/100 [00:02<?, ?it/s][日志] Loading Weights: 100%|██████████| 100/100 [00:02<00:00, 199.71it/s]✅ 权重加载完成[日志] Loading Weights: 100%|██████████| 100/100 [00:02<00:00, 199.71it/s]✅ 权重加载完成[日志] Loading Weights: 100%|██████████| 100/100 [00:03<00:00, 33.28it/s]✅ 权重加载完成[日志] [日志] Loading Layers:   0%|          | 0/100 [00:00<?, ?it/s][日志] Loading Layers:  94%|█████████▍| 94/100 [00:00<00:00, 1528157.27it/s][日志] Loading Layers: 100%|██████████| 100/100 [00:00<00:00, 199.85it/s]✅ 层加载完成[日志] Loading Layers: 100%|██████████| 100/100 [00:00<00:00, 199.85it/s]✅ 层加载完成[日志] Loading Layers: 100%|██████████| 100/100 [00:01<00:00, 99.87it/s]✅ 层加载完成[日志] INFO     2025-08-03 14:29:00,968 30523 engine.py[line:276] Worker processes are launched with 13.940714836120605 seconds.✅ 工作进程启动[日志] INFO     2025-08-03 14:29:00,969 30523 api_server.py[line:91] Launching metrics service at http://0.0.0.0:8181/metrics[日志] INFO     2025-08-03 14:29:00,969 30523 api_server.py[line:94] Launching chat completion service at http://0.0.0.0:8180/v1/chat/completions[日志] INFO     2025-08-03 14:29:00,969 30523 api_server.py[line:97] Launching completion service at http://0.0.0.0:8180/v1/completions[日志] INFO:     Started server process [30523][日志] INFO:     Waiting for application startup.[日志] [32m[2025-08-03 14:29:02,039] [    INFO][0m - Special tokens have been added in the vocabulary, make sure the associated word embeddings are fine-tuned or trained.[0m[日志] INFO:     Application startup complete.[日志] INFO:     Uvicorn running on http://0.0.0.0:8180 (Press CTRL+C to quit)🎉 服务启动完成!--------------------------------------------------🔌 测试模型连接...✅ 模型测试成功!🤖 回复: 你好!我是百度开发的AI助手,很高兴和你交流。如果你有任何问题或想了解AI相关的信息,我随时在这里为你解答。🎊 启动成功!现在可以运行知识图谱代码

五、模型测试

import openaihost = "0.0.0.0"port = "8180"client = openai.Client(base_url=f"http://{host}:{port}/v1", api_key="null")response = client.chat.completions.create(    model="null",    messages=[        {"role": "system", "content": "您好,我是资深心理咨询师,有什么我可以帮助您的吗?"},        {"role": "user", "content": "我对生活中的一切感到非常不堪重负,不知道该如何应对所有正在发生的事情。"},    ],    stream=True,)for chunk in response:    if chunk.choices[0].delta:        print(chunk.choices[0].delta.content, end='')print('\n')
我能感受到你此刻的沉重和迷茫,这种被生活压得喘不过气的感觉真的很不容易。请先停下来,我们慢慢梳理。或许可以试着给生活画一个"压力曲线":当生活让你感到焦虑时,在哪个时间段会有明显的压力感?这些时刻记录下来,看看它们是如何起因的。有时候我们可以调整自己的应对模式。你愿意分享一下最近发生的事情吗?是工作、人际关系,还是某些具体场景?哪怕只是"被领导批评""家庭争吵",这些碎片都是未被察觉的痛。如果愿意,我们可以一起做个小实验:用10分钟写下"今天我能做哪些小事"(哪怕只是整理桌面),你会发现很多灰色地带。有时候我们可以把模糊的焦虑转化为具体的小行动。你愿意和我分享一个让你感到平静的瞬间吗?或许我们可以一起尝试着重建掌控感。你愿意先选择其中一项尝试吗?

六、模型上传

import os# 需要填写aistudio-access-token, 位置在我的工作台--令牌获取os.environ["AISTUDIO_ACCESS_TOKEN"] = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"from aistudio_sdk.hub import uploadres = upload(    # 填写模型详情页面中的repo_id    repo_id='javaroom/ERNIE_PSY',    # 填写要上传的文件在本地的路径,如'./ERNIE-X1/README.md'    path_or_fileobj='/home/aistudio/ERNIE/output/export/model-00001-of-00001.safetensors',    # 填写上传至repo后的文件路径及文件名,如填写"README.md",则会在master分支的根目录内,上传README.md    path_in_repo='model-00001-of-00001.safetensors',    # 填写commit信息,非必填    commit_message='upload model file to repo')print(res)
uploading file, checking params ..checking file size ..checking is file using lfs ..Start uploading LFS file.

七、项目地址

aistudio.baidu.com/projectdeta…

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

ERNIE SFT LoRA FastDeploy AI模型部署
相关文章