Python实现Elman RNN与混合RNN神经网络对航空客运量、啤酒产量、电力产量时间序列数据预测可视化对比

news/2025/9/19 18:51:30/文章来源:https://www.cnblogs.com/tecdat/p/19101507

全文链接:https://tecdat.cn/?p=43924
原文出处:拓端数据部落公众号
视频出处:拓端抖音号@拓端tecdat

封面

一、引言

作为长期深耕时间序列预测领域的数据科学家,我们在项目中频繁发现一个共性痛点:多数团队在选择循环神经网络(RNN)架构时,常因不了解不同架构对数据特性的适配性、预处理方法对精度的影响,导致模型落地效果不佳。

本文改编自我们为客户提供的“需求预测优化”项目——不同RNN架构的选型争议成为项目瓶颈。基于此项目的技术沉淀,我们将对比三种经典RNN架构,为学生及从业者提供“从原理到代码”的可落地参考。


研究中,我们基于Python与NumPy手动实现Elman RNN、Jordan RNN和Multi-RNN,针对航空客运量(Air Passengers)、啤酒产量(Beer Production)、电力产量(Electricity Production)、黄金价格(Gold Price)、雅虎股票(Yahoo Stock)五类典型时间序列数据,结合多项式去趋势与Min-Max归一化预处理,通过网格搜索调优超参数,并采用MSE、MAE、RMSE、MAPE四项指标及Friedman检验验证模型有效性。

本文内容源自过往项目技术沉淀与已通过实际业务校验,该项目完整代码与数据已分享至交流社群。阅读原文进群,可与600+行业人士交流成长;还提供人工答疑,拆解核心原理、代码逻辑与业务适配思路,帮大家既懂怎么做,也懂为什么这么做;遇代码运行问题,更能享24小时调试支持。

相关视频

【讲解】共享单车使用量预测:RNN, LSTM,GRU循环神经网络和传统机器学习

三、项目概述

3.1 研究目标

本次研究核心是解决“不同RNN架构如何适配平稳与非平稳时间序列数据”的问题——我们通过对比三种RNN架构的预测效果,分析架构设计、数据预处理步骤、超参数设置对预测精度的影响,最终为实际业务(如销量预测、价格预测)提供架构选型建议。

3.2 核心分析架构

我们选择了三类在时间序列领域应用广泛的RNN架构,重点关注它们捕捉“时间依赖关系”的差异:

  • Elman RNN:通过“隐藏层到自身的循环连接”捕捉短期时间依赖,适合波动较规律、短期趋势明显的数据;
  • Jordan RNN:新增“输出层到隐藏层的反馈环”,能强化对长期依赖的捕捉,适合趋势复杂、周期较长的数据;
  • Multi-RNN:混合Elman的隐藏层循环与Jordan的输出层反馈,试图兼顾短期与长期依赖,属于折中方案。

四、数据集介绍

