深度学习之图像回归(二)

ops/2025/3/1 17:31:42/

前言

这篇文章主要是在图像回归(一)的基础上对该项目进行的优化。(一)主要是帮助迅速入门 理清一个深度学习项目的逻辑 这篇文章则主要注重在此基础上对于数据预处理和模型训练进行优化前者会通过涉及PCA主成分分析  特征选择 后者通过正则化

数据预处理

数据预处理的原因

思路链

未经过处理的原始数据存在一些问题-> 对数据进行处理 (涉及多种方法)->提升模型性能

数据可能存在的问题

  • 冗余信息:数据中可能存在重复的特征或高度相关的特征,这些信息不会为模型提供额外的价值,反而会增加计算负担和过拟合的风险。

  • 噪声数据:数据中可能存在错误、异常值或随机波动,这些数据点可能来源于测量失误、数据录入错误或自然噪声。

  • 高重复度:某些特征或数据点可能高度相似或重复,导致模型对这些重复模式过度拟合,而无法泛化到新的数据。

  • 缺失值:数据中可能存在缺失值,需要通过填充或删除等方式处理。

  • 不一致性:数据可能来自不同的源,格式或单位不一致,需要进行标准化或归一化处理。

最终的目的

  • 提高模型训练效率:减少特征数量和数据维度,降低计算复杂度。

  • 增强模型泛化能力:去除噪声和冗余信息,使模型更专注于数据中的真实规律。

  • 提升模型可解释性:减少特征数量后,模型更容易理解和解释。

数据预处理的方式

这里的几种方式就是对原始数据特征提取的方式不同 或者对

PCA主成分分析

核心:化繁为简 直击核心

是什么:

降维 用更少的维度就表现出多维数据的特征 

为什么:

简化数据 去除冗余

怎么做:

步骤1:整理数据

首先,要把数据整理好,让每个特征的平均值为0。这就好比把一堆杂乱的数据“摆平”,让它们在一个统一的水平线上。这一步在数学上叫做“数据标准化”。

步骤2:找最重要的方向

接下来,PCA会找数据中最“重要”的方向。这里的“重要”是指数据变化最大的方向。想象一下,你有一堆点,PCA会找一条线,让这些点在这条线上的投影尽可能地分散。这条线就是第一个“主成分”。

步骤3:找次重要的方向

找到第一个最重要的方向后,PCA会继续找第二个最重要的方向,但这个方向要和第一个方向垂直(也就是完全独立)。这样,PCA会依次找到多个方向,每个方向都比前一个方向“重要”一点。

步骤4:简化数据

最后,PCA会把数据投影到这些最重要的方向上,得到新的特征。这些新特征就是“主成分”。因为主成分的数量通常比原始特征少,所以数据就被简化了。

def get_feature_importance_with_pca(feature_data, k=4, column=None):"""使用PCA进行特征降维,并找出对前k个主成分影响最大的原始特征。参数:feature_data (pd.DataFrame or np.ndarray): 特征数据。k (int): 选择的主成分数目,默认为4。column (list, optional): 特征名称列表。如果feature_data是DataFrame,则可以省略此参数。返回:X_new (np.ndarray): 降维后的特征数据。selected_features (list of lists): 对每个主成分影响最大的原始特征及其载荷。"""# 如果提供了列名或feature_data是DataFrame,获取列名if column is None and hasattr(feature_data, 'columns'):column = feature_data.columns.tolist()elif column is None:raise ValueError("Column names must be provided if feature_data is not a DataFrame.")# 数据标准化scaler = StandardScaler()feature_data_scaled = scaler.fit_transform(feature_data)# 应用PCApca = PCA(n_components=k)X_new = pca.fit_transform(feature_data_scaled)return X_new

载荷?

每个数据的原始特征在主成分中的重要性。比如用苹果 梨子 香蕉做一杯混合果汁 最后得到的果汁的新特征包含上述三种水果的味道和营养 这就是一个主成分 是原始特征的线性组合

载荷指的是每种水果在果汁中的贡献比例

比如

50% 苹果 + 30% 橙子 + 20% 香蕉

这里的“50%”、“30%”和“20%”就是每种水果在果汁中的“贡献比例”,在PCA中,这些比例就叫做载荷

