cnn 参数调优

CNN 训练模型时,需要调优,与哪些因素有关

学习率(Learning Rate):

学习率决定了参数在每次迭代中的更新幅度。过大的学习率可能导致震荡或发散,而过小的学习率可能导致训练过慢。通常需要通过尝试不同的学习率来找到最佳值。

批量大小(Batch Size):

批量大小定义了每次更新模型参数时使用的样本数量。较大的批量大小可能加速训练,但会增加内存需求。较小的批量大小可能提高模型的泛化能力,但可能导致训练过程更加不稳定。

迭代次数(Epochs):

迭代次数表示整个训练数据集被模型处理的次数。增加迭代次数通常可以提高模型性能,但过多的迭代可能导致过拟合。
权重初始化(Weight Initialization):

初始化权重的方式可能影响模型的收敛速度和性能。一些常见的初始化方法包括随机初始化、Xavier/Glorot初始化等。

优化器的选择:

不同的优化器对模型的性能有显著影响。常见的优化器包括随机梯度下降(SGD)、Adam、RMSprop等。每个优化器都有其优点和缺点,因此选择合适的优化器非常重要。

正则化(Regularization):

正则化方法,如L1正则化和L2正则化,可以帮助防止过拟合。它们通过向损失函数添加正则化项来对模型参数进行惩罚。

数据增强(Data Augmentation):

数据增强是通过对训练数据进行随机变换来扩充数据集。这有助于提高模型的泛化能力,减轻过拟合问题。

网络架构:

CNN的网络架构包括卷积层、池化层、全连接层等。调整网络深度、卷积核大小、池化方式等超参数可能对性能产生重要影响。

学习率调度(Learning Rate Scheduling):

逐渐降低学习率可以帮助模型更好地收敛。学习率调度方法包括按步骤调整、余弦退火等。

批量归一化(Batch Normalization):

批量归一化可以加速训练过程并提高模型的泛化能力。它在每个批次的数据上进行归一化,有助于缓解梯度消失和爆炸问题。
在调优过程中,往往需要进行反复尝试和实验,以找到最佳的超参数组合,从而获得性能良好的CNN模型。

根据参数调优的类别,分析调优效果

先对上一篇文章的代码做如下修改

创建数据生成器

import time

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
import numpy as np
import matplotlib.pyplot as plt

# 数据生成器
class DataGenerator(object):
    def __init__(self):
        super(DataGenerator, self).__init__()
        self.trainData = []
        self.trainLabels = []
        self.valData = []
        self.valLabels = []

    # 检查tx,ty坐标象限分类
    def check_quadrant(self, tx, ty):
        if (tx > 0) and (ty > 0):
            return 0  # 第一象限
        if (tx < 0) and (ty > 0):
            return 1  # 第二象限
        if (tx < 0) and (ty < 0):
            return 2  # 第三象限
        if (tx > 0) and (ty < 0):
            return 3  # 第四象限
        # 浮点数比较
        if (abs(tx) > 0) and (abs(ty) < 0.000001):
            return 4  # x轴
        # 浮点数比较
        if (abs(tx) < 0.000001) and (abs(ty) > 0):
            return 5  # y轴
        return 6  # 原点

    # 随机整数
    def random_int(self, low, high):
        return np.random.randint(low, high)

    # 随机生成训练和验证数据
    # train(low, high, count)
    # val(low, high, count)
    def generateData(self, train: tuple[int, int, int], val: tuple[int, int, int]):
        self.trainData.clear()
        self.trainLabels.clear()
        self.valData.clear()
        self.valLabels.clear()
        low = train[0]
        high = train[1]
        count = train[2]
        for i in range(count):
            tx = self.random_int(low, high)
            ty = self.random_int(low, high)
            tl = self.check_quadrant(tx, ty)
            self.trainData.append([tx, ty])
            self.trainLabels.append(tl)

        low = val[0]
        high = val[1]
        count = val[2]
        for i in range(count):
            tx = self.random_int(low, high)
            ty = self.random_int(low, high)
            tl = self.check_quadrant(tx, ty)
            self.valData.append([tx, ty])
            self.valLabels.append(tl)

    # 转化为Numpy数据格式
    def toNumpyObj(self):
        return np.array(self.trainData, dtype=np.float32), np.array(self.trainLabels, dtype=np.int64), \
               np.array(self.valData, dtype=np.float32), np.array(self.valLabels, dtype=np.int64)

    # 转化为张量对象
    def toTensorObj(self):
        td, tl, vd, vl = self.toNumpyObj()
        return torch.from_numpy(td), torch.from_numpy(tl), torch.from_numpy(vd), torch.from_numpy(vl)