为确保研究结果的通用性,我们选择了5类特性差异显著的时间序列数据,覆盖“平稳/非平稳”“低频/高频”“周期性/随机性”等典型场景,每类数据均包含原始序列与去趋势后的序列(便于对比预处理效果):

 
  1.  
    # 多项式去趋势函数:通过拟合指定次数的多项式消除时间序列数据中的长期趋势,输出去趋势后的数据和平滑趋势线
  2.  
    # 核心作用:时间序列数据常包含长期增长/下降趋势(如航空客运量逐年增长),这类非平稳数据会导致模型优先学习趋势而非真实波动规律,
  3.  
    # 此函数通过"拟合趋势→剥离趋势"使数据平稳,为后续RNN建模提供更可靠的输入
  4.  
    def polynomial_detrend(data, degree=2):
  5.  
    # 1. 构建时间变量x:用数据的索引(0,1,2,...N-1)作为自变量,代表时间序列的"时间步"
  6.  
    # 转float64类型:避免整数运算的截断误差,提升后续多项式拟合的计算精度
  7.  
    x = np.arange(len(data)).astype('float64')
  8.  
    # 2. 处理原始数据y:将输入的时间序列数据转为float64类型,确保与x的数据类型一致,避免矩阵运算类型不匹配
  9.  
    y = data.astype('float64')
  10.  
     
  11.  
    # 3. 拟合多项式系数:使用numpy的polyfit函数,根据x(时间步)和y(原始数据)拟合degree次多项式
  12.  
    # 输出coeffs:多项式系数数组,顺序为"最高次项系数→常数项"(如degree=2时,coeffs=[a, b, c]对应 a*x² + b*x + c)
  13.  
    # 作用:通过系数描述数据的长期趋势规律
  14.  
    coeffs = np.polyfit(x, y, degree)
  15.  
     
  16.  
    # 4. 生成拟合的趋势线:使用numpy的polyval函数,代入系数coeffs和时间步x,计算每个时间步对应的"理论趋势值"
  17.  
    # 输出trend:与原始数据长度一致的数组,代表拟合出的平滑长期趋势(如航空客运量的逐年增长曲线)
  18.  
    trend = np.polyval(coeffs, x)
  19.  
     
  20.  
    # 5. 计算去趋势后的数据:用原始数据y减去拟合的趋势线trend,剥离长期趋势成分
  21.  
    # 输出detrended:消除了长期趋势的平稳数据(仅保留短期波动,如季节波动、随机波动),适合RNN捕捉时序依赖
  22.  
    detrended = y - trend
  23.  
     
  24.  
    # 返回两个结果:
  25.  
    # - detrended:去趋势后的平稳数据(用于后续模型训练)
  26.  
    # - trend:拟合的长期趋势线(后续可用于将预测结果"还原回原始数据尺度",计算真实场景下的误差指标)
  27.  
    return detrended, trend
 
 

4.1 航空客运量(Air Passengers)

数据为1949-1960年的月度总客运量,具有明显的长期增长趋势和年度周期性(如节假日出行高峰),属于典型非平稳数据。

4.2 啤酒产量(Beer Production)

数据为奥地利月度啤酒产量,季节性波动明显(如夏季销量高),但长期趋势平缓,预处理后易转为平稳数据。

4.3 电力产量(Electricity Production)

数据为月度电力产量,受季节(如夏季空调、冬季取暖)和经济活动影响,波动规律且趋势稳定,对模型的“短期依赖捕捉能力”要求高。

4.4 黄金价格(Gold Price)

数据为日度黄金价格波动,受政策、经济事件影响大,随机性强且长期趋势不明显,考验模型对“突发波动”的适应能力。

4.5 雅虎股票(Yahoo Stock)

数据为日度雅虎股票收盘价,属于高频金融数据,波动剧烈且无明显周期,是三类架构中最难预测的数据集之一。

五、研究方法

5.1 数据预处理

预处理是时间序列预测的关键步骤——非平稳数据会导致模型“学错趋势”,未归一化数据会延缓训练收敛。我们采用两步预处理:

  1. 去趋势(Detrending):用多项式回归拟合数据的长期趋势线(如用numpy.polyfit拟合3次多项式),再用原始数据减去趋势线,将非平稳数据转为平稳数据;
  2. 归一化(Normalization):采用Min-Max scaling将数据压缩到[0,1]区间,公式为normalized_x = (x - x_min) / (x_max - x_min),避免因数据量级差异导致模型偏向预测大值。

相关文章

Python用Transformer、SARIMAX、RNN、LSTM、Prophet时间序列预测对比分析用电量、零售销售、公共安全、交通事故数据

原文链接:https://tecdat.cn/?p=42219


5.2 超参数调优

超参数直接影响模型性能,我们通过“网格搜索”遍历关键参数的组合,选择验证集上精度最高的组合:

  • 输入大小(Input size):即“用前多少个时间步预测下一个步”,测试12、24两个取值(分别对应月度数据的1年、2年窗口);
  • 隐藏层大小(Hidden size):隐藏层神经元数量,测试32、64两个取值(数量过少会欠拟合,过多会过拟合);
  • 学习率(Learning rate):控制参数更新幅度,测试0.001、0.01两个取值(过小训练慢,过大易震荡);
  • 训练轮次(Number of epochs):模型遍历训练集的次数,测试100、200两个取值(过多会过拟合,过少会欠拟合)。

5.3 评估指标

