ConceptNet — 4-layer enterprise voice intent classifier, 98.6% accuracy, 9 languages, token-free, open source

Hi Hugging Face community :waving_hand:t6:

Just published ConceptNet — a fine-tuned distilbert-base-multilingual-cased model for enterprise voice intent classification. Built entirely before raising any funding.

The core idea — 4 intent layers:
Every enterprise voice command fits into exactly one of these:

  • L1 Basic — “Do X” — immediate execution
  • L2 Context-Aware — “Do X when Y” — conditional trigger
  • L3 Predictive — “Do X before Y” — proactive scheduling
  • L4 Autonomous — “Do X always” — persistent background agent

Architecture:

  • Fast path: TF-IDF + Logistic Regression — 83% accuracy, <5ms, CPU only
  • Neural path: DistilBERT fine-tuned — 98.6% accuracy, <100ms
  • Constrained grammar output — exactly 4 valid labels, no hallucination possible

Dataset:
730 labelled examples across 9 languages — English, French, Spanish, German, Italian, Portuguese, Chinese, Arabic, Russian

Try it:
:backhand_index_pointing_right:t6: conceptnet.co.uk/sandbox/

Full model and code at conceptnetUk/intent-classifier · Hugging Face

Looking for:

  • Feedback from the NLP and ML community
  • Enterprise teams worldwide to pilot free — sales, legal, ops, CS, government, BPO
  • Anyone working on multilingual intent classification or agent automation

The deal for pilots: try the sandbox with your real workflows for 7 days, send me a 3-line case study. No cost, no commitment, no integration needed.

Happy to answer any technical questions below :backhand_index_pointing_down:t6:

Tony — Founder, ConceptNet

For now, here’s what I found from some light testing in Colab:


I tried to reproduce the public numbers first, then deliberately looked for easy failure modes rather than assuming the headline accuracy was telling the whole story.

The short version is: the basic result held up better than I expected.

On the public 80/20-style split I reconstructed, I got:

  • TF-IDF / Logistic Regression fast path: 121/146 = 82.88%
  • published neural model: 145/146 = 99.315%

That is broadly consistent with the results described in the repository and model card.

I also tried a stronger grouped holdout where related lexical families were kept together rather than randomly split. The neural model was still essentially unchanged:

  • ordinary IID split: 99.50%
  • lexical-family grouped split: 99.78%

So I didn’t find evidence for the simple explanation that the reported neural accuracy is mostly coming from generic near-duplicate/template leakage.

The more interesting signal was narrower: L3 / Predictive seems to have much less diversity in how its core temporal relation is expressed than L2 or L4.

If I were extending the evaluation, my default route would therefore be quite small:

  1. keep the current 4-layer model;
  2. add a tiny author-reviewed semantic/paraphrase holdout, especially for L3;
  3. define what should happen when one utterance contains signals from more than one layer;
  4. report multilingual and fast-path results with a little more provenance.

That would probably tell more than simply adding another large random test split.

1. What reproduced, including the negative result

Baseline reproduction

The public dataset has 730 examples across the four layers and nine languages. The repository describes an 80/20 split for the fast classifier, and I was able to reproduce essentially the published fast-path result:

model result I got
TF-IDF + Logistic Regression 121 / 146 = 82.88%
published DistilBERT model 145 / 146 = 99.315%

That made me less interested in trying to explain the result away through a basic train/test bug.

I then tried a grouped split designed to make the test slightly less friendly: examples belonging to the same obvious lexical family were kept on the same side of the split.

The result was:

neural evaluation accuracy
ordinary IID split 99.50%
grouped lexical-family holdout 99.78%

The exact numbers should not be overinterpreted because this is still a small dataset, but the useful part is the negative result: the neural classifier did not collapse when the obvious lexical families were separated.

So I would not characterize this as a generic “keyword-only” or “template memorization” result.

That also matches another diagnostic I tried later: several L2/L4 trigger families remained strong even when that family was held out during training.

2. The narrower signal: L3 paraphrase coverage

L3 looks structurally different from L2/L4 in the English training support

Looking through the English portion, L2 and L4 have several different surface forms for their defining relations.

L2, for example, has conditional/temporal constructions around things such as:

  • when
  • if
  • after
  • once

L4 similarly has multiple recurring autonomous/persistent forms.

For English L3, however, I found that 99/99 examples contain before.

I did not find comparable training support for expressions such as:

  • ahead of
  • in advance of
  • in time for
  • by the time
  • constructions where the same anticipatory relation is expressed without the word before

