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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 527f52e23121cbda5ce138a74c200f473813ea306aa04f50bd74f2d1230ff45b
4
- data.tar.gz: ca52b0412149e3e6bf3d885d12ef5485ab1a70d6dcc4cb659121733bfcced2da
3
+ metadata.gz: 28ad3396d95e315e11525b87393aaf3d081cf537f63dee0ba89fdd1a8acab4ee
4
+ data.tar.gz: 03b0b12d04d9ed05878a1b5a659383b72eb4eba12b6eaecf6b02b0356ee524b9
5
5
  SHA512:
6
- metadata.gz: 9ddcb1c5c85229beef3b8b048f65fae551ae97ac032e51f07937470070f48cb2d56adca0bd30df1f981a93e44004e572a258c9ecf7dbfed26f8005ca52247714
7
- data.tar.gz: 79ffe1a234a988c5d026740405c350f2222a15eaf6a562e4b1da39cf6bdf1aaea43dbfe85bfc55db9830684a10eea3c9f4df0eae4921374fe2d6ac0d4c7b7606
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—no coding required:
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
- [CLI Guide →](https://rubyclassifier.com/docs/guides/cli/basics)
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") # => {:rubi => 1.0}
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 rebuilding the entire index—400x faster for streaming data:
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: ["Ruby is elegant", "Python is popular"])
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
- # These use Brand's algorithm—no full rebuild
147
- lsi.add(tech: "Go is fast")
148
- lsi.add(tech: "Rust is safe")
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
- [Learn more →](https://rubyclassifier.com/docs/guides/lsi/basics)
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
+ ```