深度学习-2.7 机器学习目标与模型评估方法

文章目录

  • 深度学习目标与模型评估方法
    • 1. 深度学习目标与模型评估方法
    • 2. 手动实现训练集和测试集切分
    • 3. Dataset和DataLoader基本使用方法与数据集切分函数
      • 1.Dataset和DataLoader的基本使用方法
      • 2.建模及评估过程
    • 4. 实用函数补充

深度学习目标与模型评估方法

1. 深度学习目标与模型评估方法

在了解深度学习基本模型的概念与实现方法后,接下来,我们将详细探讨深度学习模型优化的常用方法。从上一课的实验中不难发现,要把一个模型“建好”已是不容易,而要想办法把模型的效果进行提升,如果没有基础理论支持和方法论工具,优化过程无异于盲人摸象。因此,本节课将从建模的根本目标出发,围绕模型优化的基本概念和核心理论进行全面梳理,并在此基础之上介绍相关实践方法,逐渐拨开模型优化面前的迷雾。

我们经常会对模型的好坏优劣进行评估,我们也使用准确率、MSE等指标评估建模结果,看起来模型评估是围绕某项指标在进行评估,指标好模型就好,指标不好模型就不好,其实并不完全如此。要了解模型的性能其实并不简单,固然我们会使用某些指标去进行模型评估,但其实指标也只是我们了解模型性能的途径而不是模型性能本身。而要真实、深刻的评判模型性能,就必须首先了解机器学习的建模目标,并在此基础之上熟悉我们判断模型是否能够完成目标的一些方法,当然,只有真实了解的模型性能,我们才能进一步考虑如何提升模型性能。因此,在正式讲解模型优化方法之前,我们需要花些时间讨论机器学习算法的建模目标、机器学习算法为了能够达到目标的一般思路,以及评估模型性能的手段,也就是模型评估指标。

无论是机器学习还是传统的统计分析模型,核心使命就是探索数字规律,而有监督学习则是希望在探索数字规律的基础上进一步对未来进行预测,当然,在数字的世界,这个预测未来,也就是预测未来某项事件的某项数值指标,如某地区未来患病人次、具备某种数字特征的图片上的动物是哪一类,此处的未来也并非指绝对意义上的以后的时间,而是在模型训练阶段暂时未接触到的数据。正是因为模型有了在未知标签情况下进行预判的能力,有监督学习才有了存在的价值,但我们知道,基本上所有的模型,都只能从以往的历史经验当中进行学习,也就是在以往的、已经知道的数据集上进行训练(如上述利用已知数据集进行模型训练,如利用过往股票数据训练时间序列模型),这里的核心矛盾在于,在以往的数据中提取出来的经验(也就是模型),怎么证明能够在接下来的数据中也具备一定的预测能力呢?或者说,要怎么训练模型,才能让模型在未知的数据集上也拥有良好的表现呢?

目的相同,但在具体的实现方法上,传统的数理统计分析建模和机器学习采用了不同的解决方案。

首先,在统计分析领域,我们会假设现在的数据和未来的数据其实都属于某个存在但不可获得的总体,也就是说,现在和未来的数据都是从某个总体中抽样而来的,都是这个总体的样本。而正式因为这些数据属于同一个总体,因此具备某些相同的规律,而现在挖掘到的数据规律也就在某些程度上可以应用到未来的数据当中去,不过呢,不同抽样的样本之间也会有个体之间的区别,另外模型本身也无法完全捕获规律,而这些就是误差的来源。

虽然样本和总体的概念是统计学概念,但样本和总体的概念所假设的前后数据的“局部规律一致性”,却是所有机器学习建模的基础。试想一下,如果获取到的数据前后描绘的不是一件事情,那么模型训练也就毫无价值(比如拿着A股走势预测的时间序列预测某地区下个季度患病人次)。因此,无论是机器学习所强调的从业务角度出发,要确保前后数据描述的一致性,还是统计分析所强调的样本和总体的概念,都是建模的基础。

在有了假设基础之后,统计分析就会利用一系列的数学方法和数理统计工具去推导总体的基本规律,也就是变量的分布规律和一些统计量的取值,由于这个过程是通过已知的样本去推断未知的总体,因此会有大量的“估计”和“检验”,在确定了总体的基本分布规律之后,才能够进一步使用统计分析模型构建模型(这也就是为什么在数理统计分析领域,构建线性回归模型需要先进行一系列的检验和变换的原因),当然,这些模型都是在总体规律基础之上、根据样本具体的数值进行的建模,我们自然有理由相信这些模型对接下来仍然是从总体中抽样而来的样本还是会具备一定的预测能力,这也就是我们对统计分析模型“信心”的来源。简单来说,就是我们通过样本推断总体的规律,然后结合总体的规律和样本的数值构建模型,由于模型也描绘了总体规律,所以模型对接下来从总体当中抽样而来的数据也会有不错的预测效果,这个过程我们可以通过下图来进行表示。

