Skip to content

16. Changelog

2026-11-05 — Free Vercel breed advisory API and Android client

  • Added advisory-api/, a dependency-free Vercel Node.js function (GET /api/advisory) serving all 68 breeds with origin, utility, milk yield, physical markers, market price and schemes.
  • Added dynamic female-adult, male-adult, calf, young and fallback advisory cards; calf responses include a vaccination/deworming timeline. Milk yield is hidden for males and calves; draught advice is hidden for females and calves.
  • Added en/ta/hi copy, localized breed names and scheme names, CORS *, 5-second client timeout and offline fallback.
  • Added breed-classifier-react/src/services/onlineAdvisory.js with fetchAdvisoryData(...); it never throws and degrades to an on-device-only message when offline or slow.
  • Breed Tips now opens whenever the device is online; the legacy Online Mode / backend-health gate was removed for this free API, and offline copy was updated.
  • Deployed the API to Vercel production at https://advisory-api-bharaths31s-projects.vercel.app/api/advisory and wired that URL as the default advisory endpoint in the Android client. The Breed Tips modal now renders the live advisory cards, vaccination timeline and schemes.
  • Added a browser landing page at / so the deployment base URL is no longer a 404, and disabled Vercel Authentication protection for public API access.
  • Verified with 7 API tests, a live local HTTP request, and a browser import of the Android client.

2026-11-05 — Center-focused guardrail, top-k OOD and selfie blocker

  • Center-focused Stage A: straight-edge and flat-patch checks now run on the central 180×180 region, with only a high-threshold global fallback. Barn roofs, fences, sky and other border clutter no longer reject a valid animal.
  • Human selfie gate: checkHumanSelfie() uses the browser FaceDetector when available, then a pure-JS YCbCr fallback (central skin ratio, 5×5 skin smoothness, bilateral skin symmetry, dark hair cap and eye/brow density). Close-up selfies return human_or_selfie before any breed prediction.
  • Top-k OOD: single-class Z_peak/Z_max rejection was replaced by top-3/top-5 breed-mass checks plus a relaxed entropy ceiling. Breeds that split probability across visually similar classes (Sahiwal/Gir, Hallikar/Khillari) are no longer falsely rejected. Binary confidence, energy and MSP floors remain.
  • Verification: all 34 cattle test images accepted; human selfie and monitor screenshot rejected. Live two-canvas verifyDynamicPredictions() passes, and client build/tests/i18n are green.

2026-11-05 — Direct 260×260 input, native camera, developer-mode simple UI

  • Camera/silhouette removal: Android now uses native <input type="file" accept="image/*" capture="environment"> for Take Photo and the gallery file input for Upload. The custom getUserMedia camera modal, silhouette HUD, foreground/contour guidance and Android camera optimization path were removed from the Android flow. The shared web CameraCapture silhouette overlay was also removed.
  • Direct downscale: both input paths decode once and draw directly to the 260×260 model canvas. Gallery pad/center/manual crop remains; the Auto/Train-Match downscale setting was removed.
  • Active ONNX model field removed: the home model/WASM/size/latency panel is gone.
  • Developer mode: Advanced Settings has a Developer mode switch (default off). Off = simplified home/result/settings: no ONNX/WASM, model telemetry, inference strip, rank/model-match badges, top-N Softmax spectrum, confidence percentages, scientific names or method text. On = full diagnostics, OOD/model controls and display settings. Recent scans are hidden when empty in simple mode and old scans without guardrail data are filtered from the ledger.
  • Zero-static estimator retained: the dynamic guardrail and dynamicAgeGender.js compute all values from the current 260×260 pixels and 68 logits; no breed-lookup age/gender fallback.
  • Verification: build, tests and i18n pass; simple/developer modes verified in the browser harness and on the connected Android device.

2026-11-05 — Multi-stage object/monitor guardrail and biometric age/gender

  • Stage A image gate (src/utils/imageGuardrails.js): Sobel-based axis-aligned edge/bezel detection, contiguous straight-edge spans, blue-cast / dark-bezel screen check, a 16×16 flat-block wall/background check with an adaptive strong-edge exception, and a whole-frame skin-dominance check for close-up people/selfies. Runs on the prepared 260×260 canvas before a prediction is accepted.
  • Stage B logit gate: normalized 68-class entropy, top-logit Z-score peak, binary-confidence floor, energy and MSP checks. Rejections return rejected: true with no species/breed and a localized reason.
  • Model-calibrated thresholds: the shipped lite2_v27 model’s raw thresholds from the original brief rejected every valid test cattle image. The final thresholds were calibrated against the test set (EDGE_SPAN_MAX 0.62, entropy 0.76, Z_peak 2.0, Z_max 1.6, binary floor 0.60, energy floor -3.2004). Valid cattle pass; the screen screenshot, human selfie and goat test image are rejected.
  • Biometric estimator (src/services/biometricAgeGender.js): juvenile/heavy breed logit mass, foreground bbox/torso fill/crown-texture biometrics, fused age scores, and gender from draught vs dairy breed mass + neck/hull ratio.
  • Corrected MSP: on-device MSP now uses the binary species head, matching the Python OODDetector.compute_msp, instead of the breed-head probability.
  • Result UI: a rejection card replaces the dossier for rejected images with a localized reason and retry action; rejected scans are not added to recent scans. Gender/age remain read-only.
  • Verification: 35 cattle images accepted (including the 0.4688 flat-patch cases through the adaptive strong-edge exception); the app screenshot (monitor UI), a human selfie and the goat image are rejected. Client build/tests and i18n checks pass.

