torchsight.loggers.print module

A logger that only uses the print() function and has static methods to read those lines.

Source code
"""A logger that only uses the print() function and has static methods to read those lines."""
import os

from .abstract import AbstractLogger


class PrintLogger(AbstractLogger):
    """Log the data only using the print() function.

    How can we store the values? Calling the script and setting the output to a file.
    Example:
    python train.py > logs.txt

    So this Logger class can be used to parse those logs and get information using static methods.

    A good practice would be having an already created directory where to store the logs and initialize
    this logger with that directory and output the stdout to a file inside that directory.
    """

    def __init__(self, description=None, directory=None):
        """Initialize the logger.

        Arguments:
            description (str, optional): A description to save in the directory as a txt file.
                Useful to store the hyperparameters of the training for example.
            directory (str, optional): The directory where to save the description file.
        """
        if directory is not None and not os.path.exists(directory):
            os.makedirs(directory)

        self.directory = directory

        if description is not None:
            with open(os.path.join(directory, 'description.txt'), 'w') as file:
                file.write(description)

    def log(self, data):
        """Log the data dict.

        It generates a line using print() that has every key-value pair of the dict like:
        [key1 value1] [key2 value2] ... [key_n value_n]

        If you want to print only a key (like 'validating') you can pass a None value.

        Arguments:
            data (dict): A dictionary with key-value pairs to log.
        """
        items = []
        for key, value in data.items():
            if value is None:
                items.append('[{}]'.format(key))
            else:
                items.append('[{} {}]'.format(key, value))
        print(' '.join(items))

    @staticmethod
    def read(filepath, keep=None):
        """Read a file that could contain lines generated by this logger.

        The file could have lines from other modules by this logger will only take the lines that start with '[' and
        finishes with ']'.

        Arguments:
            filepath (str): The path to the file that contains the lines generated by this logger.
            keep (function, optional): A function that returns True if the line must be keeped
                or False if not.
                Example: A line could be '[Validating] [Epoch 10] ...' so you can implement a lambda like
                lambda x: x[:13] == '[Validating]' to return only the lines of the validation.

        Returns:
            list: A list with each logged dict.
        """
        with open(filepath, 'r') as file:
            lines = file.read().split('\n')

        # Clean the not logs lines
        logs = []
        for line in lines:
            if not line:
                continue

            if line[0] != '[' or line[-1] != ']':
                continue

            if keep is not None and not keep(line):
                continue

            line = line[1:-1]  # Remove the first '[' and the last ']'
            pairs = line.split('] [')  # Get the key-value pairs
            current = {}  # Current log dict
            for pair in pairs:
                try:
                    key, value = pair.split(' ')
                    current[key] = value
                except ValueError:
                    # There is only a key without a value
                    current[pair] = None
            logs.append(current)

        return logs

    @staticmethod
    def epochs_losses(filepath, epoch_key='epoch', loss_key='loss', keep=None):
        """Get the average loss per epoch given a logs files.

        Arguments:
            filepath (str): The path to the file that contains the lines generated by this logger.
            epoch_key (str, optional): The key of the epoch in the log dict.
            loss_key (str, optional): The key of the loss in the log dict.
            keep (function, optional): See read() method.

        Returns:
            dict: Dict with epoch as key and an other dict as value with 'sum', 'count' and 'average'.
                Where 'sum' is the sum of the losses of the epochs, 'count' is how many logs does the
                epoch have and 'average' is simply 'sum' divided by 'count'.
        """
        losses = {}  # Initialize the return value

        for log in PrintLogger.read(filepath, keep):
            epoch = log[epoch_key]
            try:
                loss = log[loss_key]
            except KeyError:
                continue
            if epoch not in losses:
                losses[epoch] = {'sum': 0, 'count': 0}
            losses[epoch]['sum'] += float(loss)
            losses[epoch]['count'] += 1
        # Get the average for each epoch
        for epoch in losses:
            losses[epoch]['average'] = losses[epoch]['sum'] / losses[epoch]['count']

        return losses

Classes

class PrintLogger (ancestors: AbstractLogger)

Log the data only using the print() function.

How can we store the values? Calling the script and setting the output to a file. Example: python train.py > logs.txt

So this Logger class can be used to parse those logs and get information using static methods.

A good practice would be having an already created directory where to store the logs and initialize this logger with that directory and output the stdout to a file inside that directory.

