rewrite Ritual to reduce code duplication
This commit is contained in:
parent
85c9b3b5c1
commit
436f45fbb0
1 changed files with 69 additions and 101 deletions
170
onn_core.py
170
onn_core.py
|
@ -1,4 +1,5 @@
|
|||
import sys
|
||||
import types
|
||||
|
||||
def lament(*args, **kwargs):
|
||||
print(*args, file=sys.stderr, **kwargs)
|
||||
|
@ -904,7 +905,7 @@ class Model:
|
|||
|
||||
# Rituals {{{1
|
||||
|
||||
class Ritual: # i'm just making up names at this point
|
||||
class Ritual: # i'm just making up names at this point.
|
||||
def __init__(self, learner=None, loss=None, mloss=None):
|
||||
self.learner = learner if learner is not None else Learner(Optimizer())
|
||||
self.loss = loss if loss is not None else Squared()
|
||||
|
@ -916,18 +917,9 @@ class Ritual: # i'm just making up names at this point
|
|||
self.en = 0
|
||||
self.bn = 0
|
||||
|
||||
def measure(self, p, y):
|
||||
return self.mloss.forward(p, y)
|
||||
|
||||
def forward(self, p, y):
|
||||
return self.loss.forward(p, y) + self.model.regulate_forward()
|
||||
|
||||
def backward(self, p, y):
|
||||
return self.loss.backward(p, y)
|
||||
|
||||
def learn(self, inputs, outputs):
|
||||
predicted = self.model.forward(inputs)
|
||||
self.model.backward(self.backward(predicted, outputs))
|
||||
self.model.backward(self.loss.backward(predicted, outputs))
|
||||
self.model.regulate()
|
||||
return predicted
|
||||
|
||||
|
@ -939,129 +931,105 @@ class Ritual: # i'm just making up names at this point
|
|||
self.bn = 0
|
||||
self.model = model
|
||||
|
||||
def train_batched_gen(self, generator, batch_count,
|
||||
return_losses=False, test_only=False):
|
||||
assert isinstance(return_losses, bool) or return_losses == 'both'
|
||||
def _train_batch(self, batch_inputs, batch_outputs, b, batch_count,
|
||||
test_only=False, loss_logging=False, mloss_logging=True):
|
||||
if not test_only and self.learner.per_batch:
|
||||
self.learner.batch(b / batch_count)
|
||||
|
||||
if not test_only:
|
||||
self.en += 1
|
||||
if test_only:
|
||||
predicted = self.model.forward(batch_inputs, deterministic=True)
|
||||
else:
|
||||
predicted = self.learn(batch_inputs, batch_outputs)
|
||||
self.model.regulate_forward()
|
||||
self.update()
|
||||
|
||||
cumsum_loss, cumsum_mloss = _0, _0
|
||||
losses, mlosses = [], []
|
||||
|
||||
prev_batch_size = None
|
||||
|
||||
for b in range(batch_count):
|
||||
if not test_only:
|
||||
self.bn += 1
|
||||
|
||||
# TODO: pass a GeneratorData object containing en, bn, ritual/model fields.
|
||||
# ...is there a pythonic way of doing that?
|
||||
batch_inputs, batch_outputs = next(generator)
|
||||
|
||||
batch_size = batch_inputs.shape[0]
|
||||
assert batch_size == prev_batch_size or prev_batch_size is None, \
|
||||
"non-constant batch size (got {} expected {})".format(
|
||||
batch_size, prev_batch_size) # TODO: lift this restriction
|
||||
prev_batch_size = batch_size
|
||||
|
||||
# same from hereon
|
||||
|
||||
if not test_only and self.learner.per_batch:
|
||||
self.learner.batch(b / batch_count)
|
||||
|
||||
if test_only:
|
||||
predicted = self.model.forward(batch_inputs, deterministic=True)
|
||||
else:
|
||||
predicted = self.learn(batch_inputs, batch_outputs)
|
||||
self.update()
|
||||
|
||||
if return_losses == 'both':
|
||||
batch_loss = self.forward(predicted, batch_outputs)
|
||||
if np.isnan(batch_loss):
|
||||
raise Exception("nan")
|
||||
losses.append(batch_loss)
|
||||
cumsum_loss += batch_loss
|
||||
if loss_logging:
|
||||
batch_loss = self.loss.forward(predicted, batch_outputs)
|
||||
if np.isnan(batch_loss):
|
||||
raise Exception("nan")
|
||||
self.losses.append(batch_loss)
|
||||
self.cumsum_loss += batch_loss
|
||||
|
||||
if mloss_logging:
|
||||
# NOTE: this can use the non-deterministic predictions. fixme?
|
||||
batch_mloss = self.measure(predicted, batch_outputs)
|
||||
batch_mloss = self.mloss.forward(predicted, batch_outputs)
|
||||
if np.isnan(batch_mloss):
|
||||
raise Exception("nan")
|
||||
if return_losses:
|
||||
mlosses.append(batch_mloss)
|
||||
cumsum_mloss += batch_mloss
|
||||
self.mlosses.append(batch_mloss)
|
||||
self.cumsum_mloss += batch_mloss
|
||||
|
||||
avg_mloss = cumsum_mloss / _f(batch_count)
|
||||
if return_losses == 'both':
|
||||
avg_loss = cumsum_loss / _f(batch_count)
|
||||
return avg_loss, avg_mloss, losses, mlosses
|
||||
elif return_losses:
|
||||
return avg_mloss, mlosses
|
||||
return avg_mloss
|
||||
|
||||
def train_batched(self, inputs, outputs, batch_size,
|
||||
def train_batched(self, inputs_or_generator, outputs_or_batch_count,
|
||||
batch_size=None,
|
||||
return_losses=False, test_only=False, shuffle=True):
|
||||
assert isinstance(return_losses, bool) or return_losses == 'both'
|
||||
|
||||
gen = isinstance(inputs_or_generator, types.GeneratorType)
|
||||
if gen:
|
||||
generator = inputs_or_generator
|
||||
batch_count = outputs_or_batch_count
|
||||
assert isinstance(batch_count, int), type(batch_count)
|
||||
else:
|
||||
inputs = inputs_or_generator
|
||||
outputs = outputs_or_batch_count
|
||||
|
||||
if not test_only:
|
||||
self.en += 1
|
||||
|
||||
if shuffle:
|
||||
if gen:
|
||||
raise Exception("shuffling is incompatibile with using a generator.")
|
||||
indices = np.arange(inputs.shape[0])
|
||||
np.random.shuffle(indices)
|
||||
inputs = inputs[indices]
|
||||
outputs = outputs[indices]
|
||||
|
||||
cumsum_loss, cumsum_mloss = _0, _0
|
||||
batch_count = inputs.shape[0] // batch_size
|
||||
losses, mlosses = [], []
|
||||
self.cumsum_loss, self.cumsum_mloss = _0, _0
|
||||
self.losses, self.mlosses = [], []
|
||||
|
||||
assert inputs.shape[0] % batch_size == 0, \
|
||||
"inputs is not evenly divisible by batch_size" # TODO: lift this restriction
|
||||
if not gen:
|
||||
batch_count = inputs.shape[0] // batch_size
|
||||
# TODO: lift this restriction
|
||||
assert inputs.shape[0] % batch_size == 0, \
|
||||
"inputs is not evenly divisible by batch_size"
|
||||
|
||||
prev_batch_size = None
|
||||
for b in range(batch_count):
|
||||
if not test_only:
|
||||
self.bn += 1
|
||||
|
||||
bi = b * batch_size
|
||||
batch_inputs = inputs[ bi:bi+batch_size]
|
||||
batch_outputs = outputs[bi:bi+batch_size]
|
||||
|
||||
# same from hereon
|
||||
|
||||
if not test_only and self.learner.per_batch:
|
||||
self.learner.batch(b / batch_count)
|
||||
|
||||
if test_only:
|
||||
predicted = self.model.forward(batch_inputs, deterministic=True)
|
||||
if gen:
|
||||
# TODO: pass a GeneratorData object containing en, bn, ritual/model fields.
|
||||
# ...is there a pythonic way of doing that?
|
||||
batch_inputs, batch_outputs = next(generator)
|
||||
batch_size = batch_inputs.shape[0]
|
||||
# TODO: lift this restriction
|
||||
assert batch_size == prev_batch_size or prev_batch_size is None, \
|
||||
"non-constant batch size (got {}, expected {})".format(batch_size, prev_batch_size)
|
||||
else:
|
||||
predicted = self.learn(batch_inputs, batch_outputs)
|
||||
self.update()
|
||||
bi = b * batch_size
|
||||
batch_inputs = inputs[ bi:bi+batch_size]
|
||||
batch_outputs = outputs[bi:bi+batch_size]
|
||||
|
||||
if return_losses == 'both':
|
||||
batch_loss = self.forward(predicted, batch_outputs)
|
||||
if np.isnan(batch_loss):
|
||||
raise Exception("nan")
|
||||
losses.append(batch_loss)
|
||||
cumsum_loss += batch_loss
|
||||
self._train_batch(batch_inputs, batch_outputs, b, batch_count,
|
||||
test_only, return_losses == 'both', return_losses)
|
||||
|
||||
# NOTE: this can use the non-deterministic predictions. fixme?
|
||||
batch_mloss = self.measure(predicted, batch_outputs)
|
||||
if np.isnan(batch_mloss):
|
||||
raise Exception("nan")
|
||||
if return_losses:
|
||||
mlosses.append(batch_mloss)
|
||||
cumsum_mloss += batch_mloss
|
||||
prev_batch_size = batch_size
|
||||
|
||||
avg_mloss = cumsum_mloss / _f(batch_count)
|
||||
avg_mloss = self.cumsum_mloss / _f(batch_count)
|
||||
if return_losses == 'both':
|
||||
avg_loss = cumsum_loss / _f(batch_count)
|
||||
return avg_loss, avg_mloss, losses, mlosses
|
||||
avg_loss = self.cumsum_loss / _f(batch_count)
|
||||
return avg_loss, avg_mloss, self.losses, self.mlosses
|
||||
elif return_losses:
|
||||
return avg_mloss, mlosses
|
||||
return avg_mloss
|
||||
|
||||
def test_batched(self, *args, **kwargs):
|
||||
return self.train_batched(*args, test_only=True, **kwargs)
|
||||
def test_batched(self, inputs, outputs, *args, **kwargs):
|
||||
return self.train_batched(inputs, outputs, *args,
|
||||
test_only=True, **kwargs)
|
||||
|
||||
def train_batched_gen(self, generator, batch_count, *args, **kwargs):
|
||||
return self.train_batched(generator, batch_count, *args,
|
||||
shuffle=False, **kwargs)
|
||||
|
||||
# Learners {{{1
|
||||
|
||||
|
|
Loading…
Reference in a new issue