pytorch基础操作(六)利用L2范数(权重衰减)解决线性模型过拟合问题

news2024/9/28 5:31:56

一、一些概念

训练误差(training error) 是指,模型在训练数据集上计算得到的误差。
泛化误差(generalization error) 是指,模型应⽤在同样从原始样本的分布中抽取的⽆限多数据样本时,模型误差的期望。

我们永远不能准确地计算出泛化误差。在实际中,我们只能通过将模型应⽤于⼀个独⽴的测试集来估计泛化误差,该测试集由随机选取的、未曾在训练集中出现的数据样本构成。

模型复杂性:通常对于神经⽹络,我们认为需要更多训练迭代的模型⽐较复杂,⽽需要“早停”(earlystopping)的模型(即较少训练迭代周期)就不那么复杂。

验证集:
我们决不能依靠测试数据进⾏模型选择。然⽽,我们也不能仅仅依靠训练数据来选择模型,因为我们⽆法估计训练数据的泛化误差。解决此问题的常⻅做法是将我们的数据分成三份,除了训练和测试数据集之外,还增加⼀个验证数据集(validation dataset),也叫验证集(validation set)。
但现实是验证数据和测试数据之间的边界十分模糊,因此我们实际上是在使⽤应该被正确地称为训练数据和验证数据的数据集,并没有真正的测试数据集。

K折交叉验证:
当训练数据稀缺时,我们甚⾄可能⽆法提供⾜够的数据来构成⼀个合适的验证集。这个问题的⼀个流⾏的解决⽅案是采⽤K折交叉验证。
这⾥,原始训练数据被分成K个不重叠的⼦集。然后执⾏K次模型训练和验证,每次在K − 1个⼦集上进⾏训练,并在剩余的⼀个⼦集(在该轮中没有⽤于训练的⼦集)上进⾏验证。
最后,通过对K次实验的结果取平均来估计训练和验证误差。

⽋拟合(underfitting): 训练误差和验证误差都很严重,但它们之间仅有⼀点差距。
过拟合(overfitting): 当我们的训练误差明显低于验证误差时要⼩⼼,这表明严重的过拟合.
注意,过拟合并不总是⼀件坏事。特别是在深度学习领域,众所周知,最好的预测模型在训练数据上的表现往往⽐在保留(验证)数据上好得多。

是否过拟合或⽋拟合可能取决于模型复杂性和可⽤训练数据集的⼤⼩。
(1)模型复杂性
⾼阶多项式函数⽐低阶多项式函数复杂得多。⾼阶多项式的参数较多,模型函数的选择范围较⼴。
因此在固定训练数据集的情况下,⾼阶多项式函数相对于低阶多项式的训练误差应该始终更低(最坏也是相等)
(2)数据集的⼤⼩
训练数据集中的样本越少,我们就越有可能(且更严重地)过拟合。
随着训练数据量的增加,泛化误差通常会减⼩。此外,⼀般来说,更多的数据不会有什么坏处。

二、过拟合和欠拟合案例

%matplotlib inline

import os
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"
import math
import numpy as np
import torch
from torch import nn


'''
二、过拟合和欠拟合

给定x,我们将使⽤以下三阶多项式来⽣成训练和测试数据的标签:

y = 5 + 1.2x − 3.4 * x^2/2! + 5.6 * x^3 / 3! + ϵ

噪声项ϵ服从均值为0且标准差为0.1的正态分布。
'''

# 1、为训练集和测试集各⽣成100个样本。
n_train = 100
n_test = 100
# 多项式的最大阶数
max_degree = 20

true_w = np.zeros(max_degree)
true_w[0:4] = np.array([5, 1.2, -3.4, 5.6])



features = np.random.normal(size=(n_train + n_test, 1))
np.random.shuffle(features)
poly_features = np.power(features, np.arange(max_degree).reshape(1, -1))
for i in range(max_degree):
    poly_features[:, i] /= math.gamma(i + 1) # gamma(n)=(n-1)!

# labels的维度:(n_train+n_test,)
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]
]

true_w,features[:2], poly_features[:2, :], labels[:2]

在这里插入图片描述

# 2、评估给定数据集上的损失
from AccumulatorClass import Accumulator
from torch.utils import data

def evaluate_loss(net, data_iter, loss):
    metric = Accumulator(2)
    for X,y in data_iter:
        out = net(X)
        y = y.reshape(out.shape)
        l = loss(out, y)
        metric.add(l.sum(), l.numel())
    return metric[0] / metric[1]
