Mercurial > hg > python
comparison hmm/semiSup.py @ 2:e07789816ca5
adding more python files from lib/python on origen
| author | Henry Thompson <ht@markup.co.uk> |
|---|---|
| date | Mon, 09 Mar 2020 16:48:09 +0000 |
| parents | |
| children | 26d9c0308fcf |
comparison
equal
deleted
inserted
replaced
| 1:0a3abe59e364 | 2:e07789816ca5 |
|---|---|
| 1 '''Exploring the claim that a small dictionary can seed | |
| 2 an otherwise unsupervised HMM to learn a decent POS-tagger''' | |
| 3 import nltk, random, itertools | |
| 4 from nltk.corpus import brown | |
| 5 from nltk.tag.hmm import HiddenMarkovModelTagger, HiddenMarkovModelTrainer, logsumexp2 | |
| 6 from nltk.probability import FreqDist,ConditionalFreqDist | |
| 7 from nltk.probability import MLEProbDist, RandomProbDist, DictionaryConditionalProbDist | |
| 8 | |
| 9 def totLogProb(self,sequences): | |
| 10 N = len(self._states) | |
| 11 M = len(self._symbols) | |
| 12 logProb = 0 | |
| 13 for sequence in sequences: | |
| 14 T = len(sequence) | |
| 15 # compute forward and backward probabilities | |
| 16 alpha = self._forward_probability(sequence) | |
| 17 beta = self._backward_probability(sequence) | |
| 18 # find the log probability of the sequence | |
| 19 logProb += logsumexp2(alpha[T-1]) | |
| 20 return logProb | |
| 21 | |
| 22 HiddenMarkovModelTagger.totLogProb=totLogProb | |
| 23 | |
| 24 trainTagsPercent=1.0 | |
| 25 trainHMMPercent=0.9 | |
| 26 knownWordsPercent=1.0 | |
| 27 | |
| 28 SST=SSW='<s>' | |
| 29 EST=ESW='</s>' | |
| 30 SS=[(SSW,SST)] | |
| 31 ES=[(ESW,EST)] | |
| 32 TAGSETS={ | |
| 33 'univ': | |
| 34 [u'ADJ', u'ADP', u'ADV', u'CONJ', u'DET', u'NOUN', u'NUM', | |
| 35 u'PRON', u'PRT', u'VERB', u'X', u'.',SST,EST], | |
| 36 'brown': | |
| 37 [u"ABL", u"ABN", u"ABX", u"AP", u"AP$", u"AP+AP", u"AT", u"BE", | |
| 38 u"BED", u"BED*", u"BEDZ", u"BEDZ*", u"BEG", u"BEM", u"BEM*", | |
| 39 u"BEN", u"BER", u"BER*", u"BEZ", u"BEZ*", u"CC", u"CD", | |
| 40 u"CD$", u"CS", u"DO", u"DO*", u"DO+PPSS", u"DOD", u"DOD*", | |
| 41 u"DOZ", u"DOZ*", u"DT", u"DT$", u"DT+BEZ", u"DT+MD", u"DTI", | |
| 42 u"DTS", u"DTS+BEZ", u"DTX", u"EX", u"EX+BEZ", u"EX+HVD", u"EX+HVZ", | |
| 43 u"EX+MD", u"FW-*", u"FW-AT", u"FW-AT+NN", u"FW-AT+NP", u"FW-BE", u"FW-BER", | |
| 44 u"FW-BEZ", u"FW-CC", u"FW-CD", u"FW-CS", u"FW-DT", u"FW-DT+BEZ", u"FW-DTS", | |
| 45 u"FW-HV", u"FW-IN", u"FW-IN+AT", u"FW-IN+NN", u"FW-IN+NP", u"FW-JJ", | |
| 46 u"FW-JJR", u"FW-JJT", u"FW-NN", u"FW-NN$", u"FW-NNS", u"FW-NP", u"FW-NPS", | |
| 47 u"FW-NR", u"FW-OD", u"FW-PN", u"FW-PP$", u"FW-PPL", u"FW-PPL+VBZ", | |
| 48 u"FW-PPO", u"FW-PPO+IN", u"FW-PPS", u"FW-PPSS", u"FW-PPSS+HV", u"FW-QL", | |
| 49 u"FW-RB", u"FW-RB+CC", u"FW-TO+VB", u"FW-UH", u"FW-VB", u"FW-VBD", | |
| 50 u"FW-VBG", u"FW-VBN", u"FW-VBZ", u"FW-WDT", u"FW-WPO", u"FW-WPS", u"HV", | |
| 51 u"HV*", u"HV+TO", u"HVD", u"HVD*", u"HVG", u"HVN", u"HVZ", u"HVZ*", u"IN", | |
| 52 u"IN+IN", u"IN+PPO", u"JJ", u"JJ$", u"JJ+JJ", u"JJR", u"JJR+CS", u"JJS", | |
| 53 u"JJT", u"MD", u"MD*", u"MD+HV", u"MD+PPSS", u"MD+TO", u"NN", u"NN$", | |
| 54 u"NN+BEZ", u"NN+HVD", u"NN+HVZ", u"NN+IN", u"NN+MD", u"NN+NN", u"NNS", | |
| 55 u"NNS$", u"NNS+MD", u"NP", u"NP$", u"NP+BEZ", u"NP+HVZ", u"NP+MD", | |
| 56 u"NPS", u"NPS$", u"NR", u"NR$", u"NR+MD", u"NRS", u"OD", | |
| 57 u"PN", u"PN$", u"PN+BEZ", u"PN+HVD", u"PN+HVZ", u"PN+MD", u"PP$", | |
| 58 u"PP$$", u"PPL", u"PPLS", u"PPO", u"PPS", u"PPS+BEZ", u"PPS+HVD", | |
| 59 u"PPS+HVZ", u"PPS+MD", u"PPSS", u"PPSS+BEM", u"PPSS+BER", u"PPSS+BEZ", | |
| 60 u"PPSS+BEZ*", u"PPSS+HV", u"PPSS+HVD", u"PPSS+MD", u"PPSS+VB", u"QL", | |
| 61 u"QLP", u"RB", u"RB$", u"RB+BEZ", u"RB+CS", u"RBR", u"RBR+CS", u"RBT", | |
| 62 u"RN", u"RP", u"RP+IN", u"TO", u"TO+VB", u"UH", u"VB", u"VB+AT", | |
| 63 u"VB+IN", u"VB+JJ", u"VB+PPO", u"VB+RP", u"VB+TO", u"VB+VB", u"VBD", | |
| 64 u"VBG", u"VBG+TO", u"VBN", u"VBN+TO", u"VBZ", u"WDT", u"WDT+BER", | |
| 65 u"WDT+BER+PP", u"WDT+BEZ", u"WDT+DO+PPS", u"WDT+DOD", u"WDT+HVZ", u"WP$", | |
| 66 u"WPO", u"WPS", u"WPS+BEZ", u"WPS+HVD", u"WPS+HVZ", u"WPS+MD", u"WQL", | |
| 67 u"WRB", u"WRB+BER", u"WRB+BEZ", u"WRB+DO", u"WRB+DOD", u"WRB+DOD*", | |
| 68 u"WRB+DOZ", u"WRB+IN", u"WRB+MD", | |
| 69 u"(", u")", u"*", u",", u"--", u".", u":"], | |
| 70 'upenn': | |
| 71 [u"CC", u"CD", u"DT", u"EX", u"FW", u"IN", u"JJ", u"JJR", u"JJS", u"LS", | |
| 72 u"MD", u"NN", u"NNP", u"NNPS", u"NNS", u"PDT", u"POS", u"PRP", u"PRP$", | |
| 73 u"RB", u"RBR", u"RBS", u"RP", u"SYM", u"TO", u"UH", u"VB", u"VBD", u"VBG", | |
| 74 u"VBN", u"VBP", u"VBZ", u"WDT", u"WP", u"WP$", u"WRB", | |
| 75 u"``", u"$", u"''", u"(", u")", u",", u"--", u".", u":"]} | |
| 76 | |
| 77 TAGSETS['universal']=TAGSETS['univ'] | |
| 78 TAGSETS['penn']=TAGSETS['upenn'] | |
| 79 | |
| 80 def setup(cat='news',tagset='brown',corpus=brown): | |
| 81 return ([list(itertools.chain(iter(SS), | |
| 82 ((word.lower(),tag) for (word,tag) in s) | |
| 83 ,iter(ES))) | |
| 84 for s in corpus.tagged_sents(categories=cat,tagset=tagset)], | |
| 85 list(itertools.chain(iter(SS), iter(ES), | |
| 86 ((word.lower(),tag) for (word,tag) in | |
| 87 corpus.tagged_words(categories=cat,tagset=tagset)))), | |
| 88 TAGSETS[tagset]) | |
| 89 | |
| 90 def notCurrent(s,missList): | |
| 91 global i,n,done | |
| 92 if done or (missList[i] is not s): | |
| 93 return True | |
| 94 else: | |
| 95 i+=1 | |
| 96 if i==n: | |
| 97 done=True | |
| 98 return False | |
| 99 | |
| 100 def splitData(words,wordPercent,sentences,sentPercent): | |
| 101 global i,n, done | |
| 102 trainWords=random.sample(words,int(wordPercent*len(words))) | |
| 103 # random.sample(sentences,int(sentPercent*len(sentences))) | |
| 104 trainSents=[s for s in sentences if random.random()<sentPercent] | |
| 105 # hack! | |
| 106 i=0 | |
| 107 n=len(trainSents) | |
| 108 done=False | |
| 109 testSents=[s for s in sentences if notCurrent(s,trainSents)] | |
| 110 return trainWords, trainSents, testSents | |
| 111 | |
| 112 def pickWords(tagged,percent): | |
| 113 #wToT=ConditionalFreqDist(tagged) | |
| 114 tToW=ConditionalFreqDist((t,w) for (w,t) in tagged) | |
| 115 #print len(tToW[u'ADV']) | |
| 116 dd=dict((tag,(lambda wl,p=percent:\ | |
| 117 wl[:int(p*len(wl))])( | |
| 118 sorted(tToW[tag].items(),key=lambda (k,v):v,reverse=True))) | |
| 119 for tag in tToW.keys()) | |
| 120 return dd | |
| 121 | |
| 122 (tagged_s,tagged_w,tagset)=setup(tagset='universal') | |
| 123 | |
| 124 true_tagged_w=tagged_w[2:] # not SS, SE | |
| 125 | |
| 126 wordTokens=FreqDist(word for word,tag in true_tagged_w) | |
| 127 wordsAsSuch=list(wordTokens.keys()) | |
| 128 print len(wordTokens), wordTokens.N() | |
| 129 | |
| 130 (trainTags,trainHMM,testHMM)=splitData(true_tagged_w,trainTagsPercent, | |
| 131 tagged_s,trainHMMPercent) | |
| 132 | |
| 133 knownWords=pickWords(trainTags,knownWordsPercent) | |
| 134 | |
| 135 class SubsetFreqDist(FreqDist): | |
| 136 def __init__(self,pairs,baseset,basecount=.05): | |
| 137 dict.update(self,pairs) | |
| 138 self._baseset=baseset | |
| 139 self._basecount=basecount | |
| 140 pn=sum(n for w,n in pairs) | |
| 141 self._N=pn+((len(baseset)-len(pairs))*basecount) | |
| 142 | |
| 143 def __getitem__(self,key): | |
| 144 return dict.__getitem__(self,key) | |
| 145 | |
| 146 def __missing__(self,key): | |
| 147 if key in self._baseset: | |
| 148 return self._basecount | |
| 149 else: | |
| 150 return 0 | |
| 151 | |
| 152 def N(self): | |
| 153 return self._N | |
| 154 | |
| 155 class Tag: | |
| 156 def __init__(self,tag,wordsAndCounts): | |
| 157 self._tag=tag | |
| 158 self._wordsAndCounts=wordsAndCounts | |
| 159 self._words=set(w for w,n in wordsAndCounts) | |
| 160 self._nTokens=sum(n for w,n in wordsAndCounts) | |
| 161 self._nTypes=len(self._words) | |
| 162 | |
| 163 def words(self): | |
| 164 return self._words | |
| 165 | |
| 166 def buildPD(self,allTokens): | |
| 167 self._sfd=SubsetFreqDist(self._wordsAndCounts,allTokens) | |
| 168 self._pd=MLEProbDist(self._sfd) | |
| 169 | |
| 170 def getSFD(self): | |
| 171 return self._sfd | |
| 172 | |
| 173 def getPD(self): | |
| 174 return self._pd | |
| 175 | |
| 176 class FixedTag(Tag): | |
| 177 def buildPD(self): | |
| 178 self._pd=MLEProbDist(FreqDist(dict(self._wordsAndCounts))) | |
| 179 | |
| 180 def getSFD(self): | |
| 181 raise NotImplementedError("not implemented for this subclass") | |
| 182 | |
| 183 tags=dict((tagName,Tag(tagName,wl)) for tagName,wl in knownWords.items()) | |
| 184 kws=dict((tagName,tag.words()) for tagName,tag in tags.items()) | |
| 185 | |
| 186 t2=list(filter(None, | |
| 187 ((lambda i:False if not i[1] else i) | |
| 188 (((tagset[i],tagset[j]), | |
| 189 kws[tagset[i]].intersection(kws[tagset[j]])),) | |
| 190 for i in xrange(0,len(tagset)-2) | |
| 191 for j in xrange(i+1,len(tagset)-2)))) | |
| 192 | |
| 193 for tag in tags.values(): | |
| 194 tag.buildPD(wordTokens) | |
| 195 | |
| 196 tags[SST]=FixedTag(SST,[(SSW,1)]) | |
| 197 tags[SST].buildPD() | |
| 198 tags[EST]=FixedTag(EST,[(ESW,1)]) | |
| 199 tags[EST].buildPD() | |
| 200 | |
| 201 priors = MLEProbDist(FreqDist(dict((tag,1 if tag==SST else 0) for tag in tagset))) | |
| 202 | |
| 203 transitions = DictionaryConditionalProbDist( | |
| 204 dict((state, RandomProbDist(tagset)) | |
| 205 for state in tagset)) | |
| 206 | |
| 207 outputs = DictionaryConditionalProbDist( | |
| 208 dict((state, tags[state].getPD()) | |
| 209 for state in tagset)) | |
| 210 | |
| 211 model = HiddenMarkovModelTagger(wordsAsSuch, tagset, | |
| 212 transitions, outputs, priors) | |
| 213 | |
| 214 print "model", model.evaluate(testHMM), model.totLogProb(testHMM) | |
| 215 | |
| 216 nm=HiddenMarkovModelTrainer(states=tagset,symbols=wordsAsSuch) | |
| 217 | |
| 218 # Note that contrary to naive reading of the documentation, | |
| 219 # train_unsupervised expects a sequence of sequences of word/tag pairs, | |
| 220 # it just ignores the tags | |
| 221 nnm=nm.train_unsupervised(trainHMM,True,model=model,max_iterations=10,testMe=testHMM) | |
| 222 | |
| 223 print nnm.totLogProb(testHMM) |
