图像识别零基础实战入门 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. 验证集是随堂测验,用来观察它是否真的学会,而不是背题
相关推荐
AngelPP3 小时前
OpenClaw 架构深度解析:如何把 AI 助手搬到你的个人设备上
人工智能
宅小年3 小时前
Claude Code 换成了Kimi K2.5后,我再也回不去了
人工智能·ai编程·claude
九狼3 小时前
Flutter URL Scheme 跨平台跳转
人工智能·flutter·github
ZFSS3 小时前
Kimi Chat Completion API 申请及使用
前端·人工智能
天翼云开发者社区5 小时前
春节复工福利就位!天翼云息壤2500万Tokens免费送,全品类大模型一键畅玩!
人工智能·算力服务·息壤
知识浅谈5 小时前
教你如何用 Gemini 将课本图片一键转为精美 PPT
人工智能
Ray Liang5 小时前
被低估的量化版模型,小身材也能干大事
人工智能·ai·ai助手·mindx
shengjk17 小时前
NanoClaw 深度剖析:一个"AI 原生"架构的个人助手是如何运转的?
人工智能
西门老铁8 小时前
🦞OpenClaw 让 MacMini 脱销了,而我拿出了6年陈的安卓机
人工智能