为全面衡量预测精度,我们选择4类常用指标,避免单一指标的局限性:

  • MSE(Mean Squared Error):平均平方误差,公式为MSE = (1/n) * Σ(y_pred - y_true)²,对大误差惩罚更重;
  • MAE(Mean Absolute Error):平均绝对误差,公式为MAE = (1/n) * Σ|y_pred - y_true|,对异常值更稳健;
  • RMSE(Root Mean Squared Error):均方根误差,公式为RMSE = √MSE,单位与原始数据一致,更易解释;
  • MAPE(Mean Absolute Percentage Error):平均绝对百分比误差,公式为MAPE = (1/n) * Σ(|y_pred - y_true|/y_true) * 100%,直接反映预测的百分比偏差。
    同时,我们采用“时间序列拆分”避免数据泄漏:将数据按80%(训练)、20%(测试)拆分(如1949-1957年为训练,1958-1960年为测试),训练集再用“滚动交叉验证”(如每次用前2年数据预测后6个月)评估稳定性。

5.4 关键代码实现(以Elman RNN为例)

我们基于NumPy手动实现RNN(避免直接调用框架,便于理解原理),核心是“前向传播”计算输出和“反向传播”更新权重。以下为Elman RNN的前向传播代码:

 
  1.  
    # Elman RNN类:实现Elman循环神经网络的核心结构
  2.  
    # 核心特点:通过"隐藏层到自身的循环连接"捕捉时间序列中的短期依赖关系,适用于规律波动的时序数据预测
  3.  
    class ElmanRNN:
  4.  
    # 类初始化方法:创建Elman RNN实例时,初始化网络结构参数和权重
  5.  
    # 参数说明:
  6.  
    # input_size:输入层维度(每个时间步的输入数据特征数,比如用前12个月数据预测时,input_size=12)
  7.  
    # hidden_size:隐藏层维度(隐藏层神经元数量,控制网络拟合能力,需根据数据复杂度调整,如32、64)
  8.  
    # output_size:输出层维度(预测结果的特征数,如预测1个时间步的数值时,output_size=1)
  9.  
    # learning_rate:学习率(控制权重更新的步长,默认0.01,过大会导致训练震荡,过小会导致训练缓慢)
  10.  
    def __init__(self, input_size, hidden_size, output_size, learning_rate=0.01):
  11.  
    # 将输入的结构参数赋值为类的属性,方便后续方法调用
  12.  
    self.input_size = input_size # 保存输入层维度
  13.  
    self.hidden_size = hidden_size # 保存隐藏层维度
  14.  
    self.output_size = output_size # 保存输出层维度
  15.  
    self.learning_rate = learning_rate # 保存学习率
  16.  
     
  17.  
    # 权重初始化:采用Xavier分布( Xavier Distribution )初始化核心权重矩阵
  18.  
    # 为什么用Xavier分布?避免初始化权重过大导致信号爆炸,或过小导致信号消失,保证梯度稳定传播
  19.  
    # 1. W_ih:输入层(Input)到隐藏层(Hidden)的权重矩阵
  20.  
    # 维度:(hidden_size, input_size) → 确保输入数据(shape: [batch, input_size])与权重矩阵可矩阵乘法
  21.  
    self.W_ih = xavier_distribution(hidden_size, input_size)
  22.  
    # 2. W_hh:隐藏层(Hidden)到自身的循环权重矩阵(Elman RNN的核心,实现时序依赖传递)
  23.  
    # 维度:(hidden_size, hidden_size) → 上一时刻隐藏层状态(shape: [batch, hidden_size])与该矩阵相乘,传递历史信息
  24.  
    self.W_hh = xavier_distribution(hidden_size, hidden_size)
 
 

六、结果分析

6.1 整体性能可视化

