全连接神经网络实现猫狗识别(基于Pytorch)

昨天项目答辩,一天讲了俩PPT
但还是因为内向,被要求每周PPT汇报一次
性格内向真要变得外向起来才行吗

import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
from torchvision import datasets, transforms
import os
import numpy as np
import cv2
from torch.utils.tensorboard import SummaryWriter

# 可视化进度
from tqdm import tqdm

root = r"D:\Cat_Dog"
label_dict = {
    
    'cat': 0, 'dog': 1}


# 全连接网络模型
class Net(nn.Module):
    def __init__(self):
        super().__init__()
        # 网络构造器
        self.layer = nn.Sequential(
            # 总共八层
            nn.Linear(1 * 32 * 32, 1024), nn.ReLU(),
            nn.Linear(1024, 512), nn.ReLU(),
            nn.Linear(512, 256), nn.ReLU(),
            nn.Linear(256, 128), nn.ReLU(),
            nn.Linear(128, 64), nn.ReLU(),
            nn.Linear(64, 32), nn.ReLU(),
            nn.Linear(32, 12), nn.ReLU(),
            nn.Linear(12, 2),
            nn.Softmax(dim=1)
        )

    def forward(self, x):
        return self.layer(x)


# 数据集
# 从先前以标签名分好的文件夹里读取数据
class CATDOG_Dataset(Dataset):
    def __init__(self, root, is_train=True):
        super().__init__()
        # 数据集
        self.dataset = []
        # 判断是测试集还是训练集
        train_or_test = "train" if is_train else "test"
        # 路径
        path = f"{
      
      root}//{
      
      train_or_test}"
        for label in os.listdir(path):  # 0, 1,    0 猫, 1 狗
            for img_path in os.listdir(f"{
      
      path}//{
      
      label}"):  # 数据集的每张图片名称
                # 将0--9 文件夹里的图片 以 (图片路径, 标签) 的形式 存放在dataset列表里
                self.dataset.append((f"{
      
      path}//{
      
      label}//{
      
      img_path}", label))
        print(len(self.dataset))

    # 存放数据和标签的 列表长度
    def __len__(self):
        return len(self.dataset)

    def __getitem__(self, index):
        # 路径读取成图像, 展平, 归一化
        data = self.dataset[index]
        # opencv
        # data[0] 图片路径
        img = cv2.imread(data[0], 0)  # 0 代表读取灰度图像
        img = cv2.resize(img, (32, 32))
        img = img.reshape(-1)  # -1 就是将数据降成一维
        img = img / 255
        # 标签  ---> one-hot
        one_hot = np.zeros(2)
        one_hot[label_dict[data[1]]] = 1
        return np.float32(img), np.float32(one_hot)


# 训练和测试
class Trainer:
    def __init__(self):
        self.device = "cuda"
        # 网络 (模型)
        self.net = Net()
        self.net.to(self.device)

        # 训练集的数据封装
        self.train_dataset = CATDOG_Dataset(root=root, is_train=True)
        self.train_loader = DataLoader(dataset=self.train_dataset, batch_size=1000, shuffle=True)

        # 测试集数据封装
        self.test_dataset = CATDOG_Dataset(root=root, is_train=False)
        self.test_loader = DataLoader(dataset=self.test_dataset, batch_size=1000, shuffle=True)

        # 模型训练完成,得到h,loss,反向更新,优化器去优化模型的权重
        self.opt = torch.optim.Adam(self.net.parameters(), lr=0.001)
        print(f"训练集批次数:{
      
      len(self.train_loader)}")
        print(f"测试集批次数:{
      
      len(self.test_loader)}")
        # 定义logs
        self.summerWriter = SummaryWriter("logs")

    # 训练
    def train(self):
        for epoch in range(10000):  # 10000训练的轮次
            sum_loss = 0
            for i, (img, label) in enumerate(tqdm(self.train_loader)):
                # 模式操作,打开训练
                self.net.train()
                # 数据也要放入 cuda或者cpu
                img, label = img.to(self.device), label.to(self.device)
                # 前向计算
                h = self.net(img)
                # 损失值loss  均方差
                loss = torch.mean((h - label) ** 2)
                # 清空梯度
                self.opt.zero_grad()
                # 反向跟新
                loss.backward()  # PyTorch 中的自动求导功能计算损失函数 loss
                self.opt.step()  # 执行优化器的一步更新操作
                sum_loss = sum_loss + loss  # 每一轮次的损失
            if epoch % 40 == 0:
                torch.save(self.net.state_dict(), f"params/{
      
      epoch}.pth")
            # 每一轮模型的平均损失
            avg_loss = sum_loss / len(self.train_loader)
            # 训练损失log
            self.summerWriter.add_scalar("训练损失", avg_loss, epoch)
            print(f"第{
      
      epoch}轮的损失:{
      
      avg_loss.item()}")

    def test(self):
        # 读取存放模型目录params 里的最后一个模型
        self.net.load_state_dict(torch.load(r"params//" + os.listdir(r"params")[-1]))
        for epoch in range(10000):
            sum_score = 0
            for i, (img, label) in enumerate(tqdm(self.test_loader)):
                # 开启测试模式
                self.net.eval()
                # 将训练集的图片和标签放入网络中测试
                img, label = img.to(self.device), label.to(self.device)
                h = self.net(img)
                a = torch.argmax(h, dim=1)
                # 获取one--hot编码为1 的索引位置
                b = torch.argmax(label, dim=1)
                score = torch.mean(torch.eq(a, b).float())
                sum_score = sum_score + score
            avg_score = sum_score / len(self.test_loader)
            self.summerWriter.add_scalar("测试得分", avg_score, epoch)
            print(f"第{
      
      epoch}轮的得分:{
      
      avg_score}")


if __name__ == '__main__':
    trainer = Trainer()
    trainer.train()
    # trainer.test()

完全的全连接囿于模型问题,准确率只能到70

正常情况都是用卷积,下次再写

猜你喜欢

转载自blog.csdn.net/weixin_44659309/article/details/130125578