Some bugfixes and misc
This commit is contained in:
@@ -5,11 +5,11 @@ namespace App\Events;
|
|||||||
use App\Models\ActivationsFunctions;
|
use App\Models\ActivationsFunctions;
|
||||||
use Illuminate\Broadcasting\Channel;
|
use Illuminate\Broadcasting\Channel;
|
||||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
|
||||||
use Illuminate\Foundation\Events\Dispatchable;
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
class PerceptronInitialization implements ShouldBroadcast
|
class PerceptronInitialization implements ShouldBroadcastNow
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ namespace App\Events;
|
|||||||
|
|
||||||
use Illuminate\Broadcasting\Channel;
|
use Illuminate\Broadcasting\Channel;
|
||||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
|
||||||
use Illuminate\Foundation\Events\Dispatchable;
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
|
||||||
class PerceptronTrainingEnded implements ShouldBroadcast
|
class PerceptronTrainingEnded implements ShouldBroadcastNow
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ namespace App\Events;
|
|||||||
|
|
||||||
use Illuminate\Broadcasting\Channel;
|
use Illuminate\Broadcasting\Channel;
|
||||||
use Illuminate\Broadcasting\InteractsWithSockets;
|
use Illuminate\Broadcasting\InteractsWithSockets;
|
||||||
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
|
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
|
||||||
use Illuminate\Foundation\Events\Dispatchable;
|
use Illuminate\Foundation\Events\Dispatchable;
|
||||||
use Illuminate\Support\Arr;
|
|
||||||
use Illuminate\Queue\SerializesModels;
|
use Illuminate\Queue\SerializesModels;
|
||||||
|
use Illuminate\Support\Arr;
|
||||||
|
|
||||||
class PerceptronTrainingIteration implements ShouldBroadcast
|
class PerceptronTrainingIteration implements ShouldBroadcastNow
|
||||||
{
|
{
|
||||||
use Dispatchable, InteractsWithSockets, SerializesModels;
|
use Dispatchable, InteractsWithSockets, SerializesModels;
|
||||||
|
|
||||||
@@ -39,19 +39,20 @@ class PerceptronTrainingIteration implements ShouldBroadcast
|
|||||||
|
|
||||||
public function broadcastWith(): array
|
public function broadcastWith(): array
|
||||||
{
|
{
|
||||||
$weights = collect($this->iterations)
|
$iterations = self::normalizeForJson($this->iterations);
|
||||||
|
$weights = collect($iterations)
|
||||||
->pluck('weights')
|
->pluck('weights')
|
||||||
->first(fn (array $weights): bool => $weights !== []);
|
->first(fn (array $weights): bool => $weights !== []);
|
||||||
$shouldBroadcastAllWeights = $weights !== null
|
$shouldBroadcastAllWeights = $weights !== null
|
||||||
&& count(Arr::flatten($weights)) <= config('perceptron.max_displayed_weights');
|
&& count(Arr::flatten($weights)) <= config('perceptron.max_displayed_weights');
|
||||||
|
|
||||||
$lastIterationIndex = count($this->iterations) - 1;
|
$lastIterationIndex = count($iterations) - 1;
|
||||||
$iterations = array_map(
|
$iterations = array_map(
|
||||||
fn (array $iteration, int $index): array => $shouldBroadcastAllWeights || $index === $lastIterationIndex
|
fn (array $iteration, int $index): array => $shouldBroadcastAllWeights || $index === $lastIterationIndex
|
||||||
? $iteration
|
? $iteration
|
||||||
: [...$iteration, 'weights' => []],
|
: [...$iteration, 'weights' => []],
|
||||||
$this->iterations,
|
$iterations,
|
||||||
array_keys($this->iterations),
|
array_keys($iterations),
|
||||||
);
|
);
|
||||||
|
|
||||||
return [
|
return [
|
||||||
@@ -59,4 +60,20 @@ class PerceptronTrainingIteration implements ShouldBroadcast
|
|||||||
'trainingId' => $this->trainingId,
|
'trainingId' => $this->trainingId,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function normalizeForJson(mixed $value): mixed
|
||||||
|
{
|
||||||
|
if (is_float($value) && ! is_finite($value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! is_array($value)) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_map(
|
||||||
|
fn (mixed $item): mixed => self::normalizeForJson($item),
|
||||||
|
$value,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ use App\Services\SynapticWeightsProvider\ISynapticWeightsProvider;
|
|||||||
use App\Services\SynapticWeightsProvider\RandomSynapticWeights;
|
use App\Services\SynapticWeightsProvider\RandomSynapticWeights;
|
||||||
use App\Services\SynapticWeightsProvider\ZeroSynapticWeights;
|
use App\Services\SynapticWeightsProvider\ZeroSynapticWeights;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\DB;
|
|
||||||
|
|
||||||
class PerceptronController extends Controller
|
class PerceptronController extends Controller
|
||||||
{
|
{
|
||||||
@@ -134,6 +133,11 @@ class PerceptronController extends Controller
|
|||||||
break;
|
break;
|
||||||
case 'table_2_11':
|
case 'table_2_11':
|
||||||
$dataset['defaultMinError'] = 0.02;
|
$dataset['defaultMinError'] = 0.02;
|
||||||
|
switch ($perceptronType) {
|
||||||
|
case 'monolayer':
|
||||||
|
$dataset['defaultLearningRate'] = 0.0015;
|
||||||
|
break;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'table_4_12':
|
case 'table_4_12':
|
||||||
switch ($perceptronType) {
|
switch ($perceptronType) {
|
||||||
@@ -145,11 +149,13 @@ class PerceptronController extends Controller
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'table_4_17':
|
case 'table_4_17':
|
||||||
|
$dataset['defaultMinError'] = 0.055;
|
||||||
switch ($perceptronType) {
|
switch ($perceptronType) {
|
||||||
case 'multilayer':
|
case 'multilayer':
|
||||||
$dataset['defaultLearningRate'] = 0.5;
|
$dataset['defaultLearningRate'] = 0.3;
|
||||||
$dataset['defaultMinError'] = 0.08;
|
|
||||||
$dataset['defaultMaxIterations'] = 400;
|
$dataset['defaultMaxIterations'] = 400;
|
||||||
|
$dataset['defaultHiddenLayers'] = 2;
|
||||||
|
$dataset['defaultHiddenLayersNeurons'] = 2;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -183,9 +189,6 @@ class PerceptronController extends Controller
|
|||||||
$sessionId = $request->input('session_id', session()->getId());
|
$sessionId = $request->input('session_id', session()->getId());
|
||||||
$trainingId = $request->input('training_id');
|
$trainingId = $request->input('training_id');
|
||||||
|
|
||||||
// Remove the jobs for the sessionId
|
|
||||||
DB::table('jobs')->where('payload', 'like', '%s:9:\"sessionId\";s:40:\"'.$sessionId.'\";%')->delete();
|
|
||||||
|
|
||||||
// Zero initialization prevents hidden layers from receiving a gradient.
|
// Zero initialization prevents hidden layers from receiving a gradient.
|
||||||
if ($perceptronType === 'multilayer' && $weightInitMethod === 'zeros') {
|
if ($perceptronType === 'multilayer' && $weightInitMethod === 'zeros') {
|
||||||
$synapticWeightsProvider = new RandomSynapticWeights;
|
$synapticWeightsProvider = new RandomSynapticWeights;
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ class MonoLayerPerceptronTraining extends NetworkTraining
|
|||||||
private array $labels;
|
private array $labels;
|
||||||
|
|
||||||
public ActivationsFunctions $activationFunction = ActivationsFunctions::LINEAR;
|
public ActivationsFunctions $activationFunction = ActivationsFunctions::LINEAR;
|
||||||
|
|
||||||
public ?ActivationsFunctions $presentationLayerActivationFunction = ActivationsFunctions::STEP;
|
public ?ActivationsFunctions $presentationLayerActivationFunction = ActivationsFunctions::STEP;
|
||||||
|
|
||||||
private float $epochError;
|
private float $epochError;
|
||||||
@@ -62,7 +63,7 @@ class MonoLayerPerceptronTraining extends NetworkTraining
|
|||||||
while ($nextRow = $this->datasetReader->getNextLine()) {
|
while ($nextRow = $this->datasetReader->getNextLine()) {
|
||||||
$inputsForCurrentEpoch[] = $nextRow;
|
$inputsForCurrentEpoch[] = $nextRow;
|
||||||
$inputs = array_slice($nextRow, 0, -1);
|
$inputs = array_slice($nextRow, 0, -1);
|
||||||
$correctOutput = (int) end($nextRow);
|
$correctOutput = (float) end($nextRow);
|
||||||
|
|
||||||
$iterationError = $this->iterationFunction($inputs, $correctOutput);
|
$iterationError = $this->iterationFunction($inputs, $correctOutput);
|
||||||
|
|
||||||
@@ -108,7 +109,7 @@ class MonoLayerPerceptronTraining extends NetworkTraining
|
|||||||
return $condition;
|
return $condition;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function iterationFunction(array $inputs, int $correctOutput): array
|
private function iterationFunction(array $inputs, float $correctOutput): array
|
||||||
{
|
{
|
||||||
$outputs = $this->network->test($inputs);
|
$outputs = $this->network->test($inputs);
|
||||||
$desiredOutput = $this->getDesiredOutputFromCorrectOutput($correctOutput);
|
$desiredOutput = $this->getDesiredOutputFromCorrectOutput($correctOutput);
|
||||||
@@ -137,7 +138,7 @@ class MonoLayerPerceptronTraining extends NetworkTraining
|
|||||||
return [$updatedWeights];
|
return [$updatedWeights];
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getDesiredOutputFromCorrectOutput(int $correctOutput): array
|
private function getDesiredOutputFromCorrectOutput(float $correctOutput): array
|
||||||
{
|
{
|
||||||
$desiredOutput = array_fill(0, count($this->labels), -1);
|
$desiredOutput = array_fill(0, count($this->labels), -1);
|
||||||
$labelIndex = Arr::first(array_keys($this->labels), fn ($key) => $this->labels[$key] == $correctOutput);
|
$labelIndex = Arr::first(array_keys($this->labels), fn ($key) => $this->labels[$key] == $correctOutput);
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ namespace App\Models\Perceptrons;
|
|||||||
|
|
||||||
class InputNeuron extends Perceptron
|
class InputNeuron extends Perceptron
|
||||||
{
|
{
|
||||||
|
private float $input = 0.0;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
) {
|
) {
|
||||||
parent::__construct([]);
|
parent::__construct([]);
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ class PerceptronIterationEventBuffer implements IPerceptronIterationEventBuffer
|
|||||||
{
|
{
|
||||||
private array $data = [];
|
private array $data = [];
|
||||||
|
|
||||||
|
private ?float $lastBroadcastAt = null;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private string $sessionId,
|
private string $sessionId,
|
||||||
private string $trainingId,
|
private string $trainingId,
|
||||||
@@ -19,7 +21,9 @@ class PerceptronIterationEventBuffer implements IPerceptronIterationEventBuffer
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->waitForBroadcastInterval();
|
||||||
event(new PerceptronTrainingIteration($this->data, $this->sessionId, $this->trainingId));
|
event(new PerceptronTrainingIteration($this->data, $this->sessionId, $this->trainingId));
|
||||||
|
$this->lastBroadcastAt = microtime(true);
|
||||||
$this->data = [];
|
$this->data = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,8 +52,22 @@ class PerceptronIterationEventBuffer implements IPerceptronIterationEventBuffer
|
|||||||
private function payloadExceedsLimit(): bool
|
private function payloadExceedsLimit(): bool
|
||||||
{
|
{
|
||||||
return strlen(json_encode([
|
return strlen(json_encode([
|
||||||
'iterations' => $this->data,
|
'iterations' => PerceptronTrainingIteration::normalizeForJson($this->data),
|
||||||
'trainingId' => $this->trainingId,
|
'trainingId' => $this->trainingId,
|
||||||
], JSON_THROW_ON_ERROR)) > config('broadcasting.broadcast_max_payload_size');
|
], JSON_THROW_ON_ERROR)) > config('broadcasting.broadcast_max_payload_size');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function waitForBroadcastInterval(): void
|
||||||
|
{
|
||||||
|
if ($this->lastBroadcastAt === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$minimumInterval = config('perceptron.broadcast_minimum_interval_ms') / 1000;
|
||||||
|
$remainingInterval = $minimumInterval - (microtime(true) - $this->lastBroadcastAt);
|
||||||
|
|
||||||
|
if ($remainingInterval > 0) {
|
||||||
|
usleep((int) ceil($remainingInterval * 1_000_000));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ class PerceptronLimitedEpochEventBuffer implements IPerceptronIterationEventBuff
|
|||||||
|
|
||||||
private bool $shouldBroadcastEpoch = false;
|
private bool $shouldBroadcastEpoch = false;
|
||||||
|
|
||||||
|
private ?float $lastBroadcastAt = null;
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private string $sessionId,
|
private string $sessionId,
|
||||||
private string $trainingId,
|
private string $trainingId,
|
||||||
@@ -24,7 +26,9 @@ class PerceptronLimitedEpochEventBuffer implements IPerceptronIterationEventBuff
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->waitForBroadcastInterval();
|
||||||
event(new PerceptronTrainingIteration($this->data, $this->sessionId, $this->trainingId));
|
event(new PerceptronTrainingIteration($this->data, $this->sessionId, $this->trainingId));
|
||||||
|
$this->lastBroadcastAt = microtime(true);
|
||||||
$this->data = [];
|
$this->data = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,8 +61,22 @@ class PerceptronLimitedEpochEventBuffer implements IPerceptronIterationEventBuff
|
|||||||
private function payloadExceedsLimit(): bool
|
private function payloadExceedsLimit(): bool
|
||||||
{
|
{
|
||||||
return strlen(json_encode([
|
return strlen(json_encode([
|
||||||
'iterations' => $this->data,
|
'iterations' => PerceptronTrainingIteration::normalizeForJson($this->data),
|
||||||
'trainingId' => $this->trainingId,
|
'trainingId' => $this->trainingId,
|
||||||
], JSON_THROW_ON_ERROR)) > config('broadcasting.broadcast_max_payload_size');
|
], JSON_THROW_ON_ERROR)) > config('broadcasting.broadcast_max_payload_size');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function waitForBroadcastInterval(): void
|
||||||
|
{
|
||||||
|
if ($this->lastBroadcastAt === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$minimumInterval = config('perceptron.broadcast_minimum_interval_ms') / 1000;
|
||||||
|
$remainingInterval = $minimumInterval - (microtime(true) - $this->lastBroadcastAt);
|
||||||
|
|
||||||
|
if ($remainingInterval > 0) {
|
||||||
|
usleep((int) ceil($remainingInterval * 1_000_000));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ return [
|
|||||||
'limited_broadcast_iterations' => 100,
|
'limited_broadcast_iterations' => 100,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* How much broadcasts is sent when in limmited broadcast mode
|
* How much broadcasts is sent when in limited broadcast mode
|
||||||
*/
|
*/
|
||||||
'limited_broadcast_number' => 100,
|
'limited_broadcast_number' => 100,
|
||||||
|
|
||||||
@@ -19,6 +19,11 @@ return [
|
|||||||
*/
|
*/
|
||||||
'broadcast_iteration_size' => 75,
|
'broadcast_iteration_size' => 75,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimum time between training progress broadcasts, in milliseconds.
|
||||||
|
*/
|
||||||
|
'broadcast_minimum_interval_ms' => 150,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum number of weights for which all iteration weights are broadcast
|
* Maximum number of weights for which all iteration weights are broadcast
|
||||||
* and displayed in the iteration table.
|
* and displayed in the iteration table.
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ComputedRef } from 'vue';
|
import { computed } from 'vue';
|
||||||
|
import type { ComputedRef } from 'vue';
|
||||||
import type { Iteration } from '@/types/perceptron';
|
import type { Iteration } from '@/types/perceptron';
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
@@ -13,7 +14,9 @@ const props = defineProps<{
|
|||||||
const allWeightPerIteration: ComputedRef<number[][]> = computed(() => {
|
const allWeightPerIteration: ComputedRef<number[][]> = computed(() => {
|
||||||
return props.iterations.map((iteration) => {
|
return props.iterations.map((iteration) => {
|
||||||
// We flatten the weights
|
// We flatten the weights
|
||||||
return iteration.weights.flat(2);
|
return iteration.weights
|
||||||
|
.flat(2)
|
||||||
|
.filter((weight): weight is number => weight !== null && Number.isFinite(weight));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -61,10 +64,10 @@ const rowBgDark = computed(() => {
|
|||||||
v-for="(weight, weightIndex) in allWeightPerIteration[index]"
|
v-for="(weight, weightIndex) in allWeightPerIteration[index]"
|
||||||
v-bind:key="weightIndex"
|
v-bind:key="weightIndex"
|
||||||
>
|
>
|
||||||
{{ weight.toFixed(2) }}
|
{{ Number.isFinite(weight) ? weight.toFixed(2) : 'N/A' }}
|
||||||
</td>
|
</td>
|
||||||
</template>
|
</template>
|
||||||
<td>{{ iteration.error.toFixed(2) }}</td>
|
<td>{{ iteration.error === null ? 'N/A' : iteration.error.toFixed(2) }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr
|
<tr
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ const datasets = computed<ErrorDataset[]>(() => {
|
|||||||
const exampleCountPerEpoch: Record<number, number> = {};
|
const exampleCountPerEpoch: Record<number, number> = {};
|
||||||
|
|
||||||
props.iterations.forEach((iteration) => {
|
props.iterations.forEach((iteration) => {
|
||||||
|
const error = iteration.error ?? 0;
|
||||||
|
|
||||||
if (!epochErrorOnly.value) {
|
if (!epochErrorOnly.value) {
|
||||||
const exampleLabel = `Exemple ${iteration.exampleIndex}`;
|
const exampleLabel = `Exemple ${iteration.exampleIndex}`;
|
||||||
let dataset = datasets.find((d) => d.label === exampleLabel);
|
let dataset = datasets.find((d) => d.label === exampleLabel);
|
||||||
@@ -45,8 +47,8 @@ const datasets = computed<ErrorDataset[]>(() => {
|
|||||||
}
|
}
|
||||||
dataset.data.push(
|
dataset.data.push(
|
||||||
props.isRegression
|
props.isRegression
|
||||||
? Math.abs(iteration.error)
|
? Math.abs(error)
|
||||||
: iteration.error,
|
: error,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +57,7 @@ const datasets = computed<ErrorDataset[]>(() => {
|
|||||||
// Epoch error
|
// Epoch error
|
||||||
epochAverageError[iteration.epoch] =
|
epochAverageError[iteration.epoch] =
|
||||||
(epochAverageError[iteration.epoch] || 0) +
|
(epochAverageError[iteration.epoch] || 0) +
|
||||||
iteration.error ** 2 / 2;
|
error ** 2 / 2;
|
||||||
});
|
});
|
||||||
|
|
||||||
// Sort dataset by label (Exemple 0, Exemple 1, ...)
|
// Sort dataset by label (Exemple 0, Exemple 1, ...)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
export type Iteration = {
|
export type Iteration = {
|
||||||
epoch: number;
|
epoch: number;
|
||||||
exampleIndex: number;
|
exampleIndex: number;
|
||||||
weights: number[][][];
|
weights: (number | null)[][][];
|
||||||
error: number;
|
error: number | null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type Dataset = {
|
export type Dataset = {
|
||||||
|
|||||||
@@ -38,4 +38,22 @@ class IterationEventBufferTest extends TestCase
|
|||||||
&& $event->iterations[0]['epoch'] === 1;
|
&& $event->iterations[0]['epoch'] === 1;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function test_non_finite_values_are_normalized_before_broadcasting(): void
|
||||||
|
{
|
||||||
|
$event = new PerceptronTrainingIteration([
|
||||||
|
[
|
||||||
|
'epoch' => 1,
|
||||||
|
'exampleIndex' => 0,
|
||||||
|
'error' => NAN,
|
||||||
|
'weights' => [[[INF]]],
|
||||||
|
],
|
||||||
|
], 'session', 'training');
|
||||||
|
|
||||||
|
$payload = $event->broadcastWith();
|
||||||
|
|
||||||
|
$this->assertNull($payload['iterations'][0]['error']);
|
||||||
|
$this->assertNull($payload['iterations'][0]['weights'][0][0][0]);
|
||||||
|
$this->assertJson(json_encode($payload, JSON_THROW_ON_ERROR));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user