natural 0.5.4 → 0.6.1
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/.dockerignore +2 -0
- package/.npmignore +1 -0
- package/.travis.yml +1 -1
- package/README.md +412 -89
- package/bayes_classifier.json +1 -1
- package/classifier.json +418 -0
- package/data/kata-dasar.txt +29933 -0
- package/lib/natural/brill_pos_tagger/lib/Brill_POS_Tagger.js +8 -9
- package/lib/natural/brill_pos_tagger/lib/Brill_POS_Tester.js +5 -5
- package/lib/natural/brill_pos_tagger/lib/Brill_POS_Trainer.js +10 -6
- package/lib/natural/brill_pos_tagger/lib/Corpus.js +21 -16
- package/lib/natural/brill_pos_tagger/lib/Lexicon.js +22 -13
- package/lib/natural/brill_pos_tagger/lib/Predicate.js +2 -2
- package/lib/natural/brill_pos_tagger/lib/RuleTemplate.js +2 -2
- package/lib/natural/brill_pos_tagger/lib/RuleTemplates.js +132 -146
- package/lib/natural/brill_pos_tagger/lib/Sentence.js +41 -0
- package/lib/natural/brill_pos_tagger/lib/TF_Parser.js +83 -76
- package/lib/natural/brill_pos_tagger/lib/TF_Parser.pegjs +19 -4
- package/lib/natural/brill_pos_tagger/lib/TransformationRule.js +10 -11
- package/lib/natural/classifiers/classifier.js +15 -8
- package/lib/natural/classifiers/maxent/Classifier.js +125 -0
- package/lib/natural/classifiers/maxent/Context.js +33 -0
- package/lib/natural/classifiers/maxent/Distribution.js +145 -0
- package/lib/natural/classifiers/maxent/Element.js +34 -0
- package/lib/natural/classifiers/maxent/Feature.js +88 -0
- package/lib/natural/classifiers/maxent/FeatureSet.js +67 -0
- package/lib/natural/classifiers/maxent/GISScaler.js +148 -0
- package/lib/natural/classifiers/maxent/POS/ME_Corpus.js +56 -0
- package/lib/natural/classifiers/maxent/POS/ME_Sentence.js +73 -0
- package/lib/natural/classifiers/maxent/POS/POS_Element.js +172 -0
- package/lib/natural/classifiers/maxent/Sample.js +141 -0
- package/lib/natural/classifiers/maxent/SimpleExample/SE_Element.js +49 -0
- package/lib/natural/distance/hamming_distance.js +52 -0
- package/lib/natural/distance/jaro-winkler_distance.js +16 -6
- package/lib/natural/distance/levenshtein_distance.js +164 -11
- package/lib/natural/index.js +26 -5
- package/lib/natural/inflectors/noun_inflector.js +15 -13
- package/lib/natural/ngrams/ngrams.js +83 -25
- package/lib/natural/normalizers/normalizer_sv.js +41 -0
- package/lib/natural/sentiment/Basque/senticon_eu.json +21557 -0
- package/lib/natural/sentiment/Catalan/senticon_ca.json +36352 -0
- package/lib/natural/sentiment/Dutch/negations_du.json +3 -0
- package/lib/natural/sentiment/Dutch/pattern-sentiment-nl.json +39650 -0
- package/lib/natural/sentiment/English/negations_en.json +3 -0
- package/lib/natural/sentiment/English/pattern-sentiment-en.json +16024 -0
- package/lib/natural/sentiment/English/senticon_en.json +124197 -0
- package/lib/natural/sentiment/French/pattern-sentiment-fr.json +40906 -0
- package/lib/natural/sentiment/Galician/senticon_gl.json +24427 -0
- package/lib/natural/sentiment/Italian/pattern-sentiment-it.json +24522 -0
- package/lib/natural/sentiment/SentimentAnalyzer.js +116 -0
- package/lib/natural/sentiment/Spanish/afinnShortSortedSpanish.json +1655 -0
- package/lib/natural/sentiment/Spanish/negations_es.json +3 -0
- package/lib/natural/sentiment/Spanish/senticon_es.json +56722 -0
- package/lib/natural/sentiment/tools/README.md +31 -0
- package/lib/natural/sentiment/tools/XmlParser4PatternData.js +31 -0
- package/lib/natural/sentiment/tools/sentimentXmlParser.js +30 -0
- package/lib/natural/stemmers/indonesian/base_stemmer_id.js +86 -0
- package/lib/natural/stemmers/indonesian/prefix_rules.js +1027 -0
- package/lib/natural/stemmers/indonesian/removal.js +49 -0
- package/lib/natural/stemmers/indonesian/stemmer_id.js +307 -0
- package/lib/natural/stemmers/indonesian/suffix_rules.js +75 -0
- package/lib/natural/stemmers/porter_stemmer_es.js +265 -199
- package/lib/natural/stemmers/porter_stemmer_fr.js +1 -1
- package/lib/natural/stemmers/porter_stemmer_nl.js +362 -0
- package/lib/natural/stemmers/porter_stemmer_sv.js +99 -0
- package/lib/natural/stemmers/stemmer_es.js +18 -12
- package/lib/natural/stemmers/stemmer_nl.js +64 -0
- package/lib/natural/stemmers/stemmer_sv.js +61 -0
- package/lib/natural/tfidf/tfidf.js +27 -4
- package/lib/natural/tokenizers/aggressive_tokenizer_id.js +44 -0
- package/lib/natural/tokenizers/aggressive_tokenizer_sv.js +40 -0
- package/lib/natural/tokenizers/aggressive_tokenizer_vi.js +37 -0
- package/lib/natural/tokenizers/orthography_matchers.js +33 -0
- package/lib/natural/tokenizers/regexp_tokenizer.js +23 -3
- package/lib/natural/tokenizers/sentence_tokenizer.js +1 -1
- package/lib/natural/util/stopwords_id.js +119 -0
- package/lib/natural/util/stopwords_no.js +1 -1
- package/lib/natural/util/stopwords_sv.js +70 -0
- package/lib/natural/wordnet/wordnet_file.js +7 -1
- package/lr_classifier.json +1 -1
- package/package-lock.json +378 -0
- package/package.json +29 -21
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Context class
|
|
3
|
+
Copyright (C) 2017 Hugo W.L. ter Doest
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License
|
|
16
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
var stringify = require('json-stable-stringify');
|
|
20
|
+
|
|
21
|
+
function Context(data) {
|
|
22
|
+
this.data = data;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Create a predictable key string for looking up in a hash
|
|
26
|
+
Context.prototype.toString = function() {
|
|
27
|
+
if (!this.key) {
|
|
28
|
+
this.key = stringify(this.data);
|
|
29
|
+
}
|
|
30
|
+
return this.key;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
module.exports = Context;
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Distribution class for probability distributions
|
|
3
|
+
Copyright (C) 2017 Hugo W.L. ter Doest
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License
|
|
16
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
var Element = require('./Element');
|
|
20
|
+
|
|
21
|
+
function Distribution(alpha, featureSet, sample) {
|
|
22
|
+
this.alpha = alpha;
|
|
23
|
+
this.featureSet = featureSet;
|
|
24
|
+
this.sample = sample;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Returns the distribution as a string that can be stored for later usage
|
|
28
|
+
Distribution.prototype.toString = function() {
|
|
29
|
+
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
Distribution.prototype.weight = function(x) {
|
|
33
|
+
var product = 1;
|
|
34
|
+
var that = this;
|
|
35
|
+
|
|
36
|
+
this.alpha.forEach(function(alpha_j, j) {
|
|
37
|
+
product *= Math.pow(alpha_j, that.featureSet.getFeatures()[j].apply(x));
|
|
38
|
+
});
|
|
39
|
+
return product;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
Distribution.prototype.calculateAPriori = function(x) {
|
|
43
|
+
if (!this.aPriorisBeforeNormalisation[x.toString()]) {
|
|
44
|
+
this.aPriorisBeforeNormalisation[x.toString()] = this.weight(x);
|
|
45
|
+
}
|
|
46
|
+
return this.aPriorisBeforeNormalisation[x.toString()];// / this.aPrioriNormalisationConstant;
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Memoize a priori probabilities of sample elements
|
|
50
|
+
Distribution.prototype.prepareWeights = function() {
|
|
51
|
+
this.aPriorisBeforeNormalisation = {};
|
|
52
|
+
this.aPrioriNormalisationConstant = 0;
|
|
53
|
+
var sum = 0;
|
|
54
|
+
var that = this;
|
|
55
|
+
this.sample.elements.forEach(function(x) {
|
|
56
|
+
that.aPriorisBeforeNormalisation[x.toString()] = that.weight(x);
|
|
57
|
+
sum += that.aPriorisBeforeNormalisation[x.toString()];
|
|
58
|
+
});
|
|
59
|
+
this.aPrioriNormalisationConstant = sum;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
Distribution.prototype.calculateAPosteriori = function(x) {
|
|
63
|
+
if (!this.aPriorisBeforeNormalisation[x.toString()]) {
|
|
64
|
+
this.aPriorisBeforeNormalisation[x.toString()] = this.weight(x);
|
|
65
|
+
}
|
|
66
|
+
if (!this.aPosterioriNormalisationConstants[x.b.toString()]) {
|
|
67
|
+
this.aPosterioriNormalisationConstants[x.b.toString()] = this.aPosterioriNormalisation(x.b);
|
|
68
|
+
}
|
|
69
|
+
return this.aPriorisBeforeNormalisation[x] / this.aPosterioriNormalisationConstants[x.b.toString()];
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
Distribution.prototype.aPosterioriNormalisation = function(b) {
|
|
73
|
+
var sum = 0;
|
|
74
|
+
|
|
75
|
+
var that = this;
|
|
76
|
+
this.sample.getClasses().forEach(function(a) {
|
|
77
|
+
sum += that.weight(new Element(a, b));
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
return(sum);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// Memoize a posteriori probabilities of sample elements
|
|
84
|
+
Distribution.prototype.prepareAPosterioris = function() {
|
|
85
|
+
this.aPosterioriNormalisationConstants = {};
|
|
86
|
+
|
|
87
|
+
var contextSeen = {};
|
|
88
|
+
var that = this;
|
|
89
|
+
this.sample.elements.forEach(function(sampleElement) {
|
|
90
|
+
var context = sampleElement.b;
|
|
91
|
+
if (!contextSeen[context]) {
|
|
92
|
+
contextSeen[context] = true;
|
|
93
|
+
that.aPosterioriNormalisationConstants[context] =
|
|
94
|
+
that.aPosterioriNormalisation(context);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// Memoize all probabilities of sample elements
|
|
100
|
+
Distribution.prototype.prepare = function() {
|
|
101
|
+
this.prepareWeights();
|
|
102
|
+
//console.log("Weights prepared");
|
|
103
|
+
this.prepareAPosterioris();
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// Relative entropy between observered distribution and derived distribution
|
|
107
|
+
Distribution.prototype.KullbackLieblerDistance = function() {
|
|
108
|
+
var sum = 0;
|
|
109
|
+
var that = this;
|
|
110
|
+
this.sample.elements.forEach(function(x) {
|
|
111
|
+
sum += that.sample.observedProbability(x) * Math.log(that.sample.observedProbability(x) / that.calculateAPriori(x));
|
|
112
|
+
});
|
|
113
|
+
return sum;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
Distribution.prototype.logLikelihood = function() {
|
|
117
|
+
var sum = 0;
|
|
118
|
+
var that = this;
|
|
119
|
+
this.sample.elements.forEach(function(x) {
|
|
120
|
+
sum += that.sample.observedProbability(x) * Math.log(that.calculateAPriori(x));
|
|
121
|
+
});
|
|
122
|
+
return sum;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
Distribution.prototype.entropy = function() {
|
|
126
|
+
var sum = 0;
|
|
127
|
+
var that = this;
|
|
128
|
+
this.sample.elements.forEach(function(x) {
|
|
129
|
+
var p = that.calculateAPriori(x);
|
|
130
|
+
sum += p * Math.log(p);
|
|
131
|
+
});
|
|
132
|
+
return sum;
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
Distribution.prototype.checkSum = function() {
|
|
136
|
+
var sum = 0;
|
|
137
|
+
var that = this;
|
|
138
|
+
this.sample.elements.forEach(function(x) {
|
|
139
|
+
sum += that.calculateAPriori(x);
|
|
140
|
+
});
|
|
141
|
+
//console.log("Distribution.checkSum is " + sum);
|
|
142
|
+
return sum;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
module.exports = Distribution;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Element class for elements in the event space
|
|
3
|
+
Copyright (C) 2017 Hugo W.L. ter Doest
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License
|
|
16
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
var Feature = require('./Feature');
|
|
20
|
+
|
|
21
|
+
// a is class and b is context
|
|
22
|
+
function Element(a, b) {
|
|
23
|
+
this.a = a;
|
|
24
|
+
this.b = b;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
Element.prototype.toString = function() {
|
|
28
|
+
if (!this.key) {
|
|
29
|
+
this.key = this.a + this.b.toString();
|
|
30
|
+
}
|
|
31
|
+
return this.key;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
module.exports = Element;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Feature class for features that fire (or don't) on combinations of context
|
|
3
|
+
and class
|
|
4
|
+
Copyright (C) 2017 Hugo W.L. ter Doest
|
|
5
|
+
|
|
6
|
+
This program is free software: you can redistribute it and/or modify
|
|
7
|
+
it under the terms of the GNU General Public License as published by
|
|
8
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
(at your option) any later version.
|
|
10
|
+
|
|
11
|
+
This program is distributed in the hope that it will be useful,
|
|
12
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
GNU General Public License for more details.
|
|
15
|
+
|
|
16
|
+
You should have received a copy of the GNU General Public License
|
|
17
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
function Feature(f, name, parameters) {
|
|
22
|
+
this.evaluate = f;
|
|
23
|
+
this.name = name;
|
|
24
|
+
this.parameters = parameters;
|
|
25
|
+
|
|
26
|
+
var tmp = "";
|
|
27
|
+
parameters.forEach(function(par) {
|
|
28
|
+
tmp += par + "|";
|
|
29
|
+
});
|
|
30
|
+
this.parametersKey = tmp.substr(0, tmp.length - 1);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
Feature.prototype.apply = function(x) {
|
|
34
|
+
return this.evaluate(x);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
Feature.prototype.expectationApprox = function(p, sample) {
|
|
38
|
+
var totalSum = 0;
|
|
39
|
+
var that = this;
|
|
40
|
+
var sum = 0;
|
|
41
|
+
var seen = {};
|
|
42
|
+
var A = sample.getClasses();
|
|
43
|
+
sample.elements.forEach(function(sampleElement) {
|
|
44
|
+
var b_i = sampleElement.b;
|
|
45
|
+
|
|
46
|
+
if (!seen[b_i.toString()]) {
|
|
47
|
+
seen[b_i.toString()] = true;
|
|
48
|
+
var Element = require('./Element');
|
|
49
|
+
|
|
50
|
+
A.forEach(function(a) {
|
|
51
|
+
var x = new Element(a, b_i);
|
|
52
|
+
sum += sample.observedProbabilityOfContext(b_i) * p.calculateAPosteriori(x) * that.apply(x);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
return sum;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Diect calculation of expected value of this feature according to distribution p
|
|
60
|
+
// In real-life applications with a lot of features this is not tractable
|
|
61
|
+
Feature.prototype.expectation = function(p, A, B) {
|
|
62
|
+
var sum = 0;
|
|
63
|
+
var that = this;
|
|
64
|
+
A.forEach(function(a) {
|
|
65
|
+
B.forEach(function(b) {
|
|
66
|
+
var x = new Element(a, b);
|
|
67
|
+
sum += (p.calculateAPriori(x) * that.apply(x));
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
return sum;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Observed expectation of this feature in the sample
|
|
74
|
+
Feature.prototype.observedExpectation = function(sample) {
|
|
75
|
+
if (this.observedExpect) {
|
|
76
|
+
return this.observedExpect;
|
|
77
|
+
}
|
|
78
|
+
var N = sample.size();
|
|
79
|
+
var sum = 0;
|
|
80
|
+
var that = this;
|
|
81
|
+
sample.elements.forEach(function(x) {
|
|
82
|
+
sum += that.apply(x);
|
|
83
|
+
});
|
|
84
|
+
this.observedExpect = sum / N;
|
|
85
|
+
return this.observedExpect;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
module.exports = Feature;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Feature set class for administrating a set of unique feature
|
|
3
|
+
Copyright (C) 2017 Hugo W.L. ter Doest
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License
|
|
16
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
function FeatureSet() {
|
|
21
|
+
this.features = [];
|
|
22
|
+
this.map = {};
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Returns true if the feature did not exist and was added
|
|
26
|
+
FeatureSet.prototype.addFeature = function(feature) {
|
|
27
|
+
if (!this.featureExists(feature)) {
|
|
28
|
+
this.map[feature.name +" | " + feature.parametersKey] = true;
|
|
29
|
+
this.features.push(feature);
|
|
30
|
+
//console.log("FeatureSet.addFeature: feature added: " + feature.name + " - " + feature.parametersKey);
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
FeatureSet.prototype.featureExists = function(feature) {
|
|
39
|
+
if (this.map[feature.name +" | " + feature.parametersKey]) {
|
|
40
|
+
//console.log("FeatureSet.featureExists: feature already exists: " +
|
|
41
|
+
// feature.name + " - " + feature.parameters);
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// Returns an array of features
|
|
50
|
+
// If the available array this.features is up to date it is returned immediately
|
|
51
|
+
FeatureSet.prototype.getFeatures = function() {
|
|
52
|
+
return this.features;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
FeatureSet.prototype.size = function() {
|
|
56
|
+
return this.features.length;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
FeatureSet.prototype.prettyPrint = function() {
|
|
60
|
+
var s = "";
|
|
61
|
+
Object.keys(this.map).forEach(function(key) {
|
|
62
|
+
s += key + "\n";
|
|
63
|
+
});
|
|
64
|
+
return s;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
module.exports = FeatureSet;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/*
|
|
2
|
+
GISScaler class that finds parameters of features
|
|
3
|
+
Copyright (C) 2017 Hugo W.L. ter Doest
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License
|
|
16
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
var Element = require('./Element');
|
|
20
|
+
var Feature = require('./Feature');
|
|
21
|
+
var Distribution = require('./Distribution');
|
|
22
|
+
|
|
23
|
+
// classes is an array of classes
|
|
24
|
+
// features is an array of feature functions
|
|
25
|
+
function GISScaler(featureSet, sample) {
|
|
26
|
+
this.featureSet = featureSet;
|
|
27
|
+
this.sample = sample;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Returns true if a correction feature is necessary
|
|
31
|
+
GISScaler.prototype.calculateMaxSumOfFeatures = function() {
|
|
32
|
+
var that = this;
|
|
33
|
+
this.C = 0;
|
|
34
|
+
this.featureSums = {};
|
|
35
|
+
var listOfSumValues = [];
|
|
36
|
+
|
|
37
|
+
// Since feature functions are derived from the sample
|
|
38
|
+
// we can use the sample to calculate the max sum
|
|
39
|
+
// We look at each sample element only once
|
|
40
|
+
this.sample.elements.forEach(function(x) {
|
|
41
|
+
if (!that.featureSums[x.toString()]) {
|
|
42
|
+
var sum = 0;
|
|
43
|
+
that.featureSet.getFeatures().forEach(function(f) {
|
|
44
|
+
sum += f.apply(x);
|
|
45
|
+
});
|
|
46
|
+
if (sum > that.C) {
|
|
47
|
+
that.C = sum;
|
|
48
|
+
}
|
|
49
|
+
that.featureSums[x.toString()] = sum;
|
|
50
|
+
listOfSumValues.push(sum);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
//console.log("GISScaler:calculateMaxSumOfFeatures:maxSum is " + this.C);
|
|
54
|
+
|
|
55
|
+
// Check if a correction feature is necessary
|
|
56
|
+
listOfSumValues.sort(function(a, b) {
|
|
57
|
+
return a - b;
|
|
58
|
+
});
|
|
59
|
+
return(listOfSumValues[0] !== listOfSumValues[listOfSumValues.length - 1]);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
GISScaler.prototype.addCorrectionFeature = function() {
|
|
63
|
+
if (this.calculateMaxSumOfFeatures()) {
|
|
64
|
+
//console.log("GISScaler:addCorrectionFeature:C is " + this.C);
|
|
65
|
+
var that = this;
|
|
66
|
+
|
|
67
|
+
function f(x) {
|
|
68
|
+
if (that.featureSums[x.toString()] !== undefined) {
|
|
69
|
+
return that.C - that.featureSums[x.toString()];
|
|
70
|
+
}
|
|
71
|
+
return 0;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
var correctionFeature = new Feature(f, "Correction feature", []);
|
|
75
|
+
//console.log("GISScaler:addCorrectionFeature:correctionFeature " + JSON.stringify(correctionFeature));
|
|
76
|
+
this.featureSet.addFeature(correctionFeature);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
//console.log("Correction feature not needed");
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// This is the Generalised Iterative Scaling algorithm
|
|
84
|
+
// It ends if the improvement in likelihood of the distribution does not
|
|
85
|
+
// improve more than minImprovement or if the maximum number of iterations is
|
|
86
|
+
// reached.
|
|
87
|
+
GISScaler.prototype.run = function(maxIterations, minImprovement) {
|
|
88
|
+
this.iteration = 0;
|
|
89
|
+
this.improvement = 0;
|
|
90
|
+
|
|
91
|
+
this.addCorrectionFeature();
|
|
92
|
+
// Build up the distribution p
|
|
93
|
+
var alpha = new Array(this.featureSet.size());
|
|
94
|
+
for (var i = 0; i < alpha.length; i++) {
|
|
95
|
+
alpha[i] = 1;
|
|
96
|
+
}
|
|
97
|
+
var p = new Distribution(alpha, this.featureSet, this.sample);
|
|
98
|
+
//console.log("Distribution created");
|
|
99
|
+
p.prepare();
|
|
100
|
+
//console.log("Distribution prepared");
|
|
101
|
+
var likelihood = p.logLikelihood();
|
|
102
|
+
var KLDistance = p.KullbackLieblerDistance();
|
|
103
|
+
|
|
104
|
+
var newAlpha = new Array(this.featureSet.size());
|
|
105
|
+
var observedExpectation = 0;
|
|
106
|
+
var expectationApprox = 0;
|
|
107
|
+
do {
|
|
108
|
+
//console.log("Iteration " + this.iteration + " - Log likelihood of sample: " + likelihood + " - Entropy: " + p.entropy());
|
|
109
|
+
for (var i = 0; i < this.featureSet.size(); i++) {
|
|
110
|
+
observedExpectation = this.featureSet.getFeatures()[i].observedExpectation(this.sample);
|
|
111
|
+
expectationApprox = this.featureSet.getFeatures()[i].expectationApprox(p, this.sample);
|
|
112
|
+
//console.log("Iteration " + this.iteration + " - Feature " + i);
|
|
113
|
+
newAlpha[i] = p.alpha[i] * Math.pow(observedExpectation / expectationApprox, 1 / this.C);
|
|
114
|
+
|
|
115
|
+
//console.log("GISScaler.run: old alpha[" + i + "]: " + p.alpha[i]);
|
|
116
|
+
//console.log("GISScaler.run: new alpha[" + i + "]: " + newAlpha[i]);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Make the newly calculated parameters current parameters
|
|
120
|
+
newAlpha.forEach(function(newAlpha_j, j) {
|
|
121
|
+
p.alpha[j] = newAlpha_j;
|
|
122
|
+
});
|
|
123
|
+
// Recalculate a priori and a posteriori probabilities
|
|
124
|
+
p.prepare();
|
|
125
|
+
|
|
126
|
+
this.iteration++;
|
|
127
|
+
var newLikelihood = p.logLikelihood();
|
|
128
|
+
var newKLDistance = p.KullbackLieblerDistance();
|
|
129
|
+
this.improvement = KLDistance - newKLDistance;
|
|
130
|
+
//console.log("Iteration " + this.iteration + " - Old likelihood: " + likelihood + " - New likelihood: " + newLikelihood);
|
|
131
|
+
//console.log("Iteration " + this.iteration + " - Old KL: " + KLDistance + " - New KL: " + newKLDistance);
|
|
132
|
+
|
|
133
|
+
likelihood = newLikelihood;
|
|
134
|
+
KLDistance = newKLDistance;
|
|
135
|
+
} while ((this.iteration < maxIterations) && (this.improvement > minImprovement));
|
|
136
|
+
//} while (iteration < maxIterations);
|
|
137
|
+
/*
|
|
138
|
+
var that = this;
|
|
139
|
+
this.featureSet.getFeatures().forEach(function(f, j) {
|
|
140
|
+
console.log("Observed expectation of feature " + j + ": " + f.observedExpectation(that.sample) +
|
|
141
|
+
" - Expection of feature according to p: " + f.expectationApprox(p, that.sample));
|
|
142
|
+
});
|
|
143
|
+
*/
|
|
144
|
+
|
|
145
|
+
return p;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
module.exports = GISScaler;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Corpus class specific for MaxEnt modeling
|
|
3
|
+
Copyright (C) 2018 Hugo W.L. ter Doest
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License
|
|
16
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
var util = require('util');
|
|
20
|
+
var Sample = require('../Sample');
|
|
21
|
+
var Corpus = require('../../../brill_pos_tagger/lib/Corpus');
|
|
22
|
+
|
|
23
|
+
function ME_Corpus(data, BROWN, SentenceClass) {
|
|
24
|
+
ME_Corpus.super_.call(this, data, BROWN, SentenceClass);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
util.inherits(ME_Corpus, Corpus);
|
|
28
|
+
|
|
29
|
+
ME_Corpus.prototype.generateSample = function() {
|
|
30
|
+
var sample = new Sample([]);
|
|
31
|
+
this.sentences.forEach(function(sentence) {
|
|
32
|
+
sentence.generateSampleElements(sample);
|
|
33
|
+
});
|
|
34
|
+
return sample;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// Splits the corpus in a training and testing set.
|
|
38
|
+
// percentageTrain is the size of the training corpus in percent
|
|
39
|
+
// Returns an array with two elements: training corpus, testing corpus
|
|
40
|
+
ME_Corpus.prototype.splitInTrainAndTest = function(percentageTrain) {
|
|
41
|
+
var corpusTrain = new ME_Corpus();
|
|
42
|
+
var corpusTest = new ME_Corpus();
|
|
43
|
+
|
|
44
|
+
var p = percentageTrain / 100;
|
|
45
|
+
this.sentences.forEach(function(sentence, i) {
|
|
46
|
+
if (Math.random() < p) {
|
|
47
|
+
corpusTrain.sentences.push(sentence);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
corpusTest.sentences.push(sentence);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
return [corpusTrain, corpusTest];
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
module.exports = ME_Corpus;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Sentence class specific for MaxEnt modeling
|
|
3
|
+
Copyright (C) 2018 Hugo W.L. ter Doest
|
|
4
|
+
|
|
5
|
+
This program is free software: you can redistribute it and/or modify
|
|
6
|
+
it under the terms of the GNU General Public License as published by
|
|
7
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
8
|
+
(at your option) any later version.
|
|
9
|
+
|
|
10
|
+
This program is distributed in the hope that it will be useful,
|
|
11
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
13
|
+
GNU General Public License for more details.
|
|
14
|
+
|
|
15
|
+
You should have received a copy of the GNU General Public License
|
|
16
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
var util = require('util');
|
|
20
|
+
var Context = require('../Context');
|
|
21
|
+
var Sentence = require('../../../brill_pos_tagger/lib/Sentence');
|
|
22
|
+
var Element = require('./POS_Element');
|
|
23
|
+
|
|
24
|
+
function ME_Sentence() {
|
|
25
|
+
ME_Sentence.super_.call(this);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
util.inherits(ME_Sentence, Sentence);
|
|
29
|
+
|
|
30
|
+
ME_Sentence.prototype.generateSampleElements = function(sample) {
|
|
31
|
+
var sentence = this.taggedWords;
|
|
32
|
+
sentence.forEach(function(token, index) {
|
|
33
|
+
var x = new Element(
|
|
34
|
+
token.tag,
|
|
35
|
+
new Context({
|
|
36
|
+
wordWindow: {},
|
|
37
|
+
tagWindow: {}
|
|
38
|
+
})
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
// Current word and tag
|
|
42
|
+
x.b.data.wordWindow["0"] = token.token;
|
|
43
|
+
x.b.data.tagWindow["0"] = sentence[index].tag;
|
|
44
|
+
|
|
45
|
+
// Previous bigram
|
|
46
|
+
if (index > 1) {
|
|
47
|
+
x.b.data.tagWindow["-2"] = sentence[index - 2].tag;
|
|
48
|
+
x.b.data.wordWindow["-2"] = sentence[index - 2].token;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Left bigram
|
|
52
|
+
if (index > 0) {
|
|
53
|
+
x.b.data.tagWindow["-1"] = sentence[index - 1].tag;
|
|
54
|
+
x.b.data.wordWindow["-1"] = sentence[index - 1].token;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Right bigram
|
|
58
|
+
if (index < sentence.length - 1) {
|
|
59
|
+
x.b.data.tagWindow["1"] = sentence[index + 1].tag;
|
|
60
|
+
x.b.data.wordWindow["1"] = sentence[index + 1].token;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Next bigram
|
|
64
|
+
if (index < sentence.length - 2) {
|
|
65
|
+
x.b.data.tagWindow["2"] = sentence[index + 2].tag;
|
|
66
|
+
x.b.data.wordWindow["2"] = sentence[index + 2].token;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
sample.addElement(x);
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
module.exports = ME_Sentence;
|