如何使用 Python 为 ARIMA 模型进行手动预测

自回归积分移动平均模型或 ARIMA 模型对于初学者来说可能令人生畏。

揭开该方法面纱的一个好方法是使用训练好的模型手动进行预测。这表明 ARIMA 本质上是一个线性回归模型。

使用拟合的 ARIMA 模型进行手动预测也可能是您项目中的一项要求,这意味着您可以保存拟合模型的系数,并在自己的代码中将其用作配置来进行预测,而无需在生产环境中依赖繁重的 Python 库。

在本教程中,您将学习如何使用 Python 中训练好的 ARIMA 模型进行手动预测。

具体来说,您将学习

  • 如何对自回归模型进行手动预测。
  • 如何对移动平均模型进行手动预测。
  • 如何使用自回归积分移动平均模型进行预测。

快速启动您的项目,阅读我的新书 《Python 时间序列预测入门》,其中包含分步教程和所有示例的Python 源代码文件。

让我们开始吧。

  • 2019 年 4 月更新:更新了数据集链接。
  • 2019年8月更新:更新了数据加载以使用新的API。
  • 2020 年 12 月更新:将 ARIMA API 更新为最新版本的 statsmodels。
How to Make Manual Predictions for ARIMA Models with Python

如何使用 Python 为 ARIMA 模型进行手动预测
图片由 Bernard Spragg. NZ 拍摄,保留部分权利。

日最低气温数据集

此数据集描述了墨尔本市 10 年(1981-1990 年)的每日最低气温。

单位是摄氏度,共有3650个观测值。数据来源归功于澳大利亚气象局。

下载数据集,并将其放入您当前的工​​作目录中,文件名为“daily-minimum-temperatures.csv”。

下面的示例演示了如何将数据集加载为 Pandas Series 并绘制加载的数据集。

运行示例将生成时间序列的折线图。

Minimum Daily Temperatures Dataset Plot

每日最低气温数据集图

停止以**慢速**学习时间序列预测!

参加我的免费7天电子邮件课程,了解如何入门(附带示例代码)。

点击注册,同时获得该课程的免费PDF电子书版本。

ARIMA 测试设置

我们将使用一致的测试框架来拟合 ARIMA 模型并评估其预测。

首先,加载的数据集被分成训练集和测试集。数据集的大部分用于拟合模型,最后 7 个观测值(一周)作为测试集,用于评估拟合模型。

采用以下向前滚动验证或滚动预测方法:

  1. 遍历测试数据集中的每个时间步。
  2. 在每次迭代中,都会在所有可用历史数据上训练一个新的 ARIMA 模型。
  3. 该模型用于对下一天进行预测。
  4. 存储预测值,并从测试集中检索“真实”观测值,然后将其添加到历史数据中,以便在下一次迭代中使用。
  5. 模型性能在最后通过计算所有预测值与测试数据集中的预期值之间的均方根误差 (RMSE) 来进行汇总。

开发了简单的 AR、MA、ARMA 和 ARMA 模型。它们未优化,仅用于演示目的。稍加调整,您肯定可以获得更好的性能。

使用了 statsmodels Python 库中的 ARIMA 实现,并通过拟合模型返回的 ARIMAResults 对象提取 AR 和 MA 系数。

ARIMA 模型通过 predict() 和 forecast() 函数支持预测。

尽管如此,在本教程中我们将使用学习到的系数进行手动预测。

这很有用,因为它表明从训练好的 ARIMA 模型中只需要系数。

statsmodels 实现的 ARIMA 模型中的系数不包含截距项。这意味着我们可以通过将学习到的系数与滞后值(对于 AR 模型)和滞后残差(对于 MA 模型)进行点积来计算输出值。例如:

可以使用以下方法从 ARIMAResults 对象访问学习到的 ARIMA 模型的系数:

  • AR 系数:model_fit.arparams
  • MA 系数:model_fit.maparams

我们可以使用这些检索到的系数,通过以下自定义的predict() 函数进行预测。

供参考,您可能会发现以下资源很有用

在此示例中,我们将使用一个简单的 AR(1) 进行演示。

进行预测需要我们从拟合的模型中检索 AR 系数,并将它们与观测值的滞后一起使用,并调用上面定义的自定义 predict() 函数。

完整的示例如下所示。

