简介:图像分类是计算机视觉的基石任务,卷积神经网络(CNN)通过局部感受野与参数共享逐层抽象特征,从边缘纹理到高级语义。对于初学者而言,掌握数据增强与防过拟合技巧是提升模型泛化能力的关键,随机旋转、缩放、翻转等操作能扩大有效样本量,缓解训练集与验证集准确率差距。这一系列技术不仅适用于宠物识别,更可迁移至目标检测、语义分割等复杂场景。以猫狗分类项目为切入点,完整走通从数据集组织、CNN结构设计、训练监控到迁移学习微调的全流程,理解每一步背后的原理与工程实践要点,为后续真实业务应用打下扎实基础。 猫狗图像分类几乎是每个深度学习初学者都会撞上的项目,它不像MNIST手写数字那样让人觉得"太玩具了",也不像ImageNet大规模分类那样让个人电脑望而却步。这个题目选得相当聪明:数据量适中、任务明确、训练时间可接受,而且做完之后你能完整走一遍图像分类的标准流程。我见过不少朋友把代码跑通就以为自己会了,结果面试时被问卷积层怎么设计、数据增强为什么有效就卡壳。这篇就按我实际做这个项目的过程来写,从数据集准备到模型训练再到调参避坑,尽量把每一步的"为什么"也讲清楚。

1. 猫狗分类的底层价值:为什么这道题的含金量被低估了

很多人觉得猫狗分类太简单,二分类而已,准确率刷到95%以上好像也不是难事。但如果你换个角度看,这个项目其实浓缩了计算机视觉领域几乎所有核心问题:数据怎么组织、图片怎么预处理、模型怎么设计、过拟合怎么防、训练过程怎么监控。把这套流程跑熟了,后面做目标检测、语义分割、人脸识别,底层思路是共通的。

1.1 二分类问题的特殊地位

猫狗分类本质上是二分类,输出层只有一个神经元,用Sigmoid激活函数映射到0到1之间的概率。以猫为正类,概率大于0.5判为猫,小于0.5判为狗。这类任务在深度学习里有着天然的教学优势:网络结构不需要太复杂就能出效果,训练时间可以控制在合理范围内,调试起来也直观。

从数学角度看,二分类的交叉熵损失函数定义如下:

loss = -(y_true * tf.math.log(y_pred) + (1 - y_true) * tf.math.log(1 - y_pred))

当真实标签y_true为1时,损失函数变成 -log(y_pred) ,预测概率越接近1损失越小;当真实标签为0时,损失函数变成 -log(1-y_pred) ,预测概率越接近0损失越小。这个损失函数能同时惩罚"该置信的不置信"和"不该置信的乱置信",比均方误差更适合分类任务。

1.2 从猫狗分类能迁移到哪些场景

跑通这个项目,你掌握的几项核心能力可以直接迁移:

  • 数据目录组织方式:任何监督学习任务都要先把数据按类别目录整理好,这个习惯养成之后终身受用
  • 数据读取与预处理管道:用 ImageDataGenerator 做归一化、缩放、增强的这套逻辑,换成其他数据集只是改参数的事
  • 卷积网络的构建思路:特征提取层加全连接分类层,这个架构范式适用于绝大多数图像任务
  • 过拟合的识别与应对:训练集准确率远高于验证集准确率,这就是过拟合的报警信号,处理手段翻来覆去就是数据增强、Dropout、正则化那几招

我面试算法岗时被问得最多的反而不是什么高深理论,而是"你对过拟合怎么理解""你的模型为什么用这个结构",这些基本功都是在猫狗分类这种项目里打下来的。

2. 环境准备与框架选型:TensorFlow比PyTorch更适合入门

动手写代码之前,先把环境搭好。很多初学者卡在环境这一步,实际上环境配置是有标准流程的,跟着踩过坑的人走能省下大量时间。

2.1 版本选型和安装细节

我推荐你使用TensorFlow 2.x加Keras接口,原因很简单:API设计更简洁,数据管道和训练流程集成度高,一个 model.fit() 就能跑完整训练。PyTorch当然也很好,但它的调试灵活性和自由度对新手来说反而容易迷茫。

Python版本建议3.8到3.10,太新的版本可能遇到某些依赖库没跟上导致安装失败。创建虚拟环境是个好习惯,避免把系统Python搞乱:

conda create -n catdog python=3.9
conda activate catdog
pip install tensorflow==2.13.0

