File size: 2,958 Bytes
2d5ae88
 
3739346
 
 
 
 
 
2d5ae88
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
# Training & Fine-tuning

## Data scope

Train or fine-tune only with captcha images from PHPWind deployments you own
or are explicitly authorized to evaluate. Keep source, consent, and version
metadata with the dataset so results remain interpretable.

## Environment

```bash
pip install torch onnx onnxscript pillow numpy opencv-python
```

## Training from scratch

### 1. Prepare data

Label format `labels.json`:
```json
{
  "captcha_001.png": {"label": "4821"},
  "captcha_002.png": {"label": "9037"}
}
```

Image dir: ~150x60 RGB captcha PNGs; filenames must match label keys.

### 2. Train

```bash
python scripts/train_fixed.py \
  <labels.json> <image_dir> <output.onnx> \
  0.08      # val fraction
  500       # epochs
  1         # augmentation on/off (1=on)
  rgb       # RGB input (important: do NOT use grayscale)
```

Example:
```bash
python scripts/train_fixed.py \
  data/1000_labels.json data/1000_raw \
  models/my_model.onnx \
  0.08 500 1 rgb
```

Outputs:
- `<output>.onnx` β€” final ONNX model
- `<output>.onnx.pt` β€” best-epoch PyTorch weights

### 3. Verify inference

```python
import numpy as np, onnxruntime as ort
from PIL import Image
sess = ort.InferenceSession("my_model.onnx", providers=["CPUExecutionProvider"])
im = Image.open("captcha.png").convert("RGB").resize((160,64), Image.BILINEAR)
x = np.asarray(im, dtype=np.float32).transpose(2,0,1)[None] / 255.0
logits = sess.run(None, {"input": x})[0]
code = "".join(str(int(logits[0,p].argmax())) for p in range(4))
```

## Fine-tuning

`train_fixed.py` auto-loads an existing `<output>.onnx.pt` and resumes from it:

```bash
cp models/captcha_1000_raw.onnx.pt models/finetuned.onnx.pt
python scripts/train_fixed.py <new_labels.json> <new_image_dir> models/finetuned.onnx 0.08 200 1 rgb
```

- 50-200 new labeled samples are enough for small changes.
- Mix old + new data when the generator changes a lot, to avoid catastrophic forgetting.
- Keep the same preprocessing (RGB 160x64 /255, no denoise) and augmentation.

## Parameters

| Param | Default | Description |
|---|---|---|
| val_frac | 0.1 | validation fraction |
| epochs | 400 | total epochs |
| aug | 1 | augmentation toggle |
| rgb | - | pass `rgb` for 3-channel input (recommended) |

## Key lessons

1. **Use RGB, not grayscale** β€” the digits are colored; grayscale loses information.
2. **Do NOT denoise** β€” morphological opening removes thin strokes, dropping val from 88.6% to 79.8%. With enough data the model learns to ignore noise lines itself.
3. **Position-preserving architecture, never global average pooling** β€” GAP destroys spatial position (val β†’ 0%); per-column grouped pooling (20 cols β†’ 4 positions) restores generalization.
4. **Human labels >> auto vision labels** β€” auto labels with ~35% noise crashed the model to 0%; clean manual labels are essential.
5. **No Gaussian noise augmentation** β€” the captcha already has noise lines; adding more obscures the training signal.