2026-11-04 — Remove audio, public API base, website-style online status, main-result-only mode

  • Audio removed: the home audio-help banner, speech state/handlers, audio icon assets and audio CSS were removed from the Android app.
  • Public API base with fallback: removed the 10.0.2.2 default and LAN-IP guidance. resolveApiBase() now follows the web app contract: Settings URL → VITE_BACKEND_URL → last backend URL that passed /api/health → current web origin. Clearing the API field reuses the last successful backend, so Breed Tips keep working without re-entering the URL. Settings shows “Using last successful backend: …” and the header returns to Connected.
  • Online mode: when enabled, the app polls GET /api/health, shows Connected / Reconnecting / Offline, and enables Breed Tips only while connected. Checks repeat every 15 seconds and restart when the URL changes.
  • Prediction view: Advanced → Result Display now has Show prediction view. Turning it off hides the whole top-N spectrum and shows only the main breed result; the breed-count selector displays Main result only. The confidence toggle and 1–5 selector remain.
  • Localisation: added English/Tamil/Hindi copy for the new backend states and prediction-view option (621 keys per language).

2026-11-04 — Remove audio help, website-style online status and main-result-only mode

  • Audio removed: the home audio-help banner, speech state, speech handlers, audio icon assets and audio CSS were removed from the Android app.
  • Online mode: now follows the website connection model. When enabled, the app polls GET /api/health on the configured backend, shows Connected / Reconnecting / Offline in the header and Settings, and enables Breed Tips only while connected. Health checks repeat every 15 seconds and restart when the backend URL changes.
  • Prediction view: Advanced → Result Display now has Show prediction view. Turning it off hides the entire top-N spectrum and shows only the main breed result; the breed-count selector displays Main result only. The existing confidence toggle and 1–5 breed selector remain.
  • Localisation: added English/Tamil/Hindi copy for the new backend and prediction-view states (619 keys per language).

2026-11-04 — Result display controls, full Android localisation and SVG icons

  • Advanced settings: added Result Display controls — a Show confidence percentages toggle and a Predicted breeds to show selector (1–5, default 3). Both persist with the rest of the settings and drive the result cards, recent badges and breed spectrum.
  • Localisation: the Android dashboard, result dossier, settings page and status messages now use the existing website t() dictionary. Added a full English / Tamil / Hindi android.* key set (614 keys per language) and used tDynamic for species, gender, age and breed names.
  • Icons: replaced malformed/mixed emoji marks with a shared inline SVG set (AndroidIcons.jsx) for the cow brand mark, gallery, camera, audio, lock, trash, language, theme, online, advanced, gender, age and actions.
  • Fixes: repaired double-encoded UTF-8 in AndroidUserPage; scroll resets on stage changes; advanced settings no longer collapse; top-3 is filtered to the predicted species and respects the selected breed count.

2026-11-04 — Stitch-aligned Android UI, dossier result and in-app language picker

  • Home: two-line BreedID / LIVESTOCK VISION brand, online pill, language chip and hamburger; audio-help banner using on-device speech synthesis; stacked ingest cards with a FAST camera badge; ONNX telemetry card (model, WASM/SIMD, size, last latency, engine state); 3-column recent scans with confidence badges, scientific names and an empty state; local-inference footer.
  • Result: replaced the four compact cards with an Identification Dossier: record ID, inference strip, analysed image, species card, breed card with rank/confidence/top-3 spectrum, read-only gender and age cards, and the tips/share/identify actions. Top-3 now filters to the predicted species.
  • Settings: full-screen page with a back button and offline-ready badge; dedicated Select Language cards (English / தமிழ் / हिन्दी) wired to the existing i18n provider and saved settings; 2×2 theme cards; online mode; collapsible advanced ONNX/OOD controls; sticky Save & Return.
  • Language access: the header now exposes the active language (EN / த / हि) and opens Settings directly to the language section.
  • Scroll/focus: stage changes reset scroll; manual scroll restoration avoids the dossier opening mid-page.
  • Verification: client tests 57/57, i18n parity/script checks pass, APK builds and runs on the connected Android 16 device.
  • Result screen: gender and age are now plain read-only cards. The manual override pills and their state were removed from AndroidUserPage; share and Breed Tips use the model's detected labels.
  • Upload latency: imagePrep.loadImageFile now decodes with createImageBitmap (off-main-thread, EXIF-aware) instead of a base64 FileReader round-trip, and releases the bitmap after drawing. The Android upload preview uses an object URL (revoked on replace/unmount) rather than reading the full-resolution file a second time.
  • Feedback: the gallery button shows "Preparing image…" and is disabled while the preview is being generated.

2026-11-04 — One-shot setup for Python guardrails and the web/Android apps

  • requirements.txt: added opencv-python-headless>=4.9,<5 (stage-2 Haar cascades; OpenCV 5 dropped the bundled XMLs) and transformers>=4.40 (CLIP guardrail stage 2c and zero-shot gender/age attributes).
  • setup_venv.py: installs the Node dependencies for breed-classifier-react/ and vet-portal/ (npm ci on a fresh tree, npm install only when package.json/package-lock.json changed), builds the web bundle when needed, runs npx cap sync android, and optionally compiles the debug APK. New flags: --no-frontend, --no-android, --android-build; --check now reports frontend/Android state too. Missing Node/Android toolchains warn instead of failing the Python setup.
  • Requirements parsing: multiple version constraints per line (>=4.9,<5) are now honoured; an existing opencv-python install suppresses the headless variant to avoid clobbering cv2.
  • setup.sh / setup.ps1 / setup.bat: documented the frontend/Android stages and prerequisites (Node 22+, JDK 21, Android SDK) and forward the new flags.