如果电脑有NVIDIA显卡,装GPU版本的TensorFlow能大幅压缩训练时间。装之前先确认CUDA和cuDNN版本匹配,TensorFlow官方文档有详细的对应关系表。没有GPU也没关系,CPU跑这个项目完全可行,只是时间会拉长到GPU版的五到十倍。我最初就是在CPU上跑的,一个epoch大概十分钟,照样把项目做完了。

2.2 数据文件夹的组织方式

训练之前,先把数据集目录结构规划好。标准做法是:

dataset/
├── train/
│   ├── cat/    # 放猫的图片
│   └── dog/    # 放狗的图片
├── val/
│   ├── cat/
│   └── dog/
└── test/
    ├── cat/
    └── dog/

训练集、验证集、测试集三者互相独立是铁律。训练集用来更新模型参数,验证集用来监控训练状态和调参,测试集只在最终评估时使用。如果验证集和训练集数据有重叠,你的验证结果就失真了,这叫数据泄漏,是新手最容易犯的错误。

3. 数据集:从Kaggle原始数据到专属训练管道

标题里把数据集单列出来是有道理的。猫狗分类最经典的数据集是Kaggle平台的Dogs vs Cats,包含25000张训练图片和12500张测试图片,猫狗各半,JPEG格式,尺寸不统一。下载后手动把它拆分成训练集和验证集。

3.1 数据集的拆分与统计

Kaggle下载的原始压缩包解压后,文件名类似 cat.0.jpg 、 dog.0.jpg 这种格式。写个小脚本按文件名前缀拆分到不同目录:

import os
import shutil
from sklearn.model_selection import train_test_split

source_dir = 'raw_data'
train_dir = 'dataset/train'
val_dir = 'dataset/val'

cats = [f'cat.{i}.jpg' for i in range(12500)]
dogs = [f'dog.{i}.jpg' for i in range(12500)]

cat_train, cat_val = train_test_split(cats, test_size=0.2, random_state=42)
dog_train, dog_val = train_test_split(dogs, test_size=0.2, random_state=42)

for fname in cat_train + dog_train:
    cls = fname.split('.')[0]
    src = os.path.join(source_dir, fname)
    dst = os.path.join(train_dir, cls, fname)
    shutil.copy(src, dst)

for fname in cat_val + dog_val:
    cls = fname.split('.')[0]
    src = os.path.join(source_dir, fname)
    dst = os.path.join(val_dir, cls, fname)
    shutil.copy(src, dst)

test_size=0.2 意味着把20%的样本留作验证集,也就是20000张训练、5000张验证。 random_state=42 固定随机种子,这样每次拆分结果一致,保证实验可复现。

拆完之后务必检查每个目录下的图片数量是否匹配预期,别偷懒跳过这一步。项目写到一半发现数据目录缺文件,排查起来非常头疼。

3.2 Keras数据预处理管道的完整写法

图片数据处理有两个关键操作:统一尺寸和归一化。原始图片尺寸参差不齐,而卷积神经网络的输入层通常要求固定尺寸。常见做法是统一缩放到150x150像素,这个尺寸在信息量和计算量之间取得了不错的平衡。再大一点比如224x224,效果可能会好一些,但训练时间会明显增加。

归一化的目的是把像素值从0到255映射到0到1区间。神经网络对数值范围敏感,绝对值大的输入会导致梯度更新不稳定,收敛过程缓慢甚至震荡。

Keras的 ImageDataGenerator 可以一步完成这两件事:

from tensorflow.keras.preprocessing.image import ImageDataGenerator

train_datagen = ImageDataGenerator(rescale=1./255)
val_datagen = ImageDataGenerator(rescale=1./255)

train_generator = train_datagen.flow_from_directory(
    'dataset/train',
    target_size=(150, 150),
    batch_size=32,
    class_mode='binary'
)

val_generator = val_datagen.flow_from_directory(
    'dataset/val',
    target_size=(150, 150),
    batch_size=32,
    class_mode='binary'
)

flow_from_directory 会根据子目录名自动生成标签, class_mode='binary' 对应二分类。跑完这两行代码,打印一下 train_generator.class_indices ,确认类别索引正确。

3.3 数据增强机制的原理与参数配置