# 3、定义训练函数
def load_array(data_arrays, batch_size, is_train=True):
    """构造⼀个PyTorch数据迭代器"""
    dataset = data.TensorDataset(*data_arrays)
    return data.DataLoader(dataset, batch_size, shuffle=is_train)


from EpochTrainClass import epoch_train
from matplotlib import pyplot as plt
from IPython import display


def set_axes(axes, xlabel, ylabel, xlim, ylim, xscale, yscale, legend):
    """设置matplotlib的轴"""
    axes.set_xlabel(xlabel)
    axes.set_ylabel(ylabel)
    axes.set_xscale(xscale)
    axes.set_yscale(yscale)
    axes.set_xlim(xlim)
    axes.set_ylim(ylim)
    if legend:
        axes.legend(legend)
    axes.grid()

class Animator:
    """在动画中绘制数据"""
    def __init__(self, xlabel=None, ylabel=None, legend=None, xlim=None,
                 ylim=None, xscale='linear', yscale='linear',
                 fmts=('-', 'm--', 'g-.', 'r:'), nrows=1, ncols=1,
                 figsize=(3.5, 2.5)):


        # 增量地绘制多条线
        if legend is None:
            legend = []
        self.fig, self.axes = plt.subplots(nrows, ncols, figsize=figsize)
        if nrows * ncols == 1:
            self.axes = [self.axes, ]
        # 使用lambda函数捕获参数
        self.config_axes = lambda: set_axes(
            self.axes[0], xlabel, ylabel, xlim, ylim, xscale, yscale, legend)
        self.X, self.Y, self.fmts = None, None, fmts

    def add(self, x, y):
        # 向图表中添加多个数据点
        if not hasattr(y, "__len__"):
            y = [y]
        n = len(y)
        if not hasattr(x, "__len__"):
            x = [x] * n
        if not self.X:
            self.X = [[] for _ in range(n)]
        if not self.Y:
            self.Y = [[] for _ in range(n)]
        for i, (a, b) in enumerate(zip(x, y)):
            if a is not None and b is not None:
                self.X[i].append(a)
                self.Y[i].append(b)
        self.axes[0].cla()
        for x, y, fmt in zip(self.X, self.Y, self.fmts):
            self.axes[0].plot(x, y, fmt)
        self.config_axes()
        display.display(self.fig)
        display.clear_output(wait=True)


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 = load_array(
        (train_features, train_labels.reshape(-1,1)),
         batch_size
    )
    test_iter = load_array(
        (test_features, test_labels.reshape(-1,1)),
         batch_size,
        is_train=False
    )
    trainer = torch.optim.SGD(net.parameters(), lr=0.01)
    animator = Animator(xlabel='epoch', ylabel='loss', yscale='log',
                      xlim=[1, num_epochs], ylim=[1e-3, 1e2],
                       legend=['train', 'test'])

    for epoch in range(num_epochs):
        epoch_train(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())

(1)正常情况

'''
  (1) 正常情况
  我们将⾸先使⽤三阶多项式函数,它与数据⽣成函数的阶数相同。
  结果表明,该模型能有效降低训练损失和测试损失。学习到的模型参数也接近真实值w = [5, 1.2, −3.4, 5.6]。
'''

# 使用3阶多项式进行拟合
# 从多项式特征中选择前4个维度,即1,x,x^2/2!,x^3/3!
train(
    poly_features[:n_train, :4],
    poly_features[n_train:, :4],
    labels[:n_train],
    labels[n_train:]
)

在这里插入图片描述

(2)线性函数拟合(⽋拟合)

'''
  (2)线性函数拟合(⽋拟合)

  在最后⼀个迭代周期完成后,训练损失仍然很⾼。
'''

# 从多项式特征中选择前2个维度,即1和x
train(
    poly_features[:n_train, :2],
    poly_features[n_train:, :2],
    labels[:n_train],
    labels[n_train:]
)

在这里插入图片描述

(3)⾼阶多项式函数拟合(过拟合)

'''
  (3)、⾼阶多项式函数拟合(过拟合)

  虽然训练损失可以有效地降低,但测试损失仍然很⾼。
'''

# 从多项式特征中选取所有维度
train(
    poly_features[:n_train, :],
    poly_features[n_train:, :],
    labels[:n_train],
    labels[n_train:],
    num_epochs=1500
)

在这里插入图片描述

三、利用权重衰减解决过拟合