2026-11-04 — Capacitor Android app, guided camera, on-device OOD

  • Single-portal Android app (AndroidUserPage): on-device ONNX Runtime Web inference, IndexedDB model/OOD persistence, hamburger Settings drawer, four themes, trilingual UI, recent scans, share/clipboard and haptics.
  • Guided camera: live getUserMedia preview with the cattle/buffalo silhouette HUD, lighting/framing/sharpness pills, torch, auto-capture, burst and a full-screen Android layout. Manual crop/fit controls are hidden on Android.
  • OOD: parses embedded ONNX model_meta (metadata_props) so the mutated *__ood model applies its own energy/MSP thresholds automatically. The bundled default model now embeds the calibrated v26 OOD profile. Non-OOD uploads are accepted but the OOD gate stays off until a manual OOD .json is uploaded. The result view no longer shows the large OOD banner.
  • Result/settings: simplified the result to four compact cards, moved the gallery downscale mode into Advanced Settings, and replaced the gear with a hamburger menu.
  • Breed Tips: added a practical-tips section tailored to species/gender/age plus offline quick tips and a fetch timeout.
  • Build: @capacitor/{core,cli,android} + onnxruntime-web, Capacitor config, Android manifest permissions, WASM copy in Vite, npm run android:assemble|android:install.

2026-11-04 — Read-only gender/age, trait-confirmation block removed

  • Big-4 is read-only: the gender and age pill toggles were removed from HeroFourGrid; the cards now show the model estimate and confidence only. ResultWorkspace no longer recalculates the advisory from manual overrides.
  • “Verify the physical traits” removed from the farmer result view, along with the now-unused TraitChecklist component and the traits visibility option. Trait values remain available in the advanced diagnostics.
  • Docs/knowledge base updated to match.

2026-11-04 — 4-layer offline input guardrails

  • New src/input_guardrails.py — InputGuardrailEngine with four stages: photorealism/drawing/screenshot, human/selfie/other-animal (OpenCV Haar + optional CLIP + extreme skin fallback), soft-routed entropy/top-1/feature norm, and the existing OODDetector. Runs before inference (stages 1–2) and after (stages 3–4); no checkpoint changes.
  • Trilingual rejection copy — rejection_info with reason_code, stage, title, message and instruction in en/ta/hi for cartoon, sketch, synthetic illustration, human/selfie, other animal, unrelated object, uncertain animal and OOD.
  • Integration — server/services/inference.py and test_model.py return rejection_info/guardrail (early rejection skips the network); BreedClassifier.predict_guarded(); /api/health and /api/guardrails/status expose engine availability; React PhotoGuidance.jsx shows the localized rejection.
  • Calibration — Stage-3 top-1 floor lowered from the spec's 0.18 to 0.08 for the current checkpoint (val top-1 ≈ 0.35); skin fallback raised to 0.92 to avoid brown-buffalo false positives. Stage 1/2/4 thresholds follow the specification.
  • Tests — tests/test_input_guardrails.py; V&V Big-4 schema test now bypasses guardrails (guardrails=False) because its synthetic scenes are intentionally non-photorealistic.

2026-11-04 — Developer model tools + vet-controlled user visibility

  • Flask model tools: POST /api/models/inspect (metadata + preprocessing + class maps + effective OOD), POST /api/models/embed (copy/in-place, optional OOD) and POST /api/models/test (single-model soft routing, top-5, OOD, repeat latency). New server/services/model_test.py avoids the advisory/CLIP path.
  • React /dev Models tab: shows Self-Describing Metadata, an Embed Metadata card (OOD + copy toggles) and a Test This Model card with repeats and top-5/latency output. Uses existing dev-card, spec-row, toggle-switch and theme tokens.
  • Vet Portal → User Page tab: controls display.hidden_user_sections plus species badge/confidence, escalation warning and technical-details flags. These display toggles moved out of the Developer Console.
  • Farmer view: language/theme toggles removed from the header and mobile sheet; section visibility is applied to photo guide, spec strip, herd session, advanced panel, dev link, escalation, footer, voice, report card, traits and advisory tabs.
  • New utilities: breed-classifier-react/src/utils/userVisibility.js, vet-portal/src/UserVisibilityPanel.jsx; DEFAULT_APP_CONFIG now includes display.hidden_user_sections.

