nenzilea commited on
Commit
64d35c5
Β·
verified Β·
1 Parent(s): 3f9f596

Upload 7 files

Browse files
.gitattributes CHANGED
@@ -33,3 +33,7 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ example_images/BMW.jpg filter=lfs diff=lfs merge=lfs -text
37
+ example_images/Dodge.jpg filter=lfs diff=lfs merge=lfs -text
38
+ example_images/Ferrari.jpg filter=lfs diff=lfs merge=lfs -text
39
+ example_images/Porsche.jpg filter=lfs diff=lfs merge=lfs -text
app.py ADDED
@@ -0,0 +1,163 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ from transformers import pipeline
3
+ import openai
4
+ import base64
5
+ import os
6
+ import json
7
+
8
+ # ---------------------------------------------------------------------------
9
+ # Car brands β€” must match the classes the ViT model was trained on
10
+ # ---------------------------------------------------------------------------
11
+ CAR_BRANDS = ['BMW', 'Ferrari', 'Ford', 'Jeep', 'Lamborghini', 'Porsche', 'Rolls-Royce', 'Toyota']
12
+
13
+ # ---------------------------------------------------------------------------
14
+ # Load models (loaded once at startup)
15
+ # ---------------------------------------------------------------------------
16
+
17
+ # Custom fine-tuned ViT model (trained on Stanford Cars, 8 brand classes)
18
+ # Replace with your actual Hugging Face model ID after training and pushing
19
+ vit_classifier = pipeline(
20
+ "image-classification",
21
+ model="nenzilea/car-classification"
22
+ )
23
+
24
+ # CLIP zero-shot classifier
25
+ clip_classifier = pipeline(
26
+ model="openai/clip-vit-large-patch14",
27
+ task="zero-shot-image-classification"
28
+ )
29
+
30
+ # ---------------------------------------------------------------------------
31
+ # OpenAI helper
32
+ # ---------------------------------------------------------------------------
33
+
34
+ def encode_image_to_base64(image_path: str) -> str:
35
+ with open(image_path, "rb") as f:
36
+ return base64.b64encode(f.read()).decode("utf-8")
37
+
38
+
39
+ def classify_with_openai(image_path: str) -> dict:
40
+ """Send image to GPT-4o and ask it to return confidence scores per brand."""
41
+ api_key = os.environ.get("OPENAI_API_KEY")
42
+ client = openai.OpenAI(api_key=api_key)
43
+
44
+ ext = os.path.splitext(image_path)[1].lower().lstrip(".")
45
+ mime_type = "image/jpeg" if ext in ("jpg", "jpeg") else f"image/{ext}"
46
+ base64_image = encode_image_to_base64(image_path)
47
+
48
+ prompt = (
49
+ f"You are a car classification expert. Classify the car brand shown in this image. "
50
+ f"The possible classes are: {', '.join(CAR_BRANDS)}. "
51
+ "Respond ONLY with a valid JSON object where each key is a brand name from the list and "
52
+ "each value is a confidence score between 0.0 and 1.0. All scores must sum to 1.0. "
53
+ 'Example format: {"BMW": 0.05, "Ferrari": 0.85, "Ford": 0.02, ...}'
54
+ )
55
+
56
+ response = client.chat.completions.create(
57
+ model="gpt-4o",
58
+ messages=[
59
+ {
60
+ "role": "user",
61
+ "content": [
62
+ {"type": "text", "text": prompt},
63
+ {
64
+ "type": "image_url",
65
+ "image_url": {"url": f"data:{mime_type};base64,{base64_image}"},
66
+ },
67
+ ],
68
+ }
69
+ ],
70
+ max_tokens=300,
71
+ )
72
+
73
+ text = response.choices[0].message.content
74
+ try:
75
+ start = text.find("{")
76
+ end = text.rfind("}") + 1
77
+ scores = json.loads(text[start:end])
78
+ return {brand: float(scores.get(brand, 0.0)) for brand in CAR_BRANDS}
79
+ except Exception:
80
+ uniform = 1.0 / len(CAR_BRANDS)
81
+ return {brand: uniform for brand in CAR_BRANDS}
82
+
83
+
84
+ # ---------------------------------------------------------------------------
85
+ # Main classification function
86
+ # ---------------------------------------------------------------------------
87
+
88
+ def classify_car(image):
89
+ if image is None:
90
+ return {}, {}, {}
91
+
92
+ # Custom ViT β€” fine-tuned on Stanford Cars
93
+ vit_results = vit_classifier(image, top_k=len(CAR_BRANDS))
94
+ vit_output = {r["label"]: round(r["score"], 4) for r in vit_results}
95
+
96
+ # CLIP β€” zero-shot with brand names as candidate labels
97
+ clip_results = clip_classifier(image, candidate_labels=CAR_BRANDS)
98
+ clip_output = {r["label"]: round(r["score"], 4) for r in clip_results}
99
+
100
+ # OpenAI GPT-4o Vision
101
+ openai_output = classify_with_openai(image)
102
+
103
+ return vit_output, clip_output, openai_output
104
+
105
+
106
+ # ---------------------------------------------------------------------------
107
+ # Example images (add representative car images to example_images/)
108
+ # ---------------------------------------------------------------------------
109
+
110
+ example_images = [
111
+ ["example_images/ferrari.jpg"],
112
+ ["example_images/lamborghini.jpg"],
113
+ ["example_images/bmw.jpg"],
114
+ ["example_images/jeep.jpg"],
115
+ ["example_images/ford.jpg"],
116
+ ["example_images/toyota.jpg"],
117
+ ["example_images/porsche.jpg"],
118
+ ["example_images/rolls_royce.jpg"],
119
+ ]
120
+
121
+ # ---------------------------------------------------------------------------
122
+ # Gradio UI
123
+ # ---------------------------------------------------------------------------
124
+
125
+ with gr.Blocks(title="Car Brand Classification Comparison") as demo:
126
+ gr.Markdown("# Car Brand Classification β€” Model Comparison")
127
+ gr.Markdown(
128
+ "Upload a car image to compare predictions from three different models:\n\n"
129
+ "- **Custom ViT** β€” fine-tuned on Stanford Cars (8 brand classes)\n"
130
+ "- **CLIP** β€” zero-shot classification with `openai/clip-vit-large-patch14`\n"
131
+ "- **OpenAI GPT-4o** β€” vision LLM classification\n\n"
132
+ f"**Classes:** {', '.join(CAR_BRANDS)}"
133
+ )
134
+
135
+ with gr.Row():
136
+ input_image = gr.Image(type="filepath", label="Upload Car Image")
137
+
138
+ classify_btn = gr.Button("Classify", variant="primary")
139
+
140
+ with gr.Row():
141
+ with gr.Column():
142
+ gr.Markdown("### Custom ViT Model")
143
+ vit_output = gr.Label(num_top_classes=8, label="ViT Predictions")
144
+ with gr.Column():
145
+ gr.Markdown("### CLIP Zero-Shot")
146
+ clip_output = gr.Label(num_top_classes=8, label="CLIP Predictions")
147
+ with gr.Column():
148
+ gr.Markdown("### OpenAI GPT-4o Vision")
149
+ openai_output = gr.Label(num_top_classes=8, label="OpenAI Predictions")
150
+
151
+ classify_btn.click(
152
+ fn=classify_car,
153
+ inputs=[input_image],
154
+ outputs=[vit_output, clip_output, openai_output],
155
+ )
156
+
157
+ gr.Examples(
158
+ examples=example_images,
159
+ inputs=input_image,
160
+ label="Example Images",
161
+ )
162
+
163
+ demo.launch()
example_images/BMW.jpg ADDED

