ibsocr1 commited on
Commit
dbb4a08
·
verified ·
1 Parent(s): 12c9bf8

Upload 6 files

Browse files
Files changed (2) hide show
  1. README.md +203 -8
  2. training/train.py +74 -28
README.md CHANGED
@@ -1,15 +1,210 @@
1
- # Ice Cream Counter Annotator — Fixed23
 
 
 
 
 
 
 
 
 
2
 
3
- This version fixes the RT-DETR training crash:
4
 
5
- `RuntimeError: Expected all tensors to be on the same device, but got index is on cpu, different from other tensors on cuda:0`
6
 
7
- The issue occurs in Hugging Face RT-DETR's optional contrastive denoising path. Fixed23 disables that auxiliary path **before model construction** and also replaces the helper with the official `(None, None, None, None)` no-denoising return as a final safeguard.
8
 
9
- The normal RT-DETR detector loss and 10-class fine-tuning remain enabled.
 
 
 
 
 
10
 
11
- The `80 -> 10` checkpoint shape messages are expected when adapting the COCO checkpoint to 10 custom classes.
12
 
13
- ## Install
14
 
15
- Replace the files in the Hugging Face Space with this ZIP and restart/rebuild the Space.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Ice Cream Dataset + Counter
3
+ emoji: 🍦
4
+ colorFrom: yellow
5
+ colorTo: red
6
+ sdk: gradio
7
+ sdk_version: 6.5.1
8
+ app_file: app.py
9
+ pinned: false
10
+ ---
11
 
12
+ # 🍦 Ice Cream Dataset + Counter — Gradio Space
13
 
14
+ This is the **Gradio + ZeroGPU-compatible** version of the uploaded Ice Cream Counter project. It does **not** use Docker, FastAPI, Uvicorn, or a custom HTML frontend.
15
 
16
+ ## What it does
17
 
18
+ 1. Upload training freezer photos.
19
+ 2. Define your product classes.
20
+ 3. Annotate each ice cream with bounding boxes.
21
+ 4. Train an RT-DETR object detector.
22
+ 5. Upload one new freezer image.
23
+ 6. Get the total count, per-product counts, confidence scores, and an annotated result image.
24
 
25
+ The model is still:
26
 
27
+ `PekingU/rtdetr_r50vd`
28
 