2026-11-04 — Self-describing .pt / .onnx models (zero external JSON)

  • src/model_introspect.py: unified loading/introspection for PyTorch and ONNX. Architecture is auto-detected from tensor shapes (backbone from the first MBConv width, CBAM vs SE from the spatial conv, head widths/counts from the final layers) and from the ONNX graph (input resolution, output widths, float16 I/O). Returns a LoadedModelBundle with the correct transform, inverse class maps, embedded OOD thresholds and a Dev-Console spec summary.
  • Normalization fix: bundle.transform stops at ToTensor() when the graph already bakes ImageNet mean/std (_MobileOutputs, *_mobile* ONNX), eliminating double normalization. Caller-normalized .pt / _RawOutputs ONNX still get Normalize.
  • scripts/embed_metadata.py: --all, --file, --inspect CLI that writes class maps, preprocessing, resolution, normalization mode and OOD thresholds into existing files. .pt metadata lives in ckpt["model_meta"] (TorchScript uses an extra model_meta.json); ONNX uses metadata_props. Writes are atomic and weights are untouched.
  • Exports/training: src/export.py embeds metadata after ONNX, ONNX-INT8 and FP16 exports and into portable model.pt; src/train.py embeds it in every checkpoint and re-embeds calibrated OOD thresholds after training.
  • Runtime: test_model.py and server/ now load through LoadedModelBundle, use per-model class maps (50- and 42-class models can coexist), cast float16 ONNX inputs automatically, and prefer embedded OOD thresholds (legacy JSON is still honoured for old artifacts). Uploads are embedded + validated on arrival.
  • Robustness: legacy 7-layer binary heads (v17/v22/v9 portable bundles) are rebuilt from tensor keys; class maps are only accepted when their length matches the head width.
  • Docs: new Self-Describing Models page.

2026-11-03 — V&V suite, real-data validation, mobile UX, 4 themes, standalone Vet Portal

  • Verification & Validation suite (python -m tests.run_vv_suite): six suites covering checkpoint/tensor integrity, the Big-4 API contract, OOD/quality safety, 68-breed knowledge/advisory switching, en/ta/hi script parity, 4 theme token sets and CPU/GPU latency budgets. Writes outputs/metrics/vv_review_report.md; latest 30 PASS / 0 FAIL / 1 WARN.
  • Real buffalo validation on atharvadarpude/indian-buffalo-dataset (2,785 images, 17 breeds, downloaded via data_collection.py): 90.0 % species, 61.8 % top-5, 18.2 % top-1 (3.3× chance), 19.1 % OOD abstentions on diagram/non-animal source files (WARN).
  • Mobile-first UI: 100dvh, safe-area insets, zero horizontal overflow at 320/390/430 px, 44 px touch targets, 16 px form text, compact header with a bottom sheet, tap/camera upload well and responsive tabs/callouts/footer.
  • Bespoke 4-theme system: Field Parchment (default), Obsidian Loam, Clinical Slate, Night Telemetry. Flat surfaces, no gradients/neon/purple, Plus Jakarta Sans + JetBrains Mono + Noto Tamil/Devanagari.
  • Trilingual reactive UI (en/ta/hi) with localized breeds, traits, gender/age, advisory prose and native-script TTS voices.
  • Standalone Vet Portal moved to vet-portal/ (port 5174), launched by run.py and linked from Developer Settings; /expert removed from the farmer app.
  • Post-hoc five outputs: Big-4 + traits, farmer knowledge base, image quality gate and human OOD explainer (see dedicated docs).
  • Cleanup: removed the unrouted legacy React tree (App, SingleImage, Batch, DevTools, ResultsPanel, ModelSelect, PredictionProgress, ResultCard), turned src/traits.py into a compatibility shim over src.model, and fixed CUDA-only trait logging that could raise NameError when trait heads were disabled.

2026-10-20 — React capture & zero-crop optimizer, Advanced panel, manual OOD recalibration

  • Zero-crop 260×260 client optimizer (breed-classifier-react/src/utils/imageOptimizer.js): stretch (default) / ImageNet-mean pad / ambient-blur pad / black pad, EXIF-upright decode, progressive halving, full metadata contract. The server's Resize(260) → CenterCrop(260) is now an identity pass-through.
  • Full-frame camera with a 1:1-aware guide, on-device quality gate, auto-capture (3 stable checks ≈ 750 ms), burst-3 sharpest frame, torch, Pad/Stretch review and optional manual framing; it hands off an optimized 260×260 file plus the original name.
  • Authoritative framing cropper with pan on both axes (clampPan clamps the target box against the source, not the viewport) and 1–3× zoom.
  • Minimal user view (original image only) with an explicit Identify / Identify Framed Breed action; new upload, crop or fit change clears stale results.
  • Header Advanced panel: model-input/original preview, the ORIGINAL / MODEL INPUT / SCALED / PADDING / PAYLOAD / TIME grid, four fit modes and a batch tester that uses the selected model and fit configuration.
  • 3-tier confidence UI (verified / look-alike / safety abstention) with the exact OOD trigger, and a Why this breed? trait matrix for 44 curated breeds.
  • Herd session log (localStorage) with statistics and CSV/JSON/ODT export; shareable PNG result card; original filename carried through predictions, exports and logs.
  • Manual OOD recalibration from a breed-named folder: new src/ood_calibration.py, GET /api/ood/data-dirs and POST /api/ood/recalibrate in both backends, React DevOODPanel controls and the test_model.py OOD tools card.
  • Fixes: test_model.py::ModelManager.ood_info no longer reads non-existent OODDetector.source/.calibrated_at/.percentile; api.predict now surfaces server errors instead of a JSON parse exception; share-card footer prints the file name.
  • Docs/knowledge: new Web Application page, REPORTS.md project report, requirements.txt now covers the Flask/Socket.IO serving stack, and the knowledge base, graph and README are refreshed.

2026-10-06 — Source consolidation: src/ reduced from 16 files to 10

Modules performing the same job were merged into cohesive homes (old import paths removed and all references updated across src/, scripts/, server/, test_model.py, colab/). config.py stays separate as the single source of truth. Every consolidated/kept module now opens with a SECTION MAP and REFERENCES header, and the large merged modules carry inline section banners.

