动手学深度学习10 模型选择+过拟合和欠拟合

动手学深度学习10 模型选择+过拟合和欠拟合

1. 模型选择

电子书https://zh-v2.d2l.ai/chapter_multilayer-perceptrons/underfit-overfit.html
视频https://www.bilibili.com/video/BV1kX4y1g7jp?p=1&vd_source=eb04c9a33e87ceba9c9a2e5f09752ef8
课件https://courses.d2l.ai/zh-v2/assets/pdfs/part-0_14.pdf
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

2. 过拟合和欠拟合

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

目的:降低泛化误差 + 降低泛化误差和训练误差的gap
为了能让泛化误差往下降,不得不承受一定程度的过拟合,能够过拟合也是一种好事。
深度学习核心:首先模型要足够大+再用别的方法控制模型容量
在这里插入图片描述
该图X轴是模型容量,是很多个模型,不是一个模型。
输出层有k个预测的y_hat,每个y_hat包含m个w参数(由上一层的节点数决定)和一个bias,所以是(m+1)k
在这里插入图片描述
林轩田讲VC维–《机器学习基石》
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.代码

import math
import numpy as np
import torch
from torch import nn
from d2l import torch as d2l


# 模拟创建数据集

max_degree = 20  # 多项式的最大阶数   特征的维度?
n_train, n_test = 100, 100   # 训练集和测试数据集的大小
true_w = np.zeros(max_degree)  # 分配大量的空间 
true_w[0:4] = np.array([5, 1.2, -3.4, 5.6])   # 真实权重只给前4个特征赋值,后面16个特征仍旧是0,作为噪声

# np.random.normal:表示使用 NumPy 中的随机模块 random 中的正态分布函数 normal
features = np.random.normal(size=(n_train+n_test, 1)) # 生成服从正态分布的随机数数组 均值0标准差1的随机数
np.random.shuffle(features) # 用于对数组或列表进行随机重排(打乱顺序)。具体来说,它会将数组或列表中的元素按随机顺序重新排列,从而达到打乱数据集或样本顺序的效果。
# np.power() 是 NumPy 库中的一个函数,用于计算数组中元素的指数幂。具体来说,np.power(x, y) 将数组 x 中的每个元素按照指数 y 进行幂运算。
# 这个函数的功能是计算数组元素的指数幂,即x^y。其中,x 是底数数组,y 是指数数组。
# 如果 y 是一个标量,则对x中的每个元素进行相同的指数幂运算;
# 如果 y 是一个数组,则要求 x 和 y 的形状相同,对应位置的元素进行指数幂运算。
# x,y 可以是标量、数组或者其他可转换为数组的对象
# NumPy 库中的一个函数,用于创建一个等差数列的一维数组。
# 具体来说,np.arange(start, stop, step) 会生成一个从起始值 start 开始,到停止值 stop 之前(不包括 stop),步长为 step 的等差数列
# start:数列的起始值(包含在数列中)。
# stop:数列的结束值(不包含在数列中)。
# step:数列的步长,即相邻两个数之间的差值。
# 如果只提供一个参数,则默认起始值为 0,步长为 1;如果提供两个参数,则默认步长为1。
# np.arange() 函数生成的数列是左闭右开区间,即包含起始值而不包含结束值。
poly_features = np.power(features, np.arange(max_degree).reshape(1, -1))
print(features.shape, np.arange(max_degree).reshape(1, -1).shape)  # (200, 1) (1, 20)
print(poly_features.shape)      # (200, 20)
print(poly_features[1, :])
for i in range(max_degree):
  # [:, i] 表示选取矩阵的所有行,但只选取第 i 列。
  poly_features[:, i] /= math.gamma(i+1)  # gamma(n)=(n-1)!
# labels的维度:(n_train+n_test,)
# np.dot() 是 NumPy 库中用于计算两个数组的点积(内积)的函数。点积是两个向量相乘并求和的结果
labels = np.dot(poly_features, true_w)
labels += np.random.normal(scale=0.1, size=labels.shape)

# Numpy ndarray转换为tensor
true_w, features, poly_features, labels = [torch.tensor(x, dtype=torch.float32) for x in [true_w, features, poly_features, labels]]
print(features[:2], poly_features[:2, :], labels[:2])

# 模型训练和测试