下图展示了三类架构在5个数据集上的综合性能(以RMSE为核心指标,数值越小精度越高):

 
  1.  
    # 最终执行函数关键代码:针对每个RNN架构,基于历史实验结果确定最优超参数,完成数据预处理、模型训练与测试,并输出最终评估指标
  2.  
    # 核心作用:串联超参数选择、数据处理、模型训练测试全流程,实现"最优配置→模型验证"的闭环
  3.  
    def final_runs():
  4.  
    # 遍历RNN架构列表与对应名称列表(zip将两者一一配对,确保每个模型对应正确名称)
  5.  
    # rnns:预先定义的不同RNN架构实例列表(如ElmanRNN、JordanRNN、MultiRNN)
  6.  
    # names:与rnns对应的架构名称列表(用于文件读取、打印标识,如["ElmanRNN", "JordanRNN", "MultiRNN"])
  7.  
    for rnn, name in zip(rnns, names):
  8.  
    # 打印当前正在执行的RNN架构名称,方便控制台追踪执行进度与区分不同模型
  9.  
    print(f'{name}:')
  10.  
     
  11.  
    # 读取历史超参数搜索结果文件(该CSV文件存储了之前超参数组合与对应性能指标)
  12.  
    # 文件路径:results文件夹下,以当前RNN名称命名的CSV文件(如results/ElmanRNN.csv)
  13.  
    with open(f'results/{name}.csv', 'r') as f:
  14.  
    results = [] # 存储处理后的超参数与指标数据(二维列表:每行对应一组超参数+各数据集指标)
  15.  
    lines = f.readlines() # 读取CSV文件所有行(每行是一个字符串,格式为"超参1,超参2,...指标1,指标2...")
  16.  
     
  17.  
    # 逐行处理CSV数据,将字符串格式转为数值格式
  18.  
    for line in lines:
  19.  
    line = line.strip().split(',') # 去除行首尾空格,按逗号分割(拆分超参与指标)
  20.  
    line = [float(x) for x in line] # 将分割后的字符串转为浮点数(CSV读取默认是字符串,需数值化才能计算)
  21.  
    results.append(line) # 将处理后的一行数据(一组超参+指标)加入results列表
  22.  
     
  23.  
    # 针对每个数据集,从历史结果中筛选最优超参数组合
  24.  
    # datasets:数据集名称列表(如["Air Passengers", "Beer Production"...])
  25.  
    # i:数据集索引,用于定位results中当前数据集的指标列
  26.  
    for dataset, i in zip(datasets, range(len(datasets))):
  27.  
    best = [0, np.inf] # 存储当前数据集的最优结果:[最优超参数组合的索引, 最小指标值]
  28.  
    # 初始值设为np.inf(无穷大),确保第一个指标值能被替换
  29.  
     
  30.  
    # 遍历所有超参数组合(results中的每一行),找到当前数据集的最优组合
  31.  
    for j in range(len(results)):
  32.  
    # results[j][i+4]:第j组超参数在第i个数据集上的指标值(前4列是超参,第5列起是各数据集指标,故i+4)
  33.  
    # 逻辑:找指标值最小的组合(假设指标是RMSE/MSE等越小越好的指标)
  34.  
    if(results[j][i+4] < best[1]):
  35.  
    best = [j, results[j][i+4]] # 更新最优索引和最小指标值
  36.  
     
  37.  
    # 提取最优超参数组合:results[best[0]]是最优行数据,前4列是超参数(输入大小、隐藏大小、学习率、epochs)
  38.  
    hyperparameters = results[best[0]][:4]
  39.  
     
  40.  
    # 超参数类型转换:输入大小、隐藏大小、训练轮次必须是整数(从CSV读入是浮点数,需转int)
  41.  
    hyperparameters[0] = int(hyperparameters[0]) # 第0列:输入序列长度(如用前12个月数据预测,值为12)
  42.  
    hyperparameters[1] = int(hyperparameters[1]) # 第1列:隐藏层神经元数量(如32、64)
  43.  
    hyperparameters[3] = int(hyperparameters[3]) # 第3列:训练轮次(epochs)
 
 

6.2 各架构详细表现

6.2.1 Elman RNN
  • 最优表现:在电力产量数据上精度最高(RMSE=0.05,MAPE=8%)——因电力数据短期波动规律,Elman的隐藏层循环能有效捕捉短期依赖;
  • 最差表现:在航空客运量数据上精度最低(RMSE=0.18,MAPE=22%)——因航空数据长期趋势强,Elman缺乏反馈环,无法有效捕捉长期依赖。
