219 lines
9.1 KiB
HTML
219 lines
9.1 KiB
HTML
<div class="card training-panel">
|
|
<div class="card-header">
|
|
<h6 class="mb-0">
|
|
<i class="fas fa-graduation-cap"></i>
|
|
Training
|
|
</h6>
|
|
</div>
|
|
<div class="card-body p-2">
|
|
<!-- Model Selection -->
|
|
<div class="mb-3">
|
|
<label for="model-select" class="form-label small">Model</label>
|
|
<select class="form-select form-select-sm" id="model-select">
|
|
<option value="StandardizedCNN">CNN Model</option>
|
|
<option value="DQN">DQN Agent</option>
|
|
<option value="Transformer">Transformer</option>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- Training Controls -->
|
|
<div class="mb-3">
|
|
<button class="btn btn-primary btn-sm w-100" id="train-model-btn">
|
|
<i class="fas fa-play"></i>
|
|
Train Model
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Training Status -->
|
|
<div id="training-status" style="display: none;">
|
|
<div class="alert alert-info py-2 px-2 mb-2">
|
|
<div class="d-flex align-items-center mb-1">
|
|
<div class="spinner-border spinner-border-sm me-2" role="status">
|
|
<span class="visually-hidden">Training...</span>
|
|
</div>
|
|
<strong class="small">Training in progress</strong>
|
|
</div>
|
|
<div class="progress mb-1" style="height: 10px;">
|
|
<div class="progress-bar progress-bar-striped progress-bar-animated"
|
|
id="training-progress-bar"
|
|
role="progressbar"
|
|
style="width: 0%"></div>
|
|
</div>
|
|
<div class="small">
|
|
<div>Epoch: <span id="training-epoch">0</span>/<span id="training-total-epochs">0</span></div>
|
|
<div>Loss: <span id="training-loss">--</span></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Training Results -->
|
|
<div id="training-results" style="display: none;">
|
|
<div class="alert alert-success py-2 px-2 mb-2">
|
|
<strong class="small">
|
|
<i class="fas fa-check-circle"></i>
|
|
Training Complete
|
|
</strong>
|
|
<div class="small mt-1">
|
|
<div>Final Loss: <span id="result-loss">--</span></div>
|
|
<div>Accuracy: <span id="result-accuracy">--</span></div>
|
|
<div>Duration: <span id="result-duration">--</span></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Inference Simulation -->
|
|
<div class="mb-3">
|
|
<button class="btn btn-secondary btn-sm w-100" id="simulate-inference-btn">
|
|
<i class="fas fa-brain"></i>
|
|
Simulate Inference
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Test Case Stats -->
|
|
<div class="small text-muted">
|
|
<div class="d-flex justify-content-between">
|
|
<span>Test Cases:</span>
|
|
<span id="testcase-count">0</span>
|
|
</div>
|
|
<div class="d-flex justify-content-between">
|
|
<span>Last Training:</span>
|
|
<span id="last-training-time">Never</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Train model button
|
|
document.getElementById('train-model-btn').addEventListener('click', function() {
|
|
const modelName = document.getElementById('model-select').value;
|
|
|
|
if (appState.annotations.length === 0) {
|
|
showError('No annotations available for training');
|
|
return;
|
|
}
|
|
|
|
// Get annotation IDs
|
|
const annotationIds = appState.annotations.map(a => a.annotation_id);
|
|
|
|
// Start training
|
|
startTraining(modelName, annotationIds);
|
|
});
|
|
|
|
function startTraining(modelName, annotationIds) {
|
|
// Show training status
|
|
document.getElementById('training-status').style.display = 'block';
|
|
document.getElementById('training-results').style.display = 'none';
|
|
document.getElementById('train-model-btn').disabled = true;
|
|
|
|
// Reset progress
|
|
document.getElementById('training-progress-bar').style.width = '0%';
|
|
document.getElementById('training-epoch').textContent = '0';
|
|
document.getElementById('training-loss').textContent = '--';
|
|
|
|
// Start training request
|
|
fetch('/api/train-model', {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({
|
|
model_name: modelName,
|
|
annotation_ids: annotationIds
|
|
})
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
// Start polling for training progress
|
|
pollTrainingProgress(data.training_id);
|
|
} else {
|
|
showError('Failed to start training: ' + data.error.message);
|
|
document.getElementById('training-status').style.display = 'none';
|
|
document.getElementById('train-model-btn').disabled = false;
|
|
}
|
|
})
|
|
.catch(error => {
|
|
showError('Network error: ' + error.message);
|
|
document.getElementById('training-status').style.display = 'none';
|
|
document.getElementById('train-model-btn').disabled = false;
|
|
});
|
|
}
|
|
|
|
function pollTrainingProgress(trainingId) {
|
|
const pollInterval = setInterval(function() {
|
|
fetch('/api/training-progress', {
|
|
method: 'POST',
|
|
headers: {'Content-Type': 'application/json'},
|
|
body: JSON.stringify({training_id: trainingId})
|
|
})
|
|
.then(response => response.json())
|
|
.then(data => {
|
|
if (data.success) {
|
|
const progress = data.progress;
|
|
|
|
// Update progress bar
|
|
const percentage = (progress.current_epoch / progress.total_epochs) * 100;
|
|
document.getElementById('training-progress-bar').style.width = percentage + '%';
|
|
document.getElementById('training-epoch').textContent = progress.current_epoch;
|
|
document.getElementById('training-total-epochs').textContent = progress.total_epochs;
|
|
document.getElementById('training-loss').textContent = progress.current_loss.toFixed(4);
|
|
|
|
// Check if complete
|
|
if (progress.status === 'completed') {
|
|
clearInterval(pollInterval);
|
|
showTrainingResults(progress);
|
|
} else if (progress.status === 'failed') {
|
|
clearInterval(pollInterval);
|
|
showError('Training failed: ' + progress.error);
|
|
document.getElementById('training-status').style.display = 'none';
|
|
document.getElementById('train-model-btn').disabled = false;
|
|
}
|
|
}
|
|
})
|
|
.catch(error => {
|
|
clearInterval(pollInterval);
|
|
showError('Failed to get training progress: ' + error.message);
|
|
document.getElementById('training-status').style.display = 'none';
|
|
document.getElementById('train-model-btn').disabled = false;
|
|
});
|
|
}, 1000); // Poll every second
|
|
}
|
|
|
|
function showTrainingResults(results) {
|
|
// Hide training status
|
|
document.getElementById('training-status').style.display = 'none';
|
|
|
|
// Show results
|
|
document.getElementById('training-results').style.display = 'block';
|
|
document.getElementById('result-loss').textContent = results.final_loss.toFixed(4);
|
|
document.getElementById('result-accuracy').textContent = (results.accuracy * 100).toFixed(2) + '%';
|
|
document.getElementById('result-duration').textContent = results.duration_seconds.toFixed(1) + 's';
|
|
|
|
// Update last training time
|
|
document.getElementById('last-training-time').textContent = new Date().toLocaleTimeString();
|
|
|
|
// Re-enable train button
|
|
document.getElementById('train-model-btn').disabled = false;
|
|
|
|
showSuccess('Training completed successfully');
|
|
}
|
|
|
|
// Simulate inference button
|
|
document.getElementById('simulate-inference-btn').addEventListener('click', function() {
|
|
const modelName = document.getElementById('model-select').value;
|
|
|
|
if (appState.annotations.length === 0) {
|
|
showError('No annotations available for inference simulation');
|
|
return;
|
|
}
|
|
|
|
// Open inference modal
|
|
const modal = new bootstrap.Modal(document.getElementById('inferenceModal'));
|
|
modal.show();
|
|
|
|
// Start inference simulation
|
|
if (appState.trainingController) {
|
|
appState.trainingController.simulateInference(modelName, appState.annotations);
|
|
}
|
|
});
|
|
</script>
|