def train_epoch_ch3(net, train_iter, loss, updater):
    """训练模型一个迭代周期(定义见第三章)"""
    # 如果模型是用nn模组定义的
    if isinstance(net, torch.nn.Module):
        net.train()  # 将模型设置为训练模式 告诉pytorch要计算梯度
    # 训练损失总和、训练准确度总和、样本数
    metric = d2l.Accumulator(3)  # 三个参数需要累加的迭代器
    for X, y in train_iter:
        # 计算梯度并更新参数
        y_hat = net(X)
        l = loss(y_hat, y) # 计算损失函数
        # 如果优化器是pytorch内置的优化器
        # 下面两个加的结果有什么区别
        # print(float(l)*len(y), accuracy(y_hat,y), y.size().numel(),
        #       float(l.sum()), accuracy(y_hat, y), y.numel())
        if isinstance(updater, torch.optim.Optimizer):
            # 使用pytorch内置的优化器和损失函数
            updater.zero_grad() # 1.梯度先置零
            l.mean().backward() # 2.计算梯度
            updater.step()      # 3.调用step参数进行一次更新
            # metric.add(float(l)*len(y), accuracy(y_hat,y), y.size().numel())
            # 报错 ValueError: only one element tensors can be converted to Python scalars
        else:
            # 使用定制的优化器和损失函数
            # 自己实现的l是一个向量
            l.sum().backward()
            updater(X.shape[0])
            # metric.add(float(l.sum()), accuracy(y_hat, y), y.numel())
        metric.add(float(l.sum()), d2l.accuracy(y_hat, y), y.numel())
    # 返回训练损失和训练精度
    # 损失累加/总样本数  训练正确的/总样本数
    return metric[0] / metric[2], metric[1] / metric[2]

# 损失函数 
def evaluate_loss(net, data_iter, loss):
  """评估给定数据集上模型的损失"""
  metric = d2l.Accumulator(2)  # 损失函数的总和,样本数量
  for X, y in data_iter:
    out = net(X)
    l = loss(out, y)
    metric.add(l.sum(), l.numel())
  return metric[0]/metric[1]

def train(train_features, test_features, train_labels, test_labels, num_epochs=400):
  loss = nn.MSELoss(reduction="none")
  input_shape = train_features.shape[-1]
  # 不设置偏置值 因为已经在多项式中实现了它
  net = nn.Sequential(nn.Linear(input_shape, 1, bias=False))
  batch_size = min(10, train_labels.shape[0])
  train_iter = d2l.load_array((train_features, train_labels.reshape(-1,1)), batch_size)
  test_iter = d2l.load_array((test_features, test_labels.reshape(-1,1)), batch_size)
  trainer = torch.optim.SGD(net.parameters(), lr=0.01)
  animator = d2l.Animator(xlabel='epoch', ylabel='loss', yscale='log', xlim=[1, num_epochs], ylim=[1e-3,1e2], legend=['trian', 'test'])

  for epoch in range(num_epochs):
    train_epoch_ch3(net, train_iter, loss, trainer)
    if epoch == 0 or (epoch+1)%20 == 0:
      animator.add(epoch+1, (evaluate_loss(net, train_iter, loss), evaluate_loss(net, test_iter, loss)))
  print('weight', net[0].weight.data.numpy())


# 正常 从多项式特征中选择前4个维度,即1,x,x^2,x^3/3!  # 真实的人工定义的权重就是4个值 
print("normal")
train(poly_features[:n_train, :4], poly_features[n_train:, :4], labels[:n_train], labels[n_train:])

print("overfitting")
# 过拟合 从多项式特征中选择所有维度 -- 全部20个维度,即1,x,x^2,x^3***/20! 
train(poly_features[:n_train, :], poly_features[n_train:, :], labels[:n_train], labels[n_train:])

print('underfitting')
# 欠拟合 从多项式特征中选择前2个维度,即1,x/3! 
train(poly_features[:n_train, :2], poly_features[n_train:, :2], labels[:n_train], labels[n_train:])

在这里插入图片描述
上面3图X轴是epoch,是同一个模型的不同epoch。epoch也是超参数。

4. QA