Source code
class PrintLogger(AbstractLogger):
    """Log the data only using the print() function.

    How can we store the values? Calling the script and setting the output to a file.
    Example:
    python train.py > logs.txt

    So this Logger class can be used to parse those logs and get information using static methods.

    A good practice would be having an already created directory where to store the logs and initialize
    this logger with that directory and output the stdout to a file inside that directory.
    """

    def __init__(self, description=None, directory=None):
        """Initialize the logger.

        Arguments:
            description (str, optional): A description to save in the directory as a txt file.
                Useful to store the hyperparameters of the training for example.
            directory (str, optional): The directory where to save the description file.
        """
        if directory is not None and not os.path.exists(directory):
            os.makedirs(directory)

        self.directory = directory

        if description is not None:
            with open(os.path.join(directory, 'description.txt'), 'w') as file:
                file.write(description)

    def log(self, data):
        """Log the data dict.

        It generates a line using print() that has every key-value pair of the dict like:
        [key1 value1] [key2 value2] ... [key_n value_n]

        If you want to print only a key (like 'validating') you can pass a None value.

        Arguments:
            data (dict): A dictionary with key-value pairs to log.
        """
        items = []
        for key, value in data.items():
            if value is None:
                items.append('[{}]'.format(key))
            else:
                items.append('[{} {}]'.format(key, value))
        print(' '.join(items))

    @staticmethod
    def read(filepath, keep=None):
        """Read a file that could contain lines generated by this logger.

        The file could have lines from other modules by this logger will only take the lines that start with '[' and
        finishes with ']'.

        Arguments:
            filepath (str): The path to the file that contains the lines generated by this logger.
            keep (function, optional): A function that returns True if the line must be keeped
                or False if not.
                Example: A line could be '[Validating] [Epoch 10] ...' so you can implement a lambda like
                lambda x: x[:13] == '[Validating]' to return only the lines of the validation.

        Returns:
            list: A list with each logged dict.
        """
        with open(filepath, 'r') as file:
            lines = file.read().split('\n')

        # Clean the not logs lines
        logs = []
        for line in lines:
            if not line:
                continue

            if line[0] != '[' or line[-1] != ']':
                continue

            if keep is not None and not keep(line):
                continue

            line = line[1:-1]  # Remove the first '[' and the last ']'
            pairs = line.split('] [')  # Get the key-value pairs
            current = {}  # Current log dict
            for pair in pairs:
                try:
                    key, value = pair.split(' ')
                    current[key] = value
                except ValueError:
                    # There is only a key without a value
                    current[pair] = None
            logs.append(current)

        return logs

    @staticmethod
    def epochs_losses(filepath, epoch_key='epoch', loss_key='loss', keep=None):
        """Get the average loss per epoch given a logs files.

        Arguments:
            filepath (str): The path to the file that contains the lines generated by this logger.
            epoch_key (str, optional): The key of the epoch in the log dict.
            loss_key (str, optional): The key of the loss in the log dict.
            keep (function, optional): See read() method.

        Returns:
            dict: Dict with epoch as key and an other dict as value with 'sum', 'count' and 'average'.
                Where 'sum' is the sum of the losses of the epochs, 'count' is how many logs does the
                epoch have and 'average' is simply 'sum' divided by 'count'.
        """
        losses = {}  # Initialize the return value

        for log in PrintLogger.read(filepath, keep):
            epoch = log[epoch_key]
            try:
                loss = log[loss_key]
            except KeyError:
                continue
            if epoch not in losses:
                losses[epoch] = {'sum': 0, 'count': 0}
            losses[epoch]['sum'] += float(loss)
            losses[epoch]['count'] += 1
        # Get the average for each epoch
        for epoch in losses:
            losses[epoch]['average'] = losses[epoch]['sum'] / losses[epoch]['count']

        return losses

Static methods

def epochs_losses(filepath, epoch_key='epoch', loss_key='loss', keep=None)

Get the average loss per epoch given a logs files.

Arguments

filepath : str
The path to the file that contains the lines generated by this logger.
epoch_key : str, optional
The key of the epoch in the log dict.
loss_key : str, optional
The key of the loss in the log dict.
keep : function, optional
See read() method.

Returns

