Titlehhhh commited on
Commit
6a376ba
·
verified ·
1 Parent(s): f5e30eb

Upload folder using huggingface_hub

Browse files
Files changed (3) hide show
  1. README.md +108 -254
  2. finetune_meta.json +5 -9
  3. model.safetensors +1 -1
README.md CHANGED
@@ -1,296 +1,150 @@
1
  ---
2
  license: apache-2.0
 
 
 
3
  library_name: transformers
4
- base_model: PaddlePaddle/PP-OCRv6_medium_rec_safetensors
5
  tags:
6
- - captcha
7
- - ocr
8
- - minecraft
9
- - anti-bot
10
- - pp-ocr
11
- - base_model:PaddlePaddle/PP-OCRv6_medium_rec_safetensors
 
 
 
12
  ---
13
 
14
  # CaptchaOCR PP-OCRv6 — a text recogniser for Minecraft anti-bot captchas
15
 
16
- > ## The weights are published. Read this before you use them.
17
- >
18
- > This repository holds the **actual weights** of a fine-tune of
19
- > [`PaddlePaddle/PP-OCRv6_medium_rec_safetensors`](https://huggingface.co/PaddlePaddle/PP-OCRv6_medium_rec_safetensors),
20
- > trained by us on the [CaptchaOCR dataset](https://huggingface.co/datasets/Titlehhhh/captchaocr-dataset)
21
- > — our synthetic Minecraft anti-bot captchas. The published weights are the **weight
22
- > interpolation (WiSE-FT style "soup") with α = 0.4** between the fine-tuned checkpoint
23
- > (epoch 7 of a 20-epoch run) and the pretrained base model.
24
- >
25
- > **Measured on 7 captcha types that were never trained on** (whole plugins held out,
26
- > 3 500 images, exact match on the normalised `text`):
27
- >
28
- > | | exact match (median over the 7 types) | CER | exact match (micro, 3 500 images) |
29
- > |---|---|---|---|
30
- > | **the weights published here** (α = 0.4) | **0.638** | 0.1503 | **0.6123** |
31
- > | the untouched base model (α = 0) | 0.588 | 0.1657 | 0.5646 |
32
- > | the fine-tune on its own (α = 1) | 0.480 | 0.2233 | 0.5480 |
33
- >
34
- > **The published weights are better than the base model: +5.0 points of median exact match
35
- > and +4.8 points of micro.** Two honest caveats, both important:
36
- >
37
- > 1. **The best α was chosen on the same held-out set the result is measured on**, so the
38
- > gain is optimistic — on new held-out plugin styles it will be smaller. The curve is
39
- > smooth (the gain holds over the whole range α ∈ [0.25, 0.6]), so this is not a single
40
- > lucky point.
41
- > 2. **The fine-tune on its own (α = 1) is *worse* than the base** (0.480 vs 0.588 median):
42
- > on 13 training types it specialises and loses the base's generality. The interpolation
43
- > is what makes the fine-tune a net win.
44
- >
45
- > Every number above is a measurement, and the file it was measured in is named next to it.
46
-
47
- > **RU:** веса опубликованы. Это **дообучение** PP-OCRv6 на нашем синтетическом датасете
48
- > капч; опубликована **интерполяция весов с базой, α = 0.4** (WiSE-FT). На 7 удержанных
49
- > типах (3 500 картинок): **0.638** exact (медиана) / 0.6123 micro против **0.588 / 0.5646**
50
- > у нетронутой базовой модели. Само дообучение без интерполяции — **0.480**, то есть хуже
51
- > базы. Оговорка: лучшее α выбрано на том же удержанном наборе, поэтому прирост оптимистичен.
52
 
53
  ## What it reads
54
 
55
- Frames from the [CaptchaOCR dataset](https://huggingface.co/datasets/Titlehhhh/captchaocr-dataset) — the kind of pictures the model is trained
56
- on. The caption gives the type and the **gold answer** from `labels.jsonl` (what the player has
57
- to type). The links are absolute: the pictures live in the dataset repository, not in this one.
 
58
 
59
- | | | |
60
- |---|---|---|
61
- | ![Minecraft map captcha](https://huggingface.co/datasets/Titlehhhh/captchaocr-dataset/resolve/main/data/out/haq/mapcha/map/001.png) | ![Latin code captcha](https://huggingface.co/datasets/Titlehhhh/captchaocr-dataset/resolve/main/data/out/LagBug/XProtect/code/001.png) | ![Cyrillic captcha](https://huggingface.co/datasets/Titlehhhh/captchaocr-dataset/resolve/main/data/out_ru/LimboFilter/symbols/001.png) |
62
- | `haq/mapcha/map` — 128×128 map, 4 Latin characters · answer `s8TO` | `LagBug/XProtect/code` — 130×130, white background, noise · answer `EzhYNY` | `LimboFilter/symbols` — 128×128, 3 Cyrillic characters · answer `ЪМИ` |
 
 
63
 
64
- ## What this model is
 
 
65
 
66
- A fine-tune of **PP-OCRv6 medium** (PaddleOCR's text recogniser) on the CaptchaOCR dataset: the
67
- captchas that open-source Minecraft anti-bot plugins draw, rendered by this project's own Java
68
- generators (46 captcha types, 23 upstream plugins).
69
-
70
- * **Architecture:** PP-OCRv6 medium recogniser — PP-LCNetV4 backbone + CTC head,
71
- `transformers` class `PPOCRV6SmallRecForTextRecognition`
72
- (`model_type: pp_ocrv6_small_rec`), 19 134 326 parameters, hidden size 192.
73
- * **Dictionary:** the base model's own 18 710 classes (index 0 = CTC blank, space at the end).
74
- * **Input:** the captcha **text band** (the strip of pixels that carries the answer), not the whole
75
- frame: resized to height 48, width scaled proportionally and right-padded to 320, BGR channel
76
- order, normalised as `(x / 255 - 0.5) / 0.5`.
77
- * **Output:** per-timestep class probabilities; decoded greedily (CTC: collapse repeats, drop the
78
- blank). Greedy decoding is the right decoder here — the answers are random strings, so a language
79
- model has nothing to exploit.
80
 
81
- **The band is the whole game.** On the 29 dataset types that carry a band, the base model scores
82
- `acc_micro = 0.5517` on the band and `acc_micro = 0.0345` on the whole frame — about
83
- 16× worse on the frame. A solver must crop first.
 
 
 
 
 
84
 
85
  ## Measured results
86
 
87
- ### The weights published here
88
-
89
- The fine-tune ran for 20 epochs (4 080 steps) on 13 captcha types; the best checkpoint by
90
- held-out median exact match was **epoch 7 (step 1 632)**. The published weights are that
91
- checkpoint interpolated with the pretrained base model, **θ = 0.4 · θ_finetuned + 0.6 · θ_base**.
92
-
93
- | α | median exact (7 types) | median CER | micro exact (3 500) | micro CER |
94
- |---|---|---|---|---|
95
- | 0.00 — the pretrained base | 0.588 | 0.1657 | 0.5646 | 0.2446 |
96
- | 0.25 | 0.620 | 0.1483 | 0.5991 | 0.2132 |
97
- | **0.40 — published** | **0.638** | **0.1503** | **0.6123** | **0.2004** |
98
- | 0.50 | 0.614 | 0.1630 | 0.6123 | 0.1964 |
99
- | 0.60 | 0.592 | 0.1773 | 0.6100 | 0.1951 |
100
- | 0.75 | 0.542 | 0.2040 | 0.5949 | 0.1995 |
101
- | 1.00 — the fine-tune alone | 0.480 | 0.2233 | 0.5480 | 0.2181 |
102
 
103
- Per held-out type, at the published α = 0.40 (exact match / CER):
 
 
 
 
 
104
 
105
- | held-out type | base (α = 0) | **published (α = 0.4)** | fine-tune alone (α = 1) |
106
  |---|---|---|---|
107
- | `out/JSH32/Minecaptcha/map` | 0.938 / 0.015 | **0.950 / 0.013** | 0.954 / 0.012 |
108
- | `out/jonesdevelopment/sonar/complex` | 0.388 / 0.291 | **0.484 / 0.219** | 0.446 / 0.223 |
109
- | `out/jonesdevelopment/sonar/legacy` | 0.342 / 0.335 | **0.458 / 0.255** | 0.380 / 0.299 |
110
- | `out/jonesdevelopment/sonar/standard` | 0.588 / 0.166 | **0.638 / 0.150** | 0.480 / 0.238 |
111
- | `out/laforetbrut/are-you-sure/code` | 0.000 / 0.808 | **0.018 / 0.681** | 0.024 / 0.600 |
112
- | `out_ru/BotFilter/map` | 0.730 / 0.090 | **0.740 / 0.084** | 0.560 / 0.152 |
113
- | `out_ru/CaptchaVerifyZ/map` | 0.966 / 0.007 | **0.998 / 0.000** | 0.992 / 0.002 |
114
- | **median over the 7 types** | **0.588** | **0.638** | **0.480** |
115
- | **micro over 3 500 images** | **0.5646** | **0.6123** | **0.5480** |
116
-
117
- **How the split was made:** whole **plugins** were held out (5 plugins, 7 types, 3 500 images) —
118
- no image of a held-out type was ever trained on. Never a random image split: several captcha
119
- types have only a few distinct answers, so a random split lets the exact gold answer of a test
120
- image appear in the training half.
121
-
122
- **The training set:** 13 of the 46 captcha types (**6 499 images with a band**), taken from the
123
- published dataset [`Titlehhhh/captchaocr-dataset`](https://huggingface.co/datasets/Titlehhhh/captchaocr-dataset)
124
- (**23 000 PNG, 46 types × 500**, commit `6248c5b9b6ce7eb6e917efd7aa3952892beb7cea`) — the very same
125
- render that is published, not a different one. The 7 held-out types (3 500 images) come from the
126
- same repository.
127
-
128
- ### The base model on the whole dataset (archived benchmark)
129
-
130
- > These three numbers were measured on an **earlier, smaller render** of the dataset
131
- > (100 images per type), before the dataset repository was re-uploaded with the
132
- > 500-per-type render (23 000 PNG) that this model was trained on. They are the base
133
- > model's numbers, not this repository's, and they were not re-measured on the new render.
134
-
135
- | measurement | value | source |
136
  |---|---|---|
137
- | exact match (micro) on the 29 band-carrying types, band crop | 0.5517 | `reports/bench_models/ppocrv6-medium.json` |
138
- | exact match (micro) on the same 29 types, whole frame | 0.0345 | `reports/bench_models/ppocrv6-medium.json` |
139
- | exact match (micro) on all 46 types, band crop | 0.4043 | `reports/bench_models/ppocrv6-medium.json` |
140
-
141
- ### Other captcha models measured on this dataset
142
-
143
- All foreign models were measured by **one protocol** (`bench/bench_models.py`): the picture is
144
- cropped by the answer band (`roi`), the input is normalised the same way for everybody, the greedy
145
- decode is the same, and the metric is exact match on the normalised `text`. The set is the
146
- **29 band-carrying types, 20 images per type (580 items)**, on the earlier 100-images-per-type
147
- render of the dataset.
148
-
149
- | model | band exact match | item set | source file |
150
- |---|---|---|---|
151
- | `PaddlePaddle/PP-OCRv6_medium_rec_onnx` | 0.5517 | 29 types / 580 items | `reports/bench_models/ppocrv6-medium.json` |
152
- | `PaddlePaddle/PP-OCRv5_mobile_rec_onnx` | 0.4621 | 29 types / 580 items | `reports/bench_models/ppocrv5-mobile.json` |
153
- | `PaddlePaddle/cyrillic_PP-OCRv5_mobile_rec_onnx` | 0.4621 | 29 types / 580 items | `reports/bench_models/ppocrv5-cyrillic.json` |
154
- | `easyocr` | 0.3281 | 8 types / 64 items (the old `legacy64` subset) | `reports/bench_models/easyocr.json` |
155
- | `anuashok/ocr-captcha-v3` | 0.2966 | 29 types / 580 items | `reports/bench_models/anuashok-v3.json` |
156
- | `graycat660/doctr-crnn-vgg16-bn-russian-onnx` | 0.2776 | 29 types / 580 items | `reports/bench_models/doctr-crnn-ru.json` |
157
- | `Graf-J/captcha-crnn-base` | 0.0828 | 29 types / 580 items | `reports/bench_models/grafj-crnn-base.json` |
158
- | `Graf-J/captcha-crnn-finetuned` | 0.0672 | 29 types / 580 items | `reports/bench_models/grafj-crnn.json` |
159
- | `Graf-J/captcha-conv-transformer-finetuned` | 0.0638 | 29 types / 580 items | `reports/bench_models/grafj-convtr.json` |
160
- | `emelnov/ocr-captcha-v4-mailru` | 0.0431 | 29 types / 580 items | `reports/bench_models/emelnov-mailru.json` |
161
- | `jtrecenti/txtcaptcha-crnn` | 0.0034 | 29 types / 580 items | `reports/bench_models/txtcaptcha-crnn.json` |
162
- | `microsoft/trocr-base-printed` | **no score — the harness failed**: `ValueError: You need to specify either 'text' or 'text_target'.` | — | `reports/bench_models/trocr-base-printed.json` |
163
-
164
- For the same base model, the whole frame instead of the band gives **0.0345** — 16× worse. The
165
- cropper, not the model, decides this task.
166
-
167
- ### Our own models — **a different protocol, not comparable with the table above**
168
 
169
- | our model | median exact (held-out types) | micro exact | held-out set | source |
170
- |---|---|---|---|---|
171
- | **this repository: PP-OCRv6 + interpolation α = 0.4** | **0.638** | **0.6123** | 7 types / 3 500 images | `reports/TRAINING_PPOCR_V6_BIG.md` |
172
- | our CRNN+CTC, trained from scratch | 0.372 | 0.381 | 4 types / 1 999 images | `reports/TRAINING_CRNN_BIG.md` |
173
- | our CRNN+CTC on its **own training plugins** | 1.000 | 1.000 | 7 types — pure memorisation | `reports/TRAINING_CRNN_BIG.md` |
174
 
175
- **These two tables are not one protocol and must not be read as one ranking.** The foreign models
176
- were measured by the benchmark harness on 29 band-carrying types (580 items, 20 images per type);
177
- our two models were measured on their **own** held-out plugin splits with their own training
178
- pipelines — 7 types / 3 500 images and 4 types / 1 999 images respectively. Different type sets,
179
- different image counts, different pipelines: the numbers are not directly comparable.
180
-
181
- Every number in this card comes from a file of the **CaptchaOCR** project repository — the
182
- measured results are in `reports/`, they are not typed by hand and nothing is extrapolated.
183
-
184
- ## Provenance and licence
185
-
186
- * **Base model:** [`PaddlePaddle/PP-OCRv6_medium_rec_safetensors`](https://huggingface.co/PaddlePaddle/PP-OCRv6_medium_rec_safetensors) — Apache-2.0, by PaddlePaddle.
187
- * **This model:** that base, fine-tuned on the CaptchaOCR dataset published here:
188
- [`Titlehhhh/captchaocr-dataset`](https://huggingface.co/datasets/Titlehhhh/captchaocr-dataset)
189
- (46 captcha types · 23 000 labelled PNG · 23 upstream plugins).
190
- * **Licence of the weights:** Apache-2.0, inherited from the base model. The fine-tuning code and
191
- this card are released under the same terms.
192
- * **Licence of the dataset:** *not* Apache-2.0 — the dataset is split by the licence of the
193
- upstream plugin of each captcha type. The dataset card has the per-type table, and the file
194
- `LICENSE` in the dataset repository spells the same table out in full.
195
 
196
  ## How to use it
197
 
198
- The model is a plain `transformers` text recogniser. `transformers` 5.x has **no** `image-to-text`
199
- pipeline for a CTC recogniser any more (that task name now belongs to vision-language chat
200
- models), so load it directly:
201
-
202
  ```python
203
  import torch
204
- from transformers import AutoModelForTextRecognition, AutoProcessor
205
  from PIL import Image
 
206
 
207
- REPO_ID = "Titlehhhh/captchaocr-ppocrv6"
208
-
209
- model = AutoModelForTextRecognition.from_pretrained(REPO_ID).eval()
210
- processor = AutoProcessor.from_pretrained(REPO_ID) # preprocessor_config.json + the dictionary
211
-
212
- image = Image.open("captcha.png").convert("RGB")
213
- x, y, w, h = (25, 77, 79, 35) # the answer band: take it from labels.jsonl
214
- band = image.crop((x, y, x + w, y + h))
215
 
 
 
216
  with torch.no_grad():
217
- out = model(**processor(images=band, return_tensors="pt"))
218
-
219
- print(processor.post_process_text_recognition(out)[0]["text"])
220
  ```
221
 
222
- The processor does the whole preprocessing: resize to height 48 (width scaled proportionally),
223
- right-pad to 320, RGB→BGR, normalise as `(x / 255 - 0.5) / 0.5`, and the greedy CTC decode that
224
- drops the blank and collapses repeats. The dictionary (18 710 characters) travels in
225
- `preprocessor_config.json` and is also readable as `character_list.txt`.
226
 
227
- > The normalisation `(x / 255 − 0.5) / 0.5` is written **explicitly** into
228
- > `preprocessor_config.json` (`image_mean = image_std = [0.5, 0.5, 0.5]`). The upstream file does
229
- > not mention it, and the `transformers` image processor would then fall back to the ImageNet
230
- > statistics — which is *not* what these weights were trained with.
231
 
232
- **The band (`roi`) is not optional:** on scene-like captchas the answer is one line among several,
233
- and the whole frame makes the model read the wrong line.
 
 
234
 
235
  ## Reproducing
236
 
237
- The training code lives in the **CaptchaOCR** project repository:
238
-
239
  ```powershell
240
- # 1. the fine-tune (13 training types, 5 plugins / 7 types held out)
241
- .venv-gpu\Scripts\python.exe train\finetune_ppocr.py --out train\run_ppocr\big --epochs 20 --batch-size 32 --lr 1e-5 --head-lr 1e-5 --workers 6 --eval-every 1 --save-every 100 --max-minutes 36
242
- # 2. the weight interpolation (7 values of alpha, the same evaluation)
243
- .venv-gpu\Scripts\python.exe train\run_ppocr\big\soup_eval.py
244
- # 3. the export into a Hugging Face folder
245
- .venv-gpu\Scripts\python.exe train\run_ppocr\export_hf.py --ckpt train\run_ppocr\big\soup_best.pt --out train\run_ppocr\big\export_soup
246
  ```
247
-
248
- `train/finetune_ppocr.py` fine-tunes `PaddlePaddle/PP-OCRv6_medium_rec_safetensors` on the dataset, holds out whole **plugins**
249
- (never a random image split: several captcha types have only a few distinct answers, so a random
250
- split lets the exact gold answer of a test image appear in the training half), and reports exact
251
- match and CER per type plus the median over types. `train/ppocr_data.py` holds the dataset loader
252
- and the preprocessing shown above.
253
-
254
- The measured report of the run quoted above is `reports/TRAINING_PPOCR_V6_BIG.md` (the analysis)
255
- and `reports/TRAINING_PPOCR_V6_BIG.json` (the numbers), in the project repository.
256
-
257
- ## Repository metadata
258
-
259
- | file | what |
260
- |---|---|
261
- | `model.safetensors` | the weights: θ = 0.4·θ_finetuned (epoch 7, step 1 632) + 0.6·θ_pretrained |
262
- | `config.json` | the architecture — copied from the base model, unchanged by the fine-tune |
263
- | `preprocessor_config.json` | the input geometry, the normalisation and the 18 710-character dictionary |
264
- | `processor_config.json` | which processor class `AutoProcessor` has to build |
265
- | `character_list.txt` | the same dictionary, one character per line, index 0 = `blank` |
266
- | `finetune_meta.json` | the provenance of this export (source checkpoint, epoch, step, α) |
267
-
268
- There is deliberately **no** `pipeline_tag`: `transformers` 5.x removed the `image-to-text`
269
- pipeline task (it belongs to vision-language chat models now), and a recogniser has to be called
270
- through `AutoModelForTextRecognition`, as in the snippet above. `library_name: transformers` is
271
- declared because the repository really is a `transformers` model.
272
-
273
- ## Limitations
274
-
275
- * **Only the text band is read.** This is a recogniser, not a solver: it does not reverse strings,
276
- does not turn Russian number words into digits and does not compute arithmetic.
277
- * **No Cyrillic in the dictionary.** The base model's 18 710 classes contain no Cyrillic at
278
- all, so the Cyrillic captcha types of the dataset cannot be expressed in this model's alphabet.
279
- * **The data is synthetic.** Every image is a render of this project's own generators — faithful to
280
- the upstream algorithms, but not a screenshot of a live server.
281
- * **Generalisation is the hard part.** A recogniser can memorise a plugin's style instead of
282
- learning the characters; only the type-held-out numbers mean anything.
283
- * **The headline number is optimistic.** α = 0.4 was picked on the same 7 held-out types the
284
- result is reported on, and the best checkpoint was picked the same way. Treat 0.638 as an upper
285
- estimate on *new* plugin styles, not as a guarantee.
286
- * **One hard type stays hard.** `out/laforetbrut/are-you-sure/code` is at 1.8 % exact match
287
- (CER 0.68): the answer band of that type does not carry a clean text line.
288
- * **The weights are an interpolation, not a clean fine-tune.** `model.safetensors` is
289
- 0.4·θ_finetuned + 0.6·θ_pretrained; the pure fine-tune is in the project repository
290
- (`train/run_ppocr/big/best.pt`) and is *worse* on unseen plugin styles.
291
-
292
- ## Citation
293
-
294
- This model is a fine-tune of PP-OCRv6 by PaddlePaddle (Apache-2.0). The dataset is the
295
- CaptchaOCR dataset, re-rendered from open-source Minecraft anti-bot plugins. See the dataset card
296
- for the provenance and the per-type licence of every captcha type.
 
1
  ---
2
  license: apache-2.0
3
+ language:
4
+ - en
5
+ - ru
6
  library_name: transformers
7
+ pipeline_tag: image-to-text
8
  tags:
9
+ - captcha
10
+ - ocr
11
+ - minecraft
12
+ - pp-ocrv6
13
+ - text-recognition
14
+ datasets:
15
+ - Titlehhhh/captchaocr-dataset
16
+ base_model:
17
+ - PaddlePaddle/PP-OCRv6_medium_rec_safetensors
18
  ---
19
 
20
  # CaptchaOCR PP-OCRv6 — a text recogniser for Minecraft anti-bot captchas
21
 
22
+ A fine-tune of **PP-OCRv6_medium_rec** on 15 captcha types of the
23
+ [`Titlehhhh/captchaocr-dataset`](https://huggingface.co/datasets/Titlehhhh/captchaocr-dataset) dataset.
24
+ Feed it one captcha image, get the string drawn on it.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
  ## What it reads
27
 
28
+ Minecraft anti-bot plugins show a player a picture with a code and ask them to retype it.
29
+ Every plugin draws differently: its own font, canvas size, noise, distortion — and its own bugs.
30
+ The 20 types in the dataset are flat captcha images; this model was trained on the **15** of them
31
+ whose alphabet fits the PP-OCRv6 dictionary.
32
 
33
+ Trained on:
34
+ `Archy-X/Krypton/map`, `InstantlyMoist/Captcha/textcaptcha`, `JSH32/Minecaptcha/map`,
35
+ `LagBug/XProtect/code`, `LagBug/XProtect/map`, `haq/mapcha/map`, `irootdev/AnBot/captcha`,
36
+ `jonesdevelopment/sonar/{standard,legacy,complex}`, `just-captcha/ProstoCaptcha/map`,
37
+ `ringoame196-s-mcPlugin/ReproductionCAPTCHA/map`, `BotFilter/map`, `CaptchaVerifyZ/map`,
38
+ `GalleryCaptcha/ru-web`.
39
 
40
+ **Not supported:** `GalleryCaptcha/{ru-numberword, ru-numberword-caption, ru-symbols}` and
41
+ `LimboFilter/{map, symbols}` — these are Cyrillic, and the PP-OCRv6 dictionary contains no
42
+ Cyrillic letters at all.
43
 
44
+ ## What this model is
 
 
 
 
 
 
 
 
 
 
 
 
 
45
 
46
+ | | |
47
+ |---|---|
48
+ | base | `PaddlePaddle/PP-OCRv6_medium_rec_safetensors` (Apache-2.0) |
49
+ | parameters | 19 176 118 |
50
+ | classes | 18 710 (index 0 = blank), the pretrained head is kept, not replaced |
51
+ | fine-tune | 15 types × 800 images, 5 epochs, 1 875 steps, batch 32, lr 1e-5, fp16 |
52
+ | best checkpoint | epoch 2 |
53
+ | weight interpolation | **none** (α = 1.0) — see "Measured results" |
54
 
55
  ## Measured results
56
 
57
+ Measured on a **fresh generator run**: 20 types × 200 = 4 000 images that were never used for
58
+ training or for choosing the epoch. 15 types were scored (the 5 Cyrillic ones are outside the
59
+ dictionary), 3 000 images in total. Crop by the text band when the type has one, full frame
60
+ otherwise; greedy CTC decode; exact match on `text` (letters/digits of any script plus `+-*/=`).
 
 
 
 
 
 
 
 
 
 
 
61
 
62
+ | aggregate | value |
63
+ |---|---|
64
+ | **median exact match over 15 types** | **0.920** |
65
+ | median CER | 0.021 |
66
+ | micro exact match (3 000 images) | 0.745 |
67
+ | micro CER | 0.098 |
68
 
69
+ | type | n | exact | CER |
70
  |---|---|---|---|
71
+ | `CaptchaVerifyZ/map` | 200 | **1.000** | 0.000 |
72
+ | `ringoame196-s-mcPlugin/ReproductionCAPTCHA/map` | 200 | 0.995 | 0.001 |
73
+ | `irootdev/AnBot/captcha` | 200 | 0.985 | 0.005 |
74
+ | `Archy-X/Krypton/map` | 200 | 0.980 | 0.004 |
75
+ | `GalleryCaptcha/ru-web` | 200 | 0.945 | 0.014 |
76
+ | `haq/mapcha/map` | 200 | 0.945 | 0.014 |
77
+ | `BotFilter/map` | 200 | 0.935 | 0.014 |
78
+ | `JSH32/Minecaptcha/map` | 200 | 0.920 | 0.021 |
79
+ | `jonesdevelopment/sonar/legacy` | 200 | 0.830 | 0.052 |
80
+ | `jonesdevelopment/sonar/complex` | 200 | 0.805 | 0.060 |
81
+ | `just-captcha/ProstoCaptcha/map` | 200 | 0.785 | 0.044 |
82
+ | `jonesdevelopment/sonar/standard` | 200 | 0.695 | 0.101 |
83
+ | `InstantlyMoist/Captcha/textcaptcha` | 200 | 0.220 | 0.438 |
84
+ | `LagBug/XProtect/code` | 200 | 0.065 | 0.351 |
85
+ | `LagBug/XProtect/map` | 200 | 0.065 | 0.343 |
86
+
87
+ **Known weak types.** `LagBug/XProtect/code` and `/map` are barely read (6.5 %): they are the same
88
+ 130×130 sheet with per-character cumulative translation and 51 interference lines. `InstantlyMoist`
89
+ (22 %) is a 128×128 sheet with thin diagonal noise. Neither is a decoding problem — the model simply
90
+ never learned these two drawing styles well enough at 800 images each.
91
+
92
+ ### Why there is no weight interpolation here
93
+
94
+ Weight interpolation towards the base (WiSE-FT) was what made the **previous** version of this
95
+ repository useful: a fine-tune on a few types degraded unseen plugins, so α = 0.4 was published.
96
+ This model is trained on most of the types it is evaluated on, so interpolation now only drags it
97
+ back towards the base. Measured on 6 held-out types, 1 200 images (200 per type):
98
+
99
+ | α | median exact | CER |
100
  |---|---|---|
101
+ | 0.0 (base) | 0.600 | 0.164 |
102
+ | 0.4 | 0.738 | 0.089 |
103
+ | 0.8 | 0.858 | 0.046 |
104
+ | **1.0 (published)** | **0.883** | 0.035 |
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
 
106
+ ### What it is NOT good at
 
 
 
 
107
 
108
+ A different plugin that draws in a style not present in the dataset will not be handled: this is a
109
+ fine-tune for a **known set of captcha types with new images**, not a general captcha solver.
110
+ Measured generalization to unseen plugins for a comparable fine-tune sits around 0.4–0.6 median
111
+ exact match, and that is the honest expectation.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
112
 
113
  ## How to use it
114
 
 
 
 
 
115
  ```python
116
  import torch
 
117
  from PIL import Image
118
+ from transformers import PPOCRV6SmallRecForTextRecognition, PPOCRV6SmallRecImageProcessor
119
 
120
+ model = PPOCRV6SmallRecForTextRecognition.from_pretrained("Titlehhhh/captchaocr-ppocrv6")
121
+ proc = PPOCRV6SmallRecImageProcessor.from_pretrained("Titlehhhh/captchaocr-ppocrv6")
 
 
 
 
 
 
122
 
123
+ image = Image.open("001.png").convert("RGB")
124
+ px = proc(images=[image], return_tensors="pt")["pixel_values"]
125
  with torch.no_grad():
126
+ logits = model.head.head(model.head.encoder(
127
+ model.model(px).last_hidden_state).last_hidden_state)
128
+ # greedy CTC decode against character_list.txt
129
  ```
130
 
131
+ The dictionary ships with the repository: `character_list.txt` (18 710 entries, index 0 = blank,
132
+ the same list as in `preprocessor_config.json`). The images are normalised with mean = std = 0.5
133
+ (PaddleOCR convention), which is already patched into `preprocessor_config.json`.
 
134
 
135
+ ## Provenance and licence
 
 
 
136
 
137
+ Base model: `PaddlePaddle/PP-OCRv6_medium_rec_safetensors`, Apache-2.0.
138
+ The dataset is synthetic: the images are re-renders of captchas drawn by open-source Minecraft
139
+ plugins, and each type carries the licence of its upstream (see the dataset card).
140
+ This repository's weights are a derivative of the Apache-2.0 base.
141
 
142
  ## Reproducing
143
 
 
 
144
  ```powershell
145
+ .venv-gpu\Scripts\python.exe train\finetune_ppocr.py --out train\runs\ppocrv6\all20 ^
146
+ --limit-per-type 1000 --holdout-per-type 200 --train-frames --val-types ^
147
+ --epochs 5 --max-minutes 16 --eval-every 1 --batch-size 32 --lr 1e-5 --head-lr 1e-5 --workers 6
148
+ .venv-gpu\Scripts\python.exe train\export_ppocr_hf.py ^
149
+ --ckpt train\runs\ppocrv6\all20\best.pt --soup-alpha 1.0 --out train\runs\ppocrv6\all20\export_alpha1
 
150
  ```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
finetune_meta.json CHANGED
@@ -1,9 +1,9 @@
1
  {
2
  "base_model": "PaddlePaddle/PP-OCRv6_medium_rec_safetensors",
3
- "checkpoint": "C:\\Users\\Title\\Projects\\CapthaModel\\train\\run_ppocr\\big\\soup_best.pt",
4
- "epoch": 7,
5
- "step": 1632,
6
- "soup_alpha": 0.4,
7
  "classes": 18710,
8
  "parameters": 19176118,
9
  "tensors": 308,
@@ -20,9 +20,5 @@
20
  ]
21
  },
22
  "transformers_class": "PPOCRV6SmallRecForTextRecognition",
23
- "recipe": "theta = alpha * theta_finetuned + (1 - alpha) * theta_pretrained",
24
- "source_checkpoint": "C:\\Users\\Title\\Projects\\CapthaModel\\train\\run_ppocr\\big\\best.pt",
25
- "source_epoch": 7,
26
- "source_step": 1632,
27
- "held_out_median_exact": 0.638
28
  }
 
1
  {
2
  "base_model": "PaddlePaddle/PP-OCRv6_medium_rec_safetensors",
3
+ "checkpoint": "C:\\Users\\Title\\Projects\\CapthaModel\\train\\runs\\ppocrv6\\all20\\best.pt",
4
+ "epoch": 2,
5
+ "step": 1125,
6
+ "soup_alpha": 1.0,
7
  "classes": 18710,
8
  "parameters": 19176118,
9
  "tensors": 308,
 
20
  ]
21
  },
22
  "transformers_class": "PPOCRV6SmallRecForTextRecognition",
23
+ "exported_by": "train\\export_ppocr_hf.py"
 
 
 
 
24
  }
model.safetensors CHANGED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:20b25a3e38d2bc4b6379d9f58c284950cad1ee9e6ca60b1b384421c5dc4b04eb
3
  size 76747448
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2185867aeedd38ca85f63f2e86272fc7eb22ddc3ca92db5c5f60b4f9bef12b22
3
  size 76747448