数据增强是防过拟合最有效的手段之一。它通过随机变换图像来生成"新"样本,每次epoch看到的数据都不完全一样,等于在不增加真实数据的情况下扩大训练集规模。这背后的逻辑很朴素:旋转、翻转、平移后的猫仍然是猫,这些变化不会改变类别标签,但能让模型学会对这些扰动不敏感,从而更好地泛化。

我实际使用的增强参数如下:

train_datagen = ImageDataGenerator(
    rescale=1./255,
    rotation_range=40,
    width_shift_range=0.2,
    height_shift_range=0.2,
    shear_range=0.2,
    zoom_range=0.2,
    horizontal_flip=True,
    fill_mode='nearest'
)

逐项解释:

  • rotation_range=40 :随机旋转角度在-40到40度之间
  • width_shift_range=0.2 、 height_shift_range=0.2 :水平垂直方向最多平移20%宽度
  • shear_range=0.2 :错切变换强度,可以理解为把图片从矩形斜拉成平行四边形
  • zoom_range=0.2 :随机缩放20%范围
  • horizontal_flip=True :随机水平翻转
  • fill_mode='nearest' :填充新增空白区域的策略,用最近邻像素填充

这些参数不是越大越好,增强幅度过大会让数据分布偏离原始分布,反而影响收敛。我试过把旋转角度加到180度,结果猫狗图片上下颠倒,模型明显学得吃力。建议就用以上参数,效果稳定。

4. 搭建CNN模型:三层卷积加两层全连接的结构拆解

很多入门教程直接甩一段模型代码让你复制,不解释每层的作用,这是不对的。理解CNN各层的工作原理,比背住代码重要得多。

4.1 卷积层到底在做什么

卷积层的核心思想是局部感受野和参数共享。人可以想象用一个小的窗口在图像上滑动,每滑到一个位置就计算窗口内像素与一组权重(称为卷积核)的点积,生成一个特征值,所有位置加起来就是一张特征图。一个卷积核负责提取一种特征,有的关注边缘,有的关注纹理,有的关注颜色。

最开始几层提取的是低级特征(边缘、角点),越往深层的卷积层提取的特征越抽象(眼睛、耳朵、整体轮廓),这就是卷积神经网络逐层抽象的基本逻辑。

我用一个具体例子帮你理解:3x3卷积核在一张150x150的图片上滑动,输出尺寸会变成148x148,因为边缘部分卷积核越界了。如果不希望尺寸缩小,需要填充。不过我们的模型不关心这个问题,因为池化层会接管尺寸缩减。

4.2 本项目模型结构与其设计逻辑

下面是我在这个项目中使用的模型结构:

from tensorflow.keras import layers, models

model = models.Sequential([
    layers.Conv2D(32, (3, 3), activation='relu', input_shape=(150, 150, 3)),
    layers.MaxPooling2D(2, 2),
    layers.Conv2D(64, (3, 3), activation='relu'),
    layers.MaxPooling2D(2, 2),
    layers.Conv2D(128, (3, 3), activation='relu'),
    layers.MaxPooling2D(2, 2),
    layers.Conv2D(128, (3, 3), activation='relu'),
    layers.MaxPooling2D(2, 2),
    layers.Flatten(),
    layers.Dropout(0.5),
    layers.Dense(512, activation='relu'),
    layers.Dense(1, activation='sigmoid')
])

model.compile(
    loss='binary_crossentropy',
    optimizer='adam',
    metrics=['accuracy']
)

设计思路解释:

  • 滤波器数量从32递增到64再到128,这是卷积网络常见的设计模式:空间尺寸不断缩小,通道数不断增加,保证信息总量不至于衰减太快
  • 每层卷积后紧跟最大池化层,池化窗口2x2,步长2,把特征图尺寸减半。最大池化不仅压缩计算量,还提供一定平移不变性:物体在图片里稍微移动一点,池化输出的差异不会太大
  • 全连接层用512个神经元,再接Dropout层以0.5的概率随机丢弃神经元连接。Dropout是防止过拟合的强力武器,它强迫网络不能过度依赖某几个特定神经元,从而提高鲁棒性
  • 最后一个输出层只有一个神经元加Sigmoid激活,输出值代表"是猫"的概率

ReLU激活函数的选用是猫狗分类项目里的标准答案。它计算量极低、梯度消失问题轻,比Sigmoid和Tanh更适合深度网络。每层卷积后面都接ReLU,而最后一层用Sigmoid是因为我们需要一个明确的概率输出,这个设计模式值得记住。