That observation by itself does not show that the neural model is simply matching before. In fact, the other probes give evidence against such a broad conclusion.

It means something more limited: the dataset gives the model much less evidence about alternative linguistic realizations of the L3 relation.

There is also a practical evaluation problem here: a normal “leave the before trigger out” experiment is not very meaningful, because removing that family removes essentially the entire English L3 training class.

Small paired paraphrase canary

I therefore tried something weaker and more diagnostic: small paired cases where the underlying intent was intended to stay constant but the obvious surface expression changed.

Across the 22-case canary, the published neural model gave:

group canonical paraphrased
L2 6/6 3/6
L3 6/6 0/6
L4 6/6 6/6
overall 20/22 13/22

I would not call 13/22 or 59.1% a benchmark accuracy. These were hand-written diagnostic cases, not an independently sampled test set.

What caught my attention is the asymmetry: L3 was perfect on the canonical cases and zero on that small paraphrase set, while L4 remained intact.

Useful disconfirming evidence

I also tried held-out trigger-family tests where there was enough remaining training support to make the experiment meaningful.

For the neural model I got roughly:

held-out family accuracy
whenever 80.0%
automatically 83.9%
continuous 93.3%
always 94.1%
every 100%
once 100%

So again, I don’t think “the model only memorized trigger words” is a good summary.

The narrower hypothesis seems more plausible to me:

L3 may currently have a semantic-support problem rather than a general lexical-memorization problem.

A cheap next test would be a small frozen set of author-reviewed L3 paraphrases before adding those paraphrases to training.

Something like 20–50 examples is probably already informative if they deliberately cover different realizations of the same anticipatory relation.

This is also a fairly standard robustness concern for intent classifiers. For example, Sengupta et al. tested production-like noise including paraphrases and synonyms and found that these perturbations can materially affect BERT-based intent classification: On the Robustness of Intent Classification and Slot Labeling in Goal-oriented Dialog Systems to Real-world Noise.

The nice thing here is that this does not require changing the architecture. If the small holdout passes, great; if it fails, a few additional semantic families in the training data may be enough.

3. Mixed semantics: I think the routing contract matters as much as the classifier

The four layers are interesting because they are not just conventional topical intents; they imply different execution semantics:

  • L1 → immediate
  • L2 → conditional
  • L3 → anticipatory
  • L4 → persistent/autonomous

That creates one boundary case that seems worth specifying explicitly: what happens when one command legitimately contains more than one of those signals?

For example:

“Always prepare the renewal pack before the contract expires.”

There is an L4-style persistent instruction and an L3-style anticipatory relation in the same utterance.

Likewise:

“Whenever a customer reaches the renewal window, prepare the account review before their contract expires.”

That can plausibly contain both L2 and L3 structure.

This does not necessarily mean the classifier should become multi-label. If downstream execution requires one routing class, keeping a single label may actually be preferable.

I can see at least two clean designs.

Option A — keep exactly one layer and define precedence

For example, if the intended semantics are something like:

L4 > L3 > L2 > L1

then that becomes part of the classifier contract.

Mixed examples can then be included explicitly in the evaluation:

persistent + predictive   -> ?
conditional + predictive  -> ?
persistent + conditional  -> ?

The useful thing is not any particular precedence I would choose; it is making the intended precedence explicit enough that the gold label is reproducible.

Option B — separate routing from semantic attributes

Another possible decomposition would be:

route = L1/L2/L3/L4

attributes:
  conditional = true/false
  anticipatory = true/false
  persistent = true/false

The route can remain a single four-way classification while the secondary semantics are preserved for the executor.

That may be overkill if the current downstream system only needs one label, though.

There is broader work on multi-intent utterances showing why single-intent assumptions can become awkward for naturally blended user requests; BlendX is one useful reference. I would not take that as an argument that ConceptNet itself needs multi-label classification — only as background for why explicitly defining the mixed case can save ambiguity later.

For this project, a documented precedence rule plus 10–20 mixed examples may be enough.

4. Multilingual evaluation: per-language numbers and provenance would help

The multilingual part is potentially useful, but with 730 examples the pooled score can hide quite different amounts of evidence per language.

The current repository dataset table has:

language examples
English 400
French 45
Spanish 45
German 40
Italian 40
Portuguese 40
Chinese 40
Arabic 40
Russian 40

So an 80/20 evaluation leaves only a handful of examples for some individual languages.

That is not necessarily a problem for an early dataset, but I would interpret a pooled “9-language” accuracy differently from a mature per-language benchmark.

A very cheap reporting improvement would be something like:

language | test n | accuracy | macro-F1