构建神经网络模型

class QuadrantClassifier(nn.Module):
    def __init__(self):
        super(QuadrantClassifier, self).__init__()
        # 输入层,中间层,输出层列表
        self.mLayers = []

    # 设置输入层,中间层,输出层(用于层数量的多少对结果性能的比较)
    # 输入层,中间层,输出层特别数要一一对应起来(否则会出错)
    # 至少要有两层(一个输入层,一个输出层)
    def setLayers(self, layers: list[tuple[int, int]]):
        self.mLayers.clear()
        layerCnt = 0
        for tmpLL in layers:
            layerCnt += 1
            tmpLayer = nn.Linear(tmpLL[0], tmpLL[1])
            # 这里需要动态设置性值(否则创建优化器时,会报模型的参数为空,不知是何原因)
            setattr(self, "layer_%d" % layerCnt, tmpLayer)
            self.mLayers.append(tmpLayer)

    # 神经网络每次调用时都需要执行的前向传播计算(使用ReLU激活函数)
    def forward(self, x):
        layerSize = len(self.mLayers)

        # 输入层和中间层都采用ReLU激活函数
        for tl in range(layerSize - 1):
            x = torch.relu(self.mLayers[tl](x))
        # 最后一层为输出层(输出层不使用激活函数)
        x = self.mLayers[layerSize - 1](x)

        return x

模型处理类

