diff --git a/app/Http/Controllers/PerceptronController.php b/app/Http/Controllers/PerceptronController.php index 1552db2..e8d54e3 100644 --- a/app/Http/Controllers/PerceptronController.php +++ b/app/Http/Controllers/PerceptronController.php @@ -8,6 +8,7 @@ use App\Models\SimpleBinaryPerceptronTraining; use App\Services\DataSetReader; use App\Services\ISynapticWeightsProvider; use App\Services\PerceptronIterationEventBuffer; +use App\Services\PerceptronLimitedEpochEventBuffer; use App\Services\ZeroSynapticWeights; use Illuminate\Http\Request; @@ -91,8 +92,15 @@ class PerceptronController extends Controller case 'simple': $dataset['defaultLearningRate'] = 0.015; break; + case 'gradientdescent': + $dataset['defaultLearningRate'] = 0.001; + $dataset['defaultMinError'] = 2.0; + break; } break; + case 'table_2_11': + $dataset['defaultMinError'] = 1.0; + break; } $datasets[] = $dataset; } @@ -121,9 +129,14 @@ class PerceptronController extends Controller $synapticWeightsProvider = new ZeroSynapticWeights(); } + $iterationEventBuffer = new PerceptronIterationEventBuffer($sessionId, $trainingId); + if ($maxIterations > config('perceptron.limited_broadcast_iterations')) { + $iterationsInterval = (int)($maxIterations / config('perceptron.limited_broadcast_iterations')); + $iterationEventBuffer = new PerceptronLimitedEpochEventBuffer($sessionId, $trainingId, $iterationsInterval); + } + $dataSetReader = $this->getDataSetReader($dataSet); - $iterationEventBuffer = new PerceptronIterationEventBuffer($sessionId, $trainingId); $networkTraining = match ($perceptronType) { 'simple' => new SimpleBinaryPerceptronTraining($dataSetReader, $learningRate, $maxIterations, $synapticWeightsProvider, $iterationEventBuffer, $sessionId, $trainingId), diff --git a/app/Services/DataSetReader.php b/app/Services/DataSetReader.php index 4e3dd76..ddd93fd 100644 --- a/app/Services/DataSetReader.php +++ b/app/Services/DataSetReader.php @@ -51,6 +51,16 @@ class DataSetReader { return $randomLine; } + public function getNextLine(): array | null { + if (!isset($this->currentLines[0])) { + return null; // No more lines to read + } + + $this->lastReadLineIndex = array_search($this->currentLines[0], $this->lines, true); + + return array_shift($this->currentLines); + } + public function getInputSize(): int { return count($this->lines[0]) - 1; // Don't count the label diff --git a/app/Services/IPerceptronIterationEventBuffer.php b/app/Services/IPerceptronIterationEventBuffer.php new file mode 100644 index 0000000..ecc1de3 --- /dev/null +++ b/app/Services/IPerceptronIterationEventBuffer.php @@ -0,0 +1,10 @@ +data = []; } - public function addIteration(int $iteration, int $exampleIndex, float $error, array $synaptic_weights): void { + public function addIteration(int $epoch, int $exampleIndex, float $error, array $synaptic_weights): void { $this->data[] = [ - "iteration" => $iteration, + "epoch" => $epoch, "exampleIndex" => $exampleIndex, "error" => $error, "weights" => $synaptic_weights, @@ -42,8 +38,8 @@ class PerceptronIterationEventBuffer { $this->flush(); $this->nextSizeIncreaseThreshold *= $this->sizeIncreaseFactor; - if ($this->nextSizeIncreaseThreshold > $this->MAX_SIZE) { - $this->nextSizeIncreaseThreshold = $this->MAX_SIZE; // Cap the threshold to the maximum size + if ($this->nextSizeIncreaseThreshold > config('perceptron.broadcast_iteration_size')) { + $this->nextSizeIncreaseThreshold = config('perceptron.broadcast_iteration_size'); // Cap the threshold to the maximum size } } } diff --git a/app/Services/PerceptronLimitedEpochEventBuffer.php b/app/Services/PerceptronLimitedEpochEventBuffer.php new file mode 100644 index 0000000..1ce7f63 --- /dev/null +++ b/app/Services/PerceptronLimitedEpochEventBuffer.php @@ -0,0 +1,51 @@ +data = []; + } + + public function flush(): void { + event(new \App\Events\PerceptronTrainingIteration($this->data, $this->sessionId, $this->trainingId)); + $this->data = []; + } + + public function addIteration(int $epoch, int $exampleIndex, float $error, array $synaptic_weights): void { + $newData = [ + "epoch" => $epoch, + "exampleIndex" => $exampleIndex, + "error" => $error, + "weights" => $synaptic_weights, + ]; + + if ($this->underSizeIncreaseCount <= $this->sizeIncreaseStart) { // Special case where we need to send each iteration separately + $this->underSizeIncreaseCount++; + $this->data[] = $newData; + $this->flush(); + return; + } + + $lastEpoch = $this->data[0]['epoch'] ?? null; + 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 + $this->flush(); // Flush all data from the previous epoch + } + else { + $this->data = []; + } + + $lastEpoch = $epoch; + } + $this->data[] = $newData; + } +} diff --git a/config/perceptron.php b/config/perceptron.php new file mode 100644 index 0000000..5fe2fe9 --- /dev/null +++ b/config/perceptron.php @@ -0,0 +1,19 @@ + 200, + + /** + * How much broadcasts is sent when in limmited broadcast mode + */ + 'limited_broadcast_number' => 200, + + 'broadcast_iteration_size' => 75, + +]; diff --git a/public/data_sets/logic_or.csv b/public/data_sets/logic_or.csv new file mode 100644 index 0000000..b6c0268 --- /dev/null +++ b/public/data_sets/logic_or.csv @@ -0,0 +1,4 @@ +0, 0, -1 +0, 1, 1 +1, 0, 1 +1, 1, 1 diff --git a/resources/js/components/IterationTable.vue b/resources/js/components/IterationTable.vue index a99f02c..c330c0a 100644 --- a/resources/js/components/IterationTable.vue +++ b/resources/js/components/IterationTable.vue @@ -15,14 +15,29 @@ const allWeightPerIteration: ComputedRef = computed(() => { return iteration.weights.flat(2); }); }); + +const rowBgDark = computed(() => { + let isEven = false; + return props.iterations.map((iteration, index, arr) => { + if (index > 0 && arr[index - 1].epoch !== iteration.epoch) { + isEven = !isEven; + } + return isEven; + }); +});