C++线性回归预测加密货币价格出现-nan(ind)错误求助
解决线性回归参数溢出与-nan(ind)问题的排查方案
我来帮你搞定这个头疼的-nan(ind)和参数溢出问题!从你给出的代码和运行数据来看,核心问题出在数据量级差异、学习率设置以及当前的梯度下降实现方式上,下面一步步拆解原因并给出解决办法:
问题原因分析
- 特征数值量级悬殊:BTC/USDT、ETH/USDT的价格都是数千甚至数万级别的,而DOGE这类小币种价格极低。你当前的
b1更新公式是b1 = b1 - alpha * err * ordersX[index].price,当ordersX[index].price是几万时,哪怕err只有几百,这个乘积都会瞬间拉满参数值,直接让b1爆炸到inf,后续运算自然出现nan。 - 学习率(alpha)过高:0.01的学习率对小量级特征(比如DOGE的价格)可能勉强能用,但对BTC这种大数值特征来说,每一步的更新幅度过大,参数直接跳出收敛范围,彻底发散。
- 单样本随机梯度下降(SGD)稳定性差:你现在每次只用单个样本更新参数,这种方式本身方差就大,再加上没有做特征归一化和学习率衰减,很容易导致参数震荡甚至直接溢出。
具体解决方法
1. 必须先做特征归一化/标准化
这是解决大数值特征梯度溢出最关键的一步,把所有X(价格)缩放到0-1或者-1到1的小范围,消除量级差异。比如用min-max归一化:
void LinearRegression::gradientDescent(std::vector<OrderBookEntry>& ordersX, std::vector<OrderBookEntry>& ordersY) { double b0 = 0; double b1 = 0; double alpha = 0.001; int epochs = 200; int n = ordersX.size(); // 第一步:计算X的最大最小值,做min-max归一化 double min_x = ordersX[0].price; double max_x = ordersX[0].price; for (auto& entry : ordersX) { min_x = std::min(min_x, entry.price); max_x = std::max(max_x, entry.price); } std::vector<double> scaled_x; for (auto& entry : ordersX) { // 缩放到0-1区间 scaled_x.push_back((entry.price - min_x) / (max_x - min_x)); } // 后续迭代用scaled_x替代原始price // ... 剩下的迭代代码 }
2. 调整学习率并添加衰减
把初始学习率调小(比如从0.01改成0.001甚至0.0001),然后随着迭代次数增加逐步降低学习率,避免后期参数震荡:
double alpha = 0.001; for (int i = 0; i < epochs; i++) { // ... 参数更新逻辑 // 每轮迭代后衰减学习率(比如乘以0.99) alpha *= 0.99; }
3. 改用批量/小批量梯度下降
相比单样本SGD,批量梯度下降用所有样本的平均梯度更新参数,稳定性强很多,不容易发散:
void LinearRegression::gradientDescent(std::vector<OrderBookEntry>& ordersX, std::vector<OrderBookEntry>& ordersY) { double b0 = 0; double b1 = 0; double alpha = 0.001; int epochs = 200; int n = ordersX.size(); // 特征归一化 double min_x = ordersX[0].price; double max_x = ordersX[0].price; for (auto& entry : ordersX) { min_x = std::min(min_x, entry.price); max_x = std::max(max_x, entry.price); } std::vector<double> scaled_x; for (auto& entry : ordersX) { scaled_x.push_back((entry.price - min_x)/(max_x - min_x)); } for (int i = 0; i < epochs; i++) { double sum_err_b0 = 0.0; double sum_err_b1 = 0.0; // 遍历所有样本计算总误差 for (int j = 0; j < n; j++) { double p = b0 + b1 * scaled_x[j]; double err = p - ordersY[j].price; sum_err_b0 += err; sum_err_b1 += err * scaled_x[j]; } // 用平均梯度更新参数 b0 -= alpha * (sum_err_b0 / n); b1 -= alpha * (sum_err_b1 / n); // 学习率衰减 alpha *= 0.99; } // 计算最小误差(和你之前的逻辑保持一致) std::vector<double> error; for (int j = 0; j < n; j++) { double p = b0 + b1 * scaled_x[j]; error.push_back(p - ordersY[j].price); } std::sort(error.begin(), error.end(), [](double a, double b) { return std::abs(a) < std::abs(b); }); std::cout << "最终值: " << "B0=" << b0 << " " << "B1=" << b1 << " " << "error=" << error[0] << std::endl; std::cout << std::endl; }
4. 添加溢出防护
可以在代码中加入判断,当参数出现inf时及时重置并降低学习率,避免直接走到nan:
// 参数更新后检查 if (std::isinf(b0) || std::isinf(b1)) { std::cout << "检测到参数溢出,降低学习率并重置参数" << std::endl; alpha *= 0.1; b0 = std::isinf(b0) ? 0 : b0; b1 = std::isinf(b1) ? 0 : b1; }
总结
先做特征归一化消除量级差异,再调整学习率并添加衰减,最后换成批量梯度下降,这三步基本就能解决BTC/USDT和ETH/USDT的参数溢出与nan问题了。你可以先试试归一化+调小学习率,应该就能看到明显改善。
内容的提问来源于stack exchange,提问作者ahmadalibin
相关产品推荐
相关产品推荐

