adaptive-classifier

codelionApache-2.0活跃维护

一个支持持续学习的自适应文本分类系统,能在数据分布变化时增量更新模型而不遗忘旧知识,适合动态场景下的分类任务。

A flexible, adaptive classification system for dynamic text classification

v0.3.1发布于 2026-10-09 03:36 UTC577star43fork同步于 2026-11-10 11:58 UTC

怎么上手

由 README 提炼,执行前请确认
pip install adaptive-classifier
硬件要求按 README 说明

README 未说明具体显存或 GPU 型号要求;基于 PyTorch 与 HuggingFace transformers,内置 ONNX Runtime 可加速 CPU 推理。

下载:源码 zip·GitHub Releases(安装包)本站不镜像安装包

项目说明

由 README 自动翻译提炼

基于 PyTorch 和 HuggingFace 的文本分类库,支持运行时加类、持续学习与零停机更新

一个支持持续学习的自适应文本分类系统,能在数据分布变化时增量更新模型而不遗忘旧知识,适合动态场景下的分类任务。

核心能力

  • 运行时动态添加新类别,无需重新训练模型
  • 持续学习新样本,用 EWC 缓解灾难性遗忘
  • 内置 ONNX Runtime,CPU 推理提速 2-4 倍
  • 用 FAISS 原型记忆做相似度检索,与神经层混合预测
  • 提供校准置信度、OOD 检测与弃答,支持主动学习与漂移检测

容易踩的坑

需要自行指定或下载一个 HuggingFace transformer 模型作为底座;0.2.0 之前默认用 CLS 池化,对 all-MiniLM-L6-v2 这类均值池化模型效果差,需注意版本;预训练企业模型需从 HuggingFace Hub 单独拉取。

内容由模型根据 README 生成,可能不完整或不准确,以官方文档为准。看原文 README

最近发版

5 个版本v0.3.1
  1. v0.3.1最新正式版2026-10-09 · 2026-10-09 03:36 UTC

    What's Changed

    Full Changelog: https://github.com/codelion/adaptive-classifier/compare/v0.3.0...v0.3.1

  2. v0.3.02026-10-09 · 2026-10-09 00:11 UTC
    查看更新说明

    What's Changed

    Full Changelog: https://github.com/codelion/adaptive-classifier/compare/v0.2.0...v0.3.0

  3. v0.2.02026-09-17 · 2026-09-17 03:46 UTC
    查看更新说明

    What's Changed

    Full Changelog: https://github.com/codelion/adaptive-classifier/compare/v0.1.2...v0.2.0

  4. v0.1.22025-10-07 · 2025-10-07 03:12 UTC
    查看更新说明
  5. v0.1.12025-10-07 · 2025-10-07 01:56 UTC
    查看更新说明

用户点评

