自己的数据集的训练&识别遇到问题

训练集结果:

在这里插入图片描述
19 Train Loss: 0.0019 Train Acc: 0.9991
19 Val Loss: 0.0005 Val Acc: 1.0000
训练和验证耗费的时间14m40s

测试结果

在这里插入图片描述
单个种类的识别率

但是输入单张图片进行检测,结果是可以正确检测出来的
在这里插入图片描述
这是为什么呢?好困惑,有懂得大佬,还希望能帮帮忙!
起初以为是测试集和训练集的名称一样的原因,进行改名称之后,重新检测,结果还是没变。

完整代码

模型代码,以LeNet-5训练为例

#导入库,约定俗称的一些库
import torch
from torch import nn  ##导入nn,表示神经网络中的一些层,比如卷积层,池化层,激活函数等,之所以深度学习火的原因,不用太过于关注内部结构,就当成搭积木即可
from torchsummary import summary #summary展示模型的一个参数用的,前向传播的输入,输出特征图大小是多少


##约定俗称的定义网络的代码(类似于原材料)
class LeNet(nn.Module):   #定义一个类,nn.Module这样就可以用nn里面所有的网络层了
    def __init__(self):      #先进行初始化,告诉模型我们需要具备的都有了,剩下的就是开始搭建模型了。
        super(LeNet,self).__init__()      ##7-9行是约定俗成的网络结构的命令行,值得注意class LeNet和super LeNet 保持一致。

##开始定义第一层,后面相当于按照规划使用所需的材料,输入层不算,尽可能按照网络结构的顺序来定义每一层,不要改池化和卷积层的顺序
         #卷积层的名称可以自己去定义,以下面的c1为例,卷积函数用Conv2d(所需要传入的参数)
        #in_channels=1,表示为输入数据的通道数,1表示输入的为灰度图,3表示输入的为彩色图,out_channels=6,表示输出的通道数,由卷积核数量决定
        # kernel_size=5,代表卷积核的大小,padding=2,代表填充的大小,这里的步长为1,默认即可,不用去写
        self.c1 = nn.Conv2d(in_channels=3,out_channels=6,kernel_size=5,padding=2)
        #定义激活函数
        self.sig = nn.Sigmoid()
        #定义池化层,用的是平均池化,不改变通道数,只传入感受野大小,步长,填充等参数
        self.s2 = nn.AvgPool2d(kernel_size=2,stride=2)
        self.c3 = nn.Conv2d(in_channels=6,out_channels=16,kernel_size=5)  #步长为1,默认就不写,默认填充表示为0
        self.s4 = nn.AvgPool2d(kernel_size=2,stride=2)
         #定义平展层
        self.flatten = nn.Flatten()
         #定义全连接层,在pytorch中全连接层表示为linear(需要传入的输入的数据大小和输出的大小,即输入为平展之后的400个,输出为120个神经元)
        self.f5 = nn.Linear(400,120)   #写具体的数字会自动弹出in_features:和out_features:)
        self.f6 = nn.Linear(120,84)
        self.f7 = nn.Linear(84,4)

        ##定义前向传播并进行调用工具,self是python的语法,会自动弹出,如果没有,就要考虑是否出现问题,x表示为输入

    def forward(self,x):
           #输入数据经过了第一层卷积和激活函数
          x = self.sig(self.c1(x))
          #通过池化层
          x = self.s2(x)
          x = self.sig(self.c3(x))
          x = self.s4(x)
          x = self.flatten(x)
          x = self.f5(x)
          x = self.f6(x)
          x = self.f7(x)
          return x
    #主函数,搭建模型
if __name__=="__main__":  #这里告诉要开始使用主函数了,这个冒号:不能少
         #在有gpu的情况下,使用pytorch这个库来判别cuda是否激活了,如果激活了,返回cuda,如果没有则返回cpu
        device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
        #模型实例化之后放到设备中
        model = LeNet().to(device)
         #打印模型的详细内容
        print(summary(model,(1,28,28)))

