图像识别零基础实战入门 3 第一次训练图像分类模型

3.1 模型如何对照片分类

有两个装满照片的文件夹:一个叫 cat ,一个叫 dog

我们希望模型学会一件事:看到一张新照片时,能判断它属于哪一个文件夹。

模型面对猫和狗,并不会"看见毛、耳朵或眼睛",它只能处理数字。

因此,希望模型进行多次练习后,可以把"猜测"变成"更接近正确的判断"。


3.2 准备好图片

你首先做的是最朴素的一件事:把图片按类别放好。

复制代码
dataset/
  train/
    cat/
    dog/
  val/
    cat/
    dog/

在这个结构里,"答案"已经写在了图片所在的文件夹里:

  • train/cat/ 的图片,正确答案就是 cat
  • train/dog/ 的图片,正确答案就是 dog

程序会把 cat、dog 变成数字标签,通常是:

  • cat = 0
  • dog = 1

这一步非常重要,因为它意味着:
训练不是凭空进行的,模型每次练习都有"标准答案"。


3.3 让图片变成模型能读的数字

模型看不懂图片文件,模型需要的是"数字形式的图片"。

因此我们做了两条最简单的统一规则:

  1. 每张图片统一尺寸(否则无法成批处理)
  2. 把图片变成张量(Tensor),让模型可以做数学计算
python 复制代码
from torchvision import transforms

transform = transforms.Compose([
    transforms.Resize((224, 224)),
    transforms.ToTensor()
])

模型并不是"看图片文件",而是"看图片转换后的数字张量"。


3.4 使用DataLoader 准备训练数据

我们把训练数据切成一份份,每份包含 8 张图片,这就是 batch。

  • batch:一次练习的题量(例如 8 张)
  • epoch:把训练集完整练完一遍
python 复制代码
from torchvision import datasets
from torch.utils.data import DataLoader

train_dataset = datasets.ImageFolder("dataset/train", transform=transform)
val_dataset   = datasets.ImageFolder("dataset/val",   transform=transform)

train_loader = DataLoader(train_dataset, batch_size=8, shuffle=True,  num_workers=0)
val_loader   = DataLoader(val_dataset,   batch_size=8, shuffle=False, num_workers=0)

print("classes:", train_dataset.classes)  # ['cat', 'dog']

可以把训练过程想象成:

模型不断拿到一份份练习卷,一份份地做题和订正。


~~## 3.5 模型不是在对图片分类,而是在"打分"

模型第一次对图片分类时:

  • 模型可能把所有图片都预测成 dog
  • 或者预测得完全没有规律
    这并不是程序错误,而是因为:
    模型输出的不是"答案",而是"评分"。
    它会为每张图片给出两个数:
  • 更像 cat 的评分
  • 更像 dog 的评分
    哪个评分大,就选哪个类别。
    这就是所谓的 logits:一组用于做出选择的内部评分。

3.6 loss 可以评估模型"错得有多严重"

只告诉模型"你错了"是不够的。

你需要一个更精确的反馈:这次到底错得有多严重。

loss 做的就是这件事:

  • loss 大:错得离谱
  • loss 小 :更接近正确
    训练的目标不是"立刻全对",而是让 loss 随着练习逐步下降。

3.7 模型进行修正学习

在整套训练流程里,真正让模型发生变化的,不是它答题,也不是你批改,而是它订正的过程。

订正集中体现在三行关键代码:

python 复制代码
optimizer.zero_grad()
loss.backward()
optimizer.step()

你可以把它理解为:

  1. 清空上一轮的订正痕迹
  2. 根据这次错题,算出应该怎么改
  3. 把模型参数调整一点点
    这就是模型的"学习"。

3.8 遍历 DataLoader 进行完整训练

如果你只拿同一份练习卷反复训练,模型很快会把那几张图背下来,loss 会迅速接近 0。

但这并不说明它会分辨猫狗,只说明它记住了少数图片。

因此真正的训练必须做到:

在一个 epoch 内,把训练集里的所有 batch 都练一遍。


3.9 全流程代码(猫狗分类:训练 + 验证)