最常⽤⽅法是将其范数作为惩罚项加到最⼩化损失的问题中。将原来的训练⽬标最⼩化训练标签上的预测损失,调整为最⼩化预测损失和惩罚项之和。

在训练参数化机器学习模型时,权重衰减(weight decay)是最⼴泛使⽤的正则化的技术之⼀,它通常也被称为L2正则化(是更为⼀般的Lp范数的特殊情况)。

L2正则化线性模型构成经典的岭回归(ridge regression)算法,L1正则化线性回归是统计学中类似的基本模型,通常被称为套索回归(lasso regression)。

使⽤L2范数的⼀个原因是它对权重向量的⼤分量施加了巨⼤的惩罚。这使得我们的学习算法偏向于在⼤量特征上均匀分布权重的模型。在实践中,这可能使它们对单个变量中的观测误差更为稳定。
相⽐之下,L1惩罚会导致模型将权重集中在⼀⼩部分特征上,⽽将其他权重清除为零。这称为特征选择(feature selection),这可能是其他场景下需要的。

(1) 权重衰减在高维线性回归的应用-手动实现

'''
             d
  y = 0.05 + ∑  0.01 *  xi + ϵ
             i=1
'''

'1、模型生成数据'
# 设置d为200,训练集数据为20,测试集数据100,5为批量大小
n_train, n_test, num_inputs, batch_size = 20, 100, 200, 5
true_w, true_b = torch.ones((num_inputs, 1)) * 0.01, 0.05


def get_data(w, b, num_samples=1000):
    """⽣成y=Xw+b+噪声"""
    X = torch.normal(0, 1, (num_samples, len(w)))
    y = torch.matmul(X, w) + b
    y += torch.normal(0, 0.01, y.shape)
    return X,y.reshape((-1, 1))

features,labels = get_data(true_w,true_b)

# 生成训练集和测试集
train_data = get_data(true_w, true_b, n_train)
test_data = get_data(true_w, true_b, n_test)

# 构造PyTorch数据迭代器
train_iter = load_array(train_data, batch_size)
test_iter = load_array(test_data, batch_size)
'2、初始化模型参数'
def init_params():
    w = torch.normal(
        0,1,size=(num_inputs, 1),requires_grad=True
    )
    b = torch.zeros(
        1,requires_grad=True
    )
    return [w,b]

w,b = init_params()
'3、定义L2范数惩罚'

# 实现这⼀惩罚最⽅便的⽅法是对所有项求平⽅后并将它们求和。
def l2_penalty(w):
    return torch.sum(w.pow(2)) / 2

l2_penalty(w)
'4、训练'

# 计算线性模型的输出, 我们只需计算输入特征(f{X})和模型权重(w)的矩阵-向量乘法后加上偏置(b)
def line_alg(X, w, b):
    return torch.matmul(X, w) + b

# 需要计算损失函数的梯度,所以我们应该先定义损失函数。
def square_loss(y_hat,y):
    return (y_hat - y.reshape(y_hat.shape)) ** 2 / 2


# 小批量随机梯度下降
def mini_batch_sgd(params, lr, batch_size):

    # torch.no_grad是一个类一个上下文管理器,disable梯度计算。
    # disable梯度计算对于推理是有用的,当你确认不会调用Tensor.backward()的时候。这可以减少计算所用内存消耗。
    # 这个模式下,每个计算结果的requires_grad=False,尽管输入的requires_grad=True。
    with torch.no_grad():
        for param in params:
            param -= lr * param.grad / batch_size
            param.grad.zero_()

def train_process(lambd):
    # 初始化权重和偏置
    w, b = init_params()
    net = lambda X: line_alg(X, w, b)
    loss = square_loss

    num_epochs = 100
    lr = 0.003
    animator = Animator(
        xlabel='epochs', ylabel='loss', yscale='log',
       xlim=[5, num_epochs], legend=['train', 'test']
    )

    for epoch in range(num_epochs):
        for X,y in train_iter:
            # 增加l2范数
            l = loss(net(X), y) + lambd * l2_penalty(w)
            l.sum().backward()
            mini_batch_sgd([w, b], lr, batch_size)
        if (epoch + 1) % 5 == 0:
              animator.add(epoch + 1,
                           (evaluate_loss(net, train_iter, loss),evaluate_loss(net, test_iter, loss))
                           )

    print('w的L2范数为:', torch.norm(w).item())
# 忽略正则化项进行训练

