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.
Files changed (406) hide show
  1. {nltk-3.2 → nltk-3.3}/INSTALL.txt +6 -6
  2. {nltk-3.2 → nltk-3.3}/LICENSE.txt +13 -13
  3. {nltk-3.2 → nltk-3.3}/MANIFEST.in +6 -6
  4. {nltk-3.2/nltk.egg-info → nltk-3.3}/PKG-INFO +13 -5
  5. nltk-3.2/README.txt → nltk-3.3/README.md +49 -48
  6. nltk-3.3/nltk/VERSION +1 -0
  7. {nltk-3.2 → nltk-3.3}/nltk/__init__.py +185 -185
  8. {nltk-3.2 → nltk-3.3}/nltk/app/__init__.py +52 -53
  9. {nltk-3.2 → nltk-3.3}/nltk/app/chartparser_app.py +2276 -2276
  10. {nltk-3.2 → nltk-3.3}/nltk/app/chunkparser_app.py +1262 -1264
  11. {nltk-3.2 → nltk-3.3}/nltk/app/collocations_app.py +347 -350
  12. {nltk-3.2 → nltk-3.3}/nltk/app/concordance_app.py +568 -570
  13. {nltk-3.2 → nltk-3.3}/nltk/app/nemo_app.py +156 -155
  14. {nltk-3.2 → nltk-3.3}/nltk/app/rdparser_app.py +892 -894
  15. {nltk-3.2 → nltk-3.3}/nltk/app/srparser_app.py +809 -809
  16. {nltk-3.2 → nltk-3.3}/nltk/app/wordfreq_app.py +32 -32
  17. {nltk-3.2 → nltk-3.3}/nltk/app/wordnet_app.py +968 -968
  18. {nltk-3.2 → nltk-3.3}/nltk/book.py +91 -90
  19. {nltk-3.2 → nltk-3.3}/nltk/ccg/__init__.py +22 -22
  20. {nltk-3.2 → nltk-3.3}/nltk/ccg/api.py +359 -335
  21. {nltk-3.2 → nltk-3.3}/nltk/ccg/chart.py +394 -361
  22. {nltk-3.2 → nltk-3.3}/nltk/ccg/combinator.py +343 -312
  23. {nltk-3.2 → nltk-3.3}/nltk/ccg/lexicon.py +328 -290
  24. nltk-3.3/nltk/ccg/logic.py +46 -0
  25. {nltk-3.2 → nltk-3.3}/nltk/chat/__init__.py +49 -49
  26. {nltk-3.2 → nltk-3.3}/nltk/chat/eliza.py +244 -244
  27. {nltk-3.2 → nltk-3.3}/nltk/chat/iesha.py +140 -140
  28. {nltk-3.2 → nltk-3.3}/nltk/chat/rude.py +92 -92
  29. {nltk-3.2 → nltk-3.3}/nltk/chat/suntsu.py +117 -117
  30. {nltk-3.2 → nltk-3.3}/nltk/chat/util.py +122 -120
  31. {nltk-3.2 → nltk-3.3}/nltk/chat/zen.py +282 -282
  32. {nltk-3.2 → nltk-3.3}/nltk/chunk/__init__.py +190 -190
  33. {nltk-3.2 → nltk-3.3}/nltk/chunk/api.py +51 -51
  34. {nltk-3.2 → nltk-3.3}/nltk/chunk/named_entity.py +331 -331
  35. {nltk-3.2 → nltk-3.3}/nltk/chunk/regexp.py +1390 -1384
  36. {nltk-3.2 → nltk-3.3}/nltk/chunk/util.py +600 -600
  37. {nltk-3.2 → nltk-3.3}/nltk/classify/__init__.py +98 -98
  38. {nltk-3.2 → nltk-3.3}/nltk/classify/api.py +193 -193
  39. {nltk-3.2 → nltk-3.3}/nltk/classify/decisiontree.py +295 -295
  40. {nltk-3.2 → nltk-3.3}/nltk/classify/maxent.py +1510 -1492
  41. {nltk-3.2 → nltk-3.3}/nltk/classify/megam.py +179 -178
  42. {nltk-3.2 → nltk-3.3}/nltk/classify/naivebayes.py +245 -242
  43. {nltk-3.2 → nltk-3.3}/nltk/classify/positivenaivebayes.py +170 -170
  44. {nltk-3.2 → nltk-3.3}/nltk/classify/rte_classify.py +160 -191
  45. {nltk-3.2 → nltk-3.3}/nltk/classify/scikitlearn.py +153 -151
  46. {nltk-3.2 → nltk-3.3}/nltk/classify/senna.py +183 -181
  47. {nltk-3.2 → nltk-3.3}/nltk/classify/svm.py +15 -15
  48. {nltk-3.2 → nltk-3.3}/nltk/classify/tadm.py +113 -112
  49. {nltk-3.2 → nltk-3.3}/nltk/classify/textcat.py +193 -193
  50. {nltk-3.2 → nltk-3.3}/nltk/classify/util.py +324 -312
  51. {nltk-3.2 → nltk-3.3}/nltk/classify/weka.py +346 -345
  52. {nltk-3.2 → nltk-3.3}/nltk/cluster/__init__.py +86 -86
  53. {nltk-3.2 → nltk-3.3}/nltk/cluster/api.py +74 -70
  54. {nltk-3.2 → nltk-3.3}/nltk/cluster/em.py +247 -247
  55. {nltk-3.2 → nltk-3.3}/nltk/cluster/gaac.py +168 -168
  56. {nltk-3.2 → nltk-3.3}/nltk/cluster/kmeans.py +221 -221
  57. {nltk-3.2 → nltk-3.3}/nltk/cluster/util.py +304 -289
  58. nltk-3.3/nltk/collections.py +640 -0
  59. {nltk-3.2 → nltk-3.3}/nltk/collocations.py +388 -388
  60. nltk-3.3/nltk/compat.py +365 -0
  61. {nltk-3.2 → nltk-3.3}/nltk/corpus/__init__.py +341 -320
  62. {nltk-3.2 → nltk-3.3}/nltk/corpus/europarl_raw.py +44 -44
  63. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/__init__.py +147 -145
  64. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/aligned.py +115 -114
  65. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/api.py +448 -446
  66. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/bnc.py +252 -252
  67. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/bracket_parse.py +227 -227
  68. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/categorized_sents.py +179 -178
  69. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/chasen.py +140 -139
  70. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/childes.py +522 -494
  71. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/chunked.py +212 -212
  72. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/cmudict.py +97 -95
  73. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/comparative_sents.py +280 -278
  74. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/conll.py +528 -521
  75. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/crubadan.py +116 -116
  76. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/dependency.py +101 -101
  77. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/framenet.py +3059 -2093
  78. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/ieer.py +112 -111
  79. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/indian.py +85 -86
  80. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/ipipan.py +332 -331
  81. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/knbc.py +187 -187
  82. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/lin.py +156 -156
  83. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/mte.py +311 -310
  84. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/nkjp.py +429 -428
  85. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/nombank.py +421 -421
  86. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/nps_chat.py +73 -73
  87. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/opinion_lexicon.py +115 -115
  88. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/panlex_lite.py +165 -165
  89. nltk-3.3/nltk/corpus/reader/pl196x.py +292 -0
  90. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/plaintext.py +232 -233
  91. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/ppattach.py +96 -95
  92. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/propbank.py +479 -481
  93. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/pros_cons.py +128 -126
  94. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/reviews.py +330 -328
  95. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/rte.py +144 -146
  96. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/semcor.py +256 -256
  97. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/senseval.py +203 -201
  98. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/sentiwordnet.py +137 -137
  99. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/sinica_treebank.py +75 -75
  100. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/string_category.py +60 -59
  101. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/switchboard.py +119 -119
  102. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/tagged.py +295 -294
  103. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/timit.py +452 -450
  104. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/toolbox.py +68 -68
  105. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/twitter.py +155 -157
  106. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/udhr.py +80 -80
  107. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/util.py +804 -800
  108. nltk-3.3/nltk/corpus/reader/verbnet.py +595 -0
  109. nltk-3.3/nltk/corpus/reader/wordlist.py +134 -0
  110. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/wordnet.py +2108 -2036
  111. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/xmldocs.py +392 -387
  112. {nltk-3.2 → nltk-3.3}/nltk/corpus/reader/ycoe.py +243 -242
  113. {nltk-3.2 → nltk-3.3}/nltk/corpus/util.py +144 -127
  114. {nltk-3.2 → nltk-3.3}/nltk/data.py +1487 -1451
  115. {nltk-3.2 → nltk-3.3}/nltk/decorators.py +219 -215
  116. {nltk-3.2 → nltk-3.3}/nltk/downloader.py +2278 -2274
  117. {nltk-3.2 → nltk-3.3}/nltk/draw/__init__.py +27 -27
  118. {nltk-3.2 → nltk-3.3}/nltk/draw/cfg.py +774 -777
  119. {nltk-3.2 → nltk-3.3}/nltk/draw/dispersion.py +58 -58
  120. {nltk-3.2 → nltk-3.3}/nltk/draw/table.py +1098 -1098
  121. {nltk-3.2 → nltk-3.3}/nltk/draw/tree.py +963 -966
  122. {nltk-3.2 → nltk-3.3}/nltk/draw/util.py +2356 -2356
  123. {nltk-3.2 → nltk-3.3}/nltk/featstruct.py +2617 -2500
  124. {nltk-3.2 → nltk-3.3}/nltk/grammar.py +1559 -1516
  125. {nltk-3.2 → nltk-3.3}/nltk/help.py +56 -56
  126. {nltk-3.2 → nltk-3.3}/nltk/inference/__init__.py +20 -20
  127. {nltk-3.2 → nltk-3.3}/nltk/inference/api.py +597 -589
  128. {nltk-3.2 → nltk-3.3}/nltk/inference/discourse.py +612 -608
  129. {nltk-3.2 → nltk-3.3}/nltk/inference/mace.py +311 -311
  130. {nltk-3.2 → nltk-3.3}/nltk/inference/nonmonotonic.py +509 -509
  131. {nltk-3.2 → nltk-3.3}/nltk/inference/prover9.py +431 -431
  132. {nltk-3.2 → nltk-3.3}/nltk/inference/resolution.py +687 -687
  133. {nltk-3.2 → nltk-3.3}/nltk/inference/tableau.py +607 -607
  134. {nltk-3.2 → nltk-3.3}/nltk/internals.py +982 -982
  135. {nltk-3.2 → nltk-3.3}/nltk/jsontags.py +63 -63
  136. {nltk-3.2 → nltk-3.3}/nltk/lazyimport.py +142 -142
  137. {nltk-3.2 → nltk-3.3}/nltk/metrics/__init__.py +30 -29
  138. {nltk-3.2 → nltk-3.3}/nltk/metrics/agreement.py +421 -401
  139. nltk-3.3/nltk/metrics/aline.py +607 -0
  140. {nltk-3.2 → nltk-3.3}/nltk/metrics/association.py +414 -410
  141. {nltk-3.2 → nltk-3.3}/nltk/metrics/confusionmatrix.py +206 -206
  142. {nltk-3.2 → nltk-3.3}/nltk/metrics/distance.py +207 -202
  143. {nltk-3.2 → nltk-3.3}/nltk/metrics/paice.py +381 -381
  144. {nltk-3.2 → nltk-3.3}/nltk/metrics/scores.py +228 -228
  145. {nltk-3.2 → nltk-3.3}/nltk/metrics/segmentation.py +231 -232
  146. {nltk-3.2 → nltk-3.3}/nltk/metrics/spearman.py +69 -69
  147. {nltk-3.2 → nltk-3.3}/nltk/misc/__init__.py +11 -11
  148. {nltk-3.2 → nltk-3.3}/nltk/misc/babelfish.py +10 -10
  149. {nltk-3.2 → nltk-3.3}/nltk/misc/chomsky.py +135 -133
  150. {nltk-3.2 → nltk-3.3}/nltk/misc/minimalset.py +83 -83
  151. {nltk-3.2 → nltk-3.3}/nltk/misc/sort.py +157 -157
  152. {nltk-3.2 → nltk-3.3}/nltk/misc/wordfinder.py +129 -129
  153. {nltk-3.2 → nltk-3.3}/nltk/parse/__init__.py +81 -80
  154. {nltk-3.2 → nltk-3.3}/nltk/parse/api.py +66 -66
  155. {nltk-3.2 → nltk-3.3}/nltk/parse/bllip.py +282 -282
  156. {nltk-3.2 → nltk-3.3}/nltk/parse/chart.py +1682 -1681
  157. nltk-3.3/nltk/parse/corenlp.py +780 -0
  158. {nltk-3.2 → nltk-3.3}/nltk/parse/dependencygraph.py +760 -758
  159. {nltk-3.2 → nltk-3.3}/nltk/parse/earleychart.py +452 -451
  160. {nltk-3.2 → nltk-3.3}/nltk/parse/evaluate.py +129 -129
  161. {nltk-3.2 → nltk-3.3}/nltk/parse/featurechart.py +580 -578
  162. {nltk-3.2 → nltk-3.3}/nltk/parse/generate.py +87 -76
  163. {nltk-3.2 → nltk-3.3}/nltk/parse/malt.py +356 -356
  164. {nltk-3.2 → nltk-3.3}/nltk/parse/nonprojectivedependencyparser.py +783 -783
  165. {nltk-3.2 → nltk-3.3}/nltk/parse/pchart.py +522 -522
  166. {nltk-3.2 → nltk-3.3}/nltk/parse/projectivedependencyparser.py +579 -578
  167. {nltk-3.2 → nltk-3.3}/nltk/parse/recursivedescent.py +655 -656
  168. {nltk-3.2 → nltk-3.3}/nltk/parse/shiftreduce.py +458 -459
  169. {nltk-3.2 → nltk-3.3}/nltk/parse/stanford.py +431 -406
  170. {nltk-3.2 → nltk-3.3}/nltk/parse/transitionparser.py +774 -772
  171. {nltk-3.2 → nltk-3.3}/nltk/parse/util.py +231 -231
  172. {nltk-3.2 → nltk-3.3}/nltk/parse/viterbi.py +401 -401
  173. {nltk-3.2 → nltk-3.3}/nltk/probability.py +2410 -2355
  174. {nltk-3.2 → nltk-3.3}/nltk/sem/__init__.py +61 -61
  175. {nltk-3.2 → nltk-3.3}/nltk/sem/boxer.py +1261 -1261
  176. {nltk-3.2 → nltk-3.3}/nltk/sem/chat80.py +782 -782
  177. {nltk-3.2 → nltk-3.3}/nltk/sem/cooper_storage.py +118 -118
  178. {nltk-3.2 → nltk-3.3}/nltk/sem/drt.py +1258 -1257
  179. {nltk-3.2 → nltk-3.3}/nltk/sem/drt_glue_demo.py +485 -489
  180. {nltk-3.2 → nltk-3.3}/nltk/sem/evaluate.py +790 -784
  181. {nltk-3.2 → nltk-3.3}/nltk/sem/glue.py +696 -694
  182. {nltk-3.2 → nltk-3.3}/nltk/sem/hole.py +386 -384
  183. {nltk-3.2 → nltk-3.3}/nltk/sem/lfg.py +210 -208
  184. {nltk-3.2 → nltk-3.3}/nltk/sem/linearlogic.py +451 -449
  185. {nltk-3.2 → nltk-3.3}/nltk/sem/logic.py +1910 -1910
  186. {nltk-3.2 → nltk-3.3}/nltk/sem/relextract.py +476 -483
  187. {nltk-3.2 → nltk-3.3}/nltk/sem/skolemize.py +101 -101
  188. {nltk-3.2 → nltk-3.3}/nltk/sem/util.py +249 -249
  189. {nltk-3.2 → nltk-3.3}/nltk/sentiment/__init__.py +14 -14
  190. {nltk-3.2 → nltk-3.3}/nltk/sentiment/sentiment_analyzer.py +229 -229
  191. {nltk-3.2 → nltk-3.3}/nltk/sentiment/util.py +762 -752
  192. {nltk-3.2 → nltk-3.3}/nltk/sentiment/vader.py +443 -445
  193. {nltk-3.2 → nltk-3.3}/nltk/stem/__init__.py +31 -33
  194. {nltk-3.2 → nltk-3.3}/nltk/stem/api.py +28 -25
  195. nltk-3.3/nltk/stem/arlstem.py +369 -0
  196. {nltk-3.2 → nltk-3.3}/nltk/stem/isri.py +345 -345
  197. {nltk-3.2 → nltk-3.3}/nltk/stem/lancaster.py +337 -310
  198. nltk-3.3/nltk/stem/porter.py +710 -0
  199. {nltk-3.2 → nltk-3.3}/nltk/stem/regexp.py +61 -61
  200. {nltk-3.2 → nltk-3.3}/nltk/stem/rslp.py +142 -142
  201. {nltk-3.2 → nltk-3.3}/nltk/stem/snowball.py +4236 -3712
  202. {nltk-3.2 → nltk-3.3}/nltk/stem/util.py +22 -12
  203. {nltk-3.2 → nltk-3.3}/nltk/stem/wordnet.py +51 -51
  204. {nltk-3.2 → nltk-3.3}/nltk/tag/__init__.py +152 -127
  205. {nltk-3.2 → nltk-3.3}/nltk/tag/api.py +86 -81
  206. {nltk-3.2 → nltk-3.3}/nltk/tag/brill.py +424 -426
  207. {nltk-3.2 → nltk-3.3}/nltk/tag/brill_trainer.py +608 -608
  208. {nltk-3.2 → nltk-3.3}/nltk/tag/crf.py +204 -200
  209. {nltk-3.2 → nltk-3.3}/nltk/tag/hmm.py +1277 -1277
  210. {nltk-3.2 → nltk-3.3}/nltk/tag/hunpos.py +132 -132
  211. {nltk-3.2 → nltk-3.3}/nltk/tag/mapping.py +101 -101
  212. {nltk-3.2 → nltk-3.3}/nltk/tag/perceptron.py +329 -314
  213. {nltk-3.2 → nltk-3.3}/nltk/tag/senna.py +147 -145
  214. {nltk-3.2 → nltk-3.3}/nltk/tag/sequential.py +740 -736
  215. {nltk-3.2 → nltk-3.3}/nltk/tag/stanford.py +219 -196
  216. {nltk-3.2 → nltk-3.3}/nltk/tag/tnt.py +595 -604
  217. {nltk-3.2 → nltk-3.3}/nltk/tag/util.py +72 -72
  218. {nltk-3.2 → nltk-3.3}/nltk/tbl/__init__.py +28 -28
  219. {nltk-3.2 → nltk-3.3}/nltk/tbl/api.py +1 -1
  220. {nltk-3.2 → nltk-3.3}/nltk/tbl/demo.py +366 -366
  221. {nltk-3.2 → nltk-3.3}/nltk/tbl/erroranalysis.py +39 -39
  222. {nltk-3.2 → nltk-3.3}/nltk/tbl/feature.py +259 -261
  223. {nltk-3.2 → nltk-3.3}/nltk/tbl/rule.py +321 -321
  224. {nltk-3.2 → nltk-3.3}/nltk/tbl/template.py +303 -308
  225. {nltk-3.2 → nltk-3.3}/nltk/test/__init__.py +18 -18
  226. {nltk-3.2 → nltk-3.3}/nltk/test/all.py +24 -23
  227. {nltk-3.2 → nltk-3.3}/nltk/test/bleu.doctest +14 -14
  228. {nltk-3.2 → nltk-3.3}/nltk/test/bnc.doctest +60 -60
  229. {nltk-3.2 → nltk-3.3}/nltk/test/ccg.doctest +376 -395
  230. nltk-3.3/nltk/test/ccg_semantics.doctest +553 -0
  231. {nltk-3.2 → nltk-3.3}/nltk/test/chat80.doctest +234 -234
  232. {nltk-3.2 → nltk-3.3}/nltk/test/childes.doctest +184 -184
  233. {nltk-3.2 → nltk-3.3}/nltk/test/childes_fixt.py +14 -13
  234. {nltk-3.2 → nltk-3.3}/nltk/test/chunk.doctest +373 -373
  235. {nltk-3.2 → nltk-3.3}/nltk/test/classify.doctest +185 -185
  236. {nltk-3.2 → nltk-3.3}/nltk/test/classify_fixt.py +11 -10
  237. nltk-3.3/nltk/test/collections.doctest +20 -0
  238. {nltk-3.2 → nltk-3.3}/nltk/test/collocations.doctest +276 -276
  239. {nltk-3.2 → nltk-3.3}/nltk/test/compat.doctest +134 -138
  240. {nltk-3.2 → nltk-3.3}/nltk/test/compat_fixt.py +9 -8
  241. nltk-3.3/nltk/test/concordance.doctest +55 -0
  242. {nltk-3.2 → nltk-3.3}/nltk/test/corpus.doctest +2200 -2212
  243. {nltk-3.2 → nltk-3.3}/nltk/test/corpus_fixt.py +3 -3
  244. {nltk-3.2 → nltk-3.3}/nltk/test/crubadan.doctest +65 -65
  245. {nltk-3.2 → nltk-3.3}/nltk/test/data.doctest +379 -379
  246. {nltk-3.2 → nltk-3.3}/nltk/test/dependency.doctest +241 -241
  247. {nltk-3.2 → nltk-3.3}/nltk/test/discourse.doctest +546 -546
  248. {nltk-3.2 → nltk-3.3}/nltk/test/discourse_fixt.py +14 -13
  249. {nltk-3.2 → nltk-3.3}/nltk/test/doctest_nose_plugin.py +159 -156
  250. {nltk-3.2 → nltk-3.3}/nltk/test/drt.doctest +517 -517
  251. {nltk-3.2 → nltk-3.3}/nltk/test/featgram.doctest +607 -607
  252. {nltk-3.2 → nltk-3.3}/nltk/test/featstruct.doctest +1229 -1230
  253. {nltk-3.2 → nltk-3.3}/nltk/test/framenet.doctest +288 -239
  254. {nltk-3.2 → nltk-3.3}/nltk/test/generate.doctest +67 -67
  255. {nltk-3.2 → nltk-3.3}/nltk/test/gensim.doctest +140 -141
  256. {nltk-3.2 → nltk-3.3}/nltk/test/gensim_fixt.py +10 -9
  257. {nltk-3.2 → nltk-3.3}/nltk/test/gluesemantics.doctest +384 -386
  258. {nltk-3.2 → nltk-3.3}/nltk/test/gluesemantics_malt.doctest +68 -68
  259. {nltk-3.2 → nltk-3.3}/nltk/test/gluesemantics_malt_fixt.py +12 -11
  260. {nltk-3.2 → nltk-3.3}/nltk/test/grammar.doctest +48 -48
  261. {nltk-3.2 → nltk-3.3}/nltk/test/grammartestsuites.doctest +109 -109
  262. {nltk-3.2 → nltk-3.3}/nltk/test/index.doctest +100 -100
  263. {nltk-3.2 → nltk-3.3}/nltk/test/inference.doctest +534 -534
  264. {nltk-3.2 → nltk-3.3}/nltk/test/inference_fixt.py +12 -11
  265. {nltk-3.2 → nltk-3.3}/nltk/test/internals.doctest +140 -140
  266. {nltk-3.2 → nltk-3.3}/nltk/test/japanese.doctest +48 -48
  267. {nltk-3.2 → nltk-3.3}/nltk/test/logic.doctest +1098 -1098
  268. {nltk-3.2 → nltk-3.3}/nltk/test/metrics.doctest +270 -270
  269. {nltk-3.2 → nltk-3.3}/nltk/test/misc.doctest +118 -118
  270. {nltk-3.2 → nltk-3.3}/nltk/test/nonmonotonic.doctest +286 -286
  271. {nltk-3.2 → nltk-3.3}/nltk/test/nonmonotonic_fixt.py +12 -11
  272. {nltk-3.2 → nltk-3.3}/nltk/test/paice.doctest +35 -35
  273. {nltk-3.2 → nltk-3.3}/nltk/test/parse.doctest +884 -884
  274. {nltk-3.2 → nltk-3.3}/nltk/test/portuguese_en.doctest +565 -565
  275. {nltk-3.2 → nltk-3.3}/nltk/test/portuguese_en_fixt.py +16 -16
  276. {nltk-3.2 → nltk-3.3}/nltk/test/probability.doctest +304 -304
  277. {nltk-3.2 → nltk-3.3}/nltk/test/probability_fixt.py +13 -12
  278. {nltk-3.2 → nltk-3.3}/nltk/test/propbank.doctest +176 -176
  279. {nltk-3.2 → nltk-3.3}/nltk/test/relextract.doctest +263 -263
  280. {nltk-3.2 → nltk-3.3}/nltk/test/resolution.doctest +221 -221
  281. {nltk-3.2 → nltk-3.3}/nltk/test/runtests.py +86 -64
  282. {nltk-3.2 → nltk-3.3}/nltk/test/segmentation_fixt.py +10 -10
  283. {nltk-3.2 → nltk-3.3}/nltk/test/semantics.doctest +665 -665
  284. {nltk-3.2 → nltk-3.3}/nltk/test/semantics_fixt.py +7 -7
  285. {nltk-3.2 → nltk-3.3}/nltk/test/sentiment.doctest +233 -233
  286. {nltk-3.2 → nltk-3.3}/nltk/test/sentiwordnet.doctest +41 -41
  287. {nltk-3.2 → nltk-3.3}/nltk/test/simple.doctest +85 -85
  288. {nltk-3.2 → nltk-3.3}/nltk/test/stem.doctest +78 -78
  289. {nltk-3.2 → nltk-3.3}/nltk/test/tag.doctest +33 -33
  290. {nltk-3.2 → nltk-3.3}/nltk/test/tokenize.doctest +273 -166
  291. {nltk-3.2 → nltk-3.3}/nltk/test/toolbox.doctest +307 -307
  292. {nltk-3.2 → nltk-3.3}/nltk/test/translate.doctest +242 -242
  293. {nltk-3.2 → nltk-3.3}/nltk/test/translate_fixt.py +3 -3
  294. {nltk-3.2 → nltk-3.3}/nltk/test/tree.doctest +1101 -1101
  295. {nltk-3.2 → nltk-3.3}/nltk/test/treeprettyprinter.doctest +127 -127
  296. {nltk-3.2 → nltk-3.3}/nltk/test/treetransforms.doctest +156 -156
  297. nltk-3.3/nltk/test/unit/test_2x_compat.py +63 -0
  298. nltk-3.3/nltk/test/unit/test_aline.py +52 -0
  299. nltk-3.3/nltk/test/unit/test_chunk.py +56 -0
  300. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_classify.py +55 -53
  301. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_collocations.py +88 -79
  302. nltk-3.3/nltk/test/unit/test_concordance.py +98 -0
  303. nltk-3.3/nltk/test/unit/test_corenlp.py +1000 -0
  304. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_corpora.py +200 -183
  305. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_corpus_views.py +45 -45
  306. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_hmm.py +88 -88
  307. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_json2csv_corpus.py +188 -187
  308. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_naivebayes.py +26 -26
  309. nltk-3.3/nltk/test/unit/test_rte_classify.py +86 -0
  310. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_seekable_unicode_stream_reader.py +124 -124
  311. nltk-3.3/nltk/test/unit/test_senna.py +74 -0
  312. nltk-3.3/nltk/test/unit/test_stem.py +121 -0
  313. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_tag.py +20 -20
  314. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_tgrep.py +628 -626
  315. nltk-3.3/nltk/test/unit/test_tokenize.py +168 -0
  316. {nltk-3.2 → nltk-3.3}/nltk/test/unit/test_twitter_auth.py +185 -185
  317. nltk-3.3/nltk/test/unit/test_wordnet.py +134 -0
  318. nltk-3.3/nltk/test/unit/translate/test_bleu.py +244 -0
  319. nltk-3.3/nltk/test/unit/translate/test_gdfa.py +57 -0
  320. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm1.py +75 -75
  321. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm2.py +87 -87
  322. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm3.py +105 -105
  323. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm4.py +126 -126
  324. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm5.py +166 -166
  325. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_ibm_model.py +270 -270
  326. nltk-3.3/nltk/test/unit/translate/test_nist.py +37 -0
  327. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/test_stack_decoder.py +300 -300
  328. {nltk-3.2 → nltk-3.3}/nltk/test/unit/utils.py +41 -41
  329. {nltk-3.2 → nltk-3.3}/nltk/test/util.doctest +48 -48
  330. {nltk-3.2 → nltk-3.3}/nltk/test/wordnet.doctest +595 -581
  331. {nltk-3.2 → nltk-3.3}/nltk/test/wordnet_fixt.py +7 -6
  332. {nltk-3.2 → nltk-3.3}/nltk/test/wordnet_lch.doctest +53 -53
  333. {nltk-3.2 → nltk-3.3}/nltk/test/wsd.doctest +68 -68
  334. {nltk-3.2 → nltk-3.3}/nltk/text.py +663 -610
  335. {nltk-3.2 → nltk-3.3}/nltk/tgrep.py +938 -937
  336. {nltk-3.2 → nltk-3.3}/nltk/tokenize/__init__.py +130 -107
  337. {nltk-3.2 → nltk-3.3}/nltk/tokenize/api.py +77 -78
  338. {nltk-3.2 → nltk-3.3}/nltk/tokenize/casual.py +343 -342
  339. {nltk-3.2 → nltk-3.3}/nltk/tokenize/mwe.py +120 -120
  340. nltk-3.3/nltk/tokenize/nist.py +173 -0
  341. {nltk-3.2 → nltk-3.3}/nltk/tokenize/punkt.py +1620 -1601
  342. {nltk-3.2 → nltk-3.3}/nltk/tokenize/regexp.py +208 -202
  343. nltk-3.3/nltk/tokenize/repp.py +151 -0
  344. {nltk-3.2 → nltk-3.3}/nltk/tokenize/sexpr.py +140 -142
  345. {nltk-3.2 → nltk-3.3}/nltk/tokenize/simple.py +138 -136
  346. {nltk-3.2 → nltk-3.3}/nltk/tokenize/stanford.py +117 -113
  347. nltk-3.3/nltk/tokenize/stanford_segmenter.py +246 -0
  348. {nltk-3.2 → nltk-3.3}/nltk/tokenize/texttiling.py +458 -460
  349. nltk-3.3/nltk/tokenize/toktok.py +155 -0
  350. nltk-3.3/nltk/tokenize/treebank.py +363 -0
  351. nltk-3.3/nltk/tokenize/util.py +261 -0
  352. {nltk-3.2 → nltk-3.3}/nltk/toolbox.py +499 -497
  353. {nltk-3.2 → nltk-3.3}/nltk/translate/__init__.py +24 -24
  354. {nltk-3.2 → nltk-3.3}/nltk/translate/api.py +331 -321
  355. {nltk-3.2 → nltk-3.3}/nltk/translate/bleu_score.py +615 -556
  356. nltk-3.3/nltk/translate/chrf_score.py +137 -0
  357. {nltk-3.2 → nltk-3.3}/nltk/translate/gale_church.py +248 -235
  358. {nltk-3.2 → nltk-3.3}/nltk/translate/gdfa.py +135 -131
  359. nltk-3.3/nltk/translate/gleu_score.py +193 -0
  360. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm1.py +250 -241
  361. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm2.py +308 -308
  362. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm3.py +337 -337
  363. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm4.py +467 -467
  364. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm5.py +639 -639
  365. {nltk-3.2 → nltk-3.3}/nltk/translate/ibm_model.py +536 -533
  366. {nltk-3.2 → nltk-3.3}/nltk/translate/metrics.py +40 -40
  367. nltk-3.3/nltk/translate/nist_score.py +178 -0
  368. {nltk-3.2 → nltk-3.3}/nltk/translate/phrase_based.py +175 -175
  369. {nltk-3.2 → nltk-3.3}/nltk/translate/ribes_score.py +325 -325
  370. {nltk-3.2 → nltk-3.3}/nltk/translate/stack_decoder.py +499 -495
  371. {nltk-3.2 → nltk-3.3}/nltk/tree.py +1605 -1603
  372. {nltk-3.2 → nltk-3.3}/nltk/treeprettyprinter.py +564 -564
  373. {nltk-3.2 → nltk-3.3}/nltk/treetransforms.py +309 -309
  374. {nltk-3.2 → nltk-3.3}/nltk/twitter/__init__.py +28 -28
  375. {nltk-3.2 → nltk-3.3}/nltk/twitter/api.py +141 -136
  376. {nltk-3.2 → nltk-3.3}/nltk/twitter/common.py +264 -258
  377. {nltk-3.2 → nltk-3.3}/nltk/twitter/twitter_demo.py +288 -288
  378. {nltk-3.2 → nltk-3.3}/nltk/twitter/twitterclient.py +530 -530
  379. {nltk-3.2 → nltk-3.3}/nltk/twitter/util.py +147 -146
  380. nltk-3.3/nltk/util.py +751 -0
  381. {nltk-3.2 → nltk-3.3}/nltk/wsd.py +53 -53
  382. {nltk-3.2 → nltk-3.3/nltk.egg-info}/PKG-INFO +13 -5
  383. {nltk-3.2 → nltk-3.3}/nltk.egg-info/SOURCES.txt +26 -3
  384. nltk-3.3/nltk.egg-info/requires.txt +31 -0
  385. {nltk-3.2 → nltk-3.3}/setup.cfg +0 -1
  386. {nltk-3.2 → nltk-3.3}/setup.py +102 -75
  387. nltk-3.2/nltk/VERSION +0 -1
  388. nltk-3.2/nltk/compat.py +0 -680
  389. nltk-3.2/nltk/corpus/reader/pl196x.py +0 -278
  390. nltk-3.2/nltk/corpus/reader/verbnet.py +0 -408
  391. nltk-3.2/nltk/corpus/reader/wordlist.py +0 -37
  392. nltk-3.2/nltk/sentiment/vader_lexicon.txt +0 -7517
  393. nltk-3.2/nltk/six.py +0 -868
  394. nltk-3.2/nltk/stem/porter.py +0 -692
  395. nltk-3.2/nltk/test/unit/test_2x_compat.py +0 -26
  396. nltk-3.2/nltk/test/unit/test_stem.py +0 -36
  397. nltk-3.2/nltk/test/unit/test_tokenize.py +0 -23
  398. nltk-3.2/nltk/test/unit/translate/test_bleu.py +0 -84
  399. nltk-3.2/nltk/tokenize/treebank.py +0 -124
  400. nltk-3.2/nltk/tokenize/util.py +0 -90
  401. nltk-3.2/nltk/util.py +0 -1364
  402. {nltk-3.2 → nltk-3.3}/nltk/test/unit/__init__.py +0 -0
  403. {nltk-3.2 → nltk-3.3}/nltk/test/unit/translate/__init__.py +0 -0
  404. {nltk-3.2 → nltk-3.3}/nltk.egg-info/dependency_links.txt +0 -0
  405. {nltk-3.2 → nltk-3.3}/nltk.egg-info/not-zip-safe +0 -0
  406. {nltk-3.2 → nltk-3.3}/nltk.egg-info/top_level.txt +0 -0
