Fix linting
This commit is contained in:
@@ -21,8 +21,7 @@ class PerceptronInitialization implements ShouldBroadcast
|
|||||||
public ActivationsFunctions $activationFunction,
|
public ActivationsFunctions $activationFunction,
|
||||||
public string $sessionId,
|
public string $sessionId,
|
||||||
public string $trainingId,
|
public string $trainingId,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,7 +33,7 @@ class PerceptronInitialization implements ShouldBroadcast
|
|||||||
public function broadcastOn(): array
|
public function broadcastOn(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
new Channel($this->sessionId . '-perceptron-training'),
|
new Channel($this->sessionId.'-perceptron-training'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,6 @@ namespace App\Events;
|
|||||||
|
|
||||||
use Illuminate\Broadcasting\Channel;
|
use Illuminate\Broadcasting\Channel;
|
||||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
use Illuminate\Broadcasting\PresenceChannel;
|
|
||||||
use Illuminate\Broadcasting\PrivateChannel;
|
|
||||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
||||||
use Illuminate\Foundation\Events\Dispatchable;
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
@@ -21,8 +19,7 @@ class PerceptronTrainingEnded implements ShouldBroadcast
|
|||||||
public string $reason,
|
public string $reason,
|
||||||
public string $sessionId,
|
public string $sessionId,
|
||||||
public string $trainingId,
|
public string $trainingId,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,7 +31,7 @@ class PerceptronTrainingEnded implements ShouldBroadcast
|
|||||||
public function broadcastOn(): array
|
public function broadcastOn(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
new Channel($this->sessionId . '-perceptron-training'),
|
new Channel($this->sessionId.'-perceptron-training'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,8 +19,7 @@ class PerceptronTrainingIteration implements ShouldBroadcast
|
|||||||
public array $iterations, // ["epoch" => int, "exampleIndex" => int, "error" => float, "synaptic_weights" => array]
|
public array $iterations, // ["epoch" => int, "exampleIndex" => int, "error" => float, "synaptic_weights" => array]
|
||||||
public string $sessionId,
|
public string $sessionId,
|
||||||
public string $trainingId,
|
public string $trainingId,
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
//
|
//
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33,7 +32,7 @@ class PerceptronTrainingIteration implements ShouldBroadcast
|
|||||||
{
|
{
|
||||||
// Log::debug("Broadcasting on channel: " . $this->sessionId . '-perceptron-training');
|
// Log::debug("Broadcasting on channel: " . $this->sessionId . '-perceptron-training');
|
||||||
return [
|
return [
|
||||||
new Channel($this->sessionId . '-perceptron-training'),
|
new Channel($this->sessionId.'-perceptron-training'),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ class PerceptronController extends Controller
|
|||||||
if (pathinfo($file, PATHINFO_EXTENSION) === 'csv') {
|
if (pathinfo($file, PATHINFO_EXTENSION) === 'csv') {
|
||||||
$dataset = [];
|
$dataset = [];
|
||||||
$dataset['label'] = str_replace('.csv', '', $file);
|
$dataset['label'] = str_replace('.csv', '', $file);
|
||||||
$dataSetReader = new LinearOrderDataSetReader($dataSetsDirectory . '/' . $file);
|
$dataSetReader = new LinearOrderDataSetReader($dataSetsDirectory.'/'.$file);
|
||||||
$dataset['data'] = [];
|
$dataset['data'] = [];
|
||||||
switch (count($dataSetReader->lines[0])) {
|
switch (count($dataSetReader->lines[0])) {
|
||||||
case 3:
|
case 3:
|
||||||
@@ -113,12 +113,14 @@ class PerceptronController extends Controller
|
|||||||
$datasets[] = $dataset;
|
$datasets[] = $dataset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $datasets;
|
return $datasets;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getDataSetReader(string $dataSet): IDataSetReader
|
private function getDataSetReader(string $dataSet): IDataSetReader
|
||||||
{
|
{
|
||||||
$dataSetFileName = "data_sets/{$dataSet}.csv";
|
$dataSetFileName = "data_sets/{$dataSet}.csv";
|
||||||
|
|
||||||
return new RandomOrderDataSetReader($dataSetFileName);
|
return new RandomOrderDataSetReader($dataSetFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,18 +136,17 @@ class PerceptronController extends Controller
|
|||||||
$trainingId = $request->input('training_id');
|
$trainingId = $request->input('training_id');
|
||||||
|
|
||||||
if ($weightInitMethod === 'zeros') {
|
if ($weightInitMethod === 'zeros') {
|
||||||
$synapticWeightsProvider = new ZeroSynapticWeights();
|
$synapticWeightsProvider = new ZeroSynapticWeights;
|
||||||
}
|
}
|
||||||
|
|
||||||
$iterationEventBuffer = new PerceptronIterationEventBuffer($sessionId, $trainingId);
|
$iterationEventBuffer = new PerceptronIterationEventBuffer($sessionId, $trainingId);
|
||||||
if ($maxEpochs > config('perceptron.limited_broadcast_iterations')) {
|
if ($maxEpochs > config('perceptron.limited_broadcast_iterations')) {
|
||||||
$iterationsInterval = (int)($maxEpochs / config('perceptron.limited_broadcast_iterations'));
|
$iterationsInterval = (int) ($maxEpochs / config('perceptron.limited_broadcast_iterations'));
|
||||||
$iterationEventBuffer = new PerceptronLimitedEpochEventBuffer($sessionId, $trainingId, $iterationsInterval);
|
$iterationEventBuffer = new PerceptronLimitedEpochEventBuffer($sessionId, $trainingId, $iterationsInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
$datasetReader = $this->getDataSetReader($dataSet);
|
$datasetReader = $this->getDataSetReader($dataSet);
|
||||||
|
|
||||||
|
|
||||||
$networkTraining = match ($perceptronType) {
|
$networkTraining = match ($perceptronType) {
|
||||||
'simple' => new SimpleBinaryPerceptronTraining($datasetReader, $learningRate, $maxEpochs, $synapticWeightsProvider, $iterationEventBuffer, $sessionId, $trainingId),
|
'simple' => new SimpleBinaryPerceptronTraining($datasetReader, $learningRate, $maxEpochs, $synapticWeightsProvider, $iterationEventBuffer, $sessionId, $trainingId),
|
||||||
'gradientdescent' => new GradientDescentPerceptronTraining($datasetReader, $learningRate, $maxEpochs, $synapticWeightsProvider, $iterationEventBuffer, $sessionId, $trainingId, $minError),
|
'gradientdescent' => new GradientDescentPerceptronTraining($datasetReader, $learningRate, $maxEpochs, $synapticWeightsProvider, $iterationEventBuffer, $sessionId, $trainingId, $minError),
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class ADALINEPerceptronTraining extends NetworkTraining
|
|||||||
$synaptic_weights = $this->perceptron->getSynapticWeights();
|
$synaptic_weights = $this->perceptron->getSynapticWeights();
|
||||||
$inputs_with_bias = array_merge([1], $inputs); // Add bias input
|
$inputs_with_bias = array_merge([1], $inputs); // Add bias input
|
||||||
$new_weights = array_map(
|
$new_weights = array_map(
|
||||||
fn($weight, $weightIndex) => $weight + ($this->learningRate * $iterationError * $inputs_with_bias[$weightIndex]),
|
fn ($weight, $weightIndex) => $weight + ($this->learningRate * $iterationError * $inputs_with_bias[$weightIndex]),
|
||||||
$synaptic_weights,
|
$synaptic_weights,
|
||||||
array_keys($synaptic_weights)
|
array_keys($synaptic_weights)
|
||||||
);
|
);
|
||||||
@@ -73,7 +73,7 @@ class ADALINEPerceptronTraining extends NetworkTraining
|
|||||||
$this->epochError /= $this->datasetReader->getEpochExamplesCount(); // Average error for the epoch
|
$this->epochError /= $this->datasetReader->getEpochExamplesCount(); // Average error for the epoch
|
||||||
|
|
||||||
$this->datasetReader->reset(); // Reset the dataset for the next iteration
|
$this->datasetReader->reset(); // Reset the dataset for the next iteration
|
||||||
} while ($this->epoch < $this->maxEpochs && !$this->stopCondition());
|
} while ($this->epoch < $this->maxEpochs && ! $this->stopCondition());
|
||||||
|
|
||||||
$this->iterationEventBuffer->flush(); // Ensure all iterations are sent to the frontend
|
$this->iterationEventBuffer->flush(); // Ensure all iterations are sent to the frontend
|
||||||
|
|
||||||
@@ -86,6 +86,7 @@ class ADALINEPerceptronTraining extends NetworkTraining
|
|||||||
if ($condition === true) {
|
if ($condition === true) {
|
||||||
event(new PerceptronTrainingEnded('Le perceptron à atteint l\'erreur minimale', $this->sessionId, $this->trainingId));
|
event(new PerceptronTrainingEnded('Le perceptron à atteint l\'erreur minimale', $this->sessionId, $this->trainingId));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $condition;
|
return $condition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -62,14 +62,14 @@ class GradientDescentPerceptronTraining extends NetworkTraining
|
|||||||
// Synaptic weights correction after each epoch
|
// Synaptic weights correction after each epoch
|
||||||
$synaptic_weights = $this->perceptron->getSynapticWeights();
|
$synaptic_weights = $this->perceptron->getSynapticWeights();
|
||||||
$new_weights = array_map(
|
$new_weights = array_map(
|
||||||
fn($weight, $weightIndex) => $weight + $this->learningRate * array_sum($epochCorrectorPerWeight[$weightIndex]),
|
fn ($weight, $weightIndex) => $weight + $this->learningRate * array_sum($epochCorrectorPerWeight[$weightIndex]),
|
||||||
$synaptic_weights,
|
$synaptic_weights,
|
||||||
array_keys($synaptic_weights)
|
array_keys($synaptic_weights)
|
||||||
);
|
);
|
||||||
$this->perceptron->setSynapticWeights($new_weights);
|
$this->perceptron->setSynapticWeights($new_weights);
|
||||||
|
|
||||||
$this->datasetReader->reset(); // Reset the dataset for the next iteration
|
$this->datasetReader->reset(); // Reset the dataset for the next iteration
|
||||||
} while ($this->epoch < $this->maxEpochs && !$this->stopCondition());
|
} while ($this->epoch < $this->maxEpochs && ! $this->stopCondition());
|
||||||
|
|
||||||
$this->iterationEventBuffer->flush(); // Ensure all iterations are sent to the frontend
|
$this->iterationEventBuffer->flush(); // Ensure all iterations are sent to the frontend
|
||||||
|
|
||||||
@@ -82,6 +82,7 @@ class GradientDescentPerceptronTraining extends NetworkTraining
|
|||||||
if ($condition === true) {
|
if ($condition === true) {
|
||||||
event(new PerceptronTrainingEnded('Le perceptron à atteint l\'erreur minimale', $this->sessionId, $this->trainingId));
|
event(new PerceptronTrainingEnded('Le perceptron à atteint l\'erreur minimale', $this->sessionId, $this->trainingId));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $condition;
|
return $condition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,9 @@
|
|||||||
namespace App\Models\NetworksTraining;
|
namespace App\Models\NetworksTraining;
|
||||||
|
|
||||||
use App\Events\PerceptronTrainingEnded;
|
use App\Events\PerceptronTrainingEnded;
|
||||||
|
use App\Models\ActivationsFunctions;
|
||||||
use App\Services\DatasetReader\IDataSetReader;
|
use App\Services\DatasetReader\IDataSetReader;
|
||||||
use App\Services\IterationEventBuffer\IPerceptronIterationEventBuffer;
|
use App\Services\IterationEventBuffer\IPerceptronIterationEventBuffer;
|
||||||
use App\Models\ActivationsFunctions;
|
|
||||||
|
|
||||||
abstract class NetworkTraining
|
abstract class NetworkTraining
|
||||||
{
|
{
|
||||||
@@ -13,7 +13,6 @@ abstract class NetworkTraining
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @abstract
|
* @abstract
|
||||||
* @var ActivationsFunctions
|
|
||||||
*/
|
*/
|
||||||
public ActivationsFunctions $activationFunction;
|
public ActivationsFunctions $activationFunction;
|
||||||
|
|
||||||
@@ -23,13 +22,14 @@ abstract class NetworkTraining
|
|||||||
protected IPerceptronIterationEventBuffer $iterationEventBuffer,
|
protected IPerceptronIterationEventBuffer $iterationEventBuffer,
|
||||||
protected string $sessionId,
|
protected string $sessionId,
|
||||||
protected string $trainingId,
|
protected string $trainingId,
|
||||||
) {
|
) {}
|
||||||
}
|
|
||||||
|
abstract public function start(): void;
|
||||||
|
|
||||||
abstract public function start() : void;
|
|
||||||
abstract protected function stopCondition(): bool;
|
abstract protected function stopCondition(): bool;
|
||||||
|
|
||||||
protected function checkPassedMaxIterations(?float $finalError) {
|
protected function checkPassedMaxIterations(?float $finalError)
|
||||||
|
{
|
||||||
if ($this->epoch >= $this->maxEpochs) {
|
if ($this->epoch >= $this->maxEpochs) {
|
||||||
$message = 'Le nombre maximal d\'epoch a été atteint';
|
$message = 'Le nombre maximal d\'epoch a été atteint';
|
||||||
if ($finalError) {
|
if ($finalError) {
|
||||||
@@ -40,7 +40,8 @@ abstract class NetworkTraining
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function addIterationToBuffer(float $error, array $synapticWeights) {
|
protected function addIterationToBuffer(float $error, array $synapticWeights)
|
||||||
|
{
|
||||||
$this->iterationEventBuffer->addIteration($this->epoch, $this->datasetReader->getLastReadLineIndex(), $error, $synapticWeights);
|
$this->iterationEventBuffer->addIteration($this->epoch, $this->datasetReader->getLastReadLineIndex(), $error, $synapticWeights);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ use App\Services\SynapticWeightsProvider\ISynapticWeightsProvider;
|
|||||||
class SimpleBinaryPerceptronTraining extends NetworkTraining
|
class SimpleBinaryPerceptronTraining extends NetworkTraining
|
||||||
{
|
{
|
||||||
private Perceptron $perceptron;
|
private Perceptron $perceptron;
|
||||||
|
|
||||||
private int $iterationErrorCounter = 0;
|
private int $iterationErrorCounter = 0;
|
||||||
|
|
||||||
public ActivationsFunctions $activationFunction = ActivationsFunctions::STEP;
|
public ActivationsFunctions $activationFunction = ActivationsFunctions::STEP;
|
||||||
@@ -51,7 +52,7 @@ class SimpleBinaryPerceptronTraining extends NetworkTraining
|
|||||||
$this->addIterationToBuffer($error, [[$this->perceptron->getSynapticWeights()]]);
|
$this->addIterationToBuffer($error, [[$this->perceptron->getSynapticWeights()]]);
|
||||||
}
|
}
|
||||||
$this->datasetReader->reset(); // Reset the dataset for the next iteration
|
$this->datasetReader->reset(); // Reset the dataset for the next iteration
|
||||||
} while ($this->epoch < $this->maxEpochs && !$this->stopCondition());
|
} while ($this->epoch < $this->maxEpochs && ! $this->stopCondition());
|
||||||
|
|
||||||
$this->iterationEventBuffer->flush(); // Ensure all iterations are sent to the frontend
|
$this->iterationEventBuffer->flush(); // Ensure all iterations are sent to the frontend
|
||||||
|
|
||||||
@@ -64,6 +65,7 @@ class SimpleBinaryPerceptronTraining extends NetworkTraining
|
|||||||
if ($condition === true) {
|
if ($condition === true) {
|
||||||
event(new PerceptronTrainingEnded('Le perceptron ne commet plus d\'erreurs sur aucune des données', $this->sessionId, $this->trainingId));
|
event(new PerceptronTrainingEnded('Le perceptron ne commet plus d\'erreurs sur aucune des données', $this->sessionId, $this->trainingId));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->iterationErrorCounter == 0;
|
return $this->iterationErrorCounter == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,9 +81,10 @@ class SimpleBinaryPerceptronTraining extends NetworkTraining
|
|||||||
if ($error !== 0) { // Update synaptic weights if needed
|
if ($error !== 0) { // Update synaptic weights if needed
|
||||||
$synaptic_weights = $this->perceptron->getSynapticWeights();
|
$synaptic_weights = $this->perceptron->getSynapticWeights();
|
||||||
$inputs_with_bias = array_merge([1], $inputs); // Add bias input
|
$inputs_with_bias = array_merge([1], $inputs); // Add bias input
|
||||||
$new_weights = array_map(fn($weight, $input) => $weight + $this->learningRate * $error * $input, $synaptic_weights, $inputs_with_bias);
|
$new_weights = array_map(fn ($weight, $input) => $weight + $this->learningRate * $error * $input, $synaptic_weights, $inputs_with_bias);
|
||||||
$this->perceptron->setSynapticWeights($new_weights);
|
$this->perceptron->setSynapticWeights($new_weights);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $error;
|
return $error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
namespace App\Models\Perceptrons;
|
namespace App\Models\Perceptrons;
|
||||||
|
|
||||||
class GradientDescentPerceptron extends Perceptron {
|
class GradientDescentPerceptron extends Perceptron
|
||||||
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
array $synaptic_weights,
|
array $synaptic_weights,
|
||||||
) {
|
) {
|
||||||
@@ -14,5 +14,4 @@ class GradientDescentPerceptron extends Perceptron {
|
|||||||
{
|
{
|
||||||
return $weighted_sum;
|
return $weighted_sum;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,10 +17,11 @@ abstract class Perceptron extends Model
|
|||||||
$inputs = array_merge([1], $inputs); // Add bias input
|
$inputs = array_merge([1], $inputs); // Add bias input
|
||||||
|
|
||||||
if (count($inputs) !== count($this->synaptic_weights)) { // Check
|
if (count($inputs) !== count($this->synaptic_weights)) { // Check
|
||||||
throw new \InvalidArgumentException("Number of inputs must match number of synaptic weights.");
|
throw new \InvalidArgumentException('Number of inputs must match number of synaptic weights.');
|
||||||
}
|
}
|
||||||
|
|
||||||
$weighted_sum = array_sum(array_map(fn($input, $weight) => $input * $weight, $inputs, $this->synaptic_weights));
|
$weighted_sum = array_sum(array_map(fn ($input, $weight) => $input * $weight, $inputs, $this->synaptic_weights));
|
||||||
|
|
||||||
return $this->activationFunction($weighted_sum);
|
return $this->activationFunction($weighted_sum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
|
|
||||||
namespace App\Models\Perceptrons;
|
namespace App\Models\Perceptrons;
|
||||||
|
|
||||||
class SimpleBinaryPerceptron extends Perceptron {
|
class SimpleBinaryPerceptron extends Perceptron
|
||||||
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
array $synaptic_weights,
|
array $synaptic_weights,
|
||||||
) {
|
) {
|
||||||
@@ -14,5 +14,4 @@ class SimpleBinaryPerceptron extends Perceptron {
|
|||||||
{
|
{
|
||||||
return $weighted_sum >= 0.0 ? 1.0 : 0.0;
|
return $weighted_sum >= 0.0 ? 1.0 : 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ use Carbon\CarbonImmutable;
|
|||||||
use Illuminate\Support\Facades\Date;
|
use Illuminate\Support\Facades\Date;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
use Illuminate\Validation\Rules\Password;
|
|
||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
class AppServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class InitialSynapticWeightsProvider extends ServiceProvider
|
|||||||
public function register(): void
|
public function register(): void
|
||||||
{
|
{
|
||||||
$this->app->singleton(ISynapticWeightsProvider::class, function ($app) {
|
$this->app->singleton(ISynapticWeightsProvider::class, function ($app) {
|
||||||
return new RandomSynapticWeights();
|
return new RandomSynapticWeights;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
namespace App\Services;
|
namespace App\Services;
|
||||||
|
|
||||||
class CsvReader {
|
class CsvReader
|
||||||
|
{
|
||||||
private $file;
|
private $file;
|
||||||
// private array $headers;
|
// private array $headers;
|
||||||
|
|
||||||
@@ -10,11 +11,10 @@ class CsvReader {
|
|||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
public string $filename,
|
public string $filename,
|
||||||
)
|
) {
|
||||||
{
|
$this->file = fopen($filename, 'r');
|
||||||
$this->file = fopen($filename, "r");
|
if (! $this->file) {
|
||||||
if (!$this->file) {
|
throw new \RuntimeException('Failed to open file: '.$filename);
|
||||||
throw new \RuntimeException("Failed to open file: " . $filename);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// $this->headers = $this->readNextLine();
|
// $this->headers = $this->readNextLine();
|
||||||
@@ -22,9 +22,10 @@ class CsvReader {
|
|||||||
|
|
||||||
public function readNextLine(): ?array
|
public function readNextLine(): ?array
|
||||||
{
|
{
|
||||||
if (($data = fgetcsv($this->file, 1000, ",")) !== FALSE) {
|
if (($data = fgetcsv($this->file, 1000, ',')) !== false) {
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null; // End of file or error
|
return null; // End of file or error
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,10 +2,15 @@
|
|||||||
|
|
||||||
namespace App\Services\DatasetReader;
|
namespace App\Services\DatasetReader;
|
||||||
|
|
||||||
interface IDataSetReader {
|
interface IDataSetReader
|
||||||
public function getNextLine(): array | null;
|
{
|
||||||
|
public function getNextLine(): ?array;
|
||||||
|
|
||||||
public function getInputSize(): int;
|
public function getInputSize(): int;
|
||||||
|
|
||||||
public function reset(): void;
|
public function reset(): void;
|
||||||
|
|
||||||
public function getLastReadLineIndex(): int;
|
public function getLastReadLineIndex(): int;
|
||||||
|
|
||||||
public function getEpochExamplesCount(): int;
|
public function getEpochExamplesCount(): int;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ namespace App\Services\DatasetReader;
|
|||||||
|
|
||||||
use App\Services\CsvReader;
|
use App\Services\CsvReader;
|
||||||
|
|
||||||
class LinearOrderDataSetReader implements IDataSetReader {
|
class LinearOrderDataSetReader implements IDataSetReader
|
||||||
|
{
|
||||||
public array $lines = [];
|
public array $lines = [];
|
||||||
|
|
||||||
private array $currentLines = [];
|
private array $currentLines = [];
|
||||||
|
|
||||||
private int $lastReadLineIndex = -1;
|
private int $lastReadLineIndex = -1;
|
||||||
@@ -36,8 +38,9 @@ class LinearOrderDataSetReader implements IDataSetReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getNextLine(): array | null {
|
public function getNextLine(): ?array
|
||||||
if (!isset($this->currentLines[0])) {
|
{
|
||||||
|
if (! isset($this->currentLines[0])) {
|
||||||
return null; // No more lines to read
|
return null; // No more lines to read
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ namespace App\Services\DatasetReader;
|
|||||||
|
|
||||||
use App\Services\CsvReader;
|
use App\Services\CsvReader;
|
||||||
|
|
||||||
class RandomOrderDataSetReader implements IDataSetReader {
|
class RandomOrderDataSetReader implements IDataSetReader
|
||||||
|
{
|
||||||
public array $lines = [];
|
public array $lines = [];
|
||||||
|
|
||||||
private array $currentLines = [];
|
private array $currentLines = [];
|
||||||
|
|
||||||
private int $lastReadLineIndex = -1;
|
private int $lastReadLineIndex = -1;
|
||||||
@@ -36,7 +38,7 @@ class RandomOrderDataSetReader implements IDataSetReader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getNextLine(): array | null
|
public function getNextLine(): ?array
|
||||||
{
|
{
|
||||||
if (empty($this->currentLines)) {
|
if (empty($this->currentLines)) {
|
||||||
return null; // No more lines to read
|
return null; // No more lines to read
|
||||||
|
|||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
namespace App\Services\IterationEventBuffer;
|
namespace App\Services\IterationEventBuffer;
|
||||||
|
|
||||||
interface IPerceptronIterationEventBuffer {
|
interface IPerceptronIterationEventBuffer
|
||||||
|
{
|
||||||
|
public function flush(): void;
|
||||||
|
|
||||||
public function flush(): void ;
|
public function addIteration(int $iteration, int $exampleIndex, float $error, array $synaptic_weights): void;
|
||||||
|
|
||||||
public function addIteration(int $iteration, int $exampleIndex, float $error, array $synaptic_weights): void ;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,12 @@
|
|||||||
|
|
||||||
namespace App\Services\IterationEventBuffer;
|
namespace App\Services\IterationEventBuffer;
|
||||||
|
|
||||||
class PerceptronIterationEventBuffer implements IPerceptronIterationEventBuffer {
|
class PerceptronIterationEventBuffer implements IPerceptronIterationEventBuffer
|
||||||
|
{
|
||||||
private $data;
|
private $data;
|
||||||
|
|
||||||
private int $nextSizeIncreaseThreshold;
|
private int $nextSizeIncreaseThreshold;
|
||||||
|
|
||||||
private int $underSizeIncreaseCount = 0;
|
private int $underSizeIncreaseCount = 0;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
@@ -17,24 +20,25 @@ class PerceptronIterationEventBuffer implements IPerceptronIterationEventBuffer
|
|||||||
$this->nextSizeIncreaseThreshold = $sizeIncreaseStart;
|
$this->nextSizeIncreaseThreshold = $sizeIncreaseStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function flush(): void {
|
public function flush(): void
|
||||||
|
{
|
||||||
event(new \App\Events\PerceptronTrainingIteration($this->data, $this->sessionId, $this->trainingId));
|
event(new \App\Events\PerceptronTrainingIteration($this->data, $this->sessionId, $this->trainingId));
|
||||||
$this->data = [];
|
$this->data = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addIteration(int $epoch, int $exampleIndex, float $error, array $synaptic_weights): void {
|
public function addIteration(int $epoch, int $exampleIndex, float $error, array $synaptic_weights): void
|
||||||
|
{
|
||||||
$this->data[] = [
|
$this->data[] = [
|
||||||
"epoch" => $epoch,
|
'epoch' => $epoch,
|
||||||
"exampleIndex" => $exampleIndex,
|
'exampleIndex' => $exampleIndex,
|
||||||
"error" => $error,
|
'error' => $error,
|
||||||
"weights" => $synaptic_weights,
|
'weights' => $synaptic_weights,
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($this->underSizeIncreaseCount <= $this->sizeIncreaseStart) { // We can still send a single date because we are under the increase start threshold
|
if ($this->underSizeIncreaseCount <= $this->sizeIncreaseStart) { // We can still send a single date because we are under the increase start threshold
|
||||||
$this->underSizeIncreaseCount++;
|
$this->underSizeIncreaseCount++;
|
||||||
$this->flush();
|
$this->flush();
|
||||||
}
|
} elseif (count($this->data) >= $this->nextSizeIncreaseThreshold) {
|
||||||
else if (count($this->data) >= $this->nextSizeIncreaseThreshold) {
|
|
||||||
$this->flush();
|
$this->flush();
|
||||||
$this->nextSizeIncreaseThreshold *= $this->sizeIncreaseFactor;
|
$this->nextSizeIncreaseThreshold *= $this->sizeIncreaseFactor;
|
||||||
|
|
||||||
|
|||||||
@@ -2,8 +2,10 @@
|
|||||||
|
|
||||||
namespace App\Services\IterationEventBuffer;
|
namespace App\Services\IterationEventBuffer;
|
||||||
|
|
||||||
class PerceptronLimitedEpochEventBuffer implements IPerceptronIterationEventBuffer {
|
class PerceptronLimitedEpochEventBuffer implements IPerceptronIterationEventBuffer
|
||||||
|
{
|
||||||
private array $data;
|
private array $data;
|
||||||
|
|
||||||
private int $underSizeIncreaseCount = 0;
|
private int $underSizeIncreaseCount = 0;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
@@ -15,23 +17,26 @@ class PerceptronLimitedEpochEventBuffer implements IPerceptronIterationEventBuff
|
|||||||
$this->data = [];
|
$this->data = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function flush(): void {
|
public function flush(): void
|
||||||
|
{
|
||||||
event(new \App\Events\PerceptronTrainingIteration($this->data, $this->sessionId, $this->trainingId));
|
event(new \App\Events\PerceptronTrainingIteration($this->data, $this->sessionId, $this->trainingId));
|
||||||
$this->data = [];
|
$this->data = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addIteration(int $epoch, int $exampleIndex, float $error, array $synaptic_weights): void {
|
public function addIteration(int $epoch, int $exampleIndex, float $error, array $synaptic_weights): void
|
||||||
|
{
|
||||||
$newData = [
|
$newData = [
|
||||||
"epoch" => $epoch,
|
'epoch' => $epoch,
|
||||||
"exampleIndex" => $exampleIndex,
|
'exampleIndex' => $exampleIndex,
|
||||||
"error" => $error,
|
'error' => $error,
|
||||||
"weights" => $synaptic_weights,
|
'weights' => $synaptic_weights,
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($this->underSizeIncreaseCount <= $this->sizeIncreaseStart) { // Special case where we need to send each iteration separately
|
if ($this->underSizeIncreaseCount <= $this->sizeIncreaseStart) { // Special case where we need to send each iteration separately
|
||||||
$this->underSizeIncreaseCount++;
|
$this->underSizeIncreaseCount++;
|
||||||
$this->data[] = $newData;
|
$this->data[] = $newData;
|
||||||
$this->flush();
|
$this->flush();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,8 +44,7 @@ class PerceptronLimitedEpochEventBuffer implements IPerceptronIterationEventBuff
|
|||||||
if ($this->data && $lastEpoch !== $epoch) { // Current Epoch has changed from the last one
|
if ($this->data && $lastEpoch !== $epoch) { // Current Epoch has changed from the last one
|
||||||
if ($lastEpoch % $this->epochInterval === 0) { // The last epoch need to be sent
|
if ($lastEpoch % $this->epochInterval === 0) { // The last epoch need to be sent
|
||||||
$this->flush(); // Flush all data from the previous epoch
|
$this->flush(); // Flush all data from the previous epoch
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
$this->data = [];
|
$this->data = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Services\SynapticWeightsProvider;
|
namespace App\Services\SynapticWeightsProvider;
|
||||||
|
|
||||||
interface ISynapticWeightsProvider {
|
interface ISynapticWeightsProvider
|
||||||
|
{
|
||||||
public function generate(int $input_size): array;
|
public function generate(int $input_size): array;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,15 @@
|
|||||||
|
|
||||||
namespace App\Services\SynapticWeightsProvider;
|
namespace App\Services\SynapticWeightsProvider;
|
||||||
|
|
||||||
class RandomSynapticWeights implements ISynapticWeightsProvider {
|
class RandomSynapticWeights implements ISynapticWeightsProvider
|
||||||
|
{
|
||||||
public function generate(int $input_size): array
|
public function generate(int $input_size): array
|
||||||
{
|
{
|
||||||
$weights = [];
|
$weights = [];
|
||||||
for ($i = 0; $i < $input_size + 1; $i++) { // +1 for bias weight
|
for ($i = 0; $i < $input_size + 1; $i++) { // +1 for bias weight
|
||||||
$weights[] = rand(-100, 100) / 100; // Random weights between -1 and 1
|
$weights[] = rand(-100, 100) / 100; // Random weights between -1 and 1
|
||||||
}
|
}
|
||||||
|
|
||||||
return $weights;
|
return $weights;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,13 +2,15 @@
|
|||||||
|
|
||||||
namespace App\Services\SynapticWeightsProvider;
|
namespace App\Services\SynapticWeightsProvider;
|
||||||
|
|
||||||
class ZeroSynapticWeights implements ISynapticWeightsProvider {
|
class ZeroSynapticWeights implements ISynapticWeightsProvider
|
||||||
|
{
|
||||||
public function generate(int $input_size): array
|
public function generate(int $input_size): array
|
||||||
{
|
{
|
||||||
$weights = [];
|
$weights = [];
|
||||||
for ($i = 0; $i < $input_size + 1; $i++) { // +1 for bias weight
|
for ($i = 0; $i < $input_size + 1; $i++) { // +1 for bias weight
|
||||||
$weights[] = 0; // Zero weights
|
$weights[] = 0; // Zero weights
|
||||||
}
|
}
|
||||||
|
|
||||||
return $weights;
|
return $weights;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,13 +7,13 @@ return [
|
|||||||
* Beyond this number of iterations, the broadcast will be splitted every x iterations,
|
* Beyond this number of iterations, the broadcast will be splitted every x iterations,
|
||||||
* x is limited_broadcast_number
|
* x is limited_broadcast_number
|
||||||
*/
|
*/
|
||||||
'limited_broadcast_iterations' => 200,
|
'limited_broadcast_iterations' => 200,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* How much broadcasts is sent when in limmited broadcast mode
|
* How much broadcasts is sent when in limmited broadcast mode
|
||||||
*/
|
*/
|
||||||
'limited_broadcast_number' => 200,
|
'limited_broadcast_number' => 200,
|
||||||
|
|
||||||
'broadcast_iteration_size' => 75,
|
'broadcast_iteration_size' => 75,
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ const props = defineProps<{
|
|||||||
iterations: Iteration[];
|
iterations: Iteration[];
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const page = usePage();
|
|
||||||
|
|
||||||
const epochErrorOnly = ref<boolean>(false);
|
const epochErrorOnly = ref<boolean>(false);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Http\Controllers\PerceptronController;
|
use App\Http\Controllers\PerceptronController;
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Support\Facades\Route;
|
use Illuminate\Support\Facades\Route;
|
||||||
|
|
||||||
Route::post('perceptron/run', [PerceptronController::class, 'run'])->name('perceptron.run');
|
Route::post('perceptron/run', [PerceptronController::class, 'run'])->name('perceptron.run');
|
||||||
|
|||||||
@@ -2,6 +2,6 @@
|
|||||||
|
|
||||||
use Illuminate\Support\Facades\Broadcast;
|
use Illuminate\Support\Facades\Broadcast;
|
||||||
|
|
||||||
Broadcast::channel(session()->getId() . '-perceptron-training', function ($user) {
|
Broadcast::channel(session()->getId().'-perceptron-training', function ($user) {
|
||||||
return $user;
|
return $user;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -4,19 +4,13 @@ namespace Tests\Services\IterationEventBuffer;
|
|||||||
|
|
||||||
use App\Services\IterationEventBuffer\IPerceptronIterationEventBuffer;
|
use App\Services\IterationEventBuffer\IPerceptronIterationEventBuffer;
|
||||||
|
|
||||||
class DullIterationEventBuffer implements IPerceptronIterationEventBuffer {
|
class DullIterationEventBuffer implements IPerceptronIterationEventBuffer
|
||||||
|
{
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
|
||||||
) {
|
) {}
|
||||||
|
|
||||||
}
|
public function flush(): void {}
|
||||||
|
|
||||||
public function flush(): void {
|
public function addIteration(int $epoch, int $exampleIndex, float $error, array $synaptic_weights): void {}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function addIteration(int $epoch, int $exampleIndex, float $error, array $synaptic_weights): void {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,15 +9,14 @@ use Tests\Services\IterationEventBuffer\DullIterationEventBuffer;
|
|||||||
|
|
||||||
class ADALINEPerceptronTest extends TrainingTestCase
|
class ADALINEPerceptronTest extends TrainingTestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
public function test_adaline_perceptron_training_logic_and()
|
public function test_adaline_perceptron_training_logic_and()
|
||||||
{
|
{
|
||||||
$training = new ADALINEPerceptronTraining(
|
$training = new ADALINEPerceptronTraining(
|
||||||
datasetReader: new LinearOrderDataSetReader(public_path('data_sets/logic_and_gradient.csv')),
|
datasetReader: new LinearOrderDataSetReader(public_path('data_sets/logic_and_gradient.csv')),
|
||||||
learningRate: 0.03,
|
learningRate: 0.03,
|
||||||
maxEpochs: 10000,
|
maxEpochs: 10000,
|
||||||
synapticWeightsProvider: new ZeroSynapticWeights(),
|
synapticWeightsProvider: new ZeroSynapticWeights,
|
||||||
iterationEventBuffer: new DullIterationEventBuffer(),
|
iterationEventBuffer: new DullIterationEventBuffer,
|
||||||
sessionId: 'test-session',
|
sessionId: 'test-session',
|
||||||
trainingId: 'test-training',
|
trainingId: 'test-training',
|
||||||
minError: 0.1251,
|
minError: 0.1251,
|
||||||
@@ -57,8 +56,8 @@ class ADALINEPerceptronTest extends TrainingTestCase
|
|||||||
datasetReader: new LinearOrderDataSetReader(public_path('data_sets/table_2_10.csv')),
|
datasetReader: new LinearOrderDataSetReader(public_path('data_sets/table_2_10.csv')),
|
||||||
learningRate: 0.0015,
|
learningRate: 0.0015,
|
||||||
maxEpochs: 1000,
|
maxEpochs: 1000,
|
||||||
synapticWeightsProvider: new ZeroSynapticWeights(),
|
synapticWeightsProvider: new ZeroSynapticWeights,
|
||||||
iterationEventBuffer: new DullIterationEventBuffer(),
|
iterationEventBuffer: new DullIterationEventBuffer,
|
||||||
sessionId: 'test-session',
|
sessionId: 'test-session',
|
||||||
trainingId: 'test-training',
|
trainingId: 'test-training',
|
||||||
// minError: 16.597077, // Impossible pour un dataset avec des labels -1 et 1 d'avoir une erreur moyenne supérieure à 2
|
// minError: 16.597077, // Impossible pour un dataset avec des labels -1 et 1 d'avoir une erreur moyenne supérieure à 2
|
||||||
|
|||||||
@@ -9,15 +9,14 @@ use Tests\Services\IterationEventBuffer\DullIterationEventBuffer;
|
|||||||
|
|
||||||
class GradientDescentPerceptronTest extends TrainingTestCase
|
class GradientDescentPerceptronTest extends TrainingTestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
public function test_gradient_descent_perceptron_training_logic_and()
|
public function test_gradient_descent_perceptron_training_logic_and()
|
||||||
{
|
{
|
||||||
$training = new GradientDescentPerceptronTraining(
|
$training = new GradientDescentPerceptronTraining(
|
||||||
datasetReader: new LinearOrderDataSetReader(public_path('data_sets/logic_and_gradient.csv')),
|
datasetReader: new LinearOrderDataSetReader(public_path('data_sets/logic_and_gradient.csv')),
|
||||||
learningRate: 0.2,
|
learningRate: 0.2,
|
||||||
maxEpochs: 100,
|
maxEpochs: 100,
|
||||||
synapticWeightsProvider: new ZeroSynapticWeights(),
|
synapticWeightsProvider: new ZeroSynapticWeights,
|
||||||
iterationEventBuffer: new DullIterationEventBuffer(),
|
iterationEventBuffer: new DullIterationEventBuffer,
|
||||||
sessionId: 'test-session',
|
sessionId: 'test-session',
|
||||||
trainingId: 'test-training',
|
trainingId: 'test-training',
|
||||||
minError: 0.125001,
|
minError: 0.125001,
|
||||||
@@ -56,8 +55,8 @@ class GradientDescentPerceptronTest extends TrainingTestCase
|
|||||||
datasetReader: new LinearOrderDataSetReader(public_path('data_sets/table_2_10.csv')),
|
datasetReader: new LinearOrderDataSetReader(public_path('data_sets/table_2_10.csv')),
|
||||||
learningRate: 0.0015,
|
learningRate: 0.0015,
|
||||||
maxEpochs: 1000,
|
maxEpochs: 1000,
|
||||||
synapticWeightsProvider: new ZeroSynapticWeights(),
|
synapticWeightsProvider: new ZeroSynapticWeights,
|
||||||
iterationEventBuffer: new DullIterationEventBuffer(),
|
iterationEventBuffer: new DullIterationEventBuffer,
|
||||||
sessionId: 'test-session',
|
sessionId: 'test-session',
|
||||||
trainingId: 'test-training',
|
trainingId: 'test-training',
|
||||||
// minError: 16.388103, // Impossible pour un dataset avec des labels -1 et 1 d'avoir une erreur moyenne supérieure à 2
|
// minError: 16.388103, // Impossible pour un dataset avec des labels -1 et 1 d'avoir une erreur moyenne supérieure à 2
|
||||||
|
|||||||
@@ -9,15 +9,14 @@ use Tests\Services\IterationEventBuffer\DullIterationEventBuffer;
|
|||||||
|
|
||||||
class SimplePerceptronTest extends TrainingTestCase
|
class SimplePerceptronTest extends TrainingTestCase
|
||||||
{
|
{
|
||||||
|
|
||||||
public function test_simple_perceptron_training_logic_and()
|
public function test_simple_perceptron_training_logic_and()
|
||||||
{
|
{
|
||||||
$training = new SimpleBinaryPerceptronTraining(
|
$training = new SimpleBinaryPerceptronTraining(
|
||||||
datasetReader: new LinearOrderDataSetReader(public_path('data_sets/logic_and.csv')),
|
datasetReader: new LinearOrderDataSetReader(public_path('data_sets/logic_and.csv')),
|
||||||
learningRate: 1.0,
|
learningRate: 1.0,
|
||||||
maxEpochs: 100,
|
maxEpochs: 100,
|
||||||
synapticWeightsProvider: new ZeroSynapticWeights(),
|
synapticWeightsProvider: new ZeroSynapticWeights,
|
||||||
iterationEventBuffer: new DullIterationEventBuffer(),
|
iterationEventBuffer: new DullIterationEventBuffer,
|
||||||
sessionId: 'test-session',
|
sessionId: 'test-session',
|
||||||
trainingId: 'test-training',
|
trainingId: 'test-training',
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ class TrainingTestCase extends TestCase
|
|||||||
{
|
{
|
||||||
$training->start();
|
$training->start();
|
||||||
|
|
||||||
|
|
||||||
// Assert that the final synaptic weights are as expected withing the margin of error
|
// Assert that the final synaptic weights are as expected withing the margin of error
|
||||||
// $finalWeights = $training->getSynapticWeights();
|
// $finalWeights = $training->getSynapticWeights();
|
||||||
// $this->assertEqualsWithDelta($expectedWeights, $finalWeights, $marginOfError, "Final synaptic weights do not match expected values.");
|
// $this->assertEqualsWithDelta($expectedWeights, $finalWeights, $marginOfError, "Final synaptic weights do not match expected values.");
|
||||||
@@ -21,5 +20,4 @@ class TrainingTestCase extends TestCase
|
|||||||
// Assert that the number of epochs taken is as expected
|
// Assert that the number of epochs taken is as expected
|
||||||
$this->assertEquals($expectedEpochs, $training->getEpoch(), "Expected training to take $expectedEpochs epochs, but it took {$training->getEpoch()} epochs.");
|
$this->assertEquals($expectedEpochs, $training->getEpoch(), "Expected training to take $expectedEpochs epochs, but it took {$training->getEpoch()} epochs.");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user