class ModelProcessor(object):
    def __init__(self):
        super(ModelProcessor, self).__init__()
        # 训练数据和验证数据
        self.mTrainData = None
        self.mTrainLabels = None
        self.mValData = None
        self.mValLabels= None

        # 模型对象
        self.mQmodel = None
        # 输入层,中间层,输出层列表
        self.mLayers = None

        # 损失函数
        self.mCriterion = None
        # 优化器
        self.mOptimizer = None
        # 数据加载器
        self.mDataloader = None
        # 学习率
        self.mLr = 0.001
        # 训练次数
        self.mEpochs = 100

        # 模型保存的路径
        self.mQmodelPath = "./model/quadrant_v2.pt"
        # 模型的准确率
        self.mSucRatio = 0.0
        # 训练所花费的总时间(s)
        self.mTrainTime = 0.0

    def setTrainValData(self, trainData, trainLabels, valData, valLabels):
        self.mTrainData = trainData
        self.mTrainLabels = trainLabels
        self.mValData = valData
        self.mValLabels = valLabels

    # 设置输入层,中间层,输出层(用于层数量的多少对结果性能的比较)
    # 输入层,中间层,输出层特别数要一一对应起来(否则会出错)
    # 至少要有两层(一个输入层,一个输出层)
    def setLayers(self, layers: list[tuple[int, int]]):
        self.mLayers = layers

    def setEpochs(self, epochs: int):
        self.mEpochs = epochs

    def setLr(self, lr: float = 0.001):
        self.mLr = lr

    def setQmodelSavePath(self, path):
        self.mQmodelPath = path

    def setTrainTime(self, sparedTime):
        self.mTrainTime = sparedTime

    # 查看模型参数数据
    def show_mode_params(self, model, tag):
        show_logs = False
        if not show_logs:
            return
        print(tag)
        for tmpP in model.parameters():
            print(tmpP)

    # 执行训练逻辑
    def doTrainProcess(self):
        print("doTrainProcess:")
        print("trainData.shape", self.mTrainData.shape)
        print("trainLabels.shape", self.mTrainLabels.shape)

        # 创建模型
        self.mQmodel = QuadrantClassifier()
        # 设置层(设置输入层,中间层,输出层)
        self.mQmodel.setLayers(self.mLayers)

        # 损失函数
        self.mCriterion = nn.CrossEntropyLoss()
        # 优化器(采用Adam算法)
        self.mOptimizer = optim.Adam(self.mQmodel.parameters(), lr=self.mLr)
        # 查看模型参数
        self.show_mode_params(self.mQmodel, "-"*30 + "first params:")

        # 将数据转换为 DataLoader
        dataset = TensorDataset(self.mTrainData, self.mTrainLabels)
        # shuffle为True 每次训练时,数据加载器会扰乱数据的先后顺序
        # batch_size: 数据加载器每一次要读取多少个原数据(用于训练)
        self.mDataloader = DataLoader(dataset, batch_size=2, shuffle=True)

        # 训练模型
        for epoch in range(self.mEpochs):
            # 查看模型参数(训练前目的:神经元网络在反向传播后,模型参数的调优情况)
            self.show_mode_params(self.mQmodel, "-" * 30 + f"epoch={epoch}-> start:")
            for inputs, targets in self.mDataloader:
                # optimizer.zero_grad()函数会遍历模型的所有参数,通过p.grad.detach_()方法截断反向传播的梯度流,
                # 再通过p.grad.zero_()函数将每个参数的梯度值设为0,即上一次的梯度记录被清空。
                self.mOptimizer.zero_grad()
                # 模型输出
                outputs = self.mQmodel(inputs)
                # 交叉熵损失
                loss = self.mCriterion(outputs, targets)
                # 反向传播求梯度
                loss.backward()
                # 更新所有参数
                self.mOptimizer.step()
            # 查看横型参数(一次训练后,loss值应该越来越小,才符合模型训练的结果)
            self.show_mode_params(self.mQmodel, "-" * 30 + f"epoch={epoch}-> end:")

        # 保存模型
        torch.save(self.mQmodel.state_dict(), self.mQmodelPath)

    # 验证模型
    def doValidateProcess(self):
        print("doValidateProcess:")
        quadrant_type = ["I", "II", "III", "IV", "X-axis", "Y-axis", "Origin"]

        # 将数据转换为DataLoader
        dataset = TensorDataset(self.mValData, self.mValLabels)
        dataLoader = DataLoader(dataset, batch_size=1, shuffle=False)
        # 验证每一个新的象素点(用数据加载器加载每一个要验证的像素点)
        total = 0
        sucCnt = 0
        for tmpNewPoint, tmpRealLabel in dataLoader:
            total += 1
            with torch.no_grad():
                self.mQmodel.eval()
                prediction = torch.argmax(self.mQmodel(tmpNewPoint), dim=1).item()
            # 验证状态,成功状态就累加(评估模型的泛化能力)
            realValue = tmpRealLabel.item()
            success = prediction == realValue
            if success:
                sucCnt += 1
            # 将Tensor转化为numpy
            tmpP = tmpNewPoint.numpy()
            tx = tmpP[0,0]
            ty = tmpP[0, 1]
            # print(f'{tx, ty} is {quadrant_type[realValue]}, predict is {quadrant_type[prediction]}, state={success}')
        self.mSucRatio = 1.0 * sucCnt / total
        print(f'success ratio:{self.mSucRatio}')

基本测试

# 数据生成器
dataGen = DataGenerator()
trainDataDesc = (-5, 6, 300)
valDataDesc = (-5, 6, 500)
dataGen.generateData(trainDataDesc, valDataDesc)
td, tl, vd, vl = dataGen.toTensorObj()
print(td.shape, tl.shape)
print(vd.shape, vl.shape)

# 模型处理类
modelProc = ModelProcessor()
modelProc.setTrainValData(td, tl, vd, vl)
modelProc.setLayers([(2, 32), (32, 16), (16, 7)])
modelProc.setEpochs(5)
# 执行训练
modelProc.doTrainProcess()
# 验证模型
modelProc.doValidateProcess()

训练数据不变,调节epochs时,分析模型泛化能力