@@ -1,608 +1,608 @@
1
- # -*- coding: utf-8 -*-
2
- # Natural Language Toolkit: Transformation-based learning
3
- #
4
- # Copyright (C) 2001-2013 NLTK Project
5
- # Author: Marcus Uneson <marcus.uneson@gmail.com>
6
- # based on previous (nltk2) version by
7
- # Christopher Maloof, Edward Loper, Steven Bird
8
- # URL: <http://nltk.org/>
9
- # For license information, see LICENSE.TXT
10
-
11
- from __future__ import print_function, division
12
-
13
- import bisect
14
- import textwrap
15
- from collections import defaultdict
16
-
17
- from nltk.tag import untag, BrillTagger
18
-
19
- ######################################################################
20
- # Brill Tagger Trainer
21
- ######################################################################
22
-
23
-
24
- class BrillTaggerTrainer(object):
25
- """
26
- A trainer for tbl taggers.
27
- """
28
- def __init__(self, initial_tagger, templates, trace=0,
29
- deterministic=None, ruleformat="str"):
30
- """
31
- Construct a Brill tagger from a baseline tagger and a
32
- set of templates
33
-
34
- :param initial_tagger: the baseline tagger
35
- :type initial_tagger: Tagger
36
- :param templates: templates to be used in training
37
- :type templates: list of Templates
38
- :param trace: verbosity level
39
- :type trace: int
40
- :param deterministic: if True, adjudicate ties deterministically
41
- :type deterministic: bool
42
- :param ruleformat: format of reported Rules
43
- :type ruleformat: str
44
- :return: An untrained BrillTagger
45
- :rtype: BrillTagger
46
- """
47
-
48
- if deterministic is None:
49
- deterministic = (trace > 0)
50
- self._initial_tagger = initial_tagger
51
- self._templates = templates
52
- self._trace = trace
53
- self._deterministic = deterministic
54
- self._ruleformat = ruleformat
55
-
56
- self._tag_positions = None
57
- """Mapping from tags to lists of positions that use that tag."""
58
-
59
- self._rules_by_position = None
60
- """Mapping from positions to the set of rules that are known
61
- to occur at that position. Position is (sentnum, wordnum).
62
- Initially, this will only contain positions where each rule
63
- applies in a helpful way; but when we examine a rule, we'll
64
- extend this list to also include positions where each rule
65
- applies in a harmful or neutral way."""
66
-
67
- self._positions_by_rule = None
68
- """Mapping from rule to position to effect, specifying the
69
- effect that each rule has on the overall score, at each
70
- position. Position is (sentnum, wordnum); and effect is
71
- -1, 0, or 1. As with _rules_by_position, this mapping starts
72
- out only containing rules with positive effects; but when
73
- we examine a rule, we'll extend this mapping to include
74
- the positions where the rule is harmful or neutral."""
75
-
76
- self._rules_by_score = None
77
- """Mapping from scores to the set of rules whose effect on the
78
- overall score is upper bounded by that score. Invariant:
79
- rulesByScore[s] will contain r iff the sum of
80
- _positions_by_rule[r] is s."""
81
-
82
- self._rule_scores = None
83
- """Mapping from rules to upper bounds on their effects on the
84
- overall score. This is the inverse mapping to _rules_by_score.
85
- Invariant: ruleScores[r] = sum(_positions_by_rule[r])"""
86
-
87
- self._first_unknown_position = None
88
- """Mapping from rules to the first position where we're unsure
89
- if the rule applies. This records the next position we
90
- need to check to see if the rule messed anything up."""
91
-
92
- # Training
93
-
94
- def train(self, train_sents, max_rules=200, min_score=2, min_acc=None):
95
- """
96
- Trains the Brill tagger on the corpus *train_sents*,
97
- producing at most *max_rules* transformations, each of which
98
- reduces the net number of errors in the corpus by at least
99
- *min_score*, and each of which has accuracy not lower than
100
- *min_acc*.
101
-
102
- #imports
103
- >>> from nltk.tbl.template import Template
104
- >>> from nltk.tag.brill import Pos, Word
105
- >>> from nltk.tag import untag, RegexpTagger, BrillTaggerTrainer
106
-
107
- #some data
108
- >>> from nltk.corpus import treebank
109
- >>> training_data = treebank.tagged_sents()[:100]
110
- >>> baseline_data = treebank.tagged_sents()[100:200]
111
- >>> gold_data = treebank.tagged_sents()[200:300]
112
- >>> testing_data = [untag(s) for s in gold_data]
113
-
114
- >>> backoff = RegexpTagger([
115
- ... (r'^-?[0-9]+(.[0-9]+)?$', 'CD'), # cardinal numbers
116
- ... (r'(The|the|A|a|An|an)$', 'AT'), # articles
117
- ... (r'.*able$', 'JJ'), # adjectives
118
- ... (r'.*ness$', 'NN'), # nouns formed from adjectives
119
- ... (r'.*ly$', 'RB'), # adverbs
120
- ... (r'.*s$', 'NNS'), # plural nouns
121
- ... (r'.*ing$', 'VBG'), # gerunds
122
- ... (r'.*ed$', 'VBD'), # past tense verbs
123
- ... (r'.*', 'NN') # nouns (default)
124
- ... ])
125
-
126
- >>> baseline = backoff #see NOTE1
127
-
128
- >>> baseline.evaluate(gold_data) #doctest: +ELLIPSIS
129
- 0.2450142...
130
-
131
- #templates
132
- >>> Template._cleartemplates() #clear any templates created in earlier tests
133
- >>> templates = [Template(Pos([-1])), Template(Pos([-1]), Word([0]))]
134
-
135
- #construct a BrillTaggerTrainer
136
- >>> tt = BrillTaggerTrainer(baseline, templates, trace=3)
137
-
138
- >>> tagger1 = tt.train(training_data, max_rules=10)
139
- TBL train (fast) (seqs: 100; tokens: 2417; tpls: 2; min score: 2; min acc: None)
140
- Finding initial useful rules...
141
- Found 845 useful rules.
142
- <BLANKLINE>
143
- B |
144
- S F r O | Score = Fixed - Broken
145
- c i o t | R Fixed = num tags changed incorrect -> correct
146
- o x k h | u Broken = num tags changed correct -> incorrect
147
- r e e e | l Other = num tags changed incorrect -> incorrect
148
- e d n r | e
149
- ------------------+-------------------------------------------------------
150
- 132 132 0 0 | AT->DT if Pos:NN@[-1]
151
- 85 85 0 0 | NN->, if Pos:NN@[-1] & Word:,@[0]
152
- 69 69 0 0 | NN->. if Pos:NN@[-1] & Word:.@[0]
153
- 51 51 0 0 | NN->IN if Pos:NN@[-1] & Word:of@[0]
154
- 47 63 16 161 | NN->IN if Pos:NNS@[-1]
155
- 33 33 0 0 | NN->TO if Pos:NN@[-1] & Word:to@[0]
156
- 26 26 0 0 | IN->. if Pos:NNS@[-1] & Word:.@[0]
157
- 24 24 0 0 | IN->, if Pos:NNS@[-1] & Word:,@[0]
158
- 22 27 5 24 | NN->-NONE- if Pos:VBD@[-1]
159
- 17 17 0 0 | NN->CC if Pos:NN@[-1] & Word:and@[0]
160
-
161
- >>> tagger1.rules()[1:3]
162
- (Rule('001', 'NN', ',', [(Pos([-1]),'NN'), (Word([0]),',')]), Rule('001', 'NN', '.', [(Pos([-1]),'NN'), (Word([0]),'.')]))
163
-
164
- >>> train_stats = tagger1.train_stats()
165
- >>> [train_stats[stat] for stat in ['initialerrors', 'finalerrors', 'rulescores']]
166
- [1775, 1269, [132, 85, 69, 51, 47, 33, 26, 24, 22, 17]]
167
-
168
- >>> tagger1.print_template_statistics(printunused=False)
169
- TEMPLATE STATISTICS (TRAIN) 2 templates, 10 rules)
170
- TRAIN ( 2417 tokens) initial 1775 0.2656 final: 1269 0.4750
171
- #ID | Score (train) | #Rules | Template
172
- --------------------------------------------
173
- 001 | 305 0.603 | 7 0.700 | Template(Pos([-1]),Word([0]))
174
- 000 | 201 0.397 | 3 0.300 | Template(Pos([-1]))
175
- <BLANKLINE>
176
- <BLANKLINE>
177
-
178
- >>> tagger1.evaluate(gold_data) # doctest: +ELLIPSIS
179
- 0.43996...
180
-
181
- >>> tagged, test_stats = tagger1.batch_tag_incremental(testing_data, gold_data)
182
-
183
- >>> tagged[33][12:] == [('foreign', 'IN'), ('debt', 'NN'), ('of', 'IN'), ('$', 'NN'), ('64', 'CD'),
184
- ... ('billion', 'NN'), ('*U*', 'NN'), ('--', 'NN'), ('the', 'DT'), ('third-highest', 'NN'), ('in', 'NN'),
185
- ... ('the', 'DT'), ('developing', 'VBG'), ('world', 'NN'), ('.', '.')]
186
- True
187
-
188
- >>> [test_stats[stat] for stat in ['initialerrors', 'finalerrors', 'rulescores']]
189
- [1855, 1376, [100, 85, 67, 58, 27, 36, 27, 16, 31, 32]]
190
-
191
- # a high-accuracy tagger
192
- >>> tagger2 = tt.train(training_data, max_rules=10, min_acc=0.99)
193
- TBL train (fast) (seqs: 100; tokens: 2417; tpls: 2; min score: 2; min acc: 0.99)
194
- Finding initial useful rules...
195
- Found 845 useful rules.
196
- <BLANKLINE>
197
- B |
198
- S F r O | Score = Fixed - Broken
199
- c i o t | R Fixed = num tags changed incorrect -> correct
200
- o x k h | u Broken = num tags changed correct -> incorrect
201
- r e e e | l Other = num tags changed incorrect -> incorrect
202
- e d n r | e
203
- ------------------+-------------------------------------------------------
204
- 132 132 0 0 | AT->DT if Pos:NN@[-1]
205
- 85 85 0 0 | NN->, if Pos:NN@[-1] & Word:,@[0]
206
- 69 69 0 0 | NN->. if Pos:NN@[-1] & Word:.@[0]
207
- 51 51 0 0 | NN->IN if Pos:NN@[-1] & Word:of@[0]
208
- 36 36 0 0 | NN->TO if Pos:NN@[-1] & Word:to@[0]
209
- 26 26 0 0 | NN->. if Pos:NNS@[-1] & Word:.@[0]
210
- 24 24 0 0 | NN->, if Pos:NNS@[-1] & Word:,@[0]
211
- 19 19 0 6 | NN->VB if Pos:TO@[-1]
212
- 18 18 0 0 | CD->-NONE- if Pos:NN@[-1] & Word:0@[0]
213
- 18 18 0 0 | NN->CC if Pos:NN@[-1] & Word:and@[0]
214
-
215
- >>> tagger2.evaluate(gold_data) # doctest: +ELLIPSIS
216
- 0.44159544...
217
- >>> tagger2.rules()[2:4]
218
- (Rule('001', 'NN', '.', [(Pos([-1]),'NN'), (Word([0]),'.')]), Rule('001', 'NN', 'IN', [(Pos([-1]),'NN'), (Word([0]),'of')]))
219
-
220
- # NOTE1: (!!FIXME) A far better baseline uses nltk.tag.UnigramTagger,
221
- # with a RegexpTagger only as backoff. For instance,
222
- # >>> baseline = UnigramTagger(baseline_data, backoff=backoff)
223
- # However, as of Nov 2013, nltk.tag.UnigramTagger does not yield consistent results
224
- # between python versions. The simplistic backoff above is a workaround to make doctests
225
- # get consistent input.
226
-
227
- :param train_sents: training data
228
- :type train_sents: list(list(tuple))
229
- :param max_rules: output at most max_rules rules
230
- :type max_rules: int
231
- :param min_score: stop training when no rules better than min_score can be found
232
- :type min_score: int
233
- :param min_acc: discard any rule with lower accuracy than min_acc
234
- :type min_acc: float or None
235
- :return: the learned tagger
236
- :rtype: BrillTagger
237
-
238
- """
239
- # FIXME: several tests are a bit too dependent on tracing format
240
- # FIXME: tests in trainer.fast and trainer.brillorig are exact duplicates
241
-
242
- # Basic idea: Keep track of the rules that apply at each position.
243
- # And keep track of the positions to which each rule applies.
244
-
245
- # Create a new copy of the training corpus, and run the
246
- # initial tagger on it. We will progressively update this
247
- # test corpus to look more like the training corpus.
248
- test_sents = [list(self._initial_tagger.tag(untag(sent)))
249
- for sent in train_sents]
250
-
251
- # Collect some statistics on the training process
252
- trainstats = {}
253
- trainstats['min_acc'] = min_acc
254
- trainstats['min_score'] = min_score
255
- trainstats['tokencount'] = sum(len(t) for t in test_sents)
256
- trainstats['sequencecount'] = len(test_sents)
257
- trainstats['templatecount'] = len(self._templates)
258
- trainstats['rulescores'] = []
259
- trainstats['initialerrors'] = sum(
260
- tag[1] != truth[1]
261
- for paired in zip(test_sents, train_sents)
262
- for (tag, truth) in zip(*paired)
263
- )
264
- trainstats['initialacc'] = 1 - trainstats['initialerrors']/trainstats['tokencount']
265
- if self._trace > 0:
266
- print("TBL train (fast) (seqs: {sequencecount}; tokens: {tokencount}; "
267
- "tpls: {templatecount}; min score: {min_score}; min acc: {min_acc})".format(**trainstats))
268
-
269
- # Initialize our mappings. This will find any errors made
270
- # by the initial tagger, and use those to generate repair
271
- # rules, which are added to the rule mappings.
272
- if self._trace:
273
- print("Finding initial useful rules...")
274
- self._init_mappings(test_sents, train_sents)
275
- if self._trace:
276
- print((" Found %d useful rules." % len(self._rule_scores)))
277
-
278
- # Let the user know what we're up to.
279
- if self._trace > 2:
280
- self._trace_header()
281
- elif self._trace == 1:
282
- print("Selecting rules...")
283
-
284
- # Repeatedly select the best rule, and add it to `rules`.
285
- rules = []
286
- try:
287
- while (len(rules) < max_rules):
288
- # Find the best rule, and add it to our rule list.
289
- rule = self._best_rule(train_sents, test_sents, min_score, min_acc)
290
- if rule:
291
- rules.append(rule)
292
- score = self._rule_scores[rule]
293
- trainstats['rulescores'].append(score)
294
- else:
295
- break # No more good rules left!
296
-
297
- # Report the rule that we found.
298
- if self._trace > 1:
299
- self._trace_rule(rule)
300
-
301
- # Apply the new rule at the relevant sites
302
- self._apply_rule(rule, test_sents)
303
-
304
- # Update _tag_positions[rule.original_tag] and
305
- # _tag_positions[rule.replacement_tag] for the affected
306
- # positions (i.e., self._positions_by_rule[rule]).
307
- self._update_tag_positions(rule)
308
-
309
- # Update rules that were affected by the change.
310
- self._update_rules(rule, train_sents, test_sents)
311
-
312
- # The user can cancel training manually:
313
- except KeyboardInterrupt:
314
- print("Training stopped manually -- %d rules found" % len(rules))
315
-
316
- # Discard our tag position mapping & rule mappings.
317
- self._clean()
318
- trainstats['finalerrors'] = trainstats['initialerrors'] - sum(trainstats['rulescores'])
319
- trainstats['finalacc'] = 1 - trainstats['finalerrors']/trainstats['tokencount']
320
- # Create and return a tagger from the rules we found.
321
- return BrillTagger(self._initial_tagger, rules, trainstats)
322
-
323
- def _init_mappings(self, test_sents, train_sents):
324
- """
325
- Initialize the tag position mapping & the rule related
326
- mappings. For each error in test_sents, find new rules that
327
- would correct them, and add them to the rule mappings.
328
- """
329
- self._tag_positions = defaultdict(list)
330
- self._rules_by_position = defaultdict(set)
331
- self._positions_by_rule = defaultdict(dict)
332
- self._rules_by_score = defaultdict(set)
333
- self._rule_scores = defaultdict(int)
334
- self._first_unknown_position = defaultdict(int)
335
- # Scan through the corpus, initializing the tag_positions
336
- # mapping and all the rule-related mappings.
337
- for sentnum, sent in enumerate(test_sents):
338
- for wordnum, (word, tag) in enumerate(sent):
339
-
340
- # Initialize tag_positions
341
- self._tag_positions[tag].append((sentnum, wordnum))
342
-
343
- # If it's an error token, update the rule-related mappings.
344
- correct_tag = train_sents[sentnum][wordnum][1]
345
- if tag != correct_tag:
346
- for rule in self._find_rules(sent, wordnum, correct_tag):
347
- self._update_rule_applies(rule, sentnum, wordnum,
348
- train_sents)
349
-
350
- def _clean(self):
351
- self._tag_positions = None
352
- self._rules_by_position = None
353
- self._positions_by_rule = None
354
- self._rules_by_score = None
355
- self._rule_scores = None
356
- self._first_unknown_position = None
357
-
358
- def _find_rules(self, sent, wordnum, new_tag):
359
- """
360
- Use the templates to find rules that apply at index *wordnum*
361
- in the sentence *sent* and generate the tag *new_tag*.
362
- """
363
- for template in self._templates:
364
- for rule in template.applicable_rules(sent, wordnum, new_tag):
365
- yield rule
366
-
367
- def _update_rule_applies(self, rule, sentnum, wordnum, train_sents):
368
- """
369
- Update the rule data tables to reflect the fact that
370
- *rule* applies at the position *(sentnum, wordnum)*.
371
- """
372
- pos = sentnum, wordnum
373
-
374
- # If the rule is already known to apply here, ignore.
375
- # (This only happens if the position's tag hasn't changed.)
376
- if pos in self._positions_by_rule[rule]:
377
- return
378
-
379
- # Update self._positions_by_rule.
380
- correct_tag = train_sents[sentnum][wordnum][1]
381
- if rule.replacement_tag == correct_tag:
382
- self._positions_by_rule[rule][pos] = 1
383
- elif rule.original_tag == correct_tag:
384
- self._positions_by_rule[rule][pos] = -1
385
- else: # was wrong, remains wrong
386
- self._positions_by_rule[rule][pos] = 0
387
-
388
- # Update _rules_by_position
389
- self._rules_by_position[pos].add(rule)
390
-
391
- # Update _rule_scores.
392
- old_score = self._rule_scores[rule]
393
- self._rule_scores[rule] += self._positions_by_rule[rule][pos]
394
-
395
- # Update _rules_by_score.
396
- self._rules_by_score[old_score].discard(rule)
397
- self._rules_by_score[self._rule_scores[rule]].add(rule)
398
-
399
- def _update_rule_not_applies(self, rule, sentnum, wordnum):
400
- """
401
- Update the rule data tables to reflect the fact that *rule*
402
- does not apply at the position *(sentnum, wordnum)*.
403
- """
404
- pos = sentnum, wordnum
405
-
406
- # Update _rule_scores.
407
- old_score = self._rule_scores[rule]
408
- self._rule_scores[rule] -= self._positions_by_rule[rule][pos]
409
-
410
- # Update _rules_by_score.
411
- self._rules_by_score[old_score].discard(rule)
412
- self._rules_by_score[self._rule_scores[rule]].add(rule)
413
-
414
- # Update _positions_by_rule
415
- del self._positions_by_rule[rule][pos]
416
- self._rules_by_position[pos].remove(rule)
417
-
418
- # Optional addition: if the rule now applies nowhere, delete
419
- # all its dictionary entries.
420
-
421
- def _best_rule(self, train_sents, test_sents, min_score, min_acc):
422
- """
423
- Find the next best rule. This is done by repeatedly taking a
424
- rule with the highest score and stepping through the corpus to
425
- see where it applies. When it makes an error (decreasing its
426
- score) it's bumped down, and we try a new rule with the
427
- highest score. When we find a rule which has the highest
428
- score *and* which has been tested against the entire corpus, we
429
- can conclude that it's the next best rule.
430
- """
431
- for max_score in sorted(self._rules_by_score.keys(), reverse=True):
432
- if len(self._rules_by_score) == 0:
433
- return None
434
- if max_score < min_score or max_score <= 0:
435
- return None
436
- best_rules = list(self._rules_by_score[max_score])
437
- if self._deterministic:
438
- best_rules.sort(key=repr)
439
- for rule in best_rules:
440
- positions = self._tag_positions[rule.original_tag]
441
-
442
- unk = self._first_unknown_position.get(rule, (0, -1))
443
- start = bisect.bisect_left(positions, unk)
444
-
445
- for i in range(start, len(positions)):
446
- sentnum, wordnum = positions[i]
447
- if rule.applies(test_sents[sentnum], wordnum):
448
- self._update_rule_applies(rule, sentnum, wordnum,
449
- train_sents)
450
- if self._rule_scores[rule] < max_score:
451
- self._first_unknown_position[rule] = (sentnum,
452
- wordnum+1)
453
- break # The update demoted the rule.
454
-
455
- if self._rule_scores[rule] == max_score:
456
- self._first_unknown_position[rule] = (len(train_sents) + 1, 0)
457
- # optimization: if no min_acc threshold given, don't bother computing accuracy
458
- if min_acc is None:
459
- return rule
460
- else:
461
- changes = self._positions_by_rule[rule].values()
462
- num_fixed = len([c for c in changes if c == 1])
463
- num_broken = len([c for c in changes if c == -1])
464
- # acc here is fixed/(fixed+broken); could also be
465
- # fixed/(fixed+broken+other) == num_fixed/len(changes)
466
- acc = num_fixed/(num_fixed+num_broken)
467
- if acc >= min_acc:
468
- return rule
469
- # else: rule too inaccurate, discard and try next
470
-
471
- # We demoted (or skipped due to < min_acc, if that was given)
472
- # all the rules with score==max_score.
473
-
474
- assert min_acc is not None or not self._rules_by_score[max_score]
475
- if not self._rules_by_score[max_score]:
476
- del self._rules_by_score[max_score]
477
-
478
- def _apply_rule(self, rule, test_sents):
479
- """
480
- Update *test_sents* by applying *rule* everywhere where its
481
- conditions are met.
482
- """
483
- update_positions = set(self._positions_by_rule[rule])
484
- new_tag = rule.replacement_tag
485
-
486
- if self._trace > 3:
487
- self._trace_apply(len(update_positions))
488
-
489
- # Update test_sents.
490
- for (sentnum, wordnum) in update_positions:
491
- text = test_sents[sentnum][wordnum][0]
492
- test_sents[sentnum][wordnum] = (text, new_tag)
493
-
494
- def _update_tag_positions(self, rule):
495
- """
496
- Update _tag_positions to reflect the changes to tags that are
497
- made by *rule*.
498
- """
499
- # Update the tag index.
500
- for pos in self._positions_by_rule[rule]:
501
- # Delete the old tag.
502
- old_tag_positions = self._tag_positions[rule.original_tag]
503
- old_index = bisect.bisect_left(old_tag_positions, pos)
504
- del old_tag_positions[old_index]
505
- # Insert the new tag.
506
- new_tag_positions = self._tag_positions[rule.replacement_tag]
507
- bisect.insort_left(new_tag_positions, pos)
508
-
509
- def _update_rules(self, rule, train_sents, test_sents):
510
- """
511
- Check if we should add or remove any rules from consideration,
512
- given the changes made by *rule*.
513
- """
514
- # Collect a list of all positions that might be affected.
515
- neighbors = set()
516
- for sentnum, wordnum in self._positions_by_rule[rule]:
517
- for template in self._templates:
518
- n = template.get_neighborhood(test_sents[sentnum], wordnum)
519
- neighbors.update([(sentnum, i) for i in n])
520
-
521
- # Update the rules at each position.
522
- num_obsolete = num_new = num_unseen = 0
523
- for sentnum, wordnum in neighbors:
524
- test_sent = test_sents[sentnum]
525
- correct_tag = train_sents[sentnum][wordnum][1]
526
-
527
- # Check if the change causes any rule at this position to
528
- # stop matching; if so, then update our rule mappings
529
- # accordingly.
530
- old_rules = set(self._rules_by_position[sentnum, wordnum])
531
- for old_rule in old_rules:
532
- if not old_rule.applies(test_sent, wordnum):
533
- num_obsolete += 1
534
- self._update_rule_not_applies(old_rule, sentnum, wordnum)
535
-
536
- # Check if the change causes our templates to propose any
537
- # new rules for this position.
538
- for template in self._templates:
539
- for new_rule in template.applicable_rules(test_sent, wordnum,
540
- correct_tag):
541
- if new_rule not in old_rules:
542
- num_new += 1
543
- if new_rule not in self._rule_scores:
544
- num_unseen += 1
545
- old_rules.add(new_rule)
546
- self._update_rule_applies(new_rule, sentnum,
547
- wordnum, train_sents)
548
-
549
- # We may have caused other rules to match here, that are
550
- # not proposed by our templates -- in particular, rules
551
- # that are harmful or neutral. We therefore need to
552
- # update any rule whose first_unknown_position is past
553
- # this rule.
554
- for new_rule, pos in self._first_unknown_position.items():
555
- if pos > (sentnum, wordnum):
556
- if new_rule not in old_rules:
557
- num_new += 1
558
- if new_rule.applies(test_sent, wordnum):
559
- self._update_rule_applies(new_rule, sentnum,
560
- wordnum, train_sents)
561
-
562
- if self._trace > 3:
563
- self._trace_update_rules(num_obsolete, num_new, num_unseen)
564
-
565
- # Tracing
566
-
567
- def _trace_header(self):
568
- print("""
569
- B |
570
- S F r O | Score = Fixed - Broken
571
- c i o t | R Fixed = num tags changed incorrect -> correct
572
- o x k h | u Broken = num tags changed correct -> incorrect
573
- r e e e | l Other = num tags changed incorrect -> incorrect
574
- e d n r | e
575
- ------------------+-------------------------------------------------------
576
- """.rstrip())
577
-
578
- def _trace_rule(self, rule):
579
- assert self._rule_scores[rule] == sum(self._positions_by_rule[rule].values())
580
-
581
- changes = self._positions_by_rule[rule].values()
582
- num_fixed = len([c for c in changes if c == 1])
583
- num_broken = len([c for c in changes if c == -1])
584
- num_other = len([c for c in changes if c == 0])
585
- score = self._rule_scores[rule]
586
-
587
- rulestr = rule.format(self._ruleformat)
588
- if self._trace > 2:
589
- print('%4d%4d%4d%4d |' % (score, num_fixed, num_broken, num_other), end=' ')
590
- print(textwrap.fill(rulestr, initial_indent=' '*20, width=79,
591
- subsequent_indent=' '*18+'| ').strip())
592
- else:
593
- print(rulestr)
594
-
595
- def _trace_apply(self, num_updates):
596
- prefix = ' '*18+'|'
597
- print(prefix)
598
- print(prefix, 'Applying rule to %d positions.' % num_updates)
599
-
600
- def _trace_update_rules(self, num_obsolete, num_new, num_unseen):
601
- prefix = ' '*18+'|'
602
- print(prefix, 'Updated rule tables:')
603
- print(prefix, (' - %d rule applications removed' % num_obsolete))
604
- print(prefix, (' - %d rule applications added (%d novel)' %
605
- (num_new, num_unseen)))
606
- print(prefix)
607
-
608
-
1
+ # -*- coding: utf-8 -*-
2
+ # Natural Language Toolkit: Transformation-based learning
3
+ #
4
+ # Copyright (C) 2001-2013 NLTK Project
5
+ # Author: Marcus Uneson <marcus.uneson@gmail.com>
6
+ # based on previous (nltk2) version by
7
+ # Christopher Maloof, Edward Loper, Steven Bird
8
+ # URL: <http://nltk.org/>
9
+ # For license information, see LICENSE.TXT
10
+
11
+ from __future__ import print_function, division
12
+
13
+ import bisect
14
+ import textwrap
15
+ from collections import defaultdict
16
+
17
+ from nltk.tag import untag, BrillTagger
18
+
19
+ ######################################################################
20
+ # Brill Tagger Trainer
21
+ ######################################################################
22
+
23
+
24
+ class BrillTaggerTrainer(object):
25
+ """
26
+ A trainer for tbl taggers.
27
+ """
28
+ def __init__(self, initial_tagger, templates, trace=0,
29
+ deterministic=None, ruleformat="str"):
30
+ """
31
+ Construct a Brill tagger from a baseline tagger and a
32
+ set of templates
33
+
34
+ :param initial_tagger: the baseline tagger
35
+ :type initial_tagger: Tagger
36
+ :param templates: templates to be used in training
37
+ :type templates: list of Templates
38
+ :param trace: verbosity level
39
+ :type trace: int
40
+ :param deterministic: if True, adjudicate ties deterministically
41
+ :type deterministic: bool
42
+ :param ruleformat: format of reported Rules
43
+ :type ruleformat: str
44
+ :return: An untrained BrillTagger
45
+ :rtype: BrillTagger
46
+ """
47
+
48
+ if deterministic is None:
49
+ deterministic = (trace > 0)
50
+ self._initial_tagger = initial_tagger
51
+ self._templates = templates
52
+ self._trace = trace
53
+ self._deterministic = deterministic
54
+ self._ruleformat = ruleformat
55
+
56
+ self._tag_positions = None
57
+ """Mapping from tags to lists of positions that use that tag."""
58
+
59
+ self._rules_by_position = None
60
+ """Mapping from positions to the set of rules that are known
61
+ to occur at that position. Position is (sentnum, wordnum).
62
+ Initially, this will only contain positions where each rule
63
+ applies in a helpful way; but when we examine a rule, we'll
64
+ extend this list to also include positions where each rule
65
+ applies in a harmful or neutral way."""
66
+
67
+ self._positions_by_rule = None
68
+ """Mapping from rule to position to effect, specifying the
69
+ effect that each rule has on the overall score, at each
70
+ position. Position is (sentnum, wordnum); and effect is
71
+ -1, 0, or 1. As with _rules_by_position, this mapping starts
72
+ out only containing rules with positive effects; but when
73
+ we examine a rule, we'll extend this mapping to include
74
+ the positions where the rule is harmful or neutral."""
75
+
76
+ self._rules_by_score = None
77
+ """Mapping from scores to the set of rules whose effect on the
78
+ overall score is upper bounded by that score. Invariant:
79
+ rulesByScore[s] will contain r iff the sum of
80
+ _positions_by_rule[r] is s."""
81
+
82
+ self._rule_scores = None
83
+ """Mapping from rules to upper bounds on their effects on the
84
+ overall score. This is the inverse mapping to _rules_by_score.
85
+ Invariant: ruleScores[r] = sum(_positions_by_rule[r])"""
86
+
87
+ self._first_unknown_position = None
88
+ """Mapping from rules to the first position where we're unsure
89
+ if the rule applies. This records the next position we
90
+ need to check to see if the rule messed anything up."""
91
+
92
+ # Training
93
+
94
+ def train(self, train_sents, max_rules=200, min_score=2, min_acc=None):
95
+ """
96
+ Trains the Brill tagger on the corpus *train_sents*,
97
+ producing at most *max_rules* transformations, each of which
98
+ reduces the net number of errors in the corpus by at least
99
+ *min_score*, and each of which has accuracy not lower than
100
+ *min_acc*.
101
+
102
+ #imports
103
+ >>> from nltk.tbl.template import Template
104
+ >>> from nltk.tag.brill import Pos, Word
105
+ >>> from nltk.tag import untag, RegexpTagger, BrillTaggerTrainer
106
+
107
+ #some data
108
+ >>> from nltk.corpus import treebank
109
+ >>> training_data = treebank.tagged_sents()[:100]
110
+ >>> baseline_data = treebank.tagged_sents()[100:200]
111
+ >>> gold_data = treebank.tagged_sents()[200:300]
112
+ >>> testing_data = [untag(s) for s in gold_data]
113
+
114
+ >>> backoff = RegexpTagger([
115
+ ... (r'^-?[0-9]+(.[0-9]+)?$', 'CD'), # cardinal numbers
116
+ ... (r'(The|the|A|a|An|an)$', 'AT'), # articles
117
+ ... (r'.*able$', 'JJ'), # adjectives
118
+ ... (r'.*ness$', 'NN'), # nouns formed from adjectives
119
+ ... (r'.*ly$', 'RB'), # adverbs
120
+ ... (r'.*s$', 'NNS'), # plural nouns
121
+ ... (r'.*ing$', 'VBG'), # gerunds
122
+ ... (r'.*ed$', 'VBD'), # past tense verbs
123
+ ... (r'.*', 'NN') # nouns (default)
124
+ ... ])
125
+
126
+ >>> baseline = backoff #see NOTE1
127
+
128
+ >>> baseline.evaluate(gold_data) #doctest: +ELLIPSIS
129
+ 0.2450142...
130
+
131
+ #templates
132
+ >>> Template._cleartemplates() #clear any templates created in earlier tests
133
+ >>> templates = [Template(Pos([-1])), Template(Pos([-1]), Word([0]))]
134
+
135
+ #construct a BrillTaggerTrainer
136
+ >>> tt = BrillTaggerTrainer(baseline, templates, trace=3)
137
+
138
+ >>> tagger1 = tt.train(training_data, max_rules=10)
139
+ TBL train (fast) (seqs: 100; tokens: 2417; tpls: 2; min score: 2; min acc: None)
140
+ Finding initial useful rules...
141
+ Found 845 useful rules.
142
+ <BLANKLINE>
143
+ B |
144
+ S F r O | Score = Fixed - Broken
145
+ c i o t | R Fixed = num tags changed incorrect -> correct
146
+ o x k h | u Broken = num tags changed correct -> incorrect
147
+ r e e e | l Other = num tags changed incorrect -> incorrect
148
+ e d n r | e
149
+ ------------------+-------------------------------------------------------
150
+ 132 132 0 0 | AT->DT if Pos:NN@[-1]
151
+ 85 85 0 0 | NN->, if Pos:NN@[-1] & Word:,@[0]
152
+ 69 69 0 0 | NN->. if Pos:NN@[-1] & Word:.@[0]
153
+ 51 51 0 0 | NN->IN if Pos:NN@[-1] & Word:of@[0]
154
+ 47 63 16 161 | NN->IN if Pos:NNS@[-1]
155
+ 33 33 0 0 | NN->TO if Pos:NN@[-1] & Word:to@[0]
156
+ 26 26 0 0 | IN->. if Pos:NNS@[-1] & Word:.@[0]
157
+ 24 24 0 0 | IN->, if Pos:NNS@[-1] & Word:,@[0]
158
+ 22 27 5 24 | NN->-NONE- if Pos:VBD@[-1]
159
+ 17 17 0 0 | NN->CC if Pos:NN@[-1] & Word:and@[0]
160
+
161
+ >>> tagger1.rules()[1:3]
162
+ (Rule('001', 'NN', ',', [(Pos([-1]),'NN'), (Word([0]),',')]), Rule('001', 'NN', '.', [(Pos([-1]),'NN'), (Word([0]),'.')]))
163
+
164
+ >>> train_stats = tagger1.train_stats()
165
+ >>> [train_stats[stat] for stat in ['initialerrors', 'finalerrors', 'rulescores']]
166
+ [1775, 1269, [132, 85, 69, 51, 47, 33, 26, 24, 22, 17]]
167
+
168
+ >>> tagger1.print_template_statistics(printunused=False)
169
+ TEMPLATE STATISTICS (TRAIN) 2 templates, 10 rules)
170
+ TRAIN ( 2417 tokens) initial 1775 0.2656 final: 1269 0.4750
171
+ #ID | Score (train) | #Rules | Template
172
+ --------------------------------------------
173
+ 001 | 305 0.603 | 7 0.700 | Template(Pos([-1]),Word([0]))
174
+ 000 | 201 0.397 | 3 0.300 | Template(Pos([-1]))
175
+ <BLANKLINE>
176
+ <BLANKLINE>
177
+
178
+ >>> tagger1.evaluate(gold_data) # doctest: +ELLIPSIS
179
+ 0.43996...
180
+
181
+ >>> tagged, test_stats = tagger1.batch_tag_incremental(testing_data, gold_data)
182
+
183
+ >>> tagged[33][12:] == [('foreign', 'IN'), ('debt', 'NN'), ('of', 'IN'), ('$', 'NN'), ('64', 'CD'),
184
+ ... ('billion', 'NN'), ('*U*', 'NN'), ('--', 'NN'), ('the', 'DT'), ('third-highest', 'NN'), ('in', 'NN'),
185
+ ... ('the', 'DT'), ('developing', 'VBG'), ('world', 'NN'), ('.', '.')]
186
+ True
187
+
188
+ >>> [test_stats[stat] for stat in ['initialerrors', 'finalerrors', 'rulescores']]
189
+ [1855, 1376, [100, 85, 67, 58, 27, 36, 27, 16, 31, 32]]
190
+
191
+ # a high-accuracy tagger
192
+ >>> tagger2 = tt.train(training_data, max_rules=10, min_acc=0.99)
193
+ TBL train (fast) (seqs: 100; tokens: 2417; tpls: 2; min score: 2; min acc: 0.99)
194
+ Finding initial useful rules...
195
+ Found 845 useful rules.
196
+ <BLANKLINE>
197
+ B |
198
+ S F r O | Score = Fixed - Broken
199
+ c i o t | R Fixed = num tags changed incorrect -> correct
200
+ o x k h | u Broken = num tags changed correct -> incorrect
201
+ r e e e | l Other = num tags changed incorrect -> incorrect
202
+ e d n r | e
203
+ ------------------+-------------------------------------------------------
204
+ 132 132 0 0 | AT->DT if Pos:NN@[-1]
205
+ 85 85 0 0 | NN->, if Pos:NN@[-1] & Word:,@[0]
206
+ 69 69 0 0 | NN->. if Pos:NN@[-1] & Word:.@[0]
207
+ 51 51 0 0 | NN->IN if Pos:NN@[-1] & Word:of@[0]
208
+ 36 36 0 0 | NN->TO if Pos:NN@[-1] & Word:to@[0]
209
+ 26 26 0 0 | NN->. if Pos:NNS@[-1] & Word:.@[0]
210
+ 24 24 0 0 | NN->, if Pos:NNS@[-1] & Word:,@[0]
211
+ 19 19 0 6 | NN->VB if Pos:TO@[-1]
212
+ 18 18 0 0 | CD->-NONE- if Pos:NN@[-1] & Word:0@[0]
213
+ 18 18 0 0 | NN->CC if Pos:NN@[-1] & Word:and@[0]
214
+
215
+ >>> tagger2.evaluate(gold_data) # doctest: +ELLIPSIS
216
+ 0.44159544...
217
+ >>> tagger2.rules()[2:4]
218
+ (Rule('001', 'NN', '.', [(Pos([-1]),'NN'), (Word([0]),'.')]), Rule('001', 'NN', 'IN', [(Pos([-1]),'NN'), (Word([0]),'of')]))
219
+
220
+ # NOTE1: (!!FIXME) A far better baseline uses nltk.tag.UnigramTagger,
221
+ # with a RegexpTagger only as backoff. For instance,
222
+ # >>> baseline = UnigramTagger(baseline_data, backoff=backoff)
223
+ # However, as of Nov 2013, nltk.tag.UnigramTagger does not yield consistent results
224
+ # between python versions. The simplistic backoff above is a workaround to make doctests
225
+ # get consistent input.
226
+
227
+ :param train_sents: training data
228
+ :type train_sents: list(list(tuple))
229
+ :param max_rules: output at most max_rules rules
230
+ :type max_rules: int
231
+ :param min_score: stop training when no rules better than min_score can be found
232
+ :type min_score: int
233
+ :param min_acc: discard any rule with lower accuracy than min_acc
234
+ :type min_acc: float or None
235
+ :return: the learned tagger
236
+ :rtype: BrillTagger
237
+
238
+ """
239
+ # FIXME: several tests are a bit too dependent on tracing format
240
+ # FIXME: tests in trainer.fast and trainer.brillorig are exact duplicates
241
+
242
+ # Basic idea: Keep track of the rules that apply at each position.
243
+ # And keep track of the positions to which each rule applies.
244
+
245
+ # Create a new copy of the training corpus, and run the
246
+ # initial tagger on it. We will progressively update this
247
+ # test corpus to look more like the training corpus.
248
+ test_sents = [list(self._initial_tagger.tag(untag(sent)))
249
+ for sent in train_sents]
250
+
251
+ # Collect some statistics on the training process
252
+ trainstats = {}
253
+ trainstats['min_acc'] = min_acc
254
+ trainstats['min_score'] = min_score
255
+ trainstats['tokencount'] = sum(len(t) for t in test_sents)
256
+ trainstats['sequencecount'] = len(test_sents)
257
+ trainstats['templatecount'] = len(self._templates)
258
+ trainstats['rulescores'] = []
259
+ trainstats['initialerrors'] = sum(
260
+ tag[1] != truth[1]
261
+ for paired in zip(test_sents, train_sents)
262
+ for (tag, truth) in zip(*paired)
263
+ )
264
+ trainstats['initialacc'] = 1 - trainstats['initialerrors']/trainstats['tokencount']
265
+ if self._trace > 0:
266
+ print("TBL train (fast) (seqs: {sequencecount}; tokens: {tokencount}; "
267
+ "tpls: {templatecount}; min score: {min_score}; min acc: {min_acc})".format(**trainstats))
268
+
269
+ # Initialize our mappings. This will find any errors made
270
+ # by the initial tagger, and use those to generate repair
271
+ # rules, which are added to the rule mappings.
272
+ if self._trace:
273
+ print("Finding initial useful rules...")
274
+ self._init_mappings(test_sents, train_sents)
275
+ if self._trace:
276
+ print((" Found %d useful rules." % len(self._rule_scores)))
277
+
278
+ # Let the user know what we're up to.
279
+ if self._trace > 2:
280
+ self._trace_header()
281
+ elif self._trace == 1:
282
+ print("Selecting rules...")
283
+
284
+ # Repeatedly select the best rule, and add it to `rules`.
285
+ rules = []
286
+ try:
287
+ while (len(rules) < max_rules):
288
+ # Find the best rule, and add it to our rule list.
289
+ rule = self._best_rule(train_sents, test_sents, min_score, min_acc)
290
+ if rule:
291
+ rules.append(rule)
292
+ score = self._rule_scores[rule]
293
+ trainstats['rulescores'].append(score)
294
+ else:
295
+ break # No more good rules left!
296
+
297
+ # Report the rule that we found.
298
+ if self._trace > 1:
299
+ self._trace_rule(rule)
300
+
301
+ # Apply the new rule at the relevant sites
302
+ self._apply_rule(rule, test_sents)
303
+
304
+ # Update _tag_positions[rule.original_tag] and
305
+ # _tag_positions[rule.replacement_tag] for the affected
306
+ # positions (i.e., self._positions_by_rule[rule]).
307
+ self._update_tag_positions(rule)
308
+
309
+ # Update rules that were affected by the change.
310
+ self._update_rules(rule, train_sents, test_sents)
311
+
312
+ # The user can cancel training manually:
313
+ except KeyboardInterrupt:
314
+ print("Training stopped manually -- %d rules found" % len(rules))
315
+
316
+ # Discard our tag position mapping & rule mappings.
317
+ self._clean()
318
+ trainstats['finalerrors'] = trainstats['initialerrors'] - sum(trainstats['rulescores'])
319
+ trainstats['finalacc'] = 1 - trainstats['finalerrors']/trainstats['tokencount']
320
+ # Create and return a tagger from the rules we found.
321
+ return BrillTagger(self._initial_tagger, rules, trainstats)
322
+
323
+ def _init_mappings(self, test_sents, train_sents):
324
+ """
325
+ Initialize the tag position mapping & the rule related
326
+ mappings. For each error in test_sents, find new rules that
327
+ would correct them, and add them to the rule mappings.
328
+ """
329
+ self._tag_positions = defaultdict(list)
330
+ self._rules_by_position = defaultdict(set)
331
+ self._positions_by_rule = defaultdict(dict)
332
+ self._rules_by_score = defaultdict(set)
333
+ self._rule_scores = defaultdict(int)
334
+ self._first_unknown_position = defaultdict(int)
335
+ # Scan through the corpus, initializing the tag_positions
336
+ # mapping and all the rule-related mappings.
337
+ for sentnum, sent in enumerate(test_sents):
338
+ for wordnum, (word, tag) in enumerate(sent):
339
+
340
+ # Initialize tag_positions
341
+ self._tag_positions[tag].append((sentnum, wordnum))
342
+
343
+ # If it's an error token, update the rule-related mappings.
344
+ correct_tag = train_sents[sentnum][wordnum][1]
345
+ if tag != correct_tag:
346
+ for rule in self._find_rules(sent, wordnum, correct_tag):
347
+ self._update_rule_applies(rule, sentnum, wordnum,
348
+ train_sents)
349
+
350
+ def _clean(self):
351
+ self._tag_positions = None
352
+ self._rules_by_position = None
353
+ self._positions_by_rule = None
354
+ self._rules_by_score = None
355
+ self._rule_scores = None
356
+ self._first_unknown_position = None
357
+
358
+ def _find_rules(self, sent, wordnum, new_tag):
359
+ """
360
+ Use the templates to find rules that apply at index *wordnum*
361
+ in the sentence *sent* and generate the tag *new_tag*.
362
+ """
363
+ for template in self._templates:
364
+ for rule in template.applicable_rules(sent, wordnum, new_tag):
365
+ yield rule
366
+
367
+ def _update_rule_applies(self, rule, sentnum, wordnum, train_sents):
368
+ """
369
+ Update the rule data tables to reflect the fact that
370
+ *rule* applies at the position *(sentnum, wordnum)*.
371
+ """
372
+ pos = sentnum, wordnum
373
+
374
+ # If the rule is already known to apply here, ignore.
375
+ # (This only happens if the position's tag hasn't changed.)
376
+ if pos in self._positions_by_rule[rule]:
377
+ return
378
+
379
+ # Update self._positions_by_rule.
380
+ correct_tag = train_sents[sentnum][wordnum][1]
381
+ if rule.replacement_tag == correct_tag:
382
+ self._positions_by_rule[rule][pos] = 1
383
+ elif rule.original_tag == correct_tag:
384
+ self._positions_by_rule[rule][pos] = -1
385
+ else: # was wrong, remains wrong
386
+ self._positions_by_rule[rule][pos] = 0
387
+
388
+ # Update _rules_by_position
389
+ self._rules_by_position[pos].add(rule)
390
+
391
+ # Update _rule_scores.
392
+ old_score = self._rule_scores[rule]
393
+ self._rule_scores[rule] += self._positions_by_rule[rule][pos]
394
+
395
+ # Update _rules_by_score.
396
+ self._rules_by_score[old_score].discard(rule)
397
+ self._rules_by_score[self._rule_scores[rule]].add(rule)
398
+
399
+ def _update_rule_not_applies(self, rule, sentnum, wordnum):
400
+ """
401
+ Update the rule data tables to reflect the fact that *rule*
402
+ does not apply at the position *(sentnum, wordnum)*.
403
+ """
404
+ pos = sentnum, wordnum
405
+
406
+ # Update _rule_scores.
407
+ old_score = self._rule_scores[rule]
408
+ self._rule_scores[rule] -= self._positions_by_rule[rule][pos]
409
+
410
+ # Update _rules_by_score.
411
+ self._rules_by_score[old_score].discard(rule)
412
+ self._rules_by_score[self._rule_scores[rule]].add(rule)
413
+
414
+ # Update _positions_by_rule
415
+ del self._positions_by_rule[rule][pos]
416
+ self._rules_by_position[pos].remove(rule)
417
+
418
+ # Optional addition: if the rule now applies nowhere, delete
419
+ # all its dictionary entries.
420
+
421
+ def _best_rule(self, train_sents, test_sents, min_score, min_acc):
422
+ """
423
+ Find the next best rule. This is done by repeatedly taking a
424
+ rule with the highest score and stepping through the corpus to
425
+ see where it applies. When it makes an error (decreasing its
426
+ score) it's bumped down, and we try a new rule with the
427
+ highest score. When we find a rule which has the highest
428
+ score *and* which has been tested against the entire corpus, we
429
+ can conclude that it's the next best rule.
430
+ """
431
+ for max_score in sorted(self._rules_by_score.keys(), reverse=True):
432
+ if len(self._rules_by_score) == 0:
433
+ return None
434
+ if max_score < min_score or max_score <= 0:
435
+ return None
436
+ best_rules = list(self._rules_by_score[max_score])
437
+ if self._deterministic:
438
+ best_rules.sort(key=repr)
439
+ for rule in best_rules:
440
+ positions = self._tag_positions[rule.original_tag]
441
+
442
+ unk = self._first_unknown_position.get(rule, (0, -1))
443
+ start = bisect.bisect_left(positions, unk)
444
+
445
+ for i in range(start, len(positions)):
446
+ sentnum, wordnum = positions[i]
447
+ if rule.applies(test_sents[sentnum], wordnum):
448
+ self._update_rule_applies(rule, sentnum, wordnum,
449
+ train_sents)
450
+ if self._rule_scores[rule] < max_score:
451
+ self._first_unknown_position[rule] = (sentnum,
452
+ wordnum+1)
453
+ break # The update demoted the rule.
454
+
455
+ if self._rule_scores[rule] == max_score:
456
+ self._first_unknown_position[rule] = (len(train_sents) + 1, 0)
457
+ # optimization: if no min_acc threshold given, don't bother computing accuracy
458
+ if min_acc is None:
459
+ return rule
460
+ else:
461
+ changes = self._positions_by_rule[rule].values()
462
+ num_fixed = len([c for c in changes if c == 1])
463
+ num_broken = len([c for c in changes if c == -1])
464
+ # acc here is fixed/(fixed+broken); could also be
465
+ # fixed/(fixed+broken+other) == num_fixed/len(changes)
466
+ acc = num_fixed/(num_fixed+num_broken)
467
+ if acc >= min_acc:
468
+ return rule
469
+ # else: rule too inaccurate, discard and try next
470
+
471
+ # We demoted (or skipped due to < min_acc, if that was given)
472
+ # all the rules with score==max_score.
473
+
474
+ assert min_acc is not None or not self._rules_by_score[max_score]
475
+ if not self._rules_by_score[max_score]:
476
+ del self._rules_by_score[max_score]
477
+
478
+ def _apply_rule(self, rule, test_sents):
479
+ """
480
+ Update *test_sents* by applying *rule* everywhere where its
481
+ conditions are met.
482
+ """
483
+ update_positions = set(self._positions_by_rule[rule])
484
+ new_tag = rule.replacement_tag
485
+
486
+ if self._trace > 3:
487
+ self._trace_apply(len(update_positions))
488
+
489
+ # Update test_sents.
490
+ for (sentnum, wordnum) in update_positions:
491
+ text = test_sents[sentnum][wordnum][0]
492
+ test_sents[sentnum][wordnum] = (text, new_tag)
493
+
494
+ def _update_tag_positions(self, rule):
495
+ """
496
+ Update _tag_positions to reflect the changes to tags that are
497
+ made by *rule*.
498
+ """
499
+ # Update the tag index.
500
+ for pos in self._positions_by_rule[rule]:
501
+ # Delete the old tag.
502
+ old_tag_positions = self._tag_positions[rule.original_tag]
503
+ old_index = bisect.bisect_left(old_tag_positions, pos)
504
+ del old_tag_positions[old_index]
505
+ # Insert the new tag.
506
+ new_tag_positions = self._tag_positions[rule.replacement_tag]
507
+ bisect.insort_left(new_tag_positions, pos)
508
+
509
+ def _update_rules(self, rule, train_sents, test_sents):
510
+ """
511
+ Check if we should add or remove any rules from consideration,
512
+ given the changes made by *rule*.
513
+ """
514
+ # Collect a list of all positions that might be affected.
515
+ neighbors = set()
516
+ for sentnum, wordnum in self._positions_by_rule[rule]:
517
+ for template in self._templates:
518
+ n = template.get_neighborhood(test_sents[sentnum], wordnum)
519
+ neighbors.update([(sentnum, i) for i in n])
520
+
521
+ # Update the rules at each position.
522
+ num_obsolete = num_new = num_unseen = 0
523
+ for sentnum, wordnum in neighbors:
524
+ test_sent = test_sents[sentnum]
525
+ correct_tag = train_sents[sentnum][wordnum][1]
526
+
527
+ # Check if the change causes any rule at this position to
528
+ # stop matching; if so, then update our rule mappings
529
+ # accordingly.
530
+ old_rules = set(self._rules_by_position[sentnum, wordnum])
531
+ for old_rule in old_rules:
532
+ if not old_rule.applies(test_sent, wordnum):
533
+ num_obsolete += 1
534
+ self._update_rule_not_applies(old_rule, sentnum, wordnum)
535
+
536
+ # Check if the change causes our templates to propose any
537
+ # new rules for this position.
538
+ for template in self._templates:
539
+ for new_rule in template.applicable_rules(test_sent, wordnum,
540
+ correct_tag):
541
+ if new_rule not in old_rules:
542
+ num_new += 1
543
+ if new_rule not in self._rule_scores:
544
+ num_unseen += 1
545
+ old_rules.add(new_rule)
546
+ self._update_rule_applies(new_rule, sentnum,
547
+ wordnum, train_sents)
548
+
549
+ # We may have caused other rules to match here, that are
550
+ # not proposed by our templates -- in particular, rules
551
+ # that are harmful or neutral. We therefore need to
552
+ # update any rule whose first_unknown_position is past
553
+ # this rule.
554
+ for new_rule, pos in self._first_unknown_position.items():
555
+ if pos > (sentnum, wordnum):
556
+ if new_rule not in old_rules:
557
+ num_new += 1
558
+ if new_rule.applies(test_sent, wordnum):
559
+ self._update_rule_applies(new_rule, sentnum,
560
+ wordnum, train_sents)
561
+
562
+ if self._trace > 3:
563
+ self._trace_update_rules(num_obsolete, num_new, num_unseen)
564
+
565
+ # Tracing
566
+
567
+ def _trace_header(self):
568
+ print("""
569
+ B |
570
+ S F r O | Score = Fixed - Broken
571
+ c i o t | R Fixed = num tags changed incorrect -> correct
572
+ o x k h | u Broken = num tags changed correct -> incorrect
573
+ r e e e | l Other = num tags changed incorrect -> incorrect
574
+ e d n r | e
575
+ ------------------+-------------------------------------------------------
576
+ """.rstrip())
577
+
578
+ def _trace_rule(self, rule):
579
+ assert self._rule_scores[rule] == sum(self._positions_by_rule[rule].values())
580
+
581
+ changes = self._positions_by_rule[rule].values()
582
+ num_fixed = len([c for c in changes if c == 1])
583
+ num_broken = len([c for c in changes if c == -1])
584
+ num_other = len([c for c in changes if c == 0])
585
+ score = self._rule_scores[rule]
586
+
587
+ rulestr = rule.format(self._ruleformat)
588
+ if self._trace > 2:
589
+ print('%4d%4d%4d%4d |' % (score, num_fixed, num_broken, num_other), end=' ')
590
+ print(textwrap.fill(rulestr, initial_indent=' '*20, width=79,
591
+ subsequent_indent=' '*18+'| ').strip())
592
+ else:
593
+ print(rulestr)
594
+
595
+ def _trace_apply(self, num_updates):
596
+ prefix = ' '*18+'|'
597
+ print(prefix)
598
+ print(prefix, 'Applying rule to %d positions.' % num_updates)
599
+
600
+ def _trace_update_rules(self, num_obsolete, num_new, num_unseen):
601
+ prefix = ' '*18+'|'
602
+ print(prefix, 'Updated rule tables:')
603
+ print(prefix, (' - %d rule applications removed' % num_obsolete))
604
+ print(prefix, (' - %d rule applications added (%d novel)' %
605
+ (num_new, num_unseen)))
606
+ print(prefix)
607
+
608
+