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.jswithfetchAdvisoryData(...); 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/advisoryand 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 browserFaceDetectorwhen 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 returnhuman_or_selfiebefore any breed prediction. - Top-k OOD: single-class
Z_peak/Z_maxrejection 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 webCameraCapturesilhouette overlay was also removed. - Direct downscale: both input paths decode once and draw directly to the
260×260model 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.jscompute all values from the current260×260pixels 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: truewith no species/breed and a localized reason. - Model-calibrated thresholds: the shipped
lite2_v27model’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, entropy0.76,Z_peak 2.0,Z_max 1.6, binary floor0.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.4688flat-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.2default 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/healthon 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 / Hindiandroid.*key set (614 keys per language) and usedtDynamicfor 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 VISIONbrand, 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.
2026-11-04 — Read-only gender/age and faster gallery uploads
- 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.loadImageFilenow decodes withcreateImageBitmap(off-main-thread, EXIF-aware) instead of a base64FileReaderround-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: addedopencv-python-headless>=4.9,<5(stage-2 Haar cascades; OpenCV 5 dropped the bundled XMLs) andtransformers>=4.40(CLIP guardrail stage 2c and zero-shot gender/age attributes).setup_venv.py: installs the Node dependencies forbreed-classifier-react/andvet-portal/(npm cion a fresh tree,npm installonly whenpackage.json/package-lock.jsonchanged), builds the web bundle when needed, runsnpx cap sync android, and optionally compiles the debug APK. New flags:--no-frontend,--no-android,--android-build;--checknow 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 existingopencv-pythoninstall suppresses the headless variant to avoid clobberingcv2. 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
getUserMediapreview 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*__oodmodel 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.jsonis 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.ResultWorkspaceno longer recalculates the advisory from manual overrides. - “Verify the physical traits” removed from the farmer result view, along
with the now-unused
TraitChecklistcomponent and thetraitsvisibility 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—InputGuardrailEnginewith 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 existingOODDetector. Runs before inference (stages 1–2) and after (stages 3–4); no checkpoint changes. - Trilingual rejection copy —
rejection_infowithreason_code,stage,title,messageandinstructionin en/ta/hi for cartoon, sketch, synthetic illustration, human/selfie, other animal, unrelated object, uncertain animal and OOD. - Integration —
server/services/inference.pyandtest_model.pyreturnrejection_info/guardrail(early rejection skips the network);BreedClassifier.predict_guarded();/api/healthand/api/guardrails/statusexpose engine availability; ReactPhotoGuidance.jsxshows 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) andPOST /api/models/test(single-model soft routing, top-5, OOD, repeat latency). Newserver/services/model_test.pyavoids the advisory/CLIP path. - React
/devModels tab: showsSelf-Describing Metadata, an Embed Metadata card (OOD + copy toggles) and a Test This Model card with repeats and top-5/latency output. Uses existingdev-card,spec-row,toggle-switchand theme tokens. - Vet Portal → User Page tab: controls
display.hidden_user_sectionsplus 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_CONFIGnow includesdisplay.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,float16I/O). Returns aLoadedModelBundlewith the correct transform, inverse class maps, embedded OOD thresholds and a Dev-Console spec summary.- Normalization fix:
bundle.transformstops atToTensor()when the graph already bakes ImageNet mean/std (_MobileOutputs,*_mobile*ONNX), eliminating double normalization. Caller-normalized.pt/_RawOutputsONNX still getNormalize. scripts/embed_metadata.py:--all,--file,--inspectCLI that writes class maps, preprocessing, resolution, normalization mode and OOD thresholds into existing files..ptmetadata lives inckpt["model_meta"](TorchScript uses an extramodel_meta.json); ONNX usesmetadata_props. Writes are atomic and weights are untouched.- Exports/training:
src/export.pyembeds metadata after ONNX, ONNX-INT8 and FP16 exports and into portablemodel.pt;src/train.pyembeds it in every checkpoint and re-embeds calibrated OOD thresholds after training. - Runtime:
test_model.pyandserver/now load throughLoadedModelBundle, use per-model class maps (50- and 42-class models can coexist), castfloat16ONNX 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. Writesoutputs/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 viadata_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 byrun.pyand linked from Developer Settings;/expertremoved 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), turnedsrc/traits.pyinto a compatibility shim oversrc.model, and fixed CUDA-only trait logging that could raiseNameErrorwhen 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'sResize(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 (
clampPanclamps 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 / TIMEgrid, 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-dirsandPOST /api/ood/recalibratein both backends, ReactDevOODPanelcontrols and thetest_model.pyOOD tools card. - Fixes:
test_model.py::ModelManager.ood_infono longer reads non-existentOODDetector.source/.calibrated_at/.percentile;api.predictnow surfaces server errors instead of a JSON parse exception; share-card footer prints the file name. - Docs/knowledge: new Web Application page,
REPORTS.mdproject report,requirements.txtnow 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): newsrc/traits.py+scripts/make_trait_template.pygeneratingdata/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 indata/splits/hashes.csv; long-tail minimums preserved. - Val-noise warning (E3):
prepare_splitsprints min/median val images per breed and warns when any breed has <VAL_MIN_WARN(2). - SupCon hard negatives (D4):
supervised_contrastive_lossup-weights confused-pair negatives (CONTRASTIVE_HARD_NEG_WEIGHT=2.0);--hard-pairsloadsconfusion_pairs.jsonproduced by the newscripts/mine_confusions.py. - Per-group LR logging (F4):
train_phaseprints + 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-runsupported), plusscripts/view_logs.py. - Cosine/ArcFace breed heads (D3,
--cosine-head):CosineHeadreplaces 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 firstCOSINE_MARGIN_RAMP_EPOCHS=10phase-2 epochs and applied to the target class inside the loss.exportandtest_model.pyauto-detect cosine checkpoints (final head layer has no bias) and build the matching model. - Tests:
scripts/test_master_cpu.pynow 61 checks (traits, dedup, hard negatives, cosine head, logger, mixing, EMA, metrics, transforms, CBAM identity). - Still pending: the hard-pair batch sampler (D5);
--hard-pairsonly up-weights SupCon negatives for now.
2026-09-24 — Unified execution logger (logs/<exec_id>/) + ema_decay fix
- Fixed
NameError: ema_decayintrain_phase(the EMA-diagnostics block referencedema_decaywithout declaring it — crashed phase 2). Added the parameter and forwarded it torun_epoch. Verified on CPU with an EMA model;pyflakesnow reports no undefined names acrosssrc/,local_train.py,test_model.py. - New
src/run_logger.py: per-execution logger writinglogs/<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.pyinitialises the logger for any Python run inside the project (usePYTHONPATH=.), and every entry point (src.train,local_train.py,test_model.py,src.export,src.evaluate,src/parity_check,src.verify) callsinit_run_logger(). Stdout/stderr are tee'd intorun.log(tqdm\rframes collapsed). - Instrumentation: training logs the plan + per-epoch raw/EMA metrics +
checkpoint saves;
local_train.pylogs stages, subprocess commands and the dataset inventory;data_pipelinelogs split summaries;test_model.pylogs one record per prediction; export/evaluate/parity log artifacts and verdicts. - New
scripts/view_logs.py:list/summary/metrics/events/diffover 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.pycrash fixed: the file is now present andlocal_train.pypreflights every requiredsrc/module, raising a clear "sync these files" error instead ofModuleNotFoundError. SAMPLER_BETAimport added tosrc/train.py(it was used inmain()but never imported → would crash).- Mixing gate fixed:
run_epochgated mixing ondesc.startswith("train"), which never matchedphase{n} e{i}/{N}, so--mixwas dead code. Replaced with an explicittraining=Trueflag and amix_statscounter; the eval log now printsmix=on (n/N batches)only when mixing actually ran. find_latest_checkpointpattern fixed to match timestamped names (lite2_phase2_best_V3.pt), andresolve_checkpoint(path_or_tag)added so--run-tag V3works with--checkpointin 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-augmentforces all off. - RRC ratio tightened to
(0.92, 1.08)and hue capped at 0.02 (--allow-hueto 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 withx*(1+gate), so attention no longer distorts pretrained features during phase-1 warmup. - Metrics:
combined_top3/5are now species-aware over the soft-routed 75-way scores; the old true-species versions arecombined_top3_oracle/combined_top5_oracle. Addedval_min_per_breed/val_median_per_breedand fixed thepred_hist_entropycomment. - Dataset inventory:
build_dataset_inventory()inlocal_train.pyand the Colab downloader writesdata/dataset_inventory/<dataset>.jsonwith 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