natural 6.10.2 → 6.10.3

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.
Files changed (42) hide show
  1. package/.github/workflows/node.js.yml +1 -1
  2. package/.github/workflows/npm-publish.yml +4 -4
  3. package/lib/natural/brill_pos_tagger/lib/Brill_POS_Tagger.js +33 -31
  4. package/lib/natural/brill_pos_tagger/lib/Brill_POS_Tester.js +33 -35
  5. package/lib/natural/brill_pos_tagger/lib/Brill_POS_Trainer.js +326 -324
  6. package/lib/natural/brill_pos_tagger/lib/Corpus.js +140 -138
  7. package/lib/natural/brill_pos_tagger/lib/Lexicon.js +78 -76
  8. package/lib/natural/brill_pos_tagger/lib/Predicate.js +19 -17
  9. package/lib/natural/brill_pos_tagger/lib/RuleSet.js +58 -56
  10. package/lib/natural/brill_pos_tagger/lib/RuleTemplate.js +11 -9
  11. package/lib/natural/brill_pos_tagger/lib/Sentence.js +21 -20
  12. package/lib/natural/brill_pos_tagger/lib/TransformationRule.js +58 -58
  13. package/lib/natural/classifiers/bayes_classifier.js +24 -29
  14. package/lib/natural/classifiers/classifier.js +136 -121
  15. package/lib/natural/classifiers/logistic_regression_classifier.js +33 -37
  16. package/lib/natural/classifiers/maxent/Classifier.js +81 -79
  17. package/lib/natural/classifiers/maxent/Context.js +11 -9
  18. package/lib/natural/classifiers/maxent/Distribution.js +107 -105
  19. package/lib/natural/classifiers/maxent/Element.js +12 -10
  20. package/lib/natural/classifiers/maxent/Feature.js +56 -54
  21. package/lib/natural/classifiers/maxent/FeatureSet.js +40 -38
  22. package/lib/natural/classifiers/maxent/GISScaler.js +110 -109
  23. package/lib/natural/classifiers/maxent/POS/ME_Corpus.js +26 -31
  24. package/lib/natural/classifiers/maxent/POS/ME_Sentence.js +37 -42
  25. package/lib/natural/classifiers/maxent/POS/POS_Element.js +119 -124
  26. package/lib/natural/classifiers/maxent/Sample.js +96 -94
  27. package/lib/natural/classifiers/maxent/SimpleExample/SE_Element.js +14 -20
  28. package/lib/natural/inflectors/fr/count_inflector.js +1 -1
  29. package/lib/natural/inflectors/fr/noun_inflector.js +179 -196
  30. package/lib/natural/inflectors/ja/noun_inflector.js +67 -84
  31. package/lib/natural/inflectors/noun_inflector.js +124 -139
  32. package/lib/natural/inflectors/present_verb_inflector.js +34 -48
  33. package/lib/natural/inflectors/singular_plural_inflector.js +62 -61
  34. package/lib/natural/tfidf/tfidf.js +118 -114
  35. package/lib/natural/trie/trie.js +161 -159
  36. package/lib/natural/util/directed_edge.js +49 -0
  37. package/lib/natural/util/edge_weighted_digraph.js +54 -74
  38. package/lib/natural/util/index.js +1 -0
  39. package/lib/natural/util/longest_path_tree.js +54 -52
  40. package/lib/natural/util/shortest_path_tree.js +56 -54
  41. package/lib/natural/util/topological.js +17 -15
  42. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  /*
2
- Feature set class for administrating a set of unique feature
3
- Copyright (C) 2017 Hugo W.L. ter Doest
2
+ Feature set class for administrating a set of unique features
3
+ Copyright (C) 2017, 2023 Hugo W.L. ter Doest
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -23,49 +23,51 @@ THE SOFTWARE.
23
23
 
24
24
  'use strict'
25
25
 
26
- function FeatureSet () {
27
- this.features = []
28
- this.map = {}
29
- }
26
+ class FeatureSet {
27
+ constructor () {
28
+ this.features = []
29
+ this.map = {}
30
+ }
30
31
 
31
- // Returns true if the feature did not exist and was added
32
- FeatureSet.prototype.addFeature = function (feature) {
33
- if (!this.featureExists(feature)) {
34
- this.map[feature.name + ' | ' + feature.parametersKey] = true
35
- this.features.push(feature)
36
- // console.log("FeatureSet.addFeature: feature added: " + feature.name + " - " + feature.parametersKey);
37
- return true
38
- } else {
39
- return false
32
+ // Returns true if the feature did not exist and was added
33
+ addFeature (feature) {
34
+ if (!this.featureExists(feature)) {
35
+ this.map[feature.name + ' | ' + feature.parametersKey] = true
36
+ this.features.push(feature)
37
+ // console.log("FeatureSet.addFeature: feature added: " + feature.name + " - " + feature.parametersKey);
38
+ return true
39
+ } else {
40
+ return false
41
+ }
40
42
  }
41
- }
42
43
 
43
- FeatureSet.prototype.featureExists = function (feature) {
44
- if (this.map[feature.name + ' | ' + feature.parametersKey]) {
45
- // console.log("FeatureSet.featureExists: feature already exists: " +
46
- // feature.name + " - " + feature.parameters);
47
- return true
48
- } else {
49
- return false
44
+ featureExists (feature) {
45
+ if (this.map[feature.name + ' | ' + feature.parametersKey]) {
46
+ // console.log("FeatureSet.featureExists: feature already exists: " +
47
+ // feature.name + " - " + feature.parameters);
48
+ return true
49
+ } else {
50
+ return false
51
+ }
50
52
  }
51
- }
52
53
 
53
- // Returns an array of features
54
- // If the available array this.features is up to date it is returned immediately
55
- FeatureSet.prototype.getFeatures = function () {
56
- return this.features
57
- }
54
+ // Returns an array of features
55
+ // If the available array this.features is up to date it is returned immediately
56
+ getFeatures () {
57
+ return this.features
58
+ }
58
59
 
59
- FeatureSet.prototype.size = function () {
60
- return this.features.length
61
- }
60
+ size () {
61
+ return this.features.length
62
+ }
62
63
 
63
- FeatureSet.prototype.prettyPrint = function () {
64
- let s = ''
65
- Object.keys(this.map).forEach(function (key) {
66
- s += key + '\n'
67
- })
68
- return s
64
+ prettyPrint () {
65
+ let s = ''
66
+ Object.keys(this.map).forEach(function (key) {
67
+ s += key + '\n'
68
+ })
69
+ return s
70
+ }
69
71
  }
70
72
 
71
73
  module.exports = FeatureSet
@@ -1,6 +1,6 @@
1
1
  /*
2
2
  GISScaler class that finds parameters of features
3
- Copyright (C) 2017 Hugo W.L. ter Doest
3
+ Copyright (C) 2017, 2023 Hugo W.L. ter Doest
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -26,125 +26,126 @@ THE SOFTWARE.
26
26
  const Feature = require('./Feature')
27
27
  const Distribution = require('./Distribution')
28
28
 
29
- // classes is an array of classes
30
- // features is an array of feature functions
31
- function GISScaler (featureSet, sample) {
32
- this.featureSet = featureSet
33
- this.sample = sample
34
- }
29
+ class GISScaler {
30
+ // features is an array of feature functions
31
+ constructor (featureSet, sample) {
32
+ this.featureSet = featureSet
33
+ this.sample = sample
34
+ }
35
35
 
36
- // Returns true if a correction feature is necessary
37
- GISScaler.prototype.calculateMaxSumOfFeatures = function () {
38
- const that = this
39
- this.C = 0
40
- this.featureSums = {}
41
- const listOfSumValues = []
42
-
43
- // Since feature functions are derived from the sample
44
- // we can use the sample to calculate the max sum
45
- // We look at each sample element only once
46
- this.sample.elements.forEach(function (x) {
47
- if (!that.featureSums[x.toString()]) {
48
- let sum = 0
49
- that.featureSet.getFeatures().forEach(function (f) {
50
- sum += f.apply(x)
51
- })
52
- if (sum > that.C) {
53
- that.C = sum
36
+ // Returns true if a correction feature is necessary
37
+ calculateMaxSumOfFeatures () {
38
+ const that = this
39
+ this.C = 0
40
+ this.featureSums = {}
41
+ const listOfSumValues = []
42
+
43
+ // Since feature functions are derived from the sample
44
+ // we can use the sample to calculate the max sum
45
+ // We look at each sample element only once
46
+ this.sample.elements.forEach(function (x) {
47
+ if (!that.featureSums[x.toString()]) {
48
+ let sum = 0
49
+ that.featureSet.getFeatures().forEach(function (f) {
50
+ sum += f.apply(x)
51
+ })
52
+ if (sum > that.C) {
53
+ that.C = sum
54
+ }
55
+ that.featureSums[x.toString()] = sum
56
+ listOfSumValues.push(sum)
54
57
  }
55
- that.featureSums[x.toString()] = sum
56
- listOfSumValues.push(sum)
57
- }
58
- })
59
- // console.log("GISScaler:calculateMaxSumOfFeatures:maxSum is " + this.C);
60
-
61
- // Check if a correction feature is necessary
62
- listOfSumValues.sort(function (a, b) {
63
- return a - b
64
- })
65
- return (listOfSumValues[0] !== listOfSumValues[listOfSumValues.length - 1])
66
- }
58
+ })
59
+ // console.log("GISScaler:calculateMaxSumOfFeatures:maxSum is " + this.C);
67
60
 
68
- GISScaler.prototype.addCorrectionFeature = function () {
69
- if (this.calculateMaxSumOfFeatures()) {
70
- // console.log("GISScaler:addCorrectionFeature:C is " + this.C);
71
- const that = this
61
+ // Check if a correction feature is necessary
62
+ listOfSumValues.sort(function (a, b) {
63
+ return a - b
64
+ })
65
+ return (listOfSumValues[0] !== listOfSumValues[listOfSumValues.length - 1])
66
+ }
72
67
 
73
- function f (x) {
74
- if (that.featureSums[x.toString()] !== undefined) {
75
- return that.C - that.featureSums[x.toString()]
68
+ addCorrectionFeature () {
69
+ if (this.calculateMaxSumOfFeatures()) {
70
+ // console.log("GISScaler:addCorrectionFeature:C is " + this.C);
71
+ const that = this
72
+
73
+ function f (x) {
74
+ if (that.featureSums[x.toString()] !== undefined) {
75
+ return that.C - that.featureSums[x.toString()]
76
+ }
77
+ return 0
76
78
  }
77
- return 0
78
- }
79
79
 
80
- const correctionFeature = new Feature(f, 'Correction feature', [])
81
- // console.log("GISScaler:addCorrectionFeature:correctionFeature " + JSON.stringify(correctionFeature));
82
- this.featureSet.addFeature(correctionFeature)
83
- } else {
84
- // console.log("Correction feature not needed");
80
+ const correctionFeature = new Feature(f, 'Correction feature', [])
81
+ // console.log("GISScaler:addCorrectionFeature:correctionFeature " + JSON.stringify(correctionFeature));
82
+ this.featureSet.addFeature(correctionFeature)
83
+ } else {
84
+ // console.log("Correction feature not needed");
85
+ }
85
86
  }
86
- }
87
87
 
88
- // This is the Generalised Iterative Scaling algorithm
89
- // It ends if the improvement in likelihood of the distribution does not
90
- // improve more than minImprovement or if the maximum number of iterations is
91
- // reached.
92
- GISScaler.prototype.run = function (maxIterations, minImprovement) {
93
- this.iteration = 0
94
- this.improvement = 0
95
-
96
- this.addCorrectionFeature()
97
- // Build up the distribution p
98
- const alpha = new Array(this.featureSet.size())
99
- for (let i = 0; i < alpha.length; i++) {
100
- alpha[i] = 1
101
- }
102
- const p = new Distribution(alpha, this.featureSet, this.sample)
103
- // console.log("Distribution created");
104
- p.prepare()
105
- // console.log("Distribution prepared");
106
- let KLDistance = p.KullbackLieblerDistance()
107
-
108
- const newAlpha = new Array(this.featureSet.size())
109
- let observedExpectation = 0
110
- let expectationApprox = 0
111
- do {
112
- // console.log("Iteration " + this.iteration + " - Log likelihood of sample: " + likelihood + " - Entropy: " + p.entropy());
113
- for (let i = 0; i < this.featureSet.size(); i++) {
114
- observedExpectation = this.featureSet.getFeatures()[i].observedExpectation(this.sample)
115
- expectationApprox = this.featureSet.getFeatures()[i].expectationApprox(p, this.sample)
116
- // console.log("Iteration " + this.iteration + " - Feature " + i);
117
- newAlpha[i] = p.alpha[i] * Math.pow(observedExpectation / expectationApprox, 1 / this.C)
118
-
119
- // console.log("GISScaler.run: old alpha[" + i + "]: " + p.alpha[i]);
120
- // console.log("GISScaler.run: new alpha[" + i + "]: " + newAlpha[i]);
88
+ // This is the Generalised Iterative Scaling algorithm
89
+ // It ends if the improvement in likelihood of the distribution does not
90
+ // improve more than minImprovement or if the maximum number of iterations is
91
+ // reached.
92
+ run (maxIterations, minImprovement) {
93
+ this.iteration = 0
94
+ this.improvement = 0
95
+
96
+ this.addCorrectionFeature()
97
+ // Build up the distribution p
98
+ const alpha = new Array(this.featureSet.size())
99
+ for (let i = 0; i < alpha.length; i++) {
100
+ alpha[i] = 1
121
101
  }
122
-
123
- // Make the newly calculated parameters current parameters
124
- newAlpha.forEach(function (newAlphaj, j) {
125
- p.alpha[j] = newAlphaj
126
- })
127
- // Recalculate a priori and a posteriori probabilities
102
+ const p = new Distribution(alpha, this.featureSet, this.sample)
103
+ // console.log("Distribution created");
128
104
  p.prepare()
105
+ // console.log("Distribution prepared");
106
+ let KLDistance = p.KullbackLieblerDistance()
107
+
108
+ const newAlpha = new Array(this.featureSet.size())
109
+ let observedExpectation = 0
110
+ let expectationApprox = 0
111
+ do {
112
+ // console.log("Iteration " + this.iteration + " - Log likelihood of sample: " + likelihood + " - Entropy: " + p.entropy());
113
+ for (let i = 0; i < this.featureSet.size(); i++) {
114
+ observedExpectation = this.featureSet.getFeatures()[i].observedExpectation(this.sample)
115
+ expectationApprox = this.featureSet.getFeatures()[i].expectationApprox(p, this.sample)
116
+ // console.log("Iteration " + this.iteration + " - Feature " + i);
117
+ newAlpha[i] = p.alpha[i] * Math.pow(observedExpectation / expectationApprox, 1 / this.C)
118
+
119
+ // console.log("GISScaler.run: old alpha[" + i + "]: " + p.alpha[i]);
120
+ // console.log("GISScaler.run: new alpha[" + i + "]: " + newAlpha[i]);
121
+ }
129
122
 
130
- this.iteration++
131
- const newKLDistance = p.KullbackLieblerDistance()
132
- this.improvement = KLDistance - newKLDistance
133
- // console.log("Iteration " + this.iteration + " - Old likelihood: " + likelihood + " - New likelihood: " + newLikelihood);
134
- // console.log("Iteration " + this.iteration + " - Old KL: " + KLDistance + " - New KL: " + newKLDistance);
135
-
136
- KLDistance = newKLDistance
137
- } while ((this.iteration < maxIterations) && (this.improvement > minImprovement))
138
- // } while (iteration < maxIterations);
139
- /*
140
- var that = this;
141
- this.featureSet.getFeatures().forEach(function(f, j) {
142
- console.log("Observed expectation of feature " + j + ": " + f.observedExpectation(that.sample) +
143
- " - Expection of feature according to p: " + f.expectationApprox(p, that.sample));
144
- });
145
- */
146
-
147
- return p
123
+ // Make the newly calculated parameters current parameters
124
+ newAlpha.forEach(function (newAlphaj, j) {
125
+ p.alpha[j] = newAlphaj
126
+ })
127
+ // Recalculate a priori and a posteriori probabilities
128
+ p.prepare()
129
+
130
+ this.iteration++
131
+ const newKLDistance = p.KullbackLieblerDistance()
132
+ this.improvement = KLDistance - newKLDistance
133
+ // console.log("Iteration " + this.iteration + " - Old likelihood: " + likelihood + " - New likelihood: " + newLikelihood);
134
+ // console.log("Iteration " + this.iteration + " - Old KL: " + KLDistance + " - New KL: " + newKLDistance);
135
+
136
+ KLDistance = newKLDistance
137
+ } while ((this.iteration < maxIterations) && (this.improvement > minImprovement))
138
+ // } while (iteration < maxIterations);
139
+ /*
140
+ var that = this;
141
+ this.featureSet.getFeatures().forEach(function(f, j) {
142
+ console.log("Observed expectation of feature " + j + ": " + f.observedExpectation(that.sample) +
143
+ " - Expectation of feature according to p: " + f.expectationApprox(p, that.sample));
144
+ });
145
+ */
146
+
147
+ return p
148
+ }
148
149
  }
