LLaMA-Factory与DeepSeek-R1-7B:微调垂直行业大模型(LORA微调
https://blog.csdn.net/2401_85325726/article/details/147037214
一、大模型微调部署框架
为了让开发者拥有一个简便、高效的工具,以便在现有的开源模型上,快速实现模型微调部署推理,因此微调大模型的框架就应运而生了。今天给介绍2个大型语言模型(微调+部署+推理)框架: LLaMA-Factory和unsloth。
1.1 LLaMA-Factory:友好的界面操作
1.什么是LLaMA-Factory?
LLaMA-Factory,全称Large Language Model Factory,即大型语言模型工厂。它是一个基于Web的用户界面,提供了对多种大型语言模型的微调、训练和部署支持。通过集成多种训练方法和先进的算法,LLaMA-Factory旨在简化大型语言模型的微调过程,让用户能够以最小的资源高效地训练和管理模型。
2.快速开始LLaMA-Factory
入门教程:https://zhuanlan.zhihu.com/p/695287607
框架文档:https://llamafactory.readthedocs.io/zh-cn/latest/
**github:**https://github.com/hiyouga/LLaMA-Factory
1.2 Unsloth:让 LLM 微调更快、更省内存的开源项目
1.unsloth是什么?
Unsloth 是一个专门为 **Llama 3.3、Mistral、Phi-4、**Qwen 2.5 和 Gemma 等模型设计的微调加速框架。该项目由 Daniel Han 和 Michael Han 领导的团队开发,旨在为开发者提供一个高效、低内存的微调解决方案。Unsloth 支持多种模型,并且提供了免费的笔记本示例,让初学者也能轻松上手。
2.快速开始Unsloth
**Unsloth 文档:**https://docs.unsloth.ai/
**Unsloth 笔记本:**https://docs.unsloth.ai/get-started/unsloth-notebooks
**微调指南:**https://docs.unsloth.ai/get-started/fine-tuning-guide
1.3 LLaMA-Factory vs Unsloth 对比表
由AI技术平台生成
🔍 选择建议
如果需要灵活的 LLM 训练方案 ➝ LLaMA-Factory ✅
如果想要更快、更省显存的 LLaMA 训练 ➝ Unsloth 🚀
✨ Unsloth 更适合轻量化训练,LLaMA-Factory 适合专业微调需求!
二、大模型微调技术简介
**微调(Fine-tuning)**是机器学习中迁移学习的一种关键技术,指在预训练模型的基础上,针对特定任务或数据集进行进一步训练调整的过程。其核心在于利用预训练模型已学习到的通用特征,通过少量数据和计算资源,使模型适应新任务的特定需求。
2.1 监督微调(Supervised Fine-Tuning,SFT)
1.冻结(Freeze)监督微调
部分或全部预训练模型的权重被冻结,仅对模型的部分层或新增的附加组件进行训练。比如下面的代码所示:
# 遍历模型的所有参数和名称
for name, param in model.named_parameters():
# 指定冻结层,(layers.27, layers.26, layers.25, layers.24, layers.23)之外的所有参数
if not any(nd in name for nd in ["layers.27", "layers.26", "layers.25", "layers.24", "layers.23"]):
# 将参数的 requires_grad 属性设置为False,即冻结该参数
param.requires_grad = False
1
2
3
4
5
6
举个🌰:
就像孩子已经学会了加减法,你不再让他重新学习这些基础运算(冻结基础运算知识),而是直接教他如何将应用题中的文字信息转化为数学表达式(训练新的解题技巧)。例如,孩子已经很熟练地掌握2+3=5,现在你教他如何解决“小明有2个苹果,小红给了他3个,现在小明有几个苹果?”这类问题。
2.全参数微调(Full Parameter Fine Tuning)
对模型的所有权重进行调整,适用于拥有大量与任务高度相关的高质量训练数据的情况。不依赖开源模型底座知识,基于行业数据重头学习。
3.部分参数微调(Sparse Fine Tuning / Selective Fine Tuning)
仅选择性地更新模型中的某些权重,适用于需要保留大部分预训练知识的情况。
2.2 参数高效微调(Parameter-Efficient Fine-Tuning,PEFT)
1.LoRA(Low-Rank Adaptation)微调
LoRA 的原理其实并不复杂,它的核心思想是在原始预训练语言模型旁边增加一个旁路,做一个降维再升维的操作,来模拟所谓的 intrinsic rank(预训练模型在各类下游任务上泛化的过程其实就是在优化各类任务的公共低维本征(low-dimensional intrinsic)子空间中非常少量的几个自由参数)。**训练的时候固定预训练语言模型的参数,只训练降维矩阵 A 与升维矩阵 B。**而模型的输入输出维度不变,输出时将 BA 与预训练语言模型的参数叠加。**用随机高斯分布初始化 A,用 0 矩阵初始化 B。**这样能保证训练开始时,新增的通路BA=0从,而对模型结果没有影响。
**在推理时,将左右两部分的结果加到一起即可,h=Wx+BAx=(W+BA)x,**所以,只要将训练完成的矩阵乘积BA跟原本的权重矩阵W加到一起作为新权重参数替换原始预训练语言模型的W即可,不会增加额外的计算资源。
举个🌰:
想象你想教学生们怎样进行快速心算而不去完全打破他们原有的学习方法。你决定只引入一个简化版本的心算技巧,让他们在现有知识的基础上进行少量调整。这就像是把原有的学习方式轻量化处理,只增加所需的少量新知识,而不是重新教授整个数学课程。
LoRA 的最大优势是速度更快,使用的内存更少;因此,可以在消费级硬件上运行。
2.QLoRA(Quantized Low-Rank Adaptation)微调
则是 LoRA 的一个扩展版本,它结合了 LoRA 的低秩适配器和量化技术。QLoRA 进一步优化了计算效率和存储需求,特别是在极端显存受限的环境下。与 LoRA 不同的是, QLoRA 会将插入的低秩适配器层的部分权重进行量化(通常是量化为INT4或INT8),在保持性能的同时显著降低模型的存储和计算需求。
举个🌰:
针对学生中一些学习资源(如时间或精力)更加有限的情况,你进一步优化教学方法,不仅简化了学习内容(类似LoRA),同时还用了一些有助于记忆的技巧(比如使用图像或口诀),从而更有效地传授知识。这样,每个学生能在有限时间内学会心算法。在技术上,QLoRA涉及量化(quantization)技术,将模型的一部分权重参数存储在较低精度的数值格式中,以此减少内存使用和计算量,同时结合LoRA的低秩调整,让适应过程更加高效。
3.Prompt Tuning微调
Prompt Tuning的出发点,是基座模型(Foundation Model)的参数不变,为每个特定任务,训练一个少量参数的小模型,在具体执行特定任务的时候按需调用。
Prompt Tuning的基本原理是在输入序列X之前,增加一些特定长度的特殊Token,以增大生成期望序列的概率。
具体来说,就是将X = [x1, x2, …, xm]变成,X = [x1, x2, ..., xk; x1, x2, …, xm], Y = WX`。
如果将大模型比做一个函数:Y=f(X),那么Prompt Tuning就是在保证函数本身不变的前提下,在X前面加上了一些特定的内容,而这些内容可以影响X生成期望中Y的概率。
4.P-tuning v2微调
Prefix Tuning的灵感来源是,基于Prompt Engineering的实践表明,在不改变大模型的前提下,在Prompt上下文中添加适当的条件,可以引导大模型有更加出色的表现。
Prefix Tuning的出发点,跟Prompt Tuning的是类似的,只不过它们的具体实现上有一些差异。Prompt Tuning是在Embedding环节,往输入序列X前面加特定的Token。而Prefix Tuning是在Transformer的Encoder和Decoder的网络中都加了一些特定的前缀。
具体来说,就是将Y=WX中的W,变成W = [Wp; W],Y=WX。
举个🌰:
假设你有一个已经会弹很多曲子的钢琴家,但他需要为一首新曲子调整演奏风格。他不会重新学习钢琴的基本指法(不改变模型主体权重),而是在演奏前先弹一段特定的音阶(可学习的prompt嵌入),让自己的手指更适应新曲子的节奏和感觉。
2.3 基于人类反馈的强化学习微调(RLHF)
将人类的反馈通过强化学习的方式引入到对大模型的微调中,使大模型生成的结果更加符合人类的期望。
举个🌰:
想象你训练一只小狗学会握手。最初,狗可能不太明白握手的意思,你通过给它零食奖励来强化它正确的握手行为。每次它抬起爪子碰到你的手,你就给它一块小零食,让它知道这样做是正确的,慢慢地,狗就会越来越熟练地学会握手这个动作,而且会根据你的反馈不断调整自己的动作以获得更多的奖励。
三、基于LLaMA-Factory微调DeepSeek-R1-Distill-7B
下面介绍基于LLaMA-Factory框架高效微调DeepSeek-R1-Distill-7B模型,并介绍微调DeepSeek的COT数据集构建和使用方法,并先使用medical-o1-reasoning-SFT数据集上完成代码测试,并最终达到问答风格优化&知识灌注目的;在进行行业垂类数据的构建和微调。
3.1 算力展示
当然复现DeepSeek-R1-7b咱们现在的算力是由多余的,你甚至可以比我这个算力更低都可以。
3.2 LLaMA-Factory环境配置
**框架仓库:**https://github.com/hiyouga/LLaMA-Factory
快速开始:
git clone --depth 1 https://github.com/hiyouga/LLaMA-Factory.git
# 1.创建python环境
cd LLaMA-Factory
conda create -n llama_env python=3.10
conda activate llama_env
# 2. LLaMA-Factory依赖库安装
pip install -r requirements.txt
pip install -e ".[torch,metrics]"
# 3.验证是否安装成功
llamafactory-cli version
1
2
3
4
5
6
7
8
9
10
3.3 DeepSeek预训练数据构建
1.大模型微调数据集的格式Alpaca 和 ShareGPT介绍
LLama-Factory支持Alpaca和ShareGPT两种格式的数据集,那我先介绍这两种数据格式的区别,搞懂数据结构后才知道数据转换脚本如何去撰写。
1)Alpaca数据格式
Alpaca 是基于 Meta 开源的 LLaMA 模型构建的一种微调数据集格式,特别用于 instruction-tuning,即指令微调。其数据格式的特点是提供了一个明确的任务描述(instruction)、输入(input)和输出(output)三部分。
{
"instruction": "Condense the passage.",
"input": "The application of artificial intelligence in healthcare is transforming diagnostics...",
"output": "AI in healthcare is revolutionizing diagnostics..."
}
1
2
3
4
5
名词解释:
instruction: 任务的指令,告诉模型需要完成什么操作。
input: 任务所需的输入。如果任务是开放式的或者不需要明确的输入,这一字段可以为空字符串。
output: 任务的期望输出,也就是模型在给定指令和输入情况下需要生成的内容。
2)ShareGPT数据格式
ShareGPT 格式来源于通过记录 ChatGPT 与用户对话的数据集,主要用于对话系统的训练。它更侧重于多轮对话数据的收集和组织,模拟用户与 AI 之间的交互。
{
"conversations":[
{
"role":"user",
"content":"What's the capital of China?"
},
{
"role":"gpt",
"content":"The capital of China is Beijing."
},
{
"role":"user",
"content":"Tell me more about Beijing."
},
{
"role":"gpt",
"content":"Beijing is the capital and a major city in China, famous for its rich history, cultural heritage, and modern development..."
}
],
"system":"You are a knowledgeable AI assistant. Provide concise and informative answers to user questions."
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
名词解释:
conversations: 这是一个对话列表,包含每轮对话的角色和内容。
role: 表示对话的角色,通常为“user”表示用户,“assistant”表示AI助手。
content: 具体的对话内容。
system:定义了 AI 助手的角色和回答问题的方式。
2.medical-o1-reasoning-SFT下载
数据集可在ModelScope平台直接下载,也可以通过命令行、SDK和git下载。比如命令行下载方式为:
**数据地址:**https://www.modelscope.cn/datasets/AI-ModelScope/medical-o1-reasoning-SFT/files
pip install modelscope
modelscope download --dataset AI-ModelScope/medical-o1-reasoning-SFT --local_dir ./dotasets
1
2
DeepSeek-R1蒸馏模型所需要的数据格式是:Question-Complex-CoT-Response。所以之后,在构建数据集的过程中我得注意到,一定要构建Complex-CoT。数据集格式如下:
数据集下载后,并不能直接在LLama-Factory中使用,我们还需要将其转为LLama-Factory可接受的微调数据格式(Alpaca 和 ShareGPT)。下面是转为ShareGPT的脚本:
#!/user/bin/env python3
# -*- coding: utf-8 -*-
# python convert_medical_to_sharegpt.py
import json
from tqdm import tqdm
import re
defclean_text(text):
"""清理文本中的多余空白字符但保留必要换行"""
text = str(text).strip()
# 合并多个连续换行为一个,保留单换行
text = re.sub(r'\n\s*\n', '\n\n', text)
# 合并连续空格
return re.sub(r'[ \t]+', ' ', text)
defconvert_medical_to_sharegpt(input_path, output_path, system_prompt=None):
"""
转换 medical-o1-reasoning-SFT 数据为 ShareGPT 格式
特点:将思维链用<think>标签包裹后与回答合并
"""
withopen(input_path, 'r', encoding='utf-8') as f:
original_data = json.load(f)
default_system = "您是一位医学专家,在临床推理、诊断和治疗计划方面拥有丰富的知识。请回答以下医学问题。"
sharegpt_data = []
for item in tqdm(original_data, desc="转换进度"):
try:
# 清洗各字段文本
question = clean_text(item["Question"])
complex_cot = clean_text(item["Complex_CoT"])
response = clean_text(item["Response"])
# 构建带思维链的回答(用<think>标签包裹)
augmented_response = f"<think>{complex_cot}</think>\n\n{response}"
conversation = {
"conversations": [
{"from": "human", "value": question},
{"from": "gpt", "value": augmented_response}
],
"system": system_prompt if system_prompt else default_system
}
sharegpt_data.append(conversation)
except KeyError as e:
print(f"跳过缺失字段的记录:{e}")
# 保存结果
withopen(output_path, 'w', encoding='utf-8') as f:
json.dump(sharegpt_data, f, ensure_ascii=False, indent=2)
print(f"转换完成!共处理 {len(sharegpt_data)} 条数据")
# 使用示例
if __name__ == "__main__":
convert_medical_to_sharegpt(
input_path="./medical_o1_sft_Chinese.json", # 替换为您的输入文件路径
output_path="data/medical_sharegpt_format.json",
system_prompt="您是一位资深医学专家,请根据最新临床指南回答问题。"# 可选自定义提示
)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
3.如何构建构建垂直行业数据集?
有监督微调(SFT)数据集制作是一个相当费时间的过程,我分享一下制作数据的真实想法,我一般有三个需求:批量出数据、快速构建数据、高质量数据。
但是经过我研究发现,我这几个需求要求蛮高的,很难达到批量出数据。那我简单说一下我自己制作数据的方法和来源吧。
1)如何获取行业数据?
开源数据网站下载:根据自己的行业去下面的网站检索下载
**hugging face:**https://huggingface.co
ModelScope: https://modelscope.cn/datasets
**百度飞桨:**https://aistudio.baidu.com/datasetoverview
**OpenDataLab:**https://opendatalab.com/
Kaggle: https://www.kaggle.com/
**AWS****亚马逊:**https://registry.opendata.aws/
**AWS网页文本数据:**https://commoncrawl.org/
**微软开源数据:**https://www.microsoft.com/en-us/research/tools/
**谷歌开源数据:**https://datasetsearch.research.google.com/
**GitHub****开源数据:**https://github.com/awesomedata/awesome-public-datasets
**visualdata数据集:**https://visualdata.io/discovery
**LUGE千言:**https://www.luge.ai/
TIANCHI阿里天池:https://tianchi.aliyun.com/dataset/
**UCI开源数据集:**https://archive.ics.uci.edu/datasets
**和鲸开源数据集:**https://www.heywhale.com/home/dataset
**启智开源数据集:**https://openi.pcl.ac.cn/explore/datasets
**payititi帕衣提提:**https://www.payititi.com/opendatasets/
**BAAI开源数据:**https://data.baai.ac.cn/data
Hyper超神经:https://hyper.ai/datasets
**Dataju聚数力:**http://dataju.cn/Dataju/web/home
2)成千上万、混乱无章法的pdf、word、txt等格式文档如何标注呢?
行业专家手搓,标注为Question-Complex-CoT-Response格式。
Question:从原始数据中提取问题部分,确保问题清晰且独立。
Complex CoT:标注思维链部分,描述解决问题的推理过程。
Response:标注最终的回答,确保与问题和思维链逻辑一致。
数据标注辅助平台标注
**Label-LLM:**支持对整段对话以及对话中的提问或回复进行标注,可适配现有大语言模型训练中绝大部分的数据标注任务需求
**RLHF的数据标注:**基于人类反馈进行强化学习的LLM数据标注
借助大模型辅助标注成对应的格式。
大模型平台技术生成:使用deepseek、ChatGPT、豆包、文兴一言等大模型
调用官方API+规则生成:基于OpenAI的文本数据标注。
我们可以采用类似的方法来撰写回复。**第一步,可以要求数据标注员根据给定的prompt编写回复。第二步,需要要求审核专家验证该回复的好坏。**两者必须具有相同的指导说明,以便编写者和审核者有相同的(编写、评判)准则。
3)基于上述构建的数据:开源数据+具身智能垂直行业数据(Question-Complex-CoT-Response格式)
具身智能垂直行业数据,使用python convert_medical_to_sharegpt.py将行业数据也转为ShareGPT数据格式。
将数据集:medical_sharegpt_format.json和jushen_sharegpt_format.json添加到LLama-Factory引用的data路径下。
修改identity.json,改成自己的身份。比如:小爱同学、siri等等。
3.4 DeepSeek-R1-Distill-Qwen-7B模型下载
数据准备好后,开始准备模型,今天下载DeepSeek-R1-Distill-Qwen-7B模型(大概15-16g),下载渠道有很多,比如Huggingface(需要 上网)和ModelScope(国内阿里)。那我就以不需要上网就能下载的方式,使用modelscope下载,命令和SDK代码两种下载方式介绍:
**模型地址:**https://www.modelscope.cn/models/deepseek-ai/DeepSeek-R1-Distill-Qwen-7B/files
方法一:modelscope命令下载
mkdir ./DeepSeek-R1-Distill-Qwen-7B
modelscope download --model deepseek-ai/DeepSeek-R1-Distill-Qwen-7B --local_dir ./DeepSeek-R1-Distill-Qwen-7B
1
2
如果使用上述命令老是中断怎么办???—那就使用方法二:SDK代码下载吧。
方法二:modelscope的SDK代码下载
from modelscope import snapshot_download
model_dir = snapshot_download('deepseek-ai/DeepSeek-R1-Distill-Qwen-7B', local_dir='./large_models/DeepSeek-R1-Distill-Qwen-7B')
print(f"Model downloaded to: {model_dir}")
1
2
3
4
5
解释说明:R1-7b模型文件包括两个模型分片文件、一些配置文件和tokenizer文件等。
3.5 启动训练
方法一:命令启动训练:参数自行根据情况修改
llamafactory-cli train \
--stage sft \
--do_train True \
--model_name_or_path /ai_data1/media/large_models/DeepSeek-R1-Distill-Qwen-7B \
--preprocessing_num_workers 16 \
--finetuning_type lora \
--template deepseek3 \
--flash_attn auto \
--dataset_dir data \
--dataset identity,medical_sharegpt_format,jushen_sharegpt_format \
--cutoff_len 2048 \
--learning_rate 2e-05 \
--num_train_epochs 3.0 \
--max_samples 100000 \
--per_device_train_batch_size 1 \
--gradient_accumulation_steps 8 \
--lr_scheduler_type cosine \
--max_grad_norm 1.0 \
--logging_steps 5 \
--save_steps 100 \
--warmup_steps 0 \
--packing False \
--report_to none \
--output_dir saves/DeepSeek-R1-7B-Distill/lora/train_2025-03-31-17-46-20 \
--fp16 True \
--plot_loss True \
--trust_remote_code True \
--ddp_timeout 180000000 \
--include_num_input_tokens_seen True \
--optim adamw_torch \
--lora_rank 8 \
--lora_alpha 16 \
--lora_dropout 0 \
--lora_target all
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
方法二:启动Web UI训练**(推荐)**
接下来就可以在LLama-Factory目录下启动LLama-Factory的Web UI界面进行模型微调训练。启动Web UI:
llamafactory-cli webui
1
NCCL通信问题:RTX 4000 系列显卡不支持通过 P2P(Peer-to-Peer)或 IB(InfiniBand)进行更快的通信带宽。这是硬件限制,NCCL(NVIDIA Collective Communications Library)尝试使用这些通信方式时会失败。
DS_SKIP_CUDA_CHECK=1 NCCL_P2P_DISABLE=1 NCCL_IB_DISABLE=1 llamafactory-cli webui
#会自动 跳转到界面,如果没有:在浏览器输入127.0.0.1:7860即可进入Web UI界面
1
2
需要配置的选项已用红框圈出,包括:微调的模型名称、模型本地路径、微调方式(我选用lora,算力有限)、数据集、学习率、训练轮数Epochs、批次大小等。
全部设置完成之后,即可开始训练:
另外因为实测下来,两张24G显存的GTX4090,是在batchsize设为1的情况下,LORA微调训练才能跑通。
如果拟采用全量微调,可能显卡不支持的,你可以尝试更小的模型,比如DeepSeek-R1-Distill-Qwen-1.5B。具体我还没试过,如果出现问题可以留言交流!!!!!!
3.6 Export模型导出
LoRA微调只保存了适配器权重:LoRA微调过程中,只训练了低秩矩阵(适配器权重),而原始模型的权重保持不变。因此,微调后的模型文件中并不包含完整的模型权重,无法直接加载。以下是具体原因和解决方案:
3.7 开始聊天
LORA微调训练完成后,直接在Web UI上测试模型微调效果。切换到chat选项,选择导入模型即可进行对话。下图是全量微调前后的问答效果对比:
全量微调的效果还是比较理想的,但LORA微调的效果可能相对较差。
普通人如何抓住AI大模型的风口?
=领取方式在文末==
为什么要学习大模型?
目前AI大模型的技术岗位与能力培养随着人工智能技术的迅速发展和应用 , 大模型作为其中的重要组成部分 , 正逐渐成为推动人工智能发展的重要引擎 。大模型以其强大的数据处理和模式识别能力, 广泛应用于自然语言处理 、计算机视觉 、 智能推荐等领域 ,为各行各业带来了革命性的改变和机遇 。
目前,开源人工智能大模型已应用于医疗、政务、法律、汽车、娱乐、金融、互联网、教育、制造业、企业服务等多个场景,其中,应用于金融、企业服务、制造业和法律领域的大模型在本次调研中占比超过 30%。
随着AI大模型技术的迅速发展,相关岗位的需求也日益增加。大模型产业链催生了一批高薪新职业:
人工智能大潮已来,不加入就可能被淘汰。如果你是技术人,尤其是互联网从业者,现在就开始学习AI大模型技术,真的是给你的人生一个重要建议!
最后
如果你真的想学习大模型,请不要去网上找那些零零碎碎的教程,真的很难学懂!你可以根据我这个学习路线和系统资料,制定一套学习计划,只要你肯花时间沉下心去学习,它们一定能帮到你!
大模型全套学习资料领取
这里我整理了一份AI大模型入门到进阶全套学习包,包含学习路线+实战案例+视频+书籍PDF+面试题+DeepSeek部署包和技巧,需要的小伙伴文在下方免费领取哦,真诚无偿分享!!!
vx扫描下方二维码即可
加上后会一个个给大家发
部分资料展示
一、 AI大模型学习路线图
整个学习分为7个阶段
二、AI大模型实战案例
涵盖AI大模型的理论研究、技术实现、行业应用等多个方面。无论您是科研人员、工程师,还是对AI大模型感兴趣的爱好者,皆可用。
三、视频和书籍PDF合集
从入门到进阶这里都有,跟着老师学习事半功倍。
四、LLM面试题
五、AI产品经理面试题
六、deepseek部署包+技巧大全
😝朋友们如果有需要的话,可以V扫描下方二维码联系领取~
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/2401_85325726/article/details/147037214