DL基础补全计划(三)---模型选择、欠拟合、过拟合

2024-06-16 06:58

本文主要是介绍DL基础补全计划(三)---模型选择、欠拟合、过拟合,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

PS:要转载请注明出处,本人版权所有。

PS: 这个只是基于《我自己》的理解,

如果和你的原则及想法相冲突,请谅解,勿喷。

环境说明
  • Windows 10
  • VSCode
  • Python 3.8.10
  • Pytorch 1.8.1
  • Cuda 10.2

前言


  在前文中,我们已经接触了两种回归模型,也接触了深度学习中的一些常见的概念。其中有趣的信息是,我们在《DL基础补全计划(二)—Softmax回归及示例(Pytorch,交叉熵损失)》中已经发现了,在softmax回归的时候,我们使用一个线性的隐藏层在其数据集上都能够达到不错的不错的准确率,这里的不错是指瞎猜和我们的模型推测的准确率,一个是10%,一个是80%左右。这至少说明了我们这个分类模型是有效的。其实后续我们就会更换线性隐藏层为其他层来实现我们的模型,比如:CNN、RNN等等,不同的隐藏层是后续我们要接触和学习的内容,这里不先做详解。

  我们假设我们已经设计出了许多的不同隐藏层的模型,这个时候有一个重要的问题就是选择哪一个模型为我们实际的要应用的模型,本文将会介绍一些方法来实现怎么选择模型的问题。





一些基本概念简介


  基本概念简介:

  • 训练误差 是指模型在参数更新后,在训练集上做一次测试,算出的和真实值的误差。

  • 泛化误差 是指模型在真实数据分布下,算出的和真实值的误差,但是一般情况下数据是无穷多的,我们只能够采集一些真实数据,并算出泛化误差。常见的情况是我们构造一个测试集来计算泛化误差。

  • 欠拟合 模型拟合能力差,训练误差和泛化误差差异小,但是两个误差都比较大,一般来说,就是模型基本没有学习到我们需要学习的规律和特征。

  • 过拟合 训练误差小,泛化误差大。一般来说就是在训练集上学习的太过分了,类似强行记住了训练集上的所有规律和特征,导致泛化能力太弱了。

  一般来说欠拟合的话,就是换网络,加深加大网络等解决问题,欠拟合其实很明显,解决方向比较明确。

  其实我们更多是遇到过拟合,因为随着发展,我们的模型越来越深和宽,但是我们能够收集到的数据是有限的,导致了我们的模型可能出现‘死记硬背’下我们的训练集,然后泛化能力就令人担忧,为了缓解这个问题,后续我们将会介绍几种缓解过拟合的方法。

  下面我们将会通过一个实例来体会一下正常拟合、欠拟合、过拟合。





一个正常拟合、过拟合、欠拟合的实例


  这里我们通过pytorch的高级API来设计一个线性规划的实例。

  首先通过如下的代码生成 Y = ( X 3 / 3 ! ) ∗ W 1 + ( X 2 / 2 ! ) ∗ W 2 + X ∗ W 3 + b + ϵ , ϵ = N ( 0 , 0. 1 2 ) Y=(X^3/3!)*W1 + (X^2/2!)*W2 + X*W3 + b + \epsilon, \epsilon=N(0, 0.1^2) Y=(X3/3!)W1+(X2/2!)W2+XW3+b+ϵ,ϵ=N(0,0.12)的特征和标签。

def synthetic_data(w, num_examples): #@save"""⽣成y = (X1^3/3!)*W1 + (X2^2/2!)*W1 + X3*W3 + b + 噪声。"""X = np.random.normal(0, 1, (num_examples, 1))y = np.dot(X**3/np.math.factorial(3), w[0]) + np.dot(X**2/np.math.factorial(2), w[1]) + np.dot(X/np.math.factorial(1), w[2]) + w[3]# 噪声y += np.random.normal(0, 0.1, y.shape)return X, y.reshape((-1, 1))

  然后通过自定义Pytorch层,通过传入参数N,计算N项多项式的结果。