def doAnalyzeByEpochs():
    # 数据生成器
    dataGen = DataGenerator()
    trainDataDesc = (-5, 6, 300)
    valDataDesc = (-2147483648, 2147483647, 500)
    layers = [(2, 32), (32, 16), (16, 7)]
    dataGen.generateData(trainDataDesc, valDataDesc)

    # 只分析epochs数据的影响,其它所有参数不变
    lstProcs = []
    model_cnt = 20
    epochs = []
    for i in range(model_cnt):
        epochs.append((i + 1) * 50)
    for i in range(model_cnt):
        print(f"test->{i}:")
        # Tensor数据对象(所有对象的训练数据和验证数据相同)
        # 重新得到Tensor对象(防止数据被不同模型修改)
        td, tl, vd, vl = dataGen.toTensorObj()

        # 模型处理类
        modelProc = ModelProcessor()
        lstProcs.append(modelProc)
        modelProc.setTrainValData(td, tl, vd, vl)
        modelProc.setLayers(layers)
        modelProc.setEpochs(epochs[i])
        modelProc.setQmodelSavePath("./model/quadrant_v2_%d.pt" % (epochs[i]))
        # 执行训练(并记录时长)
        lasttime = time.time()
        modelProc.doTrainProcess()
        curtime = time.time()
        modelProc.setTrainTime(curtime - lasttime)

        # 验证模型
        modelProc.doValidateProcess()

    # 数据可视化显示
    sucRatios = []
    trainTimes = []
    for tmpModel in lstProcs:
        sucRatios.append(tmpModel.mSucRatio)
        trainTimes.append(tmpModel.mTrainTime)
    # 用来正常显示中文标签
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.subplot(1,2,1)
    # 绘制点线图
    plt.plot(epochs, sucRatios, marker='o', linestyle='-', color='blue')
    plt.xlabel('训练次数')
    plt.ylabel('预测准确率')
    plt.title('次数与性能关系')
    plt.subplot(1,2,2)
    plt.plot(epochs, trainTimes, marker='o', linestyle='-', color='red')
    plt.xlabel('训练次数')
    plt.ylabel('耗时(s)')
    plt.title('次数与时长关系')
    plt.savefig('./model/model_epochs_ratio.png')
    plt.show()

改变训练数据(分布范围),其它数据不变,分析模型泛化能力

def doAnalyzeByTrainData():
    model_cnt = 20
    valDataDesc = (-2147483648, 2147483647, 500)
    layers = [(2, 32), (32, 16), (16, 7)]
    epochs = 300
    # 将本样本数据的范围扩大,其它数据不变
    lstProcs = []
    highs = []
    for i in range(model_cnt):
        print(f"test->{i}:")
        dataGen = DataGenerator()
        # 范围扩大后(原点和x,y轴上的数据随机生成的概率就急速变小(需要注意))
        low = -5 - 100 * i
        high = 6 + 100 * i
        trainDataDesc = (low, high, 300)
        highs.append(high - low)
        dataGen.generateData(trainDataDesc, valDataDesc)
        td, tl, vd, vl = dataGen.toTensorObj()

        # 模型处理类
        modelProc = ModelProcessor()
        lstProcs.append(modelProc)
        modelProc.setTrainValData(td, tl, vd, vl)
        modelProc.setLayers(layers)
        modelProc.setEpochs(epochs)
        modelProc.setQmodelSavePath("./model/quadrant_v2_td%d.pt" % (i))
        # 执行训练(并记录时长)
        lasttime = time.time()
        modelProc.doTrainProcess()
        curtime = time.time()
        modelProc.setTrainTime(curtime - lasttime)

        # 验证模型
        modelProc.doValidateProcess()

    # 数据可视化显示
    plt.rcParams['font.sans-serif'] = ['SimHei']
    sucRatios = []
    for tmpModel in lstProcs:
        sucRatios.append(tmpModel.mSucRatio)
    # 绘制点线图
    plt.plot(highs, sucRatios, marker='o', linestyle='-', color='blue')
    plt.xlabel('样本的宽度')
    plt.ylabel('预测准确率')
    plt.title('样本范围与性能关系')
    plt.savefig('./model/model_traindata_ratio.png')
    plt.show()
if __name__ == "__main__":
    # doCommonTest()
    doAnalyzeByEpochs()
    # doAnalyzeByTrainData()

训练数据不变,调节epochs时,分析模型泛化能力
在这里插入图片描述
通过上图了解到:
1.通过更改训练次数,模型的准确率没有太大的提高,也不是线性关系
2.随着训练次数的增加,训练时间增长(基本符合线性关系,不同设备可能不同)
改变训练数据(分布范围),其它数据不变,分析模型泛化能力
在这里插入图片描述通过上图了解到:
1.随着样本范围扩大,模型准确率显示有提高
2.样本范围扩大后,模型准确率提高不是线性关系