特征选择

是什么

假设你有一个数据集,数据集中有很多特征(比如100个特征),但并不是所有特征都对预测结果有帮助。有些特征可能是多余的,或者和目标没有关系。特征选择的目的就是从这100个特征中挑选出最有用的几个特征(比如5个或10个),让模型只用这些重要的特征来学习。

为什么
  1. 减少计算量:模型只需要处理少量的特征,训练速度会更快。

  2. 提高模型性能:去除无关特征后,模型可以更专注于重要的特征,预测效果可能会更好。

怎么办
def get_feature_importance(feature_data, label_data, k =4,column = None):model = SelectKBest(chi2, k=k)      # 定义k个特征值的卡方检验得到的特征选择模型feature_data = np.array(feature_data, dtype=np.float64) # 特征值转化成浮点数 X_new = model.fit_transform(feature_data, label_data)   # 用这个函数选择k个最佳特征print('x_new', X_new)scores = model.scores_                # scores即每一列与结果的相关性indices = np.argsort(scores)[::-1]        # 默认小到大排序 [::-1]表示反转一个列表或者矩阵。 最终实现大到小排序 if column:                            # 打印k_best_features = [column[i+1] for i in indices[0:k].tolist()]         # 选中这些列 打印print('k best features are: ', k_best_features)return X_new, indices[0:k]                  # 返回选中列的特征和他们的下标。
卡方检验

适用场景:

①非负。卡方检验基于频率分布,计算的是观测频数与期望频数之间的差异。因此,它要求输入特征必须是非负的,例如布尔值(0或1)或频率计数

②离散特征。对于连续的需要先进行离散化处理。

③简单易用 快速筛选 

λ的设置
  • λ 过大 → 模型欠拟合(训练和验证损失都很高)
  • \lambdaλ 过小 → 可能过拟合(训练损失低但验证损失高)
  • \lambdaλ 适中 → 平衡拟合与泛化

 正则化的影响路径
  • 正向传播:损失值=预测误差 + 0.00075×所有权重平方和
  • 反向传播:梯度=原始梯度 + 2×0.00075×权重 → 权重会自动变小
  • 物理意义:强制让模型参数趋向于较小的值,抑制模型复杂度

模型训练的优化

模型复杂度的体现

在机器学习中,模型复杂度通常指的是模型的参数数量和参数的大小。一个复杂的模型可能具有以下特点:

  • 参数数量多:模型中有大量的参数,例如深度神经网络中的权重和偏置。

  • 参数值大:模型的参数值(权重)可能很大,这意味着模型对输入特征的响应非常敏感。

复杂模型虽然能够很好地拟合训练数据(甚至可以完美拟合),但往往会过拟合,即在训练数据上表现很好,但在新的、未见过的数据上表现不佳。这是因为复杂模型可能会学习到训练数据中的噪声和细节,而不是数据的真实规律。

为什么要限制模型复杂度

限制模型复杂度的主要目的是提高模型的泛化能力,即让模型在新的、未见过的数据上表现更好。具体原因包括:

  1. 防止过拟合:复杂模型容易过拟合,因为它们可以学习到训练数据中的噪声和细节。通过限制复杂度,模型更倾向于学习数据的基本规律,而不是噪声。

  2. 简化模型:减少模型的参数数量或参数大小,可以使模型更简洁、更易于解释。这也有助于减少计算成本和训练时间。

  3. 提高稳定性:限制复杂度可以减少模型对输入数据的敏感性,使其在面对小的扰动时更加稳定。

如何限制模型复杂度

通过正则化项对损失函数进行约束

原始的损失函数计算

def mseLoss(pred, target, model):loss = nn.MSELoss(reduction='mean')        # 计算loss函数regularization_loss = 0                    # 正则项for param in model.parameters():# regularization_loss += torch.sum(abs(param)) # 参数的绝对值之和 L1正则项regularization_loss += torch.sum(param ** 2)   # 计算所有参数平方 L2正则项return loss(pred, target) + 0.00075 * regularization_loss             # 返回损失。

