Skip to content

Commit e83aa1e

Browse files
committed
Live Train: train/pause buttons with different speeds and immediate feedback.
1 parent 3a72f94 commit e83aa1e

File tree

4 files changed

+93
-34
lines changed

4 files changed

+93
-34
lines changed

web/assets/css/main.css

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.icons-close i + i {
2+
margin-left: -15px;
3+
}

web/content/control/data.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,10 +61,11 @@
6161
</div>
6262
<div class="col">
6363
<div class="px-2">
64+
<p>Data is always one-hot encoded if categorical columns are found.</p>
6465
<div class="form-check">
6566
<input class="form-check-input" type="checkbox" id="dataEncodeDataCheck"
6667
checked onchange="handleDataOptionsChange()">
67-
<label class="form-check-label" for="dataEncodeDataCheck">Encode Data (If Necessary)</label>
68+
<label class="form-check-label" for="dataEncodeDataCheck">Show Encoded Data</label>
6869
</div>
6970
</div>
7071
<script>

web/content/control/train.html

Lines changed: 87 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<script>
2+
let trainIntervalId = null;
23
let trainingErrors = [];
34
let testingErrors = [];
45
let svg, xScale, yScale, xAxisGroup, yAxisGroup;
@@ -69,29 +70,71 @@
6970
// Update the axes to reflect the reset domains
7071
xAxisGroup.call(d3.axisBottom(xScale));
7172
yAxisGroup.call(d3.axisLeft(yScale));
73+
74+
// Reset any ongoing training
75+
if (trainIntervalId) {
76+
clearInterval(trainIntervalId);
77+
trainIntervalId = null;
78+
}
7279
}
7380
</script>
7481
<div class="row justify-content-center">
7582
<div class="col">
76-
<h5 class="py-4">Epochs:</h5>
77-
<div class="btn-group" role="group" aria-label="Basic radio toggle button group">
78-
<input type="radio" class="btn-check" name="epochsRadio" id="epochsRadio1"
79-
autocomplete="off" value="1" checked>
80-
<label class="btn btn-light fs-5" for="epochsRadio1">1</label>
81-
<input type="radio" class="btn-check" name="epochsRadio" id="epochsRadio5"
82-
autocomplete="off" value="5">
83-
<label class="btn btn-light fs-5" for="epochsRadio5">5</label>
84-
<input type="radio" class="btn-check" name="epochsRadio" id="epochsRadio10"
85-
autocomplete="off" value="10">
86-
<label class="btn btn-light fs-5" for="epochsRadio10">10</label>
87-
<input type="radio" class="btn-check" name="epochsRadio" id="epochsRadio50"
88-
autocomplete="off" value="50">
89-
<label class="btn btn-light fs-5" for="epochsRadio50">50</label>
90-
<input type="radio" class="btn-check" name="epochsRadio" id="epochsRadio100"
91-
autocomplete="off" value="100">
92-
<label class="btn btn-light fs-5" for="epochsRadio100">100</label>
93-
<button type="button" title="start" onclick="handleStartTrain()"
94-
class="btn btn-lg btn-success bi bi-play-fill fs-5"></button>
83+
<div>
84+
<h5 class="py-4">Fixed Epochs:</h5>
85+
<div class="btn-group" role="group" aria-label="Basic radio toggle button group">
86+
<input type="radio" class="btn-check" name="epochsRadio" id="epochsRadio1"
87+
autocomplete="off" value="1" checked>
88+
<label class="btn btn-light fs-5" for="epochsRadio1">1</label>
89+
<input type="radio" class="btn-check" name="epochsRadio" id="epochsRadio5"
90+
autocomplete="off" value="5">
91+
<label class="btn btn-light fs-5" for="epochsRadio5">5</label>
92+
<input type="radio" class="btn-check" name="epochsRadio" id="epochsRadio10"
93+
autocomplete="off" value="10">
94+
<label class="btn btn-light fs-5" for="epochsRadio10">10</label>
95+
<input type="radio" class="btn-check" name="epochsRadio" id="epochsRadio50"
96+
autocomplete="off" value="50">
97+
<label class="btn btn-light fs-5" for="epochsRadio50">50</label>
98+
<input type="radio" class="btn-check" name="epochsRadio" id="epochsRadio100"
99+
autocomplete="off" value="100">
100+
<label class="btn btn-light fs-5" for="epochsRadio100">100</label>
101+
<button type="button" title="start" onclick="trainAndUpdateAfter(getSelectedEpochValue())"
102+
class="btn btn-lg btn-success bi bi-play-fill fs-5"></button>
103+
</div>
104+
</div>
105+
<script>
106+
function getSelectedEpochValue() {
107+
const radioButtons = document.querySelectorAll('input[name="epochsRadio"]');
108+
for (const radioButton of radioButtons) {
109+
if (radioButton.checked) {
110+
return Number(radioButton.value);
111+
}
112+
}
113+
}
114+
</script>
115+
<div>
116+
<h5 class="py-4">Train/Pause:</h5>
117+
<div class="btn-group" role="group" aria-label="Train Speeds">
118+
<button type="button" class="btn btn-lg btn-success fs-5 icons-close"
119+
onclick="handleTrainPause('pause')">
120+
<i class="bi bi-pause"></i>
121+
</button>
122+
<button type="button" class="btn btn-lg btn-success fs-5 icons-close"
123+
onclick="handleTrainPause('slow')">
124+
<i class="bi bi-chevron-right"></i>
125+
</button>
126+
<button type="button" class="btn btn-lg btn-success fs-5 icons-close"
127+
onclick="handleTrainPause('medium')">
128+
<i class="bi bi-chevron-right"></i>
129+
<i class="bi bi-chevron-right"></i>
130+
</button>
131+
<button type="button" class="btn btn-lg btn-success fs-5 icons-close"
132+
onclick="handleTrainPause('fast')">
133+
<i class="bi bi-chevron-right"></i>
134+
<i class="bi bi-chevron-right"></i>
135+
<i class="bi bi-chevron-right"></i>
136+
</button>
137+
</div>
95138
</div>
96139
<div>
97140
<h5 class="py-4">Options:</h5>
@@ -104,7 +147,7 @@ <h5 class="py-4">Options:</h5>
104147
</div>
105148
<hr class="my-4">
106149
<div class="d-flex justify-content-center">
107-
<button type="button" title="start" id="refreshWeightsButton" onclick="handleRandomizeWeights()"
150+
<button type="button" title="start" id="refreshWeightsButton" onclick="network.build()"
108151
class="btn btn-lg btn-danger bi bi-arrow-clockwise fs-5"> Randomize Weights
109152
</button>
110153
</div>
@@ -153,8 +196,20 @@ <h5 class="py-4">Training and Testing Errors:</h5>
153196
</div>
154197
</div>
155198
<script>
156-
function handleStartTrain() {
157-
const epochs = getSelectedEpochValue();
199+
function getSpeedFromType(speedType) {
200+
switch (speedType) {
201+
case 'fast':
202+
return 0;
203+
case 'medium':
204+
return 20;
205+
case 'slow':
206+
return 100;
207+
default:
208+
return 20;
209+
}
210+
}
211+
212+
function trainAndUpdateAfter(epochs) {
158213
if (document.getElementById('useTestDataCheck').checked) {
159214
const res = toArrArr(network.trainAndTestFor(epochs));
160215
for (let pair of res) {
@@ -168,17 +223,16 @@ <h5 class="py-4">Training and Testing Errors:</h5>
168223
updateErrorChart();
169224
}
170225

171-
function handleRandomizeWeights() {
172-
network.build();
173-
clearErrorChart();
174-
}
175-
176-
function getSelectedEpochValue() {
177-
const radioButtons = document.querySelectorAll('input[name="epochsRadio"]');
178-
for (const radioButton of radioButtons) {
179-
if (radioButton.checked) {
180-
return Number(radioButton.value);
181-
}
226+
async function handleTrainPause(speedType) {
227+
if (trainIntervalId) {
228+
clearInterval(trainIntervalId);
229+
trainIntervalId = null;
230+
}
231+
if (speedType !== 'pause') {
232+
const speed = getSpeedFromType(speedType);
233+
trainIntervalId = setInterval(() => {
234+
trainAndUpdateAfter(1);
235+
}, speed);
182236
}
183237
}
184238
</script>

web/content/setup/structure.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434

3535
function onNetworkBuilt() {
3636
drawNetwork(toArr(network.getDimensions()));
37+
clearErrorChart();
3738
}
3839
</script>
3940
<div class="container flex-column gap-4">

0 commit comments

Comments
 (0)