class TestLayer(nn.Module):def __init__(self, n, **kwargs):super(TestLayer, self).__init__(**kwargs)self.n = nself.w_array = nn.Parameter(torch.tensor( np.random.normal(0, 0.1, (1, n))).reshape(-1, 1))self.b = nn.Parameter(torch.tensor(np.random.normal(0, 0.1, 1)))def cal(self, X, n):X = X.reshape(batch_size, 1, 1)Y = self.bfor i in range(n):# print(X.shape)# print(self.w_array.shape)# print(Y.shape)Y  = Y + torch.matmul(X**(i + 1)/torch.tensor(np.math.factorial(i + 1)), self.w_array[i])return Ydef forward(self, x):return self.cal(x, self.n)class TestNet(nn.Module):def __init__(self, n):super(TestNet, self).__init__()self.test_net = nn.Sequential(TestLayer(n))   def forward(self, x):return self.test_net(x)

  最终完整代码如下:

import torch
from torch import nn
import numpy as np
import matplotlib.pyplot as plt
from torch.utils import data
from matplotlib.pyplot import MultipleLocatorfig, ax = plt.subplots()
xdata0, ydata0 = [], []
xdata1, ydata1 = [], []
line0, = ax.plot([], [], 'r-', label='TrainError')
line1, = ax.plot([], [], 'b-', label='TestError')def init_and_show():ax.set_xlabel('epoch')ax.set_ylabel('loss')ax.set_title('Train/Test Loss')ax.set_xlim(0, epochs)ax.set_ylim(0.05, 100)ax.set_yscale('log')# y_locator = MultipleLocator(0.1)# ax.yaxis.set_major_locator(y_locator)ax.legend([line0, line1], ('TrainError', 'TestError'))# ax.legend([line1], ('TestError', ))line0.set_data(xdata0, ydata0)line1.set_data(xdata1, ydata1)plt.show()def synthetic_data(w, num_examples): #@save"""⽣成y = X1^3*W1 + X2^2*W1 + X3*W3 + b + 噪声。"""X = np.random.normal(0, 1, (num_examples, 1))y = np.dot(X**3/np.math.factorial(3), w[0]) + np.dot(X**2/np.math.factorial(2), w[1]) + np.dot(X/np.math.factorial(1), w[2]) + w[3]# 噪声y += np.random.normal(0, 0.1, y.shape)return X, y.reshape((-1, 1))class TestLayer(nn.Module):def __init__(self, n, **kwargs):super(TestLayer, self).__init__(**kwargs)self.n = nself.w_array = nn.Parameter(torch.tensor( np.random.normal(0, 0.1, (1, n))).reshape(-1, 1))self.b = nn.Parameter(torch.tensor(np.random.normal(0, 0.1, 1)))def cal(self, X, n):X = X.reshape(batch_size, 1, 1)Y = self.bfor i in range(n):# print(X.shape)# print(self.w_array.shape)# print(Y.shape)Y  = Y + torch.matmul(X**(i + 1)/torch.tensor(np.math.factorial(i + 1)), self.w_array[i])return Ydef forward(self, x):return self.cal(x, self.n)class TestNet(nn.Module):def __init__(self, n):super(TestNet, self).__init__()self.test_net = nn.Sequential(TestLayer(n))   def forward(self, x):return self.test_net(x)# copy from d2l/torch.py
def load_array(data_arrays, batch_size, is_train=True):"""Construct a PyTorch data iterator."""dataset = data.TensorDataset(*data_arrays)return data.DataLoader(dataset, batch_size, shuffle=is_train)# def data_loader(batch_size, features, labels):
#     num_examples = len(features)
#     indices = list(range(num_examples))
#     np.random.shuffle(indices) # 样本的读取顺序是随机的#     for i in range(0, num_examples, batch_size):
#         j = np.array(indices[i: min(i + batch_size, num_examples)])
#         yield torch.tensor(features.take(j, 0)), torch.tensor(labels.take(j)) # take函数根据索引返回对应元素def train(dataloader, model, loss_fn, optimizer):size = train_examplesnum_batches = train_examples / batch_sizetrain_loss_sum = 0for batch, (X, y) in enumerate(dataloader):# move X, y to gpuif torch.cuda.is_available():X = X.to('cuda')y = y.to('cuda')# Compute prediction and losspred = model(X)loss = loss_fn(pred, y)# Backpropagationoptimizer.zero_grad()loss.backward()optimizer.step()train_loss_sum += loss.item()if batch % 5 == 0:loss, current = loss.item(), batch * len(X)print(f"loss: {loss:>7f}  [{current:>5d}/{size:>5d}]")print(f"Train Error: \n Avg loss: {train_loss_sum/num_batches:>8f} \n")return train_loss_sum/num_batchesdef test(dataloader, model, loss_fn):num_batches = test_examples / batch_sizetest_loss = 0with torch.no_grad():for X, y in dataloader:# move X, y to gpuif torch.cuda.is_available():X = X.to('cuda')y = y.to('cuda')pred = model(X)test_loss += loss_fn(pred, y).item()test_loss /= num_batchesprint(f"Test Error: \n Avg loss: {test_loss:>8f} \n")return test_lossif __name__ == '__main__':device = 'cuda' if torch.cuda.is_available() else 'cpu'print('Using {} device'.format(device))true_w1 = [1.65]true_w2 = [-2.46]true_w3 = [3.54]true_b = 0.78    test_examples = 100train_examples = 100num_examples = test_examples + train_examplesf1, labels = synthetic_data([true_w1, true_w2, true_w3, true_b], num_examples)print(f1.shape)print(labels.shape)num_weight = 3l1_loss_fn = torch.nn.MSELoss()learning_rate = 0.01model = TestNet(num_weight)optimizer = torch.optim.SGD(model.parameters(), lr=learning_rate)model = model.to(device)print(model)epochs = 1500model.train()batch_size = 10train_data = (torch.tensor(f1[:train_examples,]), torch.tensor(labels[:train_examples,]))test_data = (torch.tensor(f1[train_examples:,]), torch.tensor(labels[train_examples:,]))train_dataloader = load_array(train_data ,batch_size, True)test_dataloader = load_array(test_data ,batch_size, True)# verify dataloader# for x,y in train_dataloader:#     print(x.shape)#     print(y.shape)#     print(torch.matmul(x**3, torch.tensor(true_w1, dtype=torch.double)) + torch.matmul(x**2, torch.tensor(true_w2, dtype=torch.double)) + torch.matmul(x, torch.tensor(true_w3, dtype=torch.double)) + true_b)#     print(y)#     breakmodel.train()for t in range(epochs):print(f"Epoch {t+1}\n-------------------------------")train_l = train(train_dataloader, model, l1_loss_fn, optimizer)test_l = test(test_dataloader, model, l1_loss_fn)ydata0.append(train_l*10)ydata1.append(test_l*10)xdata0.append(t)xdata1.append(t)print("Done!")init_and_show()param_iter = model.parameters()print('W = ')print(next(param_iter)[: num_weight, :])print('b = ')print(next(param_iter))

  注意,此最终代码首先生成了100个训练集和100个测试集。通过num_weight可以控制参与训练的多项式个数,话句话说,可以控制参与拟合训练的参数个数。下面通过三个说明我们来看看,不同num_weight下,TrainErr和TestErr和迭代次数,参与拟合训练的参数的关系。



