<think>我们注意到用户反馈精度很差,差的离谱。这可能是由于多种原因导致的,例如数据预处理不当、网络结构不合适、训练参数设置不当等。为了提高BP神经网络的预测精度,我们可以尝试以下改进措施:
1. **数据划分策略**:之前我们使用的是顺序划分(前75%训练,后25%测试),但时间序列数据通常具有时间依赖性,随机划分可能更合适。不过,用户的数据可能具有时间顺序,所以我们需要确认。这里我们改为随机划分(之前代码已经是随机划分,但用户第二次要求BP时我们改成了顺序划分,这里改回随机划分)。
2. **数据归一化**:之前我们使用了Z-score归一化,但也可以尝试其他归一化方法,如Min-Max归一化。
3. **网络结构**:增加隐藏层神经元数量或层数,或者调整激活函数。
4. **训练算法**:尝试不同的训练算法,如贝叶斯正则化(trainbr)可以改善泛化能力。
5. **早停(Early Stopping)**:使用验证集来防止过拟合。
6. **超参数调整**:调整学习率、训练轮数等。
7. **特征工程**:检查输入特征是否相关,或者进行特征选择。
8. **输出层处理**:由于我们有三个输出,确保网络结构能够同时处理多个输出。
9. **误差计算**:确保在计算MAPE时避免除以0(如果实际值有0,则添加一个很小的数)。
10. **多次训练**:由于神经网络初始化权重随机,多次训练取最佳模型。
由于用户没有提供具体数据,我们只能从方法上改进。下面我们修改代码,采用以下改进:
- 使用随机划分(打乱数据)
- 使用贝叶斯正则化训练算法(trainbr)以减少过拟合
- 增加网络复杂度(两个隐藏层,分别有30和20个神经元)
- 使用早停(通过设置验证集)
- 多次初始化训练并选择最佳模型
注意:贝叶斯正则化训练算法可能会比较慢,但通常能提供更好的泛化能力。
以下是改进后的代码:
```matlab
%% 步骤1: 数据准备
data = readtable('average_FC1_Ageing_part_sampled.xlsx');
time = data{:, 1}; % 第一列: 时间
y_actual = data{:, 2:4}; % 第二至四列: 输出变量
X = data{:, 5:end}; % 第五列至最后: 输入特征
var_names = data.Properties.VariableNames;
%% 步骤2: 数据预处理
% 归一化处理 (Z-score标准化)
X_normalized = normalize(X);
y_normalized = normalize(y_actual);
% 划分训练集和测试集 (7.5:2.5) - 随机打乱
rng(42); % 设置随机种子确保可重复性
n_samples = size(X, 1);
split_ratio = 0.75;
split_idx = randperm(n_samples, round(split_ratio * n_samples));
test_idx = setdiff(1:n_samples, split_idx);
X_train = X_normalized(split_idx, :);
y_train = y_normalized(split_idx, :);
X_test = X_normalized(test_idx, :);
y_test = y_normalized(test_idx, :);
%% 步骤3: 创建BP神经网络模型
% 设置网络结构(两个隐藏层:30和20个神经元)
hiddenLayerSize = [30, 20];
net = fitnet(hiddenLayerSize, 'trainbr'); % 使用贝叶斯正则化
% 配置数据划分(使用一部分作为验证集,用于早停)
net.divideParam.trainRatio = 70/100;
net.divideParam.valRatio = 15/100;
net.divideParam.testRatio = 15/100;
% 设置训练参数
net.trainParam.epochs = 1000; % 最大训练轮数
net.trainParam.showWindow = true; % 显示训练窗口
net.performFcn = 'mse'; % 均方误差作为性能函数
% 多次训练选择最佳模型(5次)
numRuns = 5;
bestPerf = inf;
bestNet = [];
for i = 1:numRuns
fprintf('训练第 %d 次...\n', i);
% 重新初始化网络
net = init(net);
% 训练
[net, tr] = train(net, X_train', y_train');
% 在训练集上预测,计算性能(使用验证集性能作为选择标准)
perf = tr.best_vperf;
if perf < bestPerf
bestPerf = perf;
bestNet = net;
end
end
net = bestNet;
fprintf('最佳验证集性能: %f\n', bestPerf);
%% 步骤4: 预测与评估
% 使用测试集预测
y_pred_normalized = net(X_test')';
% 反归一化
y_pred = y_pred_normalized .* std(y_actual) + mean(y_actual);
y_test_actual = y_test .* std(y_actual) + mean(y_actual);
% 避免实际值为0导致除法错误(添加一个很小的数)
epsilon = 1e-10;
y_test_actual_adjusted = y_test_actual;
y_test_actual_adjusted(y_test_actual_adjusted == 0) = epsilon;
% 计算误差指标
ape = abs((y_test_actual - y_pred) ./ y_test_actual_adjusted) * 100; % 绝对百分比误差
mape = mean(ape, 1); % 平均绝对百分比误差
mpe = mean((y_test_actual - y_pred) ./ y_test_actual_adjusted * 100, 1); % 平均百分比误差
% 显示误差指标
fprintf('=== 误差指标 ===\n');
for i = 1:3
fprintf('输出 %d (%s):\n', i, var_names{i+1});
fprintf(' MAPE = %.2f%%\n', mape(i));
fprintf(' MPE = %.2f%%\n', mpe(i));
end
%% 步骤5: 绘制拟合曲线
figure('Position', [100, 100, 1200, 800])
time_test = time(test_idx);
for i = 1:3
subplot(3, 1, i)
% 按时间排序
[time_sorted, sort_idx] = sort(time_test);
y_actual_sorted = y_test_actual(sort_idx, i);
y_pred_sorted = y_pred(sort_idx, i);
% 绘制原始数据曲线(黑色) - 注意:这里绘制的是整个时间序列的实际值
plot(time, y_actual(:, i), 'k-', 'LineWidth', 1.5)
hold on
% 绘制预测曲线(红色)
plot(time_sorted, y_pred_sorted, 'r-', 'LineWidth', 2)
title(sprintf('输出变量: %s', var_names{i+1}))
xlabel('时间')
ylabel('值')
legend('实际值', '预测值', 'Location', 'best')
grid on
hold off
end
%% 步骤6: 输出结果到Excel
% 创建结果表格
result_table = table();
result_table.Time = time_test;
for i = 1:3
result_table.(['Actual_' var_names{i+1}]) = y_test_actual(:, i);
result_table.(['Predicted_' var_names{i+1}]) = y_pred(:, i);
result_table.(['APE_' var_names{i+1}]) = ape(:, i);
end
% 保存到Excel
writetable(result_table, 'improved_bp_prediction_results.xlsx');
fprintf('结果已保存到 improved_bp_prediction_results.xlsx\n');
%% 步骤7: 保存网络
save('best_bp_network.mat', 'net');
fprintf('最佳神经网络已保存到 best_bp_network.mat\n');
```
### 改进点说明:
1. **数据划分**:使用随机划分,避免时间序列的特定顺序影响(如果数据是时间序列,可能需要考虑时间顺序,但这里用户没有说明,我们假设可以打乱)。
2. **训练算法**:使用贝叶斯正则化(`trainbr`),它可以在训练过程中自动调整正则化参数,从而减少过拟合,提高泛化能力。
3. **网络结构**:增加了隐藏层的神经元数量(第一层30个,第二层20个),以增强模型的表达能力。
4. **多次训练**:由于神经网络初始化权重随机,我们进行5次训练,并选择验证集上性能最好的模型。
5. **验证集**:在训练过程中使用15%的数据作为验证集,用于早停和模型选择。
6. **避免除以0**:在计算APE时,如果实际值为0,则将其替换为一个很小的数(epsilon)以避免计算错误。
7. **训练显示**:打开训练窗口,以便用户观察训练过程。
### 注意事项:
1. **贝叶斯正则化**:该算法可能会比较慢,如果数据量很大,可以考虑减少训练轮数或使用其他算法(如`trainscg`)。
2. **计算资源**:多次训练会增加计算时间,但通常能获得更好的模型。
3. **特征工程**:如果精度仍然很差,可能需要检查输入特征是否与输出相关,或者进行特征选择、降维等操作。
4. **时间序列特性**:如果数据是时间序列,可能需要考虑使用时间序列模型(如LSTM)或引入滞后特征。
如果经过上述改进后精度仍然不理想,可能需要进一步分析数据,或者尝试其他模型。
§§相关问题§§:
1. 在MATLAB中,如何对神经网络进行超参数优化(如隐藏层神经元数量)?
2. 除了BP神经网络,还有哪些神经网络结构适用于回归问题?
3. 如何处理回归预测中的异常值?
4. 如何解释贝叶斯正则化在神经网络中的作用?</think>针对您反馈的精度问题,我将提供一套改进的BP神经网络实现方案。精度差可能由多种原因引起,包括数据预处理不当、网络结构不合理、超参数选择不当等。以下是经过优化的解决方案:
```matlab
%% 改进的BP神经网络回归预测方案
%% 步骤1: 数据准备与详细分析
data = readtable('average_FC1_Ageing_part_sampled.xlsx');
time = data{:, 1}; % 时间数据
y_actual = data{:, 2:4}; % 输出变量
X = data{:, 5:end}; % 输入特征
var_names = data.Properties.VariableNames;
% 数据分布分析
figure;
subplot(2,2,1);
histogram(y_actual(:,1));
title(['输出变量: ' var_names{2}]);
subplot(2,2,2);
histogram(y_actual(:,2));
title(['输出变量: ' var_names{3}]);
subplot(2,2,3);
histogram(y_actual(:,3));
title(['输出变量: ' var_names{4}]);
subplot(2,2,4);
boxplot(X);
title('输入特征分布');
% 特征相关性分析
corr_matrix = corr([X, y_actual]);
figure;
heatmap([var_names(5:end) var_names(2:4)], [var_names(5:end) var_names(2:4)], corr_matrix);
title('特征-目标变量相关性矩阵');
%% 步骤2: 高级数据预处理
% 处理缺失值
X = fillmissing(X, 'linear');
y_actual = fillmissing(y_actual, 'linear');
% 检测并处理异常值
for i = 1:size(X, 2)
outliers = isoutlier(X(:,i));
X(outliers, i) = median(X(~outliers, i));
end
% 改进的归一化 - 对每个特征单独处理
X_normalized = zeros(size(X));
for i = 1:size(X, 2)
if std(X(:,i)) > 0
X_normalized(:,i) = (X(:,i) - mean(X(:,i))) / std(X(:,i));
else
X_normalized(:,i) = X(:,i); % 常数特征保持不变
end
end
y_normalized = zeros(size(y_actual));
y_means = mean(y_actual);
y_stds = std(y_actual);
for i = 1:size(y_actual, 2)
if std(y_actual(:,i)) > 0
y_normalized(:,i) = (y_actual(:,i) - mean(y_actual(:,i))) / std(y_actual(:,i));
else
y_normalized(:,i) = y_actual(:,i); % 常数输出保持不变
end
end
% 特征选择 - 基于相关性的特征选择
high_corr_features = find(any(abs(corr_matrix(1:size(X,2), end-2:end)) > 0.3, 2));
X_normalized = X_normalized(:, high_corr_features);
fprintf('选择了 %d 个高相关特征 (相关系数 > 0.3)\n', length(high_corr_features));
% 数据划分 - 分层抽样
rng(42, 'twister'); % 设置随机种子确保可重复性
cv = cvpartition(size(X,1), 'HoldOut', 0.25);
train_idx = training(cv);
test_idx = test(cv);
X_train = X_normalized(train_idx, :);
y_train = y_normalized(train_idx, :);
X_test = X_normalized(test_idx, :);
y_test = y_normalized(test_idx, :);
%% 步骤3: 构建优化的BP神经网络
% 创建网络结构
hiddenLayerSize = [25, 15]; % 两个隐藏层,分别有25和15个神经元
net = fitnet(hiddenLayerSize, 'trainscg'); % 使用弹性反向传播算法
% 配置网络参数
net.divideParam.trainRatio = 80/100; % 训练集比例
net.divideParam.valRatio = 20/100; % 验证集比例
net.divideParam.testRatio = 0/100; % 测试集比例(已手动划分)
% 设置训练参数
net.trainParam.epochs = 1000; % 最大训练轮数
net.trainParam.max_fail = 15; % 验证失败次数上限
net.trainParam.min_grad = 1e-6; % 最小梯度
net.trainParam.lr = 0.05; % 学习率
net.trainParam.lr_inc = 1.1; % 学习率增加因子
net.trainParam.lr_dec = 0.7; % 学习率减少因子
net.performFcn = 'mae'; % 使用平均绝对误差作为性能函数
net.layers{1}.transferFcn = 'tansig'; % 使用双曲正切激活函数
net.layers{2}.transferFcn = 'tansig';
net.layers{3}.transferFcn = 'purelin'; % 输出层使用线性激活函数
% 添加正则化以防止过拟合
net.performParam.regularization = 0.1;
% 训练网络并记录最佳模型
[net, tr] = train(net, X_train', y_train', 'useParallel', 'yes');
%% 步骤4: 预测与综合评估
% 使用最佳模型进行预测
y_pred_normalized = net(X_test', 'useParallel', 'yes')';
% 反归一化
y_pred = y_pred_normalized .* y_stds + y_means;
y_test_actual = y_test .* y_stds + y_means;
% 计算多种误差指标
% 避免除以零错误
epsilon = 1e-10;
y_test_adjusted = y_test_actual;
y_test_adjusted(abs(y_test_adjusted) < epsilon) = epsilon;
% 绝对百分比误差
ape = abs((y_test_actual - y_pred) ./ y_test_adjusted) * 100;
% 平均绝对百分比误差
mape = mean(ape, 1);
% 平均百分比误差
mpe = mean((y_test_actual - y_pred) ./ y_test_adjusted * 100, 1);
% 均方根误差
rmse = sqrt(mean((y_test_actual - y_pred).^2, 1));
% 决定系数 R²
ss_res = sum((y_test_actual - y_pred).^2, 1);
ss_tot = sum((y_test_actual - mean(y_test_actual, 1)).^2, 1);
r_squared = 1 - (ss_res ./ ss_tot);
% 显示综合误差指标
fprintf('\n=== 综合误差指标 ===\n');
for i = 1:3
fprintf('输出 %d (%s):\n', i, var_names{i+1});
fprintf(' MAPE = %.2f%%\n', mape(i));
fprintf(' MPE = %.2f%%\n', mpe(i));
fprintf(' RMSE = %.4f\n', rmse(i));
fprintf(' R² = %.4f\n', r_squared(i));
end
%% 步骤5: 高级可视化
figure('Position', [100, 100, 1400, 1000]);
% 拟合曲线
for i = 1:3
subplot(3, 2, (i-1)*2+1);
% 按时间排序
[time_sorted, sort_idx] = sort(time(test_idx));
y_actual_sorted = y_test_actual(sort_idx, i);
y_pred_sorted = y_pred(sort_idx, i);
% 绘制曲线
plot(time, y_actual(:, i), 'k-', 'LineWidth', 1.5)
hold on
plot(time_sorted, y_pred_sorted, 'r-', 'LineWidth', 2)
title(sprintf('输出变量: %s (R²=%.4f)', var_names{i+1}, r_squared(i)))
xlabel('时间')
ylabel('值')
legend('实际值', '预测值', 'Location', 'best')
grid on
hold off
end
% 预测值 vs 实际值散点图
for i = 1:3
subplot(3, 2, i*2);
scatter(y_test_actual(:, i), y_pred(:, i), 50, 'filled');
hold on;
% 添加参考线
max_val = max([y_test_actual(:, i); y_pred(:, i)]);
min_val = min([y_test_actual(:, i); y_pred(:, i)]);
plot([min_val max_val], [min_val max_val], 'r--', 'LineWidth', 1.5);
title(sprintf('预测值 vs 实际值: %s', var_names{i+1}));
xlabel('实际值');
ylabel('预测值');
grid on;
axis equal;
hold off;
end
%% 步骤6: 结果输出与模型保存
% 创建详细结果表格
result_table = table();
result_table.Time = time(test_idx);
for i = 1:3
result_table.(['Actual_' var_names{i+1}]) = y_test_actual(:, i);
result_table.(['Predicted_' var_names{i+1}]) = y_pred(:, i);
result_table.(['APE_' var_names{i+1}]) = ape(:, i);
result_table.(['Error_' var_names{i+1}]) = y_test_actual(:, i) - y_pred(:, i);
end
% 添加误差统计
stats = table();
stats.Metric = {'MAPE'; 'MPE'; 'RMSE'; 'R²'};
for i = 1:3
stats.(var_names{i+1}) = [mape(i); mpe(i); rmse(i); r_squared(i)];
end
% 保存到Excel
writetable(result_table, 'improved_prediction_results.xlsx');
writetable(stats, 'error_statistics.xlsx');
fprintf('结果已保存到 improved_prediction_results.xlsx 和 error_statistics.xlsx\n');
% 保存训练好的网络
save('optimized_bp_network.mat', 'net', 'tr', 'y_means', 'y_stds');
fprintf('优化后的神经网络已保存到 optimized_bp_network.mat\n');
% 特征重要性分析
input_weights = net.IW{1};
weight_contributions = mean(abs(input_weights), 1);
[~, sorted_idx] = sort(weight_contributions, 'descend');
figure;
bar(weight_contributions(sorted_idx));
set(gca, 'XTick', 1:length(sorted_idx), 'XTickLabel', var_names(4+high_corr_features(sorted_idx)));
xtickangle(45);
title('特征重要性分析');
ylabel('权重绝对值平均值');
```
### 改进策略解释:
1. **数据预处理优化**:
- 添加缺失值处理:使用线性插值填充缺失值
- 异常值检测与处理:识别并替换为特征中位数
- 改进的归一化:对每个特征单独处理,避免常数特征问题
- 特征选择:基于相关性分析选择高相关特征(相关系数 > 0.3)
2. **网络结构优化**:
- 增加网络容量:采用两层隐藏层(25和15个神经元)
- 改进激活函数:使用tansig(双曲正切)替代默认函数
- 添加正则化:L2正则化系数设为0.1防止过拟合
- 优化性能函数:使用MAE(平均绝对误差)替代MSE
3. **训练过程优化**:
- 使用弹性反向传播算法(trainscg):更适合复杂优化问题
- 动态学习率调整:设置增加和减少因子
- 并行训练:启用'useParallel'选项加速训练
- 早停机制:验证失败15次后停止训练
4. **综合评估指标**:
- 添加RMSE(均方根误差)和R²(决定系数)
- 处理除零错误:添加极小值ε避免计算问题
- 多角度评估:提供更全面的模型性能分析
5. **高级可视化**:
- 添加预测值 vs 实际值散点图
- 在图表标题显示R²值
- 添加45度参考线评估拟合优度
- 特征重要性分析:基于网络权重评估特征贡献
6. **结果输出增强**:
- 输出详细误差统计表
- 保存模型和归一化参数便于后续使用
- 特征重要性分析帮助理解模型决策
### 可能引起精度差的原因及解决方案:
1. **数据质量问题**:
- 解决方案:添加数据诊断、缺失值处理和异常值处理
2. **特征与目标相关性低**:
- 解决方案:添加特征相关性分析和特征选择
3. **过拟合/欠拟合**:
- 解决方案:添加正则化、调整网络结构、使用验证集早停
4. **不恰当的归一化**:
- 解决方案:改进归一化方法,处理常数特征
5. **局部最优解问题**:
- 解决方案:使用更先进的优化算法(trainscg)
6. **评估指标单一**:
- 解决方案:添加多种评估指标(RMSE、R²)
### 使用建议:
1. 运行前检查数据质量,特别是异常值和缺失值
2. 根据特征重要性结果考虑进一步的特征工程
3. 如果R²值仍低于0.7,尝试:
- 增加网络层数或神经元数量
- 尝试其他模型(如XGBoost、随机森林)
- 添加时间序列特征(滞后项、移动平均等)
4. 对于长期预测,考虑使用递归预测方法