public function partial(Dataset $dataset) : void
    {
        if (!$this->network) {
            $this->train($dataset);

            return;
        }

        if (!$dataset instanceof Labeled) {
            throw new InvalidArgumentException('This estimator requires a'
                . ' labeled training set.');
        }

        DatasetIsCompatibleWithEstimator::check($dataset, $this);

        if ($this->logger) {
            $this->logger->info('Learner init ' . Params::stringify([
                'batch_size' => $this->batchSize,
                'optimizer' => $this->optimizer,
                'alpha' => $this->alpha,
                'epochs' => $this->epochs,
                'min_change' => $this->minChange,
                'cost_fn' => $this->costFn,
            ]));
        }
        
        $n = $dataset->numRows();

        $randomize = $n > $this->batchSize ? true : false;

        $previous = INF;

        for ($epoch = 1; $epoch <= $this->epochs; $epoch++) {
            if ($randomize) {
                $dataset->randomize();
            }

            $batches = $dataset->batch($this->batchSize);

            $loss = 0.;

            foreach ($batches as $batch) {
                $loss += $this->network->roundtrip($batch);
            }

            $loss /= $n;

            $this->steps[] = $loss;

            if ($this->logger) {
                $this->logger->info("Epoch $epoch complete, loss=$loss");
            }

            if (is_nan($loss)) {
                break 1;
            }

            if (abs($previous - $loss) < $this->minChange) {
                break 1;
            }

            $previous = $loss;
        }

        if ($this->logger) {
            $this->logger->info('Training complete');
        }
    }