在这里插入图片描述

而对于机器学习来说,并没有借助“样本-总体”的基本理论,而是简单的采用了一种后验的方法来判别模型有效性,前面说到,我们假设前后获取的数据拥有规律一致性,但数据彼此之间又略有不同,为了能够在捕捉规律的同时又能考虑到“略有不同”所带来的误差,机器学习会把当前能获取到的数据划分成训练集(trainSet)和测试集(testSet),在训练集上构建模型,然后带入测试集的数据,观测在测试集上模型预测结果和真实结果之间的差异。这个过程其实就是在模拟获取到真实数据之后模型预测的情况,此前说到,模型能够在未知标签的数据集上进行预测,就是模型的核心价值,此时的测试集就是用于模拟未来的未知标签的数据集。如果模型能够在测试集上有不错的预测效果,我们就“简单粗暴”的认为模型可以在真实的未来获取的未知数据集上有不错的表现。其一般过程可以由下图表示。

在这里插入图片描述

虽然对比起数理统计分析,机器学习的证明模型有效性的过程更加“简单”,毕竟只要一次“模拟”成功,我们就认为模型对未来的数据也拥有判别效力,但这种“简单”的处理方式却非常实用,可以说,这是一种经过长期实践被证明的行之有效的方法。这也是为什么机器学习很多时候也被认为是实证类的方法,而在以后的学习中,我们也将了解到,机器学习有很多方法都是“经验总结的结果”。相比数理统计分析,确实没有“那么严谨”,但更易于理解的理论和更通用的方法,却使得机器学习可以在更为广泛的应用场景中发挥作用。(当然,负面影响却是,机器学习在曾经的很长一段时间内并不是主流的算法。)

据此,我们称模型在训练集上误差称为训练误差,在测试集上的误差称为泛化误差,不过毕竟在测试集上进行测试还只是模拟演习,我们采用模型的泛化能力来描述模型在未知数据上的判别能力,当然泛化能力无法准确衡量(未知的数据还未到来,到来的数据都变成了已知数据),我们只能通过模型在训练集和测试集上的表现,判别模型泛化能力,当然,就像此前说的一样,最基本的,我们会通过模型在测试集上的表现来判断模型的泛化能力。

2. 手动实现训练集和测试集切分

接下来我们开始实践模型评估过程,首先是对训练集和测试集的划分,我们尝试创建一个切分训练集和测试集的函数。


def data_split(features, labels, rate=0.7):
    """
    训练集和测试集切分函数
    :param features: 输入的特征张量
    :param labels:输入的标签张量
    :param rate:训练集占所有数据的比例
    :return Xtrain, Xtest, ytrain, ytest:返回特征张量的训练集、测试集,以及标签张量的训练集、测试集
    """
    num_examples = len(features)                              # 总数据量
    indices = list(range(num_examples))                       # 数据集行索引
    random.shuffle(indices)                                   # 乱序调整                    
    num_train = int(num_examples * rate)                      # 训练集数量
    indices_train = torch.tensor(indices[: num_train])        # 在已经乱序的的indices中挑出前num_train数量的行索引值
    indices_test = torch.tensor(indices[num_train: ])        
    Xtrain = features[indices_train]                          # 训练集特征
    ytrain = labels[indices_train]                            # 训练集标签
    Xtest = features[indices_test]                            # 测试集特征
    ytest = labels[indices_test]                              # 测试集标签
    return Xtrain, Xtest, ytrain, ytest

一般来说,训练集和测试集可以按照8:2或7:3比例进行划分。在进行数据划分的过程中,如果测试集划分数据过多,参与模型训练的数据就会相应减少,而训练数据不足则会导致模型无法正常训练、损失函数无法收敛、模型过拟合等问题,但如果反过来测试集划分数据过少,则无法代表一般数据情况测试模型是否对未知数据也有很好的预测作用。因此,根据经验,我们一般来说会按照8:2或7:3比例进行划分。

3. Dataset和DataLoader基本使用方法与数据集切分函数

接下来,我们尝试使用PyTorch原生库来实现上述功能,不过这个实现过程略显复杂,首先我们需要了解Dataset和DataLoader的基本使用方法。

