Added configuration panel datasets, back-end refactor and others
This commit is contained in:
@@ -3,8 +3,11 @@
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\SimplePerceptron;
|
||||
use App\Models\SimplePerceptronTraining;
|
||||
use App\Services\DataSetReader;
|
||||
use App\Services\ISynapticWeights;
|
||||
use App\Services\ISynapticWeightsProvider;
|
||||
use App\Services\PerceptronIterationEventBuffer;
|
||||
use App\Services\ZeroSynapticWeights;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
@@ -16,111 +19,86 @@ class PerceptronController extends Controller
|
||||
public function index(Request $request)
|
||||
{
|
||||
$perceptronType = $request->query('type', 'simple');
|
||||
$dataSet = $request->input('data_set', 'logic_and');
|
||||
$dataSetReader = $this->getDataSetReader($dataSet);
|
||||
|
||||
$learningRate = 0.1;
|
||||
$maxIterations = 100;
|
||||
|
||||
switch ($perceptronType) {
|
||||
case 'simple':
|
||||
$learningRate = 0.015;
|
||||
$maxIterations = 100;
|
||||
break;
|
||||
}
|
||||
|
||||
return inertia('PerceptronViewer', [
|
||||
'type' => $perceptronType,
|
||||
'sessionId' => session()->getId(),
|
||||
'dataset' => $dataSetReader->lines,
|
||||
'csrf_token' => csrf_token(),
|
||||
'datasets' => $this->getDatasets(),
|
||||
'minError' => 0.01,
|
||||
'learningRate' => $learningRate,
|
||||
'maxIterations' => $maxIterations,
|
||||
]);
|
||||
}
|
||||
|
||||
private function getDatasets(): array
|
||||
{
|
||||
$dataSetsDirectory = public_path('data_sets');
|
||||
$files = scandir($dataSetsDirectory);
|
||||
$datasets = [];
|
||||
foreach ($files as $file) {
|
||||
if (pathinfo($file, PATHINFO_EXTENSION) === 'csv') {
|
||||
$dataset = [];
|
||||
$dataset['label'] = str_replace('.csv', '', $file);
|
||||
$dataSetReader = new DataSetReader($dataSetsDirectory . '/' . $file);
|
||||
$dataset['data'] = $dataSetReader->lines;
|
||||
|
||||
switch ($dataset['label']) {
|
||||
case '2.9':
|
||||
$dataset['defaultLearningRate'] = 0.015;
|
||||
break;
|
||||
}
|
||||
$datasets[] = $dataset;
|
||||
}
|
||||
}
|
||||
return $datasets;
|
||||
}
|
||||
|
||||
private function getDataSetReader(string $dataSet): DataSetReader
|
||||
{
|
||||
$dataSetFileName = "data_sets/{$dataSet}.csv";
|
||||
return new DataSetReader($dataSetFileName);
|
||||
}
|
||||
|
||||
public function run(Request $request, ISynapticWeights $synapticWeightsProvider)
|
||||
public function run(Request $request, ISynapticWeightsProvider $synapticWeightsProvider)
|
||||
{
|
||||
$perceptronType = $request->query('type', 'simple');
|
||||
$minError = $request->query('min_error', 0.01);
|
||||
$dataSet = $request->input('data_set', 'logic_and');
|
||||
$learningRate = $request->input('learning_rate', 0.1);
|
||||
$perceptronType = $request->input('type', 'simple');
|
||||
$minError = $request->input('min_error', 0.01);
|
||||
$weightInitMethod = $request->input('weight_init_method', 'random');
|
||||
$dataSet = $request->input('dataset');
|
||||
$learningRate = $request->input('learning_rate', 0.015);
|
||||
$maxIterations = $request->input('max_iterations', 100);
|
||||
$sessionId = $request->input('session_id', session()->getId());
|
||||
$trainingId = $request->input('training_id');
|
||||
|
||||
if ($weightInitMethod === 'zeros') {
|
||||
$synapticWeightsProvider = new ZeroSynapticWeights();
|
||||
}
|
||||
|
||||
$dataSetReader = $this->getDataSetReader($dataSet);
|
||||
|
||||
$MAX_ITERATIONS = 100;
|
||||
$stopCondition;
|
||||
$trainFunction;
|
||||
$trainFunctionState = [];
|
||||
$iterationEventBuffer = new PerceptronIterationEventBuffer($sessionId, $trainingId);
|
||||
|
||||
switch ($perceptronType) {
|
||||
case 'simple':
|
||||
$stopCondition = function($iteration, $iterationErrorCounter) use ($sessionId) {
|
||||
$condition = $iterationErrorCounter == 0;
|
||||
if ($condition === true) {
|
||||
Log::info("Perceptron training ended after {$iteration} iterations with no errors.");
|
||||
event(new \App\Events\PerceptronTrainingEnded('Le perceptron ne commet plus d\'erreurs sur aucune des données', $sessionId));
|
||||
}
|
||||
return $iterationErrorCounter == 0;
|
||||
};
|
||||
$iterationFunction = function(&$state) use ($synapticWeightsProvider, $learningRate, $minError) {
|
||||
if (!isset($state['perceptron'])) {
|
||||
$state['perceptron'] = new SimplePerceptron($synapticWeightsProvider->generate(2));
|
||||
}
|
||||
$networkTraining = match ($perceptronType) {
|
||||
'simple' => new SimplePerceptronTraining($dataSetReader, $learningRate, $maxIterations, $synapticWeightsProvider, $iterationEventBuffer, $sessionId, $trainingId),
|
||||
default => null,
|
||||
};
|
||||
|
||||
$perceptron = $state['perceptron'];
|
||||
$inputs = $state['inputs'];
|
||||
$correctOutput = $state['correctOutput'];
|
||||
$iterationErrorCounter = $state['iterationErrorCounter'] ?? 0;
|
||||
event(new \App\Events\PerceptronInitialization($dataSetReader->lines, $networkTraining->activationFunction, $sessionId, $trainingId));
|
||||
|
||||
$output = $perceptron->test($inputs);
|
||||
|
||||
$error = $correctOutput - $output;
|
||||
if (abs($error) > $minError) {
|
||||
$iterationErrorCounter++;
|
||||
}
|
||||
|
||||
if ($error !== 0) { // Update synaptic weights if needed
|
||||
$synaptic_weights = $perceptron->getSynapticWeights();
|
||||
$inputs_with_bias = array_merge([1], $inputs); // Add bias input
|
||||
$new_weights = array_map(fn($weight, $input) => $weight + $learningRate * $error * $input, $synaptic_weights, $inputs_with_bias);
|
||||
$perceptron->setSynapticWeights($new_weights);
|
||||
}
|
||||
|
||||
return [$error, $perceptron->getSynapticWeights(), $iterationErrorCounter, $state];
|
||||
};
|
||||
break;
|
||||
default:
|
||||
return response()->json(['error' => 'Invalid perceptron type'], 400);
|
||||
}
|
||||
|
||||
$iteration = 0;
|
||||
$error = 1.0; // Initial error
|
||||
do {
|
||||
$iterationErrorCounter = 0;
|
||||
$iteration++;
|
||||
|
||||
while ($nextRow = $dataSetReader->getRandomLine()) {
|
||||
$inputs = array_slice($nextRow, 0, -1);
|
||||
$correctOutput = end($nextRow);
|
||||
$trainFunctionState['inputs'] = $inputs;
|
||||
$trainFunctionState['correctOutput'] = $correctOutput;
|
||||
$trainFunctionState['iterationErrorCounter'] = $iterationErrorCounter;
|
||||
|
||||
[$error, $synaptic_weights, $iterationErrorCounter, $trainFunctionState] = $iterationFunction($trainFunctionState);
|
||||
|
||||
$error = abs($error); // Use absolute error
|
||||
|
||||
// Broadcast the training iteration event
|
||||
event(new \App\Events\PerceptronTrainingIteration($iteration, $dataSetReader->getLastReadLineIndex(), $error, $synaptic_weights, $sessionId));
|
||||
}
|
||||
$dataSetReader->reset(); // Reset the dataset for the next iteration
|
||||
} while ($iteration < $MAX_ITERATIONS && !$stopCondition($iteration, $iterationErrorCounter));
|
||||
|
||||
if ($iteration >= $MAX_ITERATIONS) {
|
||||
event(new \App\Events\PerceptronTrainingEnded('Le nombre maximal d\'itérations a été atteint', $sessionId));
|
||||
}
|
||||
$networkTraining->start();
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Training completed',
|
||||
'iterations' => $iteration,
|
||||
'final_error' => $error,
|
||||
'final_synaptic_weights' => isset($trainFunctionState['perceptron']) ? $trainFunctionState['perceptron']->getSynapticWeights() : [0],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ class HandleAppearance
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
View::share('appearance', $request->cookie('appearance') ?? 'system');
|
||||
View::share('appearance', 'dark');
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user