模型训练代码

#加载数据,并进行可视化,loss,准确率等的展示
import copy
import time

import pandas as pd
import torch
from torchvision.datasets import ImageFolder     #导入库,专门处理自己的数据集
from torchvision import transforms
import torch.utils.data as Data
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt
#从model.py导入我们的模型
from model import LeNet

#训练集和验证集的定义,名称尽可能一目了然,用于处理训练集和验证集
def train_val_data_process():
    # 相对路径,反斜杠,注意需要大写,前面有一个r,数据大小224*224,格式为ToTensor()
    # 定义数据集的路径
    ROOT_TRAIN = r'data\train'
    normalize = transforms.Normalize([0.012, 0.024, 0.187], [0.008, 0.0178, 0.054])
    # 定义数据集处理方法变量
    train_transform = transforms.Compose([transforms.Resize((28, 28)), transforms.ToTensor()])
    # 加载数据集,传入数据集的路径,及处理数据的方法
    train_data = ImageFolder(ROOT_TRAIN, transform=train_transform)
    #把数据集划分为训练集和验证集=8:2
    train_data,val_data = Data.random_split(train_data,[round(0.8*len(train_data)),round(0.2*len(train_data))])
    #先放入数据,一批次数量(数据量比较小,6G运存应该是够的),shuffle是否打乱数据,进程是8,电脑垃圾的话,就改小一点
    train_dataloader = Data.DataLoader(dataset=train_data,
                                       batch_size=32,
                                       shuffle=True,
                                       num_workers=2)
    ##最好训练和验证集的操作是一样的
    val_dataloader = Data.DataLoader(dataset=val_data,
                                       batch_size=32,
                                       shuffle=True,
                                       num_workers=2)
    return train_dataloader , val_dataloader   #返回训练集和验证集

    #模型训练过程,传入模型,训练,验证集,训练的轮次