完整代码如下:

"""
CNN 参数调优
"""

import time

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset
import numpy as np
import matplotlib.pyplot as plt

# 数据生成器
class DataGenerator(object):
    def __init__(self):
        super(DataGenerator, self).__init__()
        self.trainData = []
        self.trainLabels = []
        self.valData = []
        self.valLabels = []

    # 检查tx,ty坐标象限分类
    def check_quadrant(self, tx, ty):
        if (tx > 0) and (ty > 0):
            return 0  # 第一象限
        if (tx < 0) and (ty > 0):
            return 1  # 第二象限
        if (tx < 0) and (ty < 0):
            return 2  # 第三象限
        if (tx > 0) and (ty < 0):
            return 3  # 第四象限
        # 浮点数比较
        if (abs(tx) > 0) and (abs(ty) < 0.000001):
            return 4  # x轴
        # 浮点数比较
        if (abs(tx) < 0.000001) and (abs(ty) > 0):
            return 5  # y轴
        return 6  # 原点

    # 随机整数
    def random_int(self, low, high):
        return np.random.randint(low, high)

    # 随机生成训练和验证数据
    # train(low, high, count)
    # val(low, high, count)
    def generateData(self, train: tuple[int, int, int], val: tuple[int, int, int]):
        self.trainData.clear()
        self.trainLabels.clear()
        self.valData.clear()
        self.valLabels.clear()
        low = train[0]
        high = train[1]
        count = train[2]
        for i in range(count):
            tx = self.random_int(low, high)
            ty = self.random_int(low, high)
            tl = self.check_quadrant(tx, ty)
            self.trainData.append([tx, ty])
            self.trainLabels.append(tl)

        low = val[0]
        high = val[1]
        count = val[2]
        for i in range(count):
            tx = self.random_int(low, high)
            ty = self.random_int(low, high)
            tl = self.check_quadrant(tx, ty)
            self.valData.append([tx, ty])
            self.valLabels.append(tl)

    # 转化为Numpy数据格式
    def toNumpyObj(self):
        return np.array(self.trainData, dtype=np.float32), np.array(self.trainLabels, dtype=np.int64), \
               np.array(self.valData, dtype=np.float32), np.array(self.valLabels, dtype=np.int64)

    # 转化为张量对象
    def toTensorObj(self):
        td, tl, vd, vl = self.toNumpyObj()
        return torch.from_numpy(td), torch.from_numpy(tl), torch.from_numpy(vd), torch.from_numpy(vl)


# 构建神经网络模型
class QuadrantClassifier(nn.Module):
    def __init__(self):
        super(QuadrantClassifier, self).__init__()
        # 输入层,中间层,输出层列表
        self.mLayers = []

    # 设置输入层,中间层,输出层(用于层数量的多少对结果性能的比较)
    # 输入层,中间层,输出层特别数要一一对应起来(否则会出错)
    # 至少要有两层(一个输入层,一个输出层)
    def setLayers(self, layers: list[tuple[int, int]]):
        self.mLayers.clear()
        layerCnt = 0
        for tmpLL in layers:
            layerCnt += 1
            tmpLayer = nn.Linear(tmpLL[0], tmpLL[1])
            # 这里需要动态设置性值(否则创建优化器时,会报模型的参数为空,不知是何原因)
            setattr(self, "layer_%d" % layerCnt, tmpLayer)
            self.mLayers.append(tmpLayer)

    # 神经网络每次调用时都需要执行的前向传播计算(使用ReLU激活函数)
    def forward(self, x):
        layerSize = len(self.mLayers)

        # 输入层和中间层都采用ReLU激活函数
        for tl in range(layerSize - 1):
            x = torch.relu(self.mLayers[tl](x))
        # 最后一层为输出层(输出层不使用激活函数)
        x = self.mLayers[layerSize - 1](x)

        return x


