DADOES / README.md
Ryan.K
Position DADOES around microsecond inference
f402098 unverified
|
Raw
History Blame Contribute Delete
12.5 kB
---
license: agpl-3.0
language:
- en
pipeline_tag: text-classification
library_name: rust
datasets:
- google-research-datasets/go_emotions
- yael-katsman/Loneliness-Causes-and-Intensity
- smit18/text_emotion
- PrajwalNayaka/Text-Emotion
- Um1neko/text_emotion
metrics:
- f1
tags:
- text-to-mood
- microsecond-inference
- mood-detection
- multi-label-classification
- rust-native
- dadoes
---
# DADOES
Do Androids Dream of Electric Sheep.
DADOES is a microsecond-level text-to-mood inference engine for Rust projects.
The crate can be used as a library by other Rust projects. It also includes CLI
and training binaries for local experimentation.
DADOES is not positioned as a replacement for transformer GoEmotions models on
full-label accuracy. Its advantage is deployment shape: a 1.7 MB checkpoint,
no Python, no PyTorch, no GPU, no Transformers runtime, and local Rust
inference at 281,972.5 texts/s, or 3.546 us/text, on the current benchmark.
Use it as a cheap first mood signal for agent reports, chat logs,
issue/support routing, local log or inbox triage, and high-volume text-stream
pre-filtering.
The current implementation is a compact inference baseline:
- multi-label mood taxonomy
- hashed text features
- linear sigmoid classifier
- SGD training on a public mixed English mood dataset assembled from
GoEmotions-compatible sources and DADOES seed examples
- validation-loss early stopping
- 1.7 MB binary checkpoint
- binary checkpoint save/load
- JSON CLI output
The classifier core is intentionally small. Dataset import uses structured CSV
and JSON parsers at the training boundary, while the public Rust interface stays
stable for downstream projects.
## Why DADOES
| Property | Current Value |
|---|---:|
| Default checkpoint | 1,704,033 bytes |
| Mean classification latency | 3.546 us/text |
| Throughput | 281,972.5 texts/s |
| Runtime dependencies | no Python, PyTorch, GPU, or Transformers runtime |
| Integration shape | Rust library, CLI, embedded default checkpoint |
## Library Usage
After the first crates.io release, add DADOES as a normal dependency:
```toml
[dependencies]
dadoes = "0.1.0"
```
For this checkout before release, use a local dependency:
```toml
[dependencies]
dadoes = { path = "/Users/ryan/DADOES" }
```
Use the embedded default checkpoint:
```rust
use dadoes::{DadoesClassifier, EmotionClassifier};
fn main() -> Result<(), dadoes::ModelIoError> {
let classifier = DadoesClassifier::from_default_model()?;
let analysis = classifier.classify(
"I missed the deadline again and felt frustrated and exhausted.",
);
if let Some(primary) = analysis.primary_mood() {
println!("primary={} score={:.3}", primary.mood.as_str(), primary.score);
}
for score in classifier.active_moods(&analysis) {
println!("active={} score={:.3}", score.mood.as_str(), score.score);
}
Ok(())
}
```
Load a custom checkpoint when you train one:
```rust
let classifier = dadoes::DadoesClassifier::from_path("models/custom.dadoes")?;
```
Runnable example:
```bash
cargo run --release --example library_usage
```
## Run
```bash
cargo run --release --bin dadoes -- "I missed the deadline again and felt frustrated and exhausted tonight."
```
The command prints:
```json
{"primary_mood":"tired","moods":[...]}
```
The CLI loads `models/goemotions-linear.dadoes` when it exists. If no local
checkpoint exists, it falls back to the embedded default checkpoint.
## Current Evaluation
The headline metric for DADOES is inference cost. Accuracy is reported here to
make the current signal quality inspectable, not to claim state-of-the-art mood
understanding.
This is a multi-label classifier, so a single "accuracy" number is less useful
than F1 and exact multi-label match rate. The closest strict accuracy measure is
`exact_match`, where every predicted label for an example must match.
Current default checkpoint:
| Split | Examples | Loss | Micro Precision | Micro Recall | Micro F1 | Exact Match |
|---|---:|---:|---:|---:|---:|---:|
| Public mixed validation | 9,423 | 0.3474 | 0.7763 | 0.5359 | 0.6341 | 0.2971 |
| Public mixed test | 10,032 | 0.3445 | 0.7832 | 0.5467 | 0.6439 | 0.3069 |
Training stopped at epoch 28, with the best validation checkpoint selected from
epoch 24.
These numbers are for the current linear hashed-feature baseline trained on the
public mixed dataset. GoEmotions is one source in that mix; optional external
sources under `data/raw/external` and DADOES seed examples are also used. They
should be treated as a baseline, not a production ceiling.
Signal quality is uneven across labels. The current checkpoint is strongest on
`happy`, `satisfied`, `hopeful`, and `lonely`, with `lonely` measured on a
small 153-example supervised slice; weaker labels include
`frustrated` at F1 0.2255, `curious` at F1 0.3839, `angry` at F1 0.4187,
`sad` at F1 0.4328, and `excited` at F1 0.4362. `bored` and `tired` do not yet
have supervised examples in the loaded mixed test, so they require held-out
reviewed data before per-label accuracy can be reported.
Mixed per-mood diagnostic metrics at threshold `0.35`:
| Mood | Supervised Examples | Positives | Accuracy | Precision | Recall | F1 | Coverage |
|---|---:|---:|---:|---:|---:|---:|---|
| happy | 9,879 | 3,534 | 0.8476 | 0.9135 | 0.6338 | 0.7484 | loaded mixed test |
| satisfied | 9,879 | 4,087 | 0.8198 | 0.8122 | 0.7343 | 0.7713 | loaded mixed test |
| excited | 9,879 | 1,504 | 0.8712 | 0.6543 | 0.3271 | 0.4362 | loaded mixed test |
| curious | 5,427 | 677 | 0.8841 | 0.5698 | 0.2895 | 0.3839 | loaded mixed test |
| anxious | 9,879 | 934 | 0.9288 | 0.6567 | 0.5182 | 0.5793 | loaded mixed test |
| frustrated | 5,427 | 699 | 0.8734 | 0.5319 | 0.1431 | 0.2255 | loaded mixed test |
| sad | 9,879 | 1,028 | 0.9167 | 0.7423 | 0.3054 | 0.4328 | loaded mixed test |
| angry | 9,879 | 1,063 | 0.9120 | 0.7245 | 0.2944 | 0.4187 | loaded mixed test |
| lonely | 153 | 89 | 0.7190 | 0.8286 | 0.6517 | 0.7296 | loaded mixed test |
| hopeful | 9,879 | 2,943 | 0.8631 | 0.8299 | 0.6799 | 0.7475 | loaded mixed test |
| neutral | 9,879 | 2,736 | 0.7973 | 0.6869 | 0.4931 | 0.5740 | loaded mixed test |
The loaded mixed test currently has no supervised examples for `bored` or
`tired`; those labels should not be published as `0/0` metrics. They require
held-out reviewed examples before a per-label accuracy can be reported.
Microsecond inference benchmark:
| Benchmark | Value |
|---|---:|
| Build | `cargo run --release --bin evaluate` |
| Platform | local `arm64`, `rustc 1.95.0` |
| Model load time | 1.356 ms |
| Test examples | 10,032 |
| Repeats | 50 |
| Total classifications | 501,600 |
| Total classification time | 1,778.897 ms |
| Throughput | 281,972.5 texts/s |
| Mean classification latency | 3.546 us/text |
Benchmark numbers measure classification after loading the model and should be
treated as local-machine measurements, not portable latency guarantees.
## Comparison
DADOES should not be read as "more accurate than BERT." The useful comparison
is operational: when a Rust project needs cheap local text-to-mood inference,
DADOES avoids the Python/PyTorch/Transformers deployment stack and runs from a
small binary checkpoint.
The table below is a reference comparison, not a leaderboard. DADOES uses a
smaller 13-label runtime taxonomy and shows local Rust performance. Most
public GoEmotions models publish the original 28-label task, often with
different thresholds, hardware, and evaluation scripts.
Accuracy comparison:
| Model | Evaluation Setup | Reported Result | Notes |
|---|---|---:|---|
| DADOES public mixed linear baseline | 13 DADOES labels, public mixed test | micro F1 0.6439 | Local result from `cargo run --release --bin train`; smaller label space than GoEmotions. |
| [GoEmotions BERT baseline](https://research.google/pubs/goemotions-a-dataset-of-fine-grained-emotions/) | Original GoEmotions taxonomy | average F1 0.46 | Official GoEmotions paper baseline. |
| [SamLowe/roberta-base-go_emotions](https://huggingface.co/SamLowe/roberta-base-go_emotions) | GoEmotions 28-label test, threshold 0.5 | F1 0.450 | Model-card aggregate metric. |
| [tasinhoque/distilbert-go-emotions](https://huggingface.co/tasinhoque/distilbert-go-emotions) | GoEmotions evaluation set | F1 0.4702 | Model-card aggregate metric. |
| [sangkm/go-emotions-fine-tuned-distilroberta](https://huggingface.co/sangkm/go-emotions-fine-tuned-distilroberta) | GoEmotions, threshold 0.5 | micro F1 0.5790 | Model-card metrics include macro F1 0.4502. |
| [sangkm/augmented-go-emotions-plus-other-datasets-fine-tuned-distilroberta-v3](https://huggingface.co/sangkm/augmented-go-emotions-plus-other-datasets-fine-tuned-distilroberta-v3) | GoEmotions plus augmented data | micro F1 0.6288 | Model-card metrics include macro F1 0.4950. |
| [cirimus/modernbert-base-go-emotions](https://huggingface.co/cirimus/modernbert-base-go-emotions) | GoEmotions test | micro F1 0.607 | Model-card metrics include macro F1 0.550. |
Microsecond inference performance:
| Model | Runtime / Artifact | Reported Performance | Notes |
|---|---|---:|---|
| DADOES public mixed linear baseline | Rust native checkpoint, 1.7 MB | 281,972.5 texts/s; 3.546 us/text | Local `arm64` benchmark from `cargo run --release --bin evaluate`. |
## Adoption Roadmap
- Publish the crate to crates.io; the manifest no longer disables publishing.
- Add a Hugging Face Space demo that accepts text and returns DADOES JSON.
- Run same-test-set baselines for fastText, TF-IDF logistic regression,
DistilRoBERTa, ModernBERT, and DADOES.
- Add DADOES-owned reviewed labels for `bored`, `tired`, `frustrated`, and
`lonely`.
- Keep the public positioning focused on microsecond-level Rust-native
text-to-mood inference, not transformer-level full-taxonomy accuracy.
## Train
Prepare the base GoEmotions split files in `data/raw/goemotions`, then run:
```bash
cargo run --release --bin train
```
Optional external datasets are read from `data/raw/external`. Public raw-file
sources are downloaded by the Rust data-preparation binary and included
automatically when their files exist:
```bash
cargo run --release --features dataset-download --bin prepare_external_datasets
cargo run --release --bin train
```
Non-commercial sources such as EmpatheticDialogues and FIG-Loneliness are kept
behind an explicit research switch. The trainer can read their exported local
CSV/JSONL files, but DADOES does not depend on Python or Hugging Face
`datasets` to produce them:
```bash
DADOES_INCLUDE_NON_COMMERCIAL=1 cargo run --release --bin train
```
The trainer skips missing optional files, but fails on malformed files that are
present. Use `DADOES_EXTERNAL_DATA_DIR=/path/to/external` to override the
external-data root.
To regenerate the evaluation and benchmark tables:
```bash
cargo run --release --bin evaluate
```
The default trainer:
- uses `train.tsv` for gradient updates
- uses `dev.tsv` for early stopping
- starts from `test.tsv` and mixes optional external test splits before final
evaluation
- repeats the local seed examples 4 times to cover DADOES labels that
GoEmotions does not annotate, such as `lonely`, `bored`, and `tired`
- mixes optional public external sources when prepared under `data/raw/external`
- only mixes non-commercial sources when `DADOES_INCLUDE_NON_COMMERCIAL=1`
Current checkpoint summary:
```text
best_epoch=24
epochs_trained=28
validation_micro_f1=0.6341
validation_exact_match=0.2971
test_micro_f1=0.6439
test_exact_match=0.3069
```
The full report is saved at `models/goemotions-linear.report.json`.
## License
DADOES is licensed under the GNU Affero General Public License v3.0 only.
## Dataset Direction
Use a public mixed dataset assembled from GoEmotions and compatible external
sources, then add curated DADOES-domain text.
Keep the runtime labels smaller than GoEmotions:
- happy
- satisfied
- excited
- curious
- anxious
- frustrated
- sad
- angry
- lonely
- bored
- tired
- hopeful
- neutral
The missing high-priority dimensions are `lonely`, `bored`, and `tired`.
GoEmotions does not directly annotate them. The current public mixed checkpoint
adds direct `lonely` supervision from public loneliness data, while `bored` and
`tired` still only have weak coverage from the small seed set unless
DADOES-owned reviewed labels are added.
The project data format is documented in `data/README.md`. The public dataset
survey and coverage matrix are documented in `data/DATASETS.md`.