train_process(lambd=0)
# 这⾥训练误差有了减少,但测试误差没有减少,这意味着出现了严重的过拟合。

在这里插入图片描述

# 使用权重衰减进行训练

train_process(lambd=3)
# 注意,在这⾥训练误差增⼤,但测试误差减⼩。这正是我们期望从正则化中得到的效果。

在这里插入图片描述

(2) 权重衰减在高维线性回归的应用-高级api

'''
             d
  y = 0.05 + ∑  0.01 *  xi + ϵ
             i=1
  由于权重衰减在神经⽹络优化中很常⽤,深度学习框架为了便于我们使⽤权重衰减,将权重衰减集成到优化算法中,以便与任何损失函数结合使⽤。
'''
def train_high_api(wd):
    net = nn.Sequential(
        nn.Linear(num_inputs,1)
    )
    for param in net.parameters():
        param.data.normal_()
    loss = nn.MSELoss(reduction='none')

    num_epochs = 100
    lr = 0.003

    # 偏置参数没有衰减
    trainer = torch.optim.SGD(
        [{"params":net[0].weight,'weight_decay': wd},
        {"params":net[0].bias}],
        lr=lr
    )

    animator = Animator(
        xlabel='epochs', ylabel='loss', yscale='log',
       xlim=[5, num_epochs], legend=['train', 'test']
    )

    for epoch in range(num_epochs):
        for X,y in train_iter:
            trainer.zero_grad()
            l = loss(net(X), y)
            l.mean().backward()
            trainer.step()
        if (epoch + 1) % 5 == 0:
              animator.add(epoch + 1,
                           (evaluate_loss(net, train_iter, loss),evaluate_loss(net, test_iter, loss))
                           )

    print('w的L2范数为:',  net[0].weight.norm().item())

# 这些图看起来和我们从零开始实现权重衰减时的图相同。然⽽,它们运⾏得更快,更容易实现。
# 正则化是处理过拟合的常⽤⽅法:在训练集的损失函数中加⼊惩罚项,以降低学习到的模型的复杂度
train_high_api(0)

在这里插入图片描述

train_high_api(3)

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux】Linux基本指令

Linux基本指令1.ls指令2.pwd指令3.cd指令3.touch 指令4.mkdir指令5. rmdir指令 && rm 指令5.1 rmdir5.2 rm6.tree 指令7.man指令8.cp指令9.mv指令10. cat指令11.echo指令12.more指令13.less指令14.wc指令15.head指令16.tail指令17. | (管道指令)…

每天五分钟机器学习:PCA算法如何确定数据压缩降维的最佳维度?

本文重点 上节课程中我们已经学习了pca算法,已经知道了如何将n维特征变量降到k维,k是PCA算法的一个参数,也被称为主成分的数量。那么现在就产生了一个问题,这个问题就是如何选择K,因为PCA要做的就是要尽量减少投射的平均均方误差,所以K的选择很关键。 平均均方误差 其中…

22.MongoDB删除操作效率及相关问题验证

最近遇到一个了一个MongoDB数据删除的问题,需要一次性删除上线即1.5年前~1年前的数据且之后每天清空一年过期的数据。在数据量比较大的情况下何种方式的删除效率最高是一个值得研究的问题,本文通过实际测试找出其中规律。 本文采用腾讯云mongodb集群进行…

基于java的连连看游戏设计-计算机毕业设计

项目介绍 基于java设计的连连看游戏规则是模仿网络上的最普通的连连看游戏,主要是鼠标点击两次的图片能否消去的问题。当前,前提是点击两张相同的图片,若是点击的是同一张图片或者两张不同的图片,则不予处理。在两张相同的图片所…

这份pdf成功让我拿下了蚂蚁金服、字节跳动、小米等大厂的offer

关于程序员,除了做项目来提高自身的技术之外,还有一种提升自己的专业技能就是:多!看!书! 小编整理出一篇Java进阶架构师之路的核心知识,同时也是面试时面试官必问的知识点,篇章也是…

点云可视化工具

点云可视化工具 平时查看点云文件主要是用CloudCompare,基本上也就是打开看看这个点云大概是个什么样子,很少会在CloudCompare对点云进行处理,它可以直接将点云拖进软件进行显示也还是挺方便的。但是还是有点点不是很方便的地方,…

Linux从入门到进阶学习(Ⅲ):Linux权限管控