4.3 感受野与参数量的粗略估算

模型参数量可以通过简单计算得出。第一层卷积层输入是150x150x3,卷积核32个、每个3x3x3,加上偏置项,参数量为:

32 * 3 * 3 * 3 + 32 = 896

第二层卷积层输入通道是32,卷积核64个、每个3x3x32,参数量为:

64 * 3 * 3 * 32 + 64 = 18496

到全连接层之前,经过四轮卷积池化,特征图尺寸从150降到9(150/2/2/2/2约等于9.375截断为9),通道数128,所以Flatten后每个样本的特征向量长度为:

9 * 9 * 128 = 10368

这些特征送到第一个全连接层Dense(512),这一层的参数量为:

10368 * 512 + 512 = 5308928

全连接层占了整个模型参数的大头,这也是Dropout放在全连接层之前效果最明显的原因。整个模型总参数量大约560万,对现代硬件来说算轻量级。

5. 训练全流程与实测记录:从过拟合到收敛

模型搭好之后进入训练阶段。训练结果和参数选择密切相关,我给出一套经过实测的配置,并解释每个决策的依据。

5.1 训练参数的合理设定

批大小设为32,这是内存和训练稳定性的折中。批太大梯度方向更准确但它也意味着每个epoch更新次数少且显存压力大;批太小梯度震荡明显、收敛缓慢。Epoch设为100,但通常配合早停策略使用——验证准确率连续若干轮不提升就提前结束训练,节省时间。

学习率保持Adam优化器的默认值0.001,这是最稳妥的选择。学习率过大导致损失震荡、无法收敛;学习率过小导致训练极其缓慢。如果你的训练曲线是锯齿状上下剧跳,第一反应应该看学习率。

训练代码:

from tensorflow.keras.callbacks import EarlyStopping

early_stop = EarlyStopping(
    monitor='val_loss',
    patience=5,
    restore_best_weights=True
)

history = model.fit(
    train_generator,
    steps_per_epoch=20000 // 32,
    epochs=100,
    validation_data=val_generator,
    validation_steps=5000 // 32,
    callbacks=[early_stop]
)

steps_per_epoch 等于训练集样本数除以批大小,表示一个epoch内需要迭代多少次才能遍历全部数据。20000张图片除以32等于625步,完全遍历一次训练集才算一个epoch。 patience=5 意味着连续5个epoch验证损失都没有改善就停止训练, restore_best_weights=True 让模型恢复到验证损失最低的权重,这个设置很关键。

5.2 训练过程中的过拟合信号识别

第一轮实验如果用较弱的增强甚至不用增强,你会看到非常典型的过拟合曲线:

  • 训练准确率从第5个epoch开始就冲上0.95,第10个epoch接近1.0
  • 验证准确率停留在0.85附近,纹丝不动甚至略有下降
  • 训练损失持续下降,验证损失先降后升

验证准确率到0.85不再上升,是因为模型开始死记硬背训练集中的具体图片,完全没有学会类比推理。要处理这个问题,我按顺序做了三步调整:

第一步,增强数据增强强度。把 rotation_range 从20提到40,加上水平翻转和缩放,让模型看到更多样化的输入。

第二步,提高Dropout比率。从0.5提升到0.6,更强力地破坏神经元之间的协同适应。

第三步,增加一个 BatchNormalization 层。批归一化层可以稳定层输入的分布,加速收敛并起到轻微正则化作用。

这三步组合下来,验证准确率稳步提升到0.90以上,最佳结果达到0.92左右。

5.3 训练曲线的临界点观察

完整训练大概25到30个epoch触发早停,CPU环境总耗时一小时左右,GPU环境只需几分钟。我记录的关键节点如下:

  • 第1个epoch结束时,训练准确率约0.62,验证准确率约0.61,模型基本是在瞎猜
  • 第5个epoch左右,训练准确率突破0.85,验证准确率约0.80,开始有泛化能力
  • 第10到15个epoch,验证准确率从0.85缓慢爬升到0.89,增长放缓
  • 第20到28个epoch,验证准确率在0.91附近波动,最终早停触发

观察loss曲线时注意一个规律:如果训练损失在下但验证损失已经止跌或回升,说明模型开始过拟合,该停了。这也是为什么 EarlyStopping 监控的是 val_loss 而不是 val_accuracy ,损失值对过拟合更敏感、判别更稳定。