登录用户的真实使用记录,可按版本和硬件筛选
共 0 条

    还没有人点评。第一个说出真实体验的人最有价值。

    README 原文

    未翻译

    PyPI - Version PyPI Downloads GitHub Stars License: Apache 2.0 GitHub Discussions


    🔗 Quick Links


    Adaptive Classifier is a PyTorch-based machine learning library that revolutionizes text classification with continuous learning, dynamic class addition, and strategic defense against adversarial inputs. Built on HuggingFace transformers, it enables zero-downtime model updates and enterprise-grade robustness.

    ✨ Key Features

    🎯 Core Capabilities

    • 🚀 Universal Compatibility - Works with any HuggingFace transformer model
    • ⚡ Optimized Inference - Built-in ONNX Runtime for 2-4x faster CPU predictions
    • 📈 Continuous Learning - Add new examples without catastrophic forgetting
    • 🔄 Dynamic Classes - Add new classes at runtime without retraining
    • ⏱️ Zero Downtime - Update models in production without service interruption
    • ✅ Knows What It Doesn't Know - Calibrated confidence, prediction sets, out-of-distribution detection and abstention
    • 🧹 Fixable - Forget a wrong example or a whole class without retraining
    • 🎣 Active Learning & Drift - Suggest which texts to label next and detect when traffic has shifted
    • 🔌 Easy to Adopt - scikit-learn interface, HTTP server, async methods, and ONNX inference outside Python

    🛡️ Advanced Defense

    • 🎮 Strategic Classification - Game-theoretic defense against adversarial manipulation
    • 🔒 Anti-Gaming Protection - Robust predictions under strategic behavior
    • ⚖️ Multiple Prediction Modes - Regular, strategic, and robust inference options

    🧠 Intelligent Architecture

    • 💾 Prototype Memory - FAISS-powered efficient similarity search
    • 🔬 Adaptive Neural Layer - Trainable classification head with EWC protection
    • 🎯 Hybrid Predictions - Combines prototype similarity and neural network outputs
    • 📊 HuggingFace Integration - Push/pull models directly from the Hub

    📊 Performance & Benchmarks

    🛡️ Strategic Classification Defense

    Tested on adversarial examples from AI-Secure/adv_glue dataset:

    MetricRegular ClassifierStrategic ClassifierImprovement
    Clean Data Accuracy80.00%82.22%+2.22%
    Adversarial Data Accuracy60.00%82.22%+22.22%
    Robustness (vs attack)-20.00% drop0.00% dropPerfect

    🔍 Hallucination Detection

    Evaluated on RAGTruth benchmark across multiple task types:

    Task TypePrecisionRecallF1 Score
    QA35.50%45.11%39.74%
    Summarization22.18%96.91%36.09%
    Data-to-Text65.00%100.0%78.79%
    Overall40.89%80.68%51.54%

    🚦 LLM Router Optimization

    Tested on arena-hard-auto-v0.1 dataset (500 queries):

    MetricWithout AdaptationWith AdaptationImprovement
    Cost Savings25.60%32.40%+6.80%
    Efficiency Ratio1.00x1.27x+27%
    Resource UtilizationStandardOptimizedBetter

    Key Insight: Adaptive classification maintains quality while significantly improving cost efficiency and robustness across all tested scenarios.


    Try Now

    Use CaseDemonstratesLink
    Basic Example (Cat or Dog)Continuous learningOpen In Colab
    Support Ticket ClassificationRealistic examplesOpen In Colab
    Query ClassificationDifferent configurationsOpen In Colab
    Multilingual Sentiment AnalysisEnsemble of classifiersOpen In Colab
    Product Category ClassificationBatch processingOpen In Colab
    Multi-label ClassificationExtensibilityOpen In Colab

    🚀 Installation

    Quick Install

    pip install adaptive-classifier
    

    Includes: ONNX Runtime for 2-4x faster CPU inference out-of-the-box

    🛠️ Development Setup

    # Clone the repository
    git clone https://github.com/codelion/adaptive-classifier.git
    cd adaptive-classifier
    
    # Install in development mode with test dependencies
    pip install -e ".[test]"
    

    Pooling

    AdaptiveClassifier reduces a model's token embeddings to one vector per text. How it does that matters, because prototype memory is cosine nearest-neighbour search over those vectors.

    Before 0.2.0 the CLS token was always used. That is wrong for most encoders. all-MiniLM-L6-v2 publishes pooling_mode_mean_tokens, so it is a mean-pooling model, and a masked language model never trains its CLS token as a sentence representation at all. On a semantic-similarity check with all-MiniLM-L6-v2, CLS scored unrelated sentence pairs at 0.53 cosine similarity while mean pooling scored them at -0.03: a 2.4x difference in separation between related and unrelated text.

    From 0.2.0 the default is auto. The model's own sentence-transformers pooling config is used when it publishes one, otherwise mean pooling.

    # use what the model was trained with (default)
    classifier = AdaptiveClassifier("sentence-transformers/all-MiniLM-L6-v2")
    
    # or choose explicitly
    classifier = AdaptiveClassifier("bert-base-uncased", config={"pooling": "mean"})
    classifier = AdaptiveClassifier("bert-base-uncased", config={"pooling": "cls"})
    

    Classifiers saved before 0.2.0 keep CLS pooling when reloaded, so upgrading does not change their predictions. Retrain, or pass pooling explicitly, to pick up the new behaviour.

    How a prediction is made

    A prediction mixes two signals: the prototype score (how close the text is to each class's mean embedding) and the neural head (a small trained network on the same embeddings). prototype_weight and neural_weight (default 0.7 / 0.3) control the mix.

    classifier = AdaptiveClassifier(
        "sentence-transformers/all-MiniLM-L6-v2",
        config={"prototype_weight": 0.3, "neural_weight": 0.7},
    )
    

    The best split depends on how well your encoder separates your classes: an encoder that bunches everything together produces weak prototypes and is better off leaning on the head.

    Classes with few examples. Until a class has new_class_example_threshold examples (default 10), the head's weight ramps up linearly from zero to neural_weight and the prototype takes the rest, so a brand-new class is judged almost entirely by its prototype. (Before 0.3.0 such classes used a fixed 0.3 / 0.7 split in favour of the head, and a head fitted to a handful of points could confidently outvote a correct prototype. To get a fixed split back, set both new_class_prototype_weight and new_class_neural_weight.)

    Recognising a new class sooner. The ramp is the dial between "protect the classes you already have" and "pick up a new class quickly". Lowering new_class_example_threshold gives the head a say earlier. Measured with MiniLM, adding classes one at a time with 4 examples each (3 seeds, 600 test texts; averages over the added classes):

    new_class_example_thresholdag_news: final accuracy / new-class recall / old-class lossemotion: final accuracy / new-class recall / old-class loss
    10 (default)0.76 / 0.77 / 0.060.35 / 0.43 / 0.09
    50.74 / 0.85 / 0.120.35 / 0.59 / 0.14
    30.72 / 0.87 / 0.150.34 / 0.59 / 0.15
    fixed 0.3 / 0.7 (pre-0.3.0 split)0.66 / 0.91 / 0.220.32 / 0.68 / 0.18

    Each step buys new-class recall with old-class accuracy. If a new class that is missed matters more to you than one that disturbs the others, try 5; the default favours stability. Two datasets and one encoder, so check it on your own data.

    Prototype sharpness. Prototype scores are softmax(-distance² / prototype_temperature) with a default temperature of 0.25. Lower values make the nearest class stand out more. Set prototype_temperature to None for the scoring used before 0.3.0, which barely separated the nearest class from the rest.

    Head training. The head is trained for at least head_steps optimiser steps (default 300) with a cosine-decayed head_learning_rate (default 0.003). That takes roughly 1-2 seconds on a small memory; lower head_steps (for example to 100) if you add examples very frequently. Before 0.3.0 the head got about ten steps, too few to learn even a simple XOR pattern, which prototypes alone cannot separate.

    Classifiers saved before 0.3.0 keep their old scoring when reloaded, so upgrading does not change their predictions. Retrain, or set the keys above, to adopt the new behaviour.

    ⚡ Quick Start

    30-Second Setup

    Get started with adaptive classification in under 30 seconds:

    from adaptive_classifier import AdaptiveClassifier
    
    # 🎯 Step 1: Initialize with any HuggingFace model
    classifier = AdaptiveClassifier("bert-base-uncased")
    
    # 📝 Step 2: Add training examples
    texts = ["The product works great!", "Terrible experience", "Neutral about this purchase"]
    labels = ["positive", "negative", "neutral"]
    classifier.add_examples(texts, labels)
    
    # 🔮 Step 3: Make predictions
    predictions = classifier.predict("This is amazing!")
    print(predictions)  
    # Output: [('positive', 0.85), ('neutral', 0.12), ('negative', 0.03)]
    

    🏷️ Multi-Label Classification

    Classify texts into multiple categories simultaneously with automatic threshold adaptation:

    from adaptive_classifier import MultiLabelAdaptiveClassifier
    
    # Initialize multi-label classifier
    classifier = MultiLabelAdaptiveClassifier(
        "bert-base-uncased",
        min_predictions=1,    # Ensure at least 1 prediction
        max_predictions=5     # Limit to top 5 predictions
    )
    
    # Multi-label training data (each text can have multiple labels)
    texts = [
        "AI researchers study climate change using machine learning",
        "Tech startup develops healthcare solutions"
    ]
    labels = [
        ["technology", "science", "climate", "ai"],
        ["technology", "business", "healthcare"]
    ]
    
    classifier.add_examples(texts, labels)
    
    # Make multi-label predictions
    predictions = classifier.predict_multilabel("Medical AI breakthrough announced")
    # Output: [('healthcare', 0.72), ('technology', 0.68), ('ai', 0.45)]
    

    💾 Save & Load Models

    # Save locally
    classifier.save("./my_classifier")
    loaded_classifier = AdaptiveClassifier.load("./my_classifier")
    
    # 🤗 HuggingFace Hub Integration
    classifier.push_to_hub("adaptive-classifier/my-model")
    hub_classifier = AdaptiveClassifier.from_pretrained("adaptive-classifier/my-model")
    

    🎮 Strategic Defense (Anti-Gaming)

    # Enable strategic classification for adversarial robustness
    config = {'enable_strategic_mode': True}
    strategic_classifier = AdaptiveClassifier("bert-base-uncased", config=config)
    
    # Robust predictions against manipulation
    predictions = strategic_classifier.predict("This product has amazing quality features!")
    # Returns predictions that consider potential gaming attempts
    

    ⚡ Optimized CPU Inference with ONNX

    Adaptive Classifier includes built-in ONNX Runtime support for 2-4x faster CPU inference with zero code changes required.

    Automatic Optimization (Default)

    ONNX Runtime is automatically used on CPU for optimal performance:

    # Automatically uses ONNX on CPU, PyTorch on GPU
    classifier = AdaptiveClassifier("bert-base-uncased")
    
    # That's it! Predictions are 2-4x faster on CPU
    predictions = classifier.predict("Fast inference!")
    
    Performance Comparison
    ConfigurationSpeedUse Case
    PyTorch (GPU)FastestGPU servers
    ONNX (CPU)2-4x fasterProduction CPU deployments
    PyTorch (CPU)BaselineDevelopment, training
    Save & Deploy with ONNX
    # Save with ONNX export (both quantized & unquantized versions)
    classifier.save("./model")
    
    # Push to Hub with ONNX (both versions included by default)
    classifier.push_to_hub("username/model")
    
    # Load automatically uses quantized ONNX on CPU (fastest, 4x smaller)
    fast_classifier = AdaptiveClassifier.load("./model")
    
    # Choose unquantized ONNX for maximum accuracy
    accurate_classifier = AdaptiveClassifier.load("./model", prefer_quantized=False)
    
    # Force PyTorch (no ONNX)
    pytorch_classifier = AdaptiveClassifier.load("./model", use_onnx=False)
    
    # Opt-out of ONNX export when saving
    classifier.save("./model", include_onnx=False)
    

    ONNX Model Versions:

    • Quantized (default): INT8 quantized, 4x smaller, ~1.14x faster on ARM, 2-4x faster on x86
    • Unquantized: Full precision, maximum accuracy, larger file size

    By default, models are saved with both versions, and the quantized version is automatically loaded for best performance. Use prefer_quantized=False if you need maximum accuracy.

    Benchmark Your Model
    # Compare PyTorch vs ONNX performance
    python scripts/benchmark_onnx.py --model bert-base-uncased --runs 100
    

    Example Results:

    Model: bert-base-uncased (CPU)
    PyTorch:  8.3ms/query  (baseline)
    ONNX:     2.1ms/query  (4.0x faster) ✓
    

    Note: ONNX optimization is included by default. For GPU inference, PyTorch is automatically used for best performance.

    Multilingual models and other runtimes (.NET, JavaScript, ...)

    Any multilingual encoder works as the base model, and a saved classifier can be run from C#, Node or the browser through an ONNX runtime. See the deployment guide and the dependency-light reference implementation in examples/portable_inference.py.

    Advanced Usage

    scikit-learn Interface

    SklearnAdaptiveClassifier works with Pipeline, cross_val_score and GridSearchCV. X is a list of strings.

    from adaptive_classifier import SklearnAdaptiveClassifier
    from sklearn.model_selection import cross_val_score
    
    clf = SklearnAdaptiveClassifier("sentence-transformers/all-MiniLM-L6-v2")
    clf.fit(texts, labels)
    clf.predict(["Where is my refund?"])          # class labels
    clf.predict_proba(["Where is my refund?"])    # columns follow clf.classes_
    cross_val_score(clf, texts, labels, cv=5)
    
    # The library's strength: keep learning, including brand-new classes
    clf.partial_fit(["App crashes on login"], ["bug"])
    
    clf.classifier_.save("./model")               # the underlying AdaptiveClassifier
    

    fit starts over each time; partial_fit adds to what is already learned. A fitted estimator can be pickled, but that copies the whole encoder, so save classifier_ instead.

    Calibrated Confidence and Prediction Sets

    Raw scores are not probabilities: a model can say 0.9 and be right 70% of the time. Fit a calibration on labelled examples the model was not trained on:

    info = classifier.calibrate(held_out_texts, held_out_labels)
    info["ece_before"], info["ece_after"]       # expected calibration error, lower is better
    
    classifier.predict(text)                    # confidences now match how often the model is right
    classifier.predict(text, abstain_below=0.8) # so thresholds like this mean what they say
    
    # A set of labels that contains the true one at least 90% of the time
    classifier.predict_set(text, alpha=0.1)     # [("billing", 0.55), ("refunds", 0.31)]
    classifier.calibration_report(test_texts, test_labels)   # check it on separate data
    

    One easy input gives a one-label set; an ambiguous one gives several. Calibration is saved with the model and discarded if you add or forget a class; re-run it after substantial new data.

    Active Learning and Drift

    # Which of these 5,000 unlabeled texts should a person label next?
    for index, score in classifier.suggest_labels(unlabeled, n=20, strategy="margin", diverse=True):
        print(unlabeled[index])
    
    classifier.suggest_labels(unlabeled, n=20, strategy="ood")   # hunt for classes you have not defined yet
    
    # Has this week's traffic moved away from what the model knows?
    report = classifier.drift_report(recent_texts)
    report["drifted"], report["ood_rate"], report["p_value"]
    

    margin (the default) finds texts on a decision boundary; ood finds texts unlike any known class. Uncertainty sampling is not guaranteed to beat labeling at random: when classes overlap so much that the confusion is irreducible noise, labeling the confusing texts teaches the model little (it did not in a synthetic test with heavily overlapping classes). It earns its keep when ambiguity comes from missing data, and ood is the reliable way to find classes you have not defined yet.

    Serving

    pip install "adaptive-classifier[serve]"
    python -m adaptive_classifier.serving ./my_classifier --port 8000
    curl -s localhost:8000/predict -H 'content-type: application/json' -d '{"text": "where is my refund?"}'
    

    Predictions, batches, conformal sets and out-of-distribution checks over HTTP, plus optional API-key-protected endpoints for adding examples and classes while it runs. One classifier is safe to share between threads, and await classifier.apredict(...) keeps an event loop responsive. See docs/serving.md and docker/Dockerfile.

    Fixing Mistakes and Knowing When to Abstain

    # Remove a wrongly labelled example, or a whole class
    classifier.remove_examples(["Refund my order"], label="technical")
    classifier.forget("obsolete_class")
    
    # Detect inputs unlike anything the classifier has seen
    classifier.is_ood("completely unrelated text")        # True / False
    classifier.ood_score("completely unrelated text")     # ~1 or below = familiar, higher = further out
    
    # Abstain instead of guessing (an empty list means "don't know")
    classifier.predict(text, abstain_below=0.6)           # low confidence
    classifier.predict(text, abstain_ood=True)            # out of distribution
    

    Out-of-distribution scores compare a text's distance from the nearest class prototype to that class's own spread, so no per-model tuning of absolute distances is needed. The default ood_threshold of 1.25 is a starting point; check it on held-out data.

    Adding New Classes Dynamically

    # Add a completely new class
    new_texts = [
        "Error code 404 appeared",
        "System crashed after update"
    ]
    new_labels = ["technical"] * 2
    
    classifier.add_examples(new_texts, new_labels)
    

    Continuous Learning

    # Add more examples to existing classes
    more_examples = [
        "Best purchase ever!",
        "Highly recommend this"
    ]
    more_labels = ["positive"] * 2
    
    classifier.add_examples(more_examples, more_labels)
    

    Multi-Label Classification with Advanced Configuration

    from adaptive_classifier import MultiLabelAdaptiveClassifier
    
    # Configure advanced multi-label settings
    classifier = MultiLabelAdaptiveClassifier(
        "bert-base-uncased",
        default_threshold=0.5,      # Base threshold for predictions
        min_predictions=1,          # Minimum labels to return
        max_predictions=10          # Maximum labels to return
    )
    
    # Training with diverse multi-label examples
    texts = [
        "Scientists develop AI for medical diagnosis and climate research",
        "Tech company launches sustainable energy and healthcare products",
        "Olympic athletes use sports science and nutrition technology"
    ]
    labels = [
        ["science", "ai", "healthcare", "research"],
        ["technology", "business", "environment", "healthcare"],
        ["sports", "science", "health", "technology"]
    ]
    
    classifier.add_examples(texts, labels)
    
    # Advanced prediction options
    predictions = classifier.predict_multilabel(
        "New research on AI applications in environmental science",
        threshold=0.3,     # Custom threshold
        max_labels=5       # Limit results
    )
    
    # Get detailed statistics
    stats = classifier.get_label_statistics()
    print(f"Adaptive threshold: {stats['adaptive_threshold']}")
    print(f"Label-specific thresholds: {stats['label_thresholds']}")
    

    Strategic Classification (Anti-Gaming)

    # Enable strategic mode to defend against adversarial inputs
    config = {
        'enable_strategic_mode': True,
        'cost_function_type': 'linear',
        # One cost per embedding dimension (384 for all-MiniLM-L6-v2, 768 for BERT-base):
        # how expensive it is for an adversary to push that dimension upward.
        'cost_coefficients': [0.3] * 768,
        'strategic_blend_regular_weight': 0.6,   # Weight for regular predictions
        'strategic_blend_strategic_weight': 0.4  # Weight for strategic predictions
    }
    
    classifier = AdaptiveClassifier("bert-base-uncased", config=config)
    classifier.add_examples(texts, labels)
    # If cost_coefficients has the wrong length, strategic mode is switched off and the
    # reason is logged; check classifier.strategic_mode.
    
    # Robust predictions that consider potential manipulation
    text = "This product has amazing quality features!"
    
    # Dual prediction (automatic blend of regular + strategic)
    predictions = classifier.predict(text)
    
    # Pure strategic prediction (simulates adversarial manipulation)
    strategic_preds = classifier.predict_strategic(text)
    
    # Robust prediction (assumes input may already be manipulated)
    robust_preds = classifier.predict_robust(text)
    
    print(f"Dual: {predictions}")
    print(f"Strategic: {strategic_preds}")
    print(f"Robust: {robust_preds}")
    

    🏷️ Multi-Label Classification

    The MultiLabelAdaptiveClassifier extends adaptive classification to handle scenarios where each text can belong to multiple categories simultaneously. It automatically handles threshold adaptation for scenarios with many labels.

    Key Features

    • 🎯 Automatic Threshold Adaptation: Dynamically adjusts thresholds based on the number of labels to prevent empty predictions
    • 📊 Sigmoid Activation: Uses proper multi-label architecture with BCE loss instead of softmax
    • ⚙️ Configurable Limits: Set minimum and maximum number of predictions per input
    • 📈 Label-Specific Thresholds: Automatically adjusts thresholds based on label frequency
    • 🔄 Incremental Learning: Add new labels and examples without retraining from scratch

    Usage

    from adaptive_classifier import MultiLabelAdaptiveClassifier
    
    # Initialize with configuration
    classifier = MultiLabelAdaptiveClassifier(
        "distilbert/distilbert-base-cased",
        default_threshold=0.5,
        min_predictions=1,
        max_predictions=5
    )
    
    # Multi-label training data
    texts = [
        "Breaking: Scientists discover AI can help predict climate change patterns",
        "Tech giant announces breakthrough in quantum computing for healthcare",
        "Olympic committee adopts new sports technology for athlete performance"
    ]
    
    labels = [
        ["science", "technology", "climate", "news"],
        ["technology", "healthcare", "quantum", "business"],
        ["sports", "technology", "performance", "news"]
    ]
    
    # Train the classifier
    classifier.add_examples(texts, labels)
    
    # Make predictions
    predictions = classifier.predict_multilabel(
        "Revolutionary medical AI system launched by tech startup"
    )
    
    # Results: [('technology', 0.85), ('healthcare', 0.72), ('business', 0.45)]
    

    Adaptive Thresholds

    The classifier automatically adjusts prediction thresholds based on the number of labels:

    Number of LabelsThresholdBenefit
    2-4 labels0.5 (default)Standard precision
    5-9 labels0.4 (20% lower)Balanced recall
    10-19 labels0.3 (40% lower)Better coverage
    20-29 labels0.2 (60% lower)Prevents empty results
    30+ labels0.1 (80% lower)Ensures predictions

    This solves the common "No labels met the threshold criteria" issue when dealing with many-label scenarios.


    🏢 Enterprise Use Cases

    🔍 Hallucination Detection

    Detect when LLMs generate information not supported by provided context (51.54% F1, 80.68% recall):

    detector = AdaptiveClassifier.from_pretrained("adaptive-classifier/llm-hallucination-detector")
    context = "France is in Western Europe. Capital: Paris. Population: ~67 million."
    response = "Paris is the capital. Population is 70 million."  # Contains hallucination
    
    prediction = detector.predict(f"Context: {context}\nAnswer: {response}")
    # Returns: [('HALLUCINATED', 0.72), ('NOT_HALLUCINATED', 0.28)]
    

    🚦 Intelligent LLM Routing

    Optimize costs by routing queries to appropriate model tiers (32.40% cost savings):

    router = AdaptiveClassifier.from_pretrained("adaptive-classifier/llm-router")
    query = "Write a function to calculate Fibonacci sequence"
    
    predictions = router.predict(query)
    # Returns: [('HIGH', 0.92), ('LOW', 0.08)]
    # Route to GPT-4 for complex tasks, GPT-3.5 for simple ones
    

    ⚙️ Configuration Optimization

    Automatically predict optimal LLM settings (temperature, top_p) for different query types:

    config_optimizer = AdaptiveClassifier.from_pretrained("adaptive-classifier/llm-config-optimizer")
    query = "Explain quantum physics concepts"
    
    predictions = config_optimizer.predict(query)
    # Returns: [('BALANCED', 0.85), ('CREATIVE', 0.10), ...]
    # Automatically suggests temperature range: 0.6-1.0 for balanced responses
    

    🛡️ Content Moderation

    Deploy enterprise-ready classifiers for various moderation tasks:

    # Available pre-trained enterprise classifiers:
    classifiers = [
        "adaptive-classifier/content-moderation",      # Content safety
        "adaptive-classifier/business-sentiment",      # Business communications
        "adaptive-classifier/pii-detection",           # Privacy protection
        "adaptive-classifier/fraud-detection",         # Financial security
        "adaptive-classifier/email-priority",          # Email routing
        "adaptive-classifier/compliance-classification" # Regulatory compliance
    ]
    
    # Easy deployment
    moderator = AdaptiveClassifier.from_pretrained("adaptive-classifier/content-moderation")
    result = moderator.predict("User generated content here...")
    

    💡 Pro Tip: All enterprise models support continuous adaptation - add your domain-specific examples to improve performance over time.


    Architecture Overview

    The Adaptive Classifier combines four key components in a unified architecture:

    Adaptive Classifier Architecture

    1. Transformer Embeddings: Uses state-of-the-art language models for text representation

    2. Prototype Memory: Maintains class prototypes for quick adaptation to new examples

    3. Adaptive Neural Layer: Learns refined decision boundaries through continuous training

    4. Strategic Classification: Defends against adversarial manipulation using game-theoretic principles. When strategic mode is enabled, the system:

      • Models potential strategic behavior of users trying to game the classifier
      • Uses cost functions to represent the difficulty of manipulating different features
      • Combines regular predictions with strategic-aware predictions for robustness
      • Provides multiple prediction modes: dual (blended), strategic (simulates manipulation), and robust (anti-manipulation)

    Why Adaptive Classification?

    Traditional classification approaches face significant limitations when dealing with evolving requirements and adversarial environments:

    Traditional vs Adaptive Classification

    The Adaptive Classifier overcomes these limitations through:

    • Dynamic class addition without full retraining
    • Strategic robustness against adversarial manipulation
    • Memory-efficient prototypes with FAISS optimization
    • Zero downtime updates for production systems
    • Game-theoretic defense mechanisms

    Continuous Learning Process

    The system evolves through distinct phases, each building upon previous knowledge without catastrophic forgetting:

    Continuous Learning Workflow

    The learning process includes:

    • Initial Training: Bootstrap with basic classes
    • Dynamic Addition: Seamlessly add new classes as they emerge
    • Continuous Learning: Refine decision boundaries with EWC protection
    • Strategic Enhancement: Develop robustness against manipulation
    • Production Deployment: Full capability with ongoing adaptation

    Order Dependency in Online Learning

    When using the adaptive classifier for true online learning (adding examples incrementally), be aware that the order in which examples are added can affect predictions. This is inherent to incremental neural network training.

    The Challenge

    # These two scenarios may produce slightly different models:
    
    # Scenario 1
    classifier.add_examples(["fish example"], ["aquatic"])
    classifier.add_examples(["bird example"], ["aerial"])
    
    # Scenario 2  
    classifier.add_examples(["bird example"], ["aerial"])
    classifier.add_examples(["fish example"], ["aquatic"])
    

    While we've implemented sorted label ID assignment to minimize this effect, the neural network component still learns incrementally, which can lead to order-dependent behavior.

    Solution: Prototype-Only Predictions

    For applications requiring strict order independence, you can configure the classifier to rely solely on prototype-based predictions:

    # Configure to use only prototypes (order-independent)
    config = {
        'prototype_weight': 1.0,  # Use only prototypes
        'neural_weight': 0.0      # Disable neural network contribution
    }
    
    classifier = AdaptiveClassifier("bert-base-uncased", config=config)
    

    With this configuration:

    • Predictions are based solely on similarity to class prototypes (mean embeddings)
    • Results are completely order-independent
    • Trade-off: May have slightly lower accuracy than the hybrid approach

    Best Practices

    1. For maximum consistency: Use prototype-only configuration
    2. For maximum accuracy: Accept some order dependency with the default hybrid approach
    3. For production systems: Consider batching updates and retraining periodically if strict consistency is required
    4. Model selection matters: Some models (e.g., google-bert/bert-large-cased) may produce poor embeddings for single words. For better results with short inputs, consider:
      • bert-base-uncased
      • sentence-transformers/all-MiniLM-L6-v2
      • Or any model specifically trained for semantic similarity

    🔗 Related Projects

    • OpenEvolve - Open-source evolutionary coding agent for algorithm discovery
    • OptiLLM - Optimizing inference proxy with 20+ techniques for 2-10x accuracy improvements

    🤝 Community & Contributing

    📚 References

    📜 Citation

    If you use this library in your research, please cite:

    @software{adaptive-classifier,
      title = {Adaptive Classifier: Dynamic Text Classification with Continuous Learning},
      author = {Asankhaya Sharma},
      year = {2025},
      publisher = {GitHub},
      url = {https://github.com/codelion/adaptive-classifier}
    }