Python - Facebook Prophet - 模型欠拟合

Python - Facebook Prophet - Model Underfitting

我是 运行 预测呼入电话量的先知模型。我花了很多时间清理数据、运行 对数刻度和超参数调整——结果是“还不错”MAPE(平均平均百分比误差)。

我现在的问题是模型一直欠拟合。尤其是在该月的前 12 天 - 尤其是在该月的前 6 天。由于运营原因,这些天的通话量总是高得多。随着下个月月初销量的增加,它们也会在接近月底时开始建造。

实际值是蓝点,预测值是灰线。这只是一个月,但它代表所有其他月份的月度季节性:

为了简单起见,我将只包含模型详细信息,而将所有数据清理过程排除在外。如果有帮助,我可以提供更多信息,但到目前为止,我得到的反馈是,额外的细节只会混淆视听。真正重要的是,在 运行 对构建模型的数据进行 boxcox 转换以及对来自模型的数据进行反向 boxcox 之后的结果:

# Create Model
M = Prophet(
    changepoint_prior_scale = 15,
    changepoint_range = .8,
    growth='linear',
    seasonality_mode= 'multiplicative',
    daily_seasonality=False,
    weekly_seasonality=False,
    yearly_seasonality=False,
    holidays=Holidays
    ).add_seasonality(
        name='monthly',
        period=30.5,
        fourier_order = 20,
        prior_scale = 45
    ).add_seasonality(
        name='daily',
        period=1,
        fourier_order=75,
        prior_scale=20
    ).add_seasonality(
        name='weekly',
        period=7,
        fourier_order=75,
        prior_scale=30
    ).add_seasonality(
        name='yearly',
        period = 365.25,
        fourier_order = 30, 
        prior_scale = 15)

总的来说,我想全面改善欠拟合情况 - 但尤其是在月初和月底。我试过增加 changepoint_range 来放松模型,但结果并不明显。我也试过增加“每月”季节性的 prior_scale,但没有比上面的屏幕截图更好的结果。

我有点不知所措。有没有一种建模技术可以与 FaceBook Prophet 模型一起使用来解决这个问题?有没有办法添加一个回归变量,将特定的季节性分配给前 12 天和最后 7 天?我做了一些研究,不确定你是否可以 and/or 那将如何工作。

如有任何帮助,我们将不胜感激。

作为更新,我尝试提高 change_point 范围和变化点先验比例,但没有任何影响。打算尝试减少训练数据量(目前使用 4 年)。

我想我找到了一个可行的解决方案,并将其记录为答案,以防其他人遇到类似问题。

因为我知道这种行为是周期性的并且我知道它存在的原因(月初有 2 个不同的月度计费周期,月末的交易量反复增加,这是不合适的),我使用Prophet 文档为这些特定时期创建额外的季节性回归量。

我首先定义季节函数(根据 Prophet 文档,示例是 NFL 赛季和 NFL 淡季):

def is_1st_billing_season(ds):
    date = pd.to_datetime(ds)
    return (date.day >= 1 and date.day <= 6)


def is_2nd_billing_season(ds):
    date = pd.to_datetime(ds)
    return (date.day >= 7 and date.day <= 12)


def EOM (ds):
    date = pd.to_datetime(ds)
    return (date.day >= 25 and date.day <= 31)

然后我将函数应用于我的数据框:

#Create Additional Seasonal Categories
Box_Cox_Data['1st_season'] = Call_Data['ds'].apply(is_1st_billing_season)
Box_Cox_Data['2nd_season'] = Call_Data['ds'].apply(is_2nd_billing_season)
Box_Cox_Data['EOM'] = Call_Data['ds'].apply(EOM)

然后我更新了我的模型以包含额外的季节性回归变量:

# Create Model
M = Prophet(
    changepoint_prior_scale = 15,
    changepoint_range = .8,
    growth='linear',
    seasonality_mode= 'multiplicative',
    daily_seasonality=False,
    weekly_seasonality=False,
    yearly_seasonality=False,
    holidays=Holidays
    ).add_seasonality(
        name='monthly',
        period=30.5,
        fourier_order = 20,
        prior_scale = 45
    ).add_seasonality(
        name='daily_1st_season',
        period=1,
         fourier_order=75,
        prior_scale=20,
        condition_name='1st_season'
    ).add_seasonality(
        name='daily_2nd_season',
        period=1,
        fourier_order=75,
        prior_scale=20,
        condition_name='2nd_season'
    ).add_seasonality(
        name='daily_EOM_season',
        period=1,
        fourier_order=75,
        prior_scale=20,
        condition_name='EOM'
    ).add_seasonality(
        name='weekly',
        period=7,
        fourier_order=75,
        prior_scale=30
    ).add_seasonality(
        name='yearly',
        period = 365.25,
        fourier_order = 30, #CHECK THIS
        prior_scale = 15)
        
#Fit Model
M.fit(Box_Cox_Data)

# Create Future Dataframe (in Hours)
future = M.make_future_dataframe(freq='H', periods = Hours_Needed)
future['1st_season'] = future['ds'].apply(is_1st_billing_season)
future['2nd_season'] = future['ds'].apply(is_2nd_billing_season)
future['EOM'] = future['ds'].apply(EOM)

# Predict Future Values
forecast = M.predict(future)

最终结果看起来好多了:

为了完全运行透明起见,此屏幕截图与原始屏幕截图的时间略有不同。对于这个项目,我的起点并不是特别重要(对未来时期的预测是主要关注点)而且我不小心 运行 了不同时间范围的交叉验证,但最终结果是更适合整个季节的预测到目前为止我看到的所有时间框架。