Git LFS Details

  • SHA256: 404caa87b01a9447e1babed2e3db8c89ee2193d2327c870965fe0e40c40c99dd
  • Pointer size: 131 Bytes
  • Size of remote file: 490 kB
example_images/Dodge.jpg ADDED

Git LFS Details

  • SHA256: ef71f45155d269d83182798c839b0042b37c1af3a9191985de80660bff6c798c
  • Pointer size: 132 Bytes
  • Size of remote file: 1.29 MB
example_images/Ferrari.jpg ADDED

Git LFS Details

  • SHA256: 519f3a234be5d1e0957daadead5f8ddbdc5e32b9b1233863e64aa9d92e2a3517
  • Pointer size: 132 Bytes
  • Size of remote file: 2.7 MB
example_images/Porsche.jpg ADDED

Git LFS Details

  • SHA256: 4e0daa58b19883e4d66c248fde83128a50881f47205e94e7663ca95a968b2eb1
  • Pointer size: 132 Bytes
  • Size of remote file: 1.56 MB
readme_.md ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Car Brand Classification App
2
+
3
+ This app compares 3 image classification approaches on car images:
4
+
5
+ - Fine-tuned ViT model ([`nenzilea/car-classification`](https://huggingface.co/nenzilea/car-classification))
6
+ - Zero-shot CLIP (`openai/clip-vit-large-patch14`)
7
+ - OpenAI vision model (GPT-4o image classification)
8
+
9
+ ## Dataset Used For Training
10
+
11
+ - Hugging Face dataset: `tanganke/stanford_cars`
12
+ - The Stanford Cars dataset contains **196 fine-grained classes** (car make/model/year combinations). We group them into **8 brand-level classes** for a cleaner, more visually meaningful classification task.
13
+ - Number of classes: `8`
14
+ - Classes: `BMW`, `Ferrari`, `Ford`, `Jeep`, `Lamborghini`, `Porsche`, `Rolls-Royce`, `Toyota`
15
+
16
+ ### Preprocessing Steps
17
+
18
+ 1. **Brand extraction** β€” each of the 196 Stanford Cars class names (e.g. `"Ferrari 458 Italia Coupe 2012"`) is mapped to one of 8 brands by substring matching.
19
+ 2. **Filtering** β€” images whose class does not belong to the 8 brands are removed from the dataset.
20
+ 3. **Label remapping** β€” original integer labels (0–195) are re-mapped to brand indices (0–7).
21
+ 4. **Train/validation/test split** β€” the original training split is divided 80/10/10 (train/validation/test) using `train_test_split(test_size=0.2, seed=42)`.
22
+ 5. **Image preprocessing** β€” images are resized to 224Γ—224 and pixel values are normalised to [-1, 1] using `AutoImageProcessor` from `google/vit-base-patch16-224`.
23
+ 6. **RGB conversion** β€” all images are converted to RGB to handle any grayscale or RGBA edge cases.
24
+
25
+ ## Trained Model
26
+
27
+ - Hugging Face model link: [https://huggingface.co/nenzilea/car-classification](https://huggingface.co/nenzilea/car-classification)
28
+ - Base model: `google/vit-base-patch16-224`
29
+ - Only the final classification head was fine-tuned (all other layers frozen).
30
+ - Trainable parameters: ~4,614 out of ~85.8M total.
31
+
32
+ ## Training Performance
33
+
34
+ | Training Loss | Epoch | Step | Validation Loss | Accuracy |
35
+ |---:|---:|---:|---:|---:|
36
+ | β€” | 1.0 | β€” | β€” | β€” |
37
+ | β€” | 2.0 | β€” | β€” | β€” |
38
+ | β€” | 3.0 | β€” | β€” | β€” |
39
+ | β€” | 4.0 | β€” | β€” | β€” |
40
+ | β€” | 5.0 | β€” | β€” | β€” |
41
+
42
+ > Fill in the table above with the values printed by the Trainer during training.
43
+
44
+ ## Hugging Face Space
45
+
46
+ - App link: [https://huggingface.co/spaces/nenzilea/car-classification](https://huggingface.co/spaces/nenzilea/car-classification)
47
+
48
+ ## Example Image Results
49
+
50
+ The table below reports the true class and Top-3 predictions for ViT, CLIP, and GPT-4o.
51
+
52
+ | Image | True Class | ViT Top-3 (score) | CLIP Top-3 (score) | OpenAI GPT-4o (label, confidence) |
53
+ |---|---|---|---|---|
54
+ | `ferrari.jpg` | `Ferrari` | β€” | β€” | β€” |
55
+ | `lamborghini.jpg` | `Lamborghini` | β€” | β€” | β€” |
56
+ | `bmw.jpg` | `BMW` | β€” | β€” | β€” |
57
+ | `jeep.jpg` | `Jeep` | β€” | β€” | β€” |
58
+ | `ford.jpg` | `Ford` | β€” | β€” | β€” |
59
+ | `toyota.jpg` | `Toyota` | β€” | β€” | β€” |
60
+ | `porsche.jpg` | `Porsche` | β€” | β€” | β€” |
61
+ | `rolls_royce.jpg` | `Rolls-Royce` | β€” | β€” | β€” |
62
+
63
+ > Fill in the table above after running the app with the example images.
64
+
65
+ ## Model Comparison Summary
66
+
67
+ | Model | Approach | Strengths | Weaknesses |
68
+ |---|---|---|---|
69
+ | **Custom ViT** | Supervised fine-tuning on 8 car brands | High accuracy on known brands | Only classifies the 8 trained brands |
70
+ | **CLIP** | Zero-shot with brand name as text prompt | No training needed, flexible labels | Lower accuracy; may confuse visually similar brands |
71
+ | **OpenAI GPT-4o** | LLM vision with natural language prompt | Strong reasoning, handles unusual angles | API cost, latency, black-box |
requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ transformers
2
+ torch
3
+ gradio
4
+ openai
5
+ Pillow