python 复制代码
import torch
import torch.nn as nn
from torch.utils.data import DataLoader
from torchvision import datasets, transforms, models

def main():
    device = "cuda" if torch.cuda.is_available() else "cpu"
    print("device:", device)

    transform = transforms.Compose([
        transforms.Resize((224, 224)),
        transforms.ToTensor()
    ])

    train_dataset = datasets.ImageFolder("dataset/train", transform=transform)
    val_dataset   = datasets.ImageFolder("dataset/val",   transform=transform)

    print("classes:", train_dataset.classes)  # ['cat', 'dog']

    train_loader = DataLoader(train_dataset, batch_size=8, shuffle=True,  num_workers=0)
    val_loader   = DataLoader(val_dataset,   batch_size=8, shuffle=False, num_workers=0)

    model = models.resnet18(pretrained=True)
    model.fc = nn.Linear(model.fc.in_features, 2)
    model = model.to(device)

    criterion = nn.CrossEntropyLoss()
    optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

    num_epochs = 5
    for epoch in range(1, num_epochs + 1):
        # 训练:做完所有"练习卷"
        model.train()
        train_loss_sum, train_count = 0.0, 0

        for images, labels in train_loader:
            images, labels = images.to(device), labels.to(device)

            outputs = model(images)
            loss = criterion(outputs, labels)

            optimizer.zero_grad()
            loss.backward()
            optimizer.step()

            bs = labels.size(0)
            train_loss_sum += loss.item() * bs
            train_count += bs

        train_avg_loss = train_loss_sum / max(train_count, 1)

        # 验证:随堂测验(只观察,不订正)
        model.eval()
        val_loss_sum, val_correct, val_count = 0.0, 0, 0

        with torch.no_grad():
            for images, labels in val_loader:
                images, labels = images.to(device), labels.to(device)

                outputs = model(images)
                loss = criterion(outputs, labels)

                _, predicted = torch.max(outputs, 1)
                val_correct += (predicted == labels).sum().item()

                bs = labels.size(0)
                val_loss_sum += loss.item() * bs
                val_count += bs

        val_avg_loss = val_loss_sum / max(val_count, 1)
        val_acc = val_correct / max(val_count, 1)

        print(f"Epoch {epoch}/{num_epochs} | "
              f"train_loss={train_avg_loss:.4f} | "
              f"val_loss={val_avg_loss:.4f} | "
              f"val_acc={val_acc:.2%}")

if __name__ == "__main__":
    main()

3.10 总结

这一章的猫狗图片分类的主要流程:

  1. 你先把猫狗图片按文件夹分好,答案就写在文件夹里
  2. 你把图片转成统一大小的数字,模型才能计算
  3. 你用 DataLoader 把题库切成一份份练习卷(batch)
  4. 模型每次先给评分,再选答案
  5. loss 像老师的批改分数,告诉它错得多严重
  6. backward + step 是订正动作,让模型一点点改变
  7. 一个 epoch 必须把训练集练完一遍,才能算"上完一节课"
  8. 验证集是随堂测验,用来观察它是否真的学会,而不是背题
相关推荐
zhangdawei8382 小时前
英伟达GB200,GB300和普通服务器如dell R740xd有什么区别?
运维·服务器·人工智能
Mintopia2 小时前
意图OS是未来软件形态,它到底解决了什么问题?
人工智能·react native·前端工程化
Mintopia2 小时前
🤖 AI 决策 + 意图OS:未来软件形态的灵魂共舞
前端·人工智能·react native
万行2 小时前
机器学习&第一章
人工智能·python·机器学习·flask·计算机组成原理
实战项目2 小时前
基于PyTorch的卷积神经网络花卉识别系统
人工智能·pytorch·cnn
shangjian0072 小时前
AI大模型-机器学习-算法-线性回归
人工智能·算法·机器学习
zuozewei2 小时前
零基础 | 一文速通 AI 大模型常见术语
人工智能
说私域2 小时前
云零售时代的S2B模式重构:AI智能名片与链动2+1模式的赋能路径
人工智能·重构·零售
划水的code搬运工小李2 小时前
EVO评估数据导出在origin中绘制
人工智能