BP实战猫狗分类数据集
1,准备和任务
(1)下载猫狗图片
https://www.microsoft.com/en-us/download/confirmation.aspx?id=54765
(2)准备好目录
(3)测试读取,路径
import numpy as np
import torch
# 数据准备
# 由于下载的图片大小不一,需要统一大小为 224 * 224
from PIL import Image
import os
#尝试读取(绝对路径)
img = Image.open("D:\\pytorch\\pytorch_study\\第五周\\train\\cat\\0.jpg") # " \ "要双
print(img)
print(type(img))
# img.show()
#图像类型转为numpy类型
num_img = np.asarray( img )
print(num_img)
print(num_img.shape) # 375(gao) * 500(kuan) 3:通道
print(type(num_img))
# 模型里面不认字符,因此需要把标签改成数字
cls = "cat"
if cls == "cat":
label = 0
else
label = 1
运行结果
2,实战
1数据的准备:
class mydataset(Dataset):
def __init__(self, data_root, data_label):
self.data_root = data_root # 路径
self.data_label = data_label # 标签
self.path = os.path.join(data_root, data_label) # 把路径和标签联系起来
self.img_path = os.listdir(self.path) # 转为列表类型
self.transform = transforms.Compose([
transforms.Resize((224, 224)), # 变大小
transforms.ToTensor()
])
# img转为tensor类型
def __getitem__(self, item):
img_name = self.img_path[item] # 第item个图像
img_item_path = os.path.join(self.path, img_name) # 图像路径
img = Image.open(img_item_path)
img = self.transform(img)
if self.data_label == "cat":
label = 1
else:
label = 0
return img, label
def __len__(self):
lenth = len(self.img_path)
return lenth
- 定义了
mydataset
类继承自Dataset---
用于处理和加载图像数据。 - 这个类将图像转换为
224x224
像素大小,然后转换为PyTorch的Tensor
格式。 - 数据集被分为
cat
和dog
。
2构建BP模型:
class BPnetwork(torch.nn.Module):
def __init__(self):
super(BPnetwork, self).__init__()
self.linear1 = torch.nn.Linear(224 * 224 * 3, 128)
self.ReLU1 = torch.nn.ReLU()
self.linear2 = torch.nn.Linear(128, 64)
self.ReLU2 = torch.nn.ReLU()
self.linear3 = torch.nn.Linear(64, 32)
self.ReLU3 = torch.nn.ReLU()
self.linear4 = torch.nn.Linear(32, 10)
self.ReLU4 = torch.nn.ReLU()
self.linear5 = torch.nn.Linear(10, 2)
self.softmax = torch.nn.LogSoftmax(dim=1)
- 输入层接收
224x224x3
的图像像素值(RGB),经过多个全连接层(linear1
到linear5
)和ReLU激活函数,最后通过Softmax函数输出两个类别的预测概率。
3数据加载:
trainloader = torch.utils.data.DataLoader(train_dataset, batch_size=16, shuffle=True)
testloader = torch.utils.data.DataLoader(test_dataset, batch_size=16, shuffle=True)
Batch Size(批大小)是机器学习和深度学习中的一个重要超参数,它指的是在每次迭代(iteration)中用于训练模型的样本集合的大小。在训练过程中,模型不会一次性处理整个数据集,而是分成若干个批次(batch)进行学习,每个批次包含特定数量的样本。Batch Size的选择直接影响到模型的训练速度、内存使用、梯度估计的准确性以及最终的模型性能
- 使用
DataLoader
从train_dataset
和test_dataset
加载数据,设置batch_size
为16。
4模型训练:
model = BPnetwork()
criterion = torch.nn.NLLLoss() # loss函数
optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9) # 优化器
eopchs = 35
for i in range(eopchs):
sumloss = 0
# print(1)
for images, lables in trainloader:
# 前向传播-预测值
# print(1)
ypre = model(images)
# 计算loss
loss = criterion(ypre, lables)
# 反向传播
loss.backward()
# 更新权重
optimizer.step()
optimizer.zero_grad()
# 损失累加
sumloss += loss.item()
print("Epoch {}, Loss: {}".format(i + 1, sumloss / len(trainloader)))
- 损失函数(
NLLLoss
)。NLLLoss
的计算等价于计算交叉熵损失,但不包括 Softmax 层,因此在使用NLLLoss
时,需要确保输入是经过 Softmax 处理的 - 优化器SGD。PyTorch中实现随机梯度下降(SGD)优化算法的类,根据损失函数相对于模型参数的梯度来更新这些参数,
model.parameters
:这是一个迭代器,包含模型中所有需要训练的参数。momentum
:动量(Momentum)因子,用于加速梯度下降并减少震荡。动量通过累积梯度信息来更新参数,类似于物理中的动量效应。 35
个周期(eopchs
)训练模型,每轮遍历所有训练数据,计算loss,进行反向传播和权重更新,损失累加。
5模型测试 :
examples = enumerate(testloader)
batch, (images, lables) = next(examples)
fig = plt.figure()
for i in range(16):
t = torch.unsqueeze(images[i], dim=0) # 增加一个维度,使t的形状与模型期望的形状相匹配
logps = model(t)
probab = list(logps.detach().numpy()[0]) # logps.detach()从计算图中分离出logps,确保后续的操作不会影响到模型的梯度。
pred_label = probab.index(max(probab))
if pred_label == 1:
label = "猫"
else:
label = "狗"
# 接着,.numpy()将张量转换为NumPy数组。[0]取出第一个元素(因为t是一个批次大小为1的数据),
# 最后list()将这个元素转换为一个列表。此时,probab是一个包含所有类别概率的列表。
# 找出probab列表中概率最大的元素的索引,这个索引即代表模型预测的类别标签。
img = torch.squeeze(images[i]) # 移除大小为1的维度,让它回到原来的形状
img1 = img.permute(1, 2, 0) # 将图像的维度从(channels, height, width)调整为(height, width, channels)
img1 = img1.numpy()
plt.subplot(4, 4, i + 1) # 创建一个2*2的子图网格,并选择第i+1个子图作为当前绘图区域
plt.tight_layout() # 自动调整子图参数,使之填充整个图像区域并尽量减少重叠
plt.imshow(img1, cmap='gray', interpolation='none')
plt.title(f"预测值:{label}")
plt.xticks([]) # 设置x轴和y轴的刻度标签为空
plt.yticks([])
plt.show()
通过testloader
取出一个批次的数据,对图像预测,展示预测结果。返回数据集中的样本和标签enumerate
函数来迭代testloader
数据加载器。返回的每个元素是一个元组,包含当前样本的索引和样本本身next
函数来获取examples
迭代器的下一个元素。元组,包含索引和数据。在这个元组中,batch
变量接收索引值,而(images, labels)
变量接收数据和标签。images
和labels
分别代表当前批次的输入图像和标签- 使用
matplotlib
库可视化测试图像的预测结果,显示其预测类别(猫或狗)。
完整代码:
import numpy as np
import torch
# 数据准备
# 由于下载的图片大小不一,需要统一大小为 224 * 224
from PIL import Image
import os
from torch.utils.data import Dataset
from torchvision.transforms import transforms
from matplotlib import pyplot as plt
import matplotlib
matplotlib.rcParams['font.sans-serif'] = ['KaiTi']
# 构造
class mydataset(Dataset):
def __init__(self, data_root, data_label):
self.data_root = data_root # 路径
self.data_label = data_label # 标签
self.path = os.path.join(data_root, data_label) # 把路径和标签联系起来
self.img_path = os.listdir(self.path) # 转为列表类型
self.transform = transforms.Compose([
transforms.Resize((224, 224)), # 变大小
transforms.ToTensor()
])
# img转为tensor类型
def __getitem__(self, item):
img_name = self.img_path[item] # 第item个图像
img_item_path = os.path.join(self.path, img_name) # 图像路径
img = Image.open(img_item_path)
img = self.transform(img)
if self.data_label == "cat":
label = 1
else:
label = 0
return img, label
def __len__(self):
lenth = len(self.img_path)
return lenth
# 2,构建BP模型
class BPnetwork(torch.nn.Module):
def __init__(self):
super(BPnetwork, self).__init__()
self.linear1 = torch.nn.Linear(224 * 224 * 3, 128)
self.ReLU1 = torch.nn.ReLU()
self.linear2 = torch.nn.Linear(128, 64)
self.ReLU2 = torch.nn.ReLU()
self.linear3 = torch.nn.Linear(64, 32)
self.ReLU3 = torch.nn.ReLU()
self.linear4 = torch.nn.Linear(32, 10)
self.ReLU4 = torch.nn.ReLU()
self.linear5 = torch.nn.Linear(10, 2)
self.softmax = torch.nn.LogSoftmax(dim=1)
def forward(self, x):
x = x.reshape(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.ReLU3(x)
x = self.linear4(x)
x = self.ReLU4(x)
x = self.linear5(x)
x = self.softmax(x)
return x
data_root = "第五周/train"
test_dir = "第五周/train"
data_label_cat = "cat"
data_label_dog = "dog"
catdataset = mydataset(data_root, data_label_cat)
dogdataset = mydataset(data_root, data_label_dog)
train_dataset = catdataset + dogdataset
cat_test = mydataset(test_dir, data_label_cat)
dog_test = mydataset(test_dir, data_label_dog)
test_dataset = cat_test + dog_test
# 数据加载器
trainloader = torch.utils.data.DataLoader(train_dataset, batch_size=16, shuffle=True)
testloader = torch.utils.data.DataLoader(test_dataset, batch_size=16, shuffle=True)
# 训练模型
model = BPnetwork()
criterion = torch.nn.NLLLoss() # loss函数
optimizer = torch.optim.SGD(model.parameters(), lr=0.001, momentum=0.9) # 优化器
eopchs = 35
for i in range(eopchs):
sumloss = 0
# print(1)
for images, lables in trainloader:
# 前向传播-预测值
# print(1)
ypre = model(images)
# 计算loss
loss = criterion(ypre, lables)
# 反向传播
loss.backward()
# 更新权重
optimizer.step()
optimizer.zero_grad()
# 损失累加
sumloss += loss.item()
print("Epoch {}, Loss: {}".format(i + 1, sumloss / len(trainloader)))
# 测试模型
print(1)
examples = enumerate(testloader)
batch, (images, lables) = next(examples)
fig = plt.figure()
for i in range(16):
t = torch.unsqueeze(images[i], dim=0) # 增加一个维度,使t的形状与模型期望的形状相匹配
logps = model(t)
probab = list(logps.detach().numpy()[0]) # logps.detach()从计算图中分离出logps,确保后续的操作不会影响到模型的梯度。
pred_label = probab.index(max(probab))
if pred_label == 1:
label = "猫"
else:
label = "狗"
# 接着,.numpy()将张量转换为NumPy数组。[0]取出第一个元素(因为t是一个批次大小为1的数据),
# 最后list()将这个元素转换为一个列表。此时,probab是一个包含所有类别概率的列表。
# 找出probab列表中概率最大的元素的索引,这个索引即代表模型预测的类别标签。
img = torch.squeeze(images[i]) # 移除大小为1的维度,让它回到原来的形状
img1 = img.permute(1, 2, 0) # 将图像的维度从(channels, height, width)调整为(height, width, channels)
img1 = img1.numpy()
plt.subplot(4, 4, i + 1) # 创建一个2*2的子图网格,并选择第i+1个子图作为当前绘图区域
plt.tight_layout() # 自动调整子图参数,使之填充整个图像区域并尽量减少重叠
plt.imshow(img1, cmap='gray', interpolation='none')
plt.title(f"预测值:{label}")
plt.xticks([]) # 设置x轴和y轴的刻度标签为空
plt.yticks([])
plt.show()
运行结果: