一.环境配置以及数据加载
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision.transforms as transforms
from torchvision.datasets import ImageFolder
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
使用pytorch来进行卷积处理
transform = transforms.Compose([
transforms.Resize((64, 64)),
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])
train_dataset = ImageFolder(root='train2/', transform=transform)
train_loader = DataLoader(train_dataset, batch_size=16, shuffle=True) # 修改批次大小
传入数据集,将图像数据进行转换和标准化,首先将图像调整为64*64大小,根据gpu和cpu来选择图像的处理,将图像转换为张量(tensor)的格式,使用normalize函数对图像进行标准化处理,格式为均值和标准差的形式。
二.cnn模型的创建
class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.conv1 = nn.Conv2d(3, 32, kernel_size=3, padding=1)
self.conv2 = nn.Conv2d(64, 128, kernel_size=3, padding=1)
self.conv3 = nn.Conv2d(64, 128, kernel_size=3, padding=1)
self.pool = nn.MaxPool2d(2, 2)
self.fc1 = nn.Linear(128 * 8 * 8, 512) # 修改全连接层的输入大小
self.fc2 = nn.Linear(512, 2) # 2 classes: cat and dog
def forward(self, x):
x = self.pool(torch.relu(self.conv1(x)))
x = self.pool(torch.relu(self.conv2(x)))
x = self.pool(torch.relu(self.conv3(x)))
x = x.view(-1, 128 * 8 * 8) # 修改视图大小
x = torch.relu(self.fc1(x))
x = self.fc2(x)
return x
在这段代码中,我们首先定义了三个卷积核,由于图像是rgb三个通道的输入,所以我们第一个卷积核的输入通道为3,输出为32,以此类推,第二个卷积核输入32,输出64,卷积核大小为3*3。
通过堆叠多个卷积层,可以逐渐扩大特征图的通道数,从而增加网络的非线性能力和表达能力。这种逐渐加深和加宽网络的设计可以更好地拟合复杂的数据分布,提高模型的泛化能力和准确性。因此,堆叠卷积层是设计深度卷积神经网络的重要手段之一。、
池化层
定义了一个最大池化层(nn.MaxPool2d)。在这个例子中,参数2, 2表示池化核大小为2x2,步幅为2。最大池化层的作用是对输入特征图进行下采样,通过保留每个池化窗口中的最大值来减少特征图的尺寸,同时保留主要特征。
我们将三个卷积核通过relu函数处理,使用池化层一一提取特征,进行下采样,得到一个128*8*8的特征。
全连接层
我们通过三次卷积叠加以及一次池化层下采样,得到了输入的特征,创建两个链接层,将第一个连接层的数据进行relu激活函数的处理最后通过激活函数输出
三.训练模型
for epoch in range(10): # 可以调整 epoch 数量
running_loss = 0.0
correct = 0
total = 0
for inputs, labels in train_loader:
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
使用了一个for循环来迭代训练模型的次数,每次迭代称为一个epoch。在每个epoch中,我们都会将运行损失(running_loss)初始化为0,正确预测数量(correct)和总样本数量(total)初始化为0。然后我们遍历训练数据加载器(train_loader)中的输入和标签对。对于每个输入,我们将优化器(optimizer)的梯度归零,将输入传递给模型(model),计算模型的输出,计算损失(loss),然后通过反向传播(loss.backward())更新模型的参数(optimizer.step())。最后,我们将每个样本的损失值加到运行损失中。
计算准确率
_, predicted = torch.max(outputs, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
epoch_loss = running_loss / len(train_loader)
epoch_accuracy = 100 * correct / total
loss_history.append(epoch_loss)
accuracy_history.append(epoch_accuracy)
在这段代码中,我们使用torch.max函数来获取模型输出outputs在第一个维度上的最大值以及对应的索引,即预测值predicted。然后,我们将标签的数量labels.size(0)加到total中,用于统计总样本数量。接着,我们将预测值与真实标签labels进行比较,如果预测值与真实标签相等,则计算出的结果为True,否则为False。将这些结果求和并转换为Python的整数类型(item()方法),然后累加到正确预测数量correct中。这段代码的目的是用于计算模型在每个epoch中的准确率。
四.源代码
import torch
import torch.nn as nn
import torch.optim as optim
import torchvision.transforms as transforms
from torchvision.datasets import ImageFolder
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt
# 定义 CNN 模型
class CNN(nn.Module):
def __init__(self):
super(CNN, self).__init__()
self.conv1 = nn.Conv2d(3, 32, kernel_size=3, padding=1)
self.conv2 = nn.Conv2d(32, 64, kernel_size=3, padding=1)
self.conv3 = nn.Conv2d(64, 128, kernel_size=3, padding=1)
self.pool = nn.MaxPool2d(2, 2)
self.fc1 = nn.Linear(128 * 8 * 8, 512) # 修改全连接层的输入大小
self.fc2 = nn.Linear(512, 2) # 2 classes: cat and dog
def forward(self, x):
x = self.pool(torch.relu(self.conv1(x)))
x = self.pool(torch.relu(self.conv2(x)))
x = self.pool(torch.relu(self.conv3(x)))
x = x.view(-1, 128 * 8 * 8) # 修改视图大小
x = torch.relu(self.fc1(x))
x = self.fc2(x)
return x
# 数据预处理和加载
transform = transforms.Compose([
transforms.Resize((64, 64)),
transforms.ToTensor(),
transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5))
])
train_dataset = ImageFolder(root='train2/', transform=transform)
train_loader = DataLoader(train_dataset, batch_size=16, shuffle=True) # 修改批次大小
# 实例化模型、损失函数和优化器
model = CNN()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 训练过程中的损失和准确率可视化
loss_history = []
accuracy_history = []
for epoch in range(10): # 可以调整 epoch 数量
running_loss = 0.0
correct = 0
total = 0
for inputs, labels in train_loader:
optimizer.zero_grad()
outputs = model(inputs)
loss = criterion(outputs, labels)
loss.backward()
optimizer.step()
running_loss += loss.item()
# 计算训练集上的准确率
_, predicted = torch.max(outputs, 1)
total += labels.size(0)
correct += (predicted == labels).sum().item()
epoch_loss = running_loss / len(train_loader)
epoch_accuracy = 100 * correct / total
loss_history.append(epoch_loss)
accuracy_history.append(epoch_accuracy)
print(f"Epoch {epoch + 1}, Loss: {epoch_loss}, Accuracy: {epoch_accuracy}%")
# 可视化损失和准确率
plt.figure(figsize=(10, 5))
plt.subplot(1, 2, 1)
plt.plot(loss_history, label='Training Loss')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.title('Training Loss')
plt.legend()
plt.subplot(1, 2, 2)
plt.plot(accuracy_history, label='Training Accuracy')
plt.xlabel('Epoch')
plt.ylabel('Accuracy (%)')
plt.title('Training Accuracy')
plt.legend()
plt.show()
# 保存模型
torch.save(model.state_dict(), 'cat_dog_classifier.pth')