def train_model_process(model,train_dataloader,val_dataloader,num_epochs):
    #设定训练所用到的设备,有GPU用GPU,没有GPU用CPU
    #告诉设备情况,这一步用到了torch,就需要在前面进行导入,就是这么一个过程,一开始不知道都用那些库,写着需要了就进行导入
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    #使用Adam优化器,便于后续的参数进行更新的,lr表示为学习率,Adam可以理解是梯度下降法的一种优化改良
    optimizer = torch.optim.Adam(model.parameters(),lr=0.001)
    #定义损失函数,在分类问题中一般损失函数用交叉熵损失函数,回归一般用均方差损失函数,来利用损失值来更新参数w和b
    criterion = nn.CrossEntropyLoss()
    #将模型放入到训练设备中
    model = model.to(device)

    #复制当前模型的参数,w,b会有一个初始化的值,随着反向传播的进行,w,b在进行不断的更新。后续把 best_model_wts保存下来(也即是权重)后续测试时可以进行加载

    best_model_wts = copy.deepcopy(model.state_dict())
    ##初始化参数
    ##最高准确度,初始化为0,后续会覆盖掉
    best_acc = 0.0

    #训练集损失值列表,改的地方
    # 训练集损失值列表
    train_loss_all = []
    # 验证集损失值列表
    val_loss_all = []
    # 训练集精度值列表
    train_acc_all = []
    # 验证集精度值/准确度列表
    val_acc_all = []
    #保存当前时间,方便知道一轮会用时多久,time.time()不要自己全部手敲,会报错,就time.等弹出选择即可
    since = time.time()

    #利用循环进行训练,打印信息的阶段
    for epoch in range(num_epochs):
        print("Epoch{}/{}".format(epoch,num_epochs-1))   #减1的原因在于,是从0开始99结束,共100轮(如果以100为例情况下)
        print("-"*10)

    #初始化参数,方便每一轮的开始值都是0,从而可以计算每一轮的loss和准确率,需要计算平均loss值,就需要定义样本数量
        #训练集损失函数
        train_loss = 0.0
        #训练集准确度
        train_corrects = 0
        # 验证集损失函数
        val_loss = 0.0
        # 验证集准确度
        val_corrects = 0

        #定义该轮次的训练集样本数量
        train_num = 0
        #验证集样本数量
        val_num = 0

        #取数据过程,模型和数据需要放在同一个设备,否则会出错
        #对每一个mini-batch训练和计算
        for step, (b_x, b_y) in enumerate(train_dataloader):
            #将特征放入到训练设备中
            b_x = b_x.to(device)
            #将标签值放入到训练设备中
            b_y = b_y.to(device)
            #设置模型的训练模式
            model.train()

            #前向传播过程,输入为一个batch,输出为一个batch中对应的预测,output不是最终的结果而是一个向量
            output = model(b_x)

            #使用softmax取概率最大的作为标签,查找每一行中最大值对应的行标(类别)
            pre_lab = torch.argmax(output,dim=1)
            #利用模型的输出和标签来计算损失,不是通过softmax之后的结果和标签计算哦
            loss = criterion(output,b_y)

            #每一轮开始前将梯度初始化为0,防止梯度累计从而对参数更新产生干扰,只有这样每一轮结束之后才能更新参数w和b
            optimizer.zero_grad()
            #反向传播计算
            loss.backward()
            #根据网络反向传播的梯度信息来更新网络参数,以起到降低loss值函数计算值的作用,利用Adam优化器进行更新参数
            optimizer.step()
            #对损失函数进行累加
            train_loss += loss.item() * b_x.size(0)
            #如果预测正确,则准确度train_corrects+1
            train_corrects += torch.sum(pre_lab == b_y.data)
            #当前该轮次用于训练的样本数量
            train_num += b_x.size(0)

            #模型验证代码部分撰写,基本和训练过程是差不多的操作,是不参与模型的训练,单纯计算loss和准确度,没有反向传播过程
            # 对每一个mini-batch训练和计算,直到最后一批次结束,意味着一轮结束。
        for step, (b_x, b_y) in enumerate(val_dataloader):
            #将特征放入到验证设备中
             b_x = b_x.to(device)
           #将标签值放入到验证设备中
             b_y = b_y.to(device)
            #设置模型的评估模式
             model.eval()
            #前向传播过程,输入为一个batch,输出为一个batch中对应的预测
             output = model(b_x)
              # 使用softmax取概率最大的作为标签,查找每一行中最大值对应的行标(类别)
             pre_lab = torch.argmax(output, dim=1)
             # 利用模型的输出和标签来计算损失,不是通过softmax之后的结果和标签计算哦
            ##相比较于训练过程,是没有反向传播,梯度更新,参数更新的过程。
             loss = criterion(output, b_y)
              #对损失函数进行累加,批次的平均loss*批次数量
             val_loss += loss.item() * b_x.size(0)
             #如果预测正确,则准确度train_corrects+1
             val_corrects += torch.sum(pre_lab == b_y.data)
             #当前用于评估/评估的样本数量
             val_num += b_x.size(0)

              #计算并保存每一次迭代(轮次epoch?)的平均loss和准确度,后续需要进行打印或者绘图
        train_loss_all.append(train_loss / train_num)
        #计算并保存训练集的准确率
        train_acc_all.append(train_corrects.double().item() / train_num)
        # 计算并保存验证集的loss值
        val_loss_all.append(val_loss / val_num)
        # 计算并保存验证集的准确率
        val_acc_all.append(val_corrects.double().item() / val_num)

              ##将上述结果进行打印[-1]表示取当前轮次的数值,print缩进的话,把内部训练的每一次都会进行打印的。
        print('{} Train Loss: {:.4f} Train Acc: {:.4f}'.format(epoch,train_loss_all[-1],train_acc_all[-1]))
        print('{} Val Loss: {:.4f} Val  Acc: {:.4f}'.format(epoch,val_loss_all[-1], val_acc_all[-1]))

        #寻找最高准确度(当前准确度和最佳准确度进行比较,有点排序算法的感觉)
        if val_acc_all[-1] > best_acc:
              #保存当前的最高准确度
               best_acc = val_acc_all[-1]
               #在当前最高的准确度情况下,保存对应的模型权重
               best_model_wts = copy.deepcopy(model.state_dict())
            #训练耗费的时间
        time_use = time.time() - since
        print("训练和验证耗费的时间{:.0f}m{:.0f}s".format(time_use//60,time_use%60))

    #选择最优参数
    #加载最高准确率下的模型参数,并进行保存到一个文件地址,项目文件夹下产生一个best_model.pth,.pth是权重的独特后缀
    #model.load_state_dict(best_model_wts),这行代码是没有用的。下面的两种保存权重方法都是可以的
    torch.save(best_model_wts, 'F:/LeNet/best_model.pth')

    #将训练的数值保存为一种文件格式,最重要的就是训练完之后,会保存最优模型的权重。
    train_process = pd.DataFrame(data={"epoch":range(num_epochs),
                                       "train_loss_all":train_loss_all,
                                       "val_loss_all":val_loss_all,
                                       "train_acc_all":train_acc_all,
                                       "val_acc_all": val_acc_all})
    return train_process
#2024年7月22日继续学习
##定义一个绘制loss和acc的绘制图的函数,将train_process作为输入传进去
def matplot_acc_loss(train_process):
    plt.figure(figsize=(12,4))     #设置图形的大小
    plt.subplot(1,2,1)        #子图可以按照matlab的画法进行理解
    plt.plot(train_process["epoch"], train_process.train_loss_all, 'ro-' , label = "train loss")    #绘制训练集损失曲线,并加上曲线等设置
    plt.plot(train_process["epoch"], train_process.val_loss_all, 'bs-', label="val loss")  # 绘制验证集损失曲线,并加上曲线等设置
    plt.legend()  #标签
    plt.xlabel("epoch")
    plt.ylabel("loss")

    plt.subplot(1, 2, 2)  # 子图可以按照matlab的画法进行理解
    plt.plot(train_process["epoch"], train_process.train_acc_all, 'ro-', label="train acc")  # 绘制训练集损失曲线,并加上曲线等设置
    plt.plot(train_process["epoch"], train_process.val_acc_all, 'bs-', label="val acc")  # 绘制验证集损失曲线,并加上曲线等设置
    plt.legend()  # 图例
    plt.xlabel("epoch")
    plt.ylabel("acc")
    plt.legend()  # 图例
    plt.show()    #绘制

#对数据开始训练
#主函数,开始运行所有的代码   注意点1:双等号。2.要有冒号:
if __name__ == "__main__":
     #将模型进行实例化,如果后续是别的模型呢?from model import LeNet,先进行搭建我们的model.py,进而将model导入进去,然后在下面进行实例化。将模型放到训练代码就可以了。有个别的模型可能数据的预处理需要改一点。
     LeNet = LeNet()
     #加载数据集,分为训练集和验证集
     train_dataloader,val_dataloader = train_val_data_process()
     #将训练集,验证集,模型,和训练次数放进训练代码中,
     train_process = train_model_process(LeNet,train_dataloader,val_dataloader,20)    #自己进行设定训练的次数,假如这里的训练次数是20轮,并保留中间值
     ##绘制损失和准确率的图形
     matplot_acc_loss(train_process)

模型测试。模型推理及单个图片的识别

import torch   #用不用都先导入进来
import torch.utils.data as Data    #导入数据处理的库
from torchvision import transforms
from torchvision.datasets import ImageFolder     #导入库,专门处理自己的数据集
from model import LeNet   #导入需要测试的模型
from PIL import Image   ##导入图片的一个库
##数据就处理好了。
##导入数据进行模型的测试,从模型训练的代码拿一部分,改改即可,batch_size=1,希望是一张一张的去进行测试,不需要什么进程,改为num_workers=0即可
def test_data_process():
    ##数据加载进来,transforms.Compose((transforms.Resize(size=28),大小设置按照模型的输入要求来
    # 相对路径,反斜杠,注意需要大写,前面有一个r,数据大小224*224,格式为ToTensor()
    # 定义数据集的路径
    ROOT_TEST = r'data\test'
    # 定义归一化的方法
    normalize = transforms.Normalize([0.012, 0.024, 0.187], [0.008, 0.0178, 0.054])
    # 定义数据集处理方法变量,大小224*224,ToTensor格式,并且进行正态分布归一化处理
    test_transform = transforms.Compose([transforms.Resize((28, 28)), transforms.ToTensor(), normalize])
    # 加载数据集,传入数据集的路径,及处理数据的方法
    test_data = ImageFolder(ROOT_TEST, transform=test_transform)

    #先放入数据,一批次数量(数据量比较小,6G运存应该是够的),shuffle是否打乱数据,进程是8,电脑垃圾的话,就改小一点
    test_dataloader = Data.DataLoader(dataset=test_data,
                                       batch_size=1,
                                       shuffle=True,
                                       num_workers=0)

    return test_dataloader

##测试,只需要导入模型和数据集,得到一个测试的结果即可
def test_model_process(model,test_dataloader):
    #设定测试用到的设备,有GPU用GPU,没有GPU用CPU
    device = "cuda" if torch.cuda.is_available() else "cpu"
    #将模型放入到测试设备当中
    model = model.to(device)
    #初始化模型参数,准确度即可,虽然已经知道测试的数据集有10000张,但为了后续不知道张数的情况下,也能进行测试。
    test_corrects = 0.0
    test_num = 0

    ##开始模型的测试,不存在反向传播,因此将梯度置为0,只有前向传播,不计算梯度,从而节省内存,加快运行速度
    with torch.no_grad():
        #直接一批次一批次的获取就好了。
        for test_data_x,test_data_y in test_dataloader:
            #将特征放入到测试设备中
            test_data_x = test_data_x.to(device)
            #将标签也放入到测试设备中
            test_data_y = test_data_y.to(device)
            #模型测试状态
            model.eval()
            #前向传播过程,输入为测试数据集,输出为对每个样本的预测值,它其实是一个10*1的矩阵,并不能看出结果来,需要经过一个softmax得到结果。最大概率对应的下标
            output = model(test_data_x)
             #预测的类别,查找每一行中最大值对应的行标
            pre_lab = torch.argmax(output,dim=1)
            #预测的结果和标签一样+1,如果预测正确,则 test_corrects加1,直到循环结束
            test_corrects += torch.sum(pre_lab == test_data_y.data)
            #将所有的测试样本进行累加
            test_num += test_data_x.size(0)

    #计算测试准确率,不在循环里面哦,注意缩进
    test_acc = test_corrects.double().item() / test_num

    print("测试的准确率为:" ,  test_acc)


#主函数进行运行程序
if __name__ == "__main__" :
    #加载模型
    model = LeNet()
    #torch.load('best_model.pth')加载训练好的模型参数,权重以字符串的形式, model.load_state_dict模型训练化,将实例化之后的模型加载上权重,规定的做法

    model.load_state_dict(torch.load('best_model.pth'))
    #导入测试集的数据集,加载测试数据
    test_dataloader = test_data_process()
    #加载模型测试的函数
    test_model_process(model, test_dataloader)
##进行模型推理过程
    #设定测试所用到的设备,有GPU用GPU,没有就用CPU
    device = "cuda" if torch.cuda.is_available() else 'cpu'
    model = model.to(device)
    # #使用列表取值的形式设置类别
    classes= ['gs','m2p0','m2p1','m3p0']
    # #测试部分,没有梯度,不存在反向传播
    # with torch.no_grad():
    #     for b_x,b_y in test_dataloader:
    #         b_x = b_x.to(device)
    #         b_y = b_y.to(device)
    #
    #         #设置模型为验证模式
    #         model.eval()
    #         output = model(b_x)
    #         ##10个通过神经元得到数值,经过softmax函数获得对应的概率,从而获得最大值的下标
    #         #ToTensor()将数据格式转换为张量的形式,只有张量的数据格式,才可以去做梯度运算,反向传播等
    #         pre_lab = torch.argmax(output, dim=1)
    #         # pre_lab.item()表示将张量中的数值取出来,以便做其他的操作(比如通过列表设置的类别,取整型方便运算,张量形式是没法进行的。
    #         result = pre_lab.item()
    #         label = b_y.item()
    #         #不仅打印数值,更应该打印出对应的类别,推理之前设置有列表,用下标指向类别
    #         print("预测值:", classes[result], "------", "真实值:",classes[label])

    # #单个图片的推理D
    # image = Image.open('Image1.jpg')
    # # 对图片进行处理
    # normalize = transforms.Normalize([0.162, 0.151, 0.138], [0.058, 0.052, 0.048])
    # # 定义数据集处理方法变量,大小224*224,ToTensor格式,并且进行正态分布归一化处理
    # test_transform = transforms.Compose([transforms.Resize((28, 28)), transforms.ToTensor(), normalize])
    # image = test_transform(image)
    # ##打印图的格式
    # # print(image.shape)
    # # 添加批次维度之后的数据,才可以送到模型当中去
    # image = image.unsqueeze(0)
    # # print(image.shape)   查看图片的输入格式情况
    # # 进行单张图片的模型推理
    # with torch.no_grad():
    #     # 模型已经放到设备上,并且打开了模型的验证模式
    #     model.eval()
    #     # 将输入数据放到设备中
    #     image = image.to(device)
    #     output = model(image)
    #     pre_lab = torch.argmax(output, dim=1)
    #     # 将数据从tensor格式转化为数字形式
    #     result = pre_lab.item()
    # print("预测值:", classes[result])

归一化处理程序

from PIL import Image
import os
import numpy as np

# 文件夹路径,包含所有图片文件
folder_path = 'data'

# 初始化累积变量
total_pixels = 0
sum_normalized_pixel_values = np.zeros(3)  # 如果是RGB图像,需要三个通道的均值和方差

# 遍历文件夹中的图片文件
for root, dirs, files in os.walk(folder_path):
    for filename in files:
        if filename.endswith(('.jpg', '.jpeg', '.png', '.bmp')):  # 可根据实际情况添加其他格式
            image_path = os.path.join(root, filename)
            image = Image.open(image_path)
            image_array = np.array(image)

            # 归一化像素值到0-1之间
            normalized_image_array = image_array / 255.0

            # print(image_path)
            # print(normalized_image_array.shape)
            # 累积归一化后的像素值和像素数量
            total_pixels += normalized_image_array.size
            sum_normalized_pixel_values += np.sum(normalized_image_array, axis=(0, 1))

# 计算均值和方差
mean = sum_normalized_pixel_values / total_pixels


sum_squared_diff = np.zeros(3)
for root, dirs, files in os.walk(folder_path):
    for filename in files:
        if filename.endswith(('.jpg', '.jpeg', '.png', '.bmp')):
            image_path = os.path.join(root, filename)
            image = Image.open(image_path)
            image_array = np.array(image)
            # 归一化像素值到0-1之间
            normalized_image_array = image_array / 255.0
            # print(normalized_image_array.shape)
            # print(mean.shape)
            # print(image_path)

            try:
                diff = (normalized_image_array - mean) ** 2
                sum_squared_diff += np.sum(diff, axis=(0, 1))
            except:
                print(f"捕获到自定义异常")
            # diff = (normalized_image_array - mean) ** 2
            # sum_squared_diff += np.sum(diff, axis=(0, 1))

variance = sum_squared_diff / total_pixels

print("Mean:", mean)
print("Variance:", variance)

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值