正常拟合(num_weight = 3)

  当num_weight = 3时,运行我们的训练脚本,我们可以清楚的看到,我们拟合出来的结果和我们的真实参数是几乎一样的。同时我们也可以看到TrainErr和TestErr快速的收敛接近0而且差别不是很大。

rep_img


欠拟合(num_weight = 1)

  当num_weight = 1时,运行我们的训练脚本,我们可以清楚的看到,损失图像到了一定程度就不下降了,不能够收敛。

rep_img
过拟合(num_weight = 20)

  当num_weight = 20时,按照我们的猜测,我们的模型应该会出现过拟合。

  正常过拟合现象, 注意观察最终输出前面3项的w和b和真实w和b存在差异。

rep_img

  从我多次的实验的结果来看,除了上面的真实出现的过拟合情况,还有一些情况是,不会出现过拟合现象,如下图。注意观察最终输出前面3项的w和b和真实w和b。

rep_img

  我们通过观察,发现了w的4到20项参数接近于0,前面3项的w和b和真实w和b是比较接近的,因此我们猜测没有出现过拟合的原因是w的4到20项的权重在整个表达式中占比非常小,因此不会过拟合。可以直接理解为w的4到20项的权重为0。

  注意过拟合这个例子,需要多次运行才会出现过拟合现象,其是波动的,其实就是我们初始化的参数充满了随机性,导致了不容易收敛。而欠拟合和正常拟合的例子不管你怎么运行,都能稳定的得到结果。





