Instructions to use freelawproject/container-yolo with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- YOLOv10
How to use freelawproject/container-yolo with YOLOv10:
from ultralytics import YOLOvv10 model = YOLOvv10.from_pretrained("freelawproject/container-yolo") source = 'http://images.cocodataset.org/val2017/000000039769.jpg' model.predict(source=source, save=True) - Notebooks
- Google Colab
- Kaggle
| license: agpl-3.0 | |
| base_model: juliozhao/DocLayout-YOLO-DocStructBench | |
| tags: | |
| - object-detection | |
| - document-layout-analysis | |
| - legal | |
| - yolov10 | |
| library_name: doclayout-yolo | |
| pipeline_tag: object-detection | |
| # container-yolo | |
| Page-container detector for scanned US case-law reporters. Given a rendered | |
| page image it locates the structural containers a downstream OCR pipeline | |
| needs in order to read the page in the right order β columns, footnote | |
| blocks, headings, page numbers, and so on. | |
| This is a fine-tune of DocLayout-YOLO (YOLOv10, 19.5M parameters) from the | |
| DocStructBench checkpoint, produced in two rounds: a supervised round on | |
| hand-reviewed pages from bound reporter volumes, then a self-distillation | |
| round on 20k pseudo-labeled pages. See Training below. | |
| ## Classes | |
| | id | class | what it marks | | |
| |---|---|---| | |
| | 0 | `column` | a body text column | | |
| | 1 | `footnote_block` | the footnote region at the foot of a page | | |
| | 2 | `caption` | a figure or table caption | | |
| | 3 | `page_number` | the printed page number | | |
| | 4 | `image` | a photograph or figure | | |
| | 5 | `table` | a tabular region | | |
| | 6 | `heading` | a running head or section heading | | |
| | 7 | `blockquote` | an indented quotation | | |
| ## Intended use | |
| Structural pre-processing for OCR of scanned case law: the containers drive | |
| reading order (page number β body by column β footnotes) and let a pipeline | |
| tell body text from marginal text. It detects *containers*, not words β it | |
| produces no text and is not an OCR model. | |
| Out of scope: born-digital PDFs (use the embedded text), non-legal document | |
| layouts, and any use where the class set above does not describe the page. | |
| ## Input | |
| Pages rendered to **1700Γ2200 RGB**, inference at `imgsz=1024`. Detections | |
| come back in 1700Γ2200 coordinate space. Feeding it a different render size | |
| will work but has not been evaluated. | |
| ## Training | |
| Two rounds. Round 1 is a supervised fine-tune on hand-reviewed pages. | |
| Round 2 is self-distillation: round 1 labels a much larger unlabeled corpus, | |
| and training continues from round-1 weights on those labels. Round 2 is the | |
| published checkpoint. | |
| ### Round 1 β supervised fine-tune on reviewed pages | |
| | | | | |
| |---|---| | |
| | base | `doclayout_yolo_docstructbench_imgsz1024.pt` | | |
| | data | 632 hand-reviewed pages β **507 train / 43 val / 82 test** | | |
| | split | by volume, so no volume appears in two splits | | |
| | schedule | 100 epochs max, patience 20 β **stopped at 74** | | |
| | imgsz / batch | 1024 / 6 | | |
| | optimizer | auto, `lr0=0.01` | | |
| | date | 2026-07-09 | | |
| ### Round 2 β pseudo-label self-distillation | |
| Round 1 was run as a teacher over **22,000 pages** sampled from a | |
| 96,131-page corpus of scanned reporters. Its raw predictions were cleaned by | |
| four deterministic rules before becoming labels: | |
| 1. de-duplicate overlapping boxes of the same class | |
| 2. keep at most one `page_number` per page | |
| 3. clip `column` boxes at the top of the footnote block, dropping any | |
| remnant under 40px | |
| 4. widen `footnote_block` to the column x-extent | |
| | | | | |
| |---|---| | |
| | base | round-1 weights (continued fine-tune) | | |
| | data | **19,790 train / 2,210 val** pseudo-labeled pages | | |
| | schedule | **12 epochs, patience 4** β deliberately short, so the student does not overfit the teacher's mistakes | | |
| | imgsz / batch | 1024 / 6 | | |
| | optimizer | auto, `lr0=0.01` | | |
| | date | 2026-07-10 | | |
| Neither training corpus is published with these weights. | |
| ## Results | |
| ### Validation, per round | |
| | | round 1 | round 2 | | |
| |---|---|---| | |
| | precision | 0.892 | 0.959 | | |
| | recall | 0.947 | 0.974 | | |
| | mAP50 | 0.954 | 0.987 | | |
| | mAP50-95 | 0.825 | 0.906 | | |
| **These two columns are not comparable.** Round 1 validates against 43 | |
| human-labeled pages; round 2 validates against 2,210 *pseudo-labeled* pages, | |
| so its numbers partly measure agreement with the teacher that produced them. | |
| Use them to read each round's own convergence, not to rank the rounds. | |
| ### Held-out test set | |
| The ranking that counts is a **30-page set, independently reviewed and never | |
| used in training or validation by either round**. Macro-F1 over the 7 classes | |
| that occur in it (`table` has no instances): | |
| | model | macro-F1 | | |
| |---|---| | |
| | **round 2 (this checkpoint)** | **0.893** | | |
| | round 1 | 0.873 | | |
| | reporter-YOLO (separate silver-label baseline) | 0.547 | | |
| | DocLayout-YOLO DocStructBench, un-finetuned | 0.190 | | |
| Per class, round 2 against round 1: | |
| | class | round 2 | round 1 | | |
| |---|---|---| | |
| | column | 0.963 | 0.953 | | |
| | footnote_block | **0.966** | 0.897 | | |
| | caption | **0.944** | 0.857 | | |
| | page_number | 0.947 | **0.983** | | |
| | image | 0.800 | 0.800 | | |
| | heading | 0.851 | 0.857 | | |
| | blockquote | 0.782 | 0.764 | | |
| Round 2 buys large gains on `footnote_block` and `caption` and gives some | |
| back on `page_number` (three misses on 30 pages). `image` rests on two | |
| instances, so treat it as noise rather than signal. | |
| ## Usage | |
| ```python | |
| from doclayout_yolo import YOLOv10 | |
| model = YOLOv10("container_round2.pt") | |
| result = model.predict(img, imgsz=1024, conf=0.2, device=0)[0] | |
| dets = [ | |
| { | |
| "label": model.names[int(b.cls[0])], | |
| "bbox": [round(float(v), 1) for v in b.xyxy[0].tolist()], | |
| "confidence": round(float(b.conf[0]), 3), | |
| } | |
| for b in result.boxes | |
| ] | |
| ``` | |
| Two upstream quirks you will hit loading this checkpoint: | |
| - torch β₯ 2.6 defaults `weights_only=True`, which rejects it β load with | |
| `weights_only=False`. | |
| - `DilatedBlock.dilated_conv` reads `self.dcv.bn`, which `fuse()` deletes; | |
| patch it to fall back to the fused conv's own bias. | |
| ## Limitations | |
| Trained on one genre β bound, scanned US case-law reporters, largely | |
| two-column with footnotes. Pages far from that (single-column slip | |
| opinions, forms, handwriting, non-English typography) are out of | |
| distribution. Bleed-through from the facing page can produce spurious | |
| detections; the pipeline that uses this model filters them downstream | |
| rather than relying on the detector. | |
| ## Licensing | |
| **AGPL-3.0**, inherited from [DocLayout-YOLO](https://github.com/opendatalab/DocLayout-YOLO), | |
| which these weights are a fine-tune of. The checkpoint carries | |
| `license: AGPL-3.0` in its own metadata for the same reason. | |
| Anything built on this model inherits those terms. Refer to the | |
| [licence text](https://www.gnu.org/licenses/agpl-3.0.html) itself. | |