1.Dataset和DataLoader的基本使用方法

  • random_split随机切分函数

首先,在PyTorch的torch.utils.data中,提供了random_split函数可用于数据集切分。

from torch.utils.data import random_split


t = torch.arange(12).reshape(4, 3)

train, test = random_split(t, [2, 2])

for tr, te in random_split(t, [2, 2]):
    print(tr, te)

  • Dataset和Dataloader

由于在大多数调库建模过程中,我们都是先通过创建Dataset的子类并将数据保存为该子类类型,然后再使用DataLoader进行数据载入,因此更为通用的做法是先利用Dataset和DatasetLoader这两个类进行数据的读取、预处理和载入,然后再使用random_split函数进行切分。
再次强调,Dataset类主要负责数据类的生成,在PyTorch中,所有数据集都是Dataset的子类;而DatasetLoader类则是加载模型训练的接口,二者基本使用流程如下:

在这里插入图片描述


from torch.utils.data import Dataset
from torch.utils.data import DataLoader

2.建模及评估过程

接下来,我们尝试通过调库实现完整的数据切分、训练、查看建模结果一整个流程。

  • 数据准备过程

# 生成数据
features, labels = tensorGenReg()
features = features[:, :-1]                                  # 剔除最后全是1的列
# 创建一个针对手动创建数据的数据类
class GenData(Dataset):
    def __init__(self, features, labels):           # 创建该类时需要输入的数据集
        self.features = features                    # features属性返回数据集特征
        self.labels = labels                        # labels属性返回数据集标签
        self.lens = len(features)                   # lens属性返回数据集大小
    def __getitem__(self, index):
        # 调用该方法时需要输入index数值,方法最终返回index对应的特征和标签
        return self.features[index,:],self.labels[index]    
    def __len__(self):
        # 调用该方法不需要输入额外参数,方法最终返回数据集大小
        return self.lens
# 实例化对象
data = GenData(features, labels)
   
# 切分数据集
num_train = int(data.lens * 0.7)
num_test = data.lens - num_train
data_train, data_test = random_split(data, [num_train, num_test])
# 加载数据
train_loader = DataLoader(data_train, batch_size=10, shuffle=True)
test_loader = DataLoader(data_test, batch_size=10, shuffle=False)


  • 构建模型

# 初始化核心参数
batch_size = 10                                # 小批的数量
lr = 0.03                                      # 学习率
num_epochs = 3                                 # 训练过程遍历几次数据
# Stage 1.定义模型
class LR(nn.Module):
    def __init__(self, in_features=2, out_features=1):       # 定义模型的点线结构
        super(LR, self).__init__()
        self.linear = nn.Linear(in_features, out_features)
       
    def forward(self, x):                                    # 定义模型的正向传播规则
        out = self.linear(x)            
        return out
# 实例化模型
LR_model = LR()
# Stage 2.定义损失函数
criterion = nn.MSELoss()
# Stage 3.定义优化方法
optimizer = optim.SGD(LR_model.parameters(), lr = 0.03)
# Stage 4.模型训练与测试
def fit(net, criterion, optimizer, batchdata, epochs=3):
    for epoch  in range(epochs):
        for X, y in batchdata:
            yhat = net.forward(X)
            loss = criterion(yhat, y)
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()


  • 模型训练与测试

fit(net = LR_model,
    criterion = criterion,
    optimizer = optimizer,
    batchdata = train_loader,
    epochs = num_epochs
)


# 查看训练模型
LR_model

# 查看模型参数
list(LR_model.parameters())

查看模型在训练集上表现,首先我们可以通过dataset和indices方法还原训练数据集


data_train.indices      


data[data_train.indices]      # 返回训练集


data[data_train.indices][0]      # 返回训练集的特征


# 计算训练集MSE
F.mse_loss(LR_model(data[data_train.indices][0]), data[data_train.indices][1])


# 计算测试集MSE
F.mse_loss(LR_model(data[data_test.indices][0]), data[data_test.indices][1])


至此,即完成了整个从数据集切分到模型训练,再到查看模型在不同数据集上表现的全过程。

4. 实用函数补充

  • 数据封装、切分和加载函数

该函数可以直接将输入的特征和标签直接进行封装、切分和加载。该函数可以直接处理此前定义的数据生成器创建的数据。


