From 8a50f492ac61c93f0ac64a7b3e6c6bd5af560755 Mon Sep 17 00:00:00 2001 From: Matthias Guillitte Date: Tue, 8 Sep 2026 18:40:56 +0200 Subject: [PATCH] Various fixess and xor --- app/Events/PerceptronTrainingIteration.php | 9 +++- app/Http/Controllers/PerceptronController.php | 21 +++++++++ config/perceptron.php | 5 ++- resources/js/components/PerceptronSetup.vue | 9 ++++ resources/js/pages/PerceptronViewer.vue | 8 +++- resources/js/types/perceptron.ts | 2 + .../PerceptronTrainingIterationTest.php | 44 +++++++++++++++++++ 7 files changed, 93 insertions(+), 5 deletions(-) create mode 100644 tests/Unit/Events/PerceptronTrainingIterationTest.php diff --git a/app/Events/PerceptronTrainingIteration.php b/app/Events/PerceptronTrainingIteration.php index b00f4e0..d9e6605 100644 --- a/app/Events/PerceptronTrainingIteration.php +++ b/app/Events/PerceptronTrainingIteration.php @@ -6,6 +6,7 @@ use Illuminate\Broadcasting\Channel; use Illuminate\Broadcasting\InteractsWithSockets; use Illuminate\Contracts\Broadcasting\ShouldBroadcast; use Illuminate\Foundation\Events\Dispatchable; +use Illuminate\Support\Arr; use Illuminate\Queue\SerializesModels; class PerceptronTrainingIteration implements ShouldBroadcast @@ -38,9 +39,15 @@ class PerceptronTrainingIteration implements ShouldBroadcast public function broadcastWith(): array { + $weights = collect($this->iterations) + ->pluck('weights') + ->first(fn (array $weights): bool => $weights !== []); + $shouldBroadcastAllWeights = $weights !== null + && count(Arr::flatten($weights)) <= config('perceptron.max_displayed_weights'); + $lastIterationIndex = count($this->iterations) - 1; $iterations = array_map( - fn (array $iteration, int $index): array => $index === $lastIterationIndex + fn (array $iteration, int $index): array => $shouldBroadcastAllWeights || $index === $lastIterationIndex ? $iteration : [...$iteration, 'weights' => []], $this->iterations, diff --git a/app/Http/Controllers/PerceptronController.php b/app/Http/Controllers/PerceptronController.php index b6ab342..10e6ef4 100644 --- a/app/Http/Controllers/PerceptronController.php +++ b/app/Http/Controllers/PerceptronController.php @@ -47,6 +47,7 @@ class PerceptronController extends Controller break; case 'multilayer': $learningRate = 0.8; + $maxIterations = 2000; break; } @@ -109,6 +110,17 @@ class PerceptronController extends Controller break; } break; + case 'logic_xor': + switch ($perceptronType) { + case 'multilayer': + $dataset['defaultLearningRate'] = 0.3; + $dataset['defaultMinError'] = 0.001; + $dataset['defaultMaxIterations'] = 500; + $dataset['defaultHiddenLayers'] = 1; + $dataset['defaultHiddenLayersNeurons'] = 2; + break; + } + break; case 'table_2_9': switch ($perceptronType) { case 'simple': @@ -132,6 +144,15 @@ class PerceptronController extends Controller break; } break; + case 'table_4_17': + switch ($perceptronType) { + case 'multilayer': + $dataset['defaultLearningRate'] = 0.5; + $dataset['defaultMinError'] = 0.08; + $dataset['defaultMaxIterations'] = 400; + break; + } + break; } $datasets[] = $dataset; } diff --git a/config/perceptron.php b/config/perceptron.php index 90976bf..13812fb 100644 --- a/config/perceptron.php +++ b/config/perceptron.php @@ -20,9 +20,10 @@ return [ 'broadcast_iteration_size' => 75, /** - * Hide the weight columns in the iteration table above this count. + * Maximum number of weights for which all iteration weights are broadcast + * and displayed in the iteration table. */ - 'max_displayed_weights' => 25, + 'max_displayed_weights' => 5, 'run_inputs_validation' => [ 'hidden_layers' => 'required|integer|min:1|max:5', diff --git a/resources/js/components/PerceptronSetup.vue b/resources/js/components/PerceptronSetup.vue index 732a59a..99789bb 100644 --- a/resources/js/components/PerceptronSetup.vue +++ b/resources/js/components/PerceptronSetup.vue @@ -66,6 +66,15 @@ watch(selectedDatasetCopy, (newvalue) => { if (selectedDatasetCopy && selectedDatasetCopy.defaultMaxIterations !== undefined) { maxIterations.value = selectedDatasetCopy.defaultMaxIterations; } + // HiddenLayers + hiddenLayers.value = props.hiddenLayers; + if (selectedDatasetCopy && selectedDatasetCopy.defaultHiddenLayers !== undefined) { + hiddenLayers.value = selectedDatasetCopy.defaultHiddenLayers; + } + hiddenLayersNeurons.value = props.hiddenLayersNeurons; + if (selectedDatasetCopy && selectedDatasetCopy.defaultHiddenLayersNeurons !== undefined) { + hiddenLayersNeurons.value = selectedDatasetCopy.defaultHiddenLayersNeurons; + } }) const trainingId = ref(''); diff --git a/resources/js/pages/PerceptronViewer.vue b/resources/js/pages/PerceptronViewer.vue index e221e2b..4f58c2a 100644 --- a/resources/js/pages/PerceptronViewer.vue +++ b/resources/js/pages/PerceptronViewer.vue @@ -140,6 +140,10 @@ function perceptronTrainingEnded(data: any) { } const activationFunction = ref(''); +const isRegression = computed( + () => props.type === 'multilayer' && activationFunction.value === 'linear', +); + function perceptroninitialization(data: any) { console.log('Perceptron training initialized:', data); if (data.trainingId !== trainingId.value) { @@ -216,7 +220,7 @@ function resetTraining() { diff --git a/resources/js/types/perceptron.ts b/resources/js/types/perceptron.ts index 7e1c10d..c6e0b0c 100644 --- a/resources/js/types/perceptron.ts +++ b/resources/js/types/perceptron.ts @@ -11,6 +11,8 @@ export type Dataset = { defaultLearningRate?: number; defaultMinError?: number; defaultMaxIterations?: number; + defaultHiddenLayers?: number; + defaultHiddenLayersNeurons?: number; }; export type DatasetPoint = { diff --git a/tests/Unit/Events/PerceptronTrainingIterationTest.php b/tests/Unit/Events/PerceptronTrainingIterationTest.php new file mode 100644 index 0000000..f347f39 --- /dev/null +++ b/tests/Unit/Events/PerceptronTrainingIterationTest.php @@ -0,0 +1,44 @@ + 1, 'exampleIndex' => 0, 'error' => 1, 'weights' => [[[1, 2]]]], + ['epoch' => 1, 'exampleIndex' => 1, 'error' => 0, 'weights' => [[[3, 4]]]], + ], + sessionId: 'session', + trainingId: 'training', + ); + + $iterations = $event->broadcastWith()['iterations']; + + $this->assertSame([[[1, 2]]], $iterations[0]['weights']); + $this->assertSame([[[3, 4]]], $iterations[1]['weights']); + } + + public function test_large_networks_only_keep_the_last_iteration_weights(): void + { + $largeWeights = [[array_fill(0, config('perceptron.max_displayed_weights') + 1, 0)]]; + $event = new PerceptronTrainingIteration( + iterations: [ + ['epoch' => 1, 'exampleIndex' => 0, 'error' => 1, 'weights' => $largeWeights], + ['epoch' => 1, 'exampleIndex' => 1, 'error' => 0, 'weights' => $largeWeights], + ], + sessionId: 'session', + trainingId: 'training', + ); + + $iterations = $event->broadcastWith()['iterations']; + + $this->assertSame([], $iterations[0]['weights']); + $this->assertSame($largeWeights, $iterations[1]['weights']); + } +}