149
150
 
150
151
  module.exports = GISScaler
@@ -23,40 +23,35 @@ THE SOFTWARE.
23
23
 
24
24
  'use strict'
25
25
 
26
- const util = require('util')
27
26
  const Sample = require('../Sample')
28
27
  const Corpus = require('../../../brill_pos_tagger/lib/Corpus')
29
28
 
30
- function MECorpus (data, BROWN, SentenceClass) {
31
- MECorpus.super_.call(this, data, BROWN, SentenceClass)
32
- }
33
-
34
- util.inherits(MECorpus, Corpus)
35
-
36
- MECorpus.prototype.generateSample = function () {
37
- const sample = new Sample([])
38
- this.sentences.forEach(function (sentence) {
39
- sentence.generateSampleElements(sample)
40
- })
41
- return sample
42
- }
43
-
44
- // Splits the corpus in a training and testing set.
45
- // percentageTrain is the size of the training corpus in percent
46
- // Returns an array with two elements: training corpus, testing corpus
47
- MECorpus.prototype.splitInTrainAndTest = function (percentageTrain) {
48
- const corpusTrain = new MECorpus()
49
- const corpusTest = new MECorpus()
50
-
51
- const p = percentageTrain / 100
52
- this.sentences.forEach(function (sentence, i) {
53
- if (Math.random() < p) {
54
- corpusTrain.sentences.push(sentence)
55
- } else {
56
- corpusTest.sentences.push(sentence)
57
- }
58
- })
59
- return [corpusTrain, corpusTest]
29
+ class MECorpus extends Corpus {
30
+ generateSample () {
31
+ const sample = new Sample([])
32
+ this.sentences.forEach(function (sentence) {
33
+ sentence.generateSampleElements(sample)
34
+ })
35
+ return sample
36
+ }
37
+
38
+ // Splits the corpus in a training and testing set.
39
+ // percentageTrain is the size of the training corpus in percent
40
+ // Returns an array with two elements: training corpus, testing corpus
41
+ splitInTrainAndTest (percentageTrain) {
42
+ const corpusTrain = new MECorpus()
43
+ const corpusTest = new MECorpus()
44
+
45
+ const p = percentageTrain / 100
46
+ this.sentences.forEach(function (sentence, i) {
47
+ if (Math.random() < p) {
48
+ corpusTrain.sentences.push(sentence)
49
+ } else {
50
+ corpusTest.sentences.push(sentence)
51
+ }
52
+ })
53
+ return [corpusTrain, corpusTest]
54
+ }
60
55
  }
61
56
 
62
57
  module.exports = MECorpus
@@ -23,58 +23,53 @@ THE SOFTWARE.
23
23
 
24
24
  'use strict'
25
25
 
26
- const util = require('util')
27
26
  const Context = require('../Context')
28
27
  const Sentence = require('../../../brill_pos_tagger/lib/Sentence')
29
28
  const Element = require('./POS_Element')
30
29
 
31
- function MESentence (data) {
32
- MESentence.super_.call(this, data)
33
- }
34
-
35
- util.inherits(MESentence, Sentence)
36
-
37
- MESentence.prototype.generateSampleElements = function (sample) {
38
- const sentence = this.taggedWords
39
- sentence.forEach(function (token, index) {
40
- const x = new Element(
41
- token.tag,
42
- new Context({
43
- wordWindow: {},
44
- tagWindow: {}
45
- })
46
- )
30
+ class MESentence extends Sentence {
31
+ generateSampleElements (sample) {
32
+ const sentence = this.taggedWords
33
+ sentence.forEach(function (token, index) {
34
+ const x = new Element(
35
+ token.tag,
36
+ new Context({
37
+ wordWindow: {},
38
+ tagWindow: {}
39
+ })
40
+ )
47
41
 
48
- // Current word and tag
49
- x.b.data.wordWindow['0'] = token.token
50
- x.b.data.tagWindow['0'] = sentence[index].tag
42
+ // Current word and tag
43
+ x.b.data.wordWindow['0'] = token.token
44
+ x.b.data.tagWindow['0'] = sentence[index].tag
51
45
 
52
- // Previous bigram
53
- if (index > 1) {
54
- x.b.data.tagWindow['-2'] = sentence[index - 2].tag
55
- x.b.data.wordWindow['-2'] = sentence[index - 2].token
56
- }
46
+ // Previous bigram
47
+ if (index > 1) {
48
+ x.b.data.tagWindow['-2'] = sentence[index - 2].tag
49
+ x.b.data.wordWindow['-2'] = sentence[index - 2].token
50
+ }
57
51
 
58
- // Left bigram
59
- if (index > 0) {
60
- x.b.data.tagWindow['-1'] = sentence[index - 1].tag
61
- x.b.data.wordWindow['-1'] = sentence[index - 1].token
62
- }
52
+ // Left bigram
53
+ if (index > 0) {
54
+ x.b.data.tagWindow['-1'] = sentence[index - 1].tag
55
+ x.b.data.wordWindow['-1'] = sentence[index - 1].token
56
+ }
63
57
 
64
- // Right bigram
65
- if (index < sentence.length - 1) {
66
- x.b.data.tagWindow['1'] = sentence[index + 1].tag
67
- x.b.data.wordWindow['1'] = sentence[index + 1].token
68
- }
58
+ // Right bigram
59
+ if (index < sentence.length - 1) {
60
+ x.b.data.tagWindow['1'] = sentence[index + 1].tag
61
+ x.b.data.wordWindow['1'] = sentence[index + 1].token
62
+ }
69
63
 
70
- // Next bigram
71
- if (index < sentence.length - 2) {
72
- x.b.data.tagWindow['2'] = sentence[index + 2].tag
73
- x.b.data.wordWindow['2'] = sentence[index + 2].token
74
- }
64
+ // Next bigram
65
+ if (index < sentence.length - 2) {
66
+ x.b.data.tagWindow['2'] = sentence[index + 2].tag
67
+ x.b.data.wordWindow['2'] = sentence[index + 2].token
68
+ }
75
69
 
76
- sample.addElement(x)
77
- })
70
+ sample.addElement(x)
71
+ })
72
+ }
78
73
  }
79
74
 
80
75
  module.exports = MESentence