后记


  这里我们从模型选择的角度出发,发现了我们训练的过程中会出现的3种现象,欠拟合,正常拟合,过拟合。其中正常拟合状态下的模型是我们需要的。

  对于欠拟合来说,就是参与训练的参数少了,换句话说我们的模型太简单了,不能够代表我们要学习的特征,导致完全不能够收敛。

  对于过拟合来说,远不止我们看到的这么简单和清晰。在这里我们只是看到了一个主要的导致训练出现大波动的原因就是参数过多,这种情况下会出现过拟合现象。由于在后面的模型中,参数都是成百上千,我们不可能一个个尝试,因此在后续,我们还会学习一些手段来抑制过拟合现象。

  这里我们也要引出一个问题,我们知道模型的复杂度(参数个数)在一个特定数据集上可能会导致过拟合,那么我们除了控制模型复杂度之外,还有其他的方案可以选择吗?

参考文献

  • https://github.com/d2l-ai/d2l-zh/releases (V1.0.0)
  • https://github.com/d2l-ai/d2l-zh/releases (V2.0.0 alpha1)



打赏、订阅、收藏、丢香蕉、硬币,请关注公众号(攻城狮的搬砖之路)
qrc_img

PS: 请尊重原创,不喜勿喷。

PS: 要转载请注明出处,本人版权所有。

PS: 有问题请留言,看到后我会第一时间回复。

这篇关于DL基础补全计划(三)---模型选择、欠拟合、过拟合的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1065759

相关文章

RedHat运维-Linux文本操作基础-AWK进阶

你不用整理,跟着敲一遍,有个印象,然后把它保存到本地,以后要用再去看,如果有了新东西,你自个再添加。这是我参考牛客上的shell编程专项题,只不过换成了问答的方式而已。不用背,就算是我自己亲自敲,我现在好多也记不住。 1. 输出nowcoder.txt文件第5行的内容 2. 输出nowcoder.txt文件第6行的内容 3. 输出nowcoder.txt文件第7行的内容 4. 输出nowcode

一份LLM资源清单围观技术大佬的日常;手把手教你在美国搭建「百万卡」AI数据中心;为啥大模型做不好简单的数学计算? | ShowMeAI日报

👀日报&周刊合集 | 🎡ShowMeAI官网 | 🧡 点赞关注评论拜托啦! 1. 为啥大模型做不好简单的数学计算?从大模型高考数学成绩不及格说起 司南评测体系 OpenCompass 选取 7 个大模型 (6 个开源模型+ GPT-4o),组织参与了 2024 年高考「新课标I卷」的语文、数学、英语考试,然后由经验丰富的判卷老师评判得分。 结果如上图所

