ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

YOLOv8动物检测实战:数据集、训练避坑与工程落地指南

2026/10/1 2:18:49 拓冰建站 浏览量
YOLOv8动物检测实战:数据集、训练避坑与工程落地指南 简介这是一套基于YOLOv8的动物检测完整工程面向计算机视觉课程大作业与实战练习者内容覆盖从数据标注、模型训练、推理验证到图形化界面展示的全流程。资源包含约5000张已标注动物图像、可直接运行的训练与验证代码、训练好的YOLO系列模型以及一套易于操作的图形化交互系统代码同时适配v3、v5、v9、v10等版本方便对比不同骨干网络的检测效果。配套的视频讲解与作者联系方式也一并提供遇到环境配置或训练调试问题时可以借助这些材料快速定位。压缩包共2000个文件其中1938个txt为标注标签文件另有Python脚本、YAML配置、Markdown说明、C/HTML界面相关文件等整体约397.56MB目录划分清楚便于按模块取用。目前已有953人学习下载适合希望快速搭建动物检测项目、完成课程大作业或入门YOLO系列模型的开发者。1. 用YOLOv8做动物检测为什么这是大作业里最不容易翻车的方向用YOLOv8做动物检测这个题目拆开就两件事YOLOv8和动物检测。前者是目前生态最友好的目标检测框架后者属于目标检测里最经典的一类任务。解压这个zip之后你真正要交付的成果是给一张图片或一段视频模型能框出动物并标出类别再配上一套能讲清原理和实验过程的材料。这个方向被选为大作业是有道理的。它同时满足了课程设计的三个刚性需求结果看得见一张图上直接画出检测框和置信度、过程可交代训练日志、损失曲线、mAP指标都能导出成图、算力要求不高哪怕是CPU也能跑显卡稍好一点就能在合理时间内训完。我见过太多人一上来就折腾什么注意力机制、改进模块结果答辩时连预训练权重都没跑通。先把从数据集到训练的链路走通后面才有资格谈优化。下文按我实际做过的流程来讲选型为什么是YOLOv8、数据集怎么准备、训练参数怎么定、哪些坑几乎人人都会踩。2. 把动物检测拆成技术任务YOLOv8选型、网络结构、输出解析“动物检测”落到技术上就是目标检测的一个子集输入图像输出每个目标的位置框和类别。常见做法是直接复用通用目标检测框架把类别定义成动物的集合。这里面有一个很多人会忽略的事实YOLOv8的官方预训练权重是用COCO数据集训练的COCO本身已经包含猫、狗、马、羊、牛、大象、斑马、长颈鹿等常见动物类别。如果只检测这些类理论上拿到权重就能开箱推理。但大作业很少允许你只调一个接口就交差通常要求具备“自己的数据集训练”这个环节。所以预训练权重只能当跳板真正的功夫在数据处理和训练配置上。2.1 为什么是YOLOv8而不是YOLOv5或Faster R-CNN大作业选型不能只比论文里的精度数字要比“复现成本”。我做过几个目标检测项目之后对这三个路的评价比较明确。方案定位大作业视角Faster R-CNN两阶段检测精度上限高环境配置偏重训练慢出图效果不够直观YOLOv5单阶段生态成熟使用广泛但官方迭代节奏已放缓文档相对散YOLOv8单阶段库一体化训练、验证、推理、导出都是一条命令答辩友好选YOLOv8最直接的理由是ultralytics这个库把整条流水线打通了。数据准备好了之后训练只跑一条命令验证只跑一条命令导出ONNX也是一条命令。这对课程设计来说非常划算你不是在研究新算法你是在交付一个能复现的检测系统。YOLOv8还引入了Anchor-Free机制意味着你不需要手动算先验框尺寸默认配置就能直接开训减少了一个可解释性很差的黑匣子。另一个常被忽略的点是这套框架的Python API和CLI两套入口完全等价写报告时可以引用官方文档答辩被追问底层结构时也有东西可讲。2.2 网络结构里影响训练的三个点C2f、SPPF与Anchor-Free不需要把YOLOv8的每层结构都背下来但有三处改动跟你的训练体验直接相关值得写在报告里。第一处是C2f模块替代了YOLOv5的C3。C2f借鉴了CSP和E-ELAN的思路把特征图在通道维度上拆分后再交叉融合让梯度流更丰富。对大作业的影响是在数据量不大的动物数据集上C2f让模型更容易学到可迁移的特征收敛相对平滑。第二处是SPPF保留在backbone尾部。它用多个尺度的池化核堆叠扩大感受野而不明显增加计算量。动物检测里经常出现“小动物在远处只占十几个像素”的场景SPPF能保证网络高层确实看到了上下文信息而不是只盯着局部纹理。第三处是Decoupled Head和Anchor-Free。分类和回归分支从共享参数改为解耦各自用独立的卷积头这让训练时梯度冲突变小。而Anchor-Free意味着输出不再依赖预设的锚框尺寸每个位置直接预测“这个位置到物体边界四条边的距离”。这条改动对你的实际价值是换数据集时不需要做k-means聚类重新算anchor少了一个调参步骤。模型尺寸方面大作业用yolov8n或yolov8s就够更深的模型在小样本数据集上更容易过拟合。2.3 预测结果怎么读框坐标、置信度与类别ID很多人跑通了推理却说不清模型到底输出了什么。以输入640×640为例模型会在三个尺度上做预测对应特征图尺寸大约是80×80、40×40和20×20总计约8400个预测位置。每个位置会回归出目标框的中心坐标和宽高同时预测一个类别概率分布。这些原始输出经过NMS非极大值抑制之后才变成你最终看到的一个个框。在ultralytics框架里这些后处理已经被封装好了。用官方API拿到的结果框已经是原图坐标系下的xyxy格式不需要你手动解析原始张量。但你要能说清这个过程答辩时这是高频追问点。from ultralytics import YOLO model YOLO(yolov8n.pt) results model.predict(sourceanimal_test.jpg, conf0.25) r results[0] xyxy r.boxes.xyxy.cpu().numpy() # 每个框的左上角和右下角坐标 conf r.boxes.conf.cpu().numpy() # 每个框的置信度 cls r.boxes.cls.cpu().numpy() # 每个框的类别ID for (x1, y1, x2, y2), c, cl in zip(xyxy, conf, cls): print(fclass_id{int(cl)} conf{c:.3f} box({x1:.1f},{y1:.1f})-({x2:.1f},{y2:.1f}))这段代码里xyxy是直接可以画图的坐标cls是整数ID需要配合类别名字典转成“cat”“dog”这样的可读名称。conf则是模型对这个框的自信程度一般筛掉0.25以下的检测结果能减少误报。注意model.predict返回的是一个列表每个元素对应一张输入图哪怕只传一张图也要取results[0]。这个封装虽然省事但如果你后续要自己写NMS或者做模型导出还是需要回到“网络输出是一张大张量”这个基本认知上去。3. 数据集从哪来LabelMe标注转YOLO格式的转换脚本与边界坑数据集是大作业质量的分水岭。训练代码大家都长得差不多数据不同结果天差地别。这个环节我一般分三步走先搞一批现成数据做baseline再标注一部分自建数据贴合题目场景最后统一转成YOLO能吃的格式并做完整性检查。3.1 用现成数据还是自己标两条路线怎么搭如果题目只要求“做动物检测”最省力的做法是直接用公开的动物数据集或者从COCO里过滤出动物类别。好处是框质量高、类别多很快就能把训练流程整体跑通坏处是类间数量通常很不均衡比如猫的样本好几千张鹿只有几十张导致模型对少数类的召回率很难看。我建议的做法是“现成数据打底自建数据补场景”先用公开数据把yolov8n训到一个能看的水平再针对你实际要检测的场景比如宿舍楼下的猫、校园里的鸟自己标几十到两百张图做微调。这样既有数据量又有场景贴合度。无论哪种来源下载的数据集通常是个压缩包解压后第一件事不是急着训练而是看目录结构。常见翻车现场是压缩包根目录套了一层同名文件夹导致路径多了一层。先用命令把结构摸清楚unzip animal_dataset.zip -d ./dataset find ./dataset -maxdepth 3 -type d | head -20 ls ./dataset/train/labels | head -5find负责列出三层以内的目录结构确认有没有多套一层目录ls看标签文件是否真的存在。这一步花两分钟能省掉后面“FileNotFoundError”反复排查的半小时。另外图片和标签一定用英文命名中文文件名在Windows和Linux之间来回解压特别容易出现编码乱码这是数据准备里最容易忽略的坑。3.2 LabelMe标注的实操原则紧贴轮廓、遮挡先标可见部分自己标注时LabelMe是常见选择因为它支持多边形标注能标出不规则形状的动物。但要注意LabelMe导出的json存的是多边形顶点坐标而YOLO需要的是一组归一化后的矩形框参数。所以你得先明确两件事多边形怎样转矩形以及标注时按什么标准画。我的原则是两条。第一多边形紧贴动物轮廓不要为了省事画一个大方框把背景全包进去。因为后续转矩形时取的是多边形顶点的min和max你画得越松转出来的框背景比例越大模型学到的特征里噪声越多。第二动物被遮挡时只标可见部分。比如一只猫躲在栅栏后面露出半个身子就把露出来的部分框出来。这与目标检测评测的通行口径一致——框住可见区域比试图脑补完整轮廓可靠得多。标注数量上每个类别最少50张能到200张更好。单张图里如果有多个动物每个都标同一只动物被截断成两部分按两个目标分别标模型对遮挡的鲁棒性会更好。另外养成习惯每天标完看一眼json里的imageWidth和imageHeight字段这两个值是后面归一化的分母一旦缺失或为0转换脚本会直接崩。3.3 转换脚本LabelMe JSON转YOLO txt的完整实现YOLO需要的标签格式是纯文本每行五个数字class_id x_center y_center width height所有值都是相对图像宽高的归一化比例。这个转换脚本我每次做新数据集都会复用核心逻辑是读json、取多边形顶点、计算外接矩形、归一化、写txt。import json import glob import os # 类别字典一旦定下来顺序就不要改了直接影响训练时的类别ID label_map {cat: 0, dog: 1, bird: 2} src labelme_json # LabelMe导出的json目录 dst labels # 输出YOLO格式的目录 os.makedirs(dst, exist_okTrue) for jf in glob.glob(os.path.join(src, *.json)): with open(jf, encodingutf-8) as f: data json.load(f) img_w data[imageWidth] img_h data[imageHeight] if not img_w or not img_h: print(f[skip] {jf} missing image size) continue txt_name os.path.splitext(os.path.basename(jf))[0] .txt lines [] for shape in data[shapes]: label shape[label] if label not in label_map: continue pts shape[points] xs [p[0] for p in pts] ys [p[1] for p in pts] # 取多边形外接矩形YOLO只接受轴对齐框 x1, x2 min(xs), max(xs) y1, y2 min(ys), max(ys) w x2 - x1 h y2 - y1 if w 0 or h 0: continue # 归一化到0~1并做边界保护 xc (x1 x2) / 2.0 / img_w yc (y1 y2) / 2.0 / img_h wn w / img_w hn h / img_h xc min(max(xc, 0.0), 1.0) yc min(max(yc, 0.0), 1.0) wn min(max(wn, 0.0), 1.0) hn min(max(hn, 0.0), 1.0) lines.append(f{label_map[label]} {xc:.6f} {yc:.6f} {wn:.6f} {hn:.6f}) with open(os.path.join(dst, txt_name), w, encodingutf-8) as f: f.write(\n.join(lines)) print(done)这段脚本有几个地方值得说明。取多边形顶点坐标的min和max得到的是轴对齐外接矩形不是最小面积矩形但对目标检测来说足够YOLO本身用的就是轴对齐框。归一化时分母是json里的imageWidth和imageHeight不是图片实际读出来的尺寸两者必须一致否则框会偏移。边界保护clip是防止动物正好顶到画面边缘时归一化值略大于1或小于0这种脏数据会让训练时损失出现突刺。类别映射用字典做好处是顺序完全可控比classes.txt自动生成的顺序更不容易出错。顺带说一句Python内置还有一个zip()函数作用是把多个列表按位置配对成元组跟压缩包zip不是一回事。写脚本时变量名别叫zip不然会跟内置函数冲突这种低级报错很折腾人。3.4 数据划分与data.yaml生成训练前最后一道检查标签转完之后下一步是把图片和标签按8:2划分到train和val两个集合并生成data.yaml。YOLOv8新版训练时不要求手动维护train.txt清单它只认data.yaml里的路径和目录结构。所以目录的一致性就是生命线images/train里放了一张图labels/train里必须有对应的同名txt文件名要完全一致包括后缀规则和大小写。import os import random import shutil random.seed(42) image_dir images label_dir labels train_ratio 0.8 os.makedirs(dataset/images/train, exist_okTrue) os.makedirs(dataset/images/val, exist_okTrue) os.makedirs(dataset/labels/train, exist_okTrue) os.makedirs(dataset/labels/val, exist_okTrue) images [f for f in os.listdir(image_dir) if f.lower().endswith((.jpg, .png, .jpeg))] random.shuffle(images) split_idx int(len(images) * train_ratio) train_imgs images[:split_idx] val_imgs images[split_idx:] for img in train_imgs: base os.path.splitext(img)[0] if not os.path.exists(os.path.join(label_dir, base .txt)): print(f[warn] no label: {img}) continue shutil.move(os.path.join(image_dir, img), dataset/images/train/) shutil.move(os.path.join(label_dir, base .txt), dataset/labels/train/) for img in val_imgs: base os.path.splitext(img)[0] if not os.path.exists(os.path.join(label_dir, base .txt)): print(f[warn] no label: {img}) continue shutil.move(os.path.join(image_dir, img), dataset/images/val/) shutil.move(os.path.join(label_dir, base .txt), dataset/labels/val/) print(ftrain: {len(train_imgs)} val: {len(val_imgs)})这个脚本做完后建议再手动抽查两个数训练集和验证集有没有类别分布明显失衡比如验证集里正好没有某种动物。另外脚本只处理了图片和标签的对应关系没有做“有标签无图”的检查那种一般不会出现但“有图无标签”很常见所以我用[warn]做了提示。划分时把随机种子固定了这样每次划分结果一致实验可复现。4. 训练自己的动物检测模型环境搭建、配置写法与必调参数到这一步数据集已经就绪训练环节的关键不是把命令跑通而是知道每条命令在做什么、每个参数在调什么。环境安装是劝退新手最多的环节但也是最不值得焦虑的环节。GPU机器就是两行pip的事CPU机器也能跑只是慢几倍。4.1 环境搭建Ubuntu 20.04 CPU版与GPU版各自怎么跑无论什么系统先建虚拟环境隔离Python依赖避免把系统环境搞乱。很多人的翻车现场是conda环境里已经装了一个老版本torch再装ultralytics时依赖冲突导致import torch失败。conda create -n yolo python3.10 -y conda activate yolo pip install ultralytics python -c import torch; print(torch.cuda.is_available())在Ubuntu 20.04上纯CPU版本也能训练只是速度要放慢心态。CPU训练yolov8n一张640×640的图大概能跑到每秒几步50个epoch可能要跑几个小时完全可以接受。GPU机器上如果最后一行输出True说明CUDA可用如果输出False先别急着重装检查是不是装了CPU版torch。一件常见做法是直接卸载torch后安装对应CUDA版本的torch这里不展开CUDA安装细节按官方索引装即可。装完先跑一次预训练模型验证链路yolo predict modelyolov8n.pt source./test.jpg首次运行会自动下载yolov8n.pt权重大概几MB级别不需要手工去找下载地址。如果这张测试图能正常出框说明ultralytics安装成功接下来就可以换成自己的数据和权重了。4.2 data.yaml里最容易写错的两个字段path和names训练数据目录和类别定义都写在data.yaml里这个文件只有几行但写错一个字段训练就会白跑一小时。我见过最多的问题是path指错了层级比如把path写到了dataset目录的上一级。YOLOv8对path的解析方式是先读path作为根目录再拼接train和val子路径。所以三者拼起来要刚好指向图片目录。# 这个文件的路径dataset/data.yaml path: ./ # 数据集根目录相对data.yaml所在位置 train: images/train val: images/val nc: 3 names: 0: cat 1: dog 2: bird这里有三个细节值得反复确认。第一path写成相对路径时它的基准是data.yaml所在目录不是当前终端的目录所以用./开头最稳妥。第二names的顺序必须和转换脚本里的label_map完全一致0、1、2的顺序一旦定下来就不能动否则会出现“标签是猫模型学的是狗”这种错位。第三nc要等于names的数量写小了训练时会直接报标签索引越界写大了训练出的模型会多出空类别拉低mAP。4.3 三组必调参数imgsz、batch、epochs附学习率与优化器训练命令长这样参数不多但每个都值得理解后再动yolo train datadataset/data.yaml modelyolov8n.pt epochs50 imgsz640 batch16model可以传一个预训练权重路径也可以传一个网络结构yaml文件名。传权重的意思是“在COCO预训练基础上微调”迁移学习效果对大作业数据集通常是捷径传yaml文件则是从随机初始化开始训练对数据量要求高很多。我建议直接用yolov8n.pt。接下来是三个影响结果的参数我按重要性排了序。参数含义我的建议imgsz训练输入分辨率默认640小目标多时直接上1280batch单次迭代样本数1660 Ti这类6G卡n模型开16s模型开8epochs训练轮数先跑50看曲线收敛慢再加到100lr0初始学习率默认0.01不收敛再往0.005调optimizer优化器默认auto即可不要乱换以GTX 1660 Ti为例6G显存跑yolov8n加640分辨率加batch16是能跑完的更稳妥是8。如果显存不足优先降batch而不是降imgsz因为对动物检测这种任务小目标对分辨率更敏感。batch-1的auto模式会不断试探最大batch机制听起来省心但它测出的最大值会顶满显存新手看到CUDA out of memory就慌所以我建议还是手写一个保守值。4.4 训练日志与损失曲线怎么判断真的收敛了训练过程中ultralytics会在runs/detect/train*/目录下生成results.png和results.csv。很多人只看最后的mAP数字其实那不够损失曲线的形状信息量更大。判断标准很简单训练损失下降、验证损失也在下降说明模型还在学训练损失降但验证损失开始回升或平台说明过拟合已经开始了这时候最合适的模型通常是验证损失最低点的那个epoch对应的权重。results.csv里每一列对应一个指标列名首尾带有空格读取时先做处理import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/detect/train/results.csv) df.columns [c.strip() for c in df.columns] plt.figure(figsize(8, 4)) plt.plot(df[epoch], df[train/box_loss], labeltrain box_loss) plt.plot(df[epoch], df[val/box_loss], labelval box_loss) plt.xlabel(epoch) plt.ylabel(loss) plt.legend() plt.grid(True) plt.savefig(loss_curve.png, dpi200)这段代码把训练和验证的框回归损失画在一起。如果两条曲线在最后20个epoch还在同步下降说明epochs给少了可以续训或把epochs加大如果验证损失在中间某个点掉头向上训练损失还在降那就是典型的过拟合信号此时再去加epochs没有意义反而应该回调到曲线最低点对应的权重。画损失函数曲线图这个动作不仅是为了报告美观它也是你最直接的“模型有没有问题”的诊断工具。5. 动物检测实训避坑解压、路径、显存与损失不降的5条记录这一章的内容全部来自踩坑记录按现象、原因、解决三段写。没有一条是玄学全是实实在在的工程细节。5.1 zip解压后多套一层目录训练直接报图片不存在现象解压完动物数据集zip按直觉把path指到dataset/训练时报FileNotFoundError提示找不到dataset/images/train下的图片。原因压缩包打包时把整个项目文件夹都包进去了解压后实际结构是dataset/动物数据集/images/train你在path里少写了一层目录。解决解压后先执行find ./dataset -maxdepth 2 -type d看清楚真实目录层级再写data.yaml。解决后要注意图片和标签的相对位置关系也要保持同步比如图片在train/images标签就要在train/labels两个目录必须平级。5.2 标签类别ID错位模型全部预测成第一个类现象训练时损失正常下降验证图也能出框但所有框的类别都是0哪怕输入的是狗。原因转换脚本里的label_map定义顺序与data.yaml里的names顺序不一致。比如脚本里{cat: 0, dog: 1}但data.yaml里写的是0: dog, 1: cat模型学的映射就全反了。解决用一套固定顺序的类别清单一条命脉走到底。我一般在项目根目录放一个classes.txt转换脚本和data.yaml都从它读取杜绝手工同步出错的概率。另外训练前写一行代码检查标签ID最大值是否小于nc如果某个txt里出现了比类别总数还大的数字说明转换脚本或原始标注漏了类别。5.3 GTX 1660 Ti训练报显存不足先降batch再降imgsz现象训练命令刚跑完第一个batch终端打出CUDA out of memory。原因batch和imgsz共同决定显存占用1660 Ti这类6G卡的余量本身不大imgsz设为640时batch16已经接近上限再用auto模式继续试探就会爆。解决先固定imgsz640把batch降到8跑通全流程确定一切正常后再加batch或提升imgsz。改动参数时每次只动一个变量这是控制变量的基本原则。另外补充一个技巧训练时把workers调低到2或4有时内存不足也会被误报成显存问题降workers能缓解数据加载端的压力。5.4 训练损失在降、验证mAP却很低先查样本分布现象训练损失掉到了0.05以下但mAP0.5只有0.3验证集上少数类别几乎是零召回。原因动物数据集里类别数量极不均衡。假设猫有300张、老虎只有15张模型学到了猫的大量特征老虎则很快被淹没。损失函数是全体样本的平均猫的loss占主导所以损失看起来在降老虎根本没学会。解决先统计每个类别的图片数把数量差距悬殊的类别处理一下。常见做法是给少数类复制增强抖动、旋转、HSV扰动或者干脆在报告里明确说明“该类别样本量不足模型对其召回有限”。如果某个类别连20张都没有建议直接剔除否则它会把mAP拖得很低答辩时不好解释。5.5 默认imgsz推理漏检小动物训练与推理分辨率要一致现象训练时mAP指标不错但拿训练好的模型去跑新的实拍图远处的猫狗全漏了。原因训练时用的是imgsz640推理时也用了640但训练数据里的目标普遍偏大模型没怎么见过小目标。动物检测和行人检测有点像目标尺度变化大你实际场景里的小目标占比跟训练集不一致模型自然漏。解决推理时把imgsz提高到1280用小图块检测通常能立刻捡回一部分漏检。提高imgsz会带来显存和耗时增加但不影响模型权重。另外有些小目标不是漏检而是NMS时和相邻大目标框重叠度太高被抑制了排查时把max_det调大、iou阈值适当调高能定位问题到底出在前处理还是后处理。6. 把训练好的模型用起来推理脚本、结果解析、模型导出训练完了最终交付是让best.pt真正能跑起来并且你能讲清楚每一步。这篇最后给一套我常用的落地组合拳。6.1 用best.pt做批量推理并解析每个框from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) results model.predict( sourcetest_images, conf0.25, iou0.45, imgsz640, saveTrue, ) for r in results: xyxy r.boxes.xyxy.cpu().numpy() confs r.boxes.conf.cpu().numpy() clss r.boxes.cls.cpu().numpy() for (x1, y1, x2, y2), c, cl in zip(xyxy, confs, clss): print(fcls{int(cl)} conf{c:.3f} box[{x1:.1f},{y1:.1f},{x2:.1f},{y2:.1f}])source可以传单张图、文件夹或视频saveTrue会把画好框的图存到runs/detect/predict/目录下。框坐标是原图尺寸的像素值不是归一化值画框时不用再乘一次宽高。conf阈值控制灵敏度调低会出更多框但误报也变多iou控制NMS时的去重力度目标密集的场景可以适当调高。6.2 用val命令确认mAP而不是肉眼判断yolo val modelruns/detect/train/weights/best.pt datadataset/data.yaml终端输出里会有mAP50和mAP50-95两个指标。mAP50是IOU阈值为0.5时的平均精度大作业答辩最常引用mAP50-95把阈值从0.5到0.95平均计算更严格。注意两点验证集必须单独留出不能用训练集的数据否则指标虚高还有Val结果要以best.pt为准而不是last.pt后者是最后一个epoch的权重未必是最好的那版。6.3 导出ONNX与后续部署方向yolo export modelruns/detect/train/weights/best.pt formatonnx opset12导出的best.onnx可以用onnxruntime跑推理速度比PyTorch原生快也为后续部署留了口子。如果题目要求上嵌入式设备常见路径是再把模型转到RK3588这类边缘板卡或者做量化压缩后落到hi3516这类方案上。这些属于部署工程的范畴但导出ONNX这一步现在做了后面就不会返工。我自己的习惯是每次训练前把data.yaml、训练命令、显存占用三个信息抄进一个实验备忘录出问题时先翻备忘录而不是先动参数。这个习惯帮我省掉了无数个“不知道自己改了哪个参数才变好”的夜晚参数也慢慢不再像玄学。希望帮到你。本文还有配套的精品资源点击获取