Occasion-Scanner / documentation.md
ganemmah's picture
Update documentation.md
0d2a936 verified
|
Raw
History Blame Contribute Delete
34.1 kB
# AI Applications Project Documentation Template
Use this template to document your project concisely and completely.
Fill in all required fields. Keep answers short and precise.
## Documentation Hint
Important:
When possible, reference the corresponding code location directly in your description.
## Project Metadata
- Project title: Occasion-Scanner: Multimodal AI Assistant for Used Car Valuation and Listing Generation
- Student: Mahasen Gane Mudiyanselage (ganemmah)
- GitHub repository URL: <https://github.com/ganemmah/Occasion-Scanner>
- Deployment URL: <https://huggingface.co/spaces/ganemmah/Occasion-Scanner>
- Submission date: 07 June 2026
### Mandatory Setup Checks
- [x] At least 2 blocks selected
- [x] Multiple and different data sources used
- [x] Deployment URL provided
- [x] Required GitHub users added to repository (`jasminh`, `bkuehnis`)
## Selected AI Blocks
- [x] ML Numeric Data
- [x] NLP
- [x] Computer Vision
Primary blocks used for core solution:
- Primary block 1: ML Numeric Data
- Primary block 2: Computer Vision
The third block, NLP, is implemented as additional graded work and is part of the deployed application.
---
## 1. Project Foundation (Short)
### 1.1 Problem Definition
- Problem statement: Private sellers often do not know how to price a used car realistically, especially when visible damage may reduce buyer trust and market value. They also need an honest but attractive listing text for online marketplaces.
- Goal: Build a deployed AI application that combines structured vehicle data, uploaded vehicle images, and text generation to recommend a Swiss used-car listing price and generate an honest listing.
- Success criteria: The system must train and compare multiple price models, analyse uploaded car images with OpenAI Vision and reproducible locally trained CV models, convert visual findings into a transparent damage adjustment, generate a listing and explanation, and run publicly on Hugging Face Spaces.
### 1.2 Integration Logic
- How the selected blocks interact: The ML model predicts the base market price from structured vehicle data. The Computer Vision component analyses uploaded front, side, rear, and interior images, identifies visible damage, and produces a `damage_score`. The NLP component uses the ML price, CV findings, damage discount, user notes, and Swiss-calibrated CHF price to generate the explanation and listing text.
- Data and output flow between blocks:
```text
Vehicle attributes → ML price model → EUR base price
EUR base price → Swiss calibration → CHF base market price
Vehicle images → OpenAI Vision or local CV models → damage labels, confidence, evidence, quality
Damage labels → transparent damage_score → CHF damage discount
CHF base price + damage discount → adjusted CHF listing price range
ML + CV + user notes → Seller Cockpit + NLP templates / GPT → condition grade, negotiation band, listing text, explanation
```
Core code references:
- Pipeline: [`app/integration.py`](app/integration.py)
- ML model: [`app/price_model.py`](app/price_model.py)
- CV analysis: [`app/damage_model.py`](app/damage_model.py)
- NLP generation: [`app/nlp_generator.py`](app/nlp_generator.py)
- Gradio interface: [`app/app.py`](app/app.py)
---
## 2. Block Documentation
### 2A. ML Numeric Data
#### 2A.1 Data Source(s)
List every usage of a data source as a separate entry. If the same source is used twice for different roles, add it twice.
| Entry | Source name or link | Type | Size | Role in this block |
| --- | --- | --- | --- | --- |
| 1 | AutoScout24 Car Listings Dataset, Kaggle: <https://www.kaggle.com/datasets/clkmuhammed/autoscout24-car-listings-dataset> | Structured tabular used-car listings | 118,382 raw listings | Main training and EDA source for price prediction |
| 2 | AutoScout24 Car Listings Dataset, Zenodo mirror: <https://zenodo.org/records/17643343> | Structured tabular used-car listings | ~120k listings | Reproducible source reference and licence check |
| 3 | User input in deployed app | Structured vehicle form fields | One vehicle per prediction | Inference input for the trained price model |
#### 2A.2 Preprocessing and Features
- Cleaning steps: Removed duplicate listings, unrealistic prices (outside EUR 500–300,000), invalid mileage values, invalid production years (outside 1990–2026), and rows without essential target or feature values. EDA kept 116,795 cleaned rows from 118,382 raw listings.
- Preprocessing steps: Standardised column names, converted production year into `vehicle_age`, normalised mileage and horsepower, encoded categorical features, used a reusable scikit-learn pipeline with `TransformedTargetRegressor` (log transform on target). Implemented in [`app/price_features.py`](app/price_features.py).
- Feature engineering and selection: Engineered features include `vehicle_age`, `mileage_per_year`, `is_premium_brand`, `is_electric`, `has_warranty`, `seller_is_dealer`, `equipment_count`, `description_length`, `had_accident`, service-history flags, ownership count, and `damage_score`. Rare makes/models are grouped into "Other" using training-set-only frequency thresholds to prevent data leakage. See [`app/price_features.py`, lines 71–101](app/price_features.py#L71-L101).
#### 2A.3 Model Selection
- Models tested: Ridge Regression, Random Forest Regressor, and HistGradientBoostingRegressor.
- Why these models were chosen: Ridge provides an interpretable linear baseline with log-transformed target. Random Forest handles nonlinear relationships and categorical interactions well. HistGradientBoosting provides a strong boosted-tree comparison without external dependencies (all three use scikit-learn only).
#### 2A.4 Model Comparison and Iterations
| Iteration | Objective | Key changes | Models used | Main metric | Change vs previous |
| --- | --- | --- | --- | --- | --- |
| 1 | Establish baseline | Clean target and numeric/categorical preprocessing | Ridge Regression | Test MAE EUR 7,865 | Baseline |
| 2 | Improve nonlinear modeling | Add engineered features and tree-based model | Random Forest | Test MAE EUR 4,929 | MAE improved by EUR 2,936 |
| 3 | Compare alternative boosted model | Add boosted-tree comparison | HistGradientBoosting | Test MAE EUR 5,953 | Worse than Random Forest but better than baseline |
#### 2A.5 Evaluation and Error Analysis
- Metrics used: MAE, RMSE, R², MAPE, stratified 70/15/15 train/validation/test split, and price-band error analysis.
- Final results: Random Forest selected by validation MAE. Test MAE EUR 4,929, Test RMSE EUR 9,656, Test R² 0.953, Test MAPE 0.110. Detailed results: [`reports/price_model_report.md`](reports/price_model_report.md).
- Error patterns and likely causes: Errors are highest for rare makes/models, high-value luxury cars (EUR 150k–300k MAE: EUR 22,038), very new low-mileage vehicles, and listings with missing history. Price-band error analysis and per-make analysis are in [`reports/price_model_error_analysis.md`](reports/price_model_error_analysis.md).
#### 2A.6 Integration with Other Block(s)
- Inputs received from other block(s): `damage_score` from the Computer Vision block is used as a post-model discount factor. The relationship between CV-detected damage and final CHF price is quantified in [`reports/damage_sensitivity_report.md`](reports/damage_sensitivity_report.md) and [`scripts/run_damage_sensitivity.py`](scripts/run_damage_sensitivity.py).
- Outputs provided to other block(s): Base market price (EUR), Swiss-calibrated CHF price, damage discount, confidence band, and top feature drivers are passed to the Seller Cockpit and NLP block. The deployed app also includes a price-sensitivity panel that reruns the model with one changed input at a time (mileage, year, warranty, seller type) to make the numeric model transparent.
### 2B. NLP
#### 2B.1 Data Source(s)
List every usage of a data source as a separate entry. If the same source is used twice for different roles, add it twice.
| Entry | Source name or link | Type | Size | Role in this block |
| --- | --- | --- | --- | --- |
| 1 | AutoScout24 listing descriptions | Text from used-car listings | Included in 118,382 raw rows | Reference style and `description_length` feature |
| 2 | AutoScout24 processed sample (RAG corpus) | Structured + text car listings | 8,000 rows from processed CSV | TF-IDF RAG retrieval corpus for comparable-car context injection |
| 3 | 10 manually defined evaluation cases | Structured vehicle specs + damage labels | 10 diverse cases | Strategy comparison and GPT-judge evaluation |
| 4 | Live user notes in the app | Free-text seller notes | One text field per prediction | Included in generated listing as seller-provided claims |
#### 2B.2 Preprocessing and Prompt Design
- Text preprocessing: Empty descriptions are handled, whitespace is normalised, `description_length` is extracted as a numeric feature, and user notes are inserted only as user-provided (unverified) claims.
- Prompt design or retrieval setup: Three prompt strategies were compared: neutral factual template (Prompt A), sales-optimised but honest template (Prompt B), and GPT listing mode (Prompt C). Prompt C sends the structured vehicle facts, ML price, CV damage evidence, discount, and seller notes as constrained LLM input. The constraint prevents hallucinated features. See [`app/nlp_generator.py`, lines 57–112](app/nlp_generator.py#L57-L112).
#### 2B.3 Approach Selection
- Approach used: Prompt engineering with deterministic templates, a live OpenAI text-model mode, and a RAG-augmented mode in [`app/nlp_generator.py`](app/nlp_generator.py). Templates provide reproducible, hallucination-safe baselines; GPT mode demonstrates real LLM generation; RAG mode retrieves 3 comparable listings from the AutoScout24 corpus via TF-IDF cosine similarity and injects them as market context into the GPT prompt.
- Alternatives considered: Pure template generation is fully reproducible but stylistically limited. Live LLM generation is more flexible but requires an API key. RAG grounds the LLM in real listing data and was added as the fourth strategy to demonstrate proper retrieval-augmented generation. A vector-embedding approach (e.g. sentence-transformers) was considered but TF-IDF over hybrid text (year + make + model + mileage + description snippet) was sufficient to retrieve relevant comparables without GPU or embedding API dependency.
#### 2B.4 Comparison and Iterations
| Iteration | Objective | Key changes | Model or prompt setup | Main metric or qualitative check | Change vs previous |
| --- | --- | --- | --- | --- | --- |
| 1 | Generate factual listing | Neutral template, direct facts only | Prompt A: neutral | Deterministic avg 4.80; factual checks 5.5/6 | Baseline |
| 2 | Improve usefulness and tone | Highlight strengths first, damage still transparent | Prompt B: sales-optimised honest | Deterministic avg 5.00; factual checks 5.8/6 | Better tone and usefulness |
| 3 | Add real LLM generation | Live OpenAI text model with constrained facts | Prompt C: GPT listing | Deterministic avg 5.00; GPT-judge avg 4.8 (when API available) | Strongest NLP evidence |
| 4 | Reduce hallucination risk | Force damage mention; exclude invented claims; add limitations | Final app + templates | Factual checks 6/6 on template outputs | More reliable final output |
| 5 | Add RAG retrieval layer | TF-IDF index (8,000 listings), cosine retrieval k=3, comparables injected into GPT prompt | Prompt D: RAG-augmented listing — [`app/rag_retriever.py`](app/rag_retriever.py) + [`scripts/build_rag_index.py`](scripts/build_rag_index.py) | Deterministic avg 5.00; factual checks 6/6; market context mentioned 10/10 cases | Grounds LLM in real market data |
#### 2B.5 Evaluation and Error Analysis
- Evaluation strategy: [`scripts/evaluate_nlp_prompts.py`](scripts/evaluate_nlp_prompts.py) evaluates **10 diverse vehicle cases** (different damage levels, fuel types, price segments, seller types) with three methods: (1) deterministic 1–5 rubric for factual correctness, usefulness, tone, damage transparency, and price explanation; (2) factual accuracy checks verifying make, model, year, damage, and price in output text; (3) GPT-as-Judge — an impartial OpenAI call scoring only the generated listing text (~300 tokens per case, no images sent). The RAG strategy is additionally evaluated by [`scripts/evaluate_rag_prompts.py`](scripts/evaluate_rag_prompts.py) which verifies retrieval quality and whether market context appears in the generated text.
- Results: Sales-optimised, GPT, and RAG listing strategies all achieved average deterministic score 5.00, factual checks 6.0/6. Neutral template averaged 4.80. RAG market context check: 10/10 cases confirm that retrieved comparables are reflected in the listing. Full results in [`reports/nlp_prompt_evaluation.md`](reports/nlp_prompt_evaluation.md) and [`reports/rag_evaluation.md`](reports/rag_evaluation.md).
- Error patterns and likely causes: Main risks are over-positive tone (mitigated by the sales-optimised honest constraint), omitting visible damage (mitigated by forcing damage labels into all templates), and lexical TF-IDF misses semantic similarity for RAG (mitigated by hybrid text combining structured features and description text). No invented features were found in template outputs in any of the 10 test cases.
#### 2B.6 Integration with Other Block(s)
- Inputs received from other block(s): Structured vehicle fields (ML), base EUR price, Swiss-calibrated CHF price, CV damage labels, confidence, image-quality warnings, damage score, damage discount, and user notes.
- Outputs provided to other block(s): User-facing listing text, price explanation, condition grade, risk notes, and limitation disclaimers. In GPT mode, the LLM receives structured ML price, CV damage evidence, and seller notes as constrained prompt input — preventing fabrication and ensuring traceability.
### 2C. Computer Vision
#### 2C.1 Data Source(s)
List every usage of a data source as a separate entry. If the same source is used twice for different roles, add it twice.
| Entry | Source name or link | Type | Size | Role in this block |
| --- | --- | --- | --- | --- |
| 1 | CarDD with YOLO annotations: <https://www.kaggle.com/datasets/gabrielfcarvalho/cardd-with-yolo-annotations-images-labels> | Vehicle damage images with object-detection labels | Thousands of labeled images | Reference dataset for damage categories and future YOLO training |
| 2 | COCO Car Damage Detection Dataset: <https://www.kaggle.com/datasets/lplenka/coco-car-damage-detection-dataset> | Small annotated damage dataset | ~80 images | Qualitative testing and backup examples |
| 3 | DrBimmer Comprehensive Car Damage: <https://huggingface.co/datasets/DrBimmer/comprehensive-car-damage> | Image classification dataset | 2,300 total; **240-image balanced subset** used locally | Local CV training and evaluation (representative subset only — no bulk API usage) |
| 4 | User-uploaded app images | Front, side, rear, and interior photos | Up to 4 images per valuation | Live inference input for damage analysis and vehicle prefill |
#### 2C.2 Preprocessing and Augmentation
- Image preprocessing: The deployed app accepts up to four uploaded images (front, side, rear, interior), labels each view, and processes them through the selected vision mode. For the hand-crafted baseline, images are resized to 128×128 and colour/edge statistics are extracted. For the CNN transfer-learning model, images are normalised via torchvision `EfficientNet_B0_Weights.DEFAULT.transforms()` and converted into 1280-dimensional pooled CNN features. Implemented in [`app/damage_model.py`, lines 155–180](app/damage_model.py#L155-L180), [`scripts/train_local_cv_model.py`](scripts/train_local_cv_model.py), and [`scripts/train_cnn_cv_model.py`](scripts/train_cnn_cv_model.py).
- Augmentation strategy: The representative 240-image subset is balanced (80 images per class) via [`scripts/prepare_local_cv_dataset.py`](scripts/prepare_local_cv_dataset.py). The CNN transfer-learning training applies label-preserving augmentation only to the training split: horizontal flip, slight brightness increase, and slight contrast increase. The held-out test split remains unaugmented. This is implemented in [`scripts/train_cnn_cv_model.py`](scripts/train_cnn_cv_model.py) and documented in [`reports/cnn_cv_model_report.md`](reports/cnn_cv_model_report.md).
#### 2C.3 Model Selection
- Vision model(s) used:
1. **OpenAI Vision** (`gpt-4o-mini`) — deployed app, multi-image reasoning with JSON-schema output. Sends only user-uploaded images (never training data).
2. **Hand-crafted local CV baseline** — 53-dim colour histogram and edge statistics with 5 sklearn classifiers (Dummy, Logistic Regression, SVM RBF, Random Forest, Gradient Boosting). Implemented in [`scripts/train_local_cv_model.py`](scripts/train_local_cv_model.py) (`vision_mode="local"`).
3. **EfficientNet B0 transfer learning** — 1280-dimensional CNN features with six sklearn classifiers. Implemented in [`scripts/train_cnn_cv_model.py`](scripts/train_cnn_cv_model.py) and evaluated in [`reports/cnn_cv_model_report.md`](reports/cnn_cv_model_report.md).
- Why these model(s) were chosen: OpenAI Vision handles realistic multi-view reasoning in the deployed app because private sellers upload uncontrolled photos with different angles, lighting conditions, and visible context. The hand-crafted baseline demonstrates independent image-model training and quantitative evaluation without API calls, using transparent features that can be inspected and reproduced. The EfficientNet B0 model demonstrates a stronger deep-learning transfer-learning approach with real training augmentation and improves held-out F1 macro from 0.570 to 0.767 on the same 240-image subset.
- Why CLIP was not selected as the final local CV model: CLIP is useful for zero-shot image-to-text matching when no labelled training data is available. In this project, a labelled car-damage dataset was available, so a trained EfficientNet-based classifier was more appropriate for demonstrating local training, cross-validation, and quantitative comparison. CLIP would be a reasonable additional baseline, but it is not required for the project because the final CV block already contains one realistic vision model, two reproducible local model variants, and explicit evaluation.
#### 2C.4 Model Comparison and Iterations
| Iteration | Objective | Key changes | Model(s) used | Main metric | Change vs previous |
| --- | --- | --- | --- | --- | --- |
| 1 | Transparent price adjustment | Manual damage labels → deterministic score weights | Rule-based fallback | Deterministic score correctness | Baseline integration |
| 2 | Hand-crafted feature baseline | 53-dim features, 5 sklearn classifiers, 5-fold CV | SVM RBF (best held-out) | Held-out F1 macro 0.570 | Local CV training evidence |
| 3 | CNN transfer learning + augmentation | EfficientNet B0 backbone (frozen), 1280-dim features, training-only augmentation, 6 classifiers, 5-fold CV | Logistic Regression (best CV) | Held-out F1 macro 0.767 | Stronger local CV model |
| 4 | OpenAI Vision multi-image | 4-view upload, JSON-schema structured output, quality warnings | OpenAI Vision | Qualitative evidence + confidence | Real multi-image support for deployed app |
#### 2C.5 Evaluation and Error Analysis
- Metrics and/or visual checks: (1) Damage-score unit tests verify correct weighting and price adjustment across 7 canonical damage cases — [`reports/damage_scoring_evaluation.md`](reports/damage_scoring_evaluation.md). (2) Hand-crafted baseline: held-out accuracy 0.583, macro F1 0.570 vs dummy F1 0.167, with 5-fold CV on a 240-image subset — [`reports/local_cv_model_report.md`](reports/local_cv_model_report.md). (3) CNN transfer learning with training-only augmentation: held-out accuracy 0.767, macro F1 0.767, CV F1 mean 0.678 for the selected Logistic Regression classifier — [`reports/cnn_cv_model_report.md`](reports/cnn_cv_model_report.md). (4) OpenAI Vision: qualitative checks on real vehicle photos — [`reports/real_vehicle_case.md`](reports/real_vehicle_case.md).
- Final results: The hand-crafted local baseline selected SVM RBF as the best held-out classifier (F1 macro 0.570). The CNN transfer-learning model selected Logistic Regression by cross-validation and reached held-out F1 macro 0.767 after label-preserving training augmentation. Both are documented as local image-level classifiers, not certified damage detectors. Damage-score deterministic cases: scratch = 0.050, scratch+dent = 0.150, severe combined = 0.350 (cap). CV–ML price integration is explicitly quantified in [`reports/damage_sensitivity_report.md`](reports/damage_sensitivity_report.md).
- Error patterns and limitations: The local baseline uses image-level classification without bounding boxes. OpenAI Vision is uncertain for dark, blurry, interior-only, or low-resolution photos; quality warnings are surfaced to the user. Only the 240-image representative subset is used for all local training — no bulk dataset is sent to any API at any point.
#### 2C.6 Integration with Other Block(s)
- Inputs received from other block(s): Vehicle context from the structured ML form can guide image interpretation.
- Outputs provided to other block(s): Damage labels, damage score, confidence, per-view evidence, image-quality warnings, and optional vehicle identity suggestions (make/model/year/body) are passed to the ML pricing pipeline and NLP generator. The damage score directly reduces the final CHF price recommendation — see [`reports/damage_sensitivity_report.md`](reports/damage_sensitivity_report.md) and [`scripts/run_damage_sensitivity.py`](scripts/run_damage_sensitivity.py) for the full quantitative integration analysis.
---
## 3. Deployment
- Deployment URL: <https://huggingface.co/spaces/ganemmah/Occasion-Scanner>
- Main user flow: The user uploads vehicle photos (up to four views), optionally runs AI identity prefill, verifies vehicle facts, selects a vision mode (OpenAI / CNN transfer learning / local baseline), selects a listing style (neutral / sales-optimised / GPT), and generates a Seller Cockpit with condition grade, confidence, CHF negotiation band, price recommendation, price-sensitivity panel, and listing text. OpenAI Vision is recommended for real seller workflows; the CNN and local baseline modes are included as API-free reproducible CV modes for project evaluation.
- Screenshot or short demo: Deployment screenshots are in `reports/screenshots/` and demonstrate the live app input flow, the updated three-mode vision selector (OpenAI Vision, CNN transfer learning, Local CV baseline), a clean valuation, a damage case, AI prefill, a real Porsche Macan multi-view case, and the GPT listing / price-sensitivity extension. Reproducible end-to-end cases are in [`reports/example_cases.md`](reports/example_cases.md) and [`reports/example_cases.json`](reports/example_cases.json). The real-photo case is in [`reports/real_vehicle_case.md`](reports/real_vehicle_case.md).
### 3.1 Hugging Face Usage Guide
The deployed Space is intended to be usable without reading the source code:
1. Open the deployment URL.
2. Upload up to four vehicle photos in the photo section. Recommended views are front, side, rear, and interior.
3. Optionally run **AI prefill vehicle fields** to suggest make, model, production year, and body type from the uploaded photos.
4. Verify every vehicle field manually. The AI prefill is only a suggestion and is not used as a certified identification.
5. Choose the vision mode:
- **OpenAI Vision** for the most realistic live multi-photo analysis.
- **CNN (EfficientNet B0)** for the locally trained transfer-learning classifier.
- **Local CV baseline** for the simple reproducible image-feature baseline.
6. Choose the listing style:
- **Sales-optimized but honest** for a marketable listing that still mentions visible damage.
- **Neutral factual** for a plain factual listing.
- **GPT listing** for live LLM text generation using constrained vehicle, price, and damage facts.
7. Click **Analyze vehicle**. The app returns the CHF base price, damage adjustment, final listing price band, condition grade, confidence, price sensitivity, visible image evidence, and listing text.
The Space requires `OPENAI_API_KEY` as a Hugging Face secret for OpenAI Vision, AI prefill, and GPT listing mode. Without that secret, the committed CNN and local baseline models still allow reproducible image-based inference.
---
## 4. Execution Instructions
### Option A: Use the deployed app (no setup required)
Open the deployment URL directly in a browser — no installation needed:
```
https://huggingface.co/spaces/ganemmah/Occasion-Scanner
```
Upload vehicle photos, fill in the form, and click **Analyze vehicle**. The app is fully functional without any local setup.
### Option B: Run locally
- Environment setup:
```bash
git clone https://github.com/ganemmah/Occasion-Scanner.git
cd Occasion-Scanner
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
```
For local CV training (CNN and hand-crafted baseline):
```bash
pip install -r requirements-cv.txt
```
- Data setup: Place the AutoScout24 raw CSV in `data/raw/` for training. The trained price model artifact is hosted publicly on Hugging Face Hub at [`ganemmah/occasion-scanner-models`](https://huggingface.co/ganemmah/occasion-scanner-models) and is downloaded automatically to `models/price_model.joblib` on first app start — no manual download required. The small local CV classifier artifacts (`models/local_damage_classifier.joblib`, `models/cnn_cv_model.joblib`) are committed to the repository.
- Training command(s):
```bash
# 1. Price model: EDA, training, and error analysis
python scripts/run_price_eda.py
python scripts/train_price_model.py
# 2. Damage scoring unit evaluation
python scripts/evaluate_damage_scoring.py
# 3. CV damage sensitivity analysis (no data required, deterministic)
python scripts/run_damage_sensitivity.py
# 4. Local CV hand-crafted baseline (5 classifiers, 5-fold CV)
python scripts/prepare_local_cv_dataset.py --max-per-class 80
python scripts/train_local_cv_model.py
# 5. Optional CNN transfer-learning extension (requires torch/torchvision)
python scripts/train_cnn_cv_model.py
# 6. Optional CV subset evaluation
python scripts/evaluate_cv_subset.py --limit 10
# 7. NLP evaluation: 10 cases, factual checks, GPT-judge (if API key set)
python scripts/evaluate_nlp_prompts.py
# 8. Build RAG TF-IDF index (8,000 listings → models/rag_index.joblib)
python scripts/build_rag_index.py
# 9. RAG vs baseline evaluation (sales_optimized vs rag_listing, 10 cases)
python scripts/evaluate_rag_prompts.py
# 10. Generate end-to-end example cases
python scripts/generate_example_cases.py
```
- Inference/run command(s):
```bash
export OPENAI_API_KEY="your_api_key_here"
export EUR_TO_CHF_RATE=0.95
export SWISS_MARKET_FACTOR=1.08
python app.py
```
- Reproducibility notes: A fresh `git clone` followed by `pip install -r requirements.txt` and `python app.py` is fully functional: the price model downloads automatically from [`ganemmah/occasion-scanner-models`](https://huggingface.co/ganemmah/occasion-scanner-models) on first start (requires internet access, ~77 MB). Without `OPENAI_API_KEY`, the app falls back to the committed CNN and local baseline classifiers. API keys are never stored in code.
---
## 5. Optional Bonus Evidence
- [x] Third selected block implemented with strong quality
- [x] More than two data sources used with clear added value
- [x] A core section is done exceptionally well
- [x] Extended evaluation
- [x] Ethics, bias, or fairness analysis
- [x] Creative or exceptional use case
Evidence for selected bonus items:
- **All three blocks integrated** in one deployed workflow. NLP receives structured ML price and CV damage evidence as constrained prompt input; CV damage_score directly changes the final CHF price; ML error analysis is referenced in the NLP risk notes.
- **Five data sources**: AutoScout24 structured listings, DrBimmer CV dataset, CarDD YOLO dataset, COCO car damage dataset, and live user-uploaded images. Each has a distinct role documented in the block tables.
- **Extended evaluation artifacts**: [`reports/price_model_report.md`](reports/price_model_report.md), [`reports/price_model_error_analysis.md`](reports/price_model_error_analysis.md), [`reports/damage_scoring_evaluation.md`](reports/damage_scoring_evaluation.md), [`reports/damage_sensitivity_report.md`](reports/damage_sensitivity_report.md), [`reports/local_cv_model_report.md`](reports/local_cv_model_report.md), [`reports/cnn_cv_model_report.md`](reports/cnn_cv_model_report.md), [`reports/nlp_prompt_evaluation.md`](reports/nlp_prompt_evaluation.md), [`reports/evaluation_results.md`](reports/evaluation_results.md), [`reports/real_vehicle_case.md`](reports/real_vehicle_case.md).
- **CV: two trained local models plus OpenAI Vision** — the hand-crafted baseline compares 5 classifiers with 5-fold CV and reaches held-out F1 macro 0.570; the EfficientNet B0 transfer-learning model uses training-only augmentation, compares 6 classifiers, and reaches held-out F1 macro 0.767; OpenAI Vision handles real multi-image inference in the deployed app without bulk API usage.
- **NLP: 10 diverse test cases** covering electric vehicles, luxury cars, commercial vans, accident history, multiple damage types, and different seller/buyer segments. GPT-as-Judge provides impartial evaluation on text only. RAG retrieval evaluation confirms market context in 10/10 cases — see [`reports/rag_evaluation.md`](reports/rag_evaluation.md).
- **Creative use case**: Multimodal Swiss used-car valuation combining market data, damage vision, and listing generation — realistic for private sellers, CHF output, Swiss-market calibration, Seller Cockpit with inspection passport.
## 6. Ethics, Bias, and Responsible Use
The application is designed as a decision-support tool, not as a certified vehicle appraisal, insurance assessment, or repair-cost estimator. The final recommendation should support a seller's decision, but it should not replace a professional inspection or an actual market comparison.
### 6.1 Pricing Bias
The structured price model is trained on listing prices, not confirmed transaction prices. Listing prices can be optimistic, outdated, negotiated later, or influenced by seller strategy. This means the model estimates a plausible advertised market price, not a guaranteed sale price.
The data also contains brand and model imbalance. Frequent brands and common vehicles are represented better than rare models, special editions, imported vehicles, or very expensive cars. This risk is visible in the price-model error analysis, where high-value and rare vehicles have larger absolute errors. The app reduces this risk by showing a recommendation range instead of a single exact value.
### 6.2 Swiss-Market Calibration
The main structured dataset is based on European used-car listings and is converted into CHF with a transparent Swiss-market calibration (`EUR_TO_CHF_RATE=0.95`, `SWISS_MARKET_FACTOR=1.08`). This makes the output more useful for a Swiss project context, but it is still an approximation. A production-grade Swiss valuation system would need real Swiss transaction data, regional demand features, service history, inspection status, and current supply on Swiss marketplaces.
### 6.3 Computer-Vision Limitations
Damage detection depends on the photo quality, angle, lighting, image resolution, and whether the relevant damage is visible in the submitted views. A vehicle may have hidden structural damage not visible in images, and a small visible scratch can look worse or less severe depending on reflections and lighting.
To reduce overconfidence, the app shows image-quality warnings, confidence values, visual evidence by view, and a condition grade. The local CV baseline and CNN transfer-learning model use image-level classification and do not estimate repair costs. The damage score is explicitly described as a transparent heuristic in all user-facing output.
### 6.4 NLP Honesty and Hallucination Risk
Generated listing text can become misleading if it hides visible damage or invents positive claims. The NLP component uses controlled templates with explicit damage constraints and a factual-accuracy check. User-provided seller notes are included as seller claims, not as verified facts. The 10-case evaluation confirmed that no invented claims appeared in template outputs.
Two listing styles are available: a sales-optimised but honest version and a neutral factual version. This demonstrates that tone can change while factual constraints remain stable.
### 6.5 Data Usage and API Costs
Only a 240-image representative subset of the DrBimmer dataset is used for local training. No bulk image dataset is sent to any external API at any point in the training or evaluation pipeline. OpenAI Vision is called only for individual user-uploaded images in the deployed app or for small controlled evaluation sets (at most 10 generated text outputs for GPT-judge evaluation). This is consistent with course guidelines on responsible API usage.
### 6.6 Privacy and Data Protection
Uploaded vehicle images may contain licence plates, people, garage names, or location hints. For a production deployment, the app should warn users before upload and anonymise sensitive image regions. The OpenAI API key is stored only as a Hugging Face Space secret and is never committed to the repository.
### 6.7 Responsible User Guidance
The user must verify all AI-prefilled vehicle facts before running the final valuation. The app explicitly explains that the damage score is a transparent heuristic and not a repair-cost estimate. The generated listing is required to mention visible damage honestly, and the final output includes limitations so that sellers do not present the result as a certified appraisal.