# 模型处理类
class ModelProcessor(object):
    def __init__(self):
        super(ModelProcessor, self).__init__()
        # 训练数据和验证数据
        self.mTrainData = None
        self.mTrainLabels = None
        self.mValData = None
        self.mValLabels= None

        # 模型对象
        self.mQmodel = None
        # 输入层,中间层,输出层列表
        self.mLayers = None

        # 损失函数
        self.mCriterion = None
        # 优化器
        self.mOptimizer = None
        # 数据加载器
        self.mDataloader = None
        # 学习率
        self.mLr = 0.001
        # 训练次数
        self.mEpochs = 100

        # 模型保存的路径
        self.mQmodelPath = "./model/quadrant_v2.pt"
        # 模型的准确率
        self.mSucRatio = 0.0
        # 训练所花费的总时间(s)
        self.mTrainTime = 0.0

    def setTrainValData(self, trainData, trainLabels, valData, valLabels):
        self.mTrainData = trainData
        self.mTrainLabels = trainLabels
        self.mValData = valData
        self.mValLabels = valLabels

    # 设置输入层,中间层,输出层(用于层数量的多少对结果性能的比较)
    # 输入层,中间层,输出层特别数要一一对应起来(否则会出错)
    # 至少要有两层(一个输入层,一个输出层)
    def setLayers(self, layers: list[tuple[int, int]]):
        self.mLayers = layers

    def setEpochs(self, epochs: int):
        self.mEpochs = epochs

    def setLr(self, lr: float = 0.001):
        self.mLr = lr

    def setQmodelSavePath(self, path):
        self.mQmodelPath = path

    def setTrainTime(self, sparedTime):
        self.mTrainTime = sparedTime

    # 查看模型参数数据
    def show_mode_params(self, model, tag):
        show_logs = False
        if not show_logs:
            return
        print(tag)
        for tmpP in model.parameters():
            print(tmpP)

    # 执行训练逻辑
    def doTrainProcess(self):
        print("doTrainProcess:")
        print("trainData.shape", self.mTrainData.shape)
        print("trainLabels.shape", self.mTrainLabels.shape)

        # 创建模型
        self.mQmodel = QuadrantClassifier()
        # 设置层(设置输入层,中间层,输出层)
        self.mQmodel.setLayers(self.mLayers)

        # 损失函数
        self.mCriterion = nn.CrossEntropyLoss()
        # 优化器(采用Adam算法)
        self.mOptimizer = optim.Adam(self.mQmodel.parameters(), lr=self.mLr)
        # 查看模型参数
        self.show_mode_params(self.mQmodel, "-"*30 + "first params:")

        # 将数据转换为 DataLoader
        dataset = TensorDataset(self.mTrainData, self.mTrainLabels)
        # shuffle为True 每次训练时,数据加载器会扰乱数据的先后顺序
        # batch_size: 数据加载器每一次要读取多少个原数据(用于训练)
        self.mDataloader = DataLoader(dataset, batch_size=2, shuffle=True)

        # 训练模型
        for epoch in range(self.mEpochs):
            # 查看模型参数(训练前目的:神经元网络在反向传播后,模型参数的调优情况)
            self.show_mode_params(self.mQmodel, "-" * 30 + f"epoch={epoch}-> start:")
            for inputs, targets in self.mDataloader:
                # optimizer.zero_grad()函数会遍历模型的所有参数,通过p.grad.detach_()方法截断反向传播的梯度流,
                # 再通过p.grad.zero_()函数将每个参数的梯度值设为0,即上一次的梯度记录被清空。
                self.mOptimizer.zero_grad()
                # 模型输出
                outputs = self.mQmodel(inputs)
                # 交叉熵损失
                loss = self.mCriterion(outputs, targets)
                # 反向传播求梯度
                loss.backward()
                # 更新所有参数
                self.mOptimizer.step()
            # 查看横型参数(一次训练后,loss值应该越来越小,才符合模型训练的结果)
            self.show_mode_params(self.mQmodel, "-" * 30 + f"epoch={epoch}-> end:")

        # 保存模型
        torch.save(self.mQmodel.state_dict(), self.mQmodelPath)

    # 验证模型
    def doValidateProcess(self):
        print("doValidateProcess:")
        quadrant_type = ["I", "II", "III", "IV", "X-axis", "Y-axis", "Origin"]

        # 将数据转换为DataLoader
        dataset = TensorDataset(self.mValData, self.mValLabels)
        dataLoader = DataLoader(dataset, batch_size=1, shuffle=False)
        # 验证每一个新的象素点(用数据加载器加载每一个要验证的像素点)
        total = 0
        sucCnt = 0
        for tmpNewPoint, tmpRealLabel in dataLoader:
            total += 1
            with torch.no_grad():
                self.mQmodel.eval()
                prediction = torch.argmax(self.mQmodel(tmpNewPoint), dim=1).item()
            # 验证状态,成功状态就累加(评估模型的泛化能力)
            realValue = tmpRealLabel.item()
            success = prediction == realValue
            if success:
                sucCnt += 1
            # 将Tensor转化为numpy
            tmpP = tmpNewPoint.numpy()
            tx = tmpP[0,0]
            ty = tmpP[0, 1]
            # print(f'{tx, ty} is {quadrant_type[realValue]}, predict is {quadrant_type[prediction]}, state={success}')
        self.mSucRatio = 1.0 * sucCnt / total
        print(f'success ratio:{self.mSucRatio}')

