6.感受野
6.1 理解感受野
字面意思是感受的视野范围
如果堆叠3个3 x 3的卷积层,并且保持滑动窗口步长为1,其感受野就是7×7的了, 这跟一个使用7x7卷积核的结果是一样的,那为什么非要堆叠3个小卷积呢?
6.2 感受野的作用
假设输入大小都是h × w × C,并且都使用C个卷积核(得到C个特征图),可以来计算 一下其各自所需参数
很明显,堆叠小的卷积核所需的参数更少一些,并且卷积过程越多,特征提取也会越细致,加入的非线性变换也随着增多,还不会增大权重参数个数,这就是感受野的基本出发点,用小的卷积核来完成体特征提取操作。
8.卷积神经网络案例
8.1 模型结构
网络结构如下:
- 输入形状: 32x32
- 第一个卷积层输入 3 个 Channel, 输出 6 个 Channel, Kernel Size 为: 3x3
- 第一个池化层输入 30x30, 输出 15x15, Kernel Size 为: 2x2, Stride 为: 2
- 第二个卷积层输入 6 个 Channel, 输出 16 个 Channel, Kernel Size 为 3x3
- 第二个池化层输入 13x13, 输出 6x6, Kernel Size 为: 2x2, Stride 为: 2
- 第一个全连接层输入 576 维, 输出 120 维
- 第二个全连接层输入 120 维, 输出 84 维
- 最后的输出层输入 84 维, 输出 10 维
我们在每个卷积计算之后应用 relu 激活函数来给网络增加非线性因素。
8.2 网络模型定义
.\model\image_classification.py
python
import torch
import torch.nn as nn
class ImageClassification(nn.Module):
def __init__(self):
super(ImageClassification, self).__init__()
# 这是一层卷积层
self.layer1 = nn.Sequential(
nn.Conv2d(in_channels=3, out_channels=32, kernel_size=3, stride=1),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2),
nn.Dropout(0.25),
)
self.layer2 = nn.Sequential(
nn.Conv2d(in_channels=32, out_channels=128, kernel_size=3, stride=1),
nn.ReLU(),
nn.MaxPool2d(kernel_size=2, stride=2),
nn.Dropout(0.25),
)
self.linear1 = nn.Sequential(
nn.Linear(128 * 6 * 6, 2048), nn.ReLU(), nn.Dropout(0.5)
)
self.linear2 = nn.Sequential(nn.Linear(2048, 1024), nn.ReLU(), nn.Dropout(0.5))
self.out = nn.Linear(1024, 10)
def forward(self, x):
x = self.layer1(x)
x = self.layer2(x)
x = x.reshape(x.size(0), -1)
x = self.linear1(x)
x = self.linear2(x)
return self.out(x)
8.3 用到的模块
python
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
from torchvision.datasets import CIFAR10
from torchvision.transforms import Compose, ToTensor
from torch.utils.data import DataLoader
import os
import time
# 从modele目录导入模型
from model.image_classification import ImageClassification
8.4 CIFAR10数据源
python
def test001():
dir = os.path.dirname(__file__)
# 加载数据集
train = CIFAR10(
root=os.path.join(dir, "data"),
train=True,
download=True,
transform=Compose([ToTensor()]),
)
vaild = CIFAR10(
root=os.path.join(dir, "data"),
train=False,
download=True,
transform=Compose([ToTensor()]),
)
# 观察一下数据集信息
print("训练数据集数量:", train.__len__())
# 观察一下数据集分类情况
print("训练数据集分类情况:", train.class_to_idx)
train_loader = DataLoader(train, batch_size=128, shuffle=True)
vaild_loader = DataLoader(vaild, batch_size=128, shuffle=True)
for i, (x, y) in enumerate(train_loader):
print(i, x.shape, y.shape)
8.5 模型训练及保存
python
def train():
dir = os.path.dirname(__file__)
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# 加载数据集
train = CIFAR10(
root=os.path.join(dir, "data"),
train=True,
download=True,
transform=transform,
)
# 导入模型
model = ImageClassification()
model.to(device)
# 定义i模型训练的超参数
epochs = 80
lr = 1e-3
batch_size = 256
loss_history = []
# 构建训练用的损失函数及优化器
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=lr)
for i, epoch in enumerate(range(epochs)):
# 构建训练数据批次
train_loader = DataLoader(train, batch_size=batch_size, shuffle=True)
# 记录样本数量
train_num = 0
# 记录总的损失值:用于计算平均损失
total_loss = 0.0
# 记录正确记录数
correct = 0
# 记录训练开始时间
start = time.time()
# 开始使用批次数据进行训练
for x, y in train_loader:
# 更改模型训练设备
x = x.to(device)
y = y.to(device)
# 送入模型
output = model(x)
# 计算损失
loss = criterion(output, y)
# 梯度清零
optimizer.zero_grad()
# 反向传播
loss.backward()
# 更新参数
optimizer.step()
# 更新训练过程的数据
train_num += len(y)
total_loss += loss.item() * len(y)
correct += output.argmax(1).eq(y).sum().item()
print(
"epoc:%d loss:%.3f accuracy:%.3f time:%.3f"
% (i + 1, total_loss / train_num, correct / train_num, time.time() - start)
)
loss_history.append(total_loss / train_num)
# 更新图形
update_plot(loss_history)
# 训练完成之后,保存模型
torch.save(model.state_dict(), os.path.join(dir, "model.pth"))
print("模型保存成功:", i)
8.6 模型加载及验证
python
# 测试集评估
def vaild():
dir = os.path.dirname(__file__)
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
# 定义超参数
batch_size = 100
# 定义测试记录数据
vaild_num = 0
total_correct = 0
vaild_data = CIFAR10(
root=os.path.join(dir, "data"),
train=False,
download=False,
transform=transform,
)
# 构建测评数据集批次
vaild_loader = DataLoader(vaild_data, batch_size=batch_size, shuffle=False)
model = ImageClassification()
# 加载模型参数
model.load_state_dict(torch.load(os.path.join(dir, "model.pth")))
# 切换为验证模式
model.to(device)
model.eval()
for x, y in vaild_loader:
x = x.to(device)
y = y.to(device)
output = model(x)
total_correct += output.argmax(1).eq(y).sum().item()
vaild_num += len(y)
print("测试集正确率:%.3f" % (total_correct / vaild_num))
我们可以从以下几个方面来调整网络:
- 增加卷积核输出通道数
- 增加全连接层的参数量
- 调整学习率
- 调整优化方法
- 修改激活函数
- 进行数据增强,等等
8.7 数据增强
python
data_transforms = {
'train': transforms.Compose([transforms.RandomRotation(45),#随机旋转,-45到45度之间随机选
transforms.CenterCrop(224),#从中心开始裁剪
transforms.RandomHorizontalFlip(p=0.5),#随机水平翻转 选择一个概率概率
transforms.RandomVerticalFlip(p=0.5),#随机垂直翻转
transforms.ColorJitter(brightness=0.2, contrast=0.1, saturation=0.1, hue=0.1),#参数1为亮度,参数2为对比度,参数3为饱和度,参数4为色相
transforms.RandomGrayscale(p=0.025),#概率转换成灰度率,3通道就是R=G=B
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])#均值,标准差
]),
'valid': transforms.Compose([transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
]),
}
python
from torchvision import transforms
from PIL import Image
# 定义图像预处理步骤
preprocess = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
])
# 打开图像
img = Image.open("path_to_image.jpg")
# 应用预处理步骤
img_tensor = preprocess(img)
8.8 实时渲染训练效果
python
# 更新图形的函数
def update_plot(loss_history):
plt.cla() # 清除之前的图形
plt.plot(loss_history, marker="o", color="green", linestyle="-") # 绘制损失率曲线
plt.xlabel("Epoch")
plt.ylabel("Loss")
plt.title("Training Loss")
plt.grid(True)
plt.pause(0.001) # 暂停一段时间以便更新图形
# 开始训练模型并动态显示损失率变化
plt.ion() # 打开交互模式
train()
plt.ioff() # 关闭交互模式
plt.show()
#在训练的时候实时更新数据
loss_history = []
loss_history.append(total_loss / train_num)
# 更新图形
update_plot(loss_history)