def split_loader(features, labels, batch_size=10, rate=0.7):
    """数据封装、切分和加载函数:
   
    :param features:输入的特征
    :param labels: 数据集标签张量
    :param batch_size:数据加载时的每一个小批数据量
    :param rate: 训练集数据占比
    :return:加载好的训练集和测试集
    """
    data = GenData(features, labels)
    num_train = int(data.lens * 0.7)
    num_test = data.lens - num_train
    data_train, data_test = random_split(data, [num_train, num_test])
    train_loader = DataLoader(data_train, batch_size=batch_size, shuffle=True)
    test_loader = DataLoader(data_test, batch_size=batch_size, shuffle=False)
    return(train_loader, test_loader)

  • 模型训练函数

模型训练函数并不是新的函数,此处正式对其进行定义并写入自定义模块中,方便后续调用。


def fit(net, criterion, optimizer, batchdata, epochs=3, cla=False):
    """模型训练函数
   
    :param net:待训练的模型
    :param criterion: 损失函数
    :param optimizer:优化算法
    :param batchdata: 训练数据集
    :param cla: 是否是分类问题
    :param epochs: 遍历数据次数
    """
    for epoch  in range(epochs):
        for X, y in batchdata:
            if cla == True:
                y = y.flatten().long()          # 如果是分类问题,需要对y进行整数转化
            yhat = net.forward(X)
            loss = criterion(yhat, y)
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()


  • MSE计算函数

接下来,我们借助F.mse_loss,定义一个可以直接根据模型输出结果和加载后的数据计算MSE的函数。


def mse_cal(data_loader, net):
    """mse计算函数
   
    :param data_loader:加载好的数据
    :param net: 模型
    :return:根据输入的数据,输出其MSE计算结果
    """
    data = data_loader.dataset                # 还原Dataset类
    X = data[:][0]                            # 还原数据的特征
    y = data[:][1]                            # 还原数据的标签
    yhat = net(X)
    return F.mse_loss(yhat, y)


  • 准确率计算函数

类似的,定义一个分类问题的准确率计算函数,同样要求输入是加载后的数据集和训练完成的模型。


def accuracy_cal(data_loader, net):
    """准确率
   
    :param data_loader:加载好的数据
    :param net: 模型
    :return:根据输入的数据,输出其准确率计算结果
    """
    data = data_loader.dataset                # 还原Dataset类
    X = data[:][0]                            # 还原数据的特征
    y = data[:][1]                            # 还原数据的标签
    zhat = net(X)                             # 默认是分类问题,并且输出结果是未经softmax转化的结果
    soft_z = F.softmax(zhat, 1)                  # 进行softmax转化
    acc_bool = torch.argmax(soft_z, 1).flatten() == y.flatten()
    acc = torch.mean(acc_bool.float())
    return acc        

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/462902.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

贪心算法(两个实例)