Former Now Section
efficientnet_lite.py model.py §1 Backbone
cbam.py model.py §2 Attention
traits.py model.py §5 Trait aux heads
ood_detector.py model.py §6 OOD detector
metrics.py train.py §7 Evaluation metrics
run_logger.py run_utils.py §2 Execution logger

Final src/: config.py, run_utils.py, data_pipeline.py, model.py, train.py, export.py, evaluate.py, parity_check.py, verify.py, __init__.py. All python -m src.<module> entry points are unchanged; the CPU suites (61 + 35 checks) and python -m src.verify still pass.

2026-09-24 — Breed trait heads, group-aware splits, hard negatives, tooling

  • Breed trait auxiliary heads (D2, opt-in --trait-weight, suggested 0.1): new src/traits.py + scripts/make_trait_template.py generating data/breed_traits.json (75 breeds × hump/horn/coat/ear/dewlap/face/size, all empty for you to fill). Trait classifiers run on the pooled 1280-d features, masked loss ignores empty traits, and per-trait val accuracy is logged each eval. Training-only; excluded from export (verified).
  • Group-aware splits (E1, --dedup-splits): dHash (no external deps) groups near-duplicates (Hamming ≤ DEDUP_HAMMING=4) within each breed so twins never cross train/val/test; hashes cached in data/splits/hashes.csv; long-tail minimums preserved.
  • Val-noise warning (E3): prepare_splits prints min/median val images per breed and warns when any breed has < VAL_MIN_WARN (2).
  • SupCon hard negatives (D4): supervised_contrastive_loss up-weights confused-pair negatives (CONTRASTIVE_HARD_NEG_WEIGHT=2.0); --hard-pairs loads confusion_pairs.json produced by the new scripts/mine_confusions.py.
  • Per-group LR logging (F4): train_phase prints + logs each optimizer group's LR and parameter count at phase start.
  • Tooling (G2/G4): scripts/mine_confusions.py (top-30 confused breed pairs as global ids), scripts/run_ablations.sh / .ps1 (quarter-data sweep R0→R7, --dry-run supported), plus scripts/view_logs.py.
  • Cosine/ArcFace breed heads (D3, --cosine-head): CosineHead replaces the final Linear on cattle/buffalo heads. Forward is margin-free scaled cosine (scale=30), so inference/export are unchanged; the additive angular margin (COSINE_MARGIN=0.3) is ramped over the first COSINE_MARGIN_RAMP_EPOCHS=10 phase-2 epochs and applied to the target class inside the loss. export and test_model.py auto-detect cosine checkpoints (final head layer has no bias) and build the matching model.
  • Tests: scripts/test_master_cpu.py now 61 checks (traits, dedup, hard negatives, cosine head, logger, mixing, EMA, metrics, transforms, CBAM identity).
  • Still pending: the hard-pair batch sampler (D5); --hard-pairs only up-weights SupCon negatives for now.

2026-09-24 — Unified execution logger (logs/<exec_id>/) + ema_decay fix

  • Fixed NameError: ema_decay in train_phase (the EMA-diagnostics block referenced ema_decay without declaring it — crashed phase 2). Added the parameter and forwarded it to run_epoch. Verified on CPU with an EMA model; pyflakes now reports no undefined names across src/, local_train.py, test_model.py.
  • New src/run_logger.py: per-execution logger writing logs/<exec_id>/{manifest.json,config.json,run.log,events.jsonl,actions.jsonl, training.jsonl,data.jsonl,test.jsonl,export.jsonl}. exec_id = YYYYmmdd-HHMMSS-<4hex>, override with --exec-id / RUN_EXEC_ID.
  • Auto-start: sitecustomize.py initialises the logger for any Python run inside the project (use PYTHONPATH=.), and every entry point (src.train, local_train.py, test_model.py, src.export, src.evaluate, src/parity_check, src.verify) calls init_run_logger(). Stdout/stderr are tee'd into run.log (tqdm \r frames collapsed).
  • Instrumentation: training logs the plan + per-epoch raw/EMA metrics + checkpoint saves; local_train.py logs stages, subprocess commands and the dataset inventory; data_pipeline logs split summaries; test_model.py logs one record per prediction; export/evaluate/parity log artifacts and verdicts.
  • New scripts/view_logs.py: list / summary / metrics / events / diff over the log folders (read-only).
  • logs/ added to .gitignore.