Vim使用基础篇

本文内容大部分来自 vimtutor,自带的教程的总结。在终端输入vimtutor 即可进入教程。 先总结一下,然后再分别介绍正常模式,插入模式,和可视模式三种模式下的命令。 目录 看完以后的汇总 1.正常模式(Normal模式) 1.移动光标 2.删除 3.【:】输入符 4.撤销 5.替换 6.重复命令【. ; ,】 7.复制粘贴 8.缩进 2.插入模式 INSERT

零基础STM32单片机编程入门(一)初识STM32单片机

文章目录 一.概要二.单片机型号命名规则三.STM32F103系统架构四.STM32F103C8T6单片机启动流程五.STM32F103C8T6单片机主要外设资源六.编程过程中芯片数据手册的作用1.单片机外设资源情况2.STM32单片机内部框图3.STM32单片机管脚图4.STM32单片机每个管脚可配功能5.单片机功耗数据6.FALSH编程时间,擦写次数7.I/O高低电平电压表格8.外设接口

大语言模型(LLMs)能够进行推理和规划吗?

大语言模型(LLMs),基本上是经过强化训练的 n-gram 模型,它们在网络规模的语言语料库(实际上,可以说是我们文明的知识库)上进行了训练,展现出了一种超乎预期的语言行为,引发了我们的广泛关注。从训练和操作的角度来看,LLMs 可以被认为是一种巨大的、非真实的记忆库,相当于为我们所有人提供了一个外部的系统 1(见图 1)。然而,它们表面上的多功能性让许多研究者好奇,这些模型是否也能在通常需要系

(超详细)YOLOV7改进-Soft-NMS(支持多种IoU变种选择)

1.在until/general.py文件最后加上下面代码 2.在general.py里面找到这代码,修改这两个地方 3.之后直接运行即可

ps基础入门

1.基础      1.1新建文件      1.2创建指定形状      1.4移动工具          1.41移动画布中的任意元素          1.42移动画布          1.43修改画布大小          1.44修改图像大小      1.5框选工具      1.6矩形工具      1.7图层          1.71图层颜色修改          1

人工和AI大语言模型成本对比 ai语音模型

这里既有AI,又有生活大道理,无数渺小的思考填满了一生。 上一专题搭建了一套GMM-HMM系统,来识别连续0123456789的英文语音。 但若不是仅针对数字,而是所有普通词汇,可能达到十几万个词,解码过程将非常复杂,识别结果组合太多,识别结果不会理想。因此只有声学模型是完全不够的,需要引入语言模型来约束识别结果。让“今天天气很好”的概率高于“今天天汽很好”的概率,得到声学模型概率高,又符合表达

智能客服到个人助理,国内AI大模型如何改变我们的生活?

引言 随着人工智能(AI)技术的高速发展,AI大模型越来越多地出现在我们的日常生活和工作中。国内的AI大模型在过去几年里取得了显著的进展,不少独创的技术点和实际应用令人瞩目。 那么,国内的AI大模型有哪些独创的技术点?它们在实际应用中又有哪些出色表现呢?此外,普通人又该如何利用这些大模型提升工作和生活的质量和效率呢?本文将为你一一解析。 一、国内AI大模型的独创技术点 多模态学习 多

OpenCompass:大模型测评工具

大模型相关目录 大模型,包括部署微调prompt/Agent应用开发、知识库增强、数据库增强、知识图谱增强、自然语言处理、多模态等大模型应用开发内容 从0起步,扬帆起航。 大模型应用向开发路径:AI代理工作流大模型应用开发实用开源项目汇总大模型问答项目问答性能评估方法大模型数据侧总结大模型token等基本概念及参数和内存的关系大模型应用开发-华为大模型生态规划从零开始的LLaMA-Factor