dict
Dict with epoch as key and an other dict as value with 'sum', 'count' and 'average'. Where 'sum' is the sum of the losses of the epochs, 'count' is how many logs does the epoch have and 'average' is simply 'sum' divided by 'count'.
Source code
@staticmethod
def epochs_losses(filepath, epoch_key='epoch', loss_key='loss', keep=None):
    """Get the average loss per epoch given a logs files.

    Arguments:
        filepath (str): The path to the file that contains the lines generated by this logger.
        epoch_key (str, optional): The key of the epoch in the log dict.
        loss_key (str, optional): The key of the loss in the log dict.
        keep (function, optional): See read() method.

    Returns:
        dict: Dict with epoch as key and an other dict as value with 'sum', 'count' and 'average'.
            Where 'sum' is the sum of the losses of the epochs, 'count' is how many logs does the
            epoch have and 'average' is simply 'sum' divided by 'count'.
    """
    losses = {}  # Initialize the return value

    for log in PrintLogger.read(filepath, keep):
        epoch = log[epoch_key]
        try:
            loss = log[loss_key]
        except KeyError:
            continue
        if epoch not in losses:
            losses[epoch] = {'sum': 0, 'count': 0}
        losses[epoch]['sum'] += float(loss)
        losses[epoch]['count'] += 1
    # Get the average for each epoch
    for epoch in losses:
        losses[epoch]['average'] = losses[epoch]['sum'] / losses[epoch]['count']

    return losses
def read(filepath, keep=None)

Read a file that could contain lines generated by this logger.

The file could have lines from other modules by this logger will only take the lines that start with '[' and finishes with ']'.

Arguments

filepath : str
The path to the file that contains the lines generated by this logger.
keep : function, optional
A function that returns True if the line must be keeped or False if not. Example: A line could be '[Validating] [Epoch 10] …' so you can implement a lambda like lambda x: x[:13] == '[Validating]' to return only the lines of the validation.

Returns

list
A list with each logged dict.
Source code
@staticmethod
def read(filepath, keep=None):
    """Read a file that could contain lines generated by this logger.

    The file could have lines from other modules by this logger will only take the lines that start with '[' and
    finishes with ']'.

    Arguments:
        filepath (str): The path to the file that contains the lines generated by this logger.
        keep (function, optional): A function that returns True if the line must be keeped
            or False if not.
            Example: A line could be '[Validating] [Epoch 10] ...' so you can implement a lambda like
            lambda x: x[:13] == '[Validating]' to return only the lines of the validation.

    Returns:
        list: A list with each logged dict.
    """
    with open(filepath, 'r') as file:
        lines = file.read().split('\n')

    # Clean the not logs lines
    logs = []
    for line in lines:
        if not line:
            continue

        if line[0] != '[' or line[-1] != ']':
            continue

        if keep is not None and not keep(line):
            continue

        line = line[1:-1]  # Remove the first '[' and the last ']'
        pairs = line.split('] [')  # Get the key-value pairs
        current = {}  # Current log dict
        for pair in pairs:
            try:
                key, value = pair.split(' ')
                current[key] = value
            except ValueError:
                # There is only a key without a value
                current[pair] = None
        logs.append(current)

    return logs

Methods

def __init__(self, description=None, directory=None)

Initialize the logger.

Arguments

description : str, optional
A description to save in the directory as a txt file. Useful to store the hyperparameters of the training for example.
directory : str, optional
The directory where to save the description file.
Source code
def __init__(self, description=None, directory=None):
    """Initialize the logger.

    Arguments:
        description (str, optional): A description to save in the directory as a txt file.
            Useful to store the hyperparameters of the training for example.
        directory (str, optional): The directory where to save the description file.
    """
    if directory is not None and not os.path.exists(directory):
        os.makedirs(directory)

    self.directory = directory

    if description is not None:
        with open(os.path.join(directory, 'description.txt'), 'w') as file:
            file.write(description)
def log(self, data)

Log the data dict.

It generates a line using print() that has every key-value pair of the dict like: [key1 value1] [key2 value2] … [key_n value_n]

If you want to print only a key (like 'validating') you can pass a None value.

Arguments

data : dict
A dictionary with key-value pairs to log.
Source code
def log(self, data):
    """Log the data dict.

    It generates a line using print() that has every key-value pair of the dict like:
    [key1 value1] [key2 value2] ... [key_n value_n]

    If you want to print only a key (like 'validating') you can pass a None value.

    Arguments:
        data (dict): A dictionary with key-value pairs to log.
    """
    items = []
    for key, value in data.items():
        if value is None:
            items.append('[{}]'.format(key))
        else:
            items.append('[{} {}]'.format(key, value))
    print(' '.join(items))