natural 6.10.2 → 6.10.4
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/.gitconfig +3 -0
- package/.github/workflows/node.js.yml +1 -1
- package/.github/workflows/npm-publish.yml +4 -4
- package/lib/natural/brill_pos_tagger/index.d.ts +32 -26
- package/lib/natural/brill_pos_tagger/lib/Brill_POS_Tagger.js +33 -31
- package/lib/natural/brill_pos_tagger/lib/Brill_POS_Tester.js +33 -35
- package/lib/natural/brill_pos_tagger/lib/Brill_POS_Trainer.js +326 -324
- package/lib/natural/brill_pos_tagger/lib/Corpus.js +140 -138
- package/lib/natural/brill_pos_tagger/lib/Lexicon.js +78 -76
- package/lib/natural/brill_pos_tagger/lib/Predicate.js +19 -17
- package/lib/natural/brill_pos_tagger/lib/RuleSet.js +58 -56
- package/lib/natural/brill_pos_tagger/lib/RuleTemplate.js +11 -9
- package/lib/natural/brill_pos_tagger/lib/Sentence.js +21 -20
- package/lib/natural/brill_pos_tagger/lib/TransformationRule.js +58 -58
- package/lib/natural/classifiers/bayes_classifier.js +24 -29
- package/lib/natural/classifiers/classifier.js +136 -121
- package/lib/natural/classifiers/logistic_regression_classifier.js +33 -37
- package/lib/natural/classifiers/maxent/Classifier.js +81 -79
- package/lib/natural/classifiers/maxent/Context.js +11 -9
- package/lib/natural/classifiers/maxent/Distribution.js +107 -105
- package/lib/natural/classifiers/maxent/Element.js +12 -10
- package/lib/natural/classifiers/maxent/Feature.js +56 -54
- package/lib/natural/classifiers/maxent/FeatureSet.js +40 -38
- package/lib/natural/classifiers/maxent/GISScaler.js +110 -109
- package/lib/natural/classifiers/maxent/POS/ME_Corpus.js +26 -31
- package/lib/natural/classifiers/maxent/POS/ME_Sentence.js +37 -42
- package/lib/natural/classifiers/maxent/POS/POS_Element.js +119 -124
- package/lib/natural/classifiers/maxent/Sample.js +96 -94
- package/lib/natural/classifiers/maxent/SimpleExample/SE_Element.js +14 -20
- package/lib/natural/inflectors/fr/count_inflector.js +1 -1
- package/lib/natural/inflectors/fr/noun_inflector.js +179 -196
- package/lib/natural/inflectors/ja/noun_inflector.js +67 -84
- package/lib/natural/inflectors/noun_inflector.js +124 -139
- package/lib/natural/inflectors/present_verb_inflector.js +34 -48
- package/lib/natural/inflectors/singular_plural_inflector.js +62 -61
- package/lib/natural/tfidf/tfidf.js +118 -114
- package/lib/natural/trie/trie.js +161 -159
- package/lib/natural/util/directed_edge.js +49 -0
- package/lib/natural/util/edge_weighted_digraph.js +54 -74
- package/lib/natural/util/index.js +1 -0
- package/lib/natural/util/longest_path_tree.js +54 -52
- package/lib/natural/util/shortest_path_tree.js +56 -54
- package/lib/natural/util/topological.js +17 -15
- package/package.json +1 -1
|
@@ -30,401 +30,403 @@ const Sentence = require('./Sentence')
|
|
|
30
30
|
// Training continues as long as there are rules with a positive score
|
|
31
31
|
// that have not been selected before
|
|
32
32
|
const minScore = 0
|
|
33
|
-
|
|
34
33
|
// After training rules with a score below scoreThreshold are pruned
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
34
|
+
|
|
35
|
+
class BrillPOSTrainer {
|
|
36
|
+
constructor (ruleScoreThreshold) {
|
|
37
|
+
if (ruleScoreThreshold) {
|
|
38
|
+
this.ruleScoreThreshold = ruleScoreThreshold
|
|
39
|
+
} else {
|
|
40
|
+
this.ruleScoreThreshold = 1
|
|
41
|
+
}
|
|
40
42
|
}
|
|
41
|
-
}
|
|
42
43
|
|
|
43
|
-
// Return the highest scoring rule from the rule set
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
// Return the highest scoring rule from the rule set
|
|
45
|
+
selectHighRule () {
|
|
46
|
+
let highestRule = null
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
48
|
+
// Walk through the map and find the rule with highest score
|
|
49
|
+
this.positiveRules.getRules().forEach(function (rule) {
|
|
50
|
+
if (highestRule === null) {
|
|
51
|
+
if (!rule.hasBeenSelectedAsHighRuleBefore) {
|
|
52
|
+
highestRule = rule
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
if ((rule.score() > highestRule.score()) &&
|
|
56
|
+
!rule.hasBeenSelectedAsHighRuleBefore) {
|
|
57
|
+
highestRule = rule
|
|
58
|
+
}
|
|
57
59
|
}
|
|
58
|
-
}
|
|
59
|
-
})
|
|
60
|
+
})
|
|
60
61
|
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
if (highestRule !== null) {
|
|
63
|
+
highestRule.hasBeenSelectedAsHighRuleBefore = true
|
|
64
|
+
}
|
|
65
|
+
// Return the rule with the highest score
|
|
66
|
+
return highestRule
|
|
63
67
|
}
|
|
64
|
-
// Return the rule with the highest score
|
|
65
|
-
return highestRule
|
|
66
|
-
}
|
|
67
68
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
69
|
+
mapRuleToSite (rule, i, j) {
|
|
70
|
+
if (!this.mapRuleToSites[rule.key()]) {
|
|
71
|
+
this.mapRuleToSites[rule.key()] = {}
|
|
72
|
+
}
|
|
73
|
+
if (!this.mapRuleToSites[rule.key()][i]) {
|
|
74
|
+
this.mapRuleToSites[rule.key()][i] = {}
|
|
75
|
+
}
|
|
76
|
+
this.mapRuleToSites[rule.key()][i][j] = true
|
|
74
77
|
}
|
|
75
|
-
this.mapRuleToSites[rule.key()][i][j] = true
|
|
76
|
-
}
|
|
77
78
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
79
|
+
mapSiteToRule (i, j, rule) {
|
|
80
|
+
if (!this.mapSiteToRules[i]) {
|
|
81
|
+
this.mapSiteToRules[i] = {}
|
|
82
|
+
}
|
|
83
|
+
if (!this.mapSiteToRules[i][j]) {
|
|
84
|
+
this.mapSiteToRules[i][j] = {}
|
|
85
|
+
}
|
|
86
|
+
this.mapSiteToRules[i][j][rule.key()] = rule
|
|
84
87
|
}
|
|
85
|
-
this.mapSiteToRules[i][j][rule.key()] = rule
|
|
86
|
-
}
|
|
87
88
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
89
|
+
associateSiteWithRule (i, j, rule) {
|
|
90
|
+
this.mapRuleToSite(rule, i, j)
|
|
91
|
+
this.mapSiteToRule(i, j, rule)
|
|
92
|
+
}
|
|
92
93
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
94
|
+
siteIsAssociatedWithRule (i, j, rule) {
|
|
95
|
+
if (this.mapSiteToRules[i]) {
|
|
96
|
+
if (this.mapSiteToRules[i][j]) {
|
|
97
|
+
if (this.mapSiteToRules[i][j][rule.key()]) {
|
|
98
|
+
return true
|
|
99
|
+
}
|
|
98
100
|
}
|
|
99
101
|
}
|
|
102
|
+
return false
|
|
100
103
|
}
|
|
101
|
-
return false
|
|
102
|
-
}
|
|
103
104
|
|
|
104
|
-
// Returns an array of all sites associated with rule
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
105
|
+
// Returns an array of all sites associated with rule
|
|
106
|
+
getSites (rule) {
|
|
107
|
+
const that = this
|
|
108
|
+
const result = []
|
|
109
|
+
Object.keys(this.mapRuleToSites[rule.key()]).forEach(function (i) {
|
|
110
|
+
Object.keys(that.mapRuleToSites[rule.key()][i]).forEach(function (j) {
|
|
111
|
+
// Unary plus the convert hash keys i and j to integer
|
|
112
|
+
result.push([+i, +j])
|
|
113
|
+
})
|
|
112
114
|
})
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}
|
|
115
|
+
// logger.debug("BrillPOSTrainer.prototype.getSites: sites " + JSON.stringify(result));
|
|
116
|
+
return (result)
|
|
117
|
+
}
|
|
117
118
|
|
|
118
|
-
// Returns an array of all rules associated with the site
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
119
|
+
// Returns an array of all rules associated with the site
|
|
120
|
+
getRules (i, j) {
|
|
121
|
+
let result = []
|
|
122
|
+
const that = this
|
|
122
123
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
124
|
+
if (this.mapSiteToRules[i]) {
|
|
125
|
+
if (this.mapSiteToRules[i][j]) {
|
|
126
|
+
result = Object.keys(this.mapSiteToRules[i][j]).map(function (key) {
|
|
127
|
+
return that.mapSiteToRules[i][j][key]
|
|
128
|
+
})
|
|
129
|
+
}
|
|
128
130
|
}
|
|
131
|
+
return result
|
|
129
132
|
}
|
|
130
|
-
return result
|
|
131
|
-
}
|
|
132
133
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
134
|
+
disconnectSiteFromRule (i, j, rule) {
|
|
135
|
+
// mapRuleToSites
|
|
136
|
+
if (this.mapRuleToSites[rule.key()]) {
|
|
137
|
+
if (this.mapRuleToSites[rule.key()][i]) {
|
|
138
|
+
if (this.mapRuleToSites[rule.key()][i][j]) {
|
|
139
|
+
delete this.mapRuleToSites[rule.key()][i][j]
|
|
140
|
+
}
|
|
139
141
|
}
|
|
140
142
|
}
|
|
141
|
-
}
|
|
142
143
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
144
|
+
// mapSiteToRules
|
|
145
|
+
if (this.mapSiteToRules[i]) {
|
|
146
|
+
if (this.mapSiteToRules[i][j]) {
|
|
147
|
+
if (this.mapSiteToRules[i][j][rule.key()]) {
|
|
148
|
+
delete this.mapSiteToRules[i][j][rule.key()]
|
|
149
|
+
}
|
|
148
150
|
}
|
|
149
151
|
}
|
|
150
152
|
}
|
|
151
|
-
}
|
|
152
153
|
|
|
153
|
-
// Adjusts the score of the rule at position i, j of the corpus
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
} else {
|
|
174
|
-
// Old tag is right
|
|
175
|
-
if (newTag === rightTag) {
|
|
176
|
-
// New tag is right --> neutral
|
|
177
|
-
rule.neutral++
|
|
178
|
-
// logger.debug("BrillPOSTrainer.prototype.scoreRule: neutral: " + rule.key() + "\t score: " + rule.neutral);
|
|
154
|
+
// Adjusts the score of the rule at position i, j of the corpus
|
|
155
|
+
scoreRule (rule, i, j) {
|
|
156
|
+
// logger.debug("BrillPOSTrainer.prototype.scoreRule: entry");
|
|
157
|
+
const token = this.corpus.sentences[i].taggedWords[j]
|
|
158
|
+
const rightTag = token.tag
|
|
159
|
+
const oldTag = token.testTag
|
|
160
|
+
const newTag = token.newTag
|
|
161
|
+
if (rightTag !== oldTag) {
|
|
162
|
+
// Old tag is wrong
|
|
163
|
+
if (newTag === rightTag) {
|
|
164
|
+
// New tag is right
|
|
165
|
+
rule.positive++
|
|
166
|
+
// If the score changes, it may be selected again as highest scoring rule
|
|
167
|
+
rule.hasBeenSelectedAsHighRuleBefore = false
|
|
168
|
+
// logger.debug("BrillPOSTrainer.prototype.scoreRule: positive: " + rule.key() + "\t score: " + rule.positive);
|
|
169
|
+
} else {
|
|
170
|
+
// New tag is wrong as well --> neutral
|
|
171
|
+
rule.neutral++
|
|
172
|
+
// logger.debug("BrillPOSTrainer.prototype.scoreRule: neutral: " + rule.key() + "\t score: " + rule.neutral);
|
|
173
|
+
}
|
|
179
174
|
} else {
|
|
180
|
-
//
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
175
|
+
// Old tag is right
|
|
176
|
+
if (newTag === rightTag) {
|
|
177
|
+
// New tag is right --> neutral
|
|
178
|
+
rule.neutral++
|
|
179
|
+
// logger.debug("BrillPOSTrainer.prototype.scoreRule: neutral: " + rule.key() + "\t score: " + rule.neutral);
|
|
180
|
+
} else {
|
|
181
|
+
// New tag is false
|
|
182
|
+
rule.negative++
|
|
183
|
+
// If the score changes, it may be selected again as highest scoring rule
|
|
184
|
+
rule.hasBeenSelectedAsHighRuleBefore = false
|
|
185
|
+
// logger.debug("BrillPOSTrainer.prototype.scoreRule: negative: " + rule.key() + "\t score: " + rule.negative);
|
|
186
|
+
}
|
|
185
187
|
}
|
|
188
|
+
// logger.debug("BrillPOSTrainer.prototype.scoreRule: exit");
|
|
186
189
|
}
|
|
187
|
-
// logger.debug("BrillPOSTrainer.prototype.scoreRule: exit");
|
|
188
|
-
}
|
|
189
190
|
|
|
190
|
-
// Generate positive rules for this given site using templates
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
191
|
+
// Generate positive rules for this given site using templates
|
|
192
|
+
generatePositiveRules (i, j) {
|
|
193
|
+
const sentence = this.corpus.sentences[i]
|
|
194
|
+
const token = sentence.taggedWords[j]
|
|
195
|
+
// A positive rule should trigger on the currently assigned testTag
|
|
196
|
+
const oldTag = token.testTag
|
|
197
|
+
// logger.debug("BrillPOSTrainer.prototype.generatePositiveRules: oldTag " + oldTag);
|
|
198
|
+
// It should assign the right tag as given by the corpus
|
|
199
|
+
const newTag = token.tag
|
|
200
|
+
// logger.debug("BrillPOSTrainer.prototype.generatePositiveRules: newTag " + newTag);
|
|
201
|
+
|
|
202
|
+
const newRules = new RuleSet()
|
|
203
|
+
// Exit if testTag already is the right tag --> will not result in positive rules
|
|
204
|
+
if (oldTag === newTag) {
|
|
205
|
+
return newRules
|
|
206
|
+
}
|
|
206
207
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
})
|
|
213
|
-
} else {
|
|
214
|
-
if (template.meta.nrParameters === 2) {
|
|
215
|
-
template.meta.parameter1Values(sentence, j).forEach(function (value1) {
|
|
216
|
-
template.meta.parameter2Values(sentence, j).forEach(function (value2) {
|
|
217
|
-
newRules.addRule(new TransformationRule(oldTag, newTag, template.predicateName, value1, value2))
|
|
218
|
-
})
|
|
208
|
+
this.templates.forEach(function (template) {
|
|
209
|
+
if (template.windowFitsSite(sentence, j)) {
|
|
210
|
+
if (template.meta.nrParameters === 1) {
|
|
211
|
+
template.meta.parameter1Values(sentence, j).forEach(function (value) {
|
|
212
|
+
newRules.addRule(new TransformationRule(oldTag, newTag, template.predicateName, value))
|
|
219
213
|
})
|
|
220
214
|
} else {
|
|
221
|
-
|
|
222
|
-
|
|
215
|
+
if (template.meta.nrParameters === 2) {
|
|
216
|
+
template.meta.parameter1Values(sentence, j).forEach(function (value1) {
|
|
217
|
+
template.meta.parameter2Values(sentence, j).forEach(function (value2) {
|
|
218
|
+
newRules.addRule(new TransformationRule(oldTag, newTag, template.predicateName, value1, value2))
|
|
219
|
+
})
|
|
220
|
+
})
|
|
221
|
+
} else {
|
|
222
|
+
// 0 paramaters
|
|
223
|
+
newRules.addRule(new TransformationRule(oldTag, newTag, template.predicateName))
|
|
224
|
+
}
|
|
223
225
|
}
|
|
224
226
|
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
}
|
|
227
|
+
})
|
|
228
|
+
return newRules
|
|
229
|
+
}
|
|
229
230
|
|
|
230
|
-
// Finds all rules that are applicable at some site
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
231
|
+
// Finds all rules that are applicable at some site
|
|
232
|
+
scanForPositiveRules () {
|
|
233
|
+
// logger.debug("BrillPOSTrainer.prototype.scanForPositiveRules: entry");
|
|
234
|
+
const that = this
|
|
235
|
+
this.corpus.sentences.forEach(function (sentence, i) {
|
|
236
|
+
sentence.taggedWords.forEach(function (token, j) {
|
|
237
|
+
// logger.debug("BrillPOSTrainer.prototype.scanForPositiveRules: sentence no " + i);
|
|
238
|
+
const newRules = that.generatePositiveRules(i, j)
|
|
239
|
+
newRules.getRules().forEach(function (rule) {
|
|
240
|
+
that.positiveRules.addRule(rule)
|
|
241
|
+
// logger.debug("BrillPOSTrainer.prototype.scanForPositiveRules: nrRules " + that.positiveRules.nrRules());
|
|
242
|
+
})
|
|
241
243
|
})
|
|
242
244
|
})
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
245
|
+
// logger.debug("BrillPOSTrainer.prototype.scanForPositiveRules: exit, number of rules: " + this.positiveRules.nrRules());
|
|
246
|
+
}
|
|
246
247
|
|
|
247
|
-
// Find all sites where the rules can be applied, register these sites and
|
|
248
|
-
// update the scores
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
248
|
+
// Find all sites where the rules can be applied, register these sites and
|
|
249
|
+
// update the scores
|
|
250
|
+
scanForSites () {
|
|
251
|
+
// logger.debug("BrillPOSTrainer.prototype.scanForSites: entry");
|
|
252
|
+
const that = this
|
|
252
253
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
254
|
+
// Scan the corpus
|
|
255
|
+
this.corpus.sentences.forEach(function (sentence, i) {
|
|
256
|
+
if (i % 100 === 0) {
|
|
257
|
+
// logger.info("BrillPOSTrainer.prototype.scanForSites: sentence " + i);
|
|
258
|
+
}
|
|
258
259
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
260
|
+
const taggedSentence = new Sentence()
|
|
261
|
+
sentence.taggedWords.forEach(function (wordObject) {
|
|
262
|
+
taggedSentence.addTaggedWord(wordObject.token, wordObject.testTag)
|
|
263
|
+
})
|
|
263
264
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
265
|
+
sentence.taggedWords.forEach(function (token, j) {
|
|
266
|
+
that.positiveRules.getRules().forEach(function (rule) {
|
|
267
|
+
if (rule.isApplicableAt(sentence, taggedSentence, j)) {
|
|
268
|
+
that.associateSiteWithRule(i, j, rule)
|
|
269
|
+
that.scoreRule(rule, i, j)
|
|
270
|
+
// logger.debug("BrillPOSTrainer.prototype.scanForSites: (sentence, token, rule): (" + i + ", " + j + ", " + rule.prettyPrint() + ")");
|
|
271
|
+
}
|
|
272
|
+
})
|
|
271
273
|
})
|
|
272
274
|
})
|
|
273
|
-
})
|
|
274
275
|
|
|
275
|
-
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
// Returns a list of sites that may have been touched by a changing tag
|
|
279
|
-
BrillPOSTrainer.prototype.neighbourhood = function (i, j) {
|
|
280
|
-
const sentenceLength = this.corpus.sentences[i].length
|
|
281
|
-
const list = []
|
|
282
|
-
|
|
283
|
-
if (this.index > 2) {
|
|
284
|
-
list.push([i, j - 3])
|
|
285
|
-
}
|
|
286
|
-
if (this.index > 1) {
|
|
287
|
-
list.push([i, j - 2])
|
|
276
|
+
// logger.debug("BrillPOSTrainer.prototype.scanForSites: exit");
|
|
288
277
|
}
|
|
289
|
-
if (this.index > 0) {
|
|
290
|
-
list.push([i, j - 1])
|
|
291
|
-
}
|
|
292
|
-
if (this.index < sentenceLength - 1) {
|
|
293
|
-
list.push([i, j + 1])
|
|
294
|
-
}
|
|
295
|
-
if (this.index < sentenceLength - 2) {
|
|
296
|
-
list.push([i, j + 2])
|
|
297
|
-
}
|
|
298
|
-
if (this.index > sentenceLength - 3) {
|
|
299
|
-
list.push([i, j + 3])
|
|
300
|
-
}
|
|
301
|
-
return list
|
|
302
|
-
}
|
|
303
278
|
|
|
304
|
-
//
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
this.mapRuleToSites = {}
|
|
312
|
-
this.mapSiteToRules = {}
|
|
313
|
-
|
|
314
|
-
// logger.debug("BrillPOSTrainer.prototype.train: entry");
|
|
315
|
-
this.corpus.tag(lexicon)
|
|
316
|
-
this.scanForPositiveRules()
|
|
317
|
-
// logger.info("BrillPOSTrainer.prototype.train: initial number of rules: " + this.positiveRules.nrRules());
|
|
318
|
-
this.scanForSites()
|
|
319
|
-
|
|
320
|
-
let highRule = this.selectHighRule()
|
|
321
|
-
let iterationNumber = 0
|
|
322
|
-
const that = this
|
|
323
|
-
while ((highRule !== null) && (highRule.score() > minScore)) {
|
|
324
|
-
if ((iterationNumber % 5) === 0) {
|
|
325
|
-
// logger.info("BrillPOSTrainer.prototype.train: training iteration: " + iterationNumber);
|
|
279
|
+
// Returns a list of sites that may have been touched by a changing tag
|
|
280
|
+
neighbourhood (i, j) {
|
|
281
|
+
const sentenceLength = this.corpus.sentences[i].length
|
|
282
|
+
const list = []
|
|
283
|
+
|
|
284
|
+
if (this.index > 2) {
|
|
285
|
+
list.push([i, j - 3])
|
|
326
286
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
287
|
+
if (this.index > 1) {
|
|
288
|
+
list.push([i, j - 2])
|
|
289
|
+
}
|
|
290
|
+
if (this.index > 0) {
|
|
291
|
+
list.push([i, j - 1])
|
|
292
|
+
}
|
|
293
|
+
if (this.index < sentenceLength - 1) {
|
|
294
|
+
list.push([i, j + 1])
|
|
295
|
+
}
|
|
296
|
+
if (this.index < sentenceLength - 2) {
|
|
297
|
+
list.push([i, j + 2])
|
|
298
|
+
}
|
|
299
|
+
if (this.index > sentenceLength - 3) {
|
|
300
|
+
list.push([i, j + 3])
|
|
301
|
+
}
|
|
302
|
+
return list
|
|
303
|
+
}
|
|
337
304
|
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
305
|
+
// corpus: an array of token arrays
|
|
306
|
+
// templates: an array of rule templates
|
|
307
|
+
// lexicon: lexicon that provides method tagWord(word)
|
|
308
|
+
train (corpus, templates, lexicon) {
|
|
309
|
+
this.corpus = corpus
|
|
310
|
+
this.templates = templates
|
|
311
|
+
this.positiveRules = new RuleSet()
|
|
312
|
+
this.mapRuleToSites = {}
|
|
313
|
+
this.mapSiteToRules = {}
|
|
314
|
+
|
|
315
|
+
// logger.debug("BrillPOSTrainer.prototype.train: entry");
|
|
316
|
+
this.corpus.tag(lexicon)
|
|
317
|
+
this.scanForPositiveRules()
|
|
318
|
+
// logger.info("BrillPOSTrainer.prototype.train: initial number of rules: " + this.positiveRules.nrRules());
|
|
319
|
+
this.scanForSites()
|
|
320
|
+
|
|
321
|
+
let highRule = this.selectHighRule()
|
|
322
|
+
let iterationNumber = 0
|
|
323
|
+
const that = this
|
|
324
|
+
while ((highRule !== null) && (highRule.score() > minScore)) {
|
|
325
|
+
if ((iterationNumber % 5) === 0) {
|
|
326
|
+
// logger.info("BrillPOSTrainer.prototype.train: training iteration: " + iterationNumber);
|
|
327
|
+
}
|
|
328
|
+
// logger.debug("BrillPOSTrainer.prototype.train: highRule selected: " + highRule.key());
|
|
329
|
+
// logger.debug("BrillPOSTrainer.prototype.train: number of rules: " + this.positiveRules.nrRules());
|
|
330
|
+
// logger.debug("BrillPOSTrainer.prototype.train: score of highRule: " + highRule.score());
|
|
331
|
+
|
|
332
|
+
// Apply the high rule to each change site on its site list
|
|
333
|
+
this.getSites(highRule).forEach(function (site) {
|
|
334
|
+
// logger.debug("BrillPOSTrainer.prototype.train: apply highRule to: " + site);
|
|
335
|
+
// logger.debug("BrillPOSTrainer.prototype.train: sentence length: " + that.corpus.sentences[site[0]].length);
|
|
336
|
+
highRule.applyAt(that.corpus.sentences[site[0]], site[1])
|
|
337
|
+
})
|
|
351
338
|
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
339
|
+
const unseenRules = new RuleSet()
|
|
340
|
+
this.getSites(highRule).forEach(function (site) {
|
|
341
|
+
that.neighbourhood(site[0], site[1]).forEach(function (testSite) {
|
|
342
|
+
// Generate positive rules for testSite
|
|
343
|
+
const newRules = that.generatePositiveRules(testSite[0], testSite[1])
|
|
344
|
+
|
|
345
|
+
// Disconnect test site from its rules
|
|
346
|
+
// because highrule has been applied
|
|
347
|
+
that.getRules(testSite[0], testSite[1]).forEach(function (rule) {
|
|
348
|
+
if (!newRules.hasRule(rule)) {
|
|
349
|
+
that.disconnectSiteFromRule(testSite[0], testSite[1], rule)
|
|
359
350
|
}
|
|
360
|
-
}
|
|
361
|
-
})
|
|
351
|
+
})
|
|
362
352
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
353
|
+
// Connect new rules not already connected to the test site
|
|
354
|
+
newRules.getRules().forEach(function (rule) {
|
|
355
|
+
if (!that.siteIsAssociatedWithRule(testSite[0].testSite[1], rule)) {
|
|
356
|
+
if (that.positiveRules.hasRule(rule)) {
|
|
357
|
+
that.associateSiteWithRule(testSite[0], testSite[1], rule)
|
|
358
|
+
} else {
|
|
359
|
+
unseenRules.addRule(rule)
|
|
360
|
+
}
|
|
361
|
+
}
|
|
367
362
|
})
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
363
|
+
|
|
364
|
+
// Process unseen rules
|
|
365
|
+
if (unseenRules.nrRules() > 0) {
|
|
366
|
+
unseenRules.getRules().forEach(function (rule) {
|
|
367
|
+
that.positiveRules.addRule(rule)
|
|
371
368
|
})
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
369
|
+
that.corpus.sentences.forEach(function (sentence, i) {
|
|
370
|
+
const taggedSentence = sentence.map(function (token) {
|
|
371
|
+
return [token.token, token.testTag]
|
|
372
|
+
})
|
|
373
|
+
sentence.forEach(function (token, j) {
|
|
374
|
+
unseenRules.getRules().forEach(function (rule) {
|
|
375
|
+
if (rule.isApplicableAt(sentence, taggedSentence, j)) {
|
|
376
|
+
that.associateSiteWithRule(i, j, rule)
|
|
377
|
+
that.scoreRule(rule, i, j)
|
|
378
|
+
}
|
|
379
|
+
})
|
|
378
380
|
})
|
|
379
381
|
})
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
+
}
|
|
383
|
+
})
|
|
382
384
|
})
|
|
383
|
-
})
|
|
384
|
-
|
|
385
|
-
// Select next highest scoring rule
|
|
386
|
-
highRule = this.selectHighRule()
|
|
387
|
-
iterationNumber++
|
|
388
|
-
}
|
|
389
|
-
// logger.info("BrillPOSTrainer.prototype.train: number of iterations: " + iterationNumber);
|
|
390
|
-
// logger.info("BrillPOSTrainer.prototype.train: number of rules: " + this.positiveRules.nrRules());
|
|
391
385
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
that.positiveRules.removeRule(rule)
|
|
386
|
+
// Select next highest scoring rule
|
|
387
|
+
highRule = this.selectHighRule()
|
|
388
|
+
iterationNumber++
|
|
396
389
|
}
|
|
397
|
-
|
|
390
|
+
// logger.info("BrillPOSTrainer.prototype.train: number of iterations: " + iterationNumber);
|
|
391
|
+
// logger.info("BrillPOSTrainer.prototype.train: number of rules: " + this.positiveRules.nrRules());
|
|
398
392
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
393
|
+
// Remove rules having a non-positive score
|
|
394
|
+
this.positiveRules.getRules().forEach(function (rule) {
|
|
395
|
+
if (rule.score() < that.ruleScoreThreshold) {
|
|
396
|
+
that.positiveRules.removeRule(rule)
|
|
397
|
+
}
|
|
398
|
+
})
|
|
399
|
+
|
|
400
|
+
// logger.info("BrillPOSTrainer.prototype.train: number of rules after pruning: " + this.positiveRules.nrRules());
|
|
401
|
+
// logger.debug("BrillPOSTrainer.prototype.train: exit");
|
|
402
|
+
return this.positiveRules
|
|
403
|
+
}
|
|
403
404
|
|
|
404
|
-
|
|
405
|
-
|
|
405
|
+
printRulesWithScores () {
|
|
406
|
+
let result = ''
|
|
406
407
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
} else {
|
|
411
|
-
if (a.score() < b.score()) {
|
|
412
|
-
return 1
|
|
408
|
+
function compareRules (a, b) {
|
|
409
|
+
if (a.score() > b.score()) {
|
|
410
|
+
return -1
|
|
413
411
|
} else {
|
|
414
|
-
|
|
412
|
+
if (a.score() < b.score()) {
|
|
413
|
+
return 1
|
|
414
|
+
} else {
|
|
415
|
+
return 0
|
|
416
|
+
}
|
|
415
417
|
}
|
|
416
418
|
}
|
|
417
|
-
}
|
|
418
419
|
|
|
419
|
-
|
|
420
|
-
|
|
420
|
+
const rules = this.positiveRules.getRules()
|
|
421
|
+
const sortedRules = rules.sort(compareRules)
|
|
421
422
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
423
|
+
sortedRules.forEach(function (rule) {
|
|
424
|
+
// if (rule.score() > 0) {
|
|
425
|
+
result += rule.score() + '\t' + rule.positive + '\t' + rule.negative + '\t' + rule.neutral + '\t' + rule.prettyPrint() + '\n'
|
|
426
|
+
// }
|
|
427
|
+
})
|
|
428
|
+
return result
|
|
429
|
+
}
|
|
428
430
|
}
|
|
429
431
|
|
|
430
432
|
module.exports = BrillPOSTrainer
|