如何控制来自fbprophet的输出?

16

我希望在拟合预测模型时能够抑制来自fbprophet的某些输出。这些输出(“Initial log joint probability...”,“Optimization terminated normally:”,“Convergence detected:...”等等)显然来自Stan的cpp代码,我无法找到任何明显的方法来控制它[我使用的是python接口]。 稍微深入代码发现,在高级stan()例程中(verbose=False)默认为False,但显然此参数不能抑制此打印输出。不进行代码修改是否可行?

2个回答

12

不幸的是,这比它本应该的更复杂。我认为一些输出来自于C或Fortran编译的代码之类的东西。以下是您可以这样做的方式(在此处找到):

import os
import sys

import pandas as pd
from fbprophet import Prophet


# from https://dev59.com/_2gu5IYBdhLWcg3wt5Lt
class suppress_stdout_stderr(object):
    '''
    A context manager for doing a "deep suppression" of stdout and stderr in
    Python, i.e. will suppress all print, even if the print originates in a
    compiled C/Fortran sub-function.
       This will not suppress raised exceptions, since exceptions are printed
    to stderr just before a script exits, and after the context manager has
    exited (at least, I think that is why it lets exceptions through).

    '''
    def __init__(self):
        # Open a pair of null files
        self.null_fds = [os.open(os.devnull, os.O_RDWR) for x in range(2)]
        # Save the actual stdout (1) and stderr (2) file descriptors.
        self.save_fds = (os.dup(1), os.dup(2))

    def __enter__(self):
        # Assign the null pointers to stdout and stderr.
        os.dup2(self.null_fds[0], 1)
        os.dup2(self.null_fds[1], 2)

    def __exit__(self, *_):
        # Re-assign the real stdout/stderr back to (1) and (2)
        os.dup2(self.save_fds[0], 1)
        os.dup2(self.save_fds[1], 2)
        # Close the null files
        os.close(self.null_fds[0])
        os.close(self.null_fds[1])

m = Prophet()
df = pd.read_csv('somefile.csv')

with suppress_stdout_stderr():
    m.fit(minimal_df)

“更简单”的方法(如果它能够工作,但实际上不能)可能是这样的:

import os
import sys

import pandas as pd
from fbprophet import Prophet

m = Prophet()
df = pd.read_csv('somefile.csv')

orig_out = sys.stdout
sys.stdout = open(os.devnull, 'w')
m.fit(df)
sys.stdout = orig_out

简单的方法已经过时了吗?因为在 Flask 应用程序中它似乎对我不起作用。 - Sayan Sil
2
我不确定更简单的方法是否有效;此时我不记得是否已经测试过了。尽管如此,我似乎记得它没有起作用。我认为这通常是一种建议,但在prophet的情况下不适用。 - wordsforthewise

0

在使用prophet.fit时,不幸的是没有简单的方法来抑制日志。
让我们深入了解一下。

概述:

  • 大多数情况下,由cmdstanpy包生成的日志是prophet依赖项,而不是prophet本身。
  • prophet包通过logging.getLogger.setLevel函数确定日志记录级别。有趣的是,似乎没有参数允许外部管理或修改cmdstanpy的此设置。
  • 如果在运行prophet fit方法之前手动设置logging级别,则没有效果,直到第一次prophet.fit()运行,而连续重新运行会出现意外的效果。这可能是由于设置日志记录级别的操作顺序以及Prophet模块何时首次导入和配置其记录器。
  • 现在可能很明显为什么设置logging.getLogger("cmdstanpy").disabled=True将抑制日志,而logging.getLogger("cmdstanpy").setLevel(logging.WARNING)logging.getLogger("prophet").setLevel(logging.WARNING)在初始运行期间都不能实现。

可以从第一次运行开始正常工作的解决方案:

禁止所有cmdstanpy日志记录。

import logging
from prophet import Prophet

logging.getLogger("cmdstanpy").disabled = True #  turn 'cmdstanpy' logs off
model = Prophet()
model.fit(df_train)
logging.getLogger("cmdstanpy").disabled = False #  revert original setting

注意:通常我不建议关闭日志记录,因为如果有任何错误,在拟合期间您将看不到它们。

抑制特定级别的cmdstanpy日志。

通过暂时涉及自定义、不可变的日志记录类来实现。

import logging
from prophet import Prophet

# 1. Immutable Logger Class Creation.
class ImmutableLogger(logging.Logger):
    def __init__(self, name, level=logging.NOTSET):
        super().__init__(name, level)
        self._level_set = False

    def setLevel(self, level):
        if not self._level_set:
            super().setLevel(level)
            self._level_set = True

# 2. Standard Logger Replacement with Immutable Logger.
logging.setLoggerClass(ImmutableLogger)

# 3. Logging Level Determination.
logging.getLogger("cmdstanpy").setLevel(logging.WARNING)

# 4. Execution of Prophet Code with level-specific log suppression.
model = Prophet()
model.fit(df_train)

# 5. Standard Logger Reversion and Cleanup.
logging.setLoggerClass(logging.Logger)
del logging.Logger.manager.loggerDict["cmdstanpy"]

步骤说明:

  1. 创建一个自定义日志类 ImmutableLogger,该类继承自 Python 标准的 logging.Logger 类。在此类中,setLevel 方法被重写,以避免在设置日志记录器级别后进行任何更改。

  2. 使用 logging.setLoggerClass 方法将 Python 的标准日志记录器类替换为自定义的 ImmutableLogger 类。

  3. cmdstanpy 的日志记录级别设置为 WARNING。由于使用了 ImmutableLogger 类,此级别从现在开始无法修改。

  4. 生成并使用训练数据拟合一个 Prophet 模型,在此过程中只显示 cmdstanpy 日志级别为 WARNING 或更高级别的日志信息。

  5. 将日志记录器类切换回 Python 的标准日志记录器类。将 cmdstanpy 日志记录器实例从记录器字典中删除,有效地“重置”日志记录器。这使其可以在随后的任何代码中重新初始化默认行为。

注意:

  • 如果你只需要在第二个fit运行中抑制特定级别的日志,而不是在初始运行中抑制,那么你可以仅使用"步骤3": logging.getLogger("cmdstanpy").setLevel(logging.WARNING)

抑制特定级别的prophet本身的日志。

import logging
from prophet import Prophet

logging.getLogger("prophet").setLevel(logging.WARNING)
model = Prophet()
model.fit(df_train)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接