Text Classification
Transformers
Safetensors
English
hs6_classifier
feature-extraction
hs-code
hs6
harmonized-system
hts
tariff
tariff-classification
customs
customs-clearance
trade-compliance
import-export
international-trade
logistics
supply-chain
ecommerce
product-classification
product-categorization
multi-class-classification
english
xlm-roberta
bge-m3
custom_code
Instructions to use Kenpache/hs-code-classifier-en with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use Kenpache/hs-code-classifier-en with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-classification", model="Kenpache/hs-code-classifier-en", trust_remote_code=True)# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("Kenpache/hs-code-classifier-en", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
File size: 8,908 Bytes
4284421 a676d03 4284421 | 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 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 | ---
language:
- en
license: mit
library_name: transformers
pipeline_tag: text-classification
base_model: BAAI/bge-m3
inference: false
metrics:
- accuracy
- recall
tags:
- hs-code
- hs6
- harmonized-system
- hts
- tariff
- tariff-classification
- customs
- customs-clearance
- trade-compliance
- import-export
- international-trade
- logistics
- supply-chain
- ecommerce
- product-classification
- product-categorization
- text-classification
- multi-class-classification
- english
- xlm-roberta
- bge-m3
---
# HS Code Classifier (HS6, English)
**Give it an English product description, get back the 6-digit Harmonized System (HS)
code** β the commodity code that drives customs tariff classification, duty rates, HTS
lookups and trade compliance.
```
"men's cotton knitted t-shirt, short sleeve" β 610910 (0.998)
"portable laptop computer, 14 inch display, 1.2 kg" β 847130 (0.994)
"lithium-ion rechargeable battery, 3.7 V, 5000 mAh" β 850760 (0.929)
"roasted arabica coffee beans, not decaffeinated" β 090121 (0.811)
```
Fine-tuned end-to-end over the full **6,750-class HS6 space** β not a toy subset of
the most common chapters. HS4 (1,266 headings) and HS2 (97 chapters) come out of the
same forward pass and are **guaranteed consistent** with the HS6 answer.
| | |
|---|---|
| **Task** | Product description β HS6 tariff code |
| **Input language** | **English** |
| **Label space** | 6,750 HS6 Β· 1,266 HS4 Β· 97 HS2 |
| **Accuracy on full product descriptions** | **71.94% top-1 Β· 91.08% top-5** |
| **Parameters** | 573.6M (fp32, 2.2 GB) |
| **Context window** | 1,024 tokens at inference |
What you get depends on what you give it: a full description runs at ~72% top-1, a
two-word catalogue stub at ~38%. The complete breakdown β by input type, by length, by
confidence β is right below.
---
## Accuracy
Measured on a frozen held-out test set of **8,065 product descriptions** that has zero
overlap with training and was never used for tuning any hyperparameter or threshold.
### Accuracy depends heavily on how much you tell it
This is the single most important table in this card:
| Input | Share of test | Top-1 | Top-5 |
|---|---:|---:|---:|
| **Full product description** | 35% | **71.94%** | **91.08%** |
| Short subject line | 22% | 46.34% | 73.17% |
| Two- or three-word phrase | 43% | 38.01% | 61.70% |
By raw character length:
| Length | Top-1 |
|---|---:|
| 0β40 chars | 39.00% |
| 40β100 | 45.15% |
| 100β200 | 46.98% |
| 200β500 | 73.75% |
| 500β1500 | **74.72%** |
| 1500+ | 64.77% |
**Do not truncate your inputs.** Material, construction, and intended use are exactly
the features that separate one subheading from another; `"two folding cots"` does not
contain them and no model can recover them. Feed the whole description β the spec
sheet, the paragraph, the invoice line with attributes β and accuracy roughly doubles.
### Averaged over the whole benchmark
The benchmark is deliberately hostile: 62% of its inputs are under 100 characters, so
the average is dragged down by fragments that carry no classifiable features at all.
Averaged over all 8,065 items:
| Metric | Score |
|---|---|
| HS6 top-1 (exact 6-digit match) | 51.70% |
| HS4 top-1 (heading) | 60.99% |
| HS2 top-1 (chapter) | 74.28% |
| HS6 top-5 recall | 74.48% |
Which of the two numbers applies to you is decided by your input, not by the model:
feed full descriptions and expect the 72% row, feed catalogue stubs and expect the 38%
row. Nothing is hidden here β both are stated so you can predict your own result
before you download 2.2 GB.
### Confidence is usable as a threshold
Top-1 softmax probability is monotonically informative, which makes a
straight-through / review split practical:
| Confidence | Share of traffic | Accuracy |
|---|---:|---:|
| 0.8 β 1.0 | 62.8% | 63.7% |
| 0.6 β 0.8 | 12.6% | 28.4% |
| 0.4 β 0.6 | 12.3% | 21.6% |
| 0.2 β 0.4 | 9.1% | 12.0% |
| 0.0 β 0.2 | 3.2% | 3.1% |
With temperature `T β 1.75` applied to the logits, calibration improves further: you
can auto-accept 41.5% of the flow at 80.0% accuracy. Recommended pattern β auto-clear
high-confidence items, route the rest to a human with the top-5 list attached
(top-5 covers 74% of everything and 91% of full descriptions).
### Where the errors go
| Outcome | Share |
|---|---:|
| Correct at top-1 | 51.70% |
| Correct code present in top-5, but not ranked first | 22.78% |
| Correct code absent from top-5 | 25.52% |
Of the misses, 18.1% stay inside the correct HS4 heading and 26.6% inside the correct
HS2 chapter β i.e. a large part of the error is near-miss, not nonsense.
---
## Quick start
```bash
pip install transformers torch sentencepiece
```
Verified on `transformers` 5.5 / `torch` 2.11. The model code uses only long-stable
`transformers` APIs, so 4.4x and later should work as well.
```python
from transformers import AutoModel, AutoTokenizer
REPO = "Kenpache/hs-code-classifier-en"
model = AutoModel.from_pretrained(REPO, trust_remote_code=True).eval()
tokenizer = AutoTokenizer.from_pretrained(REPO, trust_remote_code=True)
model.classify(["men's cotton knitted t-shirt, short sleeve"], tokenizer, top_k=5)
# [[{'hs6': '610910', 'score': 0.9983},
# {'hs6': '610990', 'score': 0.0010},
# {'hs6': '611020', 'score': 0.0005}, ...]]
```
`classify()` batches for you and accepts a list of any length:
```python
codes = model.classify(descriptions, tokenizer, top_k=5, batch_size=32)
```
### On GPU
```python
model = AutoModel.from_pretrained(REPO, trust_remote_code=True).to("cuda").eval()
```
CUDA, Apple Silicon (`mps`) and CPU all work. Roughly 3 GB of VRAM at batch 16 /
length 1024; about 4 GB of RAM on CPU.
**Leave the context window at 1,024 tokens.** It is already the default in
`config.json`; lowering `recommended_max_length` costs about 2.9 points on texts longer
than 1,500 characters, and raising it to 2,048 adds nothing.
### All three HS levels at once
```python
import torch
enc = tokenizer(["woven cotton fabric, dyed, 200 g/m2"], truncation=True,
max_length=1024, return_tensors="pt")
with torch.no_grad():
out = model(**enc)
hs6 = model.config.id2label[out.logits.argmax(-1).item()] # '520839'
hs4 = model.config.id2hs4[out.logits_hs4.argmax(-1).item()] # '5208'
hs2 = model.config.id2hs2[out.logits_hs2.argmax(-1).item()] # '52'
```
`hs4` and `hs2` are marginals of the same distribution (logsumexp over the children
of each parent), so **the levels can never contradict each other**: the model cannot
return heading `6109` and a subheading that lives under `6110`.
---
## Limitations
1. **English only.** The encoder is multilingual, but the head was trained on English
product descriptions. Other languages are untested and expected to be much weaker.
2. **HS6 only.** The first six digits are internationally harmonized; national
8β10 digit tariff lines are out of scope and this model does not predict them.
3. **Short inputs are hard**, as the tables above show. Under ~40 characters, expect
~39% top-1.
4. **Coverage is uneven across the 6,750 classes.** Rarely-seen codes are much weaker
than the headline number suggests; roughly 3,800 codes carry the bulk of the
model's competence.
5. **The ceiling is domain-imposed, not model-imposed.** Identical descriptions
legitimately receive different codes depending on context (end use, material
composition, degree of processing), which caps any text-only classifier.
6. **Not legal or customs advice.** Output is a ranked suggestion. Binding
classification is a decision of the competent authority. Use this to triage, to
pre-fill, and to route to a human β not to file unattended.
---
## Intended use
Good fits:
- pre-filling HS6 on customs declarations, then human review of low-confidence rows
- catalogue / marketplace enrichment at scale
- landed-cost and duty estimation tooling
- deduplicating and sanity-checking existing classifications (flag rows where the
model is confident and disagrees)
Poor fits: unattended filing, national tariff lines beyond 6 digits, non-English input.
---
## Files
| File | What it is |
|---|---|
| `model.safetensors` | weights, fp32, 2.2 GB |
| `config.json` | encoder config + head config + `id2label` for all 6,750 HS6 codes |
| `modeling_hs6.py`, `configuration_hs6.py` | model definition (loaded via `trust_remote_code=True`) |
| `tokenizer.json`, `sentencepiece.bpe.model`, β¦ | XLM-R tokenizer, `model_max_length` 1024 |
## License
MIT, following the `BAAI/bge-m3` base model.
## Citation
```bibtex
@misc{hs6_classifier_en,
title = {HS Code Classifier (HS6, English)},
year = {2026},
note = {XLM-RoBERTa-large (bge-m3) with a flat 6,750-class HS6 head
and marginalized HS4/HS2 levels},
url = {https://huggingface.co/Kenpache/hs-code-classifier-en}
}
```
|