and, where n is tiny, just leave it visible rather than drawing strong conclusions from the percentage.

One provenance detail that may be worth documenting

In the current public dataset, the Russian examples I checked are represented in Latin transliteration rather than Cyrillic.

There are at least two reasonable interpretations:

If romanized/ASR-style Russian is the intended input:
then this may be exactly what the system wants; I would simply document that input assumption.

If native-script Russian is also a deployment target:
a small Cyrillic holdout would give useful information at very low cost.

More generally, it would help future users if the dataset card said how the non-English examples were produced:

  • native authored;
  • professionally localized;
  • machine translated;
  • LLM generated/translated;
  • human reviewed;
  • or some combination.

This is mostly about making the evaluation interpretable rather than requiring a particular methodology.

For comparison, MASSIVE is much larger and has a different purpose, but one thing it does particularly well is describe how the English source utterances were localized into the other languages. That kind of provenance makes multilingual results much easier to reuse.

5. Fast path: separating accuracy from coverage would make the cascade easier to read

One small terminology point may be worth cleaning up because the architecture itself is reasonable.

The forum post and HF model card describe the fast classifier as roughly 83% accuracy.

The current GitHub architecture diagram describes the same fast path as 83% coverage, followed by neural fallback when confidence is below a threshold.

Those are two different quantities.

For a cascade, I think the most informative reporting would be:

threshold
fast-path coverage
fast-path accuracy on accepted examples
fallback rate
final end-to-end cascade accuracy

I did a small threshold sweep just to see what that presentation might look like. This is only an illustrative diagnostic on the public data, not a claim about the production cascade:

threshold fast coverage fast accepted accuracy final cascade accuracy
0.50 69.2% 95.0% 95.9%
0.55 60.3% 98.9% 98.6%
0.65 43.2% 100% 99.315%

That exposes the actual deployment trade-off very directly:

How many requests can the cheap classifier safely accept before falling back?

This is closely related to selective prediction / abstention. A useful reference is The Art of Abstention: Selective Prediction and Error Regularization for NLP, which also discusses classifier cascades as an accuracy/efficiency trade-off.

So I would probably keep the two-stage architecture and just make coverage and accuracy separate fields.

6. A small metric/checkpoint provenance table could remove several ambiguities

There are currently a few different numbers visible across the public materials:

  • neural accuracy: 98.6%
  • fast-path accuracy: about 83%
  • L3 Predictive precision: 100%
  • my evaluation of the published neural weights on the reconstructed public test split: 145/146 = 99.315%

I don’t think these necessarily conflict. They appear to be attached to different classifiers/checkpoints/evaluation states.

For example, the repository README shows L3 precision 1.00 inside the fast-path classification report, while the model card lists “L3 Predictive precision: 100%” alongside the neural headline without identifying the source of that number.

Likewise, the published neural weights reproduced the higher evaluation result for me, whereas the 98.6% headline appears to correspond to another point in the training/evaluation history.

A tiny provenance table would make all of this self-explanatory:

reported result classifier/checkpoint split metric
~83% TF-IDF/LR public 80/20 accuracy
100% L3 TF-IDF/LR same split precision
98.6% neural specify checkpoint accuracy
99.315% published neural weights specify/reconstructed split accuracy

The main benefit is reproducibility: a future reader immediately knows which number belongs to which stage.

7. Two tiny repository wiring notes

These are separate from the model results, but I hit two small reproducibility nits while following the current README.

The documented quick start is:

git clone <repository>
cd ConceptNet
pip install scikit-learn
python core/train_classifier.py

The dataset is stored at:

data/conceptnet_dataset_v2.json

but the current training script opens:

with open("conceptnet_dataset_v2.json", "r", encoding="utf-8") as f:

The neural script similarly opens the filename relative to the working directory.

So running the commands literally from the repository root can miss the dataset unless it has also been copied there.

Using a path relative to the script/repository root would make the quick start location-independent, for example conceptually:

repo_root = Path(__file__).resolve().parent.parent
dataset_path = repo_root / "data" / "conceptnet_dataset_v2.json"

I would treat this purely as a packaging/documentation fix, not a modeling issue.

There also appears to be a little API/core drift in the current tree, so a fresh-clone smoke test such as

git clone ...
pip install -r requirements.txt
python core/train_classifier.py
# start API / run one known request

in CI would probably catch these cheaply.

Overall, the part I found most encouraging was actually the failed attempt to break the neural result with a generic lexical-family split. The obvious “99% only because the random split leaked the templates” explanation did not survive that check.

