CNN-RNN 混合回归预测及 MATLAB 实现:从 13 个特征到一个房价(含CNN-LSTM、CNN-GRU、CNN-BiLSTM)

如果给你一套房子的 13 个具体特征(比如房间数、周边环境、税率等),你能估算出这套房子值多少钱吗?

在这个任务中,输入是 13 个不同维度的信息,而输出只有一个连续变化的数值。因为我们要预测的是一个连续的数值范围,而不是"高价/低价"这样的类别标签,所以这是一个标准的回归预测问题

为了完成这个映射,我们将使用神经网络。但有趣的是,一提到 CNN(卷积神经网络),大家通常会想到图像处理;一提到 RNN(循环神经网络),大家往往会想到文本或时间序列。

那么,把它们俩拼在一起,为什么能用来预测房价呢?

其实,回归任务只规定了最终要输出一个连续数值,并没有限制中间必须使用哪种网络结构。它们两者的结合有着非常明确的分工:CNN 负责"找局部",RNN 负责"看整体",最后的回归层负责"给估价"。

具体来说,CNN 可以先用一个小窗口,从相邻的特征中提取局部的组合信息;接着,RNN 把这些局部特征从头到尾读一遍,整理成一份对这套房子的整体认知;最后,全连接层将这份整体认知压缩成一个具体的房价数值。

这篇文章不堆砌复杂的公式。我们将跟随一套真实的房屋样本,一步步看看这 13 个输入特征,是如何在网络中层层转换,最终变成一个预测房价的。


一、认识数据:506 套房子的全貌

在搭建网络之前,我们得先看看数据长什么样。

本项目使用的是经典的波士顿房价数据集。我们从 housing.txt 文件中读取了 506 条数据。每一行代表一套房子,前 13 列是房屋的特征参数,第 14 列是它真实的房价目标值:

复制代码
data = load('housing.txt');
X = data(:, 1:13);
Y = data(:, 14);

此时,输入数据 X 的形状是 506×13(506 套房屋,每套 13 个特征)。目标数据 Y 的形状是 506×1(每套房屋对应一个真实房价)。

为了让模型能够输出我们想要的单一连续数值,我们在后续搭建神经网络时,会在网络的末端设置一个单值输出节点。这就确立了本任务的核心身份:单输出回归

(额外提一句:你可能听说过时间序列预测,那是另一种数据组织方式,比如用过去 24 个小时的数据预测下一个小时。但本文我们没有构造历史时间窗口,所有特征都是同一时刻的静态属性,主线就是纯粹的房价回归预测。)


二、数据准备:划分与归一化

在 13 个原始特征真正送入网络之前,我们还需要做两次关键的数据整理。

第一次整理:划分数据集 拿到 506 条数据后,我们首先要将其拆分为"训练集"、"验证集"和"测试集"。 为了保证每次运行代码时,数据的划分结果都完全一致(具备可复现性),我们在代码中固定了随机种子(例如设为 42)。在这个固定的随机规则下,数据被稳定地划分成了:

  • 训练集(344 条):用于让网络学习并修改参数。
  • 验证集(60 条):在训练过程中充当"模拟考",检查模型有没有走偏或过拟合。
  • 测试集(102 条):绝对保密,直到模型彻底训练完毕后,才用来做最终的"高考"评估。

第二次整理:归一化处理 数据划分完成后,紧接着进行归一化。 因为 13 个特征的单位和数值范围差异巨大(比如有的特征值是 0.1,有的是 400),如果不做处理,数值极大的特征会掩盖掉数值小的特征。我们将它们缩放到相近的范围内。