6.2.2 Jordan RNN
  • 整体最优:在5个数据集上均排名前2,尤其在航空客运量(RMSE=0.11,MAPE=13%)和黄金价格(RMSE=0.07,MAPE=9%)上表现突出;
  • 核心优势:输出层反馈环能“记住”更早时间步的信息,适合长期趋势复杂或随机性强的数据,这也是它在实际业务中(如年度销量预测)更常用的原因。
6.2.3 Multi-RNN
  • 表现不稳定:在啤酒产量数据上表现较好(RMSE=0.06,MAPE=7%)——因啤酒数据趋势平缓,混合架构能兼顾短期波动;
  • 明显短板:在雅虎股票数据上精度最差(RMSE=0.21,MAPE=25%)——因股票数据波动剧烈,混合架构的“兼顾设计”反而导致对极端值的适配性不足。

6.3 统计显著性验证

为确认“Jordan RNN最优”不是偶然,我们用Friedman检验(一种非参数检验,用于比较多个模型的差异显著性)验证,结果如下:

 
  1.  
     
  2.  
    def friedman_test(data):
  3.  
    k = data.shape[1] # number of models
  4.  
    n = data.shape[0] # number of datasets
  5.  
     
  6.  
    # Convert data to ranks
  7.  
    ranks = np.zeros_like(data)
  8.  
    for i in range(n):
  9.  
    ranks[i] = stats.rankdata(data[i])
  10.  
     
  11.  
    # Calculate R_j (sum of ranks for each model)
  12.  
    R_j = np.sum(ranks, axis=0)
 
 



检验结果显示:p值=0.023(<0.05),说明三类架构的性能差异具有统计显著性;进一步的事后检验(Nemenyi检验)表明,Jordan RNN与Elman RNN、Multi-RNN的差异均显著,而Elman RNN与Multi-RNN的差异不显著。

七、结论

7.1 研究结论

  1. 架构选型建议:Jordan RNN是三类架构中最适合时间序列预测的方案,尤其适合长期趋势复杂(如航空客运量)或随机性强(如黄金价格)的数据,可优先用于实际业务(如供应链预测、金融价格预测);
  2. 预处理重要性:去趋势能使模型精度平均提升15%-20%,未去趋势的模型在非平稳数据上会出现“趋势偏移”(如预测值始终高于实际值);
  3. 未来方向:可将对比范围扩展到LSTM、GRU等现代架构——这些架构通过“门控机制”进一步优化长期依赖捕捉,在更长时间序列(如日度数据预测年度趋势)上可能有更优表现。

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

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

相关文章

4G/Wi-Fi/以太网三网合一,智能融合通信实战案例集

聚焦通信技术落地痛点,4G/Wi-Fi/以太网智能多网融合方案,通过动态负载均衡与故障自愈机制,构建高可用通信链路。实战场景覆盖远程监控、智能交通等领域,技术细节与实施步骤全解析。 本文以Air8000开发板WebSocket应…

关于介绍自己的第一篇随笔

你好啊,我是张家瑞,一名普通平凡的学生。 仔细想来我的兴趣爱好也没什么特别的,也同他人一样爱打打游戏听听音乐,非常喜欢格斗游戏和卡牌游戏,若有喜欢街霸,罪恶装备,游戏王的同学可以找我打(开个玩笑),稍微…

深入解析:N32G43x Flash 驱动移植与封装实践

深入解析:N32G43x Flash 驱动移植与封装实践pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", &qu…

Backblaze上如何传大文件

问题 创建好一个存储桶后,直接点击上传文件即可。但是当文件很大时将会显示:第一次通过以下步骤解决 第一步:准备工作 1.安装命令行工具 (B2 CLI) pip install --upgrade b2检验安装是否成功(可选) b2 version显示:…

解题报告-老逗找基友 (friends)

老逗找基友 (friends) 题目背景 吴老逗有 \(n\) 个基友,位于平面直角坐标系的整点上。每个基友已与其最近的基友(如有多个则取编号最小)建立了双向心灵感应。但这样形成的网络可能不连通,因此吴老逗可以使用爱之魔…

Python_occ 学习记录 | 细观建模(1) - 教程

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

深入解析:uv:用 Rust 重写的极速 Python 包管理器

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

Caused by: java.lang.ClassNotFoundException: org.apache.rocketmq.remoting.common.RemotingUtil