例一:调度问题 问题:由n项任务,每项任务的加工时间已知,从零时刻开始陆续加入一台机器上去加工,每个任务完成的时间是从0时刻到任务加工截至的时间。 求总完成时间(所有任务完成时间最短计划方案&#xf…

Transformer学习笔记(二)

一、文本嵌入层Embedding 1、作用: 无论是源文本嵌入还是目标文本嵌入,都是为了将文本中词汇的数字表示转变为向量表示,希望在这样的高维空间捕捉词汇间的关系。 二、位置编码器Positional Encoding 1、作用: 因为在Transformer…

AcWing 2. 01背包问题

题目描述 解题思路: 相关代码: import java.util.Scanner; public class Main {public static void main(String[] args){Scanner scanner new Scanner(System.in);/** 背包问题的物品下标最好从1开始。* *//*定义一f[i][j]数组,i表示的…

复习 --- windows 上安装 git,使用相关命令

文章目录 很少使用windows的git工具,这次借助这个任务,记录下使用过程,其他的等有空在整理。 其中,还使用了浏览器的AI小助手,复习了git相关的命令:图片放最后

Linux学习方法-框架学习法——Linux系统框架

配套视频学习链接:https://www.bilibili.com/video/BV1HE411w7by?p2&vd_sourced488bc722b90657aaa06a1e8647eddfc 目录 Linux系统框架(从裸机到OS) Linux可看成是一个大软件/大程序 应用和驱动 内核态和用户态 Linux的文件系统 Linux初学者首先要搞清楚三…

如何通过小程序上的产品力和品牌力提升用户的复购能力?

随着网络购物小程序的发展以及内容电商、社交电商、垂直电商、品牌自营等多个细分类型的出现,小程序成为用户日常购物、大促囤货以及首发抢购的重要场景,市场竞争也逐渐激烈。如何在用户侧获得更多转化、留存与复购,成为企业品牌日益关注的话…

javaweb员工健康管理监护系统

项目演示视频 (链接:https://pan.baidu.com/s/1WliYEUH4c0HVB7s0-1WDUA 提取码:1234 --来自百度网盘超级会员V5的分享) 该项目所用到技术 java ssh框架 3:该项目的用到的开发工具? eclipse和idea都可以、m…

将 OpenCV 与 Eclipse 结合使用(插件 CDT)

返回:OpenCV系列文章目录(持续更新中......) 上一篇:将OpenCV与gcc和CMake结合使用 下一篇:OpenCV4.9.0在windows系统下的安装 警告: 本教程可以包含过时的信息。 先决条件 两种方式,一种…

软考78-上午题-【面向对象技术3-设计模式】-结构型设计模式01

一、适配器模式 1-1、意图 个类的接口转换成客户希望的另外一个接口。 Adapter 模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。 1-2、结构 适配器模式分为: 1、适配器类模式; 2、适配器对象模式 类适配器使用多重继承对一个接口与另…

软考80-上午题-【面向对象技术3-设计模式】-结构型设计模式03

一、外观模式 1-1、意图 为子系统中的一组接口提供一个一致的界面。 Facade 模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。 1-2、结构 Facade 知道哪些子系统类负责处理请求:将客户的请求代理给适当的子系统对象。Subsvstem classes …

CI/CD实战-git工具使用 1

版本控制系统 本地版本控制系统 集中化的版本控制系统 分布式版本控制系统 git官网文档:https://git-scm.com/book/zh/v2 Git 有三种状态:已提交(committed)、已修改(modified) 和 已暂存(sta…

194 基于matlab的日历GUI制作

基于matlab的日历GUI制作,可实时显示当前的日期和时间,精确到秒。非常漂亮,也很基础,学习GUI的不错程序,程序已调通,可直接运行。 194 matlab 日历制作 GUI可视化 - 小红书 (xiaohongshu.com)

Spark-Scala语言实战(1)

在之前的文章中,我们学习了如何在Linux安装Spark以及Scala,想了解的朋友可以查看这篇文章。同时,希望我的文章能帮助到你,如果觉得我的文章写的不错,请留下你宝贵的点赞,谢谢。 Spark及Scala的安装https:/…

CSS3技巧38:3D 翻转数字效果

博主其它CSS3 3D的文章: CSS3干货4:CSS中3D运用_css 3d-CSDN博客 CSS3干货5:CSS中3D运用-2_中3d-2-CSDN博客 CSS3干货6:CSS中3D运用-3_css3d 使用-CSDN博客 最近工作上烦心的事情太多,只有周末才能让我冷静一下 cod…

uniapp 写安卓app,运行到手机端 调试

手机 设置》关于手机》点击版本号 4-5次,弹出手机锁屏页面,输入手机锁屏密码 2.手机 设置中 》搜索 开发人员选项 》 调试》打开USB调试 同页面 找到 选择USB配置》选择 MIDIhbuilder 编辑器 点击 》运行》运行到手机或模拟器》运行到Android App基座 》…

【日常记录】【JS】input标签输入中文导致value值的不正确

文章目录 1、描述2、解决方案3、参考 1、描述 在 input标签中,输入中文时,如若监听 input 事件,会得到意想不到的结果,如下面的图所示 2、解决方案 可以用到 compositionstart 监听输入中文开始的事件,compositionend …

inux(CentOS)/Windows-C++ 云备份项目(项目文件操作工具类设计,完成项目基本文件操作-读写-压缩-目录操作)

文章目录 1. 项目文件操作工具类设计 1. 项目文件操作工具类设计 根据前面的分析,这个文件类的基本属性如下: 文件大小信息文件最后修改时间文件最后一次访问时间,方便文件的热点管理文件名称,需要从http 请求行上的uri中获取基…

html编辑器

HTML 编辑器推荐 html可以使用记事本编辑 但是更建议使用专业的 HTML 编辑器来编辑 HTML,我在这里给大家推荐几款常用的编辑器: VS Code:https://code.visualstudio.com/WebStorm: https://www.jetbrains.com/webstorm/Notepad: https://no…

【八】【算法分析与设计】双指针(2)

11. 盛最多水的容器 给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。 说明:你不能…

pytorch 入门基础知识一(Pytorch 01)

一 深度学习基础相关 深度学习三个主要的方向:计算机视觉,自然语言,语音识别。 机器学习核心组件:1 数据集(data),2 前向传播的model(net),3 目标函数(loss), 4 调整模型参数和优化函数的算法…