R使用neuralnet做时间序列预测时归一化后出现NaN/NA及结构异常问题
异常原因与修复方案
测试集归一化后出现全NaN、维度错乱
问题成因
- 对矩阵直接调用
lapply的逻辑完全不符合R的遍历规则:R的矩阵本质是带行列维度的数值向量,lapply传入矩阵时会逐元素遍历,不会按列拆分。原本预期每列作为整体传入归一化函数,实际是把每个单独的标量值传给了归一化函数。 - 标量输入下的归一化计算必然产生NaN:当传入的值是单个标量x时,
min(x)=x、max(x)=x,分母max(x)-min(x)=0,计算结果为(x-x)/0 = NaN。22行4列的延时矩阵共88个元素,最终输出自然是1行88列、全为NaN的结构,和实际看到的输出完全一致。 - 附带的子集提取逻辑错误:原代码用
subset(dataDelay, select = c(dataDelay[1], dataDelay[2], dataDelay[3]))提取输入特征时,dataDelay[1]是矩阵的第一个标量元素,不是第一列,就算归一化步骤正常,提取的特征也完全不符合预期,无法正常训练模型。
修复操作
- 替换遍历方式:把
lapply(dataDelay, normalize)改成按列遍历的apply(dataDelay, 2, normalize),强制将每列作为整体传入归一化函数,保证输出是22行4列的结构,避免逐元素计算的问题。 - 矩阵转数据框:也可以先把
dataDelay转成data.frame再传给lapply,此时lapply会按列遍历,和预期逻辑一致。 - 修正子集提取逻辑:将矩阵转成带明确列名的data.frame后,用列名选择输入输出,避免位置索引错位。
全量数据集生成的延时矩阵全为NA
问题成因
- 自定义归一化函数没有处理NA值的逻辑:R原生的
min()/max()默认不会忽略输入里的NA,只要原始汇率序列里有1个空值,计算出的min/max就会返回NA,最终整个归一化序列全为NA,基于这个序列生成的延时矩阵自然全是NA。 - 数据读取环节没有做类型校验:读取csv的时候没有检查第三列的类型,如果第三列被读成字符型、因子型,或者有格式不统一的空单元格、字符串值,会导致min/max计算失效,返回NA,最终归一化结果全NA。
- 这里需要注意:“先对原始序列归一化、再生成延时矩阵”的逻辑本身是正确的,完全不存在顺序问题,反而能避免按列归一化延时矩阵时引入未来数据的统计量、造成数据泄露的常见错误,不要改回先生成矩阵再归一化的顺序。
修复操作
- 优化归一化函数的鲁棒性,增加NA处理逻辑,避免单个NA值导致全量结果失效:
normalize <- function(x, na.rm = TRUE){ min_val <- min(x, na.rm = na.rm) max_val <- max(x, na.rm = na.rm) return((x - min_val)/(max_val - min_val)) } - 增加读取后的校验步骤,避免类型不匹配导致的计算失效:
exchangeData <- read.csv("ExchangeUSDcsv.csv") # 检查第三列的类型 str(exchangeData[,3]) # 非数值类型时强制转换,检查无效值占比 data <- as.numeric(exchangeData[,3]) sum(is.na(data)) # 存在无效值时直接剔除后再做后续处理 data <- na.omit(data)
模型训练的适配修正
原代码的neuralnet(Output~Input, hidden = 1, data = dataDelay)写法不符合neuralnet包的输入要求:这个函数要求公式里的变量名必须和传入data参数的data.frame列名完全匹配,需要把归一化后的延时矩阵设置为带明确列名的data.frame,示例写法:
currencyNorm <- as.data.frame(apply(dataDelay, 2, normalize)) colnames(currencyNorm) <- c("Lag3","Lag2","Lag1","Target") currency_model <- neuralnet(Target ~ Lag1 + Lag2 + Lag3, hidden = 1, data = currencyNorm)
内容的提问来源于stack exchange,提问作者Krellex
相关产品推荐
相关产品推荐