6. 踩坑实记:我在猫狗分类项目里犯过的错

这一部分是我最想分享的内容。很多人看教程只看到顺利的部分,实际动手时遇到的各种报错和诡异现象能把人逼疯。下面按我真实踩坑的时间线来写。

6.1 图片路径里的中文和空格

Windows环境下的一个经典问题:我一开始把数据集放在 D:\我的项目\猫狗分类\dataset\ 下面,运行 flow_from_directory 时直接报错,错误信息指向文件编码问题。原因是Windows文件系统默认编码和Python字符串处理不一致,中文路径在底层调用时会出现编码冲突。

解决方法有两个:一是路径全部改用英文命名,这是最省事的办法;如果真的必须在中文路径上做,需要设置文件系统编码。我强烈建议你所有深度学习项目的目录和文件名都坚持用英文,否则后患无穷。

6.2 图像通道顺序的微妙差异

另一个隐蔽问题涉及图像通道顺序。OpenCV读取图片默认是BGR顺序而不是RGB,如果你先用 cv2.imread 读图,再手动喂给模型预测,颜色通道就反了。猫狗分类对颜色顺序不太敏感,准确率影响不超过1%,但在处理交通标志、医疗图像这类任务时,通道顺序错误会严重影响结果。

如果你在网上下载的模型权重是在ImageNet上预训练的,训练时用的几乎都是RGB顺序。所以用 matplotlib.image.imread 或 tensorflow.keras.preprocessing.image.load_img 读图比较安全,它们默认遵循RGB顺序。

6.3 显存不足导致训练中断

batch_size设为32有时也会触发显存不足错误,特别是在老显卡上。程序直接抛出 ResourceExhaustedError ,然后整个训练进程崩溃。这个问题不算难解决,降低批大小是第一选择:从32降到16,显存占用直接减半。你只需要把 flow_from_directory 里的 batch_size 改掉,其他代码不用动,代价是每个epoch的训练步数增加,耗时略长。

如果降低批大小仍不够,只能换用更轻量的模型结构,比如把卷积核数量从128降到64。不过以我们这个轻量网络加150x150的输入尺寸,大多数显卡都够用。

6.4 训练结果完美的幻觉:验证集分布太简单

有过一次让我印象很深的失败:在某个案例中验证准确率高达0.98,我感觉这个模型强得离谱,结果拿到真实拍摄的图片一预测,效果惨不忍睹。排查下来发现,验证集是直接从原训练文件夹随机划分的,和训练集来自同一个数据源,同场景、同光照条件、同拍摄设备,模型等于在开卷考试。

正确的验证集应该尽量模拟真实应用场景:用不同来源、不同时间拍摄的照片做验证。如果条件有限,至少保证验证集划分采用完全随机的方式,并增加验证图片的多样性。

6.5 全连接层特征向量形状不匹配

加入 BatchNormalization 或调整卷积层参数后,可能报一堆维度不匹配的错误。出错信息类似于 Negative dimension size caused by subtracting 2 from 1 ,这种报错几乎都指向特征图在逐层传递时尺寸计算不对。排查思路很简单:从第一层卷积开始,逐层手动计算特征图尺寸,结合padding和stride的取值就能定位哪一层出了问题。

7. 从0.92到0.97:迁移学习带来的质变

手写CNN模型做到0.92准确率,已经是一个合格的项目了。但如果继续想提高精度,手写网络的天花板很明显。此时可以引入迁移学习的思路。

7.1 预训练模型为什么比从头训练强

ImageNet预训练的模型已经学会了大量通用视觉特征,包括边缘、纹理、形状等。这些特征在猫狗分类上也同样有效。我们不必重新学一遍这些基本特征,只需要在预训练模型的基础上,微调最后几层来适配猫狗任务。

我用 VGG16 做过对比实验,效果提升非常明显:

from tensorflow.keras.applications import VGG16

base_model = VGG16(
    weights='imagenet',
    include_top=False,
    input_shape=(150, 150, 3)
)

base_model.trainable = False

model = models.Sequential([
    base_model,
    layers.Flatten(),
    layers.Dense(256, activation='relu'),
    layers.Dropout(0.5),
    layers.Dense(1, activation='sigmoid')
])

include_top=False 表示不加载VGG16原本的分类头,只保留特征提取部分。 base_model.trainable = False 冻结卷积基,训练时只更新新增的全连接层参数。这样参数量比从头训练小得多,训练速度快,而且效果更好。

