Формирователь Цепочки - Питон - Логистическая Регрессия


Я создал простую модель логистической регрессии с помощью Python и формирователь цепочки. Хотелось бы оптимизировать код, для которого я хотел бы получить некоторую помощь.

Одно ограничение: обмен на реализованных функций на уже существующие функциональные возможности формирователя цепочки не допускается. Я знаю, что есть функции потерь в формирователь цепочки, которые достигают почти той же, но более сложную модель я создаю с помощью пользовательской функции потерь.

Код является следующим:

# The goal of this Gist is to implement a simple (Logistic Regression) model such that most of the functionalities of Chainer are used
# @author  K.M.J. Jacobs
# @date    2018-02-24
# @website https://www.data-blogger.com

import chainer
from chainer import reporter as reporter_module
from chainer.training.extensions import LogReport
from chainer import iterators
from chainer import training
from chainer.datasets import TransformDataset
from chainer.training import extensions
from chainer.datasets import split_dataset
from chainer import optimizers
import chainer.optimizer
import chainer.initializers
import chainer.links as L
import chainer.functions as F
from chainer import Chain
import numpy as np

class LogisticRegressionModel(Chain):

    def __init__(self):
        super(LogisticRegressionModel, self).__init__()
        with self.init_scope():
            self.w = chainer.Parameter(initializer=chainer.initializers.Normal())
            self.w.initialize([3, 1])

    def __call__(self, x, t):
        # Call the loss function
        return self.loss(x, t)

    def predict(self, x):
        # Predict given an input (a, b, 1)
        z = F.matmul(x, self.w)
        return 1. / (1. + F.exp(-z))

    def loss(self, x, t):
        # Compute the loss for a given input (a, b, 1) and target
        y = self.predict(x)
        loss = -t * F.log(y) - (1 - t) * F.log(1 - y)
        reporter_module.report({'loss': loss.data[0, 0]}, self)
        reporter_module.report({'w': self.w[0, 0]}, self)
        return loss

def converter(minibatch, device=None):
    # For splitting array into inputs / targets
    inputs = []
    targets = []
    for item in minibatch:
        inputs.append(item[:3])
        targets.append(item[3])
    inputs = np.matrix(inputs)
    targets = np.array(targets)
    return inputs, targets

# Set the seed for reproduction
np.random.seed(0)

# The dataset consists of samples (a, b, 1) and the target is a function f such that f(a, b, 1) = a > b
# So for example: f(0.5, 0.6, 1) = 0. (False) and f(0.8, 0.2, 1) = 1. (True) since 0.8 > 0.2
# The 1 serves as bias so the model can train for a constant offset
N = 10000
data = np.random.random((N, 4))
data[:, 2] = 1.
data[:, 3] = data[:, 0] > data[:, 1]

# Split the data into a train and a test set such that there are 10 examples in the test set
data_test, data_train = split_dataset(data, 10)
train_iter = iterators.SerialIterator(data_train, 1, False, False)
test_iter = iterators.SerialIterator(data_test, 1, False, False)

# Setup the model
model = LogisticRegressionModel()

# Create the optimizer for the model
optimizer = optimizers.SGD()
optimizer.use_cleargrads(True)
optimizer.setup(model)

# Setup the training loop (and use the Evaluator, LogReport and PrintReport extension) with the following properties:
# - Run for 10.000 iterations
# - Evaluate every 1.000 iterations
# - Write logs every 1.000 iterations
# - Print the losses and the epoch, iteration and elapsed_time
trainer = training.Trainer(training.StandardUpdater(train_iter, optimizer, converter), (10001, 'iteration'), out='result')
trainer.extend(extensions.Evaluator(test_iter, model, converter), trigger=(1000, 'iteration'))
trainer.extend(extensions.LogReport(trigger=(1000, 'iteration')))
trainer.extend(extensions.PrintReport(['epoch', 'iteration', 'main/loss', 'validation/main/loss', 'main/w', 'validation/main/w', 'elapsed_time']))#, 'main/loss', 'validation/main/loss', 'elapsed_time'], ))
trainer.run()

Я бы хотел, чтобы держать код модели максимально чистой, но, как видите, __call__ способ направлен в сторону loss метод и я подозреваю, что есть более рациональный способ, чтобы вызвать loss метод в тренировочном цикле. Я подумал, что было бы чище, если __call__ метод выводит предсказание и есть отдельно loss метод для вычисления потери. Каковы ваши мысли по этому поводу?

Я тоже не уверен на функции конвертера. Есть ли лучший способ добиться того же результата?

У вас есть какие-либо замечания или рекомендации по написанию кода формирователя цепочки?



Комментарии