classifier 2.6.0 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +165 -0
- data/README.md +76 -11
- data/docs/README.md +49 -0
- data/docs/bayes.md +108 -0
- data/docs/cli.md +144 -0
- data/docs/configuration.md +100 -0
- data/docs/keywords.md +171 -0
- data/docs/knn.md +92 -0
- data/docs/logistic-regression.md +107 -0
- data/docs/lsi.md +219 -0
- data/docs/persistence.md +124 -0
- data/docs/streaming.md +104 -0
- data/docs/tfidf.md +137 -0
- data/exe/classifier +0 -1
- data/exe/keywords +15 -0
- data/lib/classifier/bayes.rb +5 -2
- data/lib/classifier/extensions/word_hash.rb +20 -0
- data/lib/classifier/keywords/cli.rb +299 -0
- data/lib/classifier/lsi.rb +3 -3
- data/lib/classifier/streaming/line_reader.rb +9 -7
- data/lib/classifier/streaming/multi_io.rb +51 -0
- data/lib/classifier/streaming.rb +1 -0
- data/lib/classifier/tfidf.rb +3 -2
- data/lib/classifier/version.rb +1 -1
- metadata +19 -4
- data/CLAUDE.md +0 -77
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 28ad3396d95e315e11525b87393aaf3d081cf537f63dee0ba89fdd1a8acab4ee
|
|
4
|
+
data.tar.gz: 03b0b12d04d9ed05878a1b5a659383b72eb4eba12b6eaecf6b02b0356ee524b9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3675894eaf015c2a2b1f49fcc62d4ad55d487955f5978628d48563f90d5513b20f0ed3e2d7f6b2fc9498c99971c71482c67a1342b3eb39f027e20ca0f2fa6ee5
|
|
7
|
+
data.tar.gz: ce38803fe759fce8f02013a9d5a6bc613af95ccf4c132ff8c203f961dffabcc0b1eca7db642fad4e3e488fdac5093dd62c8fdafde1831f2d9aa97d15f07fcb62
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 2.7.0 - 2026-08-15
|
|
4
|
+
|
|
5
|
+
- Add a `keywords` executable for TF-IDF keyword extraction. `keywords fit`
|
|
6
|
+
builds a vocabulary from files or standard input, `keywords extract` reads a
|
|
7
|
+
file, `keywords info` prints model statistics, and a bare text argument
|
|
8
|
+
transforms that text. The command prints `term:score` pairs and maps stems
|
|
9
|
+
back to whole words, so `keywords "Ruby is elegant"` prints
|
|
10
|
+
`elegant:0.61 ruby:0.61`. Options set the model path, the top-N terms, quiet
|
|
11
|
+
mode, `--min-df`, `--max-df`, and `--ngram`. Usage errors exit 2 and other
|
|
12
|
+
errors exit 1. The gem now installs two executables, `classifier` and
|
|
13
|
+
`keywords`.
|
|
14
|
+
- Treat each line as a separate document during `keywords fit`. The document
|
|
15
|
+
count controls the inverse document frequency, so a file with many lines
|
|
16
|
+
contributes many documents.
|
|
17
|
+
- Add `Classifier::Streaming::MultiIO`. It reads several IO objects or file
|
|
18
|
+
paths as one sequential stream. It opens and closes each path one at a time,
|
|
19
|
+
so a corpus larger than the file descriptor limit still fits.
|
|
20
|
+
- Add `String#stem_to_word_hash`. It maps each stemmed root to the most
|
|
21
|
+
frequent original word in the text.
|
|
22
|
+
- Add the `min_df` and `max_df` readers to `Classifier::TFIDF`.
|
|
23
|
+
- Accept a `MultiIO` in `TFIDF#fit_from_stream` and `Streaming::LineReader`.
|
|
24
|
+
- Fix `Marshal` support in `Classifier::Bayes`. The dump left out
|
|
25
|
+
`min_word_length`, so the restored classifier raised
|
|
26
|
+
`ArgumentError: comparison of Integer with nil failed` on its first
|
|
27
|
+
`classify`. A dump written by an older version still loads, and takes the
|
|
28
|
+
configured default.
|
|
29
|
+
- Fix `LSI#highest_relative_content`, which returned an Enumerator rather than
|
|
30
|
+
the documented array of documents.
|
|
31
|
+
- Fix `LSI#highest_ranked_stems`, which repeated one stem when the document
|
|
32
|
+
vector held equal weights. It looked up each weight by value, so tied weights
|
|
33
|
+
all resolved to the same index.
|
|
34
|
+
- Add a `docs/` reference for the command line tools, each classifier,
|
|
35
|
+
persistence, streaming, and configuration.
|
|
36
|
+
|
|
37
|
+
## 2.6.0 - 2026-06-25
|
|
38
|
+
|
|
39
|
+
- Add a `--search` flag to the command line tool to filter local models.
|
|
40
|
+
- Add a model detail view to the command line tool.
|
|
41
|
+
- Read model information in advance when the tool lists local models.
|
|
42
|
+
|
|
43
|
+
## 2.5.0 - 2026-06-01
|
|
44
|
+
|
|
45
|
+
- Accept keyword arguments in `train_from_stream`.
|
|
46
|
+
|
|
47
|
+
## 2.4.0 - 2026-05-19
|
|
48
|
+
|
|
49
|
+
- Make `min_word_length` configurable, so a caller can keep or drop short
|
|
50
|
+
words during tokenization.
|
|
51
|
+
- Add a Claude Code plugin with a skill and slash commands.
|
|
52
|
+
|
|
53
|
+
## 2.3.2 - 2026-01-01
|
|
54
|
+
|
|
55
|
+
- Force UTF-8 encoding on the HTTP response body, so a remote model loads
|
|
56
|
+
under any locale.
|
|
57
|
+
|
|
58
|
+
## 2.3.1 - 2026-01-01
|
|
59
|
+
|
|
60
|
+
- Force UTF-8 encoding when the locale is not UTF-8. Model data and user input
|
|
61
|
+
no longer raise an encoding error.
|
|
62
|
+
|
|
63
|
+
## 2.3.0 - 2025-12-31
|
|
64
|
+
|
|
65
|
+
- Add a `classifier` executable with a model registry. The tool trains,
|
|
66
|
+
classifies, and manages saved models from the shell.
|
|
67
|
+
- Fix the examples and the broken links in the README.
|
|
68
|
+
- Add Dependabot for automated dependency updates.
|
|
69
|
+
|
|
70
|
+
## 2.2.0 - 2025-12-29
|
|
71
|
+
|
|
72
|
+
- **Breaking:** set `required_ruby_version` to `>= 3.1`. Older Ruby versions
|
|
73
|
+
are no longer supported.
|
|
74
|
+
- Add a k-Nearest Neighbors classifier.
|
|
75
|
+
- Add a Logistic Regression classifier.
|
|
76
|
+
- Add a TF-IDF vectorizer.
|
|
77
|
+
- Add streaming training and incremental SVD, so a corpus larger than memory
|
|
78
|
+
can train a model.
|
|
79
|
+
- Add a hash-style API to add items to LSI.
|
|
80
|
+
- Add keyword arguments to `Bayes#train` and `Bayes#untrain`.
|
|
81
|
+
- Accept an array of categories in the classifier constructor.
|
|
82
|
+
- Fix the sentence and paragraph splits in `Summary`.
|
|
83
|
+
- Add property-based tests for the probabilistic invariants.
|
|
84
|
+
|
|
85
|
+
## 2.1.0 - 2025-12-28
|
|
86
|
+
|
|
87
|
+
- Replace the optional GSL dependency with a bundled C extension for LSI. The
|
|
88
|
+
extension has no external dependency and falls back to pure Ruby.
|
|
89
|
+
- Add pluggable persistence backends through a storage API.
|
|
90
|
+
- Add `save` and `load` methods for classifier persistence.
|
|
91
|
+
- Add thread safety to the Bayes and LSI classifiers.
|
|
92
|
+
- Expose the LSI tuning parameters, with validation and an introspection API.
|
|
93
|
+
- Cache the expensive computations in the Bayes classifier.
|
|
94
|
+
|
|
95
|
+
## 2.0.0 - 2025-12-27
|
|
96
|
+
|
|
97
|
+
- **Breaking:** replace the fixed 0.1 constant in the Bayes classifier with
|
|
98
|
+
add-one (Laplace) smoothing, where
|
|
99
|
+
`P(word|category) = (count + 1) / (total + vocabulary_size)`. The smoothing
|
|
100
|
+
now scales with the vocabulary size and applies to seen and unseen words
|
|
101
|
+
alike. Classification scores change as a result.
|
|
102
|
+
- Fix an LSI dimension mismatch in the pure Ruby SVD.
|
|
103
|
+
- Fix the numerical stability of the SVD implementation.
|
|
104
|
+
- Replace the separate RBS files with inline annotations.
|
|
105
|
+
- Add a GitHub Actions workflow that publishes the gem on a version tag.
|
|
106
|
+
- Add an LSI benchmark that compares GSL against pure Ruby.
|
|
107
|
+
- Add RuboCop and SimpleCov.
|
|
108
|
+
|
|
109
|
+
## 1.4.4 - 2024-07-31
|
|
110
|
+
|
|
111
|
+
- Improve the scaling of the LSI content node.
|
|
112
|
+
|
|
113
|
+
## 1.4.3 - 2024-07-31
|
|
114
|
+
|
|
115
|
+
- Require `set` and use the explicit `::Set` namespace.
|
|
116
|
+
- Refactor `prepare_category_name`.
|
|
117
|
+
|
|
118
|
+
## 1.4.2 - 2024-07-31
|
|
119
|
+
|
|
120
|
+
- Fix the word count when `remove_category` runs.
|
|
121
|
+
- Add the `mutex_m` dependency and update the `fast-stemmer` version.
|
|
122
|
+
|
|
123
|
+
## 1.4.1 - 2024-07-31
|
|
124
|
+
|
|
125
|
+
- Add `remove_category` to the Bayes classifier.
|
|
126
|
+
|
|
127
|
+
## 1.4.0 - 2024-07-31
|
|
128
|
+
|
|
129
|
+
- Add `classify_with_confidence` to the LSI classifier.
|
|
130
|
+
- Require `mathn` only for Ruby 2.5 and later, and add `cmath` for Ruby 2.7
|
|
131
|
+
and later.
|
|
132
|
+
- Silence the warnings about an uninitialized `$GSL`, and correct the rb-gsl
|
|
133
|
+
URL hint.
|
|
134
|
+
- Package the test files in the gem.
|
|
135
|
+
- Add a Gemfile.lock.
|
|
136
|
+
|
|
137
|
+
## 1.3.5 - 2018-04-17
|
|
138
|
+
|
|
139
|
+
- Use Minitest for the test suite.
|
|
140
|
+
- Add the `mathn` dependency, which Ruby 2.5.0 removed.
|
|
141
|
+
- Make the gem installable through Bundler and a git remote.
|
|
142
|
+
- Fix the gemspec and the unit tests.
|
|
143
|
+
|
|
144
|
+
## 1.3.4 - 2013-12-31
|
|
145
|
+
|
|
146
|
+
- Use a prior in the Bayes classifier.
|
|
147
|
+
- Change the skip word list from an array to a set.
|
|
148
|
+
- Reduce the number of regular expression matches during tokenization.
|
|
149
|
+
- Stem only the words that the tokenizer keeps.
|
|
150
|
+
- Default the word hash values to 0.
|
|
151
|
+
- Add a gemspec, a README, and Travis CI.
|
|
152
|
+
|
|
153
|
+
## 1.3.3 - 2010-07-06
|
|
154
|
+
|
|
155
|
+
- Use fast-stemmer for the Porter stemmer.
|
|
156
|
+
- Check `$GSL` before a call to `Matrix.diag`, so the code uses `GSL::Matrix`.
|
|
157
|
+
|
|
158
|
+
## 1.3.2 - 2010-07-06
|
|
159
|
+
|
|
160
|
+
- Fix the reported issue #1.
|
|
161
|
+
|
|
162
|
+
## 1.3.1 and earlier
|
|
163
|
+
|
|
164
|
+
Versions 1.0 through 1.3.1 reached RubyGems on 2009-07-25. The git history
|
|
165
|
+
before 2010 is too sparse to attribute each change to one of these versions.
|
data/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
Text classification in Ruby. Five algorithms, native performance, streaming support.
|
|
8
8
|
|
|
9
|
-
**[Documentation](https://rubyclassifier.com/docs)** · **[Tutorials](https://rubyclassifier.com/docs/tutorials)** · **[API Reference](https://rubydoc.info/gems/classifier)**
|
|
9
|
+
**[Reference](docs/)** · **[Documentation](https://rubyclassifier.com/docs)** · **[Tutorials](https://rubyclassifier.com/docs/tutorials)** · **[API Reference](https://rubydoc.info/gems/classifier)**
|
|
10
10
|
|
|
11
11
|
## Why This Library?
|
|
12
12
|
|
|
@@ -32,7 +32,7 @@ brew install cardmagic/tap/classifier
|
|
|
32
32
|
|
|
33
33
|
## Command Line
|
|
34
34
|
|
|
35
|
-
Classify text instantly with pre-trained models
|
|
35
|
+
Classify text instantly with pre-trained models. No code required:
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
38
|
# Detect spam
|
|
@@ -63,7 +63,61 @@ classifier "Great product, highly recommend"
|
|
|
63
63
|
# => positive
|
|
64
64
|
```
|
|
65
65
|
|
|
66
|
-
|
|
66
|
+
The `keywords` command scores term importance with TF-IDF. It has no
|
|
67
|
+
pre-trained models, so build a vocabulary first. Every later command reads
|
|
68
|
+
that model:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# Fit from multiple files. Each line becomes a separate document.
|
|
72
|
+
keywords fit corpus/*.txt
|
|
73
|
+
# => Saved to "/path/to/keywords.json"
|
|
74
|
+
|
|
75
|
+
# Fit from stdin
|
|
76
|
+
cat documents.txt | keywords fit
|
|
77
|
+
|
|
78
|
+
# Tune the vocabulary filters during the fit
|
|
79
|
+
keywords fit --min-df 2 --max-df 0.85 --ngram 1,2 corpus/*.txt
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
Then score any text against that vocabulary:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# Score a raw string
|
|
86
|
+
keywords "Ruby is a programming language"
|
|
87
|
+
# => language:0.58 programming:0.58 ruby:0.58
|
|
88
|
+
|
|
89
|
+
# Score a file
|
|
90
|
+
keywords extract article.txt
|
|
91
|
+
# => machine:0.58 network:0.47 neural:0.47 learning:0.47
|
|
92
|
+
|
|
93
|
+
# Pipeline with stdin and web data
|
|
94
|
+
curl -s https://example.com/article | keywords extract
|
|
95
|
+
|
|
96
|
+
# Get the top 5 terms only
|
|
97
|
+
keywords -n 5 "long document with many terms..."
|
|
98
|
+
|
|
99
|
+
# Use a different model file
|
|
100
|
+
keywords -m custom_model.json "Ruby is a programming language"
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Inspect the model:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
keywords info
|
|
107
|
+
# => Documents: 1,234
|
|
108
|
+
# => Vocabulary: 5,678
|
|
109
|
+
# => Min DF: 1
|
|
110
|
+
# => Max DF: 1.0
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
The output maps stems back to whole words, so a model built from `programming`
|
|
114
|
+
prints `programming`, not `program`. An n-gram label joins its parts with a
|
|
115
|
+
space, as in `machine learning:0.35`.
|
|
116
|
+
|
|
117
|
+
Run `keywords --help` for the full option list. A usage error exits 2 and any
|
|
118
|
+
other error exits 1, so scripts can tell the two apart.
|
|
119
|
+
|
|
120
|
+
[keywords reference →](docs/keywords.md) · [CLI Guide →](https://rubyclassifier.com/docs/guides/cli/basics)
|
|
67
121
|
|
|
68
122
|
### Claude Code Plugin
|
|
69
123
|
|
|
@@ -100,6 +154,7 @@ classifier.classify("Cheap pills!") # => "Spam"
|
|
|
100
154
|
classifier = Classifier::LogisticRegression.new(:positive, :negative)
|
|
101
155
|
classifier.train(positive: "love amazing great wonderful")
|
|
102
156
|
classifier.train(negative: "hate terrible awful bad")
|
|
157
|
+
classifier.fit # required before the first classify
|
|
103
158
|
classifier.classify("I love it!") # => "Positive"
|
|
104
159
|
```
|
|
105
160
|
[Logistic Regression Guide →](https://rubyclassifier.com/docs/guides/logisticregression/basics)
|
|
@@ -128,7 +183,7 @@ knn.classify("programming code") # => "tech"
|
|
|
128
183
|
```ruby
|
|
129
184
|
tfidf = Classifier::TFIDF.new
|
|
130
185
|
tfidf.fit(["Ruby is great", "Python is great", "Ruby on Rails"])
|
|
131
|
-
tfidf.transform("Ruby programming") # => {:
|
|
186
|
+
tfidf.transform("Ruby programming") # => {rubi: 1.0}
|
|
132
187
|
```
|
|
133
188
|
[TF-IDF Guide →](https://rubyclassifier.com/docs/guides/tfidf/basics)
|
|
134
189
|
|
|
@@ -136,19 +191,29 @@ tfidf.transform("Ruby programming") # => {:rubi => 1.0}
|
|
|
136
191
|
|
|
137
192
|
### Incremental LSI
|
|
138
193
|
|
|
139
|
-
Add documents without
|
|
194
|
+
Add documents without a rebuild of the whole index. Turn `auto_rebuild` off, add
|
|
195
|
+
the starting corpus, then build once:
|
|
140
196
|
|
|
141
197
|
```ruby
|
|
142
|
-
lsi = Classifier::LSI.new(incremental: true)
|
|
143
|
-
lsi.add(tech: [
|
|
198
|
+
lsi = Classifier::LSI.new(incremental: true, auto_rebuild: false)
|
|
199
|
+
lsi.add(tech: [
|
|
200
|
+
"Ruby is an elegant programming language for web development",
|
|
201
|
+
"Python is a popular programming language for data science",
|
|
202
|
+
"JavaScript runs in browsers and powers modern web applications",
|
|
203
|
+
"Java is a compiled language used for enterprise backend systems",
|
|
204
|
+
"Rust provides memory safety without a garbage collector runtime"
|
|
205
|
+
])
|
|
144
206
|
lsi.build_index
|
|
145
207
|
|
|
146
|
-
#
|
|
147
|
-
lsi.add(tech: "Go is fast")
|
|
148
|
-
lsi.
|
|
208
|
+
# This uses Brand's algorithm. No full rebuild.
|
|
209
|
+
lsi.add(tech: "Go is a fast compiled language for backend systems")
|
|
210
|
+
lsi.incremental_enabled? # => true
|
|
149
211
|
```
|
|
150
212
|
|
|
151
|
-
|
|
213
|
+
Incremental mode needs the starting corpus in place before the first build, and
|
|
214
|
+
it falls back to a full rebuild when one document grows the vocabulary too far.
|
|
215
|
+
|
|
216
|
+
[Incremental LSI →](docs/lsi.md#incremental-mode) · [Learn more →](https://rubyclassifier.com/docs/guides/lsi/basics)
|
|
152
217
|
|
|
153
218
|
### Persistence
|
|
154
219
|
|
data/docs/README.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Classifier reference
|
|
2
|
+
|
|
3
|
+
Feature reference for the `classifier` gem. Every example here runs against the
|
|
4
|
+
version in this repository.
|
|
5
|
+
|
|
6
|
+
The [README](../README.md) gives the short tour. These pages give the detail.
|
|
7
|
+
|
|
8
|
+
## Command line
|
|
9
|
+
|
|
10
|
+
| Page | Contents |
|
|
11
|
+
|:--|:--|
|
|
12
|
+
| [classifier](cli.md) | Train, classify, and manage models from the shell |
|
|
13
|
+
| [keywords](keywords.md) | TF-IDF keyword extraction and term scores |
|
|
14
|
+
|
|
15
|
+
## Classifiers
|
|
16
|
+
|
|
17
|
+
| Page | Use it for |
|
|
18
|
+
|:--|:--|
|
|
19
|
+
| [Bayes](bayes.md) | Fast probabilistic classification. The default choice |
|
|
20
|
+
| [Logistic Regression](logistic-regression.md) | Linear classification with calibrated probabilities |
|
|
21
|
+
| [LSI](lsi.md) | Semantic similarity, search, related documents, and summaries |
|
|
22
|
+
| [k-Nearest Neighbors](knn.md) | Classification with the nearest examples and their votes |
|
|
23
|
+
|
|
24
|
+
## Vectorization
|
|
25
|
+
|
|
26
|
+
| Page | Contents |
|
|
27
|
+
|:--|:--|
|
|
28
|
+
| [TF-IDF](tfidf.md) | Term weights, n-grams, document frequency filters |
|
|
29
|
+
|
|
30
|
+
## Shared behavior
|
|
31
|
+
|
|
32
|
+
| Page | Contents |
|
|
33
|
+
|:--|:--|
|
|
34
|
+
| [Persistence](persistence.md) | Save, load, storage backends, and custom backends |
|
|
35
|
+
| [Streaming](streaming.md) | Training on data larger than memory |
|
|
36
|
+
| [Configuration](configuration.md) | Global settings and the native extension |
|
|
37
|
+
|
|
38
|
+
## Which classifier
|
|
39
|
+
|
|
40
|
+
Start with Bayes. It trains in one pass, needs no fit step, and handles most
|
|
41
|
+
text classification tasks.
|
|
42
|
+
|
|
43
|
+
- Choose **Logistic Regression** when you need a probability per category, and
|
|
44
|
+
you accept a `fit` step after training.
|
|
45
|
+
- Choose **LSI** when you need similarity, search, or related documents, and not
|
|
46
|
+
only a label.
|
|
47
|
+
- Choose **k-Nearest Neighbors** when you want to see which examples drove the
|
|
48
|
+
answer.
|
|
49
|
+
- Choose **TF-IDF** when you want term weights rather than a category.
|
data/docs/bayes.md
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Bayes
|
|
2
|
+
|
|
3
|
+
`Classifier::Bayes` is a Naive Bayesian classifier. It trains in one pass, needs
|
|
4
|
+
no fit step, and suits most text classification tasks.
|
|
5
|
+
|
|
6
|
+
It uses log probabilities for numerical stability, and add-one (Laplace)
|
|
7
|
+
smoothing, where `P(word|category) = (count + 1) / (total + vocabulary_size)`.
|
|
8
|
+
|
|
9
|
+
## Train and classify
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
require "classifier"
|
|
13
|
+
|
|
14
|
+
classifier = Classifier::Bayes.new(:spam, :ham)
|
|
15
|
+
classifier.train(spam: "Buy viagra cheap pills now")
|
|
16
|
+
classifier.train(spam: "You won million dollars prize")
|
|
17
|
+
classifier.train(ham: ["Meeting tomorrow at 3pm", "Quarterly report attached"])
|
|
18
|
+
|
|
19
|
+
classifier.classify("Cheap pills!")
|
|
20
|
+
# => "Spam"
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
A category name comes back capitalized. Pass an array to train several documents
|
|
24
|
+
against one category in a single call.
|
|
25
|
+
|
|
26
|
+
## Scores per category
|
|
27
|
+
|
|
28
|
+
```ruby
|
|
29
|
+
classifier.classifications("Cheap pills!")
|
|
30
|
+
# => {"Spam" => -8.579980179515003, "Ham" => -9.680344001221918}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
These are log probabilities, so they are negative, and the highest value wins.
|
|
34
|
+
|
|
35
|
+
## Dynamic training methods
|
|
36
|
+
|
|
37
|
+
A `train_<category>` method exists for every category:
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
classifier = Classifier::Bayes.new(:spam, :ham)
|
|
41
|
+
classifier.train_spam("cheap pills")
|
|
42
|
+
classifier.train_ham("meeting tomorrow")
|
|
43
|
+
classifier.classify("pills")
|
|
44
|
+
# => "Spam"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
`untrain_<category>` removes a document the same way.
|
|
48
|
+
|
|
49
|
+
## Manage categories
|
|
50
|
+
|
|
51
|
+
```ruby
|
|
52
|
+
classifier.categories
|
|
53
|
+
# => ["Spam", "Ham"]
|
|
54
|
+
|
|
55
|
+
classifier.add_category(:other)
|
|
56
|
+
classifier.categories
|
|
57
|
+
# => ["Spam", "Ham", "Other"]
|
|
58
|
+
|
|
59
|
+
classifier.remove_category(:other)
|
|
60
|
+
classifier.categories
|
|
61
|
+
# => ["Spam", "Ham"]
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
`remove_category` also removes that category's word counts. `append_category` is
|
|
65
|
+
an alias of `add_category`.
|
|
66
|
+
|
|
67
|
+
## Untrain
|
|
68
|
+
|
|
69
|
+
```ruby
|
|
70
|
+
classifier.untrain(spam: "Buy viagra cheap pills now")
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Untrain the same text you trained. A document you never trained corrupts the
|
|
74
|
+
counts.
|
|
75
|
+
|
|
76
|
+
## Short words
|
|
77
|
+
|
|
78
|
+
The tokenizer drops words shorter than `min_word_length`, which defaults to 3.
|
|
79
|
+
Raise or lower it per classifier:
|
|
80
|
+
|
|
81
|
+
```ruby
|
|
82
|
+
classifier = Classifier::Bayes.new(:spam, :ham, min_word_length: 2)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
See [Configuration](configuration.md) to change the default for every
|
|
86
|
+
classifier.
|
|
87
|
+
|
|
88
|
+
## Constructor
|
|
89
|
+
|
|
90
|
+
```ruby
|
|
91
|
+
Classifier::Bayes.new(*categories, min_word_length: 3)
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
An array of categories also works:
|
|
95
|
+
|
|
96
|
+
```ruby
|
|
97
|
+
Classifier::Bayes.new([:spam, :ham])
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Save and load
|
|
101
|
+
|
|
102
|
+
```ruby
|
|
103
|
+
classifier.save_to_file("model.json")
|
|
104
|
+
loaded = Classifier::Bayes.load_from_file("model.json")
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
See [Persistence](persistence.md) for storage backends, and
|
|
108
|
+
[Streaming](streaming.md) for corpora larger than memory.
|
data/docs/cli.md
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# classifier
|
|
2
|
+
|
|
3
|
+
`classifier` trains and runs text classifiers from the shell. For term scores
|
|
4
|
+
rather than categories, use [`keywords`](keywords.md).
|
|
5
|
+
|
|
6
|
+
## Pre-trained models
|
|
7
|
+
|
|
8
|
+
The `-r` flag pulls a model from the registry, so classification works with no
|
|
9
|
+
training:
|
|
10
|
+
|
|
11
|
+
```console
|
|
12
|
+
$ classifier -r sms-spam-filter "You won a free iPhone"
|
|
13
|
+
spam
|
|
14
|
+
|
|
15
|
+
$ classifier -r imdb-sentiment "This movie was absolutely amazing"
|
|
16
|
+
positive
|
|
17
|
+
|
|
18
|
+
$ classifier models
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Train your own
|
|
22
|
+
|
|
23
|
+
```console
|
|
24
|
+
$ classifier train positive reviews/good/*.txt
|
|
25
|
+
$ classifier train negative reviews/bad/*.txt
|
|
26
|
+
|
|
27
|
+
$ classifier "Great product, highly recommend"
|
|
28
|
+
positive
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Training reads standard input when you name no file:
|
|
32
|
+
|
|
33
|
+
```console
|
|
34
|
+
$ echo "amazing fantastic" | classifier train positive
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
The default model file is `./classifier.json`. Use `-f` for any other path. Each
|
|
38
|
+
`train` call updates that file in place.
|
|
39
|
+
|
|
40
|
+
## Probabilities
|
|
41
|
+
|
|
42
|
+
```console
|
|
43
|
+
$ classifier -p "Great product, highly recommend"
|
|
44
|
+
positive:0.89 negative:0.11
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Model information
|
|
48
|
+
|
|
49
|
+
```console
|
|
50
|
+
$ classifier info
|
|
51
|
+
{
|
|
52
|
+
"file": "./classifier.json",
|
|
53
|
+
"type": "bayes",
|
|
54
|
+
"categories": [
|
|
55
|
+
"Positive",
|
|
56
|
+
"Negative"
|
|
57
|
+
],
|
|
58
|
+
"category_stats": {
|
|
59
|
+
"Positive": {
|
|
60
|
+
"unique_words": 7,
|
|
61
|
+
"total_words": 7
|
|
62
|
+
},
|
|
63
|
+
"Negative": {
|
|
64
|
+
"unique_words": 7,
|
|
65
|
+
"total_words": 7
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Other classifiers
|
|
72
|
+
|
|
73
|
+
`-m` selects the algorithm. The default is `bayes`.
|
|
74
|
+
|
|
75
|
+
```console
|
|
76
|
+
$ classifier -m knn -k 3 train tech docs/tech/*.txt
|
|
77
|
+
$ classifier -m lr train positive reviews/good/*.txt
|
|
78
|
+
$ classifier -m lsi train dogs corpus/dogs/*.txt
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Logistic regression needs a fit step after training:
|
|
82
|
+
|
|
83
|
+
```console
|
|
84
|
+
$ classifier -m lr train positive reviews/good/*.txt
|
|
85
|
+
$ classifier -m lr train negative reviews/bad/*.txt
|
|
86
|
+
$ classifier -m lr fit
|
|
87
|
+
Model fitted successfully
|
|
88
|
+
$ classifier -m lr "I love it"
|
|
89
|
+
positive
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Search and related documents
|
|
93
|
+
|
|
94
|
+
These two commands need an LSI model.
|
|
95
|
+
|
|
96
|
+
```console
|
|
97
|
+
$ classifier -m lsi search "machine learning"
|
|
98
|
+
$ classifier -m lsi related article.txt
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
`-n` sets how many results come back. The default is 10.
|
|
102
|
+
|
|
103
|
+
## Commands
|
|
104
|
+
|
|
105
|
+
| Command | Action |
|
|
106
|
+
|:--|:--|
|
|
107
|
+
| `train <category> [files...]` | Train a category from files or standard input |
|
|
108
|
+
| `info` | Print model information |
|
|
109
|
+
| `fit` | Fit the model. Logistic regression only |
|
|
110
|
+
| `search <query>` | Semantic search. LSI only |
|
|
111
|
+
| `related <item>` | Find related documents. LSI only |
|
|
112
|
+
| `models [registry]` | List the models in a registry |
|
|
113
|
+
| `pull <model>` | Download a model from the registry |
|
|
114
|
+
| `push <file>` | Contribute a model to the registry |
|
|
115
|
+
| `<text>` | Classify the text. The default action |
|
|
116
|
+
|
|
117
|
+
## Options
|
|
118
|
+
|
|
119
|
+
| Option | Meaning |
|
|
120
|
+
|:--|:--|
|
|
121
|
+
| `-f`, `--file FILE` | Model file. Default `./classifier.json` |
|
|
122
|
+
| `-m`, `--model TYPE` | Algorithm: `bayes`, `lsi`, `knn`, or `lr`. Default `bayes` |
|
|
123
|
+
| `-r`, `--remote MODEL` | Use a remote model, by name or `@user/repo:name` |
|
|
124
|
+
| `--search TEXT` | Search remote models by name and description, and local models by name |
|
|
125
|
+
| `-o`, `--output FILE` | Output path for `pull` |
|
|
126
|
+
| `-p` | Print probabilities |
|
|
127
|
+
| `-n`, `--count N` | Result count for `search` and `related`. Default 10 |
|
|
128
|
+
| `-k`, `--neighbors N` | Neighbor count for kNN. Default 5 |
|
|
129
|
+
| `--weighted` | Use distance-weighted voting for kNN |
|
|
130
|
+
| `--learning-rate N` | Learning rate for logistic regression. Default 0.1 |
|
|
131
|
+
| `--regularization N` | L2 regularization for logistic regression. Default 0.01 |
|
|
132
|
+
| `--max-iterations N` | Maximum iterations for logistic regression. Default 100 |
|
|
133
|
+
| `-q` | Quiet mode |
|
|
134
|
+
| `--local` | List locally cached models, with the `models` command |
|
|
135
|
+
| `-v`, `--version` | Print the gem version |
|
|
136
|
+
| `-h`, `--help` | Print the full usage |
|
|
137
|
+
|
|
138
|
+
## Install without Ruby
|
|
139
|
+
|
|
140
|
+
Homebrew installs the command line tools on their own:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
brew install cardmagic/tap/classifier
|
|
144
|
+
```
|