ResNet18从零开始:没Linux经验?Windows也能轻松玩
引言
作为一名Windows用户,当你想要学习深度学习中的经典模型ResNet18时,是不是经常被各种Linux命令和复杂的开发环境配置劝退?别担心,这篇文章就是为你量身定制的Windows友好方案。
ResNet18是深度学习领域最经典的图像分类模型之一,由微软研究院在2015年提出。它通过"残差连接"的创新设计,解决了深度神经网络训练中的梯度消失问题,使得训练更深的网络成为可能。虽然名字里有"18",但它其实只有18层(包含池化和全连接层),非常适合初学者学习和实践。
本文将带你从零开始,在Windows系统上完成ResNet18的完整训练流程,包括:
- 无需Linux命令,纯Windows环境配置
- 使用PyTorch框架的简化操作
- 从数据准备到模型训练的全流程
- 常见问题的解决方案
即使你没有任何Linux经验,也能跟着步骤轻松上手。让我们开始这段深度学习之旅吧!
1. 环境准备:Windows下的深度学习配置
1.1 安装Python和PyTorch
首先,我们需要在Windows上搭建Python开发环境:
- 访问Python官网下载最新版的Python安装包(推荐3.8-3.10版本)
- 安装时务必勾选"Add Python to PATH"选项
- 安装完成后,打开命令提示符(Win+R,输入cmd),验证安装:
python --version pip --version接下来安装PyTorch,这是Facebook开源的深度学习框架:
pip install torch torchvision torchaudio💡 提示
如果你的电脑有NVIDIA显卡,可以安装支持CUDA的PyTorch版本以加速训练。访问PyTorch官网获取适合你显卡的安装命令。
1.2 安装其他必要库
我们还需要一些辅助工具库:
pip install numpy matplotlib opencv-python tqdm1.3 验证环境
创建一个Python脚本check_env.py,内容如下:
import torch print(f"PyTorch版本: {torch.__version__}") print(f"CUDA可用: {torch.cuda.is_available()}") print(f"GPU型号: {torch.cuda.get_device_name(0) if torch.cuda.is_available() else '无'}")运行后如果能看到PyTorch版本和CUDA状态,说明环境配置成功。
2. 数据准备:构建你的第一个图像数据集
2.1 选择数据集
为了快速上手,我们可以使用经典的CIFAR-10数据集,它包含10个类别的6万张32x32彩色图像:
from torchvision import datasets, transforms # 定义数据转换 transform = transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) ]) # 下载并加载数据集 train_set = datasets.CIFAR10(root='./data', train=True, download=True, transform=transform) test_set = datasets.CIFAR10(root='./data', train=False, download=True, transform=transform)2.2 自定义数据集(可选)
如果你想使用自己的图片进行分类,可以按照以下结构组织文件夹:
my_dataset/ train/ class1/ img1.jpg img2.jpg ... class2/ img1.jpg ... test/ class1/ img1.jpg ... class2/ img1.jpg ...然后使用ImageFolder加载:
from torchvision.datasets import ImageFolder train_set = ImageFolder('my_dataset/train', transform=transform) test_set = ImageFolder('my_dataset/test', transform=transform)2.3 创建数据加载器
数据加载器(DataLoader)可以帮助我们批量加载数据:
from torch.utils.data import DataLoader batch_size = 32 train_loader = DataLoader(train_set, batch_size=batch_size, shuffle=True) test_loader = DataLoader(test_set, batch_size=batch_size, shuffle=False)3. 模型构建:理解并实现ResNet18
3.1 ResNet18结构解析
ResNet18的核心是"残差块"(Residual Block),它通过"跳跃连接"(skip connection)将输入直接加到输出上:
输入 → 卷积层1 → 卷积层2 → + → 输出 |________________|这种设计解决了深度网络中的梯度消失问题,使得网络可以更深而不会影响训练效果。
3.2 使用预训练模型
PyTorch已经内置了ResNet18的实现,我们可以直接加载:
import torchvision.models as models model = models.resnet18(pretrained=True)3.3 修改输出层
预训练模型是在ImageNet(1000类)上训练的,我们需要修改最后一层以适应我们的分类任务(如CIFAR-10的10类):
import torch.nn as nn num_classes = 10 # CIFAR-10有10个类别 model.fc = nn.Linear(model.fc.in_features, num_classes)3.4 完整模型定义
如果你想从头实现ResNet18,可以参考以下代码:
import torch.nn as nn class BasicBlock(nn.Module): expansion = 1 def __init__(self, in_channels, out_channels, stride=1): super().__init__() self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False) self.bn1 = nn.BatchNorm2d(out_channels) self.relu = nn.ReLU(inplace=True) self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False) self.bn2 = nn.BatchNorm2d(out_channels) self.shortcut = nn.Sequential() if stride != 1 or in_channels != self.expansion * out_channels: self.shortcut = nn.Sequential( nn.Conv2d(in_channels, self.expansion * out_channels, kernel_size=1, stride=stride, bias=False), nn.BatchNorm2d(self.expansion * out_channels) ) def forward(self, x): out = self.relu(self.bn1(self.conv1(x))) out = self.bn2(self.conv2(out)) out += self.shortcut(x) out = self.relu(out) return out class ResNet(nn.Module): def __init__(self, block, num_blocks, num_classes=10): super().__init__() self.in_channels = 64 self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False) self.bn1 = nn.BatchNorm2d(64) self.relu = nn.ReLU(inplace=True) self.layer1 = self._make_layer(block, 64, num_blocks[0], stride=1) self.layer2 = self._make_layer(block, 128, num_blocks[1], stride=2) self.layer3 = self._make_layer(block, 256, num_blocks[2], stride=2) self.layer4 = self._make_layer(block, 512, num_blocks[3], stride=2) self.avg_pool = nn.AdaptiveAvgPool2d((1, 1)) self.fc = nn.Linear(512 * block.expansion, num_classes) def _make_layer(self, block, out_channels, num_blocks, stride): strides = [stride] + [1]*(num_blocks-1) layers = [] for stride in strides: layers.append(block(self.in_channels, out_channels, stride)) self.in_channels = out_channels * block.expansion return nn.Sequential(*layers) def forward(self, x): out = self.relu(self.bn1(self.conv1(x))) out = self.layer1(out) out = self.layer2(out) out = self.layer3(out) out = self.layer4(out) out = self.avg_pool(out) out = out.view(out.size(0), -1) out = self.fc(out) return out def ResNet18(): return ResNet(BasicBlock, [2, 2, 2, 2]) model = ResNet18()4. 模型训练与评估
4.1 定义损失函数和优化器
import torch.optim as optim criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.9, weight_decay=5e-4) scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)4.2 训练循环
def train(epoch): model.train() train_loss = 0 correct = 0 total = 0 for batch_idx, (inputs, targets) in enumerate(train_loader): optimizer.zero_grad() outputs = model(inputs) loss = criterion(outputs, targets) loss.backward() optimizer.step() train_loss += loss.item() _, predicted = outputs.max(1) total += targets.size(0) correct += predicted.eq(targets).sum().item() if batch_idx % 100 == 0: print(f'Epoch: {epoch} | Batch: {batch_idx}/{len(train_loader)} | Loss: {loss.item():.3f} | Acc: {100.*correct/total:.1f}%') return train_loss/(batch_idx+1), 100.*correct/total4.3 测试函数
def test(epoch): model.eval() test_loss = 0 correct = 0 total = 0 with torch.no_grad(): for batch_idx, (inputs, targets) in enumerate(test_loader): outputs = model(inputs) loss = criterion(outputs, targets) test_loss += loss.item() _, predicted = outputs.max(1) total += targets.size(0) correct += predicted.eq(targets).sum().item() print(f'Test Epoch: {epoch} | Loss: {test_loss/(batch_idx+1):.3f} | Acc: {100.*correct/total:.1f}%') return test_loss/(batch_idx+1), 100.*correct/total4.4 开始训练
epochs = 100 best_acc = 0 for epoch in range(epochs): train_loss, train_acc = train(epoch) test_loss, test_acc = test(epoch) scheduler.step() if test_acc > best_acc: print('Saving best model..') torch.save(model.state_dict(), 'resnet18_best.pth') best_acc = test_acc5. 常见问题与解决方案
5.1 训练速度慢
- 原因:可能没有使用GPU加速
- 解决:确保安装了CUDA版本的PyTorch,并将模型和数据移动到GPU:
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu") model = model.to(device) # 在训练循环中 inputs, targets = inputs.to(device), targets.to(device)5.2 内存不足
- 原因:批量大小(batch_size)设置过大
- 解决:减小batch_size(如从32降到16),或使用梯度累积:
accumulation_steps = 4 optimizer.zero_grad() for i, (inputs, targets) in enumerate(train_loader): outputs = model(inputs) loss = criterion(outputs, targets) loss = loss / accumulation_steps loss.backward() if (i+1) % accumulation_steps == 0: optimizer.step() optimizer.zero_grad()5.3 过拟合
- 原因:模型过于复杂或训练数据不足
- 解决:
- 增加数据增强:
python transform_train = transforms.Compose([ transforms.RandomHorizontalFlip(), transforms.RandomCrop(32, padding=4), transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) ])
- 添加正则化(如Dropout)
- 使用早停(early stopping)
6. 模型应用:使用训练好的模型进行预测
训练完成后,我们可以使用模型对新图像进行分类:
from PIL import Image def predict(image_path): # 加载图像 image = Image.open(image_path) # 预处理 transform = transforms.Compose([ transforms.Resize(32), transforms.CenterCrop(32), transforms.ToTensor(), transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)) ]) image = transform(image).unsqueeze(0) # 预测 model.eval() with torch.no_grad(): output = model(image) _, predicted = torch.max(output, 1) # CIFAR-10类别 classes = ('plane', 'car', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck') return classes[predicted[0]] # 使用示例 print(predict('test_image.jpg'))7. 总结
通过本文的学习,你应该已经掌握了在Windows系统上使用PyTorch训练ResNet18模型的完整流程。让我们回顾一下核心要点:
- 环境配置简单:只需安装Python和PyTorch,无需复杂的Linux环境
- 数据准备灵活:可以使用标准数据集如CIFAR-10,也可以轻松加载自定义数据集
- 模型构建清晰:理解了ResNet18的核心结构,并学会了如何修改预训练模型
- 训练流程完整:从损失函数定义到训练循环,掌握了深度学习的核心训练方法
- 问题解决全面:针对常见问题提供了实用的解决方案
现在,你可以尝试使用自己的数据集来训练ResNet18模型了。深度学习的世界充满可能,ResNet18只是开始,期待你探索更多精彩的模型和应用!
💡获取更多AI镜像
想探索更多AI镜像和应用场景?访问 CSDN星图镜像广场,提供丰富的预置镜像,覆盖大模型推理、图像生成、视频生成、模型微调等多个领域,支持一键部署。