自从目标检测进入工程落地阶段之后我越来越觉得真正麻烦的往往不是模型本身而是怎么把模型、数据和界面串成一个完整可用的系统。最近帮朋友做了一个食物检测的项目核心是“基于深度学习的食物检测系统”技术栈是YOLOv10加YOLO数据集再加上一个可视化UI界面最后交付完整可运行的模型。整个过程做下来挺有代表性踩了不少文档里找不到的坑写一篇文章复盘一下希望能给准备做类似项目的同学一点参考。这个系统解决的实际问题很明确输入一张图片、一帧视频或者实时摄像头画面系统自动识别画面中的食物类别并框出位置同时展示置信度。因为加了UI界面用户可以像用普通软件一样操作不需要写任何代码。所以它特别适合用在食堂自助结算、食物热量估算、饮食记录APP的预处理、以及高校实验室的课程设计或毕业设计场景。下面直接从项目拆解开始讲。1. 项目概述与整体设计思路1.1 这个系统到底在做什么先把这个项目拆开看。标题里的几个关键词其实对应了系统的核心模块深度学习模型承担视觉识别的核心能力也就是“认出食物”的部分。YOLOv10当前迭代较新的目标检测框架负责目标定位和分类。YOLO数据集训练模型所需的数据基础。这里指的是数据集的格式是YOLO标准的txt标注格式而不是某个固定数据源。UI界面让非技术用户能直接操作的可视化交互层。模型训练完成后生成的权重文件是整个系统的推理核心。换句话说这个项目的本质不是单纯跑一个训练脚本而是把“数据准备—模型训练—模型部署—界面交互”这一整条链路走通。我在实际做的过程中把项目分成了数据准备、模型训练、界面集成、封装部署四个阶段每一阶段都有独立的输出物。1.2 为什么选YOLOv10而不是其他版本这个是很多初学者问得最多的问题。我先说结论不是YOLOv10比其他版本强多少而是它在当前版本迭代中的综合体验最好。YOLO系列经历了从v5到v8再到v10的快速迭代。YOLOv5是经典中的经典社区资料最全但代码结构相对老一些YOLOv8引入了更清晰的模块化设计训练体验要比v5顺滑不少YOLOv10在v8的基础上重点优化了两个方向一个是去掉了非极大值抑制NMS另一个是改进了模型结构提高了推理效率。很多人听到“去掉NMS”觉得很神奇我简单解释一下。传统目标检测在预测阶段会产生大量重叠的候选框NMS的作用就是把这些重叠的框合并成一个最合适的框。YOLOv10通过一种叫做“一致性双分配”的策略让模型在训练阶段就学会直接输出最终框推理时就不再需要额外做一次NMS了。这样做的好处非常直接推理速度更快延迟更稳定部署到边缘设备上更友好。当然如果你用的是老设备或者希望参考资料更丰富用YOLOv8也完全没问题。从项目交付角度来看v10和v8的训练接口都基于ultralytics框架迁移成本很低。后面涉及到代码我会按v10来写但很多内容对v8也通用。1.3 技术选型的整体权衡这个项目的技术选型我说一下自己的思考和取舍供参考。模型框架选ultralytics这个库把训练、验证、导出封装得比较完整适合快速迭代。UI方案选PyQt5考虑到项目需要在本地Windows环境跑PyQt5跨平台稳定做桌面应用交互体验好。比用Web前端方案省去了开服务、管理端口的麻烦。数据标注格式选YOLO txt因为ultralytics原生支持训练时不需要额外转换。模型导出考虑ONNX虽然交付的时候用了PyTorch权重但为了性能考虑后面也做了ONNX导出方便在有加速设备的机器上跑。做技术选型的时候我的原则是“够用就好别炫技”。目标检测的模型可选范围很大数据格式、UI框架也有很多组合方式但工程项目的核心是先跑通再优化。选ultralyticsYOLOv10PyQt5这套组合正是因为它们在配合上很成熟文档多、踩坑少、交付稳定。2. YOLOv10核心机制与食物检测适配2.1 先搞懂anchor-free和NMS-free先说anchor-free。旧版YOLO需要预先定义一组不同大小、不同长宽比的anchor框训练时让模型在anchor的基础上做微调。anchor的设计往往直接影响小目标检测效果。YOLOv10是anchor-free结构模型直接预测物体中心点和宽高尺寸少了一个需要手工设计的环节对食物这种有大量不规则形状、大小差异明显的目标来说反而更省心。再说NMS-free。刚才提到YOLOv10采用了一致性双分配策略。它的核心思想是在训练的时候给每个目标分配两个分支一个主分支用于计算最终损失一个辅助分支用于辅助收敛推理阶段只用主分支因此不需要再做NMS。带来的直接感受是检测速度变快同时因为少了一个后处理步骤代码调用的逻辑也更简单。不过要提醒一点NMS-free不代表完全不会出现重复框。在实际测试中当画面中两个物体挨得非常近比如餐盘里两份菜贴在一起偶尔还是会出现一个目标被框两次的情况。这个问题可以通过调节置信度阈值来控制我后面会在问题排查章节再展开。2.2 食物检测用哪个规格的模型YOLOv10按照参数量从低到高提供了nano、small、medium、large、xlarge五个规格。食物检测这种任务目标类别不算特别多常见的就是几十个类别的菜品不需要追求极高的类别区分度所以我选择了small版本作为主力模型。选small的原因是nano模型训练速度快但精度损失比较明显尤其是对一些外形相似的食物比如不同做法的鸡胸肉和猪肉片区分度不够。medium和large的精度会更好一点但训练时间大幅增加推理帧率也会下降。small正好在精度和速度之间取了一个合适的平衡点。我实际测试下来small模型在640×640输入分辨率下GPU环境下单张图片推理大约在十几毫秒到几十毫秒的范围内完全满足界面实时交互的需求。如果后面有更高精度需要可以在同类数据上继续训练或者平滑升级到mediumYOLOv10的权重切换非常方便。2.3 输入分辨率怎么定YOLO系列默认的输入分辨率是640×640这个值对于大多数场景都够用。不过食物检测有一点特殊食物在图片中的占比通常比较大因为拍照的时候手机离餐盘比较近单独的菜品在画幅里能占20%~40%的像素面积。所以输入分辨率不一定要拉得很高。分辨率太高会增加计算开销而且如果没有大量高分辨率训练样本实际提升有限。我建议食物检测统一用640×640训练代码里保持默认不需要额外调大。后面的UI界面上倒是可以做两个入口一个入口处理本地图片一个入口处理实时视频流视频流为了流畅度可以缩放到480p左右再送进模型。3. 食物数据集的构建与预处理3.1 数据集从哪里来食物检测项目里面数据集的质量直接决定模型的上限。这是整个项目里最花时间、也最容易被忽视的部分。我的建议是先选一个公开的食品数据集做底座然后再补充自建数据。完全从零标注几百张图需要好几天而且类别很难覆盖全面。如果完全依赖公共数据集又会遇到类别定义和实际场景不匹配的问题——比如大家常说的“宫保鸡丁”各家的做法差异很大数据集里可能只有一种形态。我做的方案是这样的基础数据用公开食物数据集里的常见类别先筛出项目需要的十几个类别不追求大而全。然后自己拍摄或收集大约每类50~100张补充图片覆盖不同角度、不同光照、不同摆盘方式。把这些图片统一整理成YOLO格式重新划分训练集、验证集、测试集。补充数据这个环节特别重要。你后面UI界面演示的时候如果用自己手机拍的照片测试模型效果好不好完全看这一块做没做。现实中的食堂餐盘、外卖盒、不同灯光下的手机照片和公开数据集里的“标准照”差别很大。3.2 标注工具与YOLO格式说明数据集准备好之后需要一个一个把目标框标出来。我用的是labelImg操作方式和很多标注工具类似打开图片、画矩形框、填类别名、保存。标注的时候有几个细节值得注意框要贴合物体边缘不要留太多空白也不要截断目标。如果一个食物被遮挡了一部分比如两块肉叠在一起框可以稍微大一点把可见部分包进去。边缘模糊的目标宁可不标也不要硬标。标一个“糊框”进去模型训练的时候会学到错误特征。标注完成后每张图对应一个txt文件内容大概是这样的0 0.521875 0.392361 0.321875 0.218750一行五个数字含义分别是类别id、中心点x坐标、中心点y坐标、框宽度、框高度。注意这里的坐标全部是归一化之后的数值范围在0到1之间用的是相对图片宽高的比例不是像素坐标。这是YOLO格式和COCO格式最大的区别经常有人在这个地方出问题。3.3 类别平衡与数据增强食物类别之间存在天然的不平衡问题。比如米饭通常是主粮出现频率极高而有些菜品本身就少见于公开数据集。如果不做任何处理模型会偏向预测高频类别导致低频类别的召回率比较差。我处理这个问题用了两个办法。一是数据层面做简单重采样对样本量不足的类别多复制几份同时对样本量过大的类别做随机抽样让每个类别在训练集中不会过于悬殊。二是训练时开启ultralytics自带的增强包括随机翻转、缩放、颜色抖动、HSV变化等这些增强方式默认都是打开的。关于数据增强我多说一句默认配置对于大多数情况已经够用但不要去开那种过强的马赛克增强。YOLO里的马赛克增强是把四张图拼在一起训练好处是模型见过更多背景变化坏处是如果训练轮数太长会导致模型对小目标过拟合。食物检测中小目标不算多所以我对马赛克增强保持适度即可不用刻意调得更强。数据目录结构我最终整理成这样的food_data/ images/ train/ image_001.jpg image_002.jpg val/ ... labels/ train/ image_001.txt image_002.txt val/ ... data.yamldata.yaml是ultralytics训练时必须的配置文件里面写清楚训练集路径、验证集路径和类别名称列表后面训练那里会详细贴出来。4. 模型训练全流程与关键参数解析4.1 训练环境配置训练环境我建议直接用conda建一个独立环境避免和其他项目互相污染依赖。核心依赖如下conda create -n food_yolo python3.10 conda activate food_yolo pip install torch torchvision pip install ultralytics如果你有NVIDIA显卡先装合适版本的CUDA版PyTorch装完可以用一段测试代码验证GPU是否可用。CPU也能训练就是慢很多几百张图片的数据集在CPU上跑几十轮可能要以小时为单位计算所以有条件还是建议用GPU。版本方面ultralytics库迭代很快文章里的代码基于ultralytics 8.x版本如果你拿到的版本更高接口变化不会特别大但遇到报错时优先去查对应版本的文档。4.2 数据配置与训练启动先看data.yaml内容很直接path: ./food_data train: images/train val: images/val names: 0: rice 1: chicken 2: beef 3: broccoli 4: egg # 按自己数据集修改注意path字段推荐用相对路径如果你把整个项目文件夹移动到另一台电脑不用改文件内容就能直接跑。如果训练时遇到图片颜色怪异的偏移检查一下这里的路径是不是指向了正确的目录。训练命令在ultralytics里非常简洁yolo detect train datadata.yaml modelyolov10s.pt epochs100 imgsz640 batch16几个核心参数我解释一下epochs食物检测数据集不算大100轮左右是一个比较合适的范围。太少的轮数模型还没收敛太多的轮数不仅耗时还容易过拟合。batch这个取决于显存大小。16是一个比较保守的数值如果你的显卡显存比较充足可以调到32或64训练速度会快一些。imgsz输入分辨率保持640默认。model这里用yolov10s.pt作为预训练权重。加载预训练权重可以加快收敛不要求类别一致。模型会自动重新适配最后一层输出维度。4.3 训练过程怎么看训练过程中终端会实时打印每一轮的损失值、精度和召回率同时ultralytics会在项目目录下生成runs/detect/train文件夹里面存放训练曲线、混淆矩阵、验证样本的可视化结果。我这里给一个实际训练过程中的指标参考。食物检测这种任务如果是十几类以内的数据集训到收敛之后验证集上的mAP50一般能做到0.75到0.85之间mAP50-95大概在0.55到0.65之间。当然这个数值受数据集规模影响很大如果你的类别确实比较多、图片比较杂低于这个范围也正常关键是看验证集和测试集的表现而不是追求某一个固定的分数。训练过程中重点观察几个曲线box_loss和cls_loss这两个损失应该随轮次增加而下降且最后趋于平缓。如果损失曲线在训练后期反弹说明学习率可能偏高或者已经过拟合了。mAP50曲线如果在验证集上先升后降出现明显峰值后再掉头向下这也是过拟合的典型信号。混淆矩阵经常被忽略但实际上很有用它直接显示哪些类别之间互相混淆得厉害。比如我之前发现“西红柿炒蛋”和“番茄酱”之间误判严重后来检查了数据发现好多标注框把整盘菜全框进去了导致模型分不清主体目标修正标注之后情况明显好转。4.4 模型导出与验证训练完成之后会生成best.pt和last.pt。best.pt是验证集上表现最好的权重我们用这个模型做后续推理。推理验证很简单from ultralytics import YOLO model YOLO(runs/detect/train/weights/best.pt) results model(test_images/rice.jpg) results[0].show()如果这一句能正常弹出来一张画了检测框的图片说明模型本身是好的。这个步骤我在每次训练完之后都会跑一遍选几张有代表性、包含多个类别、光照条件不太好的图片来测试确认模型在真实场景下确实能用再进入UI开发阶段。5. UI界面设计与系统集成5.1 UI方案选型与整体布局UI界面是整个项目里价值感最强的部分。模型再厉害藏在脚本里非技术用户看不到但一旦有一个窗口化界面立刻就有了“软件”的感觉毕设答辩或者项目展示的时候观感完全不一样。我推荐用PyQt5做桌面应用。原因有三点跨平台支持好Windows和macOS都能跑。控件丰富文件选择框、标签、按钮、图片显示区都是现成的开发效率高。打包成exe的生态比较成熟后面方便交付。UI布局我采用了上下结构顶部是操作区用来选择图片、启动摄像头、调整置信度阈值中间是大面积图片显示区用于展示检测结果底部是信息栏显示检测类别、数量、单帧耗时等。布局的核心原则是“用户不需要说明书也能猜得到怎么用”。我不建议把所有按钮堆在一起你可以按模块分区块比如把“文件输入类”的操作放左上方“识别控制类”的操作放右上方中间图片区保持干净。5.2 界面中如何集成YOLO模型界面里面加载模型非常直接关键是处理好模型的生命周期和结果展示。初始化的时候创建模型实例class FoodDetectorApp: def __init__(self): self.detector YOLO(best.pt) self.conf_threshold 0.5检测图片的核心函数大概是这样def detect_image(self, image_path): results self.detector(image_path, confself.conf_threshold) img_bgr cv2.imread(image_path) for result in results: boxes result.boxes for box in boxes: x1, y1, x2, y2 map(int, box.xyxy[0]) class_id int(box.cls[0]) score float(box.conf[0]) label f{self.detector.names[class_id]}: {score:.2f} cv2.rectangle(img_bgr, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img_bgr, label, (x1, y1 - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 0), 2) return img_bgr这里强调两点一是坐标类型转换YOLO输出的xyxy是像素坐标但因为推理时内部可能做了缩放返回的坐标已经是映射到原图的直接画框没问题二是opencv读取的图片是BGR顺序如果你用matplotlib直接显示颜色会偏所以显示时要用cv2.imshow或者先转换成RGB再交给Qt界面。在PyQt界面里显示图片通常是通过QPixmap来做的这个环节有一个常见的坑QPixmap默认不支持直接从cv2的numpy数组构建需要先做一次转换。转换代码如下rgb_image cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB) h, w, ch rgb_image.shape bytes_per_line ch * w qimage QImage(rgb_image.data, w, h, bytes_per_line, QImage.Format_RGB888) pixmap QPixmap.fromImage(qimage).scaled( self.image_label.width(), self.image_label.height(), Qt.KeepAspectRatio, ) self.image_label.setPixmap(pixmap)如果你不想在界面显示时做这么多处理也可以直接在函数里把检测结果保存成jpg再用QLabel加载图片显示多了之后会略卡但差别不大。我做的是实时加载模式所以用的是上面的内存转换方案。5.3 扩展功能视频与摄像头检测只在界面上检测单张图片演示的效果会比较平淡。我后面加了两个扩展接口一个是视频文件检测一个是摄像头实时检测。摄像头实时检测在答辩演示时是非常加分的一项。核心逻辑是用OpenCV的VideoCapture循环读帧每一帧都调用YOLO模型做检测再把带框的画面显示在界面上。要注意帧率控制因为模型推理本身需要时间不加限制的话整个界面会变得卡顿。我在每一帧处理完之后加了一个小延时控制处理频率在实际可接受范围内。另外线程问题必须处理。直接在UI主线程里跑摄像头循环会导致窗口拖不动、按钮点不了原因是主线程被耗时操作阻塞了。我用了QThread来跑摄像头循环通过信号把新的一帧画面传回主线程更新UI。这个处理方式稍微增加了一点代码量但体验会流畅很多。这里也建议你封装成一个独立的CameraThread类而不是全塞在窗口类里面。5.4 模型封装与系统目录组织等模型稳定、界面也写好之后整个项目的目录我推荐组织成这样的结构food_detector/ main.py # 界面入口 detector.py # YOLO模型的封装类 camera_thread.py # 摄像头线程类 models/ best.pt # 训练好的模型权重 food_data.yaml # 类别名称映射 test_images/ # 测试用图片 requirements.txt把模型封装成独立类的好处是界面逻辑和模型逻辑解耦以后如果换模型版本、换推理框架只需要改detector.py一个文件界面完全不用动。这个习惯如果你打算长期做深度学习相关项目越早养成越好。6. 常见问题与排查技巧实录6.1 高频问题速查表我整理了一些实际操作中高频遇到的问题和排查思路做成一个速查表方便你照着看问题现象可能原因排查方法训练时loss为nan学习率过高或数据里有异常标注检查图片是否损坏、标注坐标是否超出0~1范围调低学习率模型什么都检测不出来置信度阈值过高或训练不充分把conf降到0.25再试查看训练曲线是否收敛检测结果全是同一个类别类别样本极不平衡检查各类别样本数量做数据均衡处理UI界面显示图片颜色怪异BGR和RGB通道顺序混用统一用cv2.cvtColor做转换摄像头检测非常卡顿在UI主线程里做了耗时操作把检测放到子线程主线程只负责刷新界面换电脑后训练报路径错误data.yaml里的path是绝对路径改成相对路径或确认项目目录完整性导出ONNX后结果不一致模型预处理或后处理方式不一致检查是否开启相同输入尺寸和置信度阈值这个表格里的问题基本都是从实际项目里踩出来的。看到这里建议你把它截图或收藏一下等项目做到后面真遇到了翻出来对一下比自己从头排查要省时间得多。6.2 踩坑实录数据标注错位问题第一次跑训练的时候我发现模型在验证集上的mAP很低于是去翻验证集的可视化结果发现了很典型的问题预测框和真实框整体位置错了一截。后来查下来原因是有一批图片在标注之后或者说图片在整理的时候被修改了尺寸而标注文件没有同步更新。也就是说图片从原图缩小了一半但标注框还是原图下的像素坐标没有归一化或者归一化基准不一致。这种情况在自建数据里特别容易发生因为大家经常用手机拍完照片用工具压缩一下再传到项目文件夹很容易忘记标注文件是否需要同步处理。我当时的解决办法是写了一个小脚本批量检测图片尺寸和标注坐标范围如果发现标注坐标聚类明显偏离合理范围就把对应图片挑出来重新标注。这里给一个自查方法正常YOLO标注的坐标都应该在0到1之间。如果你的txt文件里出现了大于1的坐标或者很多坐标都是四舍五入的整数比如0.5、1.0那大概率标注没转换正确训练前一定要先处理干净。6.3 踩坑实录UI界面线程卡顿问题摄像头检测那一块最早版本我图省事直接在窗口类的按钮事件里写了循环检测逻辑结果一启动摄像头窗口就变成“半死不活”的状态拖拽窗口非常卡关闭按钮点了十几秒没反应。原因是PyQt5的主线程被while循环占死了窗口事件完全得不到处理。解决办法就是前面提到过的把摄像头循环搬进QThread。这个改动不算复杂基本思路是定义一个继承QThread的子类重写run方法在run里循环读帧。每一帧检测完成后通过signal把处理好的图像发送到主线程。主线程中的槽函数只需要负责把收到的图显示到界面上。做完这一步之后摄像头检测帧率虽然还是受模型推理速度限制但界面操作是完全流畅的。如果你做视频检测也卡顿同样先检查是否把耗时操作放到了主线程。6.4 踩坑实录模型部署时的环境问题训练好模型之后为了演示方便我们把项目打包到了另一台没有深度学习环境的机器上。原本以为直接装一遍依赖就行结果在ultralytics版本不匹配的问题上栽了跟头。最好的做法是项目里放一个requirements.txt把顶层依赖锁好版本比如torch、ultralytics、opencv-python、PyQt5这些大项都写上。然后在目标机器上新建虚拟环境安装避免全局环境互相污染。如果你追求更稳妥的交付可以在同一版本环境下把整个环境用conda导出。版本问题听起来不大但真到了演示现场模型加载报错会非常耽误事。另外如果你在机器上没有GPU跑CPU推理也是可以的。YOLOv10的small模型在CPU上单张图片推理可能需要几百毫秒到一秒左右作为演示已经够用只是摄像头实时性会差一些。7. 从模型到应用的进一步扩展7.1 模型精度不够怎么办如果训练完之后验证集表现尚可但一到真实场景就拉胯大概率问题出在域差异上。所谓域差异说白了就是训练图片和你实际使用时的图片风格不一致。公开数据集里的图片很多是接近“证件照”风格的干净背景、均匀打光而你现场拍摄的照片有食堂灯光、桌面纹理、餐盘反光模型没见过这些样子自然容易出错。解决思路很朴素把实际使用场景里可能出现的图尽量多收集一些加进去重新训练。这个过程叫“增量训练”不是从零开始重新训练而是在已有权重的基础上用新数据继续训练命令几乎一样只需要在训练时指定新数据集路径即可。增量训练的优势是收敛快不容易破坏已有的识别能力是实际项目里提升精度的常用手段。7.2 模型导出与硬件加速如果你希望系统在部署时能跑得更快推荐把PyTorch权重导出为ONNX格式。导出的方法在ultralytics里非常简洁model.export(formatonnx, imgsz640)导出ONNX之后可以用ONNX Runtime来推理这样以后即使不装PyTorch也能跑模型部署起来的重量会轻很多。另外如果你的目标机器有支持加速的硬件比如某些边缘计算盒子或新显卡ONNX格式更容易接入对应的加速后端。需要注意的是导出和推理时的输入尺寸要保持一致。如果导出时是640推理时也要用640否则可能出现resize带来的精度损失或者某些算子报尺寸不匹配的错误。7.3 系统还能做什么扩展最后聊一下这个系统还可以往哪些方向扩展方便你自己有思路继续改营养估算检测出食物类别之后根据事先整理的“每100克热量表”在UI界面自动累加热量和三大营养素占比这是很多饮食管理类App的核心逻辑。批量检测与报表导出对一批食堂菜品图批量检测输出Excel表格统计每种菜出现的次数、平均置信度可用于食堂运营分析。实时视频流监控厨房档口摄像头画面接入系统实时统计每个菜品被取用的频次辅助备餐决策。模型轻量化把YOLOv10-small蒸馏到更小的模型或者用TensorRT做进一步的推理加速让系统能在嵌入式设备上离线运行。这些扩展方向没有一个需要推翻现有架构基本都是在现有检测结果的基础上加业务逻辑。所以当初做系统设计的时候把模型封装、UI逻辑、业务逻辑分开写这个决定在后面扩展的时候省了非常多事。8. 写在最后的一点经验这个项目从数据整理到最终界面交付前后折腾了大概两周真正花时间最多的不是代码而是数据。很多人一开始上手第一反应是跑模型拿到预训练权重随便找点图片就开始训发现效果不好就换模型继续重训。这个思路弯路走得最远。模型训练这项工作的上限基本由数据决定数据干净、标注准确、类别均衡哪怕模型版本旧一点效果都不会太差。另外一点体会就是一个深度学习的项目一定要把“能给别人看”当成验收标准。模型指标高是一回事但如果你能把模型接进一个界面让别人点两下鼠标就看到识别结果这个项目的完整度立刻就上了一个档次。答辩也好、作品展示也好这种完整系统的说服力远远大于贴一张训练曲线图。还有一个建议是刚开始做的时候类别不要贪多先控制在10到15个类别以内把整个流程跑顺再慢慢扩充。类别越多数据集处理、标注校验的难度翻倍对新手来说很容易因为数据问题而对项目失去耐心。先做出一个“麻雀虽小五脏俱全”的完整系统再迭代升级路径会顺畅得多。
阅读完成 · 觉得有帮助?