You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何逻辑斯蒂曲线拟合R²≈0.99但预测值偏差极大?

逻辑斯蒂函数拟合GDP时早期年份预测值偏差问题

问题背景

使用scipy.optimize.curve_fit拟合中国GDP数据集的逻辑斯蒂函数,拟合结果的R²≈0.99,曲线与数据整体匹配度高,但预测1970年GDP时得到约1.92e11,远高于真实值9.15e10,怀疑缩放处理、函数定义或初始参数设置存在问题。

代码与现象

执行代码

import numpy as np
import pandas as pd
from scipy.optimize import curve_fit
from scipy.special import expit

df = pd.read_csv("china_gdp.csv")
msk = np.random.rand(len(df)) < 0.8
x = df['Year'].values.astype(float)
y = df['Value'].values.astype(float)

# 归一化x, y到[0,1]
x_s = (x - x.min()) / (x.max() - x.min())
y_s = (y - y.min()) / (y.max() - y.min())

def logistic(x, B1, B2, B3, B4):
    return B3 + B4 * expit(B1*(x - B2))

# 初始猜测和边界
p0 = (5.0, 0.5, 0.0, 1.0)
bounds = ([0.0, 0.0, -np.inf, 0.0], [np.inf, 1.0, np.inf, np.inf])

popt, pcov = curve_fit(logistic, x_s, y_s, p0=p0, bounds=bounds)
print(dict(zip(['B1','B2','B3','B4'], popt)))

# 查看拟合效果
import matplotlib.pyplot as plt
xx = np.linspace(0, 1, 300)
yy = logistic(xx, *popt)
plt.scatter(x_s, y_s, s=15, label='data')
plt.plot(xx, yy, label='fit')
plt.legend(); plt.show()

from sklearn.metrics import r2_score
xtest = x_s[~msk]
ytest = y_s[~msk]
ypred = logistic(xtest, *popt)
print(r2_score(ytest, ypred))

# 预测1970年GDP
x_in = 1970
rs = logistic((x_in - x.min()) / (x.max() - x.min()), *popt)
r = rs * (y.max() - y.min()) + y.min()
print(r)

输出结果

0.9985891574981185
192285528141.3661

真实值

1970年GDP为91506211306.3745

问题分析

  1. R²的误导性:R²衡量整体拟合度,GDP数据后期数值远大于早期,早期数据的残差平方在总平方和中占比极低,因此即使早期点偏差明显,整体R²依然会保持高位。
  2. 参数拟合偏差:当前使用的4参数逻辑斯蒂函数边界设置过于宽松,容易拟合出偏离预期的参数(如B3为负、B4大于1),导致反归一化后早期年份的预测值被放大。
  3. 归一化的潜在影响:虽然归一化逻辑本身正确,但当拟合参数偏离归一化后的y范围[0,1]时,反归一化会进一步放大偏差。

解决方案

方案1:使用标准逻辑斯蒂增长模型

减少参数数量,贴合GDP增长的实际逻辑,直接用原始数据拟合无需归一化:

def standard_logistic(x, L, k, x0):
    # L: 饱和值, k: 增长速率, x0: 拐点年份
    return L / (1 + np.exp(-k*(x - x0)))

# 初始参数参考数据设置
p0 = [1.2e13, 0.1, 2000]  # 饱和值略高于2014年GDP,增长速率0.1,拐点2000年
bounds = ([1e12, 0.01, 1980], [2e13, 0.5, 2010])
popt, pcov = curve_fit(standard_logistic, x, y, p0=p0, bounds=bounds)

# 预测1970年GDP
print(standard_logistic(1970, *popt))

该模型更符合增长趋势,能显著降低早期年份的预测偏差。

方案2:收紧参数边界(保留原有函数)

如果坚持使用原有函数和归一化,需限制参数范围以贴合归一化后的y值区间:

# 限制B3在[-0.1,0.1],B4在[0.9,1.1],避免偏离归一化范围
bounds = ([0.0, 0.0, -0.1, 0.9], [10.0, 1.0, 0.1, 1.1])
popt, pcov = curve_fit(logistic, x_s, y_s, p0=p0, bounds=bounds)

内容的提问来源于stack exchange,提问作者MSo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 11:27:33