Yi-Coder-1.5B机器学习入门:CNN图像分类实战

📅 发布时间:2026/7/9 10:09:32 👁️ 浏览次数:
Yi-Coder-1.5B机器学习入门:CNN图像分类实战
Yi-Coder-1.5B机器学习入门CNN图像分类实战1. 这不是你想象中的CNN教程看到标题里的“Yi-Coder-1.5B”和“CNN图像分类”你可能会下意识地皱眉——这到底是讲代码大模型还是讲图像识别两者怎么扯上关系的其实这里有个常见的误解。Yi-Coder-1.5B是一款专注于代码理解和生成的开源语言模型它不处理图片也不做图像分类。它的强项是读懂你的编程意图、补全函数、解释报错信息、把自然语言需求转成可运行代码——比如当你想实现一个CNN图像分类器时它能帮你写清数据加载、模型搭建、训练循环的每一步。而CNN卷积神经网络是图像分类任务的经典架构需要你用PyTorch或TensorFlow动手搭建和训练。Yi-Coder-1.5B不会替你跑GPU但它可以成为你写代码时最懂你的“编程搭档”。这篇教程不绕弯子不堆概念。我们会用最直白的方式带你从零完成一个能实际运行的CNN图像分类项目用PyTorch写模型、用CIFAR-10数据集训练、用Yi-Coder-1.5B辅助解决编码卡点。整个过程不需要你提前掌握深度学习公式只要你会写基础Python就能跟下来。你不需要记住“卷积核”“池化层”这些词只需要知道它们在代码里对应哪几行以及为什么这么写。就像学骑自行车重点不是背力学原理而是先坐上去、蹬起来、保持平衡。2. 准备工作环境、数据与工具2.1 本地开发环境搭建我们用最轻量、最通用的方式Python PyTorch Jupyter Notebook。不需要Docker不依赖云平台笔记本电脑就能跑通。首先确认你有Python 3.9或更高版本python --version # 输出类似Python 3.10.12如果没有去python.org下载安装即可。接着安装核心库pip install torch torchvision matplotlib numpy tqdmtorchPyTorch深度学习框架torchvision提供常用数据集如CIFAR-10和预训练模型matplotlib画训练曲线、查看图片numpy数值计算基础tqdm给训练过程加进度条看着不焦虑小提示如果你用的是Mac M系列芯片推荐安装torch的Metal版本能显著提速。执行pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/macos-arm64即可。2.2 数据集CIFAR-10——图像分类的“Hello World”我们不用自己收集图片直接用学术界公认的入门数据集CIFAR-10。它包含6万张32×32像素的彩色图片分为10类飞机、汽车、鸟、猫、鹿、狗、青蛙、马、船、卡车。每类6000张其中5万张用于训练1万张用于测试。尺寸小、类别少、加载快特别适合新手第一次跑通全流程。在代码里它一行就能自动下载并加载import torchvision.datasets as datasets import torchvision.transforms as transforms # 定义图片预处理转为Tensor 归一化 transform transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)) ]) # 加载训练集和测试集 train_dataset datasets.CIFAR10(root./data, trainTrue, downloadTrue, transformtransform) test_dataset datasets.CIFAR10(root./data, trainFalse, downloadTrue, transformtransform) print(f训练集大小{len(train_dataset)}) print(f测试集大小{len(test_dataset)}) # 输出训练集大小50000测试集大小10000这段代码会自动检查./data目录下有没有数据。没有就联网下载首次运行需几分钟有就直接读取。transform的作用是把PIL图片转成PyTorch能处理的张量并做标准化——这是训练稳定的关键一步但你不用深究数字怎么来的照着抄就行。2.3 让Yi-Coder-1.5B成为你的实时编程助手现在来请出本教程的“隐形主角”Yi-Coder-1.5B。它不是用来训练模型的而是当你写代码遇到问题时给你精准、可执行的建议。最简单的方式是用Ollama本地运行无需GPU# 1. 下载并安装Ollama官网 ollama.com/download # 2. 在终端运行以下命令下载模型 ollama run yi-coder:1.5b你会看到一个交互式界面输入类似这样的问题它会立刻返回完整代码帮我写一个PyTorch的CNN模型输入是32x32的RGB图片输出是10个类别的概率要求包含两个卷积块每个含卷积ReLU池化然后接全连接层。它给出的代码可以直接复制进你的Notebook稍作调整就能运行。比起在Stack Overflow大海捞针或者反复查文档这种方式快得多、准得多。实用技巧在Jupyter里你可以把Yi-Coder的回复粘贴到一个新cell加上# TODO: 理解这段代码注释然后逐行运行、打印中间变量形状比如print(x.shape)亲眼看到数据流怎么穿过每一层。这才是真正学会的开始。3. 动手搭建从零实现CNN图像分类器3.1 模型架构三层结构清晰易懂我们不追求SOTA当前最优而是用一个足够简单、足够典型的CNN结构让你一眼看懂数据怎么流动特征提取层两个“卷积→激活→池化”组合负责从像素中抓取边缘、纹理等局部特征特征整合层把二维特征图拉平成一维向量准备交给分类器分类层一个全连接层把特征向量映射到10个类别的得分logits用PyTorch实现不到30行import torch import torch.nn as nn class SimpleCNN(nn.Module): def __init__(self, num_classes10): super().__init__() # 第一个卷积块32x32 → 16x16 self.conv1 nn.Conv2d(3, 32, kernel_size3, padding1) # 输入3通道输出32通道 self.relu1 nn.ReLU() self.pool1 nn.MaxPool2d(2) # 2倍下采样 # 第二个卷积块16x16 → 8x8 self.conv2 nn.Conv2d(32, 64, kernel_size3, padding1) # 输入32通道输出64通道 self.relu2 nn.ReLU() self.pool2 nn.MaxPool2d(2) # 再次2倍下采样 # 分类层把8x8x64的特征图展平接全连接 self.flatten nn.Flatten() self.fc nn.Linear(64 * 8 * 8, num_classes) # 64*8*8 4096 def forward(self, x): # 第一个块 x self.pool1(self.relu1(self.conv1(x))) # 第二个块 x self.pool2(self.relu2(self.conv2(x))) # 展平 分类 x self.flatten(x) x self.fc(x) return x # 创建模型实例 model SimpleCNN() print(model)运行后你会看到模型的完整结构。注意几个关键数字Conv2d(3, 32, ...)从3通道RGB输入生成32个特征图MaxPool2d(2)每次把宽高减半所以32→16→8Linear(4096, 10)最后把8×8×644096维特征压缩成10个类别的分数这个结构不是凭空设计的。你可以问Yi-Coder“为什么第一个卷积输出32通道第二个是64”它会告诉你通道数通常逐层翻倍因为越深层的特征越抽象需要更多维度来表达。3.2 数据加载让图片乖乖排队模型有了数据也下载好了下一步是把图片“喂”给模型。我们需要两个东西DataLoader把数据集打包成一个个“批次”batch比如每次送32张图进来而不是一张张送太慢数据增强可选但推荐在训练时对图片做轻微变换如随机水平翻转、裁剪让模型见多识广不容易过拟合from torch.utils.data import DataLoader # 训练时加一点增强测试时不加要测真实水平 train_transform transforms.Compose([ transforms.RandomHorizontalFlip(), # 随机左右翻转 transforms.RandomCrop(32, padding4), # 随机裁剪再补回32x32 transforms.ToTensor(), transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)) ]) test_transform transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)) ]) # 重新加载数据集用新transform train_dataset datasets.CIFAR10(./data, trainTrue, downloadTrue, transformtrain_transform) test_dataset datasets.CIFAR10(./data, trainFalse, downloadTrue, transformtest_transform) # 创建DataLoaderbatch_size128开4个进程并行读数据 train_loader DataLoader(train_dataset, batch_size128, shuffleTrue, num_workers4) test_loader DataLoader(test_dataset, batch_size100, shuffleFalse, num_workers4) # 检查一个batch的数据形状 dataiter iter(train_loader) images, labels next(dataiter) print(f一个batch的图片形状{images.shape}) # torch.Size([128, 3, 32, 32]) print(f一个batch的标签形状{labels.shape}) # torch.Size([128])这里batch_size128是个经验值。太大显存可能爆太小训练太慢。128在大多数笔记本上很友好。3.3 训练循环五步走稳扎稳打训练模型的核心逻辑就五句话我们把它封装成一个清晰的函数def train_one_epoch(model, train_loader, optimizer, criterion, device): model.train() # 切换到训练模式影响Dropout/BatchNorm等 running_loss 0.0 correct 0 total 0 for images, labels in train_loader: images, labels images.to(device), labels.to(device) # 送到GPU如有 # 1. 清空梯度 optimizer.zero_grad() # 2. 前向传播算预测 outputs model(images) # 3. 计算损失 loss criterion(outputs, labels) # 4. 反向传播算梯度 loss.backward() # 5. 更新参数 optimizer.step() # 统计指标 running_loss loss.item() _, predicted outputs.max(1) total labels.size(0) correct predicted.eq(labels).sum().item() acc 100. * correct / total avg_loss running_loss / len(train_loader) return avg_loss, acc # 同理写一个测试函数不更新参数只评估 def evaluate(model, test_loader, device): model.eval() correct 0 total 0 with torch.no_grad(): # 关闭梯度计算省显存 for images, labels in test_loader: images, labels images.to(device), labels.to(device) outputs model(images) _, predicted outputs.max(1) total labels.size(0) correct predicted.eq(labels).sum().item() return 100. * correct / total现在把所有零件组装起来开始训练import torch.optim as optim # 设置设备优先用GPU device torch.device(cuda if torch.cuda.is_available() else cpu) model SimpleCNN().to(device) # 定义损失函数和优化器 criterion nn.CrossEntropyLoss() # 分类任务的标准损失 optimizer optim.Adam(model.parameters(), lr0.001) # Adam优化器学习率0.001 # 训练主循环 num_epochs 10 train_losses [] train_accs [] test_accs [] for epoch in range(num_epochs): print(f\nEpoch {epoch1}/{num_epochs}) # 训练一轮 loss, acc train_one_epoch(model, train_loader, optimizer, criterion, device) train_losses.append(loss) train_accs.append(acc) # 测试一轮 test_acc evaluate(model, test_loader, device) test_accs.append(test_acc) print(fTrain Loss: {loss:.4f} | Train Acc: {acc:.2f}% | Test Acc: {test_acc:.2f}%) print(\n训练完成)第一次运行时你会看到准确率从50%左右开始爬升10轮后稳定在75%-80%之间。这已经比随机猜10%好太多了。如果想更高我们可以后续加技巧但此刻的目标是先跑通再优化。4. 调参与优化让模型更聪明的三个实用技巧4.1 学习率调度别让模型“迈大步”学习率lr就像走路的步长。太大模型在最优解附近来回震荡甚至跳过去太小进步缓慢训练像蜗牛。我们用PyTorch内置的StepLR每过5轮就把学习率砍半from torch.optim.lr_scheduler import StepLR # 替换原来的optimizer定义 optimizer optim.Adam(model.parameters(), lr0.001) scheduler StepLR(optimizer, step_size5, gamma0.5) # 每5轮lr * 0.5 # 在训练循环里每轮结束后调用 for epoch in range(num_epochs): # ... 训练代码 ... scheduler.step() # 这行加在每轮末尾 print(f当前学习率{scheduler.get_last_lr()[0]:.6f})加了这个你会发现后期准确率提升更稳不容易卡住。这是最简单、最有效的调参技巧之一。4.2 添加Dropout给神经元“放假”过拟合是新手常踩的坑模型在训练集上99%准确一到测试集就跌到60%。Dropout是经典解法——在训练时随机“关闭”一部分神经元设为0强迫网络不依赖个别神经元从而泛化更好。在模型里加两行class SimpleCNN(nn.Module): def __init__(self, num_classes10): super().__init__() # ... 原有代码 ... self.dropout nn.Dropout(0.5) # 50%概率置零 def forward(self, x): # ... 原有代码 ... x self.flatten(x) x self.dropout(x) # 在展平后加dropout x self.fc(x) return x加了Dropout训练准确率会略降因为部分神经元不工作但测试准确率通常会上升2-3个百分点。这就是“以退为进”的智慧。4.3 使用预训练特征站在巨人的肩膀上上面的CNN是从头学起scratch training。但ImageNet上已有大量高质量预训练模型如ResNet、VGG它们学到了通用的视觉特征。我们可以“迁移学习”冻结前面的层只训练最后的分类头。用PyTorch实现只需几行from torchvision import models # 加载预训练的ResNet18在ImageNet上训练过 base_model models.resnet18(weightsmodels.ResNet18_Weights.IMAGENET1K_V1) # 冻结所有层不更新参数 for param in base_model.parameters(): param.requires_grad False # 替换最后的全连接层适配CIFAR-10的10类 base_model.fc nn.Linear(base_model.fc.in_features, 10) # 现在base_model就是我们的新模型 model base_model.to(device)这样改完训练10轮后测试准确率往往能冲到85%以上。因为ResNet已经学会了“什么是轮子”“什么是翅膀”你只需要教它“轮子多的是汽车翅膀大的是飞机”。小提醒迁移学习需要更多显存ResNet比SimpleCNN大得多如果显存紧张优先用前面两个技巧。5. 结果分析与可视化看见模型的思考过程训练完不能只看一个数字。我们要深入进去看看模型到底学到了什么。5.1 绘制训练曲线一目了然看趋势用matplotlib画出损失和准确率的变化import matplotlib.pyplot as plt plt.figure(figsize(12, 4)) plt.subplot(1, 2, 1) plt.plot(train_losses, labelTrain Loss) plt.title(Training Loss Over Epochs) plt.xlabel(Epoch) plt.ylabel(Loss) plt.legend() plt.subplot(1, 2, 2) plt.plot(train_accs, labelTrain Accuracy) plt.plot(test_accs, labelTest Accuracy) plt.title(Accuracy Over Epochs) plt.xlabel(Epoch) plt.ylabel(Accuracy (%)) plt.legend() plt.tight_layout() plt.show()好的曲线应该是损失持续下降训练和测试准确率都稳步上升且两条线距离不大说明没过拟合。如果测试线突然掉下去就要检查数据增强或Dropout是否够用。5.2 查看错误案例理解模型的“盲区”模型在哪类图片上总犯错我们找出测试集中被分错的前10张人工看看原因def show_mistakes(model, test_loader, classes, device, n10): model.eval() mistakes [] with torch.no_grad(): for images, labels in test_loader: images, labels images.to(device), labels.to(device) outputs model(images) _, predicted outputs.max(1) wrong_mask predicted ! labels for i in range(len(images)): if len(mistakes) n: break if wrong_mask[i]: img images[i].cpu() true_label classes[labels[i]] pred_label classes[predicted[i]] mistakes.append((img, true_label, pred_label)) # 画图 fig, axes plt.subplots(2, 5, figsize(12, 6)) axes axes.flatten() for i, (img, true, pred) in enumerate(mistakes): # 反归一化让图片能正常显示 img img * torch.tensor([0.2023, 0.1994, 0.2010]).view(3,1,1) torch.tensor([0.4914, 0.4822, 0.4465]).view(3,1,1) img torch.clamp(img, 0, 1) # 保证值在0-1间 axes[i].imshow(img.permute(1,2,0)) axes[i].set_title(fTrue:{true}\nPred:{pred}, fontsize9) axes[i].axis(off) plt.suptitle(Top 10 Misclassified Examples) plt.show() # 类别名 classes (plane, car, bird, cat, deer, dog, frog, horse, ship, truck) show_mistakes(model, test_loader, classes, device)你可能会发现模型把“猫”认成“狗”把“青蛙”认成“猫”——因为它们都有大眼睛、圆脸。这很正常说明模型在学语义相似性而不是死记硬背。这也提示你如果业务场景里这两类必须严格区分就需要更多针对性数据或更强的模型。5.3 特征可视化看卷积层在“看”什么最后我们用一个经典技巧可视化第一个卷积层的32个滤波器filter在“检测”什么模式。def visualize_filters(model): # 获取第一层卷积权重 weights model.conv1.weight.data.cpu() # shape: [32, 3, 3, 3] # 画32个3x3的滤波器 fig, axes plt.subplots(4, 8, figsize(12, 6)) axes axes.flatten() for i in range(32): # 取第一个通道R通道的权重归一化到0-1 filter_img weights[i, 0] # [3,3] filter_img (filter_img - filter_img.min()) / (filter_img.max() - filter_img.min()) axes[i].imshow(filter_img, cmapgray) axes[i].axis(off) plt.suptitle(First Conv Layer Filters (R channel)) plt.show() visualize_filters(model)你会看到一些滤波器像细线检测边缘一些像斑点检测纹理还有一些像模糊的色块——这正是CNN从底层学起的过程。虽然我们没手动设计它们但模型自己找到了最适合的“视觉探测器”。6. 总结你已经掌握了图像分类的核心脉络回看整个过程我们其实只做了三件关键的事第一把问题拆解成可执行的步骤数据怎么来、模型怎么搭、训练怎么写、结果怎么看。每一步都有明确的代码对应没有玄学。第二用工具放大你的能力Yi-Coder-1.5B不是替代你思考而是帮你把“我想实现XX功能”快速转成“这段代码该怎么写”。它省下的是查文档、试语法、调bug的时间让你专注在逻辑和设计上。第三在实践中建立直觉看到训练曲线波动你知道可能是学习率问题看到测试准确率上不去你会想到加Dropout或换数据增强看到错误案例你能判断是数据问题还是模型容量问题。这种直觉只有亲手跑过、调过、错过的工程师才会有。你现在完全可以用这套方法换成自己的数据集比如手机拍的几十张水果照片微调一下做出一个能识别苹果、香蕉、橙子的小应用。门槛没那么高关键是迈出第一步。如果后续想深入可以尝试用更大的模型ResNet50、加更多数据增强AutoAugment、试试不同的优化器SGD with momentum、或者把模型部署到手机上。但那些都是锦上添花今天你已经拿到了打开AI大门的钥匙。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。