Show HN:本地预训练分类器,无需 GPU
Show HN: Local pretrained classifiers, GPU not needed

原始链接: https://github.com/nicobrenner/jeffy

JEFFY 是一个采用 MIT 许可证、基于 CPU 的文本分类工具包,内置 13 个预训练分类器,并支持使用 CSV、TSV 和 JSONL 数据训练自定义模型。它使用共享的 BGE-large 英文编码器(约 1.2 GB,首次使用时下载),并以可移植的 NumPy 文件形式存储轻量级逻辑回归分类头。 内置任务包括垃圾短信检测、银行和语音意图识别、新闻主题、情感、情绪、自然语言推理(NLI)以及冒犯性语言检测。模型可通过本地网页界面、REST API 或 Python SDK 访问。自定义模型可配置正则化强度、测试集划分和交叉验证,并可与预训练分类器一同使用。 据报告,留出测试集的准确率从 SMS 垃圾邮件检测的 99.1% 到 SNLI 的 65.6% 不等。CPU 推理通常需要 50–80 毫秒,约占用 2 GB 内存。其局限性包括概率未经校准、情绪类别覆盖有限,以及部分任务上的性能较弱。模型清单提供数据集详细信息和完整性哈希。数据集许可证因数据集而异,相关说明记录在 `ATTRIBUTION.md` 中;自定义 pickle 备份只能从可信来源加载。

**“Show HN:本地预训练分类器,无需 GPU。”** 的作者 nico 介绍了 **Jeffy**:这是一套可在普通硬件上本地运行的轻量级分类器集合。它包含 13 个预训练模型,可用于垃圾邮件检测、意图路由、主题分类、情感分析,以及构建《毁灭战士》游戏智能体,同时也支持训练自定义分类器。 作者分享了一些临时的在线体验演示,其中包括《毁灭战士》智能体,并邀请大家建议实用的任务和替代架构方案。评论者质疑其创新性是否被夸大——有人将它与马尔可夫链聊天机器人作比较——也有人惊讶于分类器直到现在才受到广泛关注。Nico 认为,大语言模型的热潮和易于使用的智能体工具让更多人接触到了模型训练;近期围绕“**Jev**”的炒作也表明,许多被宣传为复杂任务的工作,其实都可以由简单分类器完成。总体而言,这个项目强调的是易于上手、可在本地 CPU 上运行的分类,而不是依赖 GPU 的机器学习。
相关文章

原文

Pretrained text classifiers you can run and retrain on CPU.

Inbox Router

Doom Battle Defend the Center

Install uv, then:

uvx --python 3.12 \
  --from "jeffy-classify @ git+https://github.com/nicobrenner/[email protected]" \
  jeffy-serve

Open http://localhost:8400, pick a classifier, and paste one of these:

Classifier Try this text
banking77 I was charged twice for the same transaction
sms_spam WINNER! You have been selected for a free cruise. Reply YES to claim.
ag_news The Federal Reserve raised interest rates by 25 basis points on Wednesday

Jeffy Playground

13 classifiers ship with the package. Weights are logistic regression coefficients (derived model parameters, not copies of training data). Source datasets and licenses are documented in ATTRIBUTION.md.

Task What it does Classes Test Acc Test F1
sms_spam SMS spam detection 2 99.1% 98.0%
dbpedia Wikipedia article category 14 96.0% 95.9%
imdb Movie review sentiment (long text) 2 94.8% 94.8%
banking77 Banking customer intent 77 94.3% 94.3%
ag_news News topic (world/sports/business/tech) 4 90.5% 90.5%
sst2 Movie review sentiment 2 90.1% 90.1%
clinc_oos Voice assistant intent + out-of-scope 151 88.4% 92.1%
massive_intent Smart home voice commands 60 88.1% 86.4%
tweet_eval_offensive Offensive language 2 81.0% 74.8%
tweet_eval_emotion Tweet emotion 4 78.1% 74.7%
emotion Text emotion (6 emotions) 6 75.5% 67.8%
tweet_eval_sentiment Tweet sentiment (3-way) 3 66.2% 65.7%
snli Natural language inference 3 65.6% 65.2%