请注意,ARIMA 实现会自动对时间序列进行趋势建模。这会在回归方程中添加一个常数,而我们为了演示目的不需要它。通过将fit() 函数中的“trend”参数设置为“nc”(表示“无常数”)来禁用此便利功能。

fit() 函数还会输出许多详细的消息,我们可以通过将“disp”参数设置为“False”来关闭它们。

运行示例将打印 7 天的预测值和预期值,最后打印出 RMSE,显示此简单模型平均误差约为 1.9 摄氏度。

尝试使用不同阶数的 AR 模型,例如 2 或更多。

移动平均模型

移动平均模型,或 MA,是滞后残差误差的线性回归模型。

滞后为 k 的 MA 模型可以在 ARIMA 模型中这样指定:

在此示例中,我们将使用一个简单的 MA(1) 进行演示。

与上面类似,进行预测需要我们从拟合模型中检索 MA 系数,并将它们与滞后残差误差一起使用,并调用上面定义的自定义 predict() 函数。

训练期间的残差误差存储在 ARIMA 模型中的 ARIMAResults 对象的“resid”参数下。

完整的示例如下所示。

运行示例将打印每次迭代的预测值和预期值,并在最后汇总所有预测的 RMSE。

模型的技能不高,您可以借此机会探索其他阶数的 MA 模型,并使用它们进行手动预测。

您可以看到,在ARIMAResults 对象之外手动跟踪残差误差是多么直接,随着新观测值的提供。例如:

接下来,让我们将 AR 和 MA 模型放在一起,看看如何进行手动预测。

自回归移动平均模型

我们现在已经看到了如何为拟合的 AR 和 MA 模型进行手动预测。

这些方法可以直接组合起来,为更完整的 ARMA 模型进行手动预测。

在本示例中,我们将拟合一个 ARMA(1,1) 模型,该模型可以在 ARIMA 模型中配置为 ARIMA(1,0,1),无需差分。

完整的示例如下所示。

您可以看到,预测值(yhat)是 AR 系数与滞后观测值进行点积,以及 MA 系数与滞后残差误差进行点积的总和。

再次运行示例将打印每次迭代的预测值和预期值,并总结所有预测的性能。

我们现在可以添加差分,并展示如何为完整的 ARIMA 模型进行预测。

自回归积分移动平均模型

ARIMA 中的“I”代表集成,是指在线性回归模型中进行预测之前对时间序列观测值执行的差分。

在进行手动预测时,我们必须在调用predict() 函数之前对数据集执行此差分。下面是一个实现了对整个数据集进行差分的函数。

一个简化方法是跟踪最旧的必需滞后值,并使用它来计算预测前所需的差分序列。

此差分函数可以为 ARIMA 模型所需的每个差分调用一次。

在本示例中,我们将使用差分级别为 1,并将其与上一节的 ARMA 示例结合起来,得到 ARIMA(1,1,1) 模型。

完整的示例如下所示。

您可以看到,滞后观测值在调用 AR 系数的predict() 函数之前进行了差分。残差误差也将根据这些差分输入值进行计算。

运行示例将打印每次迭代的预测值和预期值,并汇总所有预测的性能。

总结

在本教程中,您学习了如何使用 Python 为 ARIMA 模型进行手动预测。

具体来说,你学到了:

  • 如何为 AR 模型进行手动预测。
  • 如何为 MA 模型进行手动预测。
  • 如何为 ARMA 和 ARIMA 模型进行手动预测。

您对进行手动预测有任何疑问吗?
在下面的评论中提出你的问题,我会尽力回答。

想用 Python 开发时间序列预测模型?

Python 时间序列预测入门

几分钟内开发您自己的预测

...只需几行python代码

在我的新电子书中探索如何实现
Python 时间序列预测入门

它涵盖了**自学教程**和**端到端项目**,主题包括:*数据加载、可视化、建模、算法调优*等等。

最终将时间序列预测带入
您自己的项目

跳过学术理论。只看结果。

查看内容