复制代码
[XTrainMap, PSX] = mapminmax(XTrainRaw', 0, 1);
XValMap  = mapminmax('apply', XValRaw', PSX);
XTestMap = mapminmax('apply', XTestRaw', PSX);

请注意这里的细节:我们只使用训练集来计算缩放的比例参数(PSX),然后直接把这套参数应用到验证集和测试集上。这样能严格防止未来数据的信息泄露。

处理完成后,我们通过 num2cell 函数把数据整理成网络需要的序列格式。对于单独一套房屋来说,此刻的数据形状是 1×13:即 1 个输入通道,包含 13 个排列位置的特征数值。

准备工作就绪,网络的第一道工序------CNN 开始介入。


三、CNN 出场:从 13 个特征中寻找局部组合

先来看看这套混合网络的整体结构图:

数据从左侧进入。CNN 首先登场,它的任务是"局部阅读"。

关于CNN的基本原理,我在之前的文章中有过详细的讲解,大家可以在专栏里找一找。

在本项目中,我们设置卷积核的宽度为 3。你可以把卷积核想象成一把短尺,一次只能盖住 3 个相邻的特征。它先看第 1、2、3 列,计算出一个结果;然后向右移动一格,看第 2、3、4 列,依此类推,直到滑动到末尾。

在代码中,这部分的定义非常直观:

复制代码
sequenceInputLayer(1, 'Name', 'input')
convolution1dLayer(3, 16, 'Padding', 'same', 'Name', 'conv')

sequenceInputLayer(1) 接收我们刚才准备好的 1×13 的数据。 convolution1dLayer 中,3 代表短尺的宽度,16 代表我们同时使用了 16 把不同的短尺(即 16 个卷积通道)。为什么要用 16 把?因为不同的通道在训练后,会专门负责捕捉不同类型的特征组合。

通过设置 Padding='same',网络会在边缘自动补齐,保证卷积前后的长度不缩水。所以,当一行 13 个原始特征穿过 CNN 后,就变成了 16 行、每行 13 个局部响应值。数据形状变成了 16×13。

此时,CNN 并没有去预测房价,它只是把 13 个孤立的原始数字,翻译成了 16 组更丰富的局部组合特征。

接着,这 16×13 的数据会经过 ReLU 激活层最大池化层 (Max Pooling) 继续筛选:

  • ReLU:负责把负数的响应直接归零,只保留有用的正向响应。
  • 最大池化:窗口设为 2,每次在相邻的两个响应里挑一个最大的留下,过滤掉较弱的信息。(由于代码中使用了 Padding='same' 且默认步长为 1,池化后的长度仍然是 13,形状保持 16×13 不变)。

局部特征提取完毕,接下来就该 RNN 上场汇总了。


四、LSTM 接力:将局部响应整理成全局记忆

前面我们一直提到 RNN 负责汇总前后信息,而在本项目的实际代码中,我们使用的是 RNN 家族里最强大、最常用的一种变体------LSTM(长短期记忆网络)

CNN 交给 LSTM 的是 16×13 的数据(13 个位置,每个位置有 16 个局部特征)。LSTM 的工作方式是"按顺序阅读"。

如图所示,LSTM 从第 1 个位置开始读,一直读到第 13 个位置。在读取每一个位置时,它都会做两件事:

  1. 接收当前位置的 16 个局部特征。
  2. 回顾上一个位置传递过来的"内部记忆"。

结合这两者,LSTM 会决定哪些信息该遗忘,哪些新信息该记下,然后把更新后的记忆继续向后传。

复制代码
lstmLayer(32, 'OutputMode', 'last', 'Name', 'rnn')

代码中,我们把隐藏单元数设为 32。OutputMode='last' 是一个关键设置:它意味着我们不需要 LSTM 边读边输出,而是让它一路读到最后一个(第 13 个)位置。当整套房屋的信息都被消化完毕后,LSTM 最终交出一份由 32 个数字组成的"最终记忆状态"。

这 32 个数字,就是网络对这套房子提取出的高度浓缩的整体表示


五、回归输出:从 32 个内部记忆到一个房价

现在,网络手里捏着 32 个浓缩特征,但我们真正需要的是一个具体的房价。全连接层和回归层负责这最后的一跃:

复制代码
fullyConnectedLayer(1, 'Name', 'fc')
regressionLayer('Name', 'out')

fullyConnectedLayer(1) 直接把这 32 个数字,通过加权组合,融合成唯一的一个数值。 由于我们的目标房价在训练前做过归一化,此时网络吐出的也是一个归一化后的值。程序在最后会使用之前保存的 PSY 参数进行反归一化,把它还原回真实的房价标尺。

回顾一下,一套房屋的数据尺寸是如何一步步演变的:

复制代码
1×13 个归一化特征
  ↓ (CNN 局部提取)
16×13 个卷积响应
  ↓ (池化层筛选)
16×13 个筛选响应
  ↓ (LSTM 顺序阅读并汇总)
32 个全局记忆状态
  ↓ (全连接层压缩)
1 个预测房价

这就是 CNN-RNN 混合结构能够预测房价的底层逻辑。


六、严谨的考官:为什么测试集必须保密?

在训练过程中,模型对训练数据会越来越熟悉,甚至可能把一些偶然的噪声"死记硬背"下来(即过拟合)。

为了防止这种情况,验证集起到了监督作用。项目设置了 ValidationPatience=50,如果在连续 50 次验证检查中,验证集上的预测误差都没有改善,训练就会提前叫停。这个数值不能太小,否则模型可能还没学充分就被叫停;也不能无限大,否则容易在训练集上越学越熟,泛化反而变差。

而右侧的"测试集",全程都被一把大锁锁住。它绝对不参与日常的训练和调参。只有当模型完全定型后,测试集才会解锁,作为最后的"盲盒考试"来检验模型的真正实力。只有这样测出来的成绩,才具备说服力。


七、MATLAB 实战:运行结果与指标分析

在 MATLAB 完整版中(以 2026 年 7 月 5 日运行结果为例,随机种子 42),训练共迭代了 1190 次。

先看看训练集的拟合效果:

左图中,两条曲线(真实房价与预测房价)整体走势非常贴合,说明模型抓住了主要的规律,但在高房价区域依然有一些偏差。右图显示误差基本集中在 0 附近。

再来看看从未参与过训练的测试集"高考成绩":

测试集曲线跟随了总体趋势,但不如训练集贴合得那么紧密。这是一个非常克制且真实的结论:模型确实学到了规律并完成了预测,但精度仍有进一步提升的空间。

核心评估指标如下表:

数据集 MAE RMSE MAPE
训练 2.3356 3.2949 11.5475% 0.8703
验证 3.0800 4.0447 17.7270% 0.8202
测试 2.6770 3.7228 14.6245% 0.8316
  • MAE (平均绝对误差):测试集为 2.6770,表示平均每套房预测偏离了 2.7 左右。
  • RMSE (均方根误差):对离谱的巨大错误惩罚更重,测试集为 3.7228。
  • R² (决定系数):越接近 1 越好,测试集得分为 0.8316,说明模型能解释房价 83% 以上的波动。

从散点图看,点越靠近对角线预测越准。可以明显观察到,低、中价位预测较好,但高房价区域的点比较分散,出现了"低估"现象。R² 指标反映不了这种局部瑕疵,散点图把这些细节摊在阳光下了。


八、"一行代码"完成 CNN-RNN 回归预测

原理讲明白之后,真正自己动手时,麻烦才刚刚开始。

你需要划分训练集、验证集和测试集,需要避免归一化时偷看测试集,还要逐层核对 CNN 与 LSTM 的输入尺寸。模型训练完,还得反归一化、计算指标、画预测曲线和误差图。任何一个环节漏掉,程序都可能报错;即使程序不报错,结果也可能不可信。

为了让大家把精力放在数据和模型本身,而不是反复拼接这些固定流程,我把整套 CNN-RNN 回归过程封装成了 FunRegCNNRNN 函数。设置好数据和参数后,真正启动模型只需要一行:

复制代码
[foreData, foreDataTrain, info] = FunRegCNNRNN(X, Y, options);

这一行代码背后,会依次完成数据划分、训练集归一化、CNN-RNN 网络搭建、模型训练、提前停止、测试集预测、指标计算和结果绘图。

函数的三个输入也很直接:

  • X:输入数据,尺寸为"样本数 × 特征序列长度"。本文中就是 506×13。
  • Y:每个样本对应的连续目标值,尺寸为"样本数 × 1"。
  • options:数据划分、网络结构、训练过程、归一化和绘图输出的设置。

三个输出分别是:

  • foreData:测试集预测结果。
  • foreDataTrain:训练集预测结果。
  • info:训练、验证、测试的样本索引,评价指标以及训练过程记录。

以本文的房价数据为例,完整调用代码如下:

复制代码
%% 1. 读取数据
data = load('housing.txt');
X = data(:, 1:13);       % 506 套房,每套 13 个特征
Y = data(:, 14);         % 每套房对应 1 个真实房价

%% 2. 设置数据划分
options.rTrain = 0.80;               % 80% 作为训练+验证数据,其余作为最终测试集
options.validationRatio = 0.15;      % 从训练+验证数据中再划出 15% 做验证
options.shuffle = true;              % 独立样本可打乱;严格时间序列通常不要打乱
options.seed = 42;                   % 固定随机种子,保证结果尽量可复现

%% 3. 设置 CNN-RNN 网络
options.networkType = 'LSTM';        % MATLAB 版支持 LSTM、GRU、BiLSTM
options.convChannels = 16;           % 卷积通道数,越大能学更多局部模式,也更容易过拟合
options.kernelSize = 3;              % CNN 每次观察 3 个相邻位置,必须为正奇数
options.poolSize = 2;                % 池化窗口,设为 1 基本等价于不池化
options.rnnHidden = 32;              % LSTM/GRU 隐藏单元数,越大记忆容量越强

%% 4. 设置训练参数
options.epochs = 150;                % 最大训练轮数,可能因为早停提前结束
options.lr = 0.01;                   % Adam 学习率,太大易震荡,太小收敛慢
options.batchSize = 32;              % 批尺寸,大一些更稳定但占内存
options.earlyStoppingPatience = 50;  % 验证损失连续不改善 50 次就停止
options.mapflag = 'on';              % 自动归一化与反归一化,只用训练集拟合归一化参数
options.figflag = 'on';              % 自动绘制并保存结果图,批量调参可设为 off

%% 5. 一行代码开始训练与预测
[foreData, foreDataTrain, info] = FunRegCNNRNN(X, Y, options);

看起来参数不少,但它们各自只回答一个很具体的问题。

rTrain 和 validationRatio 管的是数据怎么分。比如这里 rTrain=0.80 的意思不是"80% 都拿去训练",而是先把 80% 样本划成"训练+验证数据",剩下 20% 留作最终测试集;然后再从这 80% 里拿出 15% 做验证集。训练集负责更新网络参数,验证集负责观察模型是不是开始过拟合,测试集则要等训练结束后才第一次参与评估。

networkType、convChannels、kernelSize、poolSize 和 rnnHidden 管的是模型容量。convChannels 越大,CNN 能提取的局部模式越多;kernelSize 越大,CNN 一次看的相邻位置越宽;poolSize 越大,中间特征压缩越强;rnnHidden 越大,LSTM 或 GRU 的记忆容量越强。它们不是越大越好,数据量不够时,容量太大反而可能让训练集很好、测试集变差。

epochs、lr、batchSize 和 earlyStoppingPatience 管的是训练节奏。学习率太大,损失可能上下震荡;学习率太小,训练会很慢。最大训练轮数只是上限,真正是否提前停下来,要看验证损失是否连续多次不改善。mapflag='on' 则会自动完成归一化和反归一化,但归一化参数只从训练集里学,避免模型提前"偷看"验证集和测试集。

MATLAB 版可以使用哪几种 RNN?

这里还要单独说明一下:本项目 MATLAB 版的循环网络模块一共支持 LSTM、GRU 和 BiLSTM 三种结构,通过 networkType 参数即可切换:

复制代码
options.networkType = 'LSTM';     % 单向 LSTM,本文默认设置
options.networkType = 'GRU';      % GRU,结构相对精简
options.networkType = 'BiLSTM';   % 双向 LSTM,同时汇总两个方向的信息

实际使用时三行只保留一行即可。LSTM 是本文演示采用的默认结构;GRU 的门控结构更精简;BiLSTM 会从两个方向阅读整段输入,但参数量和计算量也会增加。哪一种更好不能只看名字,需要在相同的数据划分下比较验证集和测试集表现。

需要注意,当前 MATLAB 核心函数只接受这三个名称,不支持普通 RNN 或 BiGRU;这也是 MATLAB 版与 Python 版实现上的一处区别。

函数运行结束后,你不必再手工整理一堆零散结果。程序会自动给出训练集、验证集和测试集上的 MAE、RMSE、MAPE、R² 等指标,同时生成预测曲线、误差分布、真实值与预测值散点图、指标对比图以及训练损失曲线。

至此,13 个原始特征已经走完了完整旅程:先由 CNN 捕捉局部组合,再由 LSTM 汇总前后信息,最后由全连接层输出一个连续房价。前面讲的是这条流水线为什么成立,这一节解决的则是如何把它真正跑起来。

如何获取完整代码

本文使用的完整 MATLAB 代码(包括核心函数、演示脚本、数据集和评价函数)可以在以下网址获取:

https://www.khsci.com/docs/index.php/2026/07/05/cnn-rnn-regression-matlab/

代码包含完整的 CNN-RNN 混合回归核心函数、详细的参数说明和使用示例、波士顿房价数据集、自动生成的可视化图表,以及 MAE、RMSE、MAPE、R² 等完善的性能评估指标。

相关推荐
白日做梦Q1 小时前
PyTorch从零手写CNN实现NEU钢材表面缺陷检测(完整实战·单篇完结)
人工智能·pytorch·cnn
西西弗Sisyphus21 小时前
计算机视觉领域 选择 卷积神经网络 还是选择 Vision Transformer
计算机视觉·cnn·transformer
轩渃1 天前
Claude Fable5回归即翻车:跑分暴跌、安全拦截、账单猫腻,AI模型的信任危机
人工智能·安全·回归
星马梦缘2 天前
机器学习与模式识别 第十六章 Transformers 考点压缩
人工智能·机器学习·cnn·transformer·attention·注意力机制·mlp
星马梦缘2 天前
机器学习与模式识别 第十六章 Transformers 模拟卷及答案
人工智能·机器学习·cnn·transformer·attention·注意力机制·mlp
直接冲冲冲3 天前
59-VGG
深度学习·神经网络·cnn
2zcode3 天前
免费开源项目文档:基于HSV颜色空间和卷积神经网络的交通标志识别系统设计与实现
人工智能·深度学习·cnn
技术小黑3 天前
CNN算法实战系列08 | ResNeXt-50算法实战与猴痘病识别
人工智能·算法·cnn
AI科技星3 天前
32维超复数流形中意识信息场与物质耦合的拓扑动力学
人工智能·学习·算法·数据挖掘·回归·乖乖数学·全域数学