# 基本测试
def doCommonTest():
    # 数据生成器
    dataGen = DataGenerator()
    trainDataDesc = (-5, 6, 300)
    valDataDesc = (-5, 6, 500)
    dataGen.generateData(trainDataDesc, valDataDesc)
    td, tl, vd, vl = dataGen.toTensorObj()
    print(td.shape, tl.shape)
    print(vd.shape, vl.shape)

    # 模型处理类
    modelProc = ModelProcessor()
    modelProc.setTrainValData(td, tl, vd, vl)
    modelProc.setLayers([(2, 32), (32, 16), (16, 7)])
    modelProc.setEpochs(5)
    # 执行训练
    modelProc.doTrainProcess()
    # 验证模型
    modelProc.doValidateProcess()


# 训练数据不变,调节epochs时,分析模型泛化能力
def doAnalyzeByEpochs():
    # 数据生成器
    dataGen = DataGenerator()
    trainDataDesc = (-5, 6, 300)
    valDataDesc = (-2147483648, 2147483647, 500)
    layers = [(2, 32), (32, 16), (16, 7)]
    dataGen.generateData(trainDataDesc, valDataDesc)

    # 只分析epochs数据的影响,其它所有参数不变
    lstProcs = []
    model_cnt = 20
    epochs = []
    for i in range(model_cnt):
        epochs.append((i + 1) * 50)
    for i in range(model_cnt):
        print(f"test->{i}:")
        # Tensor数据对象(所有对象的训练数据和验证数据相同)
        # 重新得到Tensor对象(防止数据被不同模型修改)
        td, tl, vd, vl = dataGen.toTensorObj()

        # 模型处理类
        modelProc = ModelProcessor()
        lstProcs.append(modelProc)
        modelProc.setTrainValData(td, tl, vd, vl)
        modelProc.setLayers(layers)
        modelProc.setEpochs(epochs[i])
        modelProc.setQmodelSavePath("./model/quadrant_v2_%d.pt" % (epochs[i]))
        # 执行训练(并记录时长)
        lasttime = time.time()
        modelProc.doTrainProcess()
        curtime = time.time()
        modelProc.setTrainTime(curtime - lasttime)

        # 验证模型
        modelProc.doValidateProcess()

    # 数据可视化显示
    sucRatios = []
    trainTimes = []
    for tmpModel in lstProcs:
        sucRatios.append(tmpModel.mSucRatio)
        trainTimes.append(tmpModel.mTrainTime)
    # 用来正常显示中文标签
    plt.rcParams['font.sans-serif'] = ['SimHei']
    plt.subplot(1,2,1)
    # 绘制点线图
    plt.plot(epochs, sucRatios, marker='o', linestyle='-', color='blue')
    plt.xlabel('训练次数')
    plt.ylabel('预测准确率')
    plt.title('次数与性能关系')
    plt.subplot(1,2,2)
    plt.plot(epochs, trainTimes, marker='o', linestyle='-', color='red')
    plt.xlabel('训练次数')
    plt.ylabel('耗时(s)')
    plt.title('次数与时长关系')
    plt.savefig('./model/model_epochs_ratio.png')
    plt.show()