29
+ No YOLO is used.
30
+
31
+ ## Create the Hugging Face Space
32
+
33
+ Create a new Space and choose:
34
+
35
+ - **SDK:** Gradio
36
+ - **Hardware:** ZeroGPU or a dedicated GPU is recommended for training
37
+
38
+ The app uses `@spaces.GPU` for training and counting, so it also boots correctly when the Space hardware is **ZeroGPU**.
39
+
40
+ Then upload these files/folders:
41
+
42
+ ```text
43
+ app.py
44
+ requirements.txt
45
+ README.md
46
+ training/
47
+ ```
48
+
49
+ You do **not** need:
50
+
51
+ ```text
52
+ Dockerfile
53
+ FastAPI
54
+ Uvicorn
55
+ static/index.html
56
+ app/main.py
57
+ ```
58
+
59
+ ## Persistent dataset and model
60
+
61
+ The app stores:
62
+
63
+ ```text
64
+ images/
65
+ dataset.json
66
+ model/
67
+ generated_dataset/
68
+ ```
69
+
70
+ When `/data` is available and writable, the app automatically uses:
71
+
72
+ ```text
73
+ /data/icecream_counter/
74
+ ```
75
+
76
+ You can also explicitly set:
77
+
78
+ ```text
79
+ DATA_DIR=/data/icecream_counter
80
+ ```
81
+
82
+ in the Space variables.
83
+
84
+ ### Important Hugging Face storage point
85
+
86
+ A normal Space filesystem is not permanent storage across every rebuild/restart. If you need the dataset and trained model to survive Space restarts/rebuilds, attach **persistent storage** to the Space or move the data/model to an external persistent service.
87
+
88
+ The Gradio conversion itself does not change this storage rule.
89
+
90
+ ## Annotation workflow
91
+
92
+ In the **Annotate** tab the actual uploaded training image is displayed directly using Gradio's Image component. This avoids browser canvas/JavaScript issues that can make the preview appear black.
93
+
94
+ 1. Select a training image.
95
+ 2. The real image appears in the preview.
96
+ 3. Read the image dimensions shown below it.
97
+ 4. Select the product class.
98
+ 5. Enter the bounding box in original-image pixel coordinates:
99
+ - X (left)
100
+ - Y (top)
101
+ - Width
102
+ - Height
103
+ 6. Click **Save Box**.
104
+ 7. Saved boxes are drawn in red on the real image.
105
+ 8. Repeat for every ice cream.
106
+ 9. Use **Delete Box** or **Clear All Boxes** when needed.
107
+
108
+ This version prioritizes a reliable visible image over the previous JavaScript canvas approach.
109
+
110
+ ## Training
111
+
112
+ The app creates an 80/20 COCO train/validation split from the annotated images and starts the existing RT-DETR training script.
113
+
114
+ Default settings:
115
+
116
+ ```text
117
+ Epochs: 30
118
+ Batch size: 2
119
+ Learning rate: 1e-5
120
+ ```
121
+
122
+ For an initial test on a small dataset, use fewer epochs such as 2–5. Once everything works, increase the epochs.
123
+
124
+ A GPU Space is strongly recommended.
125
+
126
+
127
+ ## Training fix
128
+
129
+ This release includes a CUDA-device fix for RT-DETR's contrastive-denoising training path. On some
130
+ Transformers/PyTorch combinations, the denoising class-index tensor can remain on CPU while the
131
+ RT-DETR class embedding is on CUDA, producing:
132
+
133
+ `RuntimeError: Expected all tensors to be on the same device ... cpu ... cuda:0`
134
+
135
+ The training script now moves nested target tensors explicitly and patches the RT-DETR denoising
136
+ helper so its target tensors follow the class-embedding device. The `num_labels=10` vs. checkpoint
137
+ `80` message is expected when fine-tuning the COCO-pretrained checkpoint for 10 custom classes;
138
+ `ignore_mismatched_sizes=True` intentionally reinitializes the classification heads.
139
+
140
+ ## Counting
141
+
142
+ After training, open the **Count** tab and upload one image.
143
+
144
+ The result contains:
145
+
146
+ ```json
147
+ {
148
+ "total": 31,
149
+ "counts": {
150
+ "cone": 6,
151
+ "correto": 7,
152
+ "cornetto": 12,
153
+ "magnum": 6
154
+ }
155
+ }
156
+ ```
157
+
158
+ The result image also shows the detected bounding boxes and confidence values.
159
+
160
+ ## Default classes
161
+
162
+ ```text
163
+ cornetto
164
+ magnum
165
+ correto
166
+ cone
167
+ cup
168
+ sandwich
169
+ stick
170
+ other
171
+ ```
172
+
173
+ You can change them from the Dataset tab.
174
+
175
+ ## Local test
176
+
177
+ ```bash
178
+ pip install -r requirements.txt
179
+ python app.py
180
+ ```
181
+
182
+ Then open:
183
+
184
+ ```text
185
+ http://localhost:7860
186
+ ```
187
+
188
+ ## Recommended Space setup
189
+
190
+ For the first deployment:
191
+
192
+ 1. Create the Space as **Gradio**.
193
+ 2. Upload `app.py`, `requirements.txt`, `README.md`, and `training/`.
194
+ 3. Wait for dependencies to install.
195
+ 4. Open the Dataset tab.
196
+ 5. Upload 2+ training images.
197
+ 6. Annotate them.
198
+ 7. Start with 2–5 epochs to verify training.
199
+ 8. After the model finishes, test the Count tab.
200
+ 9. For serious training, attach a GPU and persistent storage.
201
+
202
+
203
+
204
+ ### RT-DETR training compatibility
205
+ The trainer disables RT-DETR contrastive denoising by default because some Transformers releases can create CPU class-index tensors while the embedding is on CUDA. The detector's normal supervised detection loss remains enabled. A device-safe denoising patch is also included for future re-enablement.
206
+
207
+
208
+ ## Fixed22 training patch
209
+
210
+ This version fixes the RT-DETR CPU/CUDA denoising crash by wrapping the denoising class embedding as a real `torch.nn.Module` and moving its index tensor to the embedding weight device before `nn.Embedding` is called. A plain Python function wrapper is intentionally not used because Transformers expects the embedding to remain a module.
training/train.py CHANGED
@@ -6,7 +6,7 @@ import torch
6
  from PIL import Image
