natural 6.11.1 → 7.0.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.
- package/.env +9 -7
- package/.eslintrc.json +13 -0
- package/.github/workflows/node.js.yml +57 -19
- package/lib/natural/analyzers/SenType.ts +30 -0
- package/lib/natural/analyzers/index.d.ts +6 -10
- package/lib/natural/brill_pos_tagger/index.d.ts +23 -14
- package/lib/natural/brill_pos_tagger/index.js +1 -1
- package/lib/natural/classifiers/bayes_classifier.js +6 -1
- package/lib/natural/classifiers/classifier.js +19 -32
- package/lib/natural/classifiers/classifier_train_parallel.js +7 -5
- package/lib/natural/classifiers/index.d.ts +36 -15
- package/lib/natural/classifiers/logistic_regression_classifier.js +7 -2
- package/lib/natural/classifiers/maxent/Classifier.js +5 -3
- package/lib/natural/distance/index.d.ts +11 -14
- package/lib/natural/inflectors/count_inflector.js +18 -21
- package/lib/natural/inflectors/fr/count_inflector.js +18 -19
- package/lib/natural/inflectors/index.d.ts +12 -3
- package/lib/natural/inflectors/index.js +3 -0
- package/lib/natural/ngrams/index.d.ts +5 -5
- package/lib/natural/normalizers/index.d.ts +24 -1
- package/lib/natural/normalizers/index.js +4 -1
- package/lib/natural/normalizers/normalizer_ja.js +109 -72
- package/lib/natural/phonetics/dm_soundex.js +52 -51
- package/lib/natural/phonetics/double_metaphone.js +397 -399
- package/lib/natural/phonetics/index.d.ts +42 -12
- package/lib/natural/phonetics/metaphone.js +134 -157
- package/lib/natural/phonetics/phonetic.js +5 -29
- package/lib/natural/phonetics/soundex.js +45 -54
- package/lib/natural/sentiment/index.d.ts +6 -6
- package/lib/natural/spellcheck/index.d.ts +2 -0
- package/lib/natural/stemmers/index.d.ts +43 -1
- package/lib/natural/stemmers/index.js +1 -0
- package/lib/natural/stemmers/token.js +131 -132
- package/lib/natural/tfidf/index.d.ts +7 -7
- package/lib/natural/tfidf/tfidf.js +7 -1
- package/lib/natural/tokenizers/index.d.ts +2 -2
- package/lib/natural/trie/index.d.ts +1 -1
- package/lib/natural/util/index.d.ts +8 -0
- package/lib/natural/util/index.js +2 -0
- package/lib/natural/util/storage/File.js +22 -0
- package/lib/natural/util/storage/Memcached.js +22 -0
- package/lib/natural/util/storage/MongoDB.js +31 -3
- package/lib/natural/util/storage/Postgres.js +49 -32
- package/lib/natural/util/storage/Redis.js +22 -0
- package/lib/natural/util/storage/StorageBackend.js +23 -0
- package/lib/natural/util/storage/index.d.ts +39 -0
- package/lib/natural/util/storage/index.js +24 -0
- package/package.json +22 -9
- package/ts_spec/MaxEntClassifier_spec.ts +130 -0
- package/ts_spec/SentimentAnalyzer_spec.ts +232 -0
- package/ts_spec/WordPunctTokenizer_spec.ts +57 -0
- package/ts_spec/aggressive_tokenizer_de_spec.ts +36 -0
- package/ts_spec/aggressive_tokenizer_es_spec.ts +39 -0
- package/ts_spec/aggressive_tokenizer_fr_spec.ts +111 -0
- package/ts_spec/aggressive_tokenizer_hi_spec.ts +70 -0
- package/ts_spec/aggressive_tokenizer_nl_spec.ts +39 -0
- package/ts_spec/aggressive_tokenizer_pt_spec.ts +48 -0
- package/ts_spec/aggressive_tokenizer_spec.ts +57 -0
- package/ts_spec/aggressive_tokenizer_sv_spec.ts +52 -0
- package/ts_spec/aggressive_tokenizer_vi_spec.ts +48 -0
- package/ts_spec/bayes_classifier_spec.ts +165 -0
- package/ts_spec/brill_pos_tagger_spec.ts +79 -0
- package/ts_spec/brill_pos_trainer_spec.ts +107 -0
- package/ts_spec/carry_stemmer_fr_spec.ts +97 -0
- package/ts_spec/classifier_spec.ts +104 -0
- package/ts_spec/count_inflector_fr_spec.ts +47 -0
- package/ts_spec/count_inflector_spec.ts +76 -0
- package/ts_spec/damerau_levenshtein_spec.ts +53 -0
- package/ts_spec/dice_coefficient_spec.ts +37 -0
- package/ts_spec/double_metaphone_spec.ts +739 -0
- package/ts_spec/hamming_distance_spec.ts +66 -0
- package/ts_spec/jaro-winkler_spec.ts +79 -0
- package/ts_spec/lancaster_stemmer_spec.ts +63 -0
- package/ts_spec/levenshtein_spec.ts +107 -0
- package/ts_spec/logistic_regression_classifier_spec.ts +93 -0
- package/ts_spec/longest_path_tree_spec.ts +111 -0
- package/ts_spec/metaphone_spec.ts +258 -0
- package/ts_spec/ngram_spec.ts +145 -0
- package/ts_spec/ngram_zh_spec.ts +124 -0
- package/ts_spec/normalizer_ja_spec.ts +165 -0
- package/ts_spec/normalizer_no_spec.ts +112 -0
- package/ts_spec/normalizer_spec.ts +141 -0
- package/ts_spec/noun_inflector_fr_spec.ts +472 -0
- package/ts_spec/noun_inflector_ja_spec.ts +116 -0
- package/ts_spec/noun_inflector_spec.ts +369 -0
- package/ts_spec/orthography_tokenizer_spec.ts +47 -0
- package/ts_spec/porter_stemmer_de_spec.ts +53 -0
- package/ts_spec/porter_stemmer_es_spec.ts +63 -0
- package/ts_spec/porter_stemmer_fr_spec.ts +103 -0
- package/ts_spec/porter_stemmer_it_spec.ts +56 -0
- package/ts_spec/porter_stemmer_nl_spec.ts +73 -0
- package/ts_spec/porter_stemmer_no_spec.ts +148 -0
- package/ts_spec/porter_stemmer_pt_spec.ts +50 -0
- package/ts_spec/porter_stemmer_ru_spec.ts +67 -0
- package/ts_spec/porter_stemmer_spec.ts +202 -0
- package/ts_spec/porter_stemmer_sv_spec.ts +41 -0
- package/ts_spec/present_verb_inflector_spec.ts +127 -0
- package/ts_spec/remove_diacritics_spec.ts +30 -0
- package/ts_spec/sentence_analyzer_spec.ts +293 -0
- package/ts_spec/sentence_tokenizer_parser_spec.ts +200 -0
- package/ts_spec/sentence_tokenizer_spec.ts +189 -0
- package/ts_spec/shortest_path_tree_spec.ts +141 -0
- package/ts_spec/soundex_spec.ts +162 -0
- package/ts_spec/spellcheck_spec.ts +112 -0
- package/ts_spec/stemmer_id_spec.ts +374 -0
- package/ts_spec/stemmer_ja_spec.ts +69 -0
- package/ts_spec/stemmer_token_spec.ts +163 -0
- package/ts_spec/tfidf_spec.ts +275 -0
- package/ts_spec/tokenizer_case_spec.ts +242 -0
- package/ts_spec/tokenizer_ja_spec.ts +63 -0
- package/ts_spec/transliterator_ja_spec.ts +105 -0
- package/ts_spec/treebank_word_tokenizer_spec.ts +38 -0
- package/ts_spec/trie_spec.ts +197 -0
- package/tsconfig.json +5 -3
- package/example.txt +0 -1
- package/f2f5d4bc-daa0-4392-8981-9614cd0c802e.json +0 -1
package/.env
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
+
NODE_PATH=.
|
|
1
2
|
# Storage backend you want to use
|
|
2
3
|
NATURAL_STORAGE=PG
|
|
3
4
|
# Settings for Postgres
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
POSTGRES_USER=user
|
|
6
|
+
POSTGRES_PASSWORD=password
|
|
7
|
+
POSTGRES_DB=naturaldb
|
|
8
|
+
POSTGRES_HOST=localhost
|
|
9
|
+
POSTGRES_PORT=5432
|
|
10
|
+
POSTGRES_TABLE=naturaltable
|
|
10
11
|
# Settings for Redis
|
|
11
12
|
REDIS_HOST=localhost
|
|
12
13
|
REDIS_PORT=6379
|
|
@@ -18,5 +19,6 @@ MEM_EXPIRES = 3600
|
|
|
18
19
|
MONGO_HOST=localhost
|
|
19
20
|
MONGO_PORT=27017
|
|
20
21
|
MONGO_DATABASE=naturaldb
|
|
22
|
+
MONGO_OBJECTMODEL=NaturalObjectModel
|
|
21
23
|
# Path to the folder writing files
|
|
22
|
-
FS_PATH
|
|
24
|
+
FS_PATH=./io_spec/tmp
|
package/.eslintrc.json
ADDED
|
@@ -1,35 +1,73 @@
|
|
|
1
|
-
# This workflow will do a clean install of node dependencies,
|
|
2
|
-
#
|
|
3
|
-
|
|
1
|
+
# This workflow will do a clean install of node dependencies,
|
|
2
|
+
# build the source code and run tests across different versions of node
|
|
3
|
+
# For more information see:
|
|
4
|
+
# https://help.github.com/actions/language-and-framework-guides/
|
|
5
|
+
# using-nodejs-with-github-actions
|
|
6
|
+
---
|
|
4
7
|
name: Node.js CI
|
|
5
8
|
|
|
6
9
|
on:
|
|
7
10
|
push:
|
|
8
|
-
branches: [
|
|
11
|
+
branches: [master]
|
|
9
12
|
pull_request:
|
|
10
|
-
branches: [
|
|
13
|
+
branches: [master]
|
|
11
14
|
|
|
12
15
|
jobs:
|
|
13
16
|
build:
|
|
14
17
|
|
|
15
18
|
runs-on: ubuntu-latest
|
|
16
19
|
|
|
20
|
+
services:
|
|
21
|
+
redis:
|
|
22
|
+
image: redis:latest
|
|
23
|
+
ports:
|
|
24
|
+
- 6379:6379
|
|
25
|
+
postgres:
|
|
26
|
+
image: postgres:latest
|
|
27
|
+
env:
|
|
28
|
+
POSTGRES_USER: user
|
|
29
|
+
POSTGRES_PASSWORD: password
|
|
30
|
+
POSTGRES_DB: naturaldb
|
|
31
|
+
POSTGRES_HOST: localhost
|
|
32
|
+
POSTGRES_PORT: 5432
|
|
33
|
+
POSTGRES_TABLE: naturaltable
|
|
34
|
+
ports:
|
|
35
|
+
- 5432:5432
|
|
36
|
+
memcached:
|
|
37
|
+
image: memcached:latest
|
|
38
|
+
ports:
|
|
39
|
+
- 11211:11211
|
|
40
|
+
mongodb:
|
|
41
|
+
image: mongo:latest
|
|
42
|
+
ports:
|
|
43
|
+
- 27017:27017
|
|
44
|
+
|
|
17
45
|
strategy:
|
|
18
46
|
matrix:
|
|
19
47
|
node-version: [16.x, 18.x, 20.x]
|
|
20
|
-
# See supported Node.js release schedule at
|
|
48
|
+
# See supported Node.js release schedule at
|
|
49
|
+
#https://nodejs.org/en/about/releases/
|
|
21
50
|
|
|
22
51
|
steps:
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
52
|
+
- name: Checkout code
|
|
53
|
+
uses: actions/checkout@v2
|
|
54
|
+
|
|
55
|
+
- name: Use Node.js ${{ matrix.node-version }}
|
|
56
|
+
uses: actions/setup-node@v4
|
|
57
|
+
with:
|
|
58
|
+
node-version: ${{ matrix.node-version }}
|
|
59
|
+
|
|
60
|
+
- name: Clean install
|
|
61
|
+
run: npm ci
|
|
62
|
+
|
|
63
|
+
- name: Build
|
|
64
|
+
run: npm run build
|
|
65
|
+
|
|
66
|
+
- name: Run Istanbul coverage
|
|
67
|
+
run: npm run coverage
|
|
68
|
+
|
|
69
|
+
# Save coverage report in Coveralls
|
|
70
|
+
- name: Coveralls
|
|
71
|
+
uses: coverallsapp/github-action@master
|
|
72
|
+
with:
|
|
73
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright (c) 2024,
|
|
3
|
+
Hugo W.L. ter Doest <https://github.com/Hugo-ter-Doest>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
export const enum SenType {
|
|
25
|
+
Unknown = 'UNKNOWN',
|
|
26
|
+
Command = 'COMMAND',
|
|
27
|
+
Interrogative = 'INTERROGATIVE',
|
|
28
|
+
Exclamatory = 'EXCLAMATORY',
|
|
29
|
+
Declarative = 'DECLARATIVE'
|
|
30
|
+
}
|
|
@@ -21,15 +21,19 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
21
21
|
THE SOFTWARE.
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
+
import type { SenType } from './SenType'
|
|
25
|
+
|
|
24
26
|
declare interface TaggedWord {
|
|
25
27
|
token: string
|
|
26
28
|
pos: string
|
|
27
29
|
pp?: boolean
|
|
28
30
|
spos?: string
|
|
29
31
|
added?: boolean
|
|
32
|
+
[key: string]: any
|
|
30
33
|
}
|
|
31
34
|
|
|
32
|
-
declare type
|
|
35
|
+
declare type PunctuationMapping = Array<Record<string, string>> | ''
|
|
36
|
+
declare type PunctuationFunction = () => PunctuationMapping
|
|
33
37
|
|
|
34
38
|
declare interface TaggedSentence {
|
|
35
39
|
tags: TaggedWord[]
|
|
@@ -38,14 +42,6 @@ declare interface TaggedSentence {
|
|
|
38
42
|
|
|
39
43
|
declare type CallbackFunction = (obj: SentenceAnalyzer) => void
|
|
40
44
|
|
|
41
|
-
declare enum SenType {
|
|
42
|
-
Unknown = 'UNKNOWN',
|
|
43
|
-
Command = 'COMMAND',
|
|
44
|
-
Interrogative = 'INTERROGATIVE',
|
|
45
|
-
Exclamatory = 'EXCLAMATORY',
|
|
46
|
-
Declarative = 'DECLARATIVE',
|
|
47
|
-
}
|
|
48
|
-
|
|
49
45
|
export class SentenceAnalyzer {
|
|
50
46
|
posObj: TaggedSentence
|
|
51
47
|
senType: SenType | null
|
|
@@ -57,5 +53,5 @@ export class SentenceAnalyzer {
|
|
|
57
53
|
predicateToString (): string
|
|
58
54
|
implicitYou (): boolean
|
|
59
55
|
toString (): string
|
|
60
|
-
type (cbf: CallbackFunction):
|
|
56
|
+
type (cbf: CallbackFunction): string
|
|
61
57
|
}
|
|
@@ -23,7 +23,7 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
23
23
|
THE SOFTWARE.
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import { Feature } from '../classifiers'
|
|
26
|
+
import type { Feature } from '../classifiers'
|
|
27
27
|
|
|
28
28
|
declare interface RuleTemplatesItem {
|
|
29
29
|
function: (sentence: Sentence, i: number, parameter1?: string, parameter2?: string) => boolean
|
|
@@ -33,9 +33,8 @@ declare interface RuleTemplatesItem {
|
|
|
33
33
|
parameter2Values?: (sentence: Sentence, i: number) => string[]
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
export
|
|
37
|
-
|
|
38
|
-
}
|
|
36
|
+
export type RuleTemplates = Record<string, RuleTemplatesItem>
|
|
37
|
+
export let ruleTemplates: RuleTemplates
|
|
39
38
|
|
|
40
39
|
export class RuleTemplate {
|
|
41
40
|
constructor (templateName: string, metadata: RuleTemplatesItem)
|
|
@@ -82,7 +81,7 @@ export class RuleSet {
|
|
|
82
81
|
|
|
83
82
|
export class Lexicon {
|
|
84
83
|
constructor (language: string, defaultCategory: string, defaultCategoryCapitalised?: string)
|
|
85
|
-
lexicon:
|
|
84
|
+
lexicon: Record<string, string[] | undefined>
|
|
86
85
|
defaultCategory: string
|
|
87
86
|
defaultCategoryCapitalised: string | undefined
|
|
88
87
|
parseLexicon (data: string): void
|
|
@@ -96,11 +95,11 @@ export class Lexicon {
|
|
|
96
95
|
}
|
|
97
96
|
|
|
98
97
|
declare class Corpus {
|
|
99
|
-
constructor (data: string |
|
|
98
|
+
constructor (data: string | TaggedCorpus, typeOfCorpus: number, SentenceClass: typeof Sentence)
|
|
100
99
|
private readonly wordCount: number
|
|
101
100
|
private readonly sentences: Sentence[]
|
|
102
|
-
private readonly tagFrequencies:
|
|
103
|
-
private readonly posTags:
|
|
101
|
+
private readonly tagFrequencies: Record<string, Record<string, number> | undefined>
|
|
102
|
+
private readonly posTags: Record<string, boolean | undefined>
|
|
104
103
|
parseBrownCorpus (data: string, SentenceClass: typeof Sentence): void
|
|
105
104
|
getTags (): string[]
|
|
106
105
|
splitInTrainAndTest (percentageTrain: number): [Corpus, Corpus]
|
|
@@ -119,6 +118,15 @@ declare interface BrillPOSTaggedWord {
|
|
|
119
118
|
tag: string
|
|
120
119
|
}
|
|
121
120
|
|
|
121
|
+
declare interface BrillPOSTaggedSentence {
|
|
122
|
+
taggedWords: BrillPOSTaggedWord[]
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
declare interface TaggedCorpus {
|
|
126
|
+
wordCount: number
|
|
127
|
+
sentences: BrillPOSTaggedSentence[]
|
|
128
|
+
}
|
|
129
|
+
|
|
122
130
|
export class Sentence {
|
|
123
131
|
constructor (data?: string[])
|
|
124
132
|
taggedWords: BrillPOSTaggedWord[]
|
|
@@ -136,9 +144,6 @@ export class BrillPOSTagger {
|
|
|
136
144
|
}
|
|
137
145
|
|
|
138
146
|
export class BrillPOSTester {
|
|
139
|
-
constructor (lexicon: Lexicon, ruleSet: RuleSet)
|
|
140
|
-
private readonly lexicon: Lexicon
|
|
141
|
-
private readonly ruleSet: RuleSet
|
|
142
147
|
test (corpus: Corpus, tagger: BrillPOSTagger): [number, number]
|
|
143
148
|
}
|
|
144
149
|
|
|
@@ -148,8 +153,8 @@ export class BrillPOSTrainer {
|
|
|
148
153
|
private readonly corpus: Corpus
|
|
149
154
|
private readonly templates: RuleTemplates
|
|
150
155
|
private readonly positiveRules: RuleSet
|
|
151
|
-
private readonly mapRuleToSites:
|
|
152
|
-
private readonly mapSiteToRules:
|
|
156
|
+
private readonly mapRuleToSites: Record<string, Record<number, Record< number, boolean | undefined> | undefined> | undefined>
|
|
157
|
+
private readonly mapSiteToRules: Record<number, Record<number, Record<string, boolean | undefined> | undefined> | undefined>
|
|
153
158
|
private selectHighRule (): TransformationRule
|
|
154
159
|
private mapRuleToSite (rule: TransformationRule, i: number, j: number): void
|
|
155
160
|
private mapSiteToRule (i: number, j: number, rule: TransformationRule): void
|
|
@@ -163,6 +168,10 @@ export class BrillPOSTrainer {
|
|
|
163
168
|
private scanForPositiveRules (): void
|
|
164
169
|
private scanForSites (): void
|
|
165
170
|
private neighbourhood (i: number, j: number): Array<[number, number]>
|
|
166
|
-
train (corpus: Corpus, templates:
|
|
171
|
+
train (corpus: Corpus, templates: RuleTemplate[], lexicon: Lexicon): RuleSet
|
|
167
172
|
printRulesWithScores (): string
|
|
168
173
|
}
|
|
174
|
+
|
|
175
|
+
export interface TagResults {
|
|
176
|
+
results: string[][][]
|
|
177
|
+
}
|
|
@@ -27,7 +27,7 @@ exports.BrillPOSTrainer = require('./lib/Brill_POS_Trainer')
|
|
|
27
27
|
exports.BrillPOSTester = require('./lib/Brill_POS_Tester')
|
|
28
28
|
exports.Lexicon = require('./lib/Lexicon')
|
|
29
29
|
exports.RuleSet = require('./lib/RuleSet')
|
|
30
|
-
exports.
|
|
30
|
+
exports.ruleTemplates = require('./lib/RuleTemplates')
|
|
31
31
|
exports.RuleTemplate = require('./lib/RuleTemplate')
|
|
32
32
|
exports.Corpus = require('./lib/Corpus')
|
|
33
33
|
exports.Sentence = require('./lib/Sentence')
|
|
@@ -44,7 +44,7 @@ class BayesClassifier extends Classifier {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
static load (filename, stemmer, callback) {
|
|
47
|
-
Classifier.load(filename, function (err, classifier) {
|
|
47
|
+
Classifier.load(filename, stemmer, function (err, classifier) {
|
|
48
48
|
if (err) {
|
|
49
49
|
return callback(err)
|
|
50
50
|
} else {
|
|
@@ -52,6 +52,11 @@ class BayesClassifier extends Classifier {
|
|
|
52
52
|
}
|
|
53
53
|
})
|
|
54
54
|
}
|
|
55
|
+
|
|
56
|
+
static async loadFrom (key, stemmer, storageBackend) {
|
|
57
|
+
const classifier = await Classifier.loadFrom(key, storageBackend)
|
|
58
|
+
return BayesClassifier.restore(classifier, stemmer)
|
|
59
|
+
}
|
|
55
60
|
}
|
|
56
61
|
|
|
57
62
|
module.exports = BayesClassifier
|
|
@@ -21,6 +21,7 @@ THE SOFTWARE.
|
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
23
|
'use strict'
|
|
24
|
+
const fs = require('fs')
|
|
24
25
|
|
|
25
26
|
const EventEmitter = require('events')
|
|
26
27
|
const PorterStemmer = require('../stemmers/porter_stemmer')
|
|
@@ -34,6 +35,12 @@ class Classifier extends EventEmitter {
|
|
|
34
35
|
this.features = {}
|
|
35
36
|
this.stemmer = stemmer || PorterStemmer
|
|
36
37
|
this.lastAdded = 0
|
|
38
|
+
|
|
39
|
+
// Add methods for parallel training
|
|
40
|
+
this.Threads = parallelTrainer.Threads
|
|
41
|
+
this.trainParallel = parallelTrainer.trainParallel
|
|
42
|
+
this.retrainParallel = parallelTrainer.retrainParallel
|
|
43
|
+
this.trainParallelBatches = parallelTrainer.trainParallelBatches
|
|
37
44
|
}
|
|
38
45
|
|
|
39
46
|
addDocument (text, classification) {
|
|
@@ -54,7 +61,7 @@ class Classifier extends EventEmitter {
|
|
|
54
61
|
|
|
55
62
|
this.docs.push({
|
|
56
63
|
label: classification,
|
|
57
|
-
text
|
|
64
|
+
text
|
|
58
65
|
})
|
|
59
66
|
|
|
60
67
|
for (let i = 0; i < text.length; i++) {
|
|
@@ -114,8 +121,8 @@ class Classifier extends EventEmitter {
|
|
|
114
121
|
this.emit('trainedWithDocument', { index: i, total: totalDocs, doc: this.docs[i] })
|
|
115
122
|
this.lastAdded++
|
|
116
123
|
}
|
|
117
|
-
this.emit('doneTraining', true)
|
|
118
124
|
this.classifier.train()
|
|
125
|
+
this.emit('doneTraining', true)
|
|
119
126
|
}
|
|
120
127
|
|
|
121
128
|
retrain () {
|
|
@@ -139,7 +146,6 @@ class Classifier extends EventEmitter {
|
|
|
139
146
|
|
|
140
147
|
save (filename, callback) {
|
|
141
148
|
const data = JSON.stringify(this)
|
|
142
|
-
const fs = require('fs')
|
|
143
149
|
const classifier = this
|
|
144
150
|
fs.writeFile(filename, data, 'utf8', function (err) {
|
|
145
151
|
if (callback) {
|
|
@@ -148,9 +154,7 @@ class Classifier extends EventEmitter {
|
|
|
148
154
|
})
|
|
149
155
|
}
|
|
150
156
|
|
|
151
|
-
static load (filename, callback) {
|
|
152
|
-
const fs = require('fs')
|
|
153
|
-
|
|
157
|
+
static load (filename, stemmer, callback) {
|
|
154
158
|
if (!callback) {
|
|
155
159
|
return
|
|
156
160
|
}
|
|
@@ -159,40 +163,23 @@ class Classifier extends EventEmitter {
|
|
|
159
163
|
callback(err, null)
|
|
160
164
|
} else {
|
|
161
165
|
const classifier = JSON.parse(data)
|
|
162
|
-
callback(err, classifier)
|
|
166
|
+
callback(err, Classifier.restore(classifier, stemmer))
|
|
163
167
|
}
|
|
164
168
|
})
|
|
165
169
|
}
|
|
166
170
|
|
|
167
|
-
|
|
168
|
-
|
|
171
|
+
async saveTo (storageBackend) {
|
|
172
|
+
return await storageBackend.store(this)
|
|
169
173
|
}
|
|
170
174
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
this.emit('No threads available')
|
|
176
|
-
return this.train()
|
|
177
|
-
}
|
|
175
|
+
static async loadFrom (key, storageBackend) {
|
|
176
|
+
const obj = await storageBackend.retrieve(key)
|
|
177
|
+
Object.setPrototypeOf(obj, Classifier.prototype)
|
|
178
|
+
return obj
|
|
178
179
|
}
|
|
179
180
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
return parallelTrainer.trainParallelBatches(options)
|
|
183
|
-
} else {
|
|
184
|
-
this.emit('No threads available')
|
|
185
|
-
return this.train()
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
retrainParallel (numThreads, callback) {
|
|
190
|
-
if (parallelTrainer.Threads) {
|
|
191
|
-
return parallelTrainer.trainParallel(numThreads, callback)
|
|
192
|
-
} else {
|
|
193
|
-
this.emit('No threads available')
|
|
194
|
-
return this.retrain()
|
|
195
|
-
}
|
|
181
|
+
setOptions (options) {
|
|
182
|
+
this.keepStops = !!(options.keepStops)
|
|
196
183
|
}
|
|
197
184
|
}
|
|
198
185
|
|
|
@@ -5,8 +5,8 @@ let Threads = null
|
|
|
5
5
|
try {
|
|
6
6
|
Threads = require('webworker-threads')
|
|
7
7
|
} catch (e) {
|
|
8
|
-
//
|
|
9
|
-
|
|
8
|
+
// Silently set Threads to null
|
|
9
|
+
Threads = null
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
function checkThreadSupport () {
|
|
@@ -27,7 +27,7 @@ function docsToFeatures (docs) {
|
|
|
27
27
|
|
|
28
28
|
parsedDocs.push({
|
|
29
29
|
index: docs[i].index,
|
|
30
|
-
features
|
|
30
|
+
features
|
|
31
31
|
})
|
|
32
32
|
}
|
|
33
33
|
|
|
@@ -44,7 +44,7 @@ function docsToObs (docs, lastAdded, stemmer, keepStops) {
|
|
|
44
44
|
}
|
|
45
45
|
obsDocs.push({
|
|
46
46
|
index: i,
|
|
47
|
-
observation
|
|
47
|
+
observation
|
|
48
48
|
})
|
|
49
49
|
}
|
|
50
50
|
return obsDocs
|
|
@@ -181,7 +181,7 @@ function trainParallelBatches (options) {
|
|
|
181
181
|
if (typeof observation === 'string') { observation = this.stemmer.tokenizeAndStem(observation, this.keepStops) }
|
|
182
182
|
obsDocs.push({
|
|
183
183
|
index: i,
|
|
184
|
-
observation
|
|
184
|
+
observation
|
|
185
185
|
})
|
|
186
186
|
}
|
|
187
187
|
|
|
@@ -266,6 +266,8 @@ function trainParallelBatches (options) {
|
|
|
266
266
|
}
|
|
267
267
|
|
|
268
268
|
function retrainParallel (numThreads, callback) {
|
|
269
|
+
checkThreadSupport()
|
|
270
|
+
|
|
269
271
|
this.classifier = new (this.classifier.constructor)()
|
|
270
272
|
this.lastAdded = 0
|
|
271
273
|
this.trainParallel(numThreads, callback)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import events from 'events'
|
|
2
|
-
import { Corpus, Sentence } from '../brill_pos_tagger'
|
|
3
|
-
import { Stemmer } from '../stemmers'
|
|
2
|
+
import type { Corpus, Sentence } from '../brill_pos_tagger'
|
|
3
|
+
import type { Stemmer } from '../stemmers'
|
|
4
|
+
import type { StorageBackend } from '../util'
|
|
4
5
|
|
|
5
6
|
// Start apparatus declarations
|
|
6
7
|
|
|
@@ -19,7 +20,8 @@ declare interface ApparatusClassification {
|
|
|
19
20
|
value: number
|
|
20
21
|
}
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
// Observations are either array of numbers or a sparse vector in the form of a map
|
|
24
|
+
declare type ApparatusObservation = number[] | Record<string, number | string | boolean>
|
|
23
25
|
|
|
24
26
|
declare class ApparatusClassifier {
|
|
25
27
|
addExample (observation: ApparatusObservation, label: string): void
|
|
@@ -31,8 +33,8 @@ declare class ApparatusClassifier {
|
|
|
31
33
|
// TODO: Not needed for natural repository, but could be moved to
|
|
32
34
|
// aparatus repository. Temporarily leaving here for copypasta.
|
|
33
35
|
declare class ApparatusBayesClassifier extends ApparatusClassifier {
|
|
34
|
-
classFeatures:
|
|
35
|
-
classTotals:
|
|
36
|
+
classFeatures: Record<string, Record<number, number>>
|
|
37
|
+
classTotals: Record<string, number>
|
|
36
38
|
totalExamples: number
|
|
37
39
|
smoothing: number
|
|
38
40
|
|
|
@@ -45,8 +47,8 @@ declare class ApparatusBayesClassifier extends ApparatusClassifier {
|
|
|
45
47
|
// TODO: Not needed for natural repository, but could be moved to
|
|
46
48
|
// aparatus repository. Temporarily leaving here for copypasta.
|
|
47
49
|
declare class ApparatusLogisticRegressionClassifier extends ApparatusClassifier {
|
|
48
|
-
examples:
|
|
49
|
-
// TODO: These appear
|
|
50
|
+
examples: Record<string, ApparatusObservation>
|
|
51
|
+
// TODO: These appear unused
|
|
50
52
|
// features: any[]
|
|
51
53
|
// featurePositions: { [key: string]: any | undefined }
|
|
52
54
|
maxFeaturePosition: number
|
|
@@ -71,11 +73,12 @@ declare interface ClassifierOptions {
|
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
declare type ClassifierCallback = (err: NodeJS.ErrnoException | null, classifier?: ClassifierBase) => void
|
|
76
|
+
declare type ParallelTrainerCallback = (err: NodeJS.ErrnoException | null) => void
|
|
74
77
|
|
|
75
78
|
declare class ClassifierBase extends events.EventEmitter {
|
|
76
79
|
classifier: ApparatusClassifier
|
|
77
80
|
docs: ClassifierDoc[]
|
|
78
|
-
features:
|
|
81
|
+
features: Record<string, number>
|
|
79
82
|
stemmer: Stemmer
|
|
80
83
|
lastAdded: number
|
|
81
84
|
|
|
@@ -89,14 +92,26 @@ declare class ClassifierBase extends events.EventEmitter {
|
|
|
89
92
|
classify (observation: string | string[]): string
|
|
90
93
|
setOptions (options: ClassifierOptions): void
|
|
91
94
|
save (filename: string, callback?: ClassifierCallback): void
|
|
95
|
+
static load (filename: string, stemmer: Stemmer | null | undefined, callback: ClassifierCallback): void
|
|
96
|
+
saveTo (storage: StorageBackend): string
|
|
97
|
+
static loadFrom (storage: StorageBackend): ClassifierBase
|
|
98
|
+
|
|
99
|
+
Threads: any
|
|
100
|
+
trainParallel (numThreads: number, callback: ParallelTrainerCallback): void
|
|
101
|
+
trainParallelBatches (options: { numThreads: number, batchSize: number }): void
|
|
102
|
+
retrainParallel (numThreads: number, callback: ParallelTrainerCallback): void
|
|
92
103
|
}
|
|
93
104
|
|
|
94
105
|
declare type BayesClassifierCallback = (err: NodeJS.ErrnoException | null, classifier?: BayesClassifier) => void
|
|
95
106
|
|
|
96
107
|
export class BayesClassifier extends ClassifierBase {
|
|
108
|
+
classifier: ApparatusBayesClassifier
|
|
109
|
+
|
|
97
110
|
constructor (stemmer?: Stemmer, smoothing?: number)
|
|
98
111
|
static load (filename: string, stemmer: Stemmer | null | undefined, callback: BayesClassifierCallback): void
|
|
99
|
-
static restore (classifier:
|
|
112
|
+
static restore (classifier: Record<string, unknown>, stemmer?: Stemmer): BayesClassifier
|
|
113
|
+
saveTo (storage: StorageBackend): string
|
|
114
|
+
static loadFrom (storage: StorageBackend): ClassifierBase
|
|
100
115
|
}
|
|
101
116
|
|
|
102
117
|
declare type LogisticRegressionClassifierCallback = (err: NodeJS.ErrnoException | null, classifier?: LogisticRegressionClassifier) => void
|
|
@@ -104,19 +119,22 @@ declare type LogisticRegressionClassifierCallback = (err: NodeJS.ErrnoException
|
|
|
104
119
|
export class LogisticRegressionClassifier extends ClassifierBase {
|
|
105
120
|
constructor (stemmer?: Stemmer)
|
|
106
121
|
static load (filename: string, stemmer: Stemmer | null | undefined, callback: LogisticRegressionClassifierCallback): void
|
|
107
|
-
static restore (classifier:
|
|
122
|
+
static restore (classifier: Record<string, unknown>, stemmer?: Stemmer): LogisticRegressionClassifier
|
|
123
|
+
saveTo (storage: StorageBackend): string
|
|
124
|
+
static loadFrom (storage: StorageBackend): ClassifierBase
|
|
108
125
|
}
|
|
109
126
|
|
|
110
|
-
declare type MaxEntClassifierCallback = (err: NodeJS.ErrnoException | null, classifier?: MaxEntClassifier
|
|
127
|
+
declare type MaxEntClassifierCallback = (err: NodeJS.ErrnoException | null, classifier?: MaxEntClassifier) => void
|
|
111
128
|
|
|
112
129
|
export class MaxEntClassifier {
|
|
113
130
|
sample: Sample
|
|
114
131
|
features: FeatureSet
|
|
132
|
+
scaler: GISScaler
|
|
115
133
|
|
|
116
134
|
constructor (features: FeatureSet, sample: Sample)
|
|
117
135
|
addElement (x: Element): void
|
|
118
136
|
addDocument (context: Context, classification: string, elementClass: Element): void
|
|
119
|
-
train (maxIterations: number, minImprovement: number
|
|
137
|
+
train (maxIterations: number, minImprovement: number): void
|
|
120
138
|
getClassifications (b: Context): ApparatusClassification[]
|
|
121
139
|
classify (b: Context): string
|
|
122
140
|
// These are not static like in other Classifier classes
|
|
@@ -160,7 +178,7 @@ export class Feature {
|
|
|
160
178
|
|
|
161
179
|
export class FeatureSet {
|
|
162
180
|
features: Feature[]
|
|
163
|
-
map:
|
|
181
|
+
map: Record<string, boolean>
|
|
164
182
|
|
|
165
183
|
addFeature (feature: Feature): boolean
|
|
166
184
|
featureExists (feature: Feature): boolean
|
|
@@ -172,8 +190,8 @@ export class FeatureSet {
|
|
|
172
190
|
declare type SampleCallback = (err: NodeJS.ErrnoException | null, sample?: Sample | null) => void
|
|
173
191
|
|
|
174
192
|
export class Sample {
|
|
175
|
-
frequencyOfContext:
|
|
176
|
-
frequency:
|
|
193
|
+
frequencyOfContext: Record<string, number>
|
|
194
|
+
frequency: Record<string, number>
|
|
177
195
|
classes: string[]
|
|
178
196
|
|
|
179
197
|
constructor (elements?: Element[])
|
|
@@ -215,6 +233,9 @@ export class POSElement extends Element {
|
|
|
215
233
|
}
|
|
216
234
|
|
|
217
235
|
export class GISScaler {
|
|
236
|
+
iteration: number
|
|
237
|
+
improvement: number
|
|
238
|
+
|
|
218
239
|
constructor (featureSet: FeatureSet, sample: Sample)
|
|
219
240
|
calculateMaxSumOfFeatures (): boolean
|
|
220
241
|
addCorrectionFeature (): void
|
|
@@ -42,15 +42,20 @@ class LogisticRegressionClassifier extends Classifier {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
static load (filename, stemmer, callback) {
|
|
45
|
-
Classifier.load(filename,
|
|
45
|
+
Classifier.load(filename, (err, classifier) => {
|
|
46
46
|
if (err) {
|
|
47
47
|
callback(err)
|
|
48
48
|
} else {
|
|
49
|
-
callback(err,
|
|
49
|
+
callback(err, LogisticRegressionClassifier.restore(classifier, stemmer))
|
|
50
50
|
}
|
|
51
51
|
})
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
static async loadFrom (key, stemmer, storageBackend) {
|
|
55
|
+
const classifier = await Classifier.loadFrom(key, storageBackend)
|
|
56
|
+
return LogisticRegressionClassifier.restore(classifier, stemmer)
|
|
57
|
+
}
|
|
58
|
+
|
|
54
59
|
train () {
|
|
55
60
|
// we need to reset the training state because logistic regression
|
|
56
61
|
// needs its matricies to have their widths synced, etc.
|
|
@@ -32,6 +32,8 @@ const Sample = require('./Sample')
|
|
|
32
32
|
const Scaler = require('./GISScaler')
|
|
33
33
|
const FeatureSet = require('./FeatureSet')
|
|
34
34
|
|
|
35
|
+
const DEBUG = false
|
|
36
|
+
|
|
35
37
|
class Classifier {
|
|
36
38
|
constructor (features, sample) {
|
|
37
39
|
if (features) {
|
|
@@ -77,7 +79,7 @@ class Classifier {
|
|
|
77
79
|
const classifier = this
|
|
78
80
|
fs.writeFile(filename, data, 'utf8', function (err) {
|
|
79
81
|
if (callback) {
|
|
80
|
-
console.log('Saved classifier to ' + filename)
|
|
82
|
+
DEBUG && console.log('Saved classifier to ' + filename)
|
|
81
83
|
callback(err, err ? null : classifier)
|
|
82
84
|
}
|
|
83
85
|
})
|
|
@@ -91,9 +93,9 @@ class Classifier {
|
|
|
91
93
|
Classifier.prototype.addElement(new ElementClass(classification, context))
|
|
92
94
|
}
|
|
93
95
|
|
|
94
|
-
train (maxIterations, minImprovement
|
|
96
|
+
train (maxIterations, minImprovement) {
|
|
95
97
|
this.scaler = new Scaler(this.features, this.sample)
|
|
96
|
-
this.p = this.scaler.run(maxIterations, minImprovement
|
|
98
|
+
this.p = this.scaler.run(maxIterations, minImprovement)
|
|
97
99
|
}
|
|
98
100
|
|
|
99
101
|
getClassifications (b) {
|