2026-09-24 — Blocking bug fixes, flip+RRC on by default, dataset inventory

  • Missing src/run_utils.py crash fixed: the file is now present and local_train.py preflights every required src/ module, raising a clear "sync these files" error instead of ModuleNotFoundError.
  • SAMPLER_BETA import added to src/train.py (it was used in main() but never imported → would crash).
  • Mixing gate fixed: run_epoch gated mixing on desc.startswith("train"), which never matched phase{n} e{i}/{N}, so --mix was dead code. Replaced with an explicit training=True flag and a mix_stats counter; the eval log now prints mix=on (n/N batches) only when mixing actually ran.
  • find_latest_checkpoint pattern fixed to match timestamped names (lite2_phase2_best_V3.pt), and resolve_checkpoint(path_or_tag) added so --run-tag V3 works with --checkpoint in export/evaluate/parity_check.
  • Class-count fail-fast: prepare_* now raises with explicit extra/missing breed names and prints names shared across species (bargur).
  • Flip + mild RRC ON by default (AUG_HORIZONTAL_FLIP, AUG_RANDOM_RESIZED_CROP) — they don't mix content between breeds. Colour jitter / RandAugment / mix stay off. --no-augment forces all off.
  • RRC ratio tightened to (0.92, 1.08) and hue capped at 0.02 (--allow-hue to raise) so body proportions and coat colour survive.
  • Breed-aware augmentation: BREED_AUG_POLICY / COAT_COLOUR_BREEDS (coat-colour breeds skip colour jitter), applied per sample in the Dataset.
  • Pad-to-square (--pad-to-square): resize long side + pad to square, keeping full-body side profiles.
  • CBAM/SE identity at init (CBAM_IDENTITY_INIT): zero-init final layers with x*(1+gate), so attention no longer distorts pretrained features during phase-1 warmup.
  • Metrics: combined_top3/5 are now species-aware over the soft-routed 75-way scores; the old true-species versions are combined_top3_oracle / combined_top5_oracle. Added val_min_per_breed / val_median_per_breed and fixed the pred_hist_entropy comment.
  • Dataset inventory: build_dataset_inventory() in local_train.py and the Colab downloader writes data/dataset_inventory/<dataset>.json with breed, species (cattle/buffalo), count, and per-image resolution for both datasets and the merged tree.
  • Tests: scripts/test_master_cpu.py (40 CPU-only synthetic checks).

2026-09-23 — Tail-bias regression fix: single imbalance mechanism, safe mixing, EMA, soft routing, timestamped outputs

Diagnosed from a 10-photo ONNX batch test (0/10 correct; top-5 dominated by rare breeds; a Gir bull predicted as a rare breed) that regressed after the 2026-09-21 overhaul.

Imbalance (src/config.py, src/data_pipeline.py, src/train.py): - LOGIT_ADJUST is now False by default — the effective-number sampler is the single long-tail mechanism. Enabling both double-corrects and over-predicts rare breeds at inference. --logit-adjust re-enables it; its prior is computed from the effective sampled distribution (--logit-adjust-prior sampled|raw), never raw counts. Startup prints the active mechanism and prior max/min ratio. - Fixed _effective_num_weights divide-by-zero for classes absent from train (previously produced NaN logit-adjustment priors).

Mixing (src/config.py, src/data_pipeline.py, src/train.py): - CutMix/MixUp now pair within the same species (_pairing_perm), so binary labels stay one-hot and breed targets stay proper distributions. - Strength reduced: CUTMIX_MIXUP_PROB 0.5→0.25, CUTMIX_ALPHA 1.0→0.4, MIXUP_ALPHA 0.3→0.2. Rare-class guard kept. - MIX_OFF_LAST_FRAC=0.15 disables mixing for the last 15% of phase 2.

Augmentation OFF by default (src/config.py, src/data_pipeline.py): - flip / ColorJitter / RandAugment / RandomResizedCrop / mixing are opt-in via --mix --flip --color-jitter --randaugment --rrc --augment-all. With all off, the train transform equals the eval transform.

EMA (src/train.py): - Updated once per optimizer step (was twice per micro-batch with grad_accum=2), with warm-up decay_t = min(0.999,(1+t)/(10+t)). - Startup prints steps/epoch, total optimizer steps and the EMA time constant, warning if it exceeds 25% of phase-2 steps. Every eval logs BOTH raw and EMA metrics and saves whichever scores better.

Checkpoint selection (src/config.py, src/metrics.py): - BEST_METRIC="blended_score" = 0.5·macro-F1 + 0.5·soft-routed top-1. - Each eval also logs few/medium/many-shot accuracy and predicted-histogram entropy.

Preprocessing (test_model.py, src/config.py): - test_model.py now uses the eval transform (shortest-side resize + CenterCrop) instead of a square Resize((260,260)). New EVAL_MATCH_TRAIN_RESOLUTION to test Resize(288)+CenterCrop(260) at eval.

Timestamped outputs (src/run_utils.py, new): - Checkpoints/exports/metrics carry a run id (DD-MM-YYYY-HH-MM or --run-tag) and never overwrite previous runs. src.export / src.evaluate / src.parity_check / local_train.py auto-discover the newest checkpoint.

Tooling (run-later): scripts/audit_data.py, scripts/diagnose_model.py, scripts/onnx_parity_10.py, scripts/test_fixes_cpu.py.


2026-09-21 — Long-Tail Accuracy Overhaul: Logit Adjustment, Feature Metric Learning, Soft Routing, 70/15/15 Splits

Motivated by a graph-assisted gap analysis: phase-2 best val top-1 was 0.578 (binary 0.95, cattle 0.59, buffalo 0.61), with 21+ indigenous cattle breeds carrying ≤14 images against gir=768, and the old 85/10/5 split leaving many rare breeds with zero test images.

Config (src/config.py): - SAMPLER_BETA 0.999 → 0.99 (softer effective-number oversampling). - Split ratios 70/15/15 (was 85/10/5); PHASE2_EPOCHS 60 → 80. - New: LOGIT_ADJUST/LOGIT_ADJUST_TAU, CONTRASTIVE_WEIGHT/CONTRASTIVE_TEMPERATURE, RARE_CLASS_THRESHOLD, PROJECTION_DIM, BEST_METRIC="balanced_score", and saturated-binary loss weights (*_FINAL, BINARY_SATURATION_ACC).