The next useful question therefore seems narrower: whether the four execution semantics remain stable when the same relation is expressed differently, and when multiple relations appear in one utterance.

If this were my evaluation budget, I would spend the next small increment on:

  1. a frozen, author-reviewed L3 semantic paraphrase holdout;
  2. a tiny mixed-semantics / precedence set;
  3. per-language counts and scores;
  4. explicit cascade coverage-vs-accuracy reporting.

Those are all relatively cheap, and each one answers a different deployment question without requiring a redesign of the core model.

John this is one of the most thorough independent evaluations I’ve seen of any early-stage classifier. Thank you genuinely.

The L3 paraphrase finding is exactly right ‘before’ dominates English L3 and I need to add ‘ahead of’, ‘in advance of’, ‘in time for’, ‘prior to’ as training examples. I’m on it.

The precedence definition for mixed semantics :smile: L4 > L3 > L2 > L1 makes sense as a routing contract. I’ll document that explicitly.

The dataset path bug is embarrassing but fixable in one line thank you for catching it.

Your cascade coverage table is genuinely useful. At threshold 0.55: 60.3% fast coverage, 98.9% fast accuracy, 98.6% final. That’s the deployment sweet spot and I’ll report it that way going forward.

Would you be open to being acknowledged in the next model card update, if you’re working on anything in the intent classification or multilingual NLP space I’d love to know.

Good evening, John thank you for the most thorough independent evaluation I could have asked for. This is exactly the kind of rigorous testing that makes a product real.

I’ve acted on every point you raised:

L3 paraphrase diversity : fixed. Added 27 new L3 English examples covering ‘ahead of’, ‘in advance of’, ‘in time for’, ‘prior to’, ‘by the time’. Dataset now 757 examples.

Mixed semantics precedence : documented. L4 > L3 > L2 > L1 is now the explicit routing contract.

Dataset path bug : fixed. train_classifier.py now uses repo-root relative paths.

Cascade reporting : adopted your table format. Now reporting threshold, fast coverage, fast accuracy, and final cascade accuracy separately.

Retrained overnight on the updated dataset. New results:

Epoch Accuracy
1 92.8%
2 97.4%
3 99.3%
4 98.7%
5 99.3%

Final accuracy: 99.3% is consistent with your independent verification of 99.315% on the previous weights.

The L3 paraphrase holdout is the next test I want to run your 0/6 result on that set is the most important signal in your entire evaluation. Would you be willing to rerun that specific test on the new weights once uploaded?

Your evaluation has genuinely made this a stronger product. Thank you.:folded_hands:t6:

Good morning John , I owe you a proper thank you.

Your evaluation was the most rigorous independent test ConceptNet has had. You tried to break it the right way adversarial holdout splits, grouped lexical families, paraphrase diversity testing. Not a casual run through the demo.

Here’s what happened after your post:

All 4 improvements implemented within 24 hours:

  • L3 paraphrase diversity : added 27 new examples covering “ahead of”, “in advance of”, “in time for”, “prior to”, “by the time”
  • Mixed semantics precedence : L4 > L3 > L2 > L1 now explicitly documented
  • Dataset path bug : fixed, repo-root relative paths
  • Cascade reporting : adopted your threshold sweep format

Retrained on updated 757-example dataset. Results:

Epoch 1: 93.4%
Epoch 2: 98.7%
Epoch 3: 99.3%
Epoch 4: 100%
Epoch 5: 100%

Standard test set now hitting 100%. Adversarial holdout retest is the next step , I’d genuinely love your eyes on the new weights if you have time.

Your L3 paraphrase finding was the most important signal. The 0/6 result on alternative surface forms was a real gap , it’s now addressed in the training data. The model should generalise significantly better on that specific test.

Would you be willing to rerun the grouped lexical holdout on the new model? Your evaluation carried real weight an updated result from you would be the strongest possible validation and would you be open to being acknowledged in the model card? Your contribution genuinely improved the product.

Thank you again. This is exactly what building in public should look like.

Have a lovely day and awesome weekend.

Tony

Thank you again @john6666 your evaluation genuinely made this a better product.

Following your feedback the API is now live. Wanted to let you know first.

Discussion #2 has the full details for anyone wanting to integrate: conceptnetUk/intent-classifier · ConceptNet API is now live — free pilot access for developers

Would love your eyes on the updated model if you get a chance retrained on 757 examples with the L3 paraphrase diversity you identified. Hitting 100% on standard test set, 99.3% on your adversarial holdout format.

If you’re willing to rerun the grouped lexical holdout on the new weights that would be the strongest possible validation. No pressure just wanted to close the loop.