Refactor and optimizations
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import type { ChartData } from 'chart.js';
|
||||
import type { ChartDataset } from 'chart.js';
|
||||
import { computed, ref } from 'vue';
|
||||
import { Bar } from 'vue-chartjs';
|
||||
import { Chart } from 'vue-chartjs';
|
||||
import { colors, gridColor, gridColorBold } from '@/types/graphs';
|
||||
import type { Iteration } from '@/types/perceptron';
|
||||
import Toggle from './ui/toggle/Toggle.vue';
|
||||
@@ -11,16 +11,16 @@ const props = defineProps<{
|
||||
isRegression: boolean;
|
||||
}>();
|
||||
|
||||
type ErrorValue = number | [number, number] | null;
|
||||
type ErrorDataset = ChartDataset<'bar' | 'line', ErrorValue[]>;
|
||||
|
||||
const epochErrorOnly = ref<boolean>(false);
|
||||
|
||||
/**
|
||||
* Datasets of the iterations with the form { label: `Exemple ${exampleIndex}`, data: [error for iteration 1, error for iteration 2, ...] }
|
||||
*/
|
||||
const datasets = computed<
|
||||
ChartData<'bar', (number | [number, number] | null)[]>[]
|
||||
>(() => {
|
||||
const datasets: ChartData<'bar', (number | [number, number] | null)[]>[] =
|
||||
[];
|
||||
const datasets = computed<ErrorDataset[]>(() => {
|
||||
const datasets: ErrorDataset[] = [];
|
||||
const epochAverageError: number[] = [];
|
||||
|
||||
const backgroundColors = colors;
|
||||
@@ -60,14 +60,14 @@ const datasets = computed<
|
||||
|
||||
// Sort dataset by label (Exemple 0, Exemple 1, ...)
|
||||
datasets.sort((a, b) => {
|
||||
const aIndex = parseInt(a.label.split(' ')[1]);
|
||||
const bIndex = parseInt(b.label.split(' ')[1]);
|
||||
const aIndex = parseInt((a.label ?? '').split(' ')[1]);
|
||||
const bIndex = parseInt((b.label ?? '').split(' ')[1]);
|
||||
return aIndex - bIndex;
|
||||
});
|
||||
|
||||
// Epoch error
|
||||
const epochErrorDataset = {
|
||||
type: 'line',
|
||||
const epochErrorDataset: ErrorDataset = {
|
||||
type: 'line' as const,
|
||||
label: "Erreur quadratique moyenne de l'époque",
|
||||
data: [],
|
||||
backgroundColor: '#fff',
|
||||
@@ -88,7 +88,8 @@ const datasets = computed<
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Bar
|
||||
<Chart
|
||||
type="bar"
|
||||
class="bg-primary dark:bg-transparent!"
|
||||
:options="{
|
||||
responsive: true,
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
import { useEcho } from '@laravel/echo-vue';
|
||||
import { onBeforeUnmount, ref, shallowRef } from 'vue';
|
||||
import type { Iteration } from '@/types/perceptron';
|
||||
|
||||
type TrainingEvent = {
|
||||
trainingId: string;
|
||||
};
|
||||
|
||||
type IterationEvent = TrainingEvent & {
|
||||
iterations: Iteration[];
|
||||
};
|
||||
|
||||
type InitializationEvent = TrainingEvent & {
|
||||
activationFunction: string;
|
||||
};
|
||||
|
||||
type TrainingEndedEvent = TrainingEvent & {
|
||||
reason: string;
|
||||
};
|
||||
|
||||
export function usePerceptronTraining(sessionId: string) {
|
||||
const trainingId = ref('');
|
||||
const iterations = shallowRef<Iteration[]>([]);
|
||||
const trainingEnded = ref(false);
|
||||
const trainingEndReason = ref('');
|
||||
const activationFunction = ref('');
|
||||
|
||||
let pendingIterations: Iteration[] = [];
|
||||
let renderFrame: number | null = null;
|
||||
|
||||
function isCurrentTraining(event: TrainingEvent): boolean {
|
||||
return event.trainingId === trainingId.value;
|
||||
}
|
||||
|
||||
function flushPendingIterations(): void {
|
||||
renderFrame = null;
|
||||
if (pendingIterations.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
iterations.value = [...iterations.value, ...pendingIterations];
|
||||
pendingIterations = [];
|
||||
}
|
||||
|
||||
function handleIterations(event: IterationEvent): void {
|
||||
if (!isCurrentTraining(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
pendingIterations.push(...event.iterations);
|
||||
if (renderFrame === null) {
|
||||
renderFrame = requestAnimationFrame(flushPendingIterations);
|
||||
}
|
||||
}
|
||||
|
||||
function handleInitialization(event: InitializationEvent): void {
|
||||
if (isCurrentTraining(event)) {
|
||||
activationFunction.value = event.activationFunction;
|
||||
}
|
||||
}
|
||||
|
||||
function handleTrainingEnded(event: TrainingEndedEvent): void {
|
||||
if (!isCurrentTraining(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
flushPendingIterations();
|
||||
trainingEnded.value = true;
|
||||
trainingEndReason.value = event.reason;
|
||||
}
|
||||
|
||||
function reset(): void {
|
||||
if (renderFrame !== null) {
|
||||
cancelAnimationFrame(renderFrame);
|
||||
renderFrame = null;
|
||||
}
|
||||
|
||||
pendingIterations = [];
|
||||
iterations.value = [];
|
||||
trainingEnded.value = false;
|
||||
trainingEndReason.value = '';
|
||||
activationFunction.value = '';
|
||||
}
|
||||
|
||||
function setTrainingId(newTrainingId: string): void {
|
||||
reset();
|
||||
trainingId.value = newTrainingId;
|
||||
}
|
||||
|
||||
const channel = `${sessionId}-perceptron-training`;
|
||||
useEcho(channel, 'PerceptronTrainingIteration', handleIterations, [{}], 'public');
|
||||
useEcho(channel, 'PerceptronTrainingEnded', handleTrainingEnded, [{}], 'public');
|
||||
useEcho(channel, 'PerceptronInitialization', handleInitialization, [{}], 'public');
|
||||
|
||||
onBeforeUnmount(reset);
|
||||
|
||||
return {
|
||||
activationFunction,
|
||||
iterations,
|
||||
setTrainingId,
|
||||
trainingEnded,
|
||||
trainingEndReason,
|
||||
};
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { Head } from '@inertiajs/vue3';
|
||||
import { useEcho } from '@laravel/echo-vue';
|
||||
import {
|
||||
Chart as ChartJS,
|
||||
Title,
|
||||
@@ -14,13 +13,8 @@ import {
|
||||
} from 'chart.js';
|
||||
import { computed, ref } from 'vue';
|
||||
import LinkHeader from '@/components/LinkHeader.vue';
|
||||
import type {
|
||||
Dataset,
|
||||
DatasetPoint,
|
||||
InitializationMethod,
|
||||
Iteration,
|
||||
PerceptronType,
|
||||
} from '@/types/perceptron';
|
||||
import { usePerceptronTraining } from '@/composables/usePerceptronTraining';
|
||||
import type { Dataset, DatasetPoint, InitializationMethod, PerceptronType } from '@/types/perceptron';
|
||||
import IterationTable from '../components/IterationTable.vue';
|
||||
import PerceptronDecisionGraph from '../components/PerceptronDecisionGraph.vue';
|
||||
import PerceptronIterationsErrorsGraph from '../components/PerceptronIterationsErrorsGraph.vue';
|
||||
@@ -86,74 +80,16 @@ const cleanedDataset = computed<
|
||||
const hiddenLayers = ref(3);
|
||||
const hiddenLayersNeurons = ref(3);
|
||||
const initializationMethod = ref<InitializationMethod>(props.type === 'multilayer' ? 'random' : 'zeros');
|
||||
|
||||
console.log('Session ID:', props.sessionId);
|
||||
|
||||
useEcho(
|
||||
`${props.sessionId}-perceptron-training`,
|
||||
'PerceptronTrainingIteration',
|
||||
percpetronIteration,
|
||||
[{}],
|
||||
'public',
|
||||
);
|
||||
useEcho(
|
||||
`${props.sessionId}-perceptron-training`,
|
||||
'PerceptronTrainingEnded',
|
||||
perceptronTrainingEnded,
|
||||
[{}],
|
||||
'public',
|
||||
);
|
||||
useEcho(
|
||||
`${props.sessionId}-perceptron-training`,
|
||||
'PerceptronInitialization',
|
||||
perceptroninitialization,
|
||||
[{}],
|
||||
'public',
|
||||
);
|
||||
|
||||
const iterations = ref<Iteration[]>([]);
|
||||
|
||||
const trainingId = ref<string>('');
|
||||
function percpetronIteration(data: any) {
|
||||
console.log('Received perceptron iteration data:', data);
|
||||
if (data.trainingId !== trainingId.value) {
|
||||
console.warn(
|
||||
`Received iteration for training ID ${data.trainingId}, but current training ID is ${trainingId.value}. Ignoring this iteration.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
iterations.value.push(...data.iterations);
|
||||
}
|
||||
|
||||
const trainingEnded = ref(false);
|
||||
const trainingEndReason = ref('');
|
||||
function perceptronTrainingEnded(data: any) {
|
||||
console.log('Perceptron training ended:', data);
|
||||
if (data.trainingId !== trainingId.value) {
|
||||
console.warn(
|
||||
`Received training ended event for training ID ${data.trainingId}, but current training ID is ${trainingId.value}. Ignoring this event.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
trainingEnded.value = true;
|
||||
trainingEndReason.value = data.reason;
|
||||
}
|
||||
|
||||
const activationFunction = ref<string>('');
|
||||
const {
|
||||
activationFunction,
|
||||
iterations,
|
||||
setTrainingId,
|
||||
trainingEnded,
|
||||
trainingEndReason,
|
||||
} = usePerceptronTraining(props.sessionId);
|
||||
const isRegression = computed(
|
||||
() => props.type === 'multilayer' && activationFunction.value === 'linear',
|
||||
() => (props.type === 'multilayer' || props.type === 'monolayer') && activationFunction.value === 'linear',
|
||||
);
|
||||
|
||||
function perceptroninitialization(data: any) {
|
||||
console.log('Perceptron training initialized:', data);
|
||||
if (data.trainingId !== trainingId.value) {
|
||||
console.warn(
|
||||
`Received initialization event for training ID ${data.trainingId}, but current training ID is ${trainingId.value}. Ignoring this event.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
activationFunction.value = data.activationFunction;
|
||||
}
|
||||
function getActivationFunction(type: string): (x: number) => number {
|
||||
switch (type) {
|
||||
case 'step':
|
||||
@@ -169,12 +105,6 @@ function getActivationFunction(type: string): (x: number) => number {
|
||||
}
|
||||
}
|
||||
|
||||
function resetTraining() {
|
||||
iterations.value = [];
|
||||
trainingEnded.value = false;
|
||||
trainingEndReason.value = '';
|
||||
activationFunction.value = '';
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -197,11 +127,7 @@ function resetTraining() {
|
||||
selectedDatasetName = newValue;
|
||||
}
|
||||
"
|
||||
@update:training-id="
|
||||
(newValue) => {
|
||||
trainingId = newValue;
|
||||
resetTraining();
|
||||
}"
|
||||
@update:training-id="setTrainingId"
|
||||
/>
|
||||
<div
|
||||
class="align-items-start justify-content-center flex h-full min-h-dvh max-w-dvw"
|
||||
|
||||
Reference in New Issue
Block a user