Imbalance (src/data_pipeline.py, src/train.py): - Logit adjustment (Menon et al., ICLR 2021): tau·log(prior) added to breed logits during training only, from smoothed train priors (compute_class_priors). - Long-tail split minimums: every (species, breed) with ≥3 images now gets ≥1 val and ≥1 test image; fixed grouping to key on (species, breed) because bargur exists under both species. - Rare-class mixing guard: breeds below RARE_CLASS_THRESHOLD (30 train images) are excluded from CutMix/MixUp (compute_rare_classes + keep mask). - Adaptive loss weights: once binary_acc ≥ 0.95, weights switch from 0.15/0.50/0.35 to 0.05/0.55/0.40 to reallocate budget to the breed heads. - --no-mix now actually disables mixing (previously it only swapped the collate fn).

Feature learning (src/model.py, src/train.py): - New auxiliary projection head + supervised contrastive (SupCon) loss on the previously-unused pooled features (CONTRASTIVE_WEIGHT=0.2); skipped on mixed batches where labels are soft. Adds a dedicated optimizer param group.

Metrics & selection (src/metrics.py): - evaluate_epoch now reports per-head macro-F1, balanced accuracy, and soft-routed combined top-1 (p(species)·softmax(head)). - Checkpoints are selected on balanced_score = ½(cattle_macro_f1 + buffalo_macro_f1) instead of combined top-1, which was dominated by ~10 large breeds.

Soft routing: BreedClassifier.predict(), test_model.py, and both Flutter engines (android_tflite_engine.dart, web_tfjs_engine.dart) now mix p(species)·softmax(head) across all 75 breeds instead of hard binary argmax, removing two-stage routing error propagation.

Export (src/export.py, local_train.py): - _sanitize_state_dict strips _orig_mod./module. prefixes and QAT fake_quant/activation_post_process/fused-BN keys, so phase-3/QAT and compiled checkpoints no longer crash ONNX/FP16/INT8 export. - local_train.py prefers the phase-2 (EMA) checkpoint and exports INT8 via the converter-side PTQ path (--mode onnx-int8), not the removed --mode int8.


2026-09-20 — Accuracy/Efficiency Overhaul: Distillation, EMA Fix, Mobile INT8 Exports

Training (src/train.py): - Fixed a critical EMA bug: the EMA now updates BatchNorm buffers (running mean/var) in addition to parameters; num_batches_tracked is hard-copied. Previously every phase-2 checkpoint exported stale BN stats, silently degrading validation accuracy and the deployed model. - Knowledge distillation: new --teacher, --teacher-backbone, --teacher-attention flags. Loss = (1-α)·masked_hard_CE + α·T²·masked_KL(teacher‖student) on all three heads (α=0.7, T=4.0). Train a lite4 teacher, distill into the unchanged lite2 student — teacher accuracy at zero on-device cost. - QAT is now opt-in (--include-qat); default training is a clean 2-phase run. QAT uses per-tensor observers (fixes the Unsupported qscheme: per_channel_affine conversion failure) and starts from the best phase-2 EMA checkpoint instead of final-epoch weights. - Binary-head species balancing: per-batch re-weighting (BALANCE_BINARY_HEAD=True) neutralizes the 57:18 breed-count species prior.

Data (src/data_pipeline.py): - Sampler switched to effective-number-of-samples weighting (SAMPLER_BETA=0.999) — softens over-oversampling of 5-image breeds. - CutMix/MixUp probability raised 0.25 → 0.5 (CUTMIX_MIXUP_PROB). - All prepare_*_splits() create the split directory if missing.

Export (src/export.py): - New --mode tflite: ONNX → onnx2tf → TFLite FP32 + full-integer INT8 PTQ (float32 [0,1] I/O), emits labels_*.txt, auto-copies into flutter_app/assets/models/. - New --mode onnx-int8: QDQ static quantization for ONNX Runtime Mobile (per-channel weights, calibrated on real train images). - ImageNet normalization is baked into mobile graphs — the Flutter app's pixel/255 preprocessing is now exactly correct with zero app changes. - Removed the broken x86 PTQ --mode int8 path (guidance stub remains).

Verification (src/parity_check.py, new): - python -m src.parity_check compares fp32 PyTorch vs TFLite/ONNX INT8 on val/test (training-equivalent metrics) or --synthetic N for artifact-only parity. Gate: INT8 within 1 pt combined_top1 of fp32.

Measured: ONNX INT8 = 7.10 MB (from 25.65 MB FP32); fp32 ONNX exact parity; INT8 max |Δlogit| ≈ 0.05–0.07 on random inputs; end-to-end smoke training (2-phase, distillation, QAT→INT8) verified on CPU.


2026-09-15 — Architecture Improvements, EMA, & ImageNet Normalization Fix

