简介:介绍了使用深度学习技术,尤其是BP神经网络对猫狗图像进行分类的过程,包括数据收集、预处理、模型设计(含ReLU和Softmax)、训练和评估的步骤,以及如何构建自定义数据集和训练模型进行预测。
前言
猫狗大战是指使用机器学习技术,特别是深度学习,对猫和狗的图片进行识别和分类的过程。这个过程的基本原理是通过训练一个深度神经网络模型,使其能够根据输入的图片数据,自动识别和区分出猫和狗。
一、基本步骤
1.数据收集:
收集大量的猫和狗的图片数据,并对这些图片进行标注,即给每张图片打上一个标签,标注出这张图片是猫还是狗。
2.数据预处理:
对收集到的图片数据进行预处理,包括调整图片的大小、裁剪、归一化等,使其满足模型训练的要求。
3.模型设计:
设计一个深度神经网络模型,这个模型通常包含多个卷积层和全连接层,用于提取图片的特征,并进行分类。
4.模型训练:
使用猫和狗的图片数据对模型进行训练,通过不断的调整模型的参数,使模型能够根据输入的图片数据,准确地识别和分类出猫和狗。
5.模型评估:
使用一部分未参与模型训练的猫和狗的图片数据,对训练好的模型进行评估,以检查模型的性能。
二、原理
1.构建BP神将网络模型
class BPNetwork(torch.nn.Module):
def __init__(self):
super(BPNetwork, self).__init__()
"""
定义第一个线性层,
输入为图片(28x28),
输出为第一个隐层的输入,大小为128。
"""
self.linear1 = torch.nn.Linear(224 * 224 * 3, 128)
# 在第一个隐层使用ReLU激活函数
self.relu1 = torch.nn.ReLU()
"""
定义第二个线性层,
输入是第一个隐层的输出,
输出为第二个隐层的输入,大小为64。
"""
self.linear2 = torch.nn.Linear(128, 64)
# 在第二个隐层使用ReLU激活函数
self.relu2 = torch.nn.ReLU()
"""
定义第三个线性层,
输入是第二个隐层的输出,
输出为输出层,大小为2
"""
self.linear3 = torch.nn.Linear(64, 2)
# 最终的输出经过softmax进行归一化
self.softmax = torch.nn.LogSoftmax(dim=1)
def forward(self, x):
"""
定义神经网络的前向传播
x: 图片数据, shape为(64, 1, 28, 28)
"""
# 首先将x的shape转为(64, 784)
x = x.view(x.shape[0], -1)
# 接下来进行前向传播
x = self.linear1(x)
x = self.relu1(x)
x = self.linear2(x)
x = self.relu2(x)
x = self.linear3(x)
x = self.softmax(x)
# 上述一串,可以直接使用 x = self.model(x) 代替。
return x
激活函数用于在神经网络中引入非线性,ReLU激活函数,作用:它将所有负值设置为零,而保持正值不变。在代码中,self.relu1 和 self.relu2 分别应用于网络的两个隐层。
Softmax 函数通常用于多分类问题的输出层,它将网络的原始输出(通常是实数)转换为概率分布。在代码中,self.softmax 使用了 LogSoftmax,它首先计算 Softmax,然后取对数,这通常用于数值稳定性。
2.制作训练集
from torch.utils.data import Dataset
class mydataset(Dataset):
def __init__(self, root_dir, label_dir):
self.root_dir = root_dir
self.label_dir = label_dir
self.path = os.path.join(self.root_dir, self.label_dir)
self.img_path = os.listdir(self.path)
self.transform = transforms.Compose([
transforms.Resize((224, 224)),
transforms.RandomRotation(20),
transforms.RandomHorizontalFlip(p=0.5),
transforms.ToTensor()
])
def __getitem__(self, item):
img_name = self.img_path[item]
img_item_path = os.path.join(self.root_dir, self.label_dir, img_name)
img = Image.open(img_item_path)
img = self.transform(img)
if(self.label_dir=="dog"):
label = 0
else:
label = 1
return img, label
def __len__(self):
return len(self.img_path)
root_dir = "dataset/train"
dog_label_dir = "dog"
cat_label_dir = "cat"
dog_dataset = mydataset(root_dir, dog_label_dir)
cat_dataset = mydataset(root_dir, cat_label_dir)
train_dataset = dog_dataset + cat_dataset
# img,label =dog_dataset[2]
# num_img = np.asarray(img)
# print(num_img)
# print(type(num_img))
# print(num_img.shape)
train_loader = torch.utils.data.DataLoader(train_dataset, batch_size=3, shuffle=True)
-
1.自定义数据集类 (mydataset): 自定义数据集类允许用户根据自己的数据集格式和需求来加载和处理数据。在这个例子中,mydataset 类用于加载和处理图像数据。
-
2.初始化方法 (init): 在初始化方法中,定义了数据集的根目录 root_dir 和标签目录 label_dir。self.path 是这两个目录的组合路径。self.img_path 是该路径下所有图像文件的列表。self.transform 是一个 transforms.Compose 对象,它定义了一系列图像预处理步骤,包括调整图像大小、随机旋转、随机水平翻转和转换为张量。
-
3.获取数据项方法 (getitem): 这个方法用于根据索引 item 获取数据集中的一个数据项。它首先根据索引找到对应的图像文件名 img_name,然后构建完整的图像路径 img_item_path。接着,使用 Image.open 打开图像文件,并应用之前定义的图像预处理步骤 self.transform。最后,根据图像的标签目录 self.label_dir 来确定图像的类别标签 label。
-
4.数据集长度方法 (len): 这个方法返回数据集中的数据项数量,即图像文件的数量。
-
5.数据集合并: 代码中创建了两个 mydataset 实例,分别对应于狗和猫的图像数据集。然后,通过简单的加法操作将两个数据集合并为一个 train_dataset。在 PyTorch 中,Dataset 类的实例可以直接相加,这会创建一个新的 Dataset 实例,其中包含了两个数据集的所有数据项。
-
6.数据加载器 (DataLoader): DataLoader 是 PyTorch 中用于批量加载数据的工具。在代码中,train_loader 是一个 DataLoader 实例,它将 train_dataset 中的数据以批量的形式加载出来。batch_size=3 表示每个批次包含 3 个数据项,shuffle=True 表示在每个 epoch 开始时随机打乱数据项的顺序。
这段代码定义了一个自定义数据集类,用于加载和处理图像数据,并创建了一个数据加载器来批量加载数据,以便于训练深度学习模型。
3.定义并训练模型
# 第3步:定义和训练模型
model = BPNetwork()
# criterion = torch.nn.MSELoss()
criterion = torch.nn.NLLLoss() # 定义loss函数
optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9) # 定义优化器
epochs = 20 # 一共训练15轮
for i in range(epochs):
running_loss = 0 # 本轮的损失值
for images, labels in train_loader:
# 前向传播获取预测值
output = model(images)
# 计算损失
loss = criterion(output, labels)
# 进行反向传播
loss.backward()
# 更新权重
optimizer.step()
# 清空梯度
optimizer.zero_grad()
# 累加损失
running_loss += loss.item()
# 一轮循环结束后打印本轮的损失函数
print("Epoch {} - Training loss: {}".format(i, running_loss / len(train_loader)))
# 第4步:测试模型
examples = enumerate(train_loader)
batch_idx, (imgs, labels) = next(examples)
print(imgs[0].shape)
fig = plt.figure()
for i in range(3):
t = torch.unsqueeze(imgs[i],dim=0)
logps = model(t) # 通过模型进行预测
probab = list(logps.detach().numpy()[0]) # 将预测结果转为概率列表。[0]是取第一张照片的10个数字的概率列表(因为一次只预测一张照片)
pred_label = probab.index(max(probab)) # 取最大的index作为预测结果
if(pred_label==0):
pre_value = "狗"
else:
pre_value = "猫"
img = torch.squeeze(imgs[i])
img = img.permute(1,2,0)
img = img.numpy()
plt.subplot(3, 2, i + 1)
plt.tight_layout()
plt.imshow(img, cmap='gray', interpolation='none')
plt.title("预测值: {}".format(pre_value))
plt.xticks([])
plt.yticks([])
plt.show()
#
选择损失函数 (NLLLoss): 损失函数用于衡量模型预测值与真实值之间的差异。这里选择了 NLLLoss(负对数似然损失),它通常用于多分类问题。
定义优化器 (SGD): 优化器负责调整模型的权重,以最小化损失函数。这里使用了随机梯度下降优化器,并设置了学习率和动量参数。
训练模型: 接下来,我们开始训练模型。训练过程分为多个轮次(epochs),在每个轮次中,模型会遍历整个训练数据集。对于每个批次的数据,模型会进行前向传播以获取预测值,计算损失,然后进行反向传播和权重更新。
打印训练损失: 每个轮次结束后,我们会计算并打印出该轮次的平均损失值,这有助于我们监控模型的训练进度。
测试模型: 训练完成后,我们使用训练数据集中的数据来测试模型的性能。我们从数据加载器中获取一批图像和标签,然后将图像通过模型进行预测。
预测结果: 模型输出的是每个类别的概率。我们选择概率最高的类别作为预测结果。
可视化测试结果: 最后,我们使用 matplotlib 库来可视化测试结果。对于每张图像,我们显示它的灰度图,并在图下方标注预测的类别(狗或猫)。
结果展示
总结
本项目通过构建一个BP神经网络模型,实现了猫狗图像的自动分类。首先,定义了数据集类mydataset,用于加载和处理图像数据,包括图像的预处理和标签的获取。接着,定义了BPNetwork模型,该模型包含多个全连接层和ReLU激活函数,以及一个输出层和LogSoftmax归一化层。在训练阶段,使用随机梯度下降(SGD)优化器和负对数似然损失函数(NLLLoss)来训练模型。通过多个轮次的训练,模型逐渐学习到区分猫和狗图像的特征。最后,在测试阶段,使用训练好的模型对图像进行预测,并通过matplotlib库来可视化预测结果,包括图像和预测的类别标签。整个项目展示了深度学习在图像处理领域的应用,以及如何通过构建和训练模型来解决实际问题。