视频:https://www.bilibili.com/video/BV1kX4y1g7jp?p=4&spm_id_from=pageDriver&vd_source=eb04c9a33e87ceba9c9a2e5f09752ef8

  1. svm缺点:通过kernel匹配模型复杂度,计算困难,很难做到100w的数据量。神经网络可以做大数据量的计算。第二:svm能调的东西不多,可调性不好。

  2. 神经网络优点:神经网络本身是一种语言,一个个的小语句,不同layer是一个个不同的小工具,一句句的连起来,通过神经网络语言编程,来描述整个物体或者整个世界或要解决问题的理解。比较不太直观,但是可编程性好的框架。可以做到很大的数据集,卷积可以很好的做特征的提取。理论上单层隐藏层MLP能拟合所有的数据集,实际上训练不出来。所以要有一个比较好的结构,尽量帮助拟合。例如cnn本身就是MLP,只是对一些weight固定住了,告诉神经网络我觉得数据有空间信息,要这样去处理空间信息。RNN告诉有时序信息,要这样去训练。用神经网络来描述对问题的理解,帮助训练。试一下模型结构看看训练效果。【艺术】

  3. 模型剪枝、蒸馏可以把模型size变小,但是很少影响模型效果。从原始大模型变成小模型,肯定是比原始就训练一个小模型效果要好一些。

  4. 测试数据集跟验证数据集的分布很可能不一样。假设数据都是独立同分布的。数据集划分常用:30%测试集,70%训练集【其中20%做5折交叉验证】。

  5. 对于时间序列的数据,训练集和验证集自相关,那么测试集和验证集需要在训练集的时间之后。

  6. 验证集和训练集的数据清洗-异常处理、特征构建-标准化,是否要一起处理?
    可以放在一起计算均值和方差,实际生产环境可能可以,对整体的数据分布更鲁棒一些;也可以分开计算。看实际应用,是否能拿到验证数据集的数据,拿不到只能用训练数据集计算。

  7. 实际生成,深度学习训练集很大,不需要做K折交叉验证,训练成本比较贵。

  8. cross validation只是选择超参数,不能解决别的问题。

  9. k折的k怎么确定:最重要一点要在自己能承受的计算成本里面。K越大效果越好,但是训练成本越高。K折交叉验证第一次氛围数据后,就确定分组了。也可以随机打乱做begging,这样是获取k个模型做平均预测。

  10. 模型参数是指训练的w b等模型训练中计算解决的参数;超参数是模型参数以外的我们能调整、可以选的参数都称为超参数,lr,线性模型还是多层感知机,模型有多少层,每层多大。

  11. cross validation每块训练时获得的最终模型参数可能是不同的,每一块报告的也是平均精度,最后可以每一块都计算取平均。
    统计意义:【大数定理

  12. 偏差和方差的区别?

  13. overfitting和underfitting现象可以告诉自己那个参数的效果比较好或坏,在范围内去调参。

  14. 如何有效设计超参数【优化 optuna】
    网格搜索,所有遍历一次。贝叶斯方法计算次数多。【一百一千一万次效果才会好一点】
    超参数设计:一靠自己的经验;二靠自己调参,判断测试结果;三靠随机,随机参数训练从中选一个效果比较好的。
    Optuna 是一个用于超参数优化的自动化调参库,它专门用于优化机器学习模型的超参数,以提高模型的性能和效果。Optuna 提供了一种高效的方法来搜索超参数空间,以找到最佳的超参数组合,从而优化模型的性能并提高模型在测试数据上的表现。

    在深度学习中,模型的性能往往受到超参数的影响。例如,学习率、批量大小、层数、隐藏单元数量等超参数都会直接影响模型的训练速度、收敛性和泛化能力。通过使用 Optuna 进行超参数优化,可以更快速地找到最佳的超参数组合,从而提高深度学习模型的性能和效果。

    总而言之,Optuna 在深度学习中表示一种用于自动化超参数优化的工具,能够帮助用户更高效地优化模型的性能,并提升模型的泛化能力和表现。

  15. 样本不平衡,二分类数据比例1:9,数据集不很大的话,最好验证集上两类数据分布差不多,避免模型偏好数据占比较大的那一类,也可加权重避免。

  16. k折交叉验证目的就是为了确定超参数,一种最常见做法再用这个超参数训练一次模型;第二不再训练模型,找到K折里精度最好【或随便】的一折,选择该参数模型,代价就是模型少看了一些训练集;第三种把K个模型全部拿下来,所有模型都预测一次然后求均值,代价是计算成本高,但是增加了模型稳定性。

  17. svm简单,不用咋调参,有数学理论,有人推,就是多层感知机要流行起来。深度学习虽没有理论,但是实际效果好,就更流行了。发展的问题。深度学习并没有改变机器学习核心的东西:要搞数据,避免overfitting,要看误差。

  18. 模型容量是指模型能够拟合函数的能力

  19. 深度学习一般特指神经网络这一块,有把随机森林做到神经网络里面的,但是问题是随机森林不是通过梯度下降计算的。一般是训练多个模型然后做投票。

  20. K折训练k个模型,把K个模型全部拿下来融合,所有模型都预测一次然后求均值;训练模型初始化随机值,那就随机初始化n次训练n个模型,测试时再输出n个模型的平均值。

  21. 标号、标注、label。

  22. 深度学习模型不做正则化、不做限制可能输出的是无限VC维的算法。

  23. vc维,是模型能记住多大的【多复杂的】数据集长什么样子。

  24. 同样模型结构+训练数据,只是随机初始化不同,最后集成都一定会好?
    模型是统计模型,优化是数值优化。【统计学、优化】最后的模型=统计学模型【模型定义】+怎么做的优化的结果。统计学模型一样的基础上,随机初始化数值不一样,最后结果不一样。每个模型都有一定的偏移,但是方差【噪音】每次优化,做n个模型取均值降低方差,可能效果比较好。

  25. 实际训练数据是噪音越少越好,人工假造的数据可以稍微加一点噪声。

  26. 训练数据集是不平衡的,如果真实世界就是不平衡的,那可以做好主流的就可以;如果只是因为采样导致数据比较少,可以通过加权让数据集变平衡【最简单数据多复制几遍让训练样本均衡;也可以loss加权,小类别加大权重】。

  27. 验证集loss先下降再上升,说明是过拟合了。

练习题 https://blog.csdn.net/weixin_45496725/article/details/136445995

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值