常见的正则化方法

  1. L1正则化(Lasso)

    • 在损失函数中添加参数的绝对值之和作为惩罚项。

    • 公式:Lossnew​=Loss+λ∑i​∣wi​∣

    • 作用:L1正则化会促使某些参数变为零,从而实现稀疏性(即自动特征选择)。

    • 直观理解:通过惩罚参数的绝对值,强迫模型丢弃一些不重要的特征。

  2. L2正则化(Ridge)

    • 在损失函数中添加参数的平方和作为惩罚项。

    • 公式:Lossnew​=Loss+λ∑i​wi2​

    • 作用:L2正则化会使得所有参数的值变得更小(但不会变为零),从而减少模型对每个特征的依赖。

    • 直观理解:通过惩罚参数的平方,限制参数的大小,使模型更“平滑”。

  3. Dropout

    • 在训练过程中随机丢弃部分神经元的输出。

    • 作用:防止神经元之间的共适应性,减少模型对某些特定输入的依赖,从而提高泛化能力。

  4. Early Stopping

    • 在训练过程中监控模型在验证集上的性能,当性能不再提升时停止训练。

    • 作用:防止模型过度拟合训练数据


http://www.ppmy.cn/ops/162291.html

相关文章

使用DeepSeek+本地知识库,尝试从0到1搭建高度定制化工作流(数据分析篇)

7.3. 数据监控与生成本地知识库 目的:监控新生成的小红书文案,记录每一次生成的小红书文案风格。后续根据输入topic,检索与某一topic有关的文案,可以根据先前的文案风格,生成类似风格的文案。实现思路: 1.…

C++内部类用法介绍

C++ 内部类(Nested Class) 是在另一个类的作用域内定义的类。它的主要作用是封装逻辑关系紧密的类,使其不能被外部直接访问,从而增强封装性和代码的可读性。 1. 内部类的基本语法 #include <iostream>class Outer {public:class Inner {

Python毕业设计选题:基于Python的社区爱心养老管理系统设计与实现_django

开发语言&#xff1a;Python框架&#xff1a;djangoPython版本&#xff1a;python3.7.7数据库&#xff1a;mysql 5.7数据库工具&#xff1a;Navicat11开发软件&#xff1a;PyCharm 系统展示 管理员登录 管理员功能界面 用户管理 身体健康界面 公共书籍界面 借阅信息界面 归还…

springcloud组件调用顺序

Spring Cloud 组件的调用顺序并不是固定不变的&#xff0c;它依赖于具体的业务场景和微服务架构的设计。然而&#xff0c;可以概括出一个典型的微服务架构中 Spring Cloud 组件的调用流程&#xff0c;这个流程大致可以分为以下几个步骤&#xff1a; 服务注册与发现&#xff1a…

JavaEE [特殊字符] TCP协议:三次握手四次挥手全图解

&#x1f31f; 一、TCP核心特性&#xff1a;可靠传输的秘密 1️⃣ 有连接 & 全双工 双向通道&#xff1a;建立连接后&#xff0c;客户端↔服务器可同时收发数据可靠传输三板斧&#xff1a; 确认应答&#xff08;ACK&#xff09; 接收方返回ACK接收序号数据长度示例&#…

齿轮制造的“精密心脏”:蜗杆状砂轮磨齿机探秘

齿轮&#xff0c;被称为工业的“骨骼关节”&#xff0c;其精度直接影响机械系统的寿命与效率。而在齿轮精加工领域&#xff0c;蜗杆状砂轮磨齿机如同一台高精度“雕刻刀”&#xff0c;凭借独特的展成磨削原理&#xff0c;成为汽车变速箱、机器人减速器等关键部件制造的“心脏设…

spring boot 连接FTP实现文件上传

spring boot 连接FTP实现文件上传 maven&#xff1a; <!--ftp--><dependency><groupId>commons-net</groupId><artifactId>commons-net</artifactId><version>3.8.0</version></dependency>接口示例&#xff1a; ApiO…

冒泡排序算法优化

一 概述 冒泡排序是一种简单的交换排序算法,其核心思想是通过相邻元素比较和交换将最大元素逐步移动到数组末尾。 二、基础冒泡排序 void bubbleSort(int arr[], int n) { for (int i = 0; i < n-1; i++) { for (int j = 0; j < n-i-1; j++) { if…