前言 启动报错,打包可以,启动报错; 针对这个问题 可以看下.ClassNotFoundException和NoClassDefFoundError:有啥区别先 org.springframework.beans.factory.BeanCreationException: Error creating bean with name …

VAE In JAX【个人记录向】

和上一篇 SAC In JAX 一样,我们用 JAX 实现 VAE,配置一样,只需要安装符合版本的 torchvision 即可,实现中提供了 tensorboard 记录以及最后的可视化展示,测试集即为最经典的 MNIST,代码如下: import jax import…

BLE蓝牙配网双模式实操:STA+SoftAP技术原理与避坑指南

想让设备同时支持蓝牙快速配网与AP热点备份?STA+SoftAP双模式是关键!本文深度解析技术原理,结合真实项目案例总结实操避坑点,助你一文搞懂双模式配网逻辑,开发少走90%弯路。 本文特别分享蓝牙配网方案: 以Air800…

【小白也能懂】PyTorch 里的 0.5 到底是干啥的?——一次把 Normalize 讲透! - 教程

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

第58天:RCE代码amp;命令执行amp;过滤绕过amp;异或无字符amp;无回显方案amp;黑白盒挖掘

第58天:RCE代码&命令执行&过滤绕过&异或无字符&无回显方案&黑白盒挖掘 案例演示RCE & 代码执行 & 命令执行 RCE-利用&绕过&异或&回显 白盒-CTF-RCE代码命令执行 黑盒-运行-R…

057-Web攻防-SSRFDemo源码Gopher项目等

057-Web攻防-SSRF&Demo源码&Gopher项目等 知识点知识点: 1、SSRF-原理-外部资源加载 2、SSRF-利用-伪协议&无回显 3、SSRF-挖掘-业务功能&URL参数案例演示1、SSRF-原理&挖掘&利用&修复 2…

060-WEB攻防-PHP反序列化POP链构造魔术方法流程漏洞触发条件属性修改

060-WEB攻防-PHP反序列化&POP链构造&魔术方法流程&漏洞触发条件&属性修改 知识点: 1、PHP-反序列化-应用&识别&函数 2、PHP-反序列化-魔术方法&触发规则 3、PHP-反序列化-联合漏洞&P…

059-Web攻防-XXE安全DTD实体复现源码等

059-Web攻防-XXE安全&DTD实体&复现源码等 知识点 XML&XXE-传输-原理&探针&利用&玩法 XML&XXE-黑盒-JS&黑盒测试&类型修改 XML&XXE-白盒-CMS&PHPSHE&无回显什么是XML?…

061-WEB攻防-PHP反序列化原生类TIPSCVE绕过漏洞属性类型特征

061-WEB攻防-PHP反序列化&原生类TIPS&CVE绕过漏洞&属性类型特征知识点 1、PHP-反序列化-属性类型&显示特征 2、PHP-反序列化-CVE绕过&字符串逃逸 3、PHP-反序列化-原生类生成&利用&配合1、…

051-Web攻防-文件安全目录安全测试源码等

051-Web攻防-文件安全&目录安全&测试源码等 知识点1、文件安全-前后台功能点-下载&读取&删除 2、目录安全-前后台功能点-目录遍历&目录穿越演示案例:➢文件安全-下载&删除-案例黑白盒 ➢目录…

Dilworth定理及其在算法题中的应用

1. Dilworth定理 Dilworth定理由数学家Robert P. Dilworth于1950年提出,它描述了偏序集中链和反链之间的关系。偏序集:一个集合 equipped with a partial order(即一个自反、反对称、传递的关系)。 链:偏序集的一…

error: xxxxx does not have a commit checked out

$ git commit -m "test" *error: AW30N does not have a commit checked outfatal: updating files failed解决方法: 删除多余的文件夹AW30N

049-WEB攻防-文件上传存储安全OSS对象分站解析安全解码还原目录执行

049-WEB攻防-文件上传&存储安全&OSS对象&分站&解析安全&解码还原&目录执行-cnblog#文件-解析方案-执行权限&解码还原 1、执行权限文件上传后存储目录不给执行权限 原理:开启禁止目录执行…