nltk 3.2__zip → 3.3__zip
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.
- {nltk-3.2 → nltk-3.3}/INSTALL.txt +6 -6
- {nltk-3.2 → nltk-3.3}/LICENSE.txt +13 -13
- {nltk-3.2 → nltk-3.3}/MANIFEST.in +6 -6
- {nltk-3.2/nltk.egg-info → nltk-3.3}/PKG-INFO +13 -5
- nltk-3.2/README.txt → nltk-3.3/README.md +49 -48
- nltk-3.3/nltk/VERSION +1 -0
- {nltk-3.2 → nltk-3.3}/nltk/__init__.py +185 -185
- {nltk-3.2 → nltk-3.3}/nltk/app/__init__.py +52 -53
- {nltk-3.2 → nltk-3.3}/nltk/app/chartparser_app.py +2276 -2276
- {nltk-3.2 → nltk-3.3}/nltk/app/chunkparser_app.py +1262 -1264
- {nltk-3.2 → nltk-3.3}/nltk/app/collocations_app.py +347 -350
- {nltk-3.2 → nltk-3.3}/nltk/app/concordance_app.py +568 -570
- {nltk-3.2 → nltk-3.3}/nltk/app/nemo_app.py +156 -155
- {nltk-3.2 → nltk-3.3}/nltk/app/rdparser_app.py +892 -894
- {nltk-3.2 → nltk-3.3}/nltk/app/srparser_app.py +809 -809
- {nltk-3.2 → nltk-3.3}/nltk/app/wordfreq_app.py +32 -32
- {nltk-3.2 → nltk-3.3}/nltk/app/wordnet_app.py +968 -968
- {nltk-3.2 → nltk-3.3}/nltk/book.py +91 -90
- {nltk-3.2 → nltk-3.3}/nltk/ccg/__init__.py +22 -22
- {nltk-3.2 → nltk-3.3}/nltk/ccg/api.py +359 -335
- {nltk-3.2 → nltk-3.3}/nltk/ccg/chart.py +394 -361
- {nltk-3.2 → nltk-3.3}/nltk/ccg/combinator.py +343 -312
- {nltk-3.2 → nltk-3.3}/nltk/ccg/lexicon.py +328 -290
- nltk-3.3/nltk/ccg/logic.py +46 -0
- {nltk-3.2 → nltk-3.3}/nltk/chat/__init__.py +49 -49
- {nltk-3.2 → nltk-3.3}/nltk/chat/eliza.py +244 -244
- {nltk-3.2 → nltk-3.3}/nltk/chat/iesha.py +140 -140
- {nltk-3.2 → nltk-3.3}/nltk/chat/rude.py +92 -92
- {nltk-3.2 → nltk-3.3}/nltk/chat/suntsu.py +117 -117
- {nltk-3.2 → nltk-3.3}/nltk/chat/util.py +122 -120
- {nltk-3.2 → nltk-3.3}/nltk/chat/zen.py +282 -282
- {nltk-3.2 → nltk-3.3}/nltk/chunk/__init__.py +190 -190
- {nltk-3.2 → nltk-3.3}/nltk/chunk/api.py +51 -51
- {nltk-3.2 → nltk-3.3}/nltk/chunk/named_entity.py +331 -331
- {nltk-3.2 → nltk-3.3}/nltk/chunk/regexp.py +1390 -1384
- {nltk-3.2 → nltk-3.3}/nltk/chunk/util.py +600 -600
- {nltk-3.2 → nltk-3.3}/nltk/classify/__init__.py +98 -98
- {nltk-3.2 → nltk-3.3}/nltk/classify/api.py +193 -193
- {nltk-3.2 → nltk-3.3}/nltk/classify/decisiontree.py +295 -295
- {nltk-3.2 → nltk-3.3}/nltk/classify/maxent.py +1510 -1492
- {nltk-3.2 → nltk-3.3}/nltk/classify/megam.py +179 -178
- {nltk-3.2 → nltk-3.3}/nltk/classify/naivebayes.py +245 -242
- {nltk-3.2 → nltk-3.3}/nltk/classify/positivenaivebayes.py +170 -170
- {nltk-3.2 → nltk-3.3}/nltk/classify/rte_classify.py +160 -191
- {nltk-3.2 → nltk-3.3}/nltk/classify/scikitlearn.py +153 -151
- {nltk-3.2 → nltk-3.3}/nltk/classify/senna.py +183 -181
- {nltk-3.2 → nltk-3.3}/nltk/classify/svm.py +15 -15
- {nltk-3.2 → nltk-3.3}/nltk/classify/tadm.py +113 -112
- {nltk-3.2 → nltk-3.3}/nltk/classify/textcat.py +193 -193
- {nltk-3.2 → nltk-3.3}/nltk/classify/util.py +324 -312
- {nltk-3.2 → nltk-3.3}/nltk/classify/weka.py +346 -345
- {nltk-3.2 → nltk-3.3}/nltk/cluster/__init__.py +86 -86
- {nltk-3.2 → nltk-3.3}/nltk/cluster/api.py +74 -70
- {nltk-3.2 → nltk-3.3}/nltk/cluster/em.py +247 -247
- {nltk-3.2 → nltk-3.3}/nltk/cluster/gaac.py +168 -168
- {nltk-3.2 → nltk-3.3}/nltk/cluster/kmeans.py +221 -221
- {nltk-3.2 → nltk-3.3}/nltk/cluster/util.py +304 -289
- nltk-3.3/nltk/collections.py +640 -0
- {nltk-3.2 → nltk-3.3}/nltk/collocations.py +388 -388
- nltk-3.3/nltk/compat.py +365 -0
- {nltk-3.2 → nltk-3.3}/nltk/corpus/__init__.py +341 -320
- {nltk-3.2 → nltk-3.3}/nltk/corpus/europarl_raw.py +44 -44
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/__init__.py +147 -145
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/aligned.py +115 -114
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/api.py +448 -446
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/bnc.py +252 -252
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/bracket_parse.py +227 -227
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/categorized_sents.py +179 -178
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/chasen.py +140 -139
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/childes.py +522 -494
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/chunked.py +212 -212
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/cmudict.py +97 -95
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/comparative_sents.py +280 -278
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/conll.py +528 -521
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/crubadan.py +116 -116
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/dependency.py +101 -101
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/framenet.py +3059 -2093
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/ieer.py +112 -111
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/indian.py +85 -86
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/ipipan.py +332 -331
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/knbc.py +187 -187
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/lin.py +156 -156
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/mte.py +311 -310
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/nkjp.py +429 -428
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/nombank.py +421 -421
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/nps_chat.py +73 -73
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/opinion_lexicon.py +115 -115
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/panlex_lite.py +165 -165
- nltk-3.3/nltk/corpus/reader/pl196x.py +292 -0
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/plaintext.py +232 -233
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/ppattach.py +96 -95
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/propbank.py +479 -481
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/pros_cons.py +128 -126
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/reviews.py +330 -328
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/rte.py +144 -146
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/semcor.py +256 -256
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/senseval.py +203 -201
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/sentiwordnet.py +137 -137
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/sinica_treebank.py +75 -75
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/string_category.py +60 -59
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/switchboard.py +119 -119
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/tagged.py +295 -294
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/timit.py +452 -450
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/toolbox.py +68 -68
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/twitter.py +155 -157
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/udhr.py +80 -80
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/util.py +804 -800
- nltk-3.3/nltk/corpus/reader/verbnet.py +595 -0
- nltk-3.3/nltk/corpus/reader/wordlist.py +134 -0
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/wordnet.py +2108 -2036
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/xmldocs.py +392 -387
- {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/ycoe.py +243 -242
- {nltk-3.2 → nltk-3.3}/nltk/corpus/util.py +144 -127
- {nltk-3.2 → nltk-3.3}/nltk/data.py +1487 -1451
- {nltk-3.2 → nltk-3.3}/nltk/decorators.py +219 -215
- {nltk-3.2 → nltk-3.3}/nltk/downloader.py +2278 -2274
- {nltk-3.2 → nltk-3.3}/nltk/draw/__init__.py +27 -27
- {nltk-3.2 → nltk-3.3}/nltk/draw/cfg.py +774 -777
- {nltk-3.2 → nltk-3.3}/nltk/draw/dispersion.py +58 -58
- {nltk-3.2 → nltk-3.3}/nltk/draw/table.py +1098 -1098
- {nltk-3.2 → nltk-3.3}/nltk/draw/tree.py +963 -966
- {nltk-3.2 → nltk-3.3}/nltk/draw/util.py +2356 -2356
- {nltk-3.2 → nltk-3.3}/nltk/featstruct.py +2617 -2500
- {nltk-3.2 → nltk-3.3}/nltk/grammar.py +1559 -1516
- {nltk-3.2 → nltk-3.3}/nltk/help.py +56 -56
- {nltk-3.2 → nltk-3.3}/nltk/inference/__init__.py +20 -20
- {nltk-3.2 → nltk-3.3}/nltk/inference/api.py +597 -589
- {nltk-3.2 → nltk-3.3}/nltk/inference/discourse.py +612 -608
- {nltk-3.2 → nltk-3.3}/nltk/inference/mace.py +311 -311
- {nltk-3.2 → nltk-3.3}/nltk/inference/nonmonotonic.py +509 -509
- {nltk-3.2 → nltk-3.3}/nltk/inference/prover9.py +431 -431
- {nltk-3.2 → nltk-3.3}/nltk/inference/resolution.py +687 -687
- {nltk-3.2 → nltk-3.3}/nltk/inference/tableau.py +607 -607
- {nltk-3.2 → nltk-3.3}/nltk/internals.py +982 -982
- {nltk-3.2 → nltk-3.3}/nltk/jsontags.py +63 -63
- {nltk-3.2 → nltk-3.3}/nltk/lazyimport.py +142 -142
- {nltk-3.2 → nltk-3.3}/nltk/metrics/__init__.py +30 -29
- {nltk-3.2 → nltk-3.3}/nltk/metrics/agreement.py +421 -401
- nltk-3.3/nltk/metrics/aline.py +607 -0
- {nltk-3.2 → nltk-3.3}/nltk/metrics/association.py +414 -410
- {nltk-3.2 → nltk-3.3}/nltk/metrics/confusionmatrix.py +206 -206
- {nltk-3.2 → nltk-3.3}/nltk/metrics/distance.py +207 -202
- {nltk-3.2 → nltk-3.3}/nltk/metrics/paice.py +381 -381
- {nltk-3.2 → nltk-3.3}/nltk/metrics/scores.py +228 -228
- {nltk-3.2 → nltk-3.3}/nltk/metrics/segmentation.py +231 -232
- {nltk-3.2 → nltk-3.3}/nltk/metrics/spearman.py +69 -69
- {nltk-3.2 → nltk-3.3}/nltk/misc/__init__.py +11 -11
- {nltk-3.2 → nltk-3.3}/nltk/misc/babelfish.py +10 -10
- {nltk-3.2 → nltk-3.3}/nltk/misc/chomsky.py +135 -133
- {nltk-3.2 → nltk-3.3}/nltk/misc/minimalset.py +83 -83
- {nltk-3.2 → nltk-3.3}/nltk/misc/sort.py +157 -157
- {nltk-3.2 → nltk-3.3}/nltk/misc/wordfinder.py +129 -129
- {nltk-3.2 → nltk-3.3}/nltk/parse/__init__.py +81 -80
- {nltk-3.2 → nltk-3.3}/nltk/parse/api.py +66 -66
- {nltk-3.2 → nltk-3.3}/nltk/parse/bllip.py +282 -282
- {nltk-3.2 → nltk-3.3}/nltk/parse/chart.py +1682 -1681
- nltk-3.3/nltk/parse/corenlp.py +780 -0
- {nltk-3.2 → nltk-3.3}/nltk/parse/dependencygraph.py +760 -758
- {nltk-3.2 → nltk-3.3}/nltk/parse/earleychart.py +452 -451
- {nltk-3.2 → nltk-3.3}/nltk/parse/evaluate.py +129 -129
- {nltk-3.2 → nltk-3.3}/nltk/parse/featurechart.py +580 -578
- {nltk-3.2 → nltk-3.3}/nltk/parse/generate.py +87 -76
- {nltk-3.2 → nltk-3.3}/nltk/parse/malt.py +356 -356
- {nltk-3.2 → nltk-3.3}/nltk/parse/nonprojectivedependencyparser.py +783 -783
- {nltk-3.2 → nltk-3.3}/nltk/parse/pchart.py +522 -522
- {nltk-3.2 → nltk-3.3}/nltk/parse/projectivedependencyparser.py +579 -578
- {nltk-3.2 → nltk-3.3}/nltk/parse/recursivedescent.py +655 -656
- {nltk-3.2 → nltk-3.3}/nltk/parse/shiftreduce.py +458 -459
- {nltk-3.2 → nltk-3.3}/nltk/parse/stanford.py +431 -406
- {nltk-3.2 → nltk-3.3}/nltk/parse/transitionparser.py +774 -772
- {nltk-3.2 → nltk-3.3}/nltk/parse/util.py +231 -231
- {nltk-3.2 → nltk-3.3}/nltk/parse/viterbi.py +401 -401
- {nltk-3.2 → nltk-3.3}/nltk/probability.py +2410 -2355
- {nltk-3.2 → nltk-3.3}/nltk/sem/__init__.py +61 -61
- {nltk-3.2 → nltk-3.3}/nltk/sem/boxer.py +1261 -1261
- {nltk-3.2 → nltk-3.3}/nltk/sem/chat80.py +782 -782
- {nltk-3.2 → nltk-3.3}/nltk/sem/cooper_storage.py +118 -118
- {nltk-3.2 → nltk-3.3}/nltk/sem/drt.py +1258 -1257
- {nltk-3.2 → nltk-3.3}/nltk/sem/drt_glue_demo.py +485 -489
- {nltk-3.2 → nltk-3.3}/nltk/sem/evaluate.py +790 -784
- {nltk-3.2 → nltk-3.3}/nltk/sem/glue.py +696 -694
- {nltk-3.2 → nltk-3.3}/nltk/sem/hole.py +386 -384
- {nltk-3.2 → nltk-3.3}/nltk/sem/lfg.py +210 -208
- {nltk-3.2 → nltk-3.3}/nltk/sem/linearlogic.py +451 -449
- {nltk-3.2 → nltk-3.3}/nltk/sem/logic.py +1910 -1910
- {nltk-3.2 → nltk-3.3}/nltk/sem/relextract.py +476 -483
- {nltk-3.2 → nltk-3.3}/nltk/sem/skolemize.py +101 -101
- {nltk-3.2 → nltk-3.3}/nltk/sem/util.py +249 -249
- {nltk-3.2 → nltk-3.3}/nltk/sentiment/__init__.py +14 -14
- {nltk-3.2 → nltk-3.3}/nltk/sentiment/sentiment_analyzer.py +229 -229
- {nltk-3.2 → nltk-3.3}/nltk/sentiment/util.py +762 -752
- {nltk-3.2 → nltk-3.3}/nltk/sentiment/vader.py +443 -445
- {nltk-3.2 → nltk-3.3}/nltk/stem/__init__.py +31 -33
- {nltk-3.2 → nltk-3.3}/nltk/stem/api.py +28 -25
- nltk-3.3/nltk/stem/arlstem.py +369 -0
- {nltk-3.2 → nltk-3.3}/nltk/stem/isri.py +345 -345
- {nltk-3.2 → nltk-3.3}/nltk/stem/lancaster.py +337 -310
- nltk-3.3/nltk/stem/porter.py +710 -0
- {nltk-3.2 → nltk-3.3}/nltk/stem/regexp.py +61 -61
- {nltk-3.2 → nltk-3.3}/nltk/stem/rslp.py +142 -142
- {nltk-3.2 → nltk-3.3}/nltk/stem/snowball.py +4236 -3712
- {nltk-3.2 → nltk-3.3}/nltk/stem/util.py +22 -12
- {nltk-3.2 → nltk-3.3}/nltk/stem/wordnet.py +51 -51
- {nltk-3.2 → nltk-3.3}/nltk/tag/__init__.py +152 -127
- {nltk-3.2 → nltk-3.3}/nltk/tag/api.py +86 -81
- {nltk-3.2 → nltk-3.3}/nltk/tag/brill.py +424 -426
- {nltk-3.2 → nltk-3.3}/nltk/tag/brill_trainer.py +608 -608
- {nltk-3.2 → nltk-3.3}/nltk/tag/crf.py +204 -200
- {nltk-3.2 → nltk-3.3}/nltk/tag/hmm.py +1277 -1277
- {nltk-3.2 → nltk-3.3}/nltk/tag/hunpos.py +132 -132
- {nltk-3.2 → nltk-3.3}/nltk/tag/mapping.py +101 -101
- {nltk-3.2 → nltk-3.3}/nltk/tag/perceptron.py +329 -314
- {nltk-3.2 → nltk-3.3}/nltk/tag/senna.py +147 -145
- {nltk-3.2 → nltk-3.3}/nltk/tag/sequential.py +740 -736
- {nltk-3.2 → nltk-3.3}/nltk/tag/stanford.py +219 -196
- {nltk-3.2 → nltk-3.3}/nltk/tag/tnt.py +595 -604
- {nltk-3.2 → nltk-3.3}/nltk/tag/util.py +72 -72
- {nltk-3.2 → nltk-3.3}/nltk/tbl/__init__.py +28 -28
- {nltk-3.2 → nltk-3.3}/nltk/tbl/api.py +1 -1
- {nltk-3.2 → nltk-3.3}/nltk/tbl/demo.py +366 -366
- {nltk-3.2 → nltk-3.3}/nltk/tbl/erroranalysis.py +39 -39
- {nltk-3.2 → nltk-3.3}/nltk/tbl/feature.py +259 -261
- {nltk-3.2 → nltk-3.3}/nltk/tbl/rule.py +321 -321
- {nltk-3.2 → nltk-3.3}/nltk/tbl/template.py +303 -308
- {nltk-3.2 → nltk-3.3}/nltk/test/__init__.py +18 -18
- {nltk-3.2 → nltk-3.3}/nltk/test/all.py +24 -23
- {nltk-3.2 → nltk-3.3}/nltk/test/bleu.doctest +14 -14
- {nltk-3.2 → nltk-3.3}/nltk/test/bnc.doctest +60 -60
- {nltk-3.2 → nltk-3.3}/nltk/test/ccg.doctest +376 -395
- nltk-3.3/nltk/test/ccg_semantics.doctest +553 -0
- {nltk-3.2 → nltk-3.3}/nltk/test/chat80.doctest +234 -234
- {nltk-3.2 → nltk-3.3}/nltk/test/childes.doctest +184 -184
- {nltk-3.2 → nltk-3.3}/nltk/test/childes_fixt.py +14 -13
- {nltk-3.2 → nltk-3.3}/nltk/test/chunk.doctest +373 -373
- {nltk-3.2 → nltk-3.3}/nltk/test/classify.doctest +185 -185
- {nltk-3.2 → nltk-3.3}/nltk/test/classify_fixt.py +11 -10
- nltk-3.3/nltk/test/collections.doctest +20 -0
- {nltk-3.2 → nltk-3.3}/nltk/test/collocations.doctest +276 -276
- {nltk-3.2 → nltk-3.3}/nltk/test/compat.doctest +134 -138
- {nltk-3.2 → nltk-3.3}/nltk/test/compat_fixt.py +9 -8
- nltk-3.3/nltk/test/concordance.doctest +55 -0
- {nltk-3.2 → nltk-3.3}/nltk/test/corpus.doctest +2200 -2212
- {nltk-3.2 → nltk-3.3}/nltk/test/corpus_fixt.py +3 -3
- {nltk-3.2 → nltk-3.3}/nltk/test/crubadan.doctest +65 -65
- {nltk-3.2 → nltk-3.3}/nltk/test/data.doctest +379 -379
- {nltk-3.2 → nltk-3.3}/nltk/test/dependency.doctest +241 -241
- {nltk-3.2 → nltk-3.3}/nltk/test/discourse.doctest +546 -546
- {nltk-3.2 → nltk-3.3}/nltk/test/discourse_fixt.py +14 -13
- {nltk-3.2 → nltk-3.3}/nltk/test/doctest_nose_plugin.py +159 -156
- {nltk-3.2 → nltk-3.3}/nltk/test/drt.doctest +517 -517
- {nltk-3.2 → nltk-3.3}/nltk/test/featgram.doctest +607 -607
- {nltk-3.2 → nltk-3.3}/nltk/test/featstruct.doctest +1229 -1230
- {nltk-3.2 → nltk-3.3}/nltk/test/framenet.doctest +288 -239
- {nltk-3.2 → nltk-3.3}/nltk/test/generate.doctest +67 -67
- {nltk-3.2 → nltk-3.3}/nltk/test/gensim.doctest +140 -141
- {nltk-3.2 → nltk-3.3}/nltk/test/gensim_fixt.py +10 -9
- {nltk-3.2 → nltk-3.3}/nltk/test/gluesemantics.doctest +384 -386
- {nltk-3.2 → nltk-3.3}/nltk/test/gluesemantics_malt.doctest +68 -68
- {nltk-3.2 → nltk-3.3}/nltk/test/gluesemantics_malt_fixt.py +12 -11
- {nltk-3.2 → nltk-3.3}/nltk/test/grammar.doctest +48 -48
- {nltk-3.2 → nltk-3.3}/nltk/test/grammartestsuites.doctest +109 -109
- {nltk-3.2 → nltk-3.3}/nltk/test/index.doctest +100 -100
- {nltk-3.2 → nltk-3.3}/nltk/test/inference.doctest +534 -534
- {nltk-3.2 → nltk-3.3}/nltk/test/inference_fixt.py +12 -11
- {nltk-3.2 → nltk-3.3}/nltk/test/internals.doctest +140 -140
- {nltk-3.2 → nltk-3.3}/nltk/test/japanese.doctest +48 -48
- {nltk-3.2 → nltk-3.3}/nltk/test/logic.doctest +1098 -1098
- {nltk-3.2 → nltk-3.3}/nltk/test/metrics.doctest +270 -270
- {nltk-3.2 → nltk-3.3}/nltk/test/misc.doctest +118 -118
- {nltk-3.2 → nltk-3.3}/nltk/test/nonmonotonic.doctest +286 -286
- {nltk-3.2 → nltk-3.3}/nltk/test/nonmonotonic_fixt.py +12 -11
- {nltk-3.2 → nltk-3.3}/nltk/test/paice.doctest +35 -35
- {nltk-3.2 → nltk-3.3}/nltk/test/parse.doctest +884 -884
- {nltk-3.2 → nltk-3.3}/nltk/test/portuguese_en.doctest +565 -565
- {nltk-3.2 → nltk-3.3}/nltk/test/portuguese_en_fixt.py +16 -16
- {nltk-3.2 → nltk-3.3}/nltk/test/probability.doctest +304 -304
- {nltk-3.2 → nltk-3.3}/nltk/test/probability_fixt.py +13 -12
- {nltk-3.2 → nltk-3.3}/nltk/test/propbank.doctest +176 -176
- {nltk-3.2 → nltk-3.3}/nltk/test/relextract.doctest +263 -263
- {nltk-3.2 → nltk-3.3}/nltk/test/resolution.doctest +221 -221
- {nltk-3.2 → nltk-3.3}/nltk/test/runtests.py +86 -64
- {nltk-3.2 → nltk-3.3}/nltk/test/segmentation_fixt.py +10 -10
- {nltk-3.2 → nltk-3.3}/nltk/test/semantics.doctest +665 -665
- {nltk-3.2 → nltk-3.3}/nltk/test/semantics_fixt.py +7 -7
- {nltk-3.2 → nltk-3.3}/nltk/test/sentiment.doctest +233 -233
- {nltk-3.2 → nltk-3.3}/nltk/test/sentiwordnet.doctest +41 -41
- {nltk-3.2 → nltk-3.3}/nltk/test/simple.doctest +85 -85
- {nltk-3.2 → nltk-3.3}/nltk/test/stem.doctest +78 -78
- {nltk-3.2 → nltk-3.3}/nltk/test/tag.doctest +33 -33
- {nltk-3.2 → nltk-3.3}/nltk/test/tokenize.doctest +273 -166
- {nltk-3.2 → nltk-3.3}/nltk/test/toolbox.doctest +307 -307
- {nltk-3.2 → nltk-3.3}/nltk/test/translate.doctest +242 -242
- {nltk-3.2 → nltk-3.3}/nltk/test/translate_fixt.py +3 -3
- {nltk-3.2 → nltk-3.3}/nltk/test/tree.doctest +1101 -1101
- {nltk-3.2 → nltk-3.3}/nltk/test/treeprettyprinter.doctest +127 -127
- {nltk-3.2 → nltk-3.3}/nltk/test/treetransforms.doctest +156 -156
- nltk-3.3/nltk/test/unit/test_2x_compat.py +63 -0
- nltk-3.3/nltk/test/unit/test_aline.py +52 -0
- nltk-3.3/nltk/test/unit/test_chunk.py +56 -0
- {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_classify.py +55 -53
- {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_collocations.py +88 -79
- nltk-3.3/nltk/test/unit/test_concordance.py +98 -0
- nltk-3.3/nltk/test/unit/test_corenlp.py +1000 -0
- {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_corpora.py +200 -183
- {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_corpus_views.py +45 -45
- {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_hmm.py +88 -88
- {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_json2csv_corpus.py +188 -187
- {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_naivebayes.py +26 -26
- nltk-3.3/nltk/test/unit/test_rte_classify.py +86 -0
- {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_seekable_unicode_stream_reader.py +124 -124
- nltk-3.3/nltk/test/unit/test_senna.py +74 -0
- nltk-3.3/nltk/test/unit/test_stem.py +121 -0
- {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_tag.py +20 -20
- {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_tgrep.py +628 -626
- nltk-3.3/nltk/test/unit/test_tokenize.py +168 -0
- {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_twitter_auth.py +185 -185
- nltk-3.3/nltk/test/unit/test_wordnet.py +134 -0
- nltk-3.3/nltk/test/unit/translate/test_bleu.py +244 -0
- nltk-3.3/nltk/test/unit/translate/test_gdfa.py +57 -0
- {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm1.py +75 -75
- {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm2.py +87 -87
- {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm3.py +105 -105
- {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm4.py +126 -126
- {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm5.py +166 -166
- {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm_model.py +270 -270
- nltk-3.3/nltk/test/unit/translate/test_nist.py +37 -0
- {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_stack_decoder.py +300 -300
- {nltk-3.2 → nltk-3.3}/nltk/test/unit/utils.py +41 -41
- {nltk-3.2 → nltk-3.3}/nltk/test/util.doctest +48 -48
- {nltk-3.2 → nltk-3.3}/nltk/test/wordnet.doctest +595 -581
- {nltk-3.2 → nltk-3.3}/nltk/test/wordnet_fixt.py +7 -6
- {nltk-3.2 → nltk-3.3}/nltk/test/wordnet_lch.doctest +53 -53
- {nltk-3.2 → nltk-3.3}/nltk/test/wsd.doctest +68 -68
- {nltk-3.2 → nltk-3.3}/nltk/text.py +663 -610
- {nltk-3.2 → nltk-3.3}/nltk/tgrep.py +938 -937
- {nltk-3.2 → nltk-3.3}/nltk/tokenize/__init__.py +130 -107
- {nltk-3.2 → nltk-3.3}/nltk/tokenize/api.py +77 -78
- {nltk-3.2 → nltk-3.3}/nltk/tokenize/casual.py +343 -342
- {nltk-3.2 → nltk-3.3}/nltk/tokenize/mwe.py +120 -120
- nltk-3.3/nltk/tokenize/nist.py +173 -0
- {nltk-3.2 → nltk-3.3}/nltk/tokenize/punkt.py +1620 -1601
- {nltk-3.2 → nltk-3.3}/nltk/tokenize/regexp.py +208 -202
- nltk-3.3/nltk/tokenize/repp.py +151 -0
- {nltk-3.2 → nltk-3.3}/nltk/tokenize/sexpr.py +140 -142
- {nltk-3.2 → nltk-3.3}/nltk/tokenize/simple.py +138 -136
- {nltk-3.2 → nltk-3.3}/nltk/tokenize/stanford.py +117 -113
- nltk-3.3/nltk/tokenize/stanford_segmenter.py +246 -0
- {nltk-3.2 → nltk-3.3}/nltk/tokenize/texttiling.py +458 -460
- nltk-3.3/nltk/tokenize/toktok.py +155 -0
- nltk-3.3/nltk/tokenize/treebank.py +363 -0
- nltk-3.3/nltk/tokenize/util.py +261 -0
- {nltk-3.2 → nltk-3.3}/nltk/toolbox.py +499 -497
- {nltk-3.2 → nltk-3.3}/nltk/translate/__init__.py +24 -24
- {nltk-3.2 → nltk-3.3}/nltk/translate/api.py +331 -321
- {nltk-3.2 → nltk-3.3}/nltk/translate/bleu_score.py +615 -556
- nltk-3.3/nltk/translate/chrf_score.py +137 -0
- {nltk-3.2 → nltk-3.3}/nltk/translate/gale_church.py +248 -235
- {nltk-3.2 → nltk-3.3}/nltk/translate/gdfa.py +135 -131
- nltk-3.3/nltk/translate/gleu_score.py +193 -0
- {nltk-3.2 → nltk-3.3}/nltk/translate/ibm1.py +250 -241
- {nltk-3.2 → nltk-3.3}/nltk/translate/ibm2.py +308 -308
- {nltk-3.2 → nltk-3.3}/nltk/translate/ibm3.py +337 -337
- {nltk-3.2 → nltk-3.3}/nltk/translate/ibm4.py +467 -467
- {nltk-3.2 → nltk-3.3}/nltk/translate/ibm5.py +639 -639
- {nltk-3.2 → nltk-3.3}/nltk/translate/ibm_model.py +536 -533
- {nltk-3.2 → nltk-3.3}/nltk/translate/metrics.py +40 -40
- nltk-3.3/nltk/translate/nist_score.py +178 -0
- {nltk-3.2 → nltk-3.3}/nltk/translate/phrase_based.py +175 -175
- {nltk-3.2 → nltk-3.3}/nltk/translate/ribes_score.py +325 -325
- {nltk-3.2 → nltk-3.3}/nltk/translate/stack_decoder.py +499 -495
- {nltk-3.2 → nltk-3.3}/nltk/tree.py +1605 -1603
- {nltk-3.2 → nltk-3.3}/nltk/treeprettyprinter.py +564 -564
- {nltk-3.2 → nltk-3.3}/nltk/treetransforms.py +309 -309
- {nltk-3.2 → nltk-3.3}/nltk/twitter/__init__.py +28 -28
- {nltk-3.2 → nltk-3.3}/nltk/twitter/api.py +141 -136
- {nltk-3.2 → nltk-3.3}/nltk/twitter/common.py +264 -258
- {nltk-3.2 → nltk-3.3}/nltk/twitter/twitter_demo.py +288 -288
- {nltk-3.2 → nltk-3.3}/nltk/twitter/twitterclient.py +530 -530
- {nltk-3.2 → nltk-3.3}/nltk/twitter/util.py +147 -146
- nltk-3.3/nltk/util.py +751 -0
- {nltk-3.2 → nltk-3.3}/nltk/wsd.py +53 -53
- {nltk-3.2 → nltk-3.3/nltk.egg-info}/PKG-INFO +13 -5
- {nltk-3.2 → nltk-3.3}/nltk.egg-info/SOURCES.txt +26 -3
- nltk-3.3/nltk.egg-info/requires.txt +31 -0
- {nltk-3.2 → nltk-3.3}/setup.cfg +0 -1
- {nltk-3.2 → nltk-3.3}/setup.py +102 -75
- nltk-3.2/nltk/VERSION +0 -1
- nltk-3.2/nltk/compat.py +0 -680
- nltk-3.2/nltk/corpus/reader/pl196x.py +0 -278
- nltk-3.2/nltk/corpus/reader/verbnet.py +0 -408
- nltk-3.2/nltk/corpus/reader/wordlist.py +0 -37
- nltk-3.2/nltk/sentiment/vader_lexicon.txt +0 -7517
- nltk-3.2/nltk/six.py +0 -868
- nltk-3.2/nltk/stem/porter.py +0 -692
- nltk-3.2/nltk/test/unit/test_2x_compat.py +0 -26
- nltk-3.2/nltk/test/unit/test_stem.py +0 -36
- nltk-3.2/nltk/test/unit/test_tokenize.py +0 -23
- nltk-3.2/nltk/test/unit/translate/test_bleu.py +0 -84
- nltk-3.2/nltk/tokenize/treebank.py +0 -124
- nltk-3.2/nltk/tokenize/util.py +0 -90
- nltk-3.2/nltk/util.py +0 -1364
- {nltk-3.2 → nltk-3.3}/nltk/test/unit/__init__.py +0 -0
- {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/__init__.py +0 -0
- {nltk-3.2 → nltk-3.3}/nltk.egg-info/dependency_links.txt +0 -0
- {nltk-3.2 → nltk-3.3}/nltk.egg-info/not-zip-safe +0 -0
- {nltk-3.2 → nltk-3.3}/nltk.egg-info/top_level.txt +0 -0
|
@@ -1,1492 +1,1510 @@
|
|
|
1
|
-
# Natural Language Toolkit: Maximum Entropy Classifiers
|
|
2
|
-
#
|
|
3
|
-
# Copyright (C) 2001-
|
|
4
|
-
# Author: Edward Loper <edloper@gmail.com>
|
|
5
|
-
# Dmitry Chichkov <dchichkov@gmail.com> (TypedMaxentFeatureEncoding)
|
|
6
|
-
# URL: <http://nltk.org/>
|
|
7
|
-
# For license information, see LICENSE.TXT
|
|
8
|
-
|
|
9
|
-
"""
|
|
10
|
-
A classifier model based on maximum entropy modeling framework. This
|
|
11
|
-
framework considers all of the probability distributions that are
|
|
12
|
-
empirically consistent with the training data; and chooses the
|
|
13
|
-
distribution with the highest entropy. A probability distribution is
|
|
14
|
-
"empirically consistent" with a set of training data if its estimated
|
|
15
|
-
frequency with which a class and a feature vector value co-occur is
|
|
16
|
-
equal to the actual frequency in the data.
|
|
17
|
-
|
|
18
|
-
Terminology: 'feature'
|
|
19
|
-
======================
|
|
20
|
-
The term *feature* is usually used to refer to some property of an
|
|
21
|
-
unlabeled token. For example, when performing word sense
|
|
22
|
-
disambiguation, we might define a ``'prevword'`` feature whose value is
|
|
23
|
-
the word preceding the target word. However, in the context of
|
|
24
|
-
maxent modeling, the term *feature* is typically used to refer to a
|
|
25
|
-
property of a "labeled" token. In order to prevent confusion, we
|
|
26
|
-
will introduce two distinct terms to disambiguate these two different
|
|
27
|
-
concepts:
|
|
28
|
-
|
|
29
|
-
- An "input-feature" is a property of an unlabeled token.
|
|
30
|
-
- A "joint-feature" is a property of a labeled token.
|
|
31
|
-
|
|
32
|
-
In the rest of the ``nltk.classify`` module, the term "features" is
|
|
33
|
-
used to refer to what we will call "input-features" in this module.
|
|
34
|
-
|
|
35
|
-
In literature that describes and discusses maximum entropy models,
|
|
36
|
-
input-features are typically called "contexts", and joint-features
|
|
37
|
-
are simply referred to as "features".
|
|
38
|
-
|
|
39
|
-
Converting Input-Features to Joint-Features
|
|
40
|
-
-------------------------------------------
|
|
41
|
-
In maximum entropy models, joint-features are required to have numeric
|
|
42
|
-
values. Typically, each input-feature ``input_feat`` is mapped to a
|
|
43
|
-
set of joint-features of the form:
|
|
44
|
-
|
|
45
|
-
| joint_feat(token, label) = { 1 if input_feat(token) == feat_val
|
|
46
|
-
| { and label == some_label
|
|
47
|
-
| {
|
|
48
|
-
| { 0 otherwise
|
|
49
|
-
|
|
50
|
-
For all values of ``feat_val`` and ``some_label``. This mapping is
|
|
51
|
-
performed by classes that implement the ``MaxentFeatureEncodingI``
|
|
52
|
-
interface.
|
|
53
|
-
"""
|
|
54
|
-
from __future__ import print_function, unicode_literals
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
import
|
|
63
|
-
import
|
|
64
|
-
from collections import defaultdict
|
|
65
|
-
|
|
66
|
-
from
|
|
67
|
-
|
|
68
|
-
from nltk
|
|
69
|
-
from nltk.
|
|
70
|
-
|
|
71
|
-
from nltk.
|
|
72
|
-
|
|
73
|
-
from nltk.classify.
|
|
74
|
-
|
|
75
|
-
from nltk.classify.
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
the
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
:
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
"""
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
"""
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
:
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
:
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
:
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
|
457
|
-
| {
|
|
458
|
-
| { 0 otherwise
|
|
459
|
-
|
|
460
|
-
Where ``
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
the
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
"""
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
self.
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
features
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
"""
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
#
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
#
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
"""
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
``
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
:
|
|
1271
|
-
:
|
|
1272
|
-
:
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
#
|
|
1301
|
-
#
|
|
1302
|
-
#
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
#
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
options += ['-
|
|
1403
|
-
if
|
|
1404
|
-
options += ['-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1
|
+
# Natural Language Toolkit: Maximum Entropy Classifiers
|
|
2
|
+
#
|
|
3
|
+
# Copyright (C) 2001-2018 NLTK Project
|
|
4
|
+
# Author: Edward Loper <edloper@gmail.com>
|
|
5
|
+
# Dmitry Chichkov <dchichkov@gmail.com> (TypedMaxentFeatureEncoding)
|
|
6
|
+
# URL: <http://nltk.org/>
|
|
7
|
+
# For license information, see LICENSE.TXT
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
A classifier model based on maximum entropy modeling framework. This
|
|
11
|
+
framework considers all of the probability distributions that are
|
|
12
|
+
empirically consistent with the training data; and chooses the
|
|
13
|
+
distribution with the highest entropy. A probability distribution is
|
|
14
|
+
"empirically consistent" with a set of training data if its estimated
|
|
15
|
+
frequency with which a class and a feature vector value co-occur is
|
|
16
|
+
equal to the actual frequency in the data.
|
|
17
|
+
|
|
18
|
+
Terminology: 'feature'
|
|
19
|
+
======================
|
|
20
|
+
The term *feature* is usually used to refer to some property of an
|
|
21
|
+
unlabeled token. For example, when performing word sense
|
|
22
|
+
disambiguation, we might define a ``'prevword'`` feature whose value is
|
|
23
|
+
the word preceding the target word. However, in the context of
|
|
24
|
+
maxent modeling, the term *feature* is typically used to refer to a
|
|
25
|
+
property of a "labeled" token. In order to prevent confusion, we
|
|
26
|
+
will introduce two distinct terms to disambiguate these two different
|
|
27
|
+
concepts:
|
|
28
|
+
|
|
29
|
+
- An "input-feature" is a property of an unlabeled token.
|
|
30
|
+
- A "joint-feature" is a property of a labeled token.
|
|
31
|
+
|
|
32
|
+
In the rest of the ``nltk.classify`` module, the term "features" is
|
|
33
|
+
used to refer to what we will call "input-features" in this module.
|
|
34
|
+
|
|
35
|
+
In literature that describes and discusses maximum entropy models,
|
|
36
|
+
input-features are typically called "contexts", and joint-features
|
|
37
|
+
are simply referred to as "features".
|
|
38
|
+
|
|
39
|
+
Converting Input-Features to Joint-Features
|
|
40
|
+
-------------------------------------------
|
|
41
|
+
In maximum entropy models, joint-features are required to have numeric
|
|
42
|
+
values. Typically, each input-feature ``input_feat`` is mapped to a
|
|
43
|
+
set of joint-features of the form:
|
|
44
|
+
|
|
45
|
+
| joint_feat(token, label) = { 1 if input_feat(token) == feat_val
|
|
46
|
+
| { and label == some_label
|
|
47
|
+
| {
|
|
48
|
+
| { 0 otherwise
|
|
49
|
+
|
|
50
|
+
For all values of ``feat_val`` and ``some_label``. This mapping is
|
|
51
|
+
performed by classes that implement the ``MaxentFeatureEncodingI``
|
|
52
|
+
interface.
|
|
53
|
+
"""
|
|
54
|
+
from __future__ import print_function, unicode_literals
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
import numpy
|
|
58
|
+
except ImportError:
|
|
59
|
+
pass
|
|
60
|
+
|
|
61
|
+
import tempfile
|
|
62
|
+
import os
|
|
63
|
+
import re
|
|
64
|
+
from collections import defaultdict
|
|
65
|
+
|
|
66
|
+
from six import integer_types
|
|
67
|
+
|
|
68
|
+
from nltk import compat
|
|
69
|
+
from nltk.data import gzip_open_unicode
|
|
70
|
+
from nltk.util import OrderedDict
|
|
71
|
+
from nltk.probability import DictionaryProbDist
|
|
72
|
+
|
|
73
|
+
from nltk.classify.api import ClassifierI
|
|
74
|
+
from nltk.classify.util import CutoffChecker, accuracy, log_likelihood
|
|
75
|
+
from nltk.classify.megam import (call_megam,
|
|
76
|
+
write_megam_file, parse_megam_weights)
|
|
77
|
+
from nltk.classify.tadm import call_tadm, write_tadm_file, parse_tadm_weights
|
|
78
|
+
|
|
79
|
+
__docformat__ = 'epytext en'
|
|
80
|
+
|
|
81
|
+
######################################################################
|
|
82
|
+
#{ Classifier Model
|
|
83
|
+
######################################################################
|
|
84
|
+
|
|
85
|
+
@compat.python_2_unicode_compatible
|
|
86
|
+
class MaxentClassifier(ClassifierI):
|
|
87
|
+
"""
|
|
88
|
+
A maximum entropy classifier (also known as a "conditional
|
|
89
|
+
exponential classifier"). This classifier is parameterized by a
|
|
90
|
+
set of "weights", which are used to combine the joint-features
|
|
91
|
+
that are generated from a featureset by an "encoding". In
|
|
92
|
+
particular, the encoding maps each ``(featureset, label)`` pair to
|
|
93
|
+
a vector. The probability of each label is then computed using
|
|
94
|
+
the following equation::
|
|
95
|
+
|
|
96
|
+
dotprod(weights, encode(fs,label))
|
|
97
|
+
prob(fs|label) = ---------------------------------------------------
|
|
98
|
+
sum(dotprod(weights, encode(fs,l)) for l in labels)
|
|
99
|
+
|
|
100
|
+
Where ``dotprod`` is the dot product::
|
|
101
|
+
|
|
102
|
+
dotprod(a,b) = sum(x*y for (x,y) in zip(a,b))
|
|
103
|
+
"""
|
|
104
|
+
def __init__(self, encoding, weights, logarithmic=True):
|
|
105
|
+
"""
|
|
106
|
+
Construct a new maxent classifier model. Typically, new
|
|
107
|
+
classifier models are created using the ``train()`` method.
|
|
108
|
+
|
|
109
|
+
:type encoding: MaxentFeatureEncodingI
|
|
110
|
+
:param encoding: An encoding that is used to convert the
|
|
111
|
+
featuresets that are given to the ``classify`` method into
|
|
112
|
+
joint-feature vectors, which are used by the maxent
|
|
113
|
+
classifier model.
|
|
114
|
+
|
|
115
|
+
:type weights: list of float
|
|
116
|
+
:param weights: The feature weight vector for this classifier.
|
|
117
|
+
|
|
118
|
+
:type logarithmic: bool
|
|
119
|
+
:param logarithmic: If false, then use non-logarithmic weights.
|
|
120
|
+
"""
|
|
121
|
+
self._encoding = encoding
|
|
122
|
+
self._weights = weights
|
|
123
|
+
self._logarithmic = logarithmic
|
|
124
|
+
#self._logarithmic = False
|
|
125
|
+
assert encoding.length() == len(weights)
|
|
126
|
+
|
|
127
|
+
def labels(self):
|
|
128
|
+
return self._encoding.labels()
|
|
129
|
+
|
|
130
|
+
def set_weights(self, new_weights):
|
|
131
|
+
"""
|
|
132
|
+
Set the feature weight vector for this classifier.
|
|
133
|
+
:param new_weights: The new feature weight vector.
|
|
134
|
+
:type new_weights: list of float
|
|
135
|
+
"""
|
|
136
|
+
self._weights = new_weights
|
|
137
|
+
assert self._encoding.length() == len(new_weights)
|
|
138
|
+
|
|
139
|
+
def weights(self):
|
|
140
|
+
"""
|
|
141
|
+
:return: The feature weight vector for this classifier.
|
|
142
|
+
:rtype: list of float
|
|
143
|
+
"""
|
|
144
|
+
return self._weights
|
|
145
|
+
|
|
146
|
+
def classify(self, featureset):
|
|
147
|
+
return self.prob_classify(featureset).max()
|
|
148
|
+
|
|
149
|
+
def prob_classify(self, featureset):
|
|
150
|
+
prob_dict = {}
|
|
151
|
+
for label in self._encoding.labels():
|
|
152
|
+
feature_vector = self._encoding.encode(featureset, label)
|
|
153
|
+
|
|
154
|
+
if self._logarithmic:
|
|
155
|
+
total = 0.0
|
|
156
|
+
for (f_id, f_val) in feature_vector:
|
|
157
|
+
total += self._weights[f_id] * f_val
|
|
158
|
+
prob_dict[label] = total
|
|
159
|
+
|
|
160
|
+
else:
|
|
161
|
+
prod = 1.0
|
|
162
|
+
for (f_id, f_val) in feature_vector:
|
|
163
|
+
prod *= self._weights[f_id] ** f_val
|
|
164
|
+
prob_dict[label] = prod
|
|
165
|
+
|
|
166
|
+
# Normalize the dictionary to give a probability distribution
|
|
167
|
+
return DictionaryProbDist(prob_dict, log=self._logarithmic,
|
|
168
|
+
normalize=True)
|
|
169
|
+
|
|
170
|
+
def explain(self, featureset, columns=4):
|
|
171
|
+
"""
|
|
172
|
+
Print a table showing the effect of each of the features in
|
|
173
|
+
the given feature set, and how they combine to determine the
|
|
174
|
+
probabilities of each label for that featureset.
|
|
175
|
+
"""
|
|
176
|
+
descr_width = 50
|
|
177
|
+
TEMPLATE = ' %-'+str(descr_width-2)+'s%s%8.3f'
|
|
178
|
+
|
|
179
|
+
pdist = self.prob_classify(featureset)
|
|
180
|
+
labels = sorted(pdist.samples(), key=pdist.prob, reverse=True)
|
|
181
|
+
labels = labels[:columns]
|
|
182
|
+
print(' Feature'.ljust(descr_width)+''.join(
|
|
183
|
+
'%8s' % (("%s" % l)[:7]) for l in labels))
|
|
184
|
+
print(' '+'-'*(descr_width-2+8*len(labels)))
|
|
185
|
+
sums = defaultdict(int)
|
|
186
|
+
for i, label in enumerate(labels):
|
|
187
|
+
feature_vector = self._encoding.encode(featureset, label)
|
|
188
|
+
feature_vector.sort(key=lambda fid__: abs(self._weights[fid__[0]]),
|
|
189
|
+
reverse=True)
|
|
190
|
+
for (f_id, f_val) in feature_vector:
|
|
191
|
+
if self._logarithmic:
|
|
192
|
+
score = self._weights[f_id] * f_val
|
|
193
|
+
else: score = self._weights[f_id] ** f_val
|
|
194
|
+
descr = self._encoding.describe(f_id)
|
|
195
|
+
descr = descr.split(' and label is ')[0] # hack
|
|
196
|
+
descr += ' (%s)' % f_val # hack
|
|
197
|
+
if len(descr) > 47:
|
|
198
|
+
descr = descr[:44]+'...'
|
|
199
|
+
print(TEMPLATE % (descr, i*8*' ', score))
|
|
200
|
+
sums[label] += score
|
|
201
|
+
print(' '+'-'*(descr_width-1+8*len(labels)))
|
|
202
|
+
print(' TOTAL:'.ljust(descr_width)+''.join(
|
|
203
|
+
'%8.3f' % sums[l] for l in labels))
|
|
204
|
+
print(' PROBS:'.ljust(descr_width)+''.join(
|
|
205
|
+
'%8.3f' % pdist.prob(l) for l in labels))
|
|
206
|
+
|
|
207
|
+
def most_informative_features(self, n=10):
|
|
208
|
+
"""
|
|
209
|
+
Generates the ranked list of informative features from most to least.
|
|
210
|
+
"""
|
|
211
|
+
if hasattr(self, '_most_informative_features'):
|
|
212
|
+
return self._most_informative_features[:n]
|
|
213
|
+
else:
|
|
214
|
+
self._most_informative_features = sorted(list(range(len(self._weights))),
|
|
215
|
+
key=lambda fid: abs(self._weights[fid]),
|
|
216
|
+
reverse=True)
|
|
217
|
+
return self._most_informative_features[:n]
|
|
218
|
+
|
|
219
|
+
def show_most_informative_features(self, n=10, show='all'):
|
|
220
|
+
"""
|
|
221
|
+
:param show: all, neg, or pos (for negative-only or positive-only)
|
|
222
|
+
:type show: str
|
|
223
|
+
:param n: The no. of top features
|
|
224
|
+
:type n: int
|
|
225
|
+
"""
|
|
226
|
+
# Use None the full list of ranked features.
|
|
227
|
+
fids = self.most_informative_features(None)
|
|
228
|
+
if show == 'pos':
|
|
229
|
+
fids = [fid for fid in fids if self._weights[fid] > 0]
|
|
230
|
+
elif show == 'neg':
|
|
231
|
+
fids = [fid for fid in fids if self._weights[fid] < 0]
|
|
232
|
+
for fid in fids[:n]:
|
|
233
|
+
print('%8.3f %s' % (self._weights[fid],
|
|
234
|
+
self._encoding.describe(fid)))
|
|
235
|
+
|
|
236
|
+
def __repr__(self):
|
|
237
|
+
return ('<ConditionalExponentialClassifier: %d labels, %d features>' %
|
|
238
|
+
(len(self._encoding.labels()), self._encoding.length()))
|
|
239
|
+
|
|
240
|
+
#: A list of the algorithm names that are accepted for the
|
|
241
|
+
#: ``train()`` method's ``algorithm`` parameter.
|
|
242
|
+
ALGORITHMS = ['GIS', 'IIS', 'MEGAM', 'TADM']
|
|
243
|
+
|
|
244
|
+
@classmethod
|
|
245
|
+
def train(cls, train_toks, algorithm=None, trace=3, encoding=None,
|
|
246
|
+
labels=None, gaussian_prior_sigma=0, **cutoffs):
|
|
247
|
+
"""
|
|
248
|
+
Train a new maxent classifier based on the given corpus of
|
|
249
|
+
training samples. This classifier will have its weights
|
|
250
|
+
chosen to maximize entropy while remaining empirically
|
|
251
|
+
consistent with the training corpus.
|
|
252
|
+
|
|
253
|
+
:rtype: MaxentClassifier
|
|
254
|
+
:return: The new maxent classifier
|
|
255
|
+
|
|
256
|
+
:type train_toks: list
|
|
257
|
+
:param train_toks: Training data, represented as a list of
|
|
258
|
+
pairs, the first member of which is a featureset,
|
|
259
|
+
and the second of which is a classification label.
|
|
260
|
+
|
|
261
|
+
:type algorithm: str
|
|
262
|
+
:param algorithm: A case-insensitive string, specifying which
|
|
263
|
+
algorithm should be used to train the classifier. The
|
|
264
|
+
following algorithms are currently available.
|
|
265
|
+
|
|
266
|
+
- Iterative Scaling Methods: Generalized Iterative Scaling (``'GIS'``),
|
|
267
|
+
Improved Iterative Scaling (``'IIS'``)
|
|
268
|
+
- External Libraries (requiring megam):
|
|
269
|
+
LM-BFGS algorithm, with training performed by Megam (``'megam'``)
|
|
270
|
+
|
|
271
|
+
The default algorithm is ``'IIS'``.
|
|
272
|
+
|
|
273
|
+
:type trace: int
|
|
274
|
+
:param trace: The level of diagnostic tracing output to produce.
|
|
275
|
+
Higher values produce more verbose output.
|
|
276
|
+
:type encoding: MaxentFeatureEncodingI
|
|
277
|
+
:param encoding: A feature encoding, used to convert featuresets
|
|
278
|
+
into feature vectors. If none is specified, then a
|
|
279
|
+
``BinaryMaxentFeatureEncoding`` will be built based on the
|
|
280
|
+
features that are attested in the training corpus.
|
|
281
|
+
:type labels: list(str)
|
|
282
|
+
:param labels: The set of possible labels. If none is given, then
|
|
283
|
+
the set of all labels attested in the training data will be
|
|
284
|
+
used instead.
|
|
285
|
+
:param gaussian_prior_sigma: The sigma value for a gaussian
|
|
286
|
+
prior on model weights. Currently, this is supported by
|
|
287
|
+
``megam``. For other algorithms, its value is ignored.
|
|
288
|
+
:param cutoffs: Arguments specifying various conditions under
|
|
289
|
+
which the training should be halted. (Some of the cutoff
|
|
290
|
+
conditions are not supported by some algorithms.)
|
|
291
|
+
|
|
292
|
+
- ``max_iter=v``: Terminate after ``v`` iterations.
|
|
293
|
+
- ``min_ll=v``: Terminate after the negative average
|
|
294
|
+
log-likelihood drops under ``v``.
|
|
295
|
+
- ``min_lldelta=v``: Terminate if a single iteration improves
|
|
296
|
+
log likelihood by less than ``v``.
|
|
297
|
+
"""
|
|
298
|
+
if algorithm is None:
|
|
299
|
+
algorithm = 'iis'
|
|
300
|
+
for key in cutoffs:
|
|
301
|
+
if key not in ('max_iter', 'min_ll', 'min_lldelta',
|
|
302
|
+
'max_acc', 'min_accdelta', 'count_cutoff',
|
|
303
|
+
'norm', 'explicit', 'bernoulli'):
|
|
304
|
+
raise TypeError('Unexpected keyword arg %r' % key)
|
|
305
|
+
algorithm = algorithm.lower()
|
|
306
|
+
if algorithm == 'iis':
|
|
307
|
+
return train_maxent_classifier_with_iis(
|
|
308
|
+
train_toks, trace, encoding, labels, **cutoffs)
|
|
309
|
+
elif algorithm == 'gis':
|
|
310
|
+
return train_maxent_classifier_with_gis(
|
|
311
|
+
train_toks, trace, encoding, labels, **cutoffs)
|
|
312
|
+
elif algorithm == 'megam':
|
|
313
|
+
return train_maxent_classifier_with_megam(
|
|
314
|
+
train_toks, trace, encoding, labels,
|
|
315
|
+
gaussian_prior_sigma, **cutoffs)
|
|
316
|
+
elif algorithm == 'tadm':
|
|
317
|
+
kwargs = cutoffs
|
|
318
|
+
kwargs['trace'] = trace
|
|
319
|
+
kwargs['encoding'] = encoding
|
|
320
|
+
kwargs['labels'] = labels
|
|
321
|
+
kwargs['gaussian_prior_sigma'] = gaussian_prior_sigma
|
|
322
|
+
return TadmMaxentClassifier.train(train_toks, **kwargs)
|
|
323
|
+
else:
|
|
324
|
+
raise ValueError('Unknown algorithm %s' % algorithm)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
#: Alias for MaxentClassifier.
|
|
328
|
+
ConditionalExponentialClassifier = MaxentClassifier
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
######################################################################
|
|
332
|
+
#{ Feature Encodings
|
|
333
|
+
######################################################################
|
|
334
|
+
|
|
335
|
+
class MaxentFeatureEncodingI(object):
|
|
336
|
+
"""
|
|
337
|
+
A mapping that converts a set of input-feature values to a vector
|
|
338
|
+
of joint-feature values, given a label. This conversion is
|
|
339
|
+
necessary to translate featuresets into a format that can be used
|
|
340
|
+
by maximum entropy models.
|
|
341
|
+
|
|
342
|
+
The set of joint-features used by a given encoding is fixed, and
|
|
343
|
+
each index in the generated joint-feature vectors corresponds to a
|
|
344
|
+
single joint-feature. The length of the generated joint-feature
|
|
345
|
+
vectors is therefore constant (for a given encoding).
|
|
346
|
+
|
|
347
|
+
Because the joint-feature vectors generated by
|
|
348
|
+
``MaxentFeatureEncodingI`` are typically very sparse, they are
|
|
349
|
+
represented as a list of ``(index, value)`` tuples, specifying the
|
|
350
|
+
value of each non-zero joint-feature.
|
|
351
|
+
|
|
352
|
+
Feature encodings are generally created using the ``train()``
|
|
353
|
+
method, which generates an appropriate encoding based on the
|
|
354
|
+
input-feature values and labels that are present in a given
|
|
355
|
+
corpus.
|
|
356
|
+
"""
|
|
357
|
+
def encode(self, featureset, label):
|
|
358
|
+
"""
|
|
359
|
+
Given a (featureset, label) pair, return the corresponding
|
|
360
|
+
vector of joint-feature values. This vector is represented as
|
|
361
|
+
a list of ``(index, value)`` tuples, specifying the value of
|
|
362
|
+
each non-zero joint-feature.
|
|
363
|
+
|
|
364
|
+
:type featureset: dict
|
|
365
|
+
:rtype: list(tuple(int, int))
|
|
366
|
+
"""
|
|
367
|
+
raise NotImplementedError()
|
|
368
|
+
|
|
369
|
+
def length(self):
|
|
370
|
+
"""
|
|
371
|
+
:return: The size of the fixed-length joint-feature vectors
|
|
372
|
+
that are generated by this encoding.
|
|
373
|
+
:rtype: int
|
|
374
|
+
"""
|
|
375
|
+
raise NotImplementedError()
|
|
376
|
+
|
|
377
|
+
def labels(self):
|
|
378
|
+
"""
|
|
379
|
+
:return: A list of the \"known labels\" -- i.e., all labels
|
|
380
|
+
``l`` such that ``self.encode(fs,l)`` can be a nonzero
|
|
381
|
+
joint-feature vector for some value of ``fs``.
|
|
382
|
+
:rtype: list
|
|
383
|
+
"""
|
|
384
|
+
raise NotImplementedError()
|
|
385
|
+
|
|
386
|
+
def describe(self, fid):
|
|
387
|
+
"""
|
|
388
|
+
:return: A string describing the value of the joint-feature
|
|
389
|
+
whose index in the generated feature vectors is ``fid``.
|
|
390
|
+
:rtype: str
|
|
391
|
+
"""
|
|
392
|
+
raise NotImplementedError()
|
|
393
|
+
|
|
394
|
+
def train(cls, train_toks):
|
|
395
|
+
"""
|
|
396
|
+
Construct and return new feature encoding, based on a given
|
|
397
|
+
training corpus ``train_toks``.
|
|
398
|
+
|
|
399
|
+
:type train_toks: list(tuple(dict, str))
|
|
400
|
+
:param train_toks: Training data, represented as a list of
|
|
401
|
+
pairs, the first member of which is a feature dictionary,
|
|
402
|
+
and the second of which is a classification label.
|
|
403
|
+
"""
|
|
404
|
+
raise NotImplementedError()
|
|
405
|
+
|
|
406
|
+
class FunctionBackedMaxentFeatureEncoding(MaxentFeatureEncodingI):
|
|
407
|
+
"""
|
|
408
|
+
A feature encoding that calls a user-supplied function to map a
|
|
409
|
+
given featureset/label pair to a sparse joint-feature vector.
|
|
410
|
+
"""
|
|
411
|
+
def __init__(self, func, length, labels):
|
|
412
|
+
"""
|
|
413
|
+
Construct a new feature encoding based on the given function.
|
|
414
|
+
|
|
415
|
+
:type func: (callable)
|
|
416
|
+
:param func: A function that takes two arguments, a featureset
|
|
417
|
+
and a label, and returns the sparse joint feature vector
|
|
418
|
+
that encodes them::
|
|
419
|
+
|
|
420
|
+
func(featureset, label) -> feature_vector
|
|
421
|
+
|
|
422
|
+
This sparse joint feature vector (``feature_vector``) is a
|
|
423
|
+
list of ``(index,value)`` tuples.
|
|
424
|
+
|
|
425
|
+
:type length: int
|
|
426
|
+
:param length: The size of the fixed-length joint-feature
|
|
427
|
+
vectors that are generated by this encoding.
|
|
428
|
+
|
|
429
|
+
:type labels: list
|
|
430
|
+
:param labels: A list of the \"known labels\" for this
|
|
431
|
+
encoding -- i.e., all labels ``l`` such that
|
|
432
|
+
``self.encode(fs,l)`` can be a nonzero joint-feature vector
|
|
433
|
+
for some value of ``fs``.
|
|
434
|
+
"""
|
|
435
|
+
self._length = length
|
|
436
|
+
self._func = func
|
|
437
|
+
self._labels = labels
|
|
438
|
+
|
|
439
|
+
def encode(self, featureset, label):
|
|
440
|
+
return self._func(featureset, label)
|
|
441
|
+
|
|
442
|
+
def length(self):
|
|
443
|
+
return self._length
|
|
444
|
+
|
|
445
|
+
def labels(self):
|
|
446
|
+
return self._labels
|
|
447
|
+
|
|
448
|
+
def describe(self, fid):
|
|
449
|
+
return 'no description available'
|
|
450
|
+
|
|
451
|
+
class BinaryMaxentFeatureEncoding(MaxentFeatureEncodingI):
|
|
452
|
+
"""
|
|
453
|
+
A feature encoding that generates vectors containing a binary
|
|
454
|
+
joint-features of the form:
|
|
455
|
+
|
|
456
|
+
| joint_feat(fs, l) = { 1 if (fs[fname] == fval) and (l == label)
|
|
457
|
+
| {
|
|
458
|
+
| { 0 otherwise
|
|
459
|
+
|
|
460
|
+
Where ``fname`` is the name of an input-feature, ``fval`` is a value
|
|
461
|
+
for that input-feature, and ``label`` is a label.
|
|
462
|
+
|
|
463
|
+
Typically, these features are constructed based on a training
|
|
464
|
+
corpus, using the ``train()`` method. This method will create one
|
|
465
|
+
feature for each combination of ``fname``, ``fval``, and ``label``
|
|
466
|
+
that occurs at least once in the training corpus.
|
|
467
|
+
|
|
468
|
+
The ``unseen_features`` parameter can be used to add "unseen-value
|
|
469
|
+
features", which are used whenever an input feature has a value
|
|
470
|
+
that was not encountered in the training corpus. These features
|
|
471
|
+
have the form:
|
|
472
|
+
|
|
473
|
+
| joint_feat(fs, l) = { 1 if is_unseen(fname, fs[fname])
|
|
474
|
+
| { and l == label
|
|
475
|
+
| {
|
|
476
|
+
| { 0 otherwise
|
|
477
|
+
|
|
478
|
+
Where ``is_unseen(fname, fval)`` is true if the encoding does not
|
|
479
|
+
contain any joint features that are true when ``fs[fname]==fval``.
|
|
480
|
+
|
|
481
|
+
The ``alwayson_features`` parameter can be used to add "always-on
|
|
482
|
+
features", which have the form::
|
|
483
|
+
|
|
484
|
+
| joint_feat(fs, l) = { 1 if (l == label)
|
|
485
|
+
| {
|
|
486
|
+
| { 0 otherwise
|
|
487
|
+
|
|
488
|
+
These always-on features allow the maxent model to directly model
|
|
489
|
+
the prior probabilities of each label.
|
|
490
|
+
"""
|
|
491
|
+
def __init__(self, labels, mapping, unseen_features=False,
|
|
492
|
+
alwayson_features=False):
|
|
493
|
+
"""
|
|
494
|
+
:param labels: A list of the \"known labels\" for this encoding.
|
|
495
|
+
|
|
496
|
+
:param mapping: A dictionary mapping from ``(fname,fval,label)``
|
|
497
|
+
tuples to corresponding joint-feature indexes. These
|
|
498
|
+
indexes must be the set of integers from 0...len(mapping).
|
|
499
|
+
If ``mapping[fname,fval,label]=id``, then
|
|
500
|
+
``self.encode(..., fname:fval, ..., label)[id]`` is 1;
|
|
501
|
+
otherwise, it is 0.
|
|
502
|
+
|
|
503
|
+
:param unseen_features: If true, then include unseen value
|
|
504
|
+
features in the generated joint-feature vectors.
|
|
505
|
+
|
|
506
|
+
:param alwayson_features: If true, then include always-on
|
|
507
|
+
features in the generated joint-feature vectors.
|
|
508
|
+
"""
|
|
509
|
+
if set(mapping.values()) != set(range(len(mapping))):
|
|
510
|
+
raise ValueError('Mapping values must be exactly the '
|
|
511
|
+
'set of integers from 0...len(mapping)')
|
|
512
|
+
|
|
513
|
+
self._labels = list(labels)
|
|
514
|
+
"""A list of attested labels."""
|
|
515
|
+
|
|
516
|
+
self._mapping = mapping
|
|
517
|
+
"""dict mapping from (fname,fval,label) -> fid"""
|
|
518
|
+
|
|
519
|
+
self._length = len(mapping)
|
|
520
|
+
"""The length of generated joint feature vectors."""
|
|
521
|
+
|
|
522
|
+
self._alwayson = None
|
|
523
|
+
"""dict mapping from label -> fid"""
|
|
524
|
+
|
|
525
|
+
self._unseen = None
|
|
526
|
+
"""dict mapping from fname -> fid"""
|
|
527
|
+
|
|
528
|
+
if alwayson_features:
|
|
529
|
+
self._alwayson = dict((label, i+self._length)
|
|
530
|
+
for (i, label) in enumerate(labels))
|
|
531
|
+
self._length += len(self._alwayson)
|
|
532
|
+
|
|
533
|
+
if unseen_features:
|
|
534
|
+
fnames = set(fname for (fname, fval, label) in mapping)
|
|
535
|
+
self._unseen = dict((fname, i+self._length)
|
|
536
|
+
for (i, fname) in enumerate(fnames))
|
|
537
|
+
self._length += len(fnames)
|
|
538
|
+
|
|
539
|
+
def encode(self, featureset, label):
|
|
540
|
+
# Inherit docs.
|
|
541
|
+
encoding = []
|
|
542
|
+
|
|
543
|
+
# Convert input-features to joint-features:
|
|
544
|
+
for fname, fval in featureset.items():
|
|
545
|
+
# Known feature name & value:
|
|
546
|
+
if (fname, fval, label) in self._mapping:
|
|
547
|
+
encoding.append((self._mapping[fname, fval, label], 1))
|
|
548
|
+
|
|
549
|
+
# Otherwise, we might want to fire an "unseen-value feature".
|
|
550
|
+
elif self._unseen:
|
|
551
|
+
# Have we seen this fname/fval combination with any label?
|
|
552
|
+
for label2 in self._labels:
|
|
553
|
+
if (fname, fval, label2) in self._mapping:
|
|
554
|
+
break # we've seen this fname/fval combo
|
|
555
|
+
# We haven't -- fire the unseen-value feature
|
|
556
|
+
else:
|
|
557
|
+
if fname in self._unseen:
|
|
558
|
+
encoding.append((self._unseen[fname], 1))
|
|
559
|
+
|
|
560
|
+
# Add always-on features:
|
|
561
|
+
if self._alwayson and label in self._alwayson:
|
|
562
|
+
encoding.append((self._alwayson[label], 1))
|
|
563
|
+
|
|
564
|
+
return encoding
|
|
565
|
+
|
|
566
|
+
def describe(self, f_id):
|
|
567
|
+
# Inherit docs.
|
|
568
|
+
if not isinstance(f_id, integer_types):
|
|
569
|
+
raise TypeError('describe() expected an int')
|
|
570
|
+
try:
|
|
571
|
+
self._inv_mapping
|
|
572
|
+
except AttributeError:
|
|
573
|
+
self._inv_mapping = [-1]*len(self._mapping)
|
|
574
|
+
for (info, i) in self._mapping.items():
|
|
575
|
+
self._inv_mapping[i] = info
|
|
576
|
+
|
|
577
|
+
if f_id < len(self._mapping):
|
|
578
|
+
(fname, fval, label) = self._inv_mapping[f_id]
|
|
579
|
+
return '%s==%r and label is %r' % (fname, fval, label)
|
|
580
|
+
elif self._alwayson and f_id in self._alwayson.values():
|
|
581
|
+
for (label, f_id2) in self._alwayson.items():
|
|
582
|
+
if f_id == f_id2:
|
|
583
|
+
return 'label is %r' % label
|
|
584
|
+
elif self._unseen and f_id in self._unseen.values():
|
|
585
|
+
for (fname, f_id2) in self._unseen.items():
|
|
586
|
+
if f_id == f_id2:
|
|
587
|
+
return '%s is unseen' % fname
|
|
588
|
+
else:
|
|
589
|
+
raise ValueError('Bad feature id')
|
|
590
|
+
|
|
591
|
+
def labels(self):
|
|
592
|
+
# Inherit docs.
|
|
593
|
+
return self._labels
|
|
594
|
+
|
|
595
|
+
def length(self):
|
|
596
|
+
# Inherit docs.
|
|
597
|
+
return self._length
|
|
598
|
+
|
|
599
|
+
@classmethod
|
|
600
|
+
def train(cls, train_toks, count_cutoff=0, labels=None, **options):
|
|
601
|
+
"""
|
|
602
|
+
Construct and return new feature encoding, based on a given
|
|
603
|
+
training corpus ``train_toks``. See the class description
|
|
604
|
+
``BinaryMaxentFeatureEncoding`` for a description of the
|
|
605
|
+
joint-features that will be included in this encoding.
|
|
606
|
+
|
|
607
|
+
:type train_toks: list(tuple(dict, str))
|
|
608
|
+
:param train_toks: Training data, represented as a list of
|
|
609
|
+
pairs, the first member of which is a feature dictionary,
|
|
610
|
+
and the second of which is a classification label.
|
|
611
|
+
|
|
612
|
+
:type count_cutoff: int
|
|
613
|
+
:param count_cutoff: A cutoff value that is used to discard
|
|
614
|
+
rare joint-features. If a joint-feature's value is 1
|
|
615
|
+
fewer than ``count_cutoff`` times in the training corpus,
|
|
616
|
+
then that joint-feature is not included in the generated
|
|
617
|
+
encoding.
|
|
618
|
+
|
|
619
|
+
:type labels: list
|
|
620
|
+
:param labels: A list of labels that should be used by the
|
|
621
|
+
classifier. If not specified, then the set of labels
|
|
622
|
+
attested in ``train_toks`` will be used.
|
|
623
|
+
|
|
624
|
+
:param options: Extra parameters for the constructor, such as
|
|
625
|
+
``unseen_features`` and ``alwayson_features``.
|
|
626
|
+
"""
|
|
627
|
+
mapping = {} # maps (fname, fval, label) -> fid
|
|
628
|
+
seen_labels = set() # The set of labels we've encountered
|
|
629
|
+
count = defaultdict(int) # maps (fname, fval) -> count
|
|
630
|
+
|
|
631
|
+
for (tok, label) in train_toks:
|
|
632
|
+
if labels and label not in labels:
|
|
633
|
+
raise ValueError('Unexpected label %s' % label)
|
|
634
|
+
seen_labels.add(label)
|
|
635
|
+
|
|
636
|
+
# Record each of the features.
|
|
637
|
+
for (fname, fval) in tok.items():
|
|
638
|
+
|
|
639
|
+
# If a count cutoff is given, then only add a joint
|
|
640
|
+
# feature once the corresponding (fname, fval, label)
|
|
641
|
+
# tuple exceeds that cutoff.
|
|
642
|
+
count[fname, fval] += 1
|
|
643
|
+
if count[fname, fval] >= count_cutoff:
|
|
644
|
+
if (fname, fval, label) not in mapping:
|
|
645
|
+
mapping[fname, fval, label] = len(mapping)
|
|
646
|
+
|
|
647
|
+
if labels is None:
|
|
648
|
+
labels = seen_labels
|
|
649
|
+
return cls(labels, mapping, **options)
|
|
650
|
+
|
|
651
|
+
class GISEncoding(BinaryMaxentFeatureEncoding):
|
|
652
|
+
"""
|
|
653
|
+
A binary feature encoding which adds one new joint-feature to the
|
|
654
|
+
joint-features defined by ``BinaryMaxentFeatureEncoding``: a
|
|
655
|
+
correction feature, whose value is chosen to ensure that the
|
|
656
|
+
sparse vector always sums to a constant non-negative number. This
|
|
657
|
+
new feature is used to ensure two preconditions for the GIS
|
|
658
|
+
training algorithm:
|
|
659
|
+
|
|
660
|
+
- At least one feature vector index must be nonzero for every
|
|
661
|
+
token.
|
|
662
|
+
- The feature vector must sum to a constant non-negative number
|
|
663
|
+
for every token.
|
|
664
|
+
"""
|
|
665
|
+
def __init__(self, labels, mapping, unseen_features=False,
|
|
666
|
+
alwayson_features=False, C=None):
|
|
667
|
+
"""
|
|
668
|
+
:param C: The correction constant. The value of the correction
|
|
669
|
+
feature is based on this value. In particular, its value is
|
|
670
|
+
``C - sum([v for (f,v) in encoding])``.
|
|
671
|
+
:seealso: ``BinaryMaxentFeatureEncoding.__init__``
|
|
672
|
+
"""
|
|
673
|
+
BinaryMaxentFeatureEncoding.__init__(
|
|
674
|
+
self, labels, mapping, unseen_features, alwayson_features)
|
|
675
|
+
if C is None:
|
|
676
|
+
C = len(set(fname for (fname, fval, label) in mapping))+1
|
|
677
|
+
self._C = C
|
|
678
|
+
|
|
679
|
+
@property
|
|
680
|
+
def C(self):
|
|
681
|
+
"""The non-negative constant that all encoded feature vectors
|
|
682
|
+
will sum to."""
|
|
683
|
+
return self._C
|
|
684
|
+
|
|
685
|
+
def encode(self, featureset, label):
|
|
686
|
+
# Get the basic encoding.
|
|
687
|
+
encoding = BinaryMaxentFeatureEncoding.encode(self, featureset, label)
|
|
688
|
+
base_length = BinaryMaxentFeatureEncoding.length(self)
|
|
689
|
+
|
|
690
|
+
# Add a correction feature.
|
|
691
|
+
total = sum(v for (f, v) in encoding)
|
|
692
|
+
if total >= self._C:
|
|
693
|
+
raise ValueError('Correction feature is not high enough!')
|
|
694
|
+
encoding.append((base_length, self._C-total))
|
|
695
|
+
|
|
696
|
+
# Return the result
|
|
697
|
+
return encoding
|
|
698
|
+
|
|
699
|
+
def length(self):
|
|
700
|
+
return BinaryMaxentFeatureEncoding.length(self) + 1
|
|
701
|
+
|
|
702
|
+
def describe(self, f_id):
|
|
703
|
+
if f_id == BinaryMaxentFeatureEncoding.length(self):
|
|
704
|
+
return 'Correction feature (%s)' % self._C
|
|
705
|
+
else:
|
|
706
|
+
return BinaryMaxentFeatureEncoding.describe(self, f_id)
|
|
707
|
+
|
|
708
|
+
|
|
709
|
+
class TadmEventMaxentFeatureEncoding(BinaryMaxentFeatureEncoding):
|
|
710
|
+
def __init__(self, labels, mapping, unseen_features=False,
|
|
711
|
+
alwayson_features=False):
|
|
712
|
+
self._mapping = OrderedDict(mapping)
|
|
713
|
+
self._label_mapping = OrderedDict()
|
|
714
|
+
BinaryMaxentFeatureEncoding.__init__(self, labels, self._mapping,
|
|
715
|
+
unseen_features,
|
|
716
|
+
alwayson_features)
|
|
717
|
+
|
|
718
|
+
def encode(self, featureset, label):
|
|
719
|
+
encoding = []
|
|
720
|
+
for feature, value in featureset.items():
|
|
721
|
+
if (feature, label) not in self._mapping:
|
|
722
|
+
self._mapping[(feature, label)] = len(self._mapping)
|
|
723
|
+
if value not in self._label_mapping:
|
|
724
|
+
if not isinstance(value, int):
|
|
725
|
+
self._label_mapping[value] = len(self._label_mapping)
|
|
726
|
+
else:
|
|
727
|
+
self._label_mapping[value] = value
|
|
728
|
+
encoding.append((self._mapping[(feature, label)],
|
|
729
|
+
self._label_mapping[value]))
|
|
730
|
+
return encoding
|
|
731
|
+
|
|
732
|
+
def labels(self):
|
|
733
|
+
return self._labels
|
|
734
|
+
|
|
735
|
+
def describe(self, fid):
|
|
736
|
+
for (feature, label) in self._mapping:
|
|
737
|
+
if self._mapping[(feature, label)] == fid:
|
|
738
|
+
return (feature, label)
|
|
739
|
+
|
|
740
|
+
def length(self):
|
|
741
|
+
return len(self._mapping)
|
|
742
|
+
|
|
743
|
+
@classmethod
|
|
744
|
+
def train(cls, train_toks, count_cutoff=0, labels=None, **options):
|
|
745
|
+
mapping = OrderedDict()
|
|
746
|
+
if not labels:
|
|
747
|
+
labels = []
|
|
748
|
+
|
|
749
|
+
# This gets read twice, so compute the values in case it's lazy.
|
|
750
|
+
train_toks = list(train_toks)
|
|
751
|
+
|
|
752
|
+
for (featureset, label) in train_toks:
|
|
753
|
+
if label not in labels:
|
|
754
|
+
labels.append(label)
|
|
755
|
+
|
|
756
|
+
for (featureset, label) in train_toks:
|
|
757
|
+
for label in labels:
|
|
758
|
+
for feature in featureset:
|
|
759
|
+
if (feature, label) not in mapping:
|
|
760
|
+
mapping[(feature, label)] = len(mapping)
|
|
761
|
+
|
|
762
|
+
return cls(labels, mapping, **options)
|
|
763
|
+
|
|
764
|
+
|
|
765
|
+
class TypedMaxentFeatureEncoding(MaxentFeatureEncodingI):
|
|
766
|
+
"""
|
|
767
|
+
A feature encoding that generates vectors containing integer,
|
|
768
|
+
float and binary joint-features of the form:
|
|
769
|
+
|
|
770
|
+
Binary (for string and boolean features):
|
|
771
|
+
|
|
772
|
+
| joint_feat(fs, l) = { 1 if (fs[fname] == fval) and (l == label)
|
|
773
|
+
| {
|
|
774
|
+
| { 0 otherwise
|
|
775
|
+
|
|
776
|
+
Value (for integer and float features):
|
|
777
|
+
|
|
778
|
+
| joint_feat(fs, l) = { fval if (fs[fname] == type(fval))
|
|
779
|
+
| { and (l == label)
|
|
780
|
+
| {
|
|
781
|
+
| { not encoded otherwise
|
|
782
|
+
|
|
783
|
+
Where ``fname`` is the name of an input-feature, ``fval`` is a value
|
|
784
|
+
for that input-feature, and ``label`` is a label.
|
|
785
|
+
|
|
786
|
+
Typically, these features are constructed based on a training
|
|
787
|
+
corpus, using the ``train()`` method.
|
|
788
|
+
|
|
789
|
+
For string and boolean features [type(fval) not in (int, float)]
|
|
790
|
+
this method will create one feature for each combination of
|
|
791
|
+
``fname``, ``fval``, and ``label`` that occurs at least once in the
|
|
792
|
+
training corpus.
|
|
793
|
+
|
|
794
|
+
For integer and float features [type(fval) in (int, float)] this
|
|
795
|
+
method will create one feature for each combination of ``fname``
|
|
796
|
+
and ``label`` that occurs at least once in the training corpus.
|
|
797
|
+
|
|
798
|
+
For binary features the ``unseen_features`` parameter can be used
|
|
799
|
+
to add "unseen-value features", which are used whenever an input
|
|
800
|
+
feature has a value that was not encountered in the training
|
|
801
|
+
corpus. These features have the form:
|
|
802
|
+
|
|
803
|
+
| joint_feat(fs, l) = { 1 if is_unseen(fname, fs[fname])
|
|
804
|
+
| { and l == label
|
|
805
|
+
| {
|
|
806
|
+
| { 0 otherwise
|
|
807
|
+
|
|
808
|
+
Where ``is_unseen(fname, fval)`` is true if the encoding does not
|
|
809
|
+
contain any joint features that are true when ``fs[fname]==fval``.
|
|
810
|
+
|
|
811
|
+
The ``alwayson_features`` parameter can be used to add "always-on
|
|
812
|
+
features", which have the form:
|
|
813
|
+
|
|
814
|
+
| joint_feat(fs, l) = { 1 if (l == label)
|
|
815
|
+
| {
|
|
816
|
+
| { 0 otherwise
|
|
817
|
+
|
|
818
|
+
These always-on features allow the maxent model to directly model
|
|
819
|
+
the prior probabilities of each label.
|
|
820
|
+
"""
|
|
821
|
+
def __init__(self, labels, mapping, unseen_features=False,
|
|
822
|
+
alwayson_features=False):
|
|
823
|
+
"""
|
|
824
|
+
:param labels: A list of the \"known labels\" for this encoding.
|
|
825
|
+
|
|
826
|
+
:param mapping: A dictionary mapping from ``(fname,fval,label)``
|
|
827
|
+
tuples to corresponding joint-feature indexes. These
|
|
828
|
+
indexes must be the set of integers from 0...len(mapping).
|
|
829
|
+
If ``mapping[fname,fval,label]=id``, then
|
|
830
|
+
``self.encode({..., fname:fval, ...``, label)[id]} is 1;
|
|
831
|
+
otherwise, it is 0.
|
|
832
|
+
|
|
833
|
+
:param unseen_features: If true, then include unseen value
|
|
834
|
+
features in the generated joint-feature vectors.
|
|
835
|
+
|
|
836
|
+
:param alwayson_features: If true, then include always-on
|
|
837
|
+
features in the generated joint-feature vectors.
|
|
838
|
+
"""
|
|
839
|
+
if set(mapping.values()) != set(range(len(mapping))):
|
|
840
|
+
raise ValueError('Mapping values must be exactly the '
|
|
841
|
+
'set of integers from 0...len(mapping)')
|
|
842
|
+
|
|
843
|
+
self._labels = list(labels)
|
|
844
|
+
"""A list of attested labels."""
|
|
845
|
+
|
|
846
|
+
self._mapping = mapping
|
|
847
|
+
"""dict mapping from (fname,fval,label) -> fid"""
|
|
848
|
+
|
|
849
|
+
self._length = len(mapping)
|
|
850
|
+
"""The length of generated joint feature vectors."""
|
|
851
|
+
|
|
852
|
+
self._alwayson = None
|
|
853
|
+
"""dict mapping from label -> fid"""
|
|
854
|
+
|
|
855
|
+
self._unseen = None
|
|
856
|
+
"""dict mapping from fname -> fid"""
|
|
857
|
+
|
|
858
|
+
if alwayson_features:
|
|
859
|
+
self._alwayson = dict((label, i+self._length)
|
|
860
|
+
for (i, label) in enumerate(labels))
|
|
861
|
+
self._length += len(self._alwayson)
|
|
862
|
+
|
|
863
|
+
if unseen_features:
|
|
864
|
+
fnames = set(fname for (fname, fval, label) in mapping)
|
|
865
|
+
self._unseen = dict((fname, i+self._length)
|
|
866
|
+
for (i, fname) in enumerate(fnames))
|
|
867
|
+
self._length += len(fnames)
|
|
868
|
+
|
|
869
|
+
def encode(self, featureset, label):
|
|
870
|
+
# Inherit docs.
|
|
871
|
+
encoding = []
|
|
872
|
+
|
|
873
|
+
# Convert input-features to joint-features:
|
|
874
|
+
for fname, fval in featureset.items():
|
|
875
|
+
if isinstance(fval, (integer_types, float)):
|
|
876
|
+
# Known feature name & value:
|
|
877
|
+
if (fname, type(fval), label) in self._mapping:
|
|
878
|
+
encoding.append((self._mapping[fname, type(fval),
|
|
879
|
+
label], fval))
|
|
880
|
+
else:
|
|
881
|
+
# Known feature name & value:
|
|
882
|
+
if (fname, fval, label) in self._mapping:
|
|
883
|
+
encoding.append((self._mapping[fname, fval, label], 1))
|
|
884
|
+
|
|
885
|
+
# Otherwise, we might want to fire an "unseen-value feature".
|
|
886
|
+
elif self._unseen:
|
|
887
|
+
# Have we seen this fname/fval combination with any label?
|
|
888
|
+
for label2 in self._labels:
|
|
889
|
+
if (fname, fval, label2) in self._mapping:
|
|
890
|
+
break # we've seen this fname/fval combo
|
|
891
|
+
# We haven't -- fire the unseen-value feature
|
|
892
|
+
else:
|
|
893
|
+
if fname in self._unseen:
|
|
894
|
+
encoding.append((self._unseen[fname], 1))
|
|
895
|
+
|
|
896
|
+
|
|
897
|
+
# Add always-on features:
|
|
898
|
+
if self._alwayson and label in self._alwayson:
|
|
899
|
+
encoding.append((self._alwayson[label], 1))
|
|
900
|
+
|
|
901
|
+
return encoding
|
|
902
|
+
|
|
903
|
+
def describe(self, f_id):
|
|
904
|
+
# Inherit docs.
|
|
905
|
+
if not isinstance(f_id, integer_types):
|
|
906
|
+
raise TypeError('describe() expected an int')
|
|
907
|
+
try:
|
|
908
|
+
self._inv_mapping
|
|
909
|
+
except AttributeError:
|
|
910
|
+
self._inv_mapping = [-1]*len(self._mapping)
|
|
911
|
+
for (info, i) in self._mapping.items():
|
|
912
|
+
self._inv_mapping[i] = info
|
|
913
|
+
|
|
914
|
+
if f_id < len(self._mapping):
|
|
915
|
+
(fname, fval, label) = self._inv_mapping[f_id]
|
|
916
|
+
return '%s==%r and label is %r' % (fname, fval, label)
|
|
917
|
+
elif self._alwayson and f_id in self._alwayson.values():
|
|
918
|
+
for (label, f_id2) in self._alwayson.items():
|
|
919
|
+
if f_id == f_id2:
|
|
920
|
+
return 'label is %r' % label
|
|
921
|
+
elif self._unseen and f_id in self._unseen.values():
|
|
922
|
+
for (fname, f_id2) in self._unseen.items():
|
|
923
|
+
if f_id == f_id2:
|
|
924
|
+
return '%s is unseen' % fname
|
|
925
|
+
else:
|
|
926
|
+
raise ValueError('Bad feature id')
|
|
927
|
+
|
|
928
|
+
def labels(self):
|
|
929
|
+
# Inherit docs.
|
|
930
|
+
return self._labels
|
|
931
|
+
|
|
932
|
+
def length(self):
|
|
933
|
+
# Inherit docs.
|
|
934
|
+
return self._length
|
|
935
|
+
|
|
936
|
+
@classmethod
|
|
937
|
+
def train(cls, train_toks, count_cutoff=0, labels=None, **options):
|
|
938
|
+
"""
|
|
939
|
+
Construct and return new feature encoding, based on a given
|
|
940
|
+
training corpus ``train_toks``. See the class description
|
|
941
|
+
``TypedMaxentFeatureEncoding`` for a description of the
|
|
942
|
+
joint-features that will be included in this encoding.
|
|
943
|
+
|
|
944
|
+
Note: recognized feature values types are (int, float), over
|
|
945
|
+
types are interpreted as regular binary features.
|
|
946
|
+
|
|
947
|
+
:type train_toks: list(tuple(dict, str))
|
|
948
|
+
:param train_toks: Training data, represented as a list of
|
|
949
|
+
pairs, the first member of which is a feature dictionary,
|
|
950
|
+
and the second of which is a classification label.
|
|
951
|
+
|
|
952
|
+
:type count_cutoff: int
|
|
953
|
+
:param count_cutoff: A cutoff value that is used to discard
|
|
954
|
+
rare joint-features. If a joint-feature's value is 1
|
|
955
|
+
fewer than ``count_cutoff`` times in the training corpus,
|
|
956
|
+
then that joint-feature is not included in the generated
|
|
957
|
+
encoding.
|
|
958
|
+
|
|
959
|
+
:type labels: list
|
|
960
|
+
:param labels: A list of labels that should be used by the
|
|
961
|
+
classifier. If not specified, then the set of labels
|
|
962
|
+
attested in ``train_toks`` will be used.
|
|
963
|
+
|
|
964
|
+
:param options: Extra parameters for the constructor, such as
|
|
965
|
+
``unseen_features`` and ``alwayson_features``.
|
|
966
|
+
"""
|
|
967
|
+
mapping = {} # maps (fname, fval, label) -> fid
|
|
968
|
+
seen_labels = set() # The set of labels we've encountered
|
|
969
|
+
count = defaultdict(int) # maps (fname, fval) -> count
|
|
970
|
+
|
|
971
|
+
for (tok, label) in train_toks:
|
|
972
|
+
if labels and label not in labels:
|
|
973
|
+
raise ValueError('Unexpected label %s' % label)
|
|
974
|
+
seen_labels.add(label)
|
|
975
|
+
|
|
976
|
+
# Record each of the features.
|
|
977
|
+
for (fname, fval) in tok.items():
|
|
978
|
+
if type(fval) in (int, float):
|
|
979
|
+
fval = type(fval)
|
|
980
|
+
# If a count cutoff is given, then only add a joint
|
|
981
|
+
# feature once the corresponding (fname, fval, label)
|
|
982
|
+
# tuple exceeds that cutoff.
|
|
983
|
+
count[fname, fval] += 1
|
|
984
|
+
if count[fname, fval] >= count_cutoff:
|
|
985
|
+
if (fname, fval, label) not in mapping:
|
|
986
|
+
mapping[fname, fval, label] = len(mapping)
|
|
987
|
+
|
|
988
|
+
if labels is None:
|
|
989
|
+
labels = seen_labels
|
|
990
|
+
return cls(labels, mapping, **options)
|
|
991
|
+
|
|
992
|
+
|
|
993
|
+
|
|
994
|
+
|
|
995
|
+
######################################################################
|
|
996
|
+
#{ Classifier Trainer: Generalized Iterative Scaling
|
|
997
|
+
######################################################################
|
|
998
|
+
|
|
999
|
+
def train_maxent_classifier_with_gis(train_toks, trace=3, encoding=None,
|
|
1000
|
+
labels=None, **cutoffs):
|
|
1001
|
+
"""
|
|
1002
|
+
Train a new ``ConditionalExponentialClassifier``, using the given
|
|
1003
|
+
training samples, using the Generalized Iterative Scaling
|
|
1004
|
+
algorithm. This ``ConditionalExponentialClassifier`` will encode
|
|
1005
|
+
the model that maximizes entropy from all the models that are
|
|
1006
|
+
empirically consistent with ``train_toks``.
|
|
1007
|
+
|
|
1008
|
+
:see: ``train_maxent_classifier()`` for parameter descriptions.
|
|
1009
|
+
"""
|
|
1010
|
+
cutoffs.setdefault('max_iter', 100)
|
|
1011
|
+
cutoffchecker = CutoffChecker(cutoffs)
|
|
1012
|
+
|
|
1013
|
+
# Construct an encoding from the training data.
|
|
1014
|
+
if encoding is None:
|
|
1015
|
+
encoding = GISEncoding.train(train_toks, labels=labels)
|
|
1016
|
+
|
|
1017
|
+
if not hasattr(encoding, 'C'):
|
|
1018
|
+
raise TypeError('The GIS algorithm requires an encoding that '
|
|
1019
|
+
'defines C (e.g., GISEncoding).')
|
|
1020
|
+
|
|
1021
|
+
# Cinv is the inverse of the sum of each joint feature vector.
|
|
1022
|
+
# This controls the learning rate: higher Cinv (or lower C) gives
|
|
1023
|
+
# faster learning.
|
|
1024
|
+
Cinv = 1.0/encoding.C
|
|
1025
|
+
|
|
1026
|
+
# Count how many times each feature occurs in the training data.
|
|
1027
|
+
empirical_fcount = calculate_empirical_fcount(train_toks, encoding)
|
|
1028
|
+
|
|
1029
|
+
# Check for any features that are not attested in train_toks.
|
|
1030
|
+
unattested = set(numpy.nonzero(empirical_fcount == 0)[0])
|
|
1031
|
+
|
|
1032
|
+
# Build the classifier. Start with weight=0 for each attested
|
|
1033
|
+
# feature, and weight=-infinity for each unattested feature.
|
|
1034
|
+
weights = numpy.zeros(len(empirical_fcount), 'd')
|
|
1035
|
+
for fid in unattested:
|
|
1036
|
+
weights[fid] = numpy.NINF
|
|
1037
|
+
classifier = ConditionalExponentialClassifier(encoding, weights)
|
|
1038
|
+
|
|
1039
|
+
# Take the log of the empirical fcount.
|
|
1040
|
+
log_empirical_fcount = numpy.log2(empirical_fcount)
|
|
1041
|
+
del empirical_fcount
|
|
1042
|
+
|
|
1043
|
+
if trace > 0:
|
|
1044
|
+
print(' ==> Training (%d iterations)' % cutoffs['max_iter'])
|
|
1045
|
+
if trace > 2:
|
|
1046
|
+
print()
|
|
1047
|
+
print(' Iteration Log Likelihood Accuracy')
|
|
1048
|
+
print(' ---------------------------------------')
|
|
1049
|
+
|
|
1050
|
+
# Train the classifier.
|
|
1051
|
+
try:
|
|
1052
|
+
while True:
|
|
1053
|
+
if trace > 2:
|
|
1054
|
+
ll = cutoffchecker.ll or log_likelihood(classifier, train_toks)
|
|
1055
|
+
acc = cutoffchecker.acc or accuracy(classifier, train_toks)
|
|
1056
|
+
iternum = cutoffchecker.iter
|
|
1057
|
+
print(' %9d %14.5f %9.3f' % (iternum, ll, acc))
|
|
1058
|
+
|
|
1059
|
+
# Use the model to estimate the number of times each
|
|
1060
|
+
# feature should occur in the training data.
|
|
1061
|
+
estimated_fcount = calculate_estimated_fcount(
|
|
1062
|
+
classifier, train_toks, encoding)
|
|
1063
|
+
|
|
1064
|
+
# Take the log of estimated fcount (avoid taking log(0).)
|
|
1065
|
+
for fid in unattested:
|
|
1066
|
+
estimated_fcount[fid] += 1
|
|
1067
|
+
log_estimated_fcount = numpy.log2(estimated_fcount)
|
|
1068
|
+
del estimated_fcount
|
|
1069
|
+
|
|
1070
|
+
# Update the classifier weights
|
|
1071
|
+
weights = classifier.weights()
|
|
1072
|
+
weights += (log_empirical_fcount - log_estimated_fcount) * Cinv
|
|
1073
|
+
classifier.set_weights(weights)
|
|
1074
|
+
|
|
1075
|
+
# Check the log-likelihood & accuracy cutoffs.
|
|
1076
|
+
if cutoffchecker.check(classifier, train_toks):
|
|
1077
|
+
break
|
|
1078
|
+
|
|
1079
|
+
except KeyboardInterrupt:
|
|
1080
|
+
print(' Training stopped: keyboard interrupt')
|
|
1081
|
+
except:
|
|
1082
|
+
raise
|
|
1083
|
+
|
|
1084
|
+
if trace > 2:
|
|
1085
|
+
ll = log_likelihood(classifier, train_toks)
|
|
1086
|
+
acc = accuracy(classifier, train_toks)
|
|
1087
|
+
print(' Final %14.5f %9.3f' % (ll, acc))
|
|
1088
|
+
|
|
1089
|
+
# Return the classifier.
|
|
1090
|
+
return classifier
|
|
1091
|
+
|
|
1092
|
+
def calculate_empirical_fcount(train_toks, encoding):
|
|
1093
|
+
fcount = numpy.zeros(encoding.length(), 'd')
|
|
1094
|
+
|
|
1095
|
+
for tok, label in train_toks:
|
|
1096
|
+
for (index, val) in encoding.encode(tok, label):
|
|
1097
|
+
fcount[index] += val
|
|
1098
|
+
|
|
1099
|
+
return fcount
|
|
1100
|
+
|
|
1101
|
+
def calculate_estimated_fcount(classifier, train_toks, encoding):
|
|
1102
|
+
fcount = numpy.zeros(encoding.length(), 'd')
|
|
1103
|
+
|
|
1104
|
+
for tok, label in train_toks:
|
|
1105
|
+
pdist = classifier.prob_classify(tok)
|
|
1106
|
+
for label in pdist.samples():
|
|
1107
|
+
prob = pdist.prob(label)
|
|
1108
|
+
for (fid, fval) in encoding.encode(tok, label):
|
|
1109
|
+
fcount[fid] += prob*fval
|
|
1110
|
+
|
|
1111
|
+
return fcount
|
|
1112
|
+
|
|
1113
|
+
|
|
1114
|
+
######################################################################
|
|
1115
|
+
#{ Classifier Trainer: Improved Iterative Scaling
|
|
1116
|
+
######################################################################
|
|
1117
|
+
|
|
1118
|
+
def train_maxent_classifier_with_iis(train_toks, trace=3, encoding=None,
|
|
1119
|
+
labels=None, **cutoffs):
|
|
1120
|
+
"""
|
|
1121
|
+
Train a new ``ConditionalExponentialClassifier``, using the given
|
|
1122
|
+
training samples, using the Improved Iterative Scaling algorithm.
|
|
1123
|
+
This ``ConditionalExponentialClassifier`` will encode the model
|
|
1124
|
+
that maximizes entropy from all the models that are empirically
|
|
1125
|
+
consistent with ``train_toks``.
|
|
1126
|
+
|
|
1127
|
+
:see: ``train_maxent_classifier()`` for parameter descriptions.
|
|
1128
|
+
"""
|
|
1129
|
+
cutoffs.setdefault('max_iter', 100)
|
|
1130
|
+
cutoffchecker = CutoffChecker(cutoffs)
|
|
1131
|
+
|
|
1132
|
+
# Construct an encoding from the training data.
|
|
1133
|
+
if encoding is None:
|
|
1134
|
+
encoding = BinaryMaxentFeatureEncoding.train(train_toks, labels=labels)
|
|
1135
|
+
|
|
1136
|
+
# Count how many times each feature occurs in the training data.
|
|
1137
|
+
empirical_ffreq = (calculate_empirical_fcount(train_toks, encoding) /
|
|
1138
|
+
len(train_toks))
|
|
1139
|
+
|
|
1140
|
+
# Find the nf map, and related variables nfarray and nfident.
|
|
1141
|
+
# nf is the sum of the features for a given labeled text.
|
|
1142
|
+
# nfmap compresses this sparse set of values to a dense list.
|
|
1143
|
+
# nfarray performs the reverse operation. nfident is
|
|
1144
|
+
# nfarray multiplied by an identity matrix.
|
|
1145
|
+
nfmap = calculate_nfmap(train_toks, encoding)
|
|
1146
|
+
nfarray = numpy.array(sorted(nfmap, key=nfmap.__getitem__), 'd')
|
|
1147
|
+
nftranspose = numpy.reshape(nfarray, (len(nfarray), 1))
|
|
1148
|
+
|
|
1149
|
+
# Check for any features that are not attested in train_toks.
|
|
1150
|
+
unattested = set(numpy.nonzero(empirical_ffreq == 0)[0])
|
|
1151
|
+
|
|
1152
|
+
# Build the classifier. Start with weight=0 for each attested
|
|
1153
|
+
# feature, and weight=-infinity for each unattested feature.
|
|
1154
|
+
weights = numpy.zeros(len(empirical_ffreq), 'd')
|
|
1155
|
+
for fid in unattested:
|
|
1156
|
+
weights[fid] = numpy.NINF
|
|
1157
|
+
classifier = ConditionalExponentialClassifier(encoding, weights)
|
|
1158
|
+
|
|
1159
|
+
if trace > 0:
|
|
1160
|
+
print(' ==> Training (%d iterations)' % cutoffs['max_iter'])
|
|
1161
|
+
if trace > 2:
|
|
1162
|
+
print()
|
|
1163
|
+
print(' Iteration Log Likelihood Accuracy')
|
|
1164
|
+
print(' ---------------------------------------')
|
|
1165
|
+
|
|
1166
|
+
# Train the classifier.
|
|
1167
|
+
try:
|
|
1168
|
+
while True:
|
|
1169
|
+
if trace > 2:
|
|
1170
|
+
ll = cutoffchecker.ll or log_likelihood(classifier, train_toks)
|
|
1171
|
+
acc = cutoffchecker.acc or accuracy(classifier, train_toks)
|
|
1172
|
+
iternum = cutoffchecker.iter
|
|
1173
|
+
print(' %9d %14.5f %9.3f' % (iternum, ll, acc))
|
|
1174
|
+
|
|
1175
|
+
# Calculate the deltas for this iteration, using Newton's method.
|
|
1176
|
+
deltas = calculate_deltas(
|
|
1177
|
+
train_toks, classifier, unattested, empirical_ffreq,
|
|
1178
|
+
nfmap, nfarray, nftranspose, encoding)
|
|
1179
|
+
|
|
1180
|
+
# Use the deltas to update our weights.
|
|
1181
|
+
weights = classifier.weights()
|
|
1182
|
+
weights += deltas
|
|
1183
|
+
classifier.set_weights(weights)
|
|
1184
|
+
|
|
1185
|
+
# Check the log-likelihood & accuracy cutoffs.
|
|
1186
|
+
if cutoffchecker.check(classifier, train_toks):
|
|
1187
|
+
break
|
|
1188
|
+
|
|
1189
|
+
except KeyboardInterrupt:
|
|
1190
|
+
print(' Training stopped: keyboard interrupt')
|
|
1191
|
+
except:
|
|
1192
|
+
raise
|
|
1193
|
+
|
|
1194
|
+
|
|
1195
|
+
if trace > 2:
|
|
1196
|
+
ll = log_likelihood(classifier, train_toks)
|
|
1197
|
+
acc = accuracy(classifier, train_toks)
|
|
1198
|
+
print(' Final %14.5f %9.3f' % (ll, acc))
|
|
1199
|
+
|
|
1200
|
+
# Return the classifier.
|
|
1201
|
+
return classifier
|
|
1202
|
+
|
|
1203
|
+
def calculate_nfmap(train_toks, encoding):
|
|
1204
|
+
"""
|
|
1205
|
+
Construct a map that can be used to compress ``nf`` (which is
|
|
1206
|
+
typically sparse).
|
|
1207
|
+
|
|
1208
|
+
*nf(feature_vector)* is the sum of the feature values for
|
|
1209
|
+
*feature_vector*.
|
|
1210
|
+
|
|
1211
|
+
This represents the number of features that are active for a
|
|
1212
|
+
given labeled text. This method finds all values of *nf(t)*
|
|
1213
|
+
that are attested for at least one token in the given list of
|
|
1214
|
+
training tokens; and constructs a dictionary mapping these
|
|
1215
|
+
attested values to a continuous range *0...N*. For example,
|
|
1216
|
+
if the only values of *nf()* that were attested were 3, 5, and
|
|
1217
|
+
7, then ``_nfmap`` might return the dictionary ``{3:0, 5:1, 7:2}``.
|
|
1218
|
+
|
|
1219
|
+
:return: A map that can be used to compress ``nf`` to a dense
|
|
1220
|
+
vector.
|
|
1221
|
+
:rtype: dict(int -> int)
|
|
1222
|
+
"""
|
|
1223
|
+
# Map from nf to indices. This allows us to use smaller arrays.
|
|
1224
|
+
nfset = set()
|
|
1225
|
+
for tok, _ in train_toks:
|
|
1226
|
+
for label in encoding.labels():
|
|
1227
|
+
nfset.add(sum(val for (id, val) in encoding.encode(tok, label)))
|
|
1228
|
+
return dict((nf, i) for (i, nf) in enumerate(nfset))
|
|
1229
|
+
|
|
1230
|
+
def calculate_deltas(train_toks, classifier, unattested, ffreq_empirical,
|
|
1231
|
+
nfmap, nfarray, nftranspose, encoding):
|
|
1232
|
+
"""
|
|
1233
|
+
Calculate the update values for the classifier weights for
|
|
1234
|
+
this iteration of IIS. These update weights are the value of
|
|
1235
|
+
``delta`` that solves the equation::
|
|
1236
|
+
|
|
1237
|
+
ffreq_empirical[i]
|
|
1238
|
+
=
|
|
1239
|
+
SUM[fs,l] (classifier.prob_classify(fs).prob(l) *
|
|
1240
|
+
feature_vector(fs,l)[i] *
|
|
1241
|
+
exp(delta[i] * nf(feature_vector(fs,l))))
|
|
1242
|
+
|
|
1243
|
+
Where:
|
|
1244
|
+
- *(fs,l)* is a (featureset, label) tuple from ``train_toks``
|
|
1245
|
+
- *feature_vector(fs,l)* = ``encoding.encode(fs,l)``
|
|
1246
|
+
- *nf(vector)* = ``sum([val for (id,val) in vector])``
|
|
1247
|
+
|
|
1248
|
+
This method uses Newton's method to solve this equation for
|
|
1249
|
+
*delta[i]*. In particular, it starts with a guess of
|
|
1250
|
+
``delta[i]`` = 1; and iteratively updates ``delta`` with:
|
|
1251
|
+
|
|
1252
|
+
| delta[i] -= (ffreq_empirical[i] - sum1[i])/(-sum2[i])
|
|
1253
|
+
|
|
1254
|
+
until convergence, where *sum1* and *sum2* are defined as:
|
|
1255
|
+
|
|
1256
|
+
| sum1[i](delta) = SUM[fs,l] f[i](fs,l,delta)
|
|
1257
|
+
| sum2[i](delta) = SUM[fs,l] (f[i](fs,l,delta).nf(feature_vector(fs,l)))
|
|
1258
|
+
| f[i](fs,l,delta) = (classifier.prob_classify(fs).prob(l) .
|
|
1259
|
+
| feature_vector(fs,l)[i] .
|
|
1260
|
+
| exp(delta[i] . nf(feature_vector(fs,l))))
|
|
1261
|
+
|
|
1262
|
+
Note that *sum1* and *sum2* depend on ``delta``; so they need
|
|
1263
|
+
to be re-computed each iteration.
|
|
1264
|
+
|
|
1265
|
+
The variables ``nfmap``, ``nfarray``, and ``nftranspose`` are
|
|
1266
|
+
used to generate a dense encoding for *nf(ltext)*. This
|
|
1267
|
+
allows ``_deltas`` to calculate *sum1* and *sum2* using
|
|
1268
|
+
matrices, which yields a significant performance improvement.
|
|
1269
|
+
|
|
1270
|
+
:param train_toks: The set of training tokens.
|
|
1271
|
+
:type train_toks: list(tuple(dict, str))
|
|
1272
|
+
:param classifier: The current classifier.
|
|
1273
|
+
:type classifier: ClassifierI
|
|
1274
|
+
:param ffreq_empirical: An array containing the empirical
|
|
1275
|
+
frequency for each feature. The *i*\ th element of this
|
|
1276
|
+
array is the empirical frequency for feature *i*.
|
|
1277
|
+
:type ffreq_empirical: sequence of float
|
|
1278
|
+
:param unattested: An array that is 1 for features that are
|
|
1279
|
+
not attested in the training data; and 0 for features that
|
|
1280
|
+
are attested. In other words, ``unattested[i]==0`` iff
|
|
1281
|
+
``ffreq_empirical[i]==0``.
|
|
1282
|
+
:type unattested: sequence of int
|
|
1283
|
+
:param nfmap: A map that can be used to compress ``nf`` to a dense
|
|
1284
|
+
vector.
|
|
1285
|
+
:type nfmap: dict(int -> int)
|
|
1286
|
+
:param nfarray: An array that can be used to uncompress ``nf``
|
|
1287
|
+
from a dense vector.
|
|
1288
|
+
:type nfarray: array(float)
|
|
1289
|
+
:param nftranspose: The transpose of ``nfarray``
|
|
1290
|
+
:type nftranspose: array(float)
|
|
1291
|
+
"""
|
|
1292
|
+
# These parameters control when we decide that we've
|
|
1293
|
+
# converged. It probably should be possible to set these
|
|
1294
|
+
# manually, via keyword arguments to train.
|
|
1295
|
+
NEWTON_CONVERGE = 1e-12
|
|
1296
|
+
MAX_NEWTON = 300
|
|
1297
|
+
|
|
1298
|
+
deltas = numpy.ones(encoding.length(), 'd')
|
|
1299
|
+
|
|
1300
|
+
# Precompute the A matrix:
|
|
1301
|
+
# A[nf][id] = sum ( p(fs) * p(label|fs) * f(fs,label) )
|
|
1302
|
+
# over all label,fs s.t. num_features[label,fs]=nf
|
|
1303
|
+
A = numpy.zeros((len(nfmap), encoding.length()), 'd')
|
|
1304
|
+
|
|
1305
|
+
for tok, label in train_toks:
|
|
1306
|
+
dist = classifier.prob_classify(tok)
|
|
1307
|
+
|
|
1308
|
+
for label in encoding.labels():
|
|
1309
|
+
# Generate the feature vector
|
|
1310
|
+
feature_vector = encoding.encode(tok, label)
|
|
1311
|
+
# Find the number of active features
|
|
1312
|
+
nf = sum(val for (id, val) in feature_vector)
|
|
1313
|
+
# Update the A matrix
|
|
1314
|
+
for (id, val) in feature_vector:
|
|
1315
|
+
A[nfmap[nf], id] += dist.prob(label) * val
|
|
1316
|
+
A /= len(train_toks)
|
|
1317
|
+
|
|
1318
|
+
# Iteratively solve for delta. Use the following variables:
|
|
1319
|
+
# - nf_delta[x][y] = nfarray[x] * delta[y]
|
|
1320
|
+
# - exp_nf_delta[x][y] = exp(nf[x] * delta[y])
|
|
1321
|
+
# - nf_exp_nf_delta[x][y] = nf[x] * exp(nf[x] * delta[y])
|
|
1322
|
+
# - sum1[i][nf] = sum p(fs)p(label|fs)f[i](label,fs)
|
|
1323
|
+
# exp(delta[i]nf)
|
|
1324
|
+
# - sum2[i][nf] = sum p(fs)p(label|fs)f[i](label,fs)
|
|
1325
|
+
# nf exp(delta[i]nf)
|
|
1326
|
+
for rangenum in range(MAX_NEWTON):
|
|
1327
|
+
nf_delta = numpy.outer(nfarray, deltas)
|
|
1328
|
+
exp_nf_delta = 2 ** nf_delta
|
|
1329
|
+
nf_exp_nf_delta = nftranspose * exp_nf_delta
|
|
1330
|
+
sum1 = numpy.sum(exp_nf_delta * A, axis=0)
|
|
1331
|
+
sum2 = numpy.sum(nf_exp_nf_delta * A, axis=0)
|
|
1332
|
+
|
|
1333
|
+
# Avoid division by zero.
|
|
1334
|
+
for fid in unattested:
|
|
1335
|
+
sum2[fid] += 1
|
|
1336
|
+
|
|
1337
|
+
# Update the deltas.
|
|
1338
|
+
deltas -= (ffreq_empirical - sum1) / -sum2
|
|
1339
|
+
|
|
1340
|
+
# We can stop once we converge.
|
|
1341
|
+
n_error = (numpy.sum(abs((ffreq_empirical-sum1)))/
|
|
1342
|
+
numpy.sum(abs(deltas)))
|
|
1343
|
+
if n_error < NEWTON_CONVERGE:
|
|
1344
|
+
return deltas
|
|
1345
|
+
|
|
1346
|
+
return deltas
|
|
1347
|
+
|
|
1348
|
+
######################################################################
|
|
1349
|
+
#{ Classifier Trainer: megam
|
|
1350
|
+
######################################################################
|
|
1351
|
+
|
|
1352
|
+
# [xx] possible extension: add support for using implicit file format;
|
|
1353
|
+
# this would need to put requirements on what encoding is used. But
|
|
1354
|
+
# we may need this for other maxent classifier trainers that require
|
|
1355
|
+
# implicit formats anyway.
|
|
1356
|
+
def train_maxent_classifier_with_megam(train_toks, trace=3, encoding=None,
|
|
1357
|
+
labels=None, gaussian_prior_sigma=0,
|
|
1358
|
+
**kwargs):
|
|
1359
|
+
"""
|
|
1360
|
+
Train a new ``ConditionalExponentialClassifier``, using the given
|
|
1361
|
+
training samples, using the external ``megam`` library. This
|
|
1362
|
+
``ConditionalExponentialClassifier`` will encode the model that
|
|
1363
|
+
maximizes entropy from all the models that are empirically
|
|
1364
|
+
consistent with ``train_toks``.
|
|
1365
|
+
|
|
1366
|
+
:see: ``train_maxent_classifier()`` for parameter descriptions.
|
|
1367
|
+
:see: ``nltk.classify.megam``
|
|
1368
|
+
"""
|
|
1369
|
+
|
|
1370
|
+
explicit = True
|
|
1371
|
+
bernoulli = True
|
|
1372
|
+
if 'explicit' in kwargs:
|
|
1373
|
+
explicit = kwargs['explicit']
|
|
1374
|
+
if 'bernoulli' in kwargs:
|
|
1375
|
+
bernoulli = kwargs['bernoulli']
|
|
1376
|
+
|
|
1377
|
+
# Construct an encoding from the training data.
|
|
1378
|
+
if encoding is None:
|
|
1379
|
+
# Count cutoff can also be controlled by megam with the -minfc
|
|
1380
|
+
# option. Not sure where the best place for it is.
|
|
1381
|
+
count_cutoff = kwargs.get('count_cutoff', 0)
|
|
1382
|
+
encoding = BinaryMaxentFeatureEncoding.train(train_toks, count_cutoff,
|
|
1383
|
+
labels=labels,
|
|
1384
|
+
alwayson_features=True)
|
|
1385
|
+
elif labels is not None:
|
|
1386
|
+
raise ValueError('Specify encoding or labels, not both')
|
|
1387
|
+
|
|
1388
|
+
# Write a training file for megam.
|
|
1389
|
+
try:
|
|
1390
|
+
fd, trainfile_name = tempfile.mkstemp(prefix='nltk-')
|
|
1391
|
+
with open(trainfile_name, 'w') as trainfile:
|
|
1392
|
+
write_megam_file(train_toks, encoding, trainfile,
|
|
1393
|
+
explicit=explicit, bernoulli=bernoulli)
|
|
1394
|
+
os.close(fd)
|
|
1395
|
+
except (OSError, IOError, ValueError) as e:
|
|
1396
|
+
raise ValueError('Error while creating megam training file: %s' % e)
|
|
1397
|
+
|
|
1398
|
+
# Run megam on the training file.
|
|
1399
|
+
options = []
|
|
1400
|
+
options += ['-nobias', '-repeat', '10']
|
|
1401
|
+
if explicit:
|
|
1402
|
+
options += ['-explicit']
|
|
1403
|
+
if not bernoulli:
|
|
1404
|
+
options += ['-fvals']
|
|
1405
|
+
if gaussian_prior_sigma:
|
|
1406
|
+
# Lambda is just the precision of the Gaussian prior, i.e. it's the
|
|
1407
|
+
# inverse variance, so the parameter conversion is 1.0/sigma**2.
|
|
1408
|
+
# See http://www.umiacs.umd.edu/~hal/docs/daume04cg-bfgs.pdf.
|
|
1409
|
+
inv_variance = 1.0 / gaussian_prior_sigma**2
|
|
1410
|
+
else:
|
|
1411
|
+
inv_variance = 0
|
|
1412
|
+
options += ['-lambda', '%.2f' % inv_variance, '-tune']
|
|
1413
|
+
if trace < 3:
|
|
1414
|
+
options += ['-quiet']
|
|
1415
|
+
if 'max_iter' in kwargs:
|
|
1416
|
+
options += ['-maxi', '%s' % kwargs['max_iter']]
|
|
1417
|
+
if 'll_delta' in kwargs:
|
|
1418
|
+
# [xx] this is actually a perplexity delta, not a log
|
|
1419
|
+
# likelihood delta
|
|
1420
|
+
options += ['-dpp', '%s' % abs(kwargs['ll_delta'])]
|
|
1421
|
+
if hasattr(encoding, 'cost'):
|
|
1422
|
+
options += ['-multilabel'] # each possible la
|
|
1423
|
+
options += ['multiclass', trainfile_name]
|
|
1424
|
+
stdout = call_megam(options)
|
|
1425
|
+
# print './megam_i686.opt ', ' '.join(options)
|
|
1426
|
+
# Delete the training file
|
|
1427
|
+
try:
|
|
1428
|
+
os.remove(trainfile_name)
|
|
1429
|
+
except (OSError, IOError) as e:
|
|
1430
|
+
print('Warning: unable to delete %s: %s' % (trainfile_name, e))
|
|
1431
|
+
|
|
1432
|
+
# Parse the generated weight vector.
|
|
1433
|
+
weights = parse_megam_weights(stdout, encoding.length(), explicit)
|
|
1434
|
+
|
|
1435
|
+
# Convert from base-e to base-2 weights.
|
|
1436
|
+
weights *= numpy.log2(numpy.e)
|
|
1437
|
+
|
|
1438
|
+
# Build the classifier
|
|
1439
|
+
return MaxentClassifier(encoding, weights)
|
|
1440
|
+
|
|
1441
|
+
######################################################################
|
|
1442
|
+
#{ Classifier Trainer: tadm
|
|
1443
|
+
######################################################################
|
|
1444
|
+
|
|
1445
|
+
class TadmMaxentClassifier(MaxentClassifier):
|
|
1446
|
+
@classmethod
|
|
1447
|
+
def train(cls, train_toks, **kwargs):
|
|
1448
|
+
algorithm = kwargs.get('algorithm', 'tao_lmvm')
|
|
1449
|
+
trace = kwargs.get('trace', 3)
|
|
1450
|
+
encoding = kwargs.get('encoding', None)
|
|
1451
|
+
labels = kwargs.get('labels', None)
|
|
1452
|
+
sigma = kwargs.get('gaussian_prior_sigma', 0)
|
|
1453
|
+
count_cutoff = kwargs.get('count_cutoff', 0)
|
|
1454
|
+
max_iter = kwargs.get('max_iter')
|
|
1455
|
+
ll_delta = kwargs.get('min_lldelta')
|
|
1456
|
+
|
|
1457
|
+
# Construct an encoding from the training data.
|
|
1458
|
+
if not encoding:
|
|
1459
|
+
encoding = TadmEventMaxentFeatureEncoding.train(train_toks,
|
|
1460
|
+
count_cutoff,
|
|
1461
|
+
labels=labels)
|
|
1462
|
+
|
|
1463
|
+
trainfile_fd, trainfile_name = \
|
|
1464
|
+
tempfile.mkstemp(prefix='nltk-tadm-events-', suffix='.gz')
|
|
1465
|
+
weightfile_fd, weightfile_name = \
|
|
1466
|
+
tempfile.mkstemp(prefix='nltk-tadm-weights-')
|
|
1467
|
+
|
|
1468
|
+
trainfile = gzip_open_unicode(trainfile_name, 'w')
|
|
1469
|
+
write_tadm_file(train_toks, encoding, trainfile)
|
|
1470
|
+
trainfile.close()
|
|
1471
|
+
|
|
1472
|
+
options = []
|
|
1473
|
+
options.extend(['-monitor'])
|
|
1474
|
+
options.extend(['-method', algorithm])
|
|
1475
|
+
if sigma:
|
|
1476
|
+
options.extend(['-l2', '%.6f' % sigma**2])
|
|
1477
|
+
if max_iter:
|
|
1478
|
+
options.extend(['-max_it', '%d' % max_iter])
|
|
1479
|
+
if ll_delta:
|
|
1480
|
+
options.extend(['-fatol', '%.6f' % abs(ll_delta)])
|
|
1481
|
+
options.extend(['-events_in', trainfile_name])
|
|
1482
|
+
options.extend(['-params_out', weightfile_name])
|
|
1483
|
+
if trace < 3:
|
|
1484
|
+
options.extend(['2>&1'])
|
|
1485
|
+
else:
|
|
1486
|
+
options.extend(['-summary'])
|
|
1487
|
+
|
|
1488
|
+
call_tadm(options)
|
|
1489
|
+
|
|
1490
|
+
with open(weightfile_name, 'r') as weightfile:
|
|
1491
|
+
weights = parse_tadm_weights(weightfile)
|
|
1492
|
+
|
|
1493
|
+
os.remove(trainfile_name)
|
|
1494
|
+
os.remove(weightfile_name)
|
|
1495
|
+
|
|
1496
|
+
# Convert from base-e to base-2 weights.
|
|
1497
|
+
weights *= numpy.log2(numpy.e)
|
|
1498
|
+
|
|
1499
|
+
# Build the classifier
|
|
1500
|
+
return cls(encoding, weights)
|
|
1501
|
+
|
|
1502
|
+
######################################################################
|
|
1503
|
+
#{ Demo
|
|
1504
|
+
######################################################################
|
|
1505
|
+
def demo():
|
|
1506
|
+
from nltk.classify.util import names_demo
|
|
1507
|
+
classifier = names_demo(MaxentClassifier.train)
|
|
1508
|
+
|
|
1509
|
+
if __name__ == '__main__':
|
|
1510
|
+
demo()
|