# 改变训练数据(分布范围),其它数据不变,分析模型泛化能力
def doAnalyzeByTrainData():
    model_cnt = 20
    valDataDesc = (-2147483648, 2147483647, 500)
    layers = [(2, 32), (32, 16), (16, 7)]
    epochs = 300
    # 将本样本数据的范围扩大,其它数据不变
    lstProcs = []
    highs = []
    for i in range(model_cnt):
        print(f"test->{i}:")
        dataGen = DataGenerator()
        # 范围扩大后(原点和x,y轴上的数据随机生成的概率就急速变小(需要注意))
        low = -5 - 100 * i
        high = 6 + 100 * i
        trainDataDesc = (low, high, 300)
        highs.append(high - low)
        dataGen.generateData(trainDataDesc, valDataDesc)
        td, tl, vd, vl = dataGen.toTensorObj()

        # 模型处理类
        modelProc = ModelProcessor()
        lstProcs.append(modelProc)
        modelProc.setTrainValData(td, tl, vd, vl)
        modelProc.setLayers(layers)
        modelProc.setEpochs(epochs)
        modelProc.setQmodelSavePath("./model/quadrant_v2_td%d.pt" % (i))
        # 执行训练(并记录时长)
        lasttime = time.time()
        modelProc.doTrainProcess()
        curtime = time.time()
        modelProc.setTrainTime(curtime - lasttime)

        # 验证模型
        modelProc.doValidateProcess()

    # 数据可视化显示
    plt.rcParams['font.sans-serif'] = ['SimHei']
    sucRatios = []
    for tmpModel in lstProcs:
        sucRatios.append(tmpModel.mSucRatio)
    # 绘制点线图
    plt.plot(highs, sucRatios, marker='o', linestyle='-', color='blue')
    plt.xlabel('样本的宽度')
    plt.ylabel('预测准确率')
    plt.title('样本范围与性能关系')
    plt.savefig('./model/model_traindata_ratio.png')
    plt.show()

# 改变学习率分析模型,其它数据不变,分析模型泛化能力
def doAnalyzeByLr():
    # 数据生成器
    dataGen = DataGenerator()
    trainDataDesc = (-10, 11, 350)
    valDataDesc = (-2147483648, 2147483647, 500)
    layers = [(2, 32), (32, 16), (16, 7)]
    dataGen.generateData(trainDataDesc, valDataDesc)

    lstProcs = []
    epochs = 500
    model_cnt = 20
    lrs = np.linspace(0.0005, 0.001, 20)
    # print(lrs)
    for i in range(model_cnt):
        print(f"test->{i}:")
        # Tensor数据对象(所有对象的训练数据和验证数据相同)
        # 重新得到Tensor对象(防止数据被不同模型修改)
        td, tl, vd, vl = dataGen.toTensorObj()

        # 模型处理类
        modelProc = ModelProcessor()
        lstProcs.append(modelProc)
        modelProc.setTrainValData(td, tl, vd, vl)
        modelProc.setLayers(layers)
        modelProc.setEpochs(epochs)
        modelProc.setLr(lrs[i])
        modelProc.setQmodelSavePath("./model/quadrant_v2_lr_%.05f.pt" % (lrs[i]))

        # 执行训练(并记录时长)
        lasttime = time.time()
        modelProc.doTrainProcess()
        curtime = time.time()
        modelProc.setTrainTime(curtime - lasttime)

        # 验证模型
        modelProc.doValidateProcess()

    # 分析各模型
    sucRatios = []
    trainTimes = []
    for tmpModel in lstProcs:
        sucRatios.append(tmpModel.mSucRatio)
        trainTimes.append(tmpModel.mTrainTime)
    plt.rcParams['font.sans-serif'] = ['SimHei']

    plt.subplot(1, 2, 1)
    # 绘制点线图
    plt.plot(lrs.tolist(), sucRatios, marker='o', linestyle='-', color='blue')
    plt.xlabel('学习率')
    plt.ylabel('预测准确率')
    plt.title('学习率与准确率关系')
    plt.subplot(1, 2, 2)
    plt.plot(lrs.tolist(), trainTimes, marker='o', linestyle='-', color='red')
    plt.xlabel('学习率')
    plt.ylabel('耗时(s)')
    plt.title('学习率与时长关系')
    plt.savefig('./model/model_lr_ratio.png')
    plt.show()

if __name__ == "__main__":
    # doCommonTest()
    # doAnalyzeByEpochs()
    # doAnalyzeByTrainData()
    doAnalyzeByLr()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值