Test accuracy on held-out splits. Details in data/eval_results/benchmark.json.

Weaknesses: SNLI (65.6%) and tweet_eval_sentiment (66.2%) are below what task-specific models achieve. Emotion (75.5%) has limited class coverage. Probabilities are uncalibrated.

Train a custom classifier

uvx --python 3.12 \
  --from "jeffy-classify @ git+https://github.com/nicobrenner/[email protected]" \
  jeffy-train --example --save-dir my_models
Loaded 24 examples from reviews.csv
Training 'reviews': 24 examples, 2 classes
  Split: 19 train, 5 test
  Test accuracy: 100.0%
Saved to my_models/reviews/

--example uses a bundled 24-row product review CSV. To bring your own:

uvx --python 3.12 \
  --from "jeffy-classify @ git+https://github.com/nicobrenner/[email protected]" \
  jeffy-train --input your_data.csv --text-col text --label-col label \
  --task-id your_task --save-dir my_models

Supports .csv, .tsv, and .jsonl.

JEFFY_PACK_DIR=my_models uvx --python 3.12 \
  --from "jeffy-classify @ git+https://github.com/nicobrenner/[email protected]" \
  jeffy-serve
curl -s -X POST http://localhost:8400/v1/predict \
  -H "Content-Type: application/json" \
  -d '{"text": "The battery life is amazing", "task": "reviews"}'
# → {"label": "positive", "confidence": 0.87, ...}
git clone https://github.com/nicobrenner/jeffy.git
cd jeffy

# With uv (recommended)
uv venv && uv pip install -e .

# Or with pip
python -m venv .venv && source .venv/bin/activate
pip install -e .

The first prediction downloads the shared encoder (bge-large-en-v1.5, ~1.2 GB, cached afterward).

from jeffy.engine import Engine

engine = Engine()
engine.load()

# List available classifiers
for name, cap in engine.capabilities.items():
    print(f"{name}: {cap.description} ({cap.n_classes} classes)")

# Classify text
result = engine.predict("banking77", "I was charged twice for the same transaction")
print(result["label"])          # "transaction_charged_twice"
print(result["confidence"])     # 0.999
print(result["probabilities"])  # {"transaction_charged_twice": 0.999, ...}

SDK walkthrough

# Banking intent
curl -s -X POST http://localhost:8400/v1/predict \
  -H "Content-Type: application/json" \
  -d '{"text": "I was charged twice for the same transaction", "task": "banking77"}'
# → {"label": "transaction_charged_twice", "confidence": 0.999, ...}

# Spam detection
curl -s -X POST http://localhost:8400/v1/predict \
  -H "Content-Type: application/json" \
  -d '{"text": "WINNER! You have been selected for a free cruise. Reply YES to claim.", "task": "sms_spam"}'
# → {"label": "spam", "confidence": 0.91, ...}

# News topic
curl -s -X POST http://localhost:8400/v1/predict \
  -H "Content-Type: application/json" \
  -d '{"text": "The Federal Reserve raised interest rates by 25 basis points on Wednesday", "task": "ag_news"}'