实测在相同数据、相同训练配置下,迁移学习模型验证准确率可以稳定达到0.97以上。

7.2 微调注意事项与结果分析

如果还想进一步榨干模型潜力,可以解冻预训练模型的最后几层,使用更小的学习率(比如0.0001)进行微调。注意解冻范围不要太大,通常只解冻最后两三层,否则容易灾难性遗忘,把已经学好的特征破坏掉。

还有一个关键细节:迁移学习输入预处理必须与预训练模型要求一致。VGG16要求把像素值按照特定均值方差做标准化,实现方式是:

from tensorflow.keras.applications.vgg16 import preprocess_input

直接用 preprocess_input 函数处理图像,不要手动做 rescale=1./255 。Manually normalizing in a different way affects the model's input distribution and degrades accuracy significantly. Keras类目录里自带这个函数,没必要自己写。

7.3 模型导出与推理部署

模型训练完成后,保存与加载是一个很容易被忽略的环节。Keras模型可以通过一行代码保存:

model.save('cat_dog_classifier.h5')

推理时加载模型,对单张图片进行预测:

from tensorflow.keras.models import load_model
from tensorflow.keras.preprocessing import image
import numpy as np

model = load_model('cat_dog_classifier.h5')

img = image.load_img('test_pic.jpg', target_size=(150, 150))
img_array = image.img_to_array(img)
img_array = np.expand_dims(img_array, axis=0)
img_array = img_array / 255.0

prediction = model.predict(img_array)[0][0]
print('是猫的概率:', prediction)

np.expand_dims 增加batch维度很关键,模型内部假设输入的形状是(None, 150, 150, 3),单张图片必须变成(1, 150, 150, 3)才能通过。这一点初学者常常忘记。

8. 项目扩展方向:从猫狗分类走向真实业务场景

把基础项目做完之后,下一步去哪。顺着猫狗分类的逻辑延伸,其实可以走向多个真实应用方向。

8.1 多分类扩展的坑

如果你把猫狗二分类扩展到猫狗鸟三分类,只需要把输出层改为三个神经元加Softmax激活,损失函数换成类别交叉熵。这里有一个新手经常踩的坑:数据目录里如果混入了损坏的图片文件,训练过程不会立刻报错,但会在某个epoch迭代到该文件时中断。建议训练前先做一轮图片完整性检查,过滤掉无法解码的文件:

from PIL import Image
import os

def filter_broken_images(root_dir):
    for root, dirs, files in os.walk(root_dir):
        for fname in files:
            filepath = os.path.join(root, fname)
            try:
                img = Image.open(filepath)
                img.verify()
            except Exception:
                os.remove(filepath)

8.2 从分类到目标检测的能力迁移

猫狗分类任务学会的是"这张图里有猫还是狗",但真实场景往往更复杂:一张图里既有猫又有狗,还需要知道它们各自在什么位置。这就从图像分类升级到了目标检测。

很多搜索热词指向YOLO训练自己的数据集,这是一个很自然的进阶方向。目标检测的数据集准备比分类麻烦得多,需要标注每个目标的边界框和类别。好在你在猫狗分类中学到的数据组织、预处理、训练监控、过拟合处理经验,在目标检测任务里同样适用,只是模型结构和标注格式不同。

8.3 把模型封装成可用的小工具

模型训练完了,可以把它做成一个实用的小工具。用Flask或FastAPI包一个HTTP接口,上传一张图片就能返回类别和置信度。部署方面的坑还是那几个老套路:离线测试没问题,线上却出错的话,先检查图片读取方式、预处理是否和训练时一致。大部分"线上效果不如线下好"的案例,最后查下来都是预处理管道没对齐。


最后说点实在的。这个项目我前后做过三遍:第一遍纯照着教程跑通代码,第二遍是自己写数据管道和模型,第三遍是拿到面试现场跟面试官拆解每一个设计决策。每一遍的收获层次完全不同。如果你的目标是真正掌握深度学习图像分类的基本功,建议别只满足于跑通教程,想清楚数据为什么这么组织、模型为什么这么搭、过拟合为什么这么防,把这些"为什么"都能说得明白,这个项目的价值才算真正吃透了。

本文还有配套的精品资源,点击获取 menu-r.4af5f7ec.gif

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