I would like to be able to suppress some output coming from fbprophet while fitting a forecasting model. This output ("Initial log joint probability...", "Optimization terminated normally:", "Convergence detected:...", etc.) is apparently coming from Stan's cpp code and I cannot find any obvious way to control it [I am using python interface]. Digging a little bit into the code discovers verbose=False as default in high level stan() routines (in pystan/api.py), but apparently this parameter does not suppress this printout. Is it feasible without code modifications?
Unfortunately, it's more complex than it should be. I think some of the output is coming from C or Fortran compiled code or something. Here is how you can do it (found here):
import os
import sys
import pandas as pd
from fbprophet import Prophet
# from https://mcmap.net/q/325042/-suppress-stdout-stderr-print-from-python-functions
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)
The 'simpler' way (if it would've worked, which it doesn't) would've been something like:
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
When using the prophet.fit
, unfortunately there is no easy ways to suppress logs.
Let's dive into details.
Overview:
- Most of the time the logs generated by
cmdstanpy
package, aprophet
dependency, notprophet
itself. prophet
package determines logging level by thelogging.getLogger.setLevel
function. Interestingly, it appears there are no parameters that allow external management or modification of this setting forcmdstanpy
.- If you manually set the
logging
level before running prophetfit
method, it has no effect, until the firstprophet.fit()
run, while consecutive re-runs surprisingly will have effect. This could be due to the order of operations in how the logging levels are set and when the Prophet module first imports and configures its logger. - Now it might be evident why setting
logging.getLogger("cmdstanpy").disabled=True
will suppresses the logs while neitherlogging.getLogger("cmdstanpy").setLevel(logging.WARNING)
norlogging.getLogger("prophet").setLevel(logging.WARNING)
can, during the initial run.
Solutions that works as expected from the first run:
Suppress all cmdstanpy
logs.
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
Note: I don't recommend turning logs off in general, as you won't see errors during the fit if any.
Suppress level-specific cmdstanpy
logs.
Achieved by temporarily involving a custom, immutable logging class.
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"]
Steps explanation:
A custom logger class
ImmutableLogger
gets created. This class inherits from Python's standardlogging.Logger
class. ThesetLevel
method in this class gets overridden to avoid any alterations to the logger's level once it's been set.The standard logger class in Python is substituted with the custom
ImmutableLogger
class usinglogging.setLoggerClass
.The logging level of
cmdstanpy
gets set toWARNING
. Because of theImmutableLogger
class, this level cannot be modified from now on.A Prophet model gets generated and fitted with the training data. During this procedure, only
cmdstanpy
logs with a level ofWARNING
or higher get displayed.The logger class gets switched back to Python's standard logger class. The
cmdstanpy
logger instance gets removed from the logger dictionary, effectively "resetting" the logger. This enables its re-initialization with default behaviors in any subsequent code.
Note:
- If it's sufficient for you to suppress level-specific logs only from the second
fit
run and not from the initial one, you may simply use just "step 3" alone:logging.getLogger("cmdstanpy").setLevel(logging.WARNING)
.
Suppress level-specific prophet
itself logs.
import logging
from prophet import Prophet
logging.getLogger("prophet").setLevel(logging.WARNING)
model = Prophet()
model.fit(df_train)
© 2022 - 2024 — McMap. All rights reserved.