Data Soup

data... sip by sip

Log training progress in Slack with Keras
Feb 6, 2019
2 minutes read

Sometimes I want to check the progress of my model without connecting to the server. Here you can make your model log its progress at each epoch into a Slack channel. Apart from Keras, it doesn’t require any installation and should be done in 5min.

The objective is to make a lightweight tool but we can imagine uploading accuracy/loss graph on on_train_end and much more if needed!

Getting a result similar to the image above, can be done in three short steps: create the bot, import the callback and attach it to fit.

Create a bot and get the webhook

  1. Create an app, name it and assign it to your workspace
  2. Activate Incoming Webhooks, set the desired channel
  3. Notice the webhook, it looks like: https://hooks.slack.com/services/...

More information on bots.

Import the callback

Complete sources here

(...)

from keras.callbacks import Callback

class SlackLogger(Callback):
    """Callback that streams epoch results to a csv file.
    Supports all values that can be represented as a string,
    including 1D iterables such as np.ndarray.
    # Example
    ```python
    slacklog = SlackLogger('WEBHOOKSTRING')
    model.fit(X_train, Y_train, callbacks=[slacklog])
    ```
    # Arguments
        webhook: webhook of your bot (https://api.slack.com/bot-users#installing-bot for more infos).
        separator: string used to separate elements each slack message
        experiment_name: name of the experimentation (will be announced when train begin)
        
    # SlackLogger is an adaptation of the CSVLogger class
    """

    (...)

    def send_message(self, message):
        command = ["curl", "-X", "POST", "-H", "'Content-type: application/json'",
                    "--data", '{{"text":"{}"}}'.format(message), self.webhook]
        subprocess.call(command)
        
    def on_train_begin(self, logs=None):
        self.send_message('⏱️ {} training started'.format(self.experiment_name))
        self.start_time = time.time()

    def on_epoch_end(self, epoch, logs=None):
        logs = logs or {}

        (...)

        row_dict = OrderedDict({})
        row_dict.update((key, handle_value(logs[key])) for key in self.keys)
        
        perfs = self.sep.join([" {}: {:.5f}".format(key, value) for key, value in row_dict.items()])
        message = '#{} {}'.format(epoch, perfs)
        self.send_message(message)

    def on_train_end(self, logs=None):
        elapsed_time = int(time.time() - start_time)
        styled_time = '{:03d}:{:02d}:{:02d}'.format(elapsed_time // 3600, (elapsed_time % 3600 // 60), elapsed_time % 60)
        self.send_message('🏁 {} training done in {}'.format(self.experiment_name, styled_time))

Attach the callback to the fit_* method

hook = 'YOUR_WEBHOOK'
experiment_name = 'Foo Bar classification'
slacklog = SlackLogger(webhook=hook, experiment_name=experiment_name)

model.(..., callbacks=[slacklog])

Back to posts


comments powered by Disqus