7
  from torch.utils.data import Dataset, DataLoader
8
  from tqdm import tqdm
9
- from transformers import RTDetrImageProcessor, RTDetrForObjectDetection, RTDetrConfig
10
 
11
  BASE_MODEL = "PekingU/rtdetr_r50vd"
12
 
@@ -88,21 +88,70 @@ def evaluate(model, loader, device):
88
 
89
 
90
  def patch_rtdetr_denoising_device():
91
- """Disable RT-DETR contrastive denoising completely.
92
 
93
- The installed Transformers build is entering the denoising helper even
94
- though the training targets are on CUDA. Denoising is auxiliary to the
95
- detector loss, so return the official no-denoising tuple directly.
96
  """
97
- import transformers.models.rt_detr.modeling_rt_detr as rtdetr_mod
98
-
99
- def disabled_denoising(*args, **kwargs):
100
- return None, None, None, None
101
-
102
- disabled_denoising._icecream_denoising_disabled = True
103
- rtdetr_mod.get_contrastive_denoising_training_group = disabled_denoising
104
- print("RT-DETR contrastive denoising DISABLED")
105
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
106
 
107
  def main():
108
  p=argparse.ArgumentParser()
@@ -126,25 +175,22 @@ def main():
126
  if len(train.category_id_to_label)!=len(classes) or len(val.category_id_to_label)!=len(classes):
127
  raise ValueError("COCO categories do not match classes.txt. Rebuild the dataset after saving the classes.")
128
 
129
- # Build from a config with denoising disabled BEFORE model construction.
130
- cfg = RTDetrConfig.from_pretrained(BASE_MODEL)
131
- if hasattr(cfg, "num_denoising"):
132
- cfg.num_denoising = 0
133
- if hasattr(cfg, "num_denoising_queries"):
134
- cfg.num_denoising_queries = 0
135
- cfg.num_labels = len(classes)
136
- cfg.id2label = id2label
137
- cfg.label2id = label2id
138
-
139
  model=RTDetrForObjectDetection.from_pretrained(
140
- BASE_MODEL, config=cfg,
141
  ignore_mismatched_sizes=True
142
  )
143
- # Belt-and-suspenders: if this Transformers build still calls the helper,
144
- # return the official no-denoising tuple instead of entering F.embedding.
145
- patch_rtdetr_denoising_device()
 
 
 
 
 
 
146
  device=torch.device("cuda" if torch.cuda.is_available() else "cpu")
147
  model.to(device)
 
148
 
149
  tr=DataLoader(train,batch_size=a.batch_size,shuffle=True,num_workers=0,collate_fn=collate_fn)
150
  va=DataLoader(val,batch_size=a.batch_size,shuffle=False,num_workers=0,collate_fn=collate_fn)
 
6
  from PIL import Image
7
  from torch.utils.data import Dataset, DataLoader
8
  from tqdm import tqdm
9
+ from transformers import RTDetrImageProcessor, RTDetrForObjectDetection
10
 
11
  BASE_MODEL = "PekingU/rtdetr_r50vd"
12
 
 
88
 
89
 
90
  def patch_rtdetr_denoising_device():
91
+ """Patch RT-DETR denoising so embedding indices are always on the embedding device.
92
 
93
+ Important: the wrapper must be an nn.Module, not a plain Python function.
94
+ Transformers may inspect/use class_embed as a module, and a plain function
95
+ loses the original module parameters/device information.
96
  """
