File size: 4,551 Bytes
48bce53
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
---
license: mit
tags:
  - computer-vision
  - image-classification
  - pytorch
  - insurance
  - claims-triage
  - grad-cam
pipeline_tag: image-classification
---

# ClaimSight β€” Vehicle Damage Triage Classifier (resnet50)

Binary image classifier that flags whether a submitted vehicle photo
shows visible damage (`00-damage`) or an intact vehicle (`01-whole`),
built as a **claims-triage decision-support tool** β€” not an autonomous
adjuster. Every flagged prediction is intended to route to a human
reviewer.

Full project, training code, and API: https://github.com/<your-username>/claimsight

## Intended use

- First-pass triage of policyholder-submitted claim photos, to route
  obviously-intact vehicles away from a manual review queue.
- **Not** a final claims-adjudication system. **Not** a repair-cost
  estimator. Every prediction should be reviewed by a human before any
  claim decision is made.

## How to use

```python
import torch
from huggingface_hub import hf_hub_download

# clone github.com/<your-username>/claimsight for src/model.py, src/preprocessing.py
from src.model import build_model
from src.preprocessing import preprocess
from src.dataset import eval_transform

checkpoint_path = hf_hub_download(
    repo_id="<your-username>/claimsight-damage-detection",
    filename="best_resnet50.pt",
)
model = build_model("resnet50", pretrained=False)
model.load_state_dict(torch.load(checkpoint_path, map_location="cpu"))
model.eval()

import cv2
image_bgr = cv2.imread("claim_photo.jpg")
image_rgb = preprocess(image_bgr)                      # same function used in training
tensor = eval_transform(image=image_rgb)["image"].unsqueeze(0)
probs = torch.softmax(model(tensor), dim=1).squeeze()
print({"00-damage": probs[1].item(), "01-whole": probs[0].item()})
```

## Training data

Car Damage Detection (Kaggle, `anujms/car-damage-detection`) β€” 2,300
images, binary folder labels only (no masks/bounding boxes), split
1,840 train / 460 validation, balanced within each split.

## Architecture & training

Transfer learning (resnet50, ImageNet-pretrained) in two phases: frozen
backbone with a fresh head first, then fine-tuning of the last block at
a reduced learning rate. `ReduceLROnPlateau` + early stopping on
validation loss. Full details, augmentation policy and reproducibility
notes: see the project README.

## Evaluation (real, on the held-out validation split)

| Metric | Value |
|---|---|
| Validation accuracy | 0.9435 |
| ROC-AUC (damage class) | 0.9858 |
| Recall β€” damage class (@ threshold 0.5) | 0.9565 |
| Precision β€” damage class (@ threshold 0.5) | 0.9322 |
| Confusion matrix (TN/FP/FN/TP) | 214/16/10/220 |

Recall on the `damage` class is the priority metric: a false negative
(damaged vehicle classified as intact) can wrongly close a legitimate
claim, while a false positive only costs one extra human review. A
recall-priority operating point was chosen by sweeping the decision
threshold: at threshold **0.25**, damage recall is
**0.9870** at precision **0.9080**
(vs. 0.9565 recall / 0.9322 precision at the default 0.5).

| Architecture | Val. accuracy | ROC-AUC |
|---|---|---|
| **resnet50** (this checkpoint) | 0.9435 | 0.9858 |
| efficientnet_b0 | 0.8870 | 0.9620 |


## Explainability

Grad-CAM (`pytorch-grad-cam`) on the last convolutional layer is used
as a **weak-localization** signal β€” a coarse heatmap of the region that
most influenced the decision. It is **not** pixel-level segmentation:
there is no mask ground truth in this dataset, so there is no IoU/Dice.
See the project repo's `outputs/gradcam/` for overlays on both correct
and incorrect predictions, including a documented shortcut-learning
check.

## Limitations

- Binary output only β€” no severity or damaged-part classification.
- Grad-CAM is weak localization, not segmentation.
- Modest dataset size (2,300 images, one source) β€” real domain-shift
  risk against a real insurer's photo distribution (different brands,
  angles, lighting, phone cameras).
- Not validated as a repair-cost estimator β€” triage signal only.
- Requires human review in every deployment path.

## Reproducibility

Fixed seed (`torch.manual_seed(42)`), deterministic cuDNN settings,
pinned `requirements.txt`. Metadata for this exact run:

```json
{
  "arch": "resnet50",
  "seed": 42,
  "device": "cuda",
  "phase1_epochs_ran": 8,
  "phase2_epochs_ran": 15,
  "best_val_loss": 0.14994667431582576,
  "checkpoint": "models\\best_resnet50.pt",
  "torch_version": "2.6.0+cu124",
  "trained_at_utc": "2026-08-01T22:51:16Z"
}
```