Yingtao-Zheng commited on
Commit
518de6c
Β·
1 Parent(s): 0616f67

fix: hybird not functioning. Change back from xgboost+geo to MLP+geo for hybird

Browse files
checkpoints/hybrid_focus_config.json CHANGED
@@ -1,14 +1,10 @@
1
  {
2
- "use_xgb": true,
3
  "w_mlp": 0.3,
4
- "w_xgb": 0.3,
5
  "w_geo": 0.7,
6
- "threshold": 0.46117913373775393,
7
  "use_yawn_veto": true,
8
  "geo_face_weight": 0.7,
9
  "geo_eye_weight": 0.3,
10
- "mar_yawn_threshold": 0.55,
11
- "metric": "f1",
12
- "combiner": "logistic",
13
- "combiner_path": "hybrid_combiner.joblib"
14
  }
 
1
  {
2
+ "use_xgb": false,
3
  "w_mlp": 0.3,
 
4
  "w_geo": 0.7,
5
+ "threshold": 0.35,
6
  "use_yawn_veto": true,
7
  "geo_face_weight": 0.7,
8
  "geo_eye_weight": 0.3,
9
+ "mar_yawn_threshold": 0.55
 
 
 
10
  }
src/components/FocusPageLocal.jsx CHANGED
@@ -43,14 +43,14 @@ const MODEL_INFO = {
43
  hybrid: {
44
  label: 'Hybrid',
45
  tagline: 'Best overall β€” combines ML with geometric scoring',
46
- how: 'Fuses XGBoost predictions (30%) with geometric face/eye scores (70%). Uses a weighted blend tuned with LOPO evaluation.',
47
  accuracy: 'N/A',
48
  f1: '0.8409',
49
  auc: 'N/A',
50
- threshold: '0.46',
51
  evaluation: 'LOPO tuning (9 participants, 144K frames)',
52
  features: '10 features: head deviation, face score, eye scores (EAR), gaze offset, pitch, horizontal gaze, PERCLOS',
53
- strengths: 'Most robust across different people. Latest LOPO mean F1 is 0.8409 at w_mlp=0.3.',
54
  badge: 'Recommended',
55
  },
56
  xgboost: {
@@ -109,10 +109,6 @@ function FocusPageLocal({ videoManager, sessionResult, setSessionResult, isActiv
109
  const [calibration, setCalibration] = useState(null);
110
  const [l2csBoost, setL2csBoost] = useState(false);
111
  const [l2csBoostAvailable, setL2csBoostAvailable] = useState(false);
112
- const [showEyeGazeModal, setShowEyeGazeModal] = useState(false);
113
- const [eyeGazeDontShow, setEyeGazeDontShow] = useState(
114
- () => localStorage.getItem('focusguard_eyegaze_noshowalert') === 'true'
115
- );
116
 
117
  const localVideoRef = useRef(null);
118
  const displayCanvasRef = useRef(null);
@@ -331,20 +327,7 @@ function FocusPageLocal({ videoManager, sessionResult, setSessionResult, isActiv
331
 
332
  const handleEyeGazeToggle = async () => {
333
  const next = !l2csBoost;
334
- if (next && !eyeGazeDontShow) {
335
- // Show the warning/calibration modal before enabling
336
- setShowEyeGazeModal(true);
337
- return;
338
- }
339
- await applyEyeGazeChange(next, true);
340
- };
341
-
342
- const handleEyeGazeModalAction = async (withCalibration) => {
343
- if (eyeGazeDontShow) {
344
- localStorage.setItem('focusguard_eyegaze_noshowalert', 'true');
345
- }
346
- setShowEyeGazeModal(false);
347
- await applyEyeGazeChange(true, withCalibration);
348
  };
349
 
350
  const handleStart = async () => {
@@ -700,91 +683,9 @@ function FocusPageLocal({ videoManager, sessionResult, setSessionResult, isActiv
700
  return null;
701
  };
702
 
703
- const renderEyeGazeModal = () => {
704
- if (!showEyeGazeModal) return null;
705
- return (
706
- <div className="focus-flow-overlay" style={{ zIndex: 2000 }}>
707
- <div className="focus-flow-card">
708
- <div className="focus-flow-header">
709
- <div>
710
- <div className="focus-flow-eyebrow">Eye Gaze Tracking</div>
711
- <h2>Before you enable</h2>
712
- </div>
713
- <div className="focus-flow-icon">
714
- <svg width="96" height="96" viewBox="0 0 96 96" aria-hidden="true">
715
- <ellipse cx="48" cy="48" rx="38" ry="24" fill="none" stroke="#007BFF" strokeWidth="5" />
716
- <circle cx="48" cy="48" r="13" fill="none" stroke="#007BFF" strokeWidth="5" />
717
- <circle cx="48" cy="48" r="5" fill="#007BFF" />
718
- </svg>
719
- </div>
720
- </div>
721
-
722
- <p className="focus-flow-lead">
723
- Eye gaze tracking runs an additional deep neural network (L2CS-Net) alongside your current model.
724
- Please read the notes below before proceeding.
725
- </p>
726
-
727
- <div className="focus-flow-grid">
728
- <article className="focus-flow-panel focus-flow-panel-warn">
729
- <h3>Performance impact</h3>
730
- <p>Enabling eye gaze tracking increases CPU usage and may reduce frame rate. If the system feels sluggish, consider disabling it.</p>
731
- </article>
732
- <article className="focus-flow-panel">
733
- <h3>Calibration (recommended)</h3>
734
- <p>For best accuracy, calibrate by looking at 9 screen positions one at a time, followed by 1 validation point. The whole process takes about 30 seconds.</p>
735
- </article>
736
- </div>
737
-
738
- <div className="focus-flow-steps">
739
- <div className="focus-flow-step">
740
- <div className="focus-flow-step-number">1</div>
741
- <div className="focus-flow-step-copy">
742
- <h3>Click "Start Calibration"</h3>
743
- <p>A dot will appear on screen. Look directly at it and keep your gaze steady. It will cycle through 9 positions then show a final validation dot.</p>
744
- </div>
745
- </div>
746
- <div className="focus-flow-step">
747
- <div className="focus-flow-step-number">2</div>
748
- <div className="focus-flow-step-copy">
749
- <h3>Or skip for now</h3>
750
- <p>Click "Skip" to enable eye gaze tracking without calibrating. You can recalibrate at any time using the "Recalibrate" button during a session.</p>
751
- </div>
752
- </div>
753
- </div>
754
-
755
- <label className="eye-gaze-modal-checkbox">
756
- <input
757
- type="checkbox"
758
- checked={eyeGazeDontShow}
759
- onChange={(e) => setEyeGazeDontShow(e.target.checked)}
760
- />
761
- Don't show this again
762
- </label>
763
-
764
- <div className="focus-flow-footer">
765
- <button
766
- type="button"
767
- className="focus-flow-secondary"
768
- onClick={() => handleEyeGazeModalAction(false)}
769
- >
770
- Skip
771
- </button>
772
- <button
773
- className="focus-flow-button"
774
- onClick={() => handleEyeGazeModalAction(true)}
775
- >
776
- Start Calibration
777
- </button>
778
- </div>
779
- </div>
780
- </div>
781
- );
782
- };
783
-
784
  return (
785
  <main id="page-b" className="page" style={pageStyle}>
786
  {renderIntroCard()}
787
- {renderEyeGazeModal()}
788
 
789
  <section id="display-area" className="focus-display-shell">
790
  <video
 
43
  hybrid: {
44
  label: 'Hybrid',
45
  tagline: 'Best overall β€” combines ML with geometric scoring',
46
+ how: 'Fuses MLP predictions (30%) with geometric face/eye scores (70%). Uses a weighted blend tuned with LOPO evaluation.',
47
  accuracy: 'N/A',
48
  f1: '0.8409',
49
  auc: 'N/A',
50
+ threshold: '0.35',
51
  evaluation: 'LOPO tuning (9 participants, 144K frames)',
52
  features: '10 features: head deviation, face score, eye scores (EAR), gaze offset, pitch, horizontal gaze, PERCLOS',
53
+ strengths: 'Most robust across different people. LOPO mean F1 is 0.8409 at w_mlp=0.3, w_geo=0.7.',
54
  badge: 'Recommended',
55
  },
56
  xgboost: {
 
109
  const [calibration, setCalibration] = useState(null);
110
  const [l2csBoost, setL2csBoost] = useState(false);
111
  const [l2csBoostAvailable, setL2csBoostAvailable] = useState(false);
 
 
 
 
112
 
113
  const localVideoRef = useRef(null);
114
  const displayCanvasRef = useRef(null);
 
327
 
328
  const handleEyeGazeToggle = async () => {
329
  const next = !l2csBoost;
330
+ await applyEyeGazeChange(next, false);
 
 
 
 
 
 
 
 
 
 
 
 
 
331
  };
332
 
333
  const handleStart = async () => {
 
683
  return null;
684
  };
685
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
686
  return (
687
  <main id="page-b" className="page" style={pageStyle}>
688
  {renderIntroCard()}
 
689
 
690
  <section id="display-area" className="focus-display-shell">
691
  <video
src/components/Help.jsx CHANGED
@@ -52,7 +52,7 @@ function Help() {
52
 
53
  <section className="help-section">
54
  <h2>Available Models</h2>
55
- <p><strong>Hybrid</strong> <em>(Recommended)</em>: Combines XGBoost predictions with geometric face/eye scoring using a trained logistic regression combiner. Most robust across different people. LOPO F1: 0.8409.</p>
56
  <p><strong>XGBoost:</strong> Gradient-boosted tree model using 10 selected features. Highest raw accuracy (95.87% pooled, LOPO AUC 0.8695). Strong on tabular data with fast inference.</p>
57
  <p><strong>MLP:</strong> Two-layer neural network (10β†’64β†’32 neurons) trained with PyTorch. Good balance of speed and accuracy (92.92% pooled, LOPO AUC 0.8624). Fastest inference.</p>
58
  <p><strong>Geometric:</strong> Rule-based scoring using head pose and eye openness. No ML model needed β€” lightweight fallback when model checkpoints are unavailable. LOPO F1: 0.8195.</p>
@@ -66,13 +66,13 @@ function Help() {
66
  <p>The <strong>Eye Gaze</strong> button enables L2CS-Net, a deep neural network that estimates your gaze direction from the eye region. It runs alongside your selected base model and can improve focus detection accuracy.</p>
67
  <p style={{ marginTop: '8px' }}><strong>Performance note:</strong> Eye gaze tracking increases CPU usage and may reduce frame rate. If the system feels sluggish, disable it.</p>
68
  <h3 style={{ marginTop: '14px', fontSize: '1rem' }}>Calibration</h3>
69
- <p>For best accuracy, calibrate when prompted:</p>
70
  <ol>
71
- <li>Click "Start Calibration" in the warning dialog (or use the "Recalibrate" button during a session)</li>
72
  <li>Look directly at each dot as it appears on screen β€” there are <strong>9 calibration points</strong> across the screen</li>
73
  <li>A final <strong>validation point</strong> confirms accuracy before calibration is applied</li>
74
  </ol>
75
- <p>You can skip calibration and recalibrate at any time using the "Recalibrate" button, which appears in the model strip when Eye Gaze is on and a session is running.</p>
76
  </section>
77
 
78
  <section className="help-section">
@@ -134,8 +134,8 @@ function Help() {
134
  <p>The face mesh overlay updates each time the server returns a detection result. The camera feed itself renders locally. Any visible lag depends on network latency and server processing time. Reducing the frame rate slider can help if lag is noticeable.</p>
135
  </details>
136
  <details>
137
- <summary>The Hybrid model doesn't seem to work differently from XGBoost β€” why?</summary>
138
- <p>The Hybrid model uses a trained logistic regression combiner on top of XGBoost and geometric scores. If the combiner file wasn't available at startup, it falls back to a simple weighted average which behaves similarly to XGBoost. Check the server logs for "[HYBRID]" messages to confirm the combiner loaded correctly.</p>
139
  </details>
140
  </section>
141
 
@@ -143,7 +143,7 @@ function Help() {
143
  <h2>Technical Info</h2>
144
  <p><strong>Face Detection:</strong> MediaPipe Face Mesh (478 landmarks)</p>
145
  <p><strong>Feature Extraction:</strong> Head pose (yaw/pitch/roll), EAR, MAR, gaze offset, PERCLOS, blink rate β€” 10 features selected via LOFO analysis</p>
146
- <p><strong>ML Models:</strong> PyTorch MLP (10β†’64β†’32β†’2), XGBoost (600 trees), Geometric (rule-based), Hybrid (LR combiner over XGBoost+Geo)</p>
147
  <p><strong>Eye Gaze:</strong> L2CS-Net (ResNet50 backbone, trained on Gaze360) with 9-point polynomial calibration</p>
148
  <p><strong>Storage:</strong> SQLite database (sessions, events, settings)</p>
149
  <p><strong>Framework:</strong> FastAPI + React (Vite) + WebSocket</p>
 
52
 
53
  <section className="help-section">
54
  <h2>Available Models</h2>
55
+ <p><strong>Hybrid</strong> <em>(Recommended)</em>: Blends MLP predictions (30%) with geometric face/eye scoring (70%) using a weighted average tuned with LOPO evaluation. Most robust across different people. LOPO F1: 0.8409.</p>
56
  <p><strong>XGBoost:</strong> Gradient-boosted tree model using 10 selected features. Highest raw accuracy (95.87% pooled, LOPO AUC 0.8695). Strong on tabular data with fast inference.</p>
57
  <p><strong>MLP:</strong> Two-layer neural network (10β†’64β†’32 neurons) trained with PyTorch. Good balance of speed and accuracy (92.92% pooled, LOPO AUC 0.8624). Fastest inference.</p>
58
  <p><strong>Geometric:</strong> Rule-based scoring using head pose and eye openness. No ML model needed β€” lightweight fallback when model checkpoints are unavailable. LOPO F1: 0.8195.</p>
 
66
  <p>The <strong>Eye Gaze</strong> button enables L2CS-Net, a deep neural network that estimates your gaze direction from the eye region. It runs alongside your selected base model and can improve focus detection accuracy.</p>
67
  <p style={{ marginTop: '8px' }}><strong>Performance note:</strong> Eye gaze tracking increases CPU usage and may reduce frame rate. If the system feels sluggish, disable it.</p>
68
  <h3 style={{ marginTop: '14px', fontSize: '1rem' }}>Calibration</h3>
69
+ <p>For best accuracy, calibrate when Eye Gaze is active:</p>
70
  <ol>
71
+ <li>Start a session, then click the <strong>"Recalibrate"</strong> button that appears in the model strip when Eye Gaze is on</li>
72
  <li>Look directly at each dot as it appears on screen β€” there are <strong>9 calibration points</strong> across the screen</li>
73
  <li>A final <strong>validation point</strong> confirms accuracy before calibration is applied</li>
74
  </ol>
75
+ <p>You can recalibrate at any time using the "Recalibrate" button, which appears in the model strip when Eye Gaze is on and a session is running.</p>
76
  </section>
77
 
78
  <section className="help-section">
 
134
  <p>The face mesh overlay updates each time the server returns a detection result. The camera feed itself renders locally. Any visible lag depends on network latency and server processing time. Reducing the frame rate slider can help if lag is noticeable.</p>
135
  </details>
136
  <details>
137
+ <summary>The Hybrid model doesn't seem to work differently from MLP β€” why?</summary>
138
+ <p>The Hybrid model blends MLP (30%) and geometric (70%) scores using a fixed weighted average. Because its geometric component dominates, it tends to be more conservative than raw MLP β€” especially when head pose or eye openness signals are borderline. It is tuned to be the most consistent across different people rather than the most aggressive.</p>
139
  </details>
140
  </section>
141
 
 
143
  <h2>Technical Info</h2>
144
  <p><strong>Face Detection:</strong> MediaPipe Face Mesh (478 landmarks)</p>
145
  <p><strong>Feature Extraction:</strong> Head pose (yaw/pitch/roll), EAR, MAR, gaze offset, PERCLOS, blink rate β€” 10 features selected via LOFO analysis</p>
146
+ <p><strong>ML Models:</strong> PyTorch MLP (10β†’64β†’32β†’2), XGBoost (600 trees), Geometric (rule-based), Hybrid (MLP 30% + Geometric 70% weighted blend)</p>
147
  <p><strong>Eye Gaze:</strong> L2CS-Net (ResNet50 backbone, trained on Gaze360) with 9-point polynomial calibration</p>
148
  <p><strong>Storage:</strong> SQLite database (sessions, events, settings)</p>
149
  <p><strong>Framework:</strong> FastAPI + React (Vite) + WebSocket</p>