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}
}
```