65 条回复:如何使用 Python 为 ARIMA 模型进行手动预测

  1. MIC 2017 年 2 月 8 日下午 4:50 #

    嗨,Jason,

    感谢这篇教程。
    现在出现以下错误,请告知。
    我也认为代码没有错误。

    —> 23 model_fit = model.fit(trend=’nc’, disp=False)

    ValueError: 无法将字符串转换为浮点数:“?0.2”

    谢谢

    • Jason Brownlee 2017 年 2 月 9 日上午 7:22 #

      打开下载的数据文件,删除所有“?”字符实例。

      • MIC 2017 年 2 月 10 日上午 11:56 #

        它运行正常。
        我没想到这会是由将文件转换为 CSV 引起的。

        谢谢你,Jason。

  2. Luca 2017 年 5 月 5 日凌晨 2:42 #

    嗨 Jason

    非常感谢您的文章,我有一个问题:在本例中,为什么我们使用迭代的方式来确定 ARIMA 参数?能否在测试数据集的循环之前固定模型参数,然后进行验证过程?

    非常感谢您提供更多见解。

    • Jason Brownlee 2017 年 5 月 5 日上午 7:33 #

      我不确定您的意思是“迭代”?您能否详细说明一下?

      • Luca 2017 年 5 月 6 日凌晨 12:20 #

        感谢您的反馈,Jason,我之前的意思是:

        for t in range(len(test))
        model = ARIMA(history, order=(1,1,1))

        我们看到在每个循环中,我们都会重新训练模型并获得一组新的参数。为什么不只根据训练集来训练模型,并固定模型中的所有参数,然后遍历所有测试集并验证它们的误差呢?

        再次感谢您的回复。

        • Jason Brownlee 2017年5月6日 上午7:46 #

          你可以这样做,但如果我们有新数据(例如,下一个月有一个新的观测值),那么我们应该使用它。

          这就是我们在这里模拟的。这叫做“前向验证”。
          https://machinelearning.org.cn/backtest-machine-learning-models-time-series-forecasting/

          • Luca 2017年5月8日 下午7:16 #

            非常感谢 Jason。
            链接里的例子很好。我发现您的帖子中有很多非常有用的信息,我会阅读其他帖子并提问(如果我有的话)。
            再次感谢您的工作,做得很好。🙂

          • Jason Brownlee 2017年5月9日 上午7:40 #

            谢谢你,Luca!

  3. Hans 2017年6月15日 下午11:37 #

    假设我每周有两组数据记录了几年,例如周一和周五的数据。这会影响模型吗?这对于差分函数有意义吗?

  4. Hans 2017年6月15日 下午11:38 #

    我读过您网站上关于ARIMA的几篇教程。有些使用差分函数,有些不使用,比如参数调优。我什么时候需要差分函数?

  5. Luca 2017年6月20日 下午6:28 #

    嗨 Jason

    由于该数据集似乎具有很强的季节性,在这种情况下,我们是否需要先分解数据,去除季节性因素,然后应用ARIMA等模型?

    谢谢

  6. David Ravnsborg 2017年7月6日 下午2:28 #

    嗨,Jason,

    感谢所有 ARIMA 教程!我正在准备分析去年夏天在生物力学实验室进行的步态数据,这些教程帮助我掌握了模型。

  7. Srini 2017年7月15日 上午4:53 #

    嗨,Jason,

    想象一个场景,我们正在使用拟合的 ARIMA 模型,即在新的(样本外)数据集上的系数。计算 AR 部分很容易。使用长度为“p”的先前数据(历史)。而对于 MA 部分,则需要过去“q”值的残差。在没有对新数据进行拟合的情况下,如何计算新数据的残差?我们是否使用训练数据中的残差?这对于 q>0(即有 MA 系数)的 ARIMA 模型很有用。

    • Jason Brownlee 2017年7月15日 上午9:46 #

      好问题。我认为 ARIMA 模型会在“model_fit.resid”中提供这些。

  8. buffy 2017年7月18日 下午4:19 #

    感谢您的帖子,很有帮助。
    但我有个问题,为什么在每个循环中历史数据都需要附加测试数据?
    model = ARIMA(history, order=(1,0,0))
    obs = test[t]
    history.append(obs)

    如果我不知道测试数据的值,例如只预测 6 个月或 1 年(365 天范围)的值,如何使用模型进行预测?就像机器学习一样,使用训练集训练模型,然后预测新数据。

  9. Kanav Kariya 2017年11月15日 上午9:54 #

    嗨,Jason,

    非常感谢这些 ARIMA 帖子,我对时间序列知之甚少,我正在学习这些内容,以便完成一个查找交叉相关矩阵的项目。我需要预白化数据,并使用了您的教程来拟合一个模型到我的一个序列上。接下来,我必须将这个模型(滤波器)应用于另一个序列,我想知道您是否可以详细说明如何使用 predict 来做到这一点。我一直在网上搜索资料,但我不知道“params”字段的具体内容和格式应该是什么。

    非常感谢!

  10. Maksouda 2018年2月27日 下午8:10 #

    您好,是否可以将其他变量集成到 ARIMA 模型中进行预测?
    我找不到关于这个问题的答案。

    • Jason Brownlee 2018年2月28日 上午6:03 #

      是的,这被称为外生变量。

      抱歉,我没有例子。

  11. William Ford 2018年3月12日 上午11:54 #

    您好!我该如何预测例如 20 个读数?
    mod =ARIMA(X, order=(self.p, self.d, self.q)
    res = mod.fit()
    res.forecast(20)

  12. Shital Bhojani 2018年3月19日 下午8:15 #

    你好 Jason,

    在示例中,您说有 3650 个观测值,并将最后 7 天作为测试集……我对我自己的数据集有点困惑。我有 30 年的 7 个天气参数的周数据。我该如何选择训练集和测试集?

  13. Mia 2018年4月24日 下午11:53 #

    我尝试了您手动预测序列的方法(我认为是正确的)以及 statsmodels 的 .predict()。您能解释一下为什么它们不完全相同吗?statsmodels 对它们如何进行预测并不太清楚。谢谢!

  14. Paola 2018年7月23日 下午8:27 #

    我有一个问题,这里的 ACF 和 PACF 是如何获得的?我有一个类似的数据集,有近 1100 个观测值,根据我的 ACF 和 PACF,ACF 在 1 到 27 的滞后项中非常显著。我还想知道当我们有如此大的值时,ARIMA 模型是否存在限制。

  15. Great 2019年1月13日 上午1:53 #

    嗨,我认为 ARIMA 模型在差分方面存在一个大错误。

    当我们使用 AIRMA(p,d,q) 时,如果 d 不等于 0,模型已经完成了差分步骤。
    因此,预测结果数据也已经进行了差分,所以我们需要恢复数据。

    但是恢复后的结果(无差分)是错误的。预测误差非常大。

    在您的许多 ARIMA 示例中,例如 ARIMA(5,1,0),d = 1,但我没有找到恢复差分。
    如果 ARIMA(5,0,0),则不需要恢复差分。
    我所说的差分是 ARIMA(p,d,q) 中的参数 d。
    我认为也许 python 的 ARIMA 模型有一些小错误。

    • Jason Brownlee 2019年1月13日 上午5:42 #

      模型将在进行预测时反转差分(如果需要)。

  16. aoyong 2019年4月3日 上午2:11 #

    嗨,Jason,我有一个关于训练和验证的问题。
    在这四个例子中,您将数据分为训练集和测试集。但在您的预测中,您还将测试集中的数据添加到历史数据中。这样做可以吗?根据我的理解,使用测试集中的数据是不正确的。

    另一个问题是关于预测。根据文档(https://statsmodels.cn/dev/generated/statsmodels.tsa.arima_model.ARMAResults.forecast.html#statsmodels.tsa.arima_model.ARMAResults.forecast),我们可以进行多步预测。为什么您不直接使用这些步数呢?

    • Jason Brownlee 2019年4月3日 上午6:47 #

      是的,这被称为前向验证,更多内容请参见此处。
      https://machinelearning.org.cn/backtest-machine-learning-models-time-series-forecasting/

      • aoyong 2019年4月3日 下午8:25 #

        我看到了链接。它很详细。我也查看了其他网页。似乎“前向验证”在机器学习中并不常见。如果我们想将结果与其他方法进行比较,我们应该怎么做?

        如果我们使用其他复杂的方法并使用“前向验证”,这将浪费大量时间来训练模型。

        谢谢。

        • Jason Brownlee 2019年4月4日 上午7:50 #

          这在时间序列预测问题中很常见。

          其他方法应该采用这种方法,如果它们不这样做,例如使用交叉验证,那么它们的结果几乎肯定无效。

  17. Naveksha Sood 2019年4月26日 下午8:04 #

    我如何计算时间序列的训练误差?

    • Jason Brownlee 2019年4月27日 上午6:29 #

      通常我们使用均方误差 (MSE) 或均方根误差 (RMSE)。

  18. Laura 2019年5月1日 上午1:18 #

    这太棒了,谢谢!我一直在寻找一种手动计算参数预测的方法,因为我需要在其他上下文中保存参数进行预测。我找不到任何让我信服的文档,所以谢谢你!

  19. Naveksha Sood 2019年5月9日 下午3:31 #

    是的,我们计算 RMSE,但是怎么计算呢?实际值和预测值将是什么?

  20. Gunay 2019年6月4日 上午4:23 #

    嗨,Jason,

    首先,感谢您的教程。我脑子里有几个问题。首先,如果我进行多步预测,并且用一些数字定义 AR 和 MA 项,这是否意味着模型会回顾一定数量的过去步骤进行预测?如果我将整个历史时间序列引入模型,模型如何利用所有历史数据进行多步预测?AR 和 MA 项中的系数是如何更新的?如果我能理解它们,我将非常感激。

    此致,
    古奈

  21. Emilio 2019年6月6日 下午6:27 #

    嗨,Jason,

    感谢您的教程!我有一个关于设置 yhat 的问题。在 (0,0,0)、(0,1,1)、(1,1,0) 和 (0,1,0) 的情况下,yhat 会等于什么?对于这些情况,我们应该为 predict 函数使用什么参数?

    谢谢你,
    Emilio

    • Jason Brownlee 2019年6月7日 上午7:53 #

      我不太明白。

      yhat 是模型做出的预测。

      顺序(例如 (0,1,0))是模型的配置。

      这有帮助吗?

      • Emilio 2019年6月10日 下午6:03 #

        所以,假设我有一个 0,1,0 ARIMA 模型。我需要一个常数项才能运行它。我应该如何将常数项纳入 predict 函数?对于 0,1,0 以及其他情况都是如此吗?

  22. Micheal 2019年7月18日 下午8:47 #

    感谢这篇文章。那么,我能否问一下如何使用这个模型来预测未来的日期?这个实践展示了对当前数据的预测。
    谢谢

  23. Cormac Murphy 2019年7月26日 上午9:36 #

    感谢您撰写此文。我一直很喜欢您的文章。

    对于像 ARIMA(4,1,1) 这样的高阶模型,这会如何工作?

  24. Shawon 2019年11月1日 下午5:23 #

    这是我的代码:

    from statsmodels.tsa.arima_model import ARIMA
    from sklearn.metrics import mean_squared_error
    train= training_set[‘Close’].values.reshape(-1, 1) # reshaping training values
    test = test_set.values.reshape(-1, 1)

    history = [x for x in train]
    predictions = list()
    for t in range(len(test))
    model = ARIMA(history, order=(1,2,1))
    model_fit = model.fit(trend=’nc’, disp=False)
    ar_coef, ma_coef = model_fit.arparams, model_fit.maparams
    resid = model_fit.resid
    diff = difference(history)
    yhat = history[-1] + predict(ar_coef, diff) + predict(ma_coef, resid)
    predictions.append(yhat)
    obs = test[t]
    history.append(obs)
    print(‘>predicted=%.3f, expected=%.3f’ % (yhat, obs))
    rmse = sqrt(mean_squared_error(test, predictions))
    print('Test RMSE: %.3f' % rmse)

    输出

    >predicted=3366.015, expected=3370.000
    >predicted=3371.046, expected=3390.000
    >predicted=3369.670, expected=3370.000
    >predicted=3392.017, expected=3377.800
    >predicted=3373.568, expected=3460.000
    >predicted=3369.171, expected=3460.000
    >predicted=3452.457, expected=3400.000
    >predicted=3469.630, expected=3458.100
    >predicted=3402.823, expected=3490.000
    >predicted=3449.645, expected=3490.000
    >predicted=3488.853, expected=3442.200
    >predicted=3498.426, expected=3468.300
    >predicted=3447.526, expected=3489.000
    >predicted=3465.161, expected=3510.000
    >predicted=3486.675, expected=3485.000
    >predicted=3513.211, expected=3498.900
    >predicted=3489.170, expected=3500.000
    >predicted=3499.777, expected=3580.000
    >predicted=3493.101, expected=3495.200
    >predicted=3583.419, expected=3545.500
    >predicted=3503.000, expected=3720.000
    —————————————————————————
    ValueError 回溯 (最近一次调用)
    in
    8 for t in range(len(test))
    9 model = ARIMA(history, order=(1,2,1))
    —> 10 model_fit = model.fit(trend=’nc’, disp=False)
    11 ar_coef, ma_coef = model_fit.arparams, model_fit.maparams
    12 resid = model_fit.resid

    ~/anaconda3/lib/python3.7/site-packages/statsmodels/tsa/arima_model.py in fit(self, start_params, trend, method, transparams, solver, maxiter, full_output, disp, callback, start_ar_lags, **kwargs)
    1155 arima_fit.mle_retvals = mlefit.mle_retvals
    1156 arima_fit.mle_settings = mlefit.mle_settings
    -> 1157
    1158 return ARIMAResultsWrapper(arima_fit)
    1159

    ~/anaconda3/lib/python3.7/site-packages/statsmodels/tsa/arima_model.py in fit(self, start_params, trend, method, transparams, solver, maxiter, full_output, disp, callback, start_ar_lags, **kwargs)
    944 kwargs.setdefault(‘pgtol’, 1e-8)
    945 kwargs.setdefault(‘factr’, 1e2)
    –> 946 kwargs.setdefault(‘m’, 12)
    947 kwargs.setdefault(‘approx_grad’, True)
    948 mlefit = super(ARMA, self).fit(start_params, method=solver,

    ~/anaconda3/lib/python3.7/site-packages/statsmodels/tsa/arima_model.py in _fit_start_params(self, order, method, start_ar_lags)
    560 pgtol=1e-7, factr=1e3,
    561 bounds=bounds, iprint=-1)
    –> 562 start_params = mlefit[0]
    563 if self.transparams
    564 start_params = self._transparams(start_params)

    ~/anaconda3/lib/python3.7/site-packages/statsmodels/tsa/arima_model.py in _fit_start_params_hr(self, order, start_ar_lags)
    546 return start_params
    547
    –> 548 def _fit_start_params(self, order, method, start_ar_lags=None)
    549 if method != ‘css-mle’: # use Hannan-Rissanen to get start params
    550 start_params = self._fit_start_params_hr(order, start_ar_lags)

    ValueError: The computed initial MA coefficients are not invertible
    您应该强制执行可逆性,选择不同的模型顺序,或者您可以
    传递您自己的 start_params。

  25. Andrew 2020年4月7日 下午5:21 #

    老师您好,我有一个关于使用手动预测和使用库进行预测的问题。
    我比较了两个结果,但它们显示的结果不同。
    我使用了这个模型拟合:
    model_fit = model.fit(trend=’nc’, disp=False)。我使用的库模型显示了恒定的结果。例如 [10.234,10.235,10.235,….10.235,]。您能帮帮我吗?🙂

    • Jason Brownlee 2020年4月8日 上午7:48 #

      您可能需要深入研究 statsmodels 的源代码,看看使用了哪些额外的步骤。

      • Andrew 2020年4月8日 下午10:53 #

        我想知道为什么我们不在此教程中使用常数,以及为什么 disp= False?我尝试使参数相同,但结果与预测不同。

        • Jason Brownlee 2020年4月9日 上午8:04 #

          我们将 display 设置为 False 以删除冗余输出。

  26. Francis 2020年5月23日 下午8:26 #

    先生您好,感谢您的贡献。

    我的数据已经预处理为 -1 到 1 的范围。我正在对 ARIMA 进行前向验证。
    模型会为某些观测值预测出该范围之外的极高值。

    有什么原因吗?

    谢谢你。

    • Jason Brownlee 2020年5月24日 上午6:08 #

      也许模型需要针对您的数据集进行进一步调整?
      也许您需要将数据缩放到不同的范围?
      也许尝试替代模型?

  27. Alan 2024年5月26日 下午11:14 #

    先生您好,我想确认几点。假设我的数据集需要“2”阶差分。
    那么它将是

    def difference(dataset)
    diff = list()
    for i in range(1, len(dataset))
    value = dataset[i] – dataset[i – 2]
    diff.append(value)
    return numpy.array(diff)

    model = ARIMA(history, order=(0,2,0))

    这样对吗?
    我其实对这个函数有点困惑。

Leave a Reply

Machine Learning Mastery 是 Guiding Tech Media 的一部分,Guiding Tech Media 是一家领先的数字媒体出版商,专注于帮助人们了解技术。访问我们的公司网站以了解更多关于我们的使命和团队的信息。