# → {"label": "Business", "confidence": 0.86, ...}
curl -s http://localhost:8400/v1/capabilities | python3 -c "
import json, sys
for c in json.load(sys.stdin)['capabilities']:
    print(f\"{c['task_id']:25s} {c['n_classes']:3d} classes  {c['test_accuracy']:.1%}  {c['name']}\")"

Each shipped classifier has a manifest.json with label names, source dataset, HuggingFace path, stated license, encoder identity, training/test counts, and integrity hashes.

curl -s http://localhost:8400/v1/capabilities/banking77 | python3 -m json.tool
from jeffy.train import train_classifier

clf = train_classifier(
    texts=["great product!", "terrible service", "fast shipping", "broken on arrival"],
    labels=["positive", "negative", "positive", "negative"],
    task_id="my_reviews",
)

result = clf.predict("the quality exceeded my expectations")
print(result["label"])  # "positive"

clf.save("my_models")
Parameter Default Description
C 0.01 How aggressively the model fits your data. Low (0.001) = conservative, keeps predictions closer to "I'm not sure." High (1.0) = trusts individual training examples more. If the model is great on training data but bad on new data (overfitting), lower C.
test_size 0.2 What fraction of your data to hold back for testing. With 100 examples at 0.2, it trains on 80 and tests on 20. Set to 0 to train on everything (useful when you have very little data and will test manually).
cv_folds 3 Cross-validation: splits your training data into 3 parts, trains on 2 and tests on 1, rotates three times, averages the scores. Gives a more reliable accuracy estimate than a single split. Set to 0 to skip (faster, less reliable estimate).

Start with the defaults. With <50 examples per class, expect noisy estimates.

# Install with build dependencies
uv pip install -e ".[build]"
# or: pip install -e ".[build]"

# Retrain all 13 heads from source datasets (~40 min, downloads ~5 GB)
jeffy-build --out data/model_pack

# Evaluate on held-out test sets with tuned baselines
jeffy-evaluate --baselines --latency --device cpu
  • SST-2: Evaluated on validation split (official test labels are not public).
  • SMS Spam: Random split (test_size=0.2, seed=42); no standard benchmark split.
  • SNLI: Input encoded as premise [SEP] hypothesis. Label -1 filtered.
  • CLINC-OOS: 151 classes including out-of-scope. In-scope accuracy 96.5%, OOS detection 51.7%.
  • MASSIVE: English only (config en).
Component Size Required for
Jeffy package (wheel) 1.5 MB Always (includes all 13 heads)
Encoder (bge-large-en-v1.5) ~1.2 GB Inference (downloaded on first use)
datasets package ~100 MB Retraining from HuggingFace only

Runtime memory: ~2 GB (encoder loaded once, shared across all heads).

Latency (CPU, single example, Linux aarch64):

Stage p50 Notes
Embedding 50–80 ms Dominates; varies with input length
Classifier <1 ms Negligible
Total 50–80 ms End-to-end

Bundled pretrained artifacts use numpy .npz format (portable, no pickle). Custom-trained models also save a joblib pickle backup. Only load custom pickle artifacts from trusted sources. Each artifact's manifest.json includes integrity hashes verified on load.

Jeffy code is MIT-licensed. Head artifacts are derived from public datasets; redistribution permissions have not been independently verified for all sources. See ATTRIBUTION.md for per-dataset license status.

Dataset Stated license
banking77, massive_intent, sms_spam CC BY 4.0
clinc_oos CC BY 3.0
dbpedia CC BY-SA 3.0
snli CC BY-SA 4.0
ag_news, imdb Academic / non-commercial
sst2 Stanford academic license
emotion Academic
tweet_eval_* Twitter TOS / academic

The encoder (bge-large-en-v1.5) is MIT-licensed.

Verified with clean-environment wheel and sdist install on Linux aarch64, Python 3.12, scikit-learn 1.9+, sentence-transformers 6.1+, numpy 2.5+. Pretrained artifacts use numpy .npz format, avoiding sklearn version coupling.

  • No zero-shot / general classification. Each task needs a trained head. Unknown tasks return an error.
  • No LLM fallback. This release is pure embedding + classifier.
  • No automatic task routing. You must specify which classifier to use.
Status Milestone
Available Pretrained classifier library, SDK/API, playground, custom training from CSV/JSONL
Next Landing page, PyPI release
Planned Broader classifier catalog, released in verified batches
Planned Automatic routing among supported classifiers
Planned Optional local/API LLM fallback for unsupported tasks
Planned Non-text classifiers (game state, sensor data, structured features)
Exploring Assisted labeling, retraining from corrections, classifier sharing

Suggestions for datasets, capabilities, or workflows are welcome as issues.

联系我们 contact @ memedata.com