目录 1 root用户 1.1 su 1.2 sudo 2 用户和用户组 3 查看权限控制信息 4 修改权限控制 4.1 chmod命令 4.2 chown命令 1 root用户 1.1 su root用户即超级管理员 su [-] [用户名] 切换到root用户,exit退回普通用户从 -:可选&#xff0…

社会网络分析工具—— Gephi 或 NetworkX的简单介绍和比较(源自GPTchat)

文章目录我的原始需求两个工具介绍二者比较下载和使用后续 使用教程我的原始需求 如何对不同认知课堂的教师提问行为序列进 行社会网络分析,计算不同认知层级提问行为的中间 中心度和接近中心度, 在进行社会网络分析时,您首先需要收集数据并…

ArcGIS基础:栅格转ASCII校正简单栅格坐标

【校正栅格坐标】 有一些数据量比较小的栅格数据,位置发生了偏移,可以使用【转换工具】对其位置进行校正。 先看一下原始数据: 有2个栅格数据,如下所示,两个数据本身是一个数据,只不过发生了偏移&#x…

java+ssh+mysql银行收银管理系统

项目介绍: 本系统为基于jspmysql的银行管理系统,包含业务员、管理员登录权限,功能如下: 业务员:可以银行系统,可以查看所有客户账户信息、可以新开账户、销户、修改账户信息、存款、取款、转账等功能。 …

C51——通过震动传感器模块让继电器的灯亮起来

继电器触发 模块可以通过跳线设置高电平触发或者低电平触发(默认) 首先明确继电器的工作逻辑 怎么控制“闭合”开关呢 怎么控制“断开”开关呢 NO口 是常开端 NC 常闭端 COM 公共端 通过IO口向继电器发出低电平 ,使得COM口和NO口连接&…

05. XSS漏洞利用

05. XSS漏洞利用 XSS漏洞利用(上) 获取cookie 什么是cookie? **定义:**cookie是指某些网站为了辨别用户身份而储存在用户本地终端(Client Side)上的数据(通常经过加密) **作用&…

jsp+ssm计算机毕业设计宠物医院信息管理系统【附源码】

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: JSPSSM mybatis Maven等等组成,B/S模式 Mave…

垃圾回收相关概念

文章目录1. System.gc()的理解2.内存溢出与内存泄漏3. Stop The World4. 并发与并行5. 安全点与安全区域的说明6. 强引用、软引用、弱引用、虚引用1. System.gc()的理解 2.内存溢出与内存泄漏 3. Stop The World 4. 并发与并行 5. 安全点与安全区域的说明 6. 强引用、软引用、弱…

jsp+ssm计算机毕业设计大学生家教服务推荐系统【附源码】

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: JSPSSM mybatis Maven等等组成,B/S模式 Mave…

nuxt.js如何将访问的外部ip修改为本地ip? Do you want Code to open the external website?

问题描述: 提示:Do you want Code to open the external website? 译文:是否希望代码打开外部网站? 说明我们nuxt.js访问的是别人的ip地址,而并不是自己的ip 问题解决: 方法一: 查看packa…

Python中GDAL批量绘制多时相栅格遥感影像的像元时间序列曲线图

本文介绍基于Python中gdal模块,对大量多时相栅格图像,批量绘制像元时间序列折线图的方法。 首先,明确一下本文需要实现的需求:现有三个文件夹,其中第一个文件夹存放了某一研究区域原始的多时相栅格遥感影像数据&#x…

C/C++socket网络编程

目录tcp和udp通信流程图socket函数bind函数listen函数accept函数connect函数recv、recvfrom、read函数send、write、sendto、sendmsg函数close、shutdown函数htonl、ntohl、htons、ntohs本地主机和网络字节序转换inet_addr、inet_aton、inet_ntop,IP地址转换函数set…

Spark-概述+快速上手+运行环境

文章目录概述Spark and HadoopSpark or Hadoop核心模块快速上手Spark运行环境Local(本地)Standalone模式搭建和使用提交参数说明配置历史服务配置高可用(HA)Yarn模式K8S & Mesos 模式部署模式对比端口号概述 Spark 是一种基于内存的快速、通用、可扩…

红米Note7pro安装TWRP及安装PixelExperience系统

参考:https://www.youtube.com/watch?vjB9ksQrxr20&ab_channelTechnologyVikram 所需文件: 一. youtube:Vikram--OTG优盘 (https://www.youtube.com/watch?vjB9ksQrxr20&ab_channelTechnologyVikram) 1.刷入TWRP a. 准备工作:打开…