Mercurial > repos > goeckslab > multimodal_learner
diff test-data/sample_output.html @ 0:375c36923da1 draft default tip
planemo upload for repository https://github.com/goeckslab/gleam.git commit 1c6c1ad7a1b2bd3645aa0eafa2167784820b52e0
| author | goeckslab |
|---|---|
| date | Tue, 09 Dec 2025 23:49:47 +0000 |
| parents | |
| children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test-data/sample_output.html Tue Dec 09 23:49:47 2025 +0000 @@ -0,0 +1,990 @@ +<!DOCTYPE html> +<html> +<head> + <meta charset="UTF-8"> + <title>Galaxy-Ludwig Report</title> + <style> + body { + font-family: Arial, sans-serif; + margin: 0; + padding: 20px; + background-color: #f4f4f4; + } + .container { + max-width: 1200px; + margin: auto; + background: white; + padding: 20px; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + overflow-x: auto; + } + h1 { + text-align: center; + color: #333; + } + h2 { + border-bottom: 2px solid #4CAF50; + color: #4CAF50; + padding-bottom: 5px; + margin-top: 28px; + } + h3 { + margin-top: 20px; + color: #333; + } + .section { + margin-bottom: 30px; + } + .section-title { + border-bottom: 2px solid #4CAF50; + color: #4CAF50; + padding-bottom: 5px; + margin-top: 28px; + } + .card { + background: #fff; + padding: 15px; + border: 1px solid #ddd; + border-radius: 4px; + margin: 10px 0; + } + + /* baseline table setup */ + table { + border-collapse: collapse; + margin: 20px 0; + width: 100%; + table-layout: fixed; + background: #fff; + } + table, th, td { + border: 1px solid #ddd; + } + th, td { + padding: 10px; + text-align: center; + vertical-align: middle; + word-break: break-word; + white-space: normal; + overflow-wrap: anywhere; + } + th { + background-color: #4CAF50; + color: white; + } + .metric-table { + width: 100%; + } + .kv-table { + width: 100%; + } + .kv-table th:first-child, + .kv-table td:first-child { + text-align: left; + width: 30%; + } + + .plot { + text-align: center; + margin: 20px 0; + } + .plot img { + max-width: 100%; + height: auto; + border: 1px solid #ddd; + } + + /* ------------------- + sortable columns (3-state: none ⇅, asc ↑, desc ↓) + ------------------- */ + table.performance-summary th.sortable { + cursor: pointer; + position: relative; + user-select: none; + } + /* default icon space */ + table.performance-summary th.sortable::after { + content: '⇅'; + position: absolute; + right: 12px; + top: 50%; + transform: translateY(-50%); + font-size: 0.8em; + color: #eaf5ea; /* light on green */ + text-shadow: 0 0 1px rgba(0,0,0,0.15); + } + /* three states override the default */ + table.performance-summary th.sortable.sorted-none::after { content: '⇅'; color: #eaf5ea; } + table.performance-summary th.sortable.sorted-asc::after { content: '↑'; color: #ffffff; } + table.performance-summary th.sortable.sorted-desc::after { content: '↓'; color: #ffffff; } + + /* show ~30 rows with a scrollbar (tweak if you want) */ + .scroll-rows-30 { + max-height: 900px; /* ~30 rows depending on row height */ + overflow-y: auto; /* vertical scrollbar ("sidebar") */ + overflow-x: auto; + } + + /* Tabs + Help button (used by build_tabbed_html) */ + .tabs { + display: flex; + align-items: center; + border-bottom: 2px solid #ccc; + margin-bottom: 1rem; + gap: 6px; + flex-wrap: wrap; + } + .tab { + padding: 10px 20px; + cursor: pointer; + border: 1px solid #ccc; + border-bottom: none; + background: #f9f9f9; + margin-right: 5px; + border-top-left-radius: 8px; + border-top-right-radius: 8px; + } + .tab.active { + background: white; + font-weight: bold; + } + .help-btn { + margin-left: auto; + padding: 6px 12px; + font-size: 0.9rem; + border: 1px solid #4CAF50; + border-radius: 4px; + background: #4CAF50; + color: white; + cursor: pointer; + } + .tab-content { + display: none; + padding: 20px; + border: 1px solid #ccc; + border-top: none; + background: #fff; + } + .tab-content.active { + display: block; + } + .plotly-center { + display: flex; + justify-content: center; + } + .plotly-center .plotly-graph-div, .plotly-center .js-plotly-plot { + margin: 0 auto !important; + } + .js-plotly-plot, .plotly-graph-div { + margin-left: auto !important; + margin-right: auto !important; + } + + /* Modal (used by get_metrics_help_modal) */ + .modal { + display: none; + position: fixed; + z-index: 9999; + left: 0; top: 0; + width: 100%; height: 100%; + overflow: auto; + background-color: rgba(0,0,0,0.4); + } + .modal-content { + background-color: #fefefe; + margin: 8% auto; + padding: 20px; + border: 1px solid #888; + width: 90%; + max-width: 900px; + border-radius: 8px; + } + .modal .close { + color: #777; + float: right; + font-size: 28px; + font-weight: bold; + line-height: 1; + margin-left: 8px; + } + .modal .close:hover, + .modal .close:focus { + color: black; + text-decoration: none; + cursor: pointer; + } + .metrics-guide h3 { margin-top: 20px; } + .metrics-guide p { margin: 6px 0; } + .metrics-guide ul { margin: 10px 0; padding-left: 20px; } + </style> + + <script> + // Guard to avoid double-initialization if this block is included twice + (function(){ + if (window.__perfSummarySortInit) return; + window.__perfSummarySortInit = true; + + function initPerfSummarySorting() { + // Record original order for "back to original" + document.querySelectorAll('table.performance-summary tbody').forEach(tbody => { + Array.from(tbody.rows).forEach((row, i) => { row.dataset.originalOrder = i; }); + }); + + const getText = td => (td?.innerText || '').trim(); + const cmp = (idx, asc) => (a, b) => { + const v1 = getText(a.children[idx]); + const v2 = getText(b.children[idx]); + const n1 = parseFloat(v1), n2 = parseFloat(v2); + if (!isNaN(n1) && !isNaN(n2)) return asc ? n1 - n2 : n2 - n1; // numeric + return asc ? v1.localeCompare(v2) : v2.localeCompare(v1); // lexical + }; + + document.querySelectorAll('table.performance-summary th.sortable').forEach(th => { + // initialize to "none" + th.classList.remove('sorted-asc','sorted-desc'); + th.classList.add('sorted-none'); + + th.addEventListener('click', () => { + const table = th.closest('table'); + const headerRow = th.parentNode; + const allTh = headerRow.querySelectorAll('th.sortable'); + const tbody = table.querySelector('tbody'); + + // Determine current state BEFORE clearing + const isAsc = th.classList.contains('sorted-asc'); + const isDesc = th.classList.contains('sorted-desc'); + + // Reset all headers in this row + allTh.forEach(x => x.classList.remove('sorted-asc','sorted-desc','sorted-none')); + + // Compute next state + let next; + if (!isAsc && !isDesc) { + next = 'asc'; + } else if (isAsc) { + next = 'desc'; + } else { + next = 'none'; + } + th.classList.add('sorted-' + next); + + // Sort rows according to the chosen state + const rows = Array.from(tbody.rows); + if (next === 'none') { + rows.sort((a, b) => (a.dataset.originalOrder - b.dataset.originalOrder)); + } else { + const idx = Array.from(headerRow.children).indexOf(th); + rows.sort(cmp(idx, next === 'asc')); + } + rows.forEach(r => tbody.appendChild(r)); + }); + }); + } + + // Run after DOM is ready + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', initPerfSummarySorting); + } else { + initPerfSummarySorting(); + } + })(); + </script> +</head> +<body> + <div class="container"> + <script src="https://cdn.plot.ly/plotly-2.30.0.min.js"></script> + <style> + .plotly-center { + display: flex; + justify-content: center; + margin: 20px 0; + width: 100%; + } + .plotly-center .plotly-graph-div, .plotly-center .js-plotly-plot { + margin: 0 auto !important; + } + .js-plotly-plot, .plotly-graph-div { + margin-left: auto !important; + margin-right: auto !important; + } + #learning-curve-accuracy, #learning-curve-loss, #threshold-plot, + #confusion-matrix, #per-class-metrics, #roc-curve, #pr-curve { + min-height: 400px; + } + </style> + + <div class="tabs"> + <div class="tab active" onclick="showTab('summary')">Model Summary & Config</div> + <div class="tab" onclick="showTab('train')">Train/Validation Summary</div> + <div class="tab" onclick="showTab('test')">Test Summary</div> + <div class="tab" onclick="showTab('feature')">Feature Importance</div> + <button id="openMetricsHelp" class="help-btn">Help</button> + </div> + + <!-- Summary Tab --> + <div id="summary" class="tab-content active"> + <section class="section"> + <h2 class="section-title">Model Performance Summary</h2> + <div class="card"> + <table class="metric-table"> + <thead><tr><th>Metric</th><th>Train</th><th>Validation</th><th>Test</th></tr></thead> + <tbody> + <tr><td>accuracy</td><td>0.9234</td><td>0.8912</td><td>0.8856</td></tr> + <tr><td>f1</td><td>0.9201</td><td>0.8876</td><td>0.8823</td></tr> + <tr><td>precision</td><td>0.9156</td><td>0.8845</td><td>0.8798</td></tr> + <tr><td>recall</td><td>0.9245</td><td>0.8907</td><td>0.8849</td></tr> + <tr><td>roc_auc</td><td>0.9789</td><td>0.9543</td><td>0.9512</td></tr> + <tr><td>log_loss</td><td>0.2134</td><td>0.2876</td><td>0.3012</td></tr> + </tbody> + </table> + </div> + </section> + + <section class="section"> + <h2 class="section-title">Run Configuration</h2> + <div class="card"> + <table class="kv-table"> + <thead><tr><th>Key</th><th>Value</th></tr></thead> + <tbody> + <tr><td>Predictor type</td><td>MultiModalPredictor</td></tr> + <tr><td>Framework</td><td>AutoGluon Multimodal</td></tr> + <tr><td>Model architecture</td><td>timm_image=resnet50, hf_text=bert-base-uncased</td></tr> + <tr><td>Modalities & Inputs</td><td>Images + Tabular</td></tr> + <tr><td>Label column</td><td>target</td></tr> + <tr><td>Image columns</td><td>image_path</td></tr> + <tr><td>Tabular columns</td><td>15</td></tr> + <tr><td>Presets</td><td>medium_quality</td></tr> + <tr><td>Eval metric</td><td>accuracy</td></tr> + <tr><td>Decision threshold calibration</td><td>enabled</td></tr> + <tr><td>Decision threshold (Test only)</td><td>0.500</td></tr> + <tr><td>Seed</td><td>42</td></tr> + <tr><td>time limit(s)</td><td>3600</td></tr> + </tbody> + </table> + </div> + </section> + + <section class="section"> + <h2 class="section-title">Class Balance (Train Full)</h2> + <div class="card"> + <h3>Class Balance (Train Full)</h3> + <table class="table"> + <thead><tr><th>Class</th><th>Count</th><th>Percent</th></tr></thead> + <tbody> + <tr><td>0</td><td>1245</td><td>45.23%</td></tr> + <tr><td>1</td><td>1508</td><td>54.77%</td></tr> + </tbody> + </table> + </div> + </section> + </div> + + <!-- Train Tab --> + <div id="train" class="tab-content"> + <h2>Train/Validation Performance Summary</h2> + + <div class="card"> + <table class="metric-table"> + <thead><tr><th>Metric</th><th>Train</th><th>Validation</th></tr></thead> + <tbody> + <tr><td>accuracy</td><td>0.9234</td><td>0.8912</td></tr> + <tr><td>f1</td><td>0.9201</td><td>0.8876</td></tr> + <tr><td>precision</td><td>0.9156</td><td>0.8845</td></tr> + <tr><td>recall</td><td>0.9245</td><td>0.8907</td></tr> + <tr><td>roc_auc</td><td>0.9789</td><td>0.9543</td></tr> + <tr><td>log_loss</td><td>0.2134</td><td>0.2876</td></tr> + </tbody> + </table> + </div> + + <h2>Learning Curves — Label Accuracy</h2> + <div class="plotly-center"> + <div id="learning-curve-accuracy" style="width:900px;height:500px;"></div> + </div> + + <h2>Learning Curves — Label Loss</h2> + <div class="plotly-center"> + <div id="learning-curve-loss" style="width:900px;height:500px;"></div> + </div> + </div> + + <!-- Test Tab --> + <div id="test" class="tab-content"> + <h2>Test Performance Summary</h2> + <table class="performance-summary"> + <thead> + <tr> + <th class="sortable">Metric</th> + <th class="sortable">Test</th> + </tr> + </thead> + <tbody> + <tr><td>accuracy</td><td>0.8856</td></tr> + <tr><td>f1</td><td>0.8823</td></tr> + <tr><td>precision</td><td>0.8798</td></tr> + <tr><td>recall</td><td>0.8849</td></tr> + <tr><td>roc_auc</td><td>0.9512</td></tr> + <tr><td>log_loss</td><td>0.3012</td></tr> + <tr><td>specificity (TNR)</td><td>0.8765</td></tr> + <tr><td>sensitivity (Sensitivity/TPR)</td><td>0.8923</td></tr> + </tbody> + </table> + + <h2>Confusion Matrix</h2> + <div class="plotly-center"> + <div id="confusion-matrix" style="width:700px;height:600px;"></div> + </div> + + <h2>Per-Class Metrics</h2> + <div class="plotly-center"> + <div id="per-class-metrics" style="width:900px;height:500px;"></div> + </div> + + <h2>ROC Curve</h2> + <div class="plotly-center"> + <div id="roc-curve" style="width:800px;height:600px;"></div> + </div> + + <h2>Precision–Recall Curve</h2> + <div class="plotly-center"> + <div id="pr-curve" style="width:800px;height:600px;"></div> + </div> + + <h2>Threshold Plot</h2> + <div class="plotly-center"> + <div id="threshold-plot" style="width:900px;height:500px;"></div> + </div> + </div> + + <!-- Feature Importance Tab --> + <div id="feature" class="tab-content"> + <section class="section"> + <h2 class="section-title">Feature Importance</h2> + <div class="card"> + <p>Permutation importance is not supported for MultiModalPredictor in this tool. For tabular-only runs, this section shows permutation importance.</p> + </div> + </section> + + <section class="section"> + <h2 class="section-title">Modalities & Inputs</h2> + <div class="card"> + <h3>Modalities & Inputs</h3> + <p>This run used <b>MultiModalPredictor</b> (images + tabular).</p> + <p><b>Label column:</b> target</p> + <p><b>Image column:</b> image_path</p> + <div><b>Tabular columns:</b> 15 + <ul> + <li>feature_1</li> + <li>feature_2</li> + <li>feature_3</li> + <li>feature_4</li> + <li>feature_5</li> + <li>feature_6</li> + <li>feature_7</li> + <li>feature_8</li> + <li>feature_9</li> + <li>feature_10</li> + <li>feature_11</li> + <li>feature_12</li> + <li>feature_13</li> + <li>feature_14</li> + <li>feature_15</li> + </ul> + </div> + </div> + </section> + </div> + + <script> + function showTab(id) { + document.querySelectorAll('.tab-content').forEach(el => el.classList.remove('active')); + document.querySelectorAll('.tab').forEach(el => el.classList.remove('active')); + document.getElementById(id).classList.add('active'); + document.querySelector(`.tab[onclick*="${id}"]`).classList.add('active'); + } + + // Fixed random data for reproducibility + const epochs = Array.from({length: 31}, (_, i) => i); + const accuracy_train = [0.552, 0.568, 0.581, 0.595, 0.612, 0.628, 0.645, 0.662, 0.678, 0.692, 0.708, 0.722, 0.735, 0.748, 0.761, 0.773, 0.784, 0.795, 0.805, 0.814, 0.823, 0.831, 0.839, 0.846, 0.853, 0.859, 0.865, 0.870, 0.875, 0.880, 0.884]; + const accuracy_val = [0.752, 0.768, 0.782, 0.795, 0.807, 0.818, 0.828, 0.837, 0.845, 0.852, 0.859, 0.865, 0.871, 0.876, 0.881, 0.885, 0.889, 0.893, 0.896, 0.899, 0.902, 0.905, 0.907, 0.909, 0.911, 0.913, 0.915, 0.917, 0.918, 0.920, 0.921]; + const loss_train = [1.352, 1.285, 1.221, 1.158, 1.098, 1.041, 0.987, 0.936, 0.888, 0.842, 0.799, 0.758, 0.720, 0.684, 0.650, 0.618, 0.588, 0.560, 0.534, 0.510, 0.487, 0.466, 0.447, 0.429, 0.412, 0.397, 0.383, 0.370, 0.358, 0.347, 0.337]; + const loss_val = [0.802, 0.765, 0.730, 0.697, 0.666, 0.637, 0.610, 0.585, 0.561, 0.539, 0.518, 0.499, 0.481, 0.464, 0.448, 0.433, 0.419, 0.406, 0.394, 0.383, 0.372, 0.362, 0.353, 0.345, 0.337, 0.330, 0.323, 0.317, 0.311, 0.306, 0.301]; + + // Wait for Plotly to load before creating plots + function createPlots() { + if (typeof Plotly === 'undefined') { + console.log('Waiting for Plotly...'); + setTimeout(createPlots, 100); + return; + } + + console.log('Plotly loaded, creating plots...'); + + // Learning Curves — Label Accuracy (with both training and validation) + try { + Plotly.newPlot('learning-curve-accuracy', [ + { + x: epochs, + y: accuracy_train, + type: 'scatter', + mode: 'lines+markers', + name: 'Training', + line: { width: 3, color: '#1f77b4', shape: 'spline' }, + marker: { size: 8, color: '#1f77b4' } + }, + { + x: epochs, + y: accuracy_val, + type: 'scatter', + mode: 'lines+markers', + name: 'Validation', + line: { width: 3, color: '#ff7f0e', shape: 'spline' }, + marker: { size: 8, color: '#ff7f0e' } + } + ], { + template: 'plotly_white', + xaxis: { + title: 'Epoch', + gridcolor: '#e0e0e0', + showgrid: true, + zeroline: false + }, + yaxis: { + title: 'Accuracy', + gridcolor: '#e0e0e0', + showgrid: true, + zeroline: false, + range: [0.5, 1.0] + }, + legend: { + orientation: 'h', + yanchor: 'bottom', + y: 1.02, + xanchor: 'right', + x: 1.0 + }, + margin: { l: 60, r: 20, t: 40, b: 50 }, + plot_bgcolor: 'white', + paper_bgcolor: 'white' + }); + console.log('Accuracy plot created'); + } catch(e) { + console.error('Error creating accuracy plot:', e); + } + + // Learning Curves — Label Loss (with both training and validation) + try { + Plotly.newPlot('learning-curve-loss', [ + { + x: epochs, + y: loss_train, + type: 'scatter', + mode: 'lines+markers', + name: 'Training Loss', + line: { width: 3, color: '#1f77b4', shape: 'spline' }, + marker: { size: 8, color: '#1f77b4' } + }, + { + x: epochs, + y: loss_val, + type: 'scatter', + mode: 'lines+markers', + name: 'Validation Loss', + line: { width: 3, color: '#ff7f0e', shape: 'spline' }, + marker: { size: 8, color: '#ff7f0e' } + } + ], { + template: 'plotly_white', + xaxis: { + title: 'Epoch', + gridcolor: '#e0e0e0', + showgrid: true, + zeroline: false + }, + yaxis: { + title: 'Loss', + gridcolor: '#e0e0e0', + showgrid: true, + zeroline: false, + range: [0.2, 1.4] + }, + legend: { + orientation: 'h', + yanchor: 'bottom', + y: 1.02, + xanchor: 'right', + x: 1.0 + }, + margin: { l: 60, r: 20, t: 40, b: 50 }, + plot_bgcolor: 'white', + paper_bgcolor: 'white' + }); + console.log('Loss plot created'); + } catch(e) { + console.error('Error creating loss plot:', e); + } + + // Threshold Plot (with fixed data matching the expected visualization) + // Precision: starts low (~0.42), increases to peak (~0.95) around threshold 0.5, then drops sharply to 0 + // Recall: starts at 1, stays at 1 until ~0.1, then decreases to 0 around 0.9 + // F1: starts around 0.6, peaks around 0.5 at ~0.95, then drops to 0 + const thresholds = Array.from({length: 101}, (_, i) => i / 100); + + // Generate precision values: low at start, peak around 0.5, drop to 0 + const precision_vals = thresholds.map(t => { + if (t <= 0.5) { + return 0.42 + (t / 0.5) * 0.53; // 0.42 to 0.95 + } else if (t <= 0.9) { + return 0.95 - ((t - 0.5) / 0.4) * 0.95; // 0.95 to 0 + } else { + return 0; + } + }); + + // Generate recall values: starts at 1, stays at 1 until ~0.1, then decreases to 0 around 0.9 + const recall_vals = thresholds.map(t => { + if (t <= 0.1) { + return 1.0; + } else if (t <= 0.9) { + return 1.0 - ((t - 0.1) / 0.8) * 1.0; // 1.0 to 0 + } else { + return 0; + } + }); + + // Calculate F1 from precision and recall + const f1_vals = thresholds.map((t, i) => { + const p = precision_vals[i]; + const r = recall_vals[i]; + if (p + r === 0) return 0; + return 2 * (p * r) / (p + r); + }); + + // Queue Rate: decreases linearly from 1 to 0 + const queue_rate = thresholds.map(t => 1 - t); + + try { + Plotly.newPlot('threshold-plot', [ + { + x: thresholds, + y: precision_vals, + type: 'scatter', + mode: 'lines', + name: 'Precision', + line: { width: 3, color: '#1f77b4' } + }, + { + x: thresholds, + y: recall_vals, + type: 'scatter', + mode: 'lines', + name: 'Recall', + line: { width: 3, color: '#ff7f0e' } + }, + { + x: thresholds, + y: f1_vals, + type: 'scatter', + mode: 'lines', + name: 'F1', + line: { width: 3, color: '#2ca02c' } + }, + { + x: thresholds, + y: queue_rate, + type: 'scatter', + mode: 'lines', + name: 'Queue Rate', + line: { width: 2, color: '#808080', dash: 'dash' } + } + ], { + template: 'plotly_white', + xaxis: { + title: 'Discrimination Threshold', + gridcolor: '#e0e0e0', + showgrid: true, + zeroline: false + }, + yaxis: { + title: 'Score', + gridcolor: '#e0e0e0', + showgrid: true, + zeroline: false, + range: [0, 1] + }, + legend: { + orientation: 'h', + yanchor: 'bottom', + y: 1.02, + xanchor: 'right', + x: 1.0 + }, + margin: { l: 60, r: 20, t: 40, b: 50 }, + plot_bgcolor: 'white', + paper_bgcolor: 'white', + shapes: [{ + type: 'line', + xref: 'x', + yref: 'y', + x0: 0.51, + y0: 0, + x1: 0.51, + y1: 1, + line: { + color: 'black', + width: 2, + dash: 'dash' + } + }], + annotations: [{ + x: 0.51, + y: 0.85, + text: 't* = 0.51', + showarrow: false, + font: { size: 12, color: 'black' } + }] + }); + console.log('Threshold plot created'); + } catch(e) { + console.error('Error creating threshold plot:', e); + } + + // Confusion Matrix (matching imagelearner style with Blues colorscale) + const cm_data = [[542, 78], [65, 515]]; + const total = cm_data.flat().reduce((a, b) => a + b, 0); + const labels = ['0', '1']; + + // Build annotations array with all white text + const annotations = []; + cm_data.forEach((row, i) => { + row.forEach((val, j) => { + const pct = ((val / total) * 100).toFixed(1); + // All text is white + const textColor = 'white'; + // Count annotation (bold, bottom) + annotations.push({ + x: labels[j], + y: labels[i], + text: '<b>' + val + '</b>', + showarrow: false, + font: { color: textColor, size: 14 }, + xanchor: 'center', + yanchor: 'bottom', + yshift: 2 + }); + // Percentage annotation (top) + annotations.push({ + x: labels[j], + y: labels[i], + text: pct + '%', + showarrow: false, + font: { color: textColor, size: 13 }, + xanchor: 'center', + yanchor: 'top', + yshift: -2 + }); + }); + }); + + try { + Plotly.newPlot('confusion-matrix', [{ + z: cm_data, + x: labels, + y: labels, + type: 'heatmap', + colorscale: 'Blues', + showscale: true, + colorbar: { title: 'Count' }, + xgap: 2, + ygap: 2, + hovertemplate: 'True=%{y}<br>Pred=%{x}<br>Count=%{z}<extra></extra>', + zmin: 0 + }], { + xaxis: { title: 'Predicted label', type: 'category' }, + yaxis: { title: 'True label', type: 'category', autorange: 'reversed' }, + margin: { l: 80, r: 20, t: 40, b: 80 }, + template: 'plotly_white', + plot_bgcolor: 'white', + paper_bgcolor: 'white', + annotations: annotations + }); + console.log('Confusion matrix created'); + } catch(e) { + console.error('Error creating confusion matrix:', e); + } + + // Per-Class Metrics + const classes = ['Class 0', 'Class 1']; + const precision_per_class = [0.8929, 0.8685]; + const recall_per_class = [0.8742, 0.8879]; + const f1_per_class = [0.8835, 0.8781]; + + try { + Plotly.newPlot('per-class-metrics', [ + { + x: classes, + y: precision_per_class, + type: 'bar', + name: 'Precision', + marker: { color: '#4CAF50' } + }, + { + x: classes, + y: recall_per_class, + type: 'bar', + name: 'Recall', + marker: { color: '#2196F3' } + }, + { + x: classes, + y: f1_per_class, + type: 'bar', + name: 'F1', + marker: { color: '#FF9800' } + } + ], { + template: 'plotly_white', + xaxis: { + title: 'Class', + gridcolor: '#e0e0e0', + showgrid: true + }, + yaxis: { + title: 'Score', + gridcolor: '#e0e0e0', + showgrid: true, + range: [0, 1] + }, + barmode: 'group', + legend: { + orientation: 'h', + yanchor: 'bottom', + y: 1.02, + xanchor: 'right', + x: 1.0 + }, + margin: { l: 60, r: 20, t: 40, b: 50 }, + plot_bgcolor: 'white', + paper_bgcolor: 'white' + }); + console.log('Per-class metrics created'); + } catch(e) { + console.error('Error creating per-class metrics:', e); + } + + // ROC Curve (with fixed data) + const fpr = [0,0.01,0.02,0.03,0.04,0.05,0.06,0.07,0.08,0.09,0.10,0.11,0.12,0.13,0.14,0.15,0.16,0.17,0.18,0.19,0.20,0.21,0.22,0.23,0.24,0.25,0.26,0.27,0.28,0.29,0.30,0.31,0.32,0.33,0.34,0.35,0.36,0.37,0.38,0.39,0.40,0.41,0.42,0.43,0.44,0.45,0.46,0.47,0.48,0.49,0.50,0.51,0.52,0.53,0.54,0.55,0.56,0.57,0.58,0.59,0.60,0.61,0.62,0.63,0.64,0.65,0.66,0.67,0.68,0.69,0.70,0.71,0.72,0.73,0.74,0.75,0.76,0.77,0.78,0.79,0.80,0.81,0.82,0.83,0.84,0.85,0.86,0.87,0.88,0.89,0.90,0.91,0.92,0.93,0.94,0.95,0.96,0.97,0.98,0.99,1.00]; + const tpr = [0,0.12,0.23,0.33,0.42,0.50,0.57,0.63,0.69,0.74,0.78,0.82,0.85,0.88,0.90,0.92,0.94,0.95,0.96,0.97,0.98,0.985,0.99,0.992,0.994,0.996,0.997,0.998,0.999,0.9995,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0]; + const auc_score = 0.935; + + try { + Plotly.newPlot('roc-curve', [ + { + x: [0, 1], + y: [0, 1], + type: 'scatter', + mode: 'lines', + name: 'Random (AUC = 0.50)', + line: { dash: 'dash', color: 'gray', width: 2 } + }, + { + x: fpr, + y: tpr, + type: 'scatter', + mode: 'lines', + name: `ROC Curve (AUC = ${auc_score.toFixed(3)})`, + line: { width: 3, color: '#1f77b4' } + } + ], { + template: 'plotly_white', + xaxis: { + title: 'False Positive Rate', + gridcolor: '#e0e0e0', + showgrid: true, + zeroline: false, + range: [0, 1] + }, + yaxis: { + title: 'True Positive Rate', + gridcolor: '#e0e0e0', + showgrid: true, + zeroline: false, + range: [0, 1] + }, + legend: { + orientation: 'h', + yanchor: 'bottom', + y: 1.02, + xanchor: 'right', + x: 1.0 + }, + margin: { l: 60, r: 20, t: 40, b: 50 }, + plot_bgcolor: 'white', + paper_bgcolor: 'white' + }); + console.log('ROC curve created'); + } catch(e) { + console.error('Error creating ROC curve:', e); + } + + // Precision-Recall Curve (with fixed data) + const recall_pr = [0,0.01,0.02,0.03,0.04,0.05,0.06,0.07,0.08,0.09,0.10,0.11,0.12,0.13,0.14,0.15,0.16,0.17,0.18,0.19,0.20,0.21,0.22,0.23,0.24,0.25,0.26,0.27,0.28,0.29,0.30,0.31,0.32,0.33,0.34,0.35,0.36,0.37,0.38,0.39,0.40,0.41,0.42,0.43,0.44,0.45,0.46,0.47,0.48,0.49,0.50,0.51,0.52,0.53,0.54,0.55,0.56,0.57,0.58,0.59,0.60,0.61,0.62,0.63,0.64,0.65,0.66,0.67,0.68,0.69,0.70,0.71,0.72,0.73,0.74,0.75,0.76,0.77,0.78,0.79,0.80,0.81,0.82,0.83,0.84,0.85,0.86,0.87,0.88,0.89,0.90,0.91,0.92,0.93,0.94,0.95,0.96,0.97,0.98,0.99,1.00]; + const precision_pr = [0.95,0.949,0.948,0.947,0.946,0.945,0.944,0.943,0.942,0.941,0.940,0.939,0.938,0.937,0.936,0.935,0.934,0.933,0.932,0.931,0.930,0.929,0.928,0.927,0.926,0.925,0.924,0.923,0.922,0.921,0.920,0.919,0.918,0.917,0.916,0.915,0.914,0.913,0.912,0.911,0.910,0.909,0.908,0.907,0.906,0.905,0.904,0.903,0.902,0.901,0.900,0.899,0.898,0.897,0.896,0.895,0.894,0.893,0.892,0.891,0.890,0.889,0.888,0.887,0.886,0.885,0.884,0.883,0.882,0.881,0.880,0.879,0.878,0.877,0.876,0.875,0.874,0.873,0.872,0.871,0.870,0.869,0.868,0.867,0.866,0.865,0.864,0.863,0.862,0.861,0.860,0.859,0.858,0.857,0.856,0.855,0.854,0.853,0.852,0.851,0.850]; + const ap_score = 0.9234; + + try { + Plotly.newPlot('pr-curve', [ + { + x: recall_pr, + y: precision_pr, + type: 'scatter', + mode: 'lines', + name: `Precision-Recall Curve (AP = ${ap_score.toFixed(4)})`, + line: { width: 3, color: '#1f77b4' } + } + ], { + template: 'plotly_white', + xaxis: { + title: 'Recall', + gridcolor: '#e0e0e0', + showgrid: true, + zeroline: false, + range: [0, 1] + }, + yaxis: { + title: 'Precision', + gridcolor: '#e0e0e0', + showgrid: true, + zeroline: false, + range: [0.6, 1] + }, + legend: { + orientation: 'h', + yanchor: 'bottom', + y: 1.02, + xanchor: 'right', + x: 1.0 + }, + margin: { l: 60, r: 20, t: 40, b: 50 }, + plot_bgcolor: 'white', + paper_bgcolor: 'white' + }); + console.log('PR curve created'); + } catch(e) { + console.error('Error creating PR curve:', e); + } + + console.log('All plots created successfully!'); + } + + // Start creating plots when DOM is ready + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', createPlots); + } else { + createPlots(); + } + </script> + </div> +</body> +</html> +