97
+ try:
98
+ import transformers.models.rt_detr.modeling_rt_detr as rtdetr_mod
99
+ import torch.nn as nn
100
+
101
+ original = rtdetr_mod.get_contrastive_denoising_training_group
102
+ if getattr(original, "_icecream_device_patch", False):
103
+ return
104
+
105
+ class DeviceSafeEmbedding(nn.Module):
106
+ def __init__(self, embedding):
107
+ super().__init__()
108
+ self.embedding = embedding
109
+
110
+ def forward(self, indices):
111
+ if torch.is_tensor(indices):
112
+ indices = indices.to(self.embedding.weight.device)
113
+ return self.embedding(indices)
114
+
115
+ def wrapped(targets, num_classes, num_queries, class_embed,
116
+ num_denoising_queries=100, label_noise_ratio=0.5,
117
+ box_noise_scale=1.0, **kwargs):
118
+ # Move every target tensor to the embedding/model device.
119
+ try:
120
+ embed_device = class_embed.weight.device
121
+ except Exception:
122
+ try:
123
+ embed_device = next(class_embed.parameters()).device
124
+ except Exception:
125
+ embed_device = None
126
+
127
+ if embed_device is not None:
128
+ fixed_targets = []
129
+ for target in targets:
130
+ if isinstance(target, dict):
131
+ target = dict(target)
132
+ for key, value in list(target.items()):
133
+ if torch.is_tensor(value):
134
+ target[key] = value.to(embed_device)
135
+ fixed_targets.append(target)
136
+ targets = fixed_targets
137
+ class_embed = DeviceSafeEmbedding(class_embed)
138
+
139
+ return original(
140
+ targets=targets,
141
+ num_classes=num_classes,
142
+ num_queries=num_queries,
143
+ class_embed=class_embed,
144
+ num_denoising_queries=num_denoising_queries,
145
+ label_noise_ratio=label_noise_ratio,
146
+ box_noise_scale=box_noise_scale,
147
+ **kwargs,
148
+ )
149
+
150
+ wrapped._icecream_device_patch = True
151
+ rtdetr_mod.get_contrastive_denoising_training_group = wrapped
152
+ print("RT-DETR denoising device patch installed")
153
+ except Exception as exc:
154
+ raise RuntimeError(f"Could not install RT-DETR denoising device patch: {exc}") from exc
155
 
156
  def main():
157
  p=argparse.ArgumentParser()
 
175
  if len(train.category_id_to_label)!=len(classes) or len(val.category_id_to_label)!=len(classes):
176
  raise ValueError("COCO categories do not match classes.txt. Rebuild the dataset after saving the classes.")
177
 
 
 
 
 
 
 
 
 
 
 
178
  model=RTDetrForObjectDetection.from_pretrained(
179
+ BASE_MODEL,num_labels=len(classes),id2label=id2label,label2id=label2id,
180
  ignore_mismatched_sizes=True
181
  )
182
+ # Some Transformers RT-DETR releases enter the denoising path whenever
183
+ # training, regardless of num_denoising. Keep the config disabled where
184
+ # supported, but also install the device-safe embedding patch below.
185
+ for cfg_owner in (model, getattr(model, "model", None)):
186
+ cfg = getattr(cfg_owner, "config", None)
187
+ if cfg is not None:
188
+ for name in ("num_denoising", "num_denoising_queries"):
189
+ if hasattr(cfg, name):
190
+ setattr(cfg, name, 0)
191
  device=torch.device("cuda" if torch.cuda.is_available() else "cpu")
192
  model.to(device)
193
+ patch_rtdetr_denoising_device()
194
 
195
  tr=DataLoader(train,batch_size=a.batch_size,shuffle=True,num_workers=0,collate_fn=collate_fn)
196
  va=DataLoader(val,batch_size=a.batch_size,shuffle=False,num_workers=0,collate_fn=collate_fn)