Architecture: - Deepened cattle_head and buffalo_head with an extra hidden layer (BREED_DIM // 2). - Integrated BatchNorm1d into both breed classification heads for better convergence and to prevent covariate shift. - Adjusted dropout values for the new layers (0.3 for first, 0.2 for second).

Training Pipeline: - Phase 1 (Warmup): Now trains all three heads (binary, cattle, buffalo) to build robust initial representations, instead of just the binary head. - Differential Learning Rates (Phase 2): Applied fine-grained LR scaling (backbone: 0.1x, attention: 0.5x, heads: 1.0x). - EMA: Integrated Exponential Moving Average (EMA) with a decay of 0.999 for model weights during Phase 2 to drastically improve evaluation stability and generalization. - Bug Fix: Fixed critical bug where the training pipeline lacked transforms.Normalize() using ImageNet statistics, aligning it properly with inference logic. - Reconfigured default portable export to securely capture the best weights from Phase 2 instead of Phase 3, avoiding the massive accuracy drop previously caused by aggressive INT8 QAT, while maintaining a very lightweight model footprint (~27.3 MB) ready for Android deployment.

Metrics: - Added vectorized Top-5 combined accuracy tracking to evaluate_epoch() alongside Top-1 and Top-3.


2026-09-15 — Presenter Mode, Logging & Advanced Image Metadata

Model Tester GUI (test_model.py): - Added --dev (default) and --present flag modes. - Developer Mode (--dev): Advanced view showing image metadata (EXIF, size, proportion), cattle/buffalo JSON data, model specifications, and options to edit the presenter's view settings. - Presenter Mode (--present): Clean, minimalist test page that hides detailed technical stats, diminishes confidence metrics, and removes the export option for a cleaner presentation. - Presenter configurations (like branding, section toggles, and confidence modes) are saved and loaded persistently via outputs/logs/presenter_config.json. - Comprehensive session logging captures all actions (start/stop, model/image selection, prediction results, reasoning) into a separate log file in outputs/logs/.


2026-09-13 — Colab Testing Notebook & Large-Scale Evaluation

Testing & Evaluation: - Added colab/cattle_buffalo_tester.py and colab/cattle_buffalo_tester.ipynb for automated evaluation of exported models on Google Colab. - Added comprehensive HTML report generation for single images and batch evaluations. - Added Large-Scale Kaggle Evaluation mode to automatically download the dataset and test all images. - Added create_test_eval_zip.py script to easily bundle test dataset splits for Colab. - Updated documentation and knowledge base (CONTEXT.md, README.md, docs/) with testing workflow details.


2026-09-08 — Fix: torch.compile on Windows

Bug Fix: - Fixed BackendCompilerFailed: Cannot find a working triton installation error that crashed phase 2 training on Windows. - Added OS detection in src/train.py to automatically disable torch.compile (fallback to eager mode) when running on Windows.


2026-09-07 — Unified Kaggle Dataset & Colab Trainer Update

Dataset Pipeline & Colab Notebook: - Updated dataset download source to unified Kaggle dataset algsoch/breed-cattle-buffalo containing pre-structured cattle/ (57 breeds) and buffalo/ (18 breeds) subdirectories. - Simplified Kaggle download logic in colab/cattle_buffalo_trainer.py to extract directly into data/raw/, eliminating redundant file moving operations and outdated inline comments. - Regenerated colab/cattle_buffalo_trainer.ipynb from updated python script. - Updated project documentation across README.md, docs/, and knowledge base.


2026-09-06 — Hotfix: CUDA total_mem AttributeError

Bug Fix: - Fixed AttributeError: 'torch._C._CudaDeviceProperties' object has no attribute 'total_mem' that crashed §6 Training on Colab T4 - Root cause: PyTorch uses total_memory, not total_mem - Fixed in src/train.py (setup_device()) and both occurrences in colab/cattle_buffalo_trainer.py - Regenerated colab/cattle_buffalo_trainer.ipynb from fixed .py

Documentation: - Added Colab gotchas table to CONTEXT.md §14 covering: total_mem bug, GitHub clone cache issue, runtime restart behaviour - Added rm -rf /content/project before git clone in §14 best practices to ensure latest code is always used


2026-09-06 — Colab + SOTA Hyperparameters + Android QAT

Colab Training: - Created colab/ directory with full training notebook - 3 project setup options: GitHub clone, zip upload, Google Drive - 3 dataset options: Kaggle API, archive upload, Google Drive - Hyperparameter configuration cell with all tunable parameters - Image prediction cell for testing with uploaded images - Export & download: portable bundle + ONNX + INT8 - GPU memory monitor cell

SOTA Hyperparameters: - Switched from Adam → AdamW (weight_decay=1e-2) - Added label smoothing (0.1) to soft cross-entropy - Added linear warmup scheduler (3 epochs) before cosine annealing - Added gradient accumulation (2 steps, effective batch=128) - Increased batch size 32 → 64 - Optimized split ratio 80/10/10 → 85/10/5 - Phase 2 epochs 30 → 40, LR 1e-4 → 2e-4 - Phase 1 LR 1e-3 → 3e-3 - Phase 3 LR 1e-5 → 5e-6 - Dropout 0.3 → 0.4

Data Pipeline: - Train augmentation: added RandomResizedCrop, RandomHorizontalFlip, ColorJitter - Added prefetch_factor=4 to all DataLoaders

Android Deployment: - QAT (Phase 3) enabled by default (not skipped) - Auto INT8 conversion after QAT - ONNX export in Colab notebook for mobile deployment

2026-09-05 — Major Update

Training: - Added CUDA optimization: cudnn.benchmark, TF32, AMP (torch.amp), GradScaler - Added gradient clipping (max_norm=1.0) - Enabled pin_memory, persistent_workers, non_blocking transfers - Smoke test now uses real mini-dataset (5 imgs/breed) instead of 2-batch limit - Auto portable export after training completes - Better tqdm progress bars throughout

Export & Standalone Packaging: - Added portable mode: self-contained folder with model + labels + metadata - Improved progress bars on INT8 calibration - Created create_training_zip.py script to generate a clean, standalone training zip package - Added .gitignore configured to track memory/ while ignoring .venv/, outputs/, data/splits/, *.zip, cache files

Config: - Added PORTABLE_EXPORT_DIR, SMOKE_SAMPLES_PER_BREED constants