started on pure cnf version, see sec6 of formulas.pdf
[dmvccm.git] / DMVCCM.org.~12~
blobf67aeb2957c731e1c1bbd895c5da52d92fd313f6
1 # -*- coding: mule-utf-8-unix -*-
2 #+OPTIONS: H:4 toc:3 ^:{}
3 #+STARTUP: overview
4 #+TAGS: OPTIMIZE PRETTIER
5 #+STARTUP: hidestars
6 #+TITLE: DMV/CCM
7 #+AUTHOR: Kevin Brubeck Unhammer
8 #+EMAIL: K.BrubeckUnhammer at student uva nl 
9 #+LANGUAGE: en
10 #+SEQ_TODO: TOGROK TODO DONE
13 * dmvccm
14   DEADLINE: <2008-06-30 Mon>
15 (But absolute, extended, really-quite-dead-now deadline: August 31...)
16 [[file:src/dmv.py][dmv.py]]
17 [[file:src/io.py][io.py]]
18 * TODO Adjacency and combining it with inner()
19 Each DMV_Rule now has both a probN and a probA, for
20 adjacencies. inner() needs the correct one in each case.
22 Adjacency gives a problem with duplicate words/tags, eg. in the
23 sentence "a a b". If this has the dependency structure b->a_{0}->a_{1},
24 then b is non-adjacent to a_{0} and should use probN (for the LRStop and
25 the attachment of a_{0}), while the other rules should all use
26 probA. But within the e(0,2,b) we can't just say "oh, a has index 0
27 so it's not adjacent to 2", since there's also an a at index 1, and
28 there's also a dependency structure b->a_{1}->a_{0} for that. We want
29 both. And in possibly much more complex versions.
31 Ideas:
32 - I first thought of decorating the individual words/tags in a
33   sentence with their indices, and perhaps just duplicating the
34   relevant rules (one for each index of the duplicate tags). But this
35   gives an explosion in attachment rules (although a contained
36   explosion, within the rules used in a sentence; but most sentences
37   will have at least two NN's so it will be a problem).
38 - Then, I had a /brilliant/ idea. Just let e(), the helper function of
39   inner(), parametrize for an extra pair of boolean values for whether
40   or not we've attached anything to the left or right yet ("yet"
41   meaning "below"). So now, e() has a chart of the form [s, t, LHS,
42   Lattach, Rattach], and of course e(s,t,LHS) is the sum of the four
43   possible values for (Lattach,Rattach). This makes e() lots more
44   complex and DMV-specific though, so it's been rewritten in
45   inner_dmv() in dmv.py.
46 ** TODO document this adjacency stuff better
47 ** TODO test and debug my brilliant idea
48 ** DONE implement my brilliant idea.
49     CLOSED: [2008-06-01 Sun 17:19]
50 [[file:src/dmv.py::def%20e%20s%20t%20LHS%20Lattach%20Rattach][e(sti) in dmv.py]]
52 ** DONE [#A] test inner() on sentences with duplicate words
53 Works with eg. the sentence "h h h"
56 * TODO [#A] P_STOP for IO/EM
57 [[file:src/dmv.py::DMV%20probabilities][dmv-P_STOP]]
58 Remember: The P_{STOP} formula is upside-down (left-to-right also).
59 (In the article..not the thesis)
61 Remember: Initialization makes some "short-cut" rules, these will also
62 have to be updated along with the other P_{STOP} updates:
63 - b[(NOBAR, n_{h}), 'h'] = 1.0       # always
64 - b[(RBAR, n_{h}), 'h'] = h_.probA  # h_ is RBAR stop rule
65 - b[(LRBAR, n_{h}), 'h'] = h_.probA * _ h_.probA
67 ** How is the P_STOP formula different given other values for dir and adj?
68 (Presumably, the P_{STOP} formula where STOP is True is just the
69 rule-probability of _ h_ -> STOP h_ or h_ -> h STOP, but how does
70 adjacency fit in here?)
72 (And P_{STOP}(-STOP|...) = 1 - P_{STOP}(STOP|...) )
73 * TODO P_CHOOSE for IO/EM
74 Write the formulas! should be easy?
75 * Initialization   
76 [[file:~/Documents/Skole/V08/Probability/dmvccm/src/dmv.py::Initialization%20todo][dmv-inits]]
78 We do have to go through the corpus, since the probabilities are based
79 on how far away in the sentence arguments are from their heads.
80 ** TODO Separate initialization to another file?                      :PRETTIER:
81 (It's rather messy.)
82 ** TOGROK CCM Initialization    
83 P_{SPLIT} used here... how, again?
84 ** DONE DMV Initialization probabilities
85 (from initialization frequency)
86 ** DONE DMV Initialization frequencies    
87    CLOSED: [2008-05-27 Tue 20:04]
88 *** P_STOP    
89 P_{STOP} is not well defined by K&M. One possible interpretation given
90 the sentence [det nn vb nn] is
91 : f_{STOP}( STOP|det, L, adj) +1
92 : f_{STOP}(-STOP|det, L, adj) +0  
93 : f_{STOP}( STOP|det, L, non_adj) +1
94 : f_{STOP}(-STOP|det, L, non_adj) +0
95 : f_{STOP}( STOP|det, R, adj) +0
96 : f_{STOP}(-STOP|det, R, adj) +1
98 : f_{STOP}( STOP|nn, L, adj) +0
99 : f_{STOP}(-STOP|nn, L, adj) +1
100 : f_{STOP}( STOP|nn, L, non_adj) +1  # since there's at least one to the left
101 : f_{STOP}(-STOP|nn, L, non_adj) +0
102 **** TODO tweak
103 # <<pstoptweak>>
104 :            f[head,  'STOP', 'LN'] += (i_h <= 1)     # first two words
105 :            f[head, '-STOP', 'LN'] += (not i_h <= 1)     
106 :            f[head,  'STOP', 'LA'] += (i_h == 0)     # very first word
107 :            f[head, '-STOP', 'LA'] += (not i_h == 0)     
108 :            f[head,  'STOP', 'RN'] += (i_h >= n - 2) # last two words
109 :            f[head, '-STOP', 'RN'] += (not i_h >= n - 2) 
110 :            f[head,  'STOP', 'RA'] += (i_h == n - 1) # very last word
111 :            f[head, '-STOP', 'RA'] += (not i_h == n - 1) 
113 :            # this one requires some additional rewriting since it
114 :            # introduces divisions by zero
115 :            f[head,  'STOP', 'LN'] += (i_h == 1)     # second word
116 :            f[head, '-STOP', 'LN'] += (not i_h <= 1) # not first two
117 :            f[head,  'STOP', 'LA'] += (i_h == 0)     # first word
118 :            f[head, '-STOP', 'LA'] += (not i_h == 0) # not first
119 :            f[head,  'STOP', 'RN'] += (i_h == n - 2)     # second-to-last
120 :            f[head, '-STOP', 'RN'] += (not i_h >= n - 2) # not last two
121 :            f[head,  'STOP', 'RA'] += (i_h == n - 1)     # last word
122 :            f[head, '-STOP', 'RA'] += (not i_h == n - 1) # not last
124 :            f[head,  'STOP', 'LN'] += (i_h == 1)     # second word
125 :            f[head, '-STOP', 'LN'] += (not i_h == 1) # not second
126 :            f[head,  'STOP', 'LA'] += (i_h == 0)     # first word
127 :            f[head, '-STOP', 'LA'] += (not i_h == 0) # not first
128 :            f[head,  'STOP', 'RN'] += (i_h == n - 2)     # second-to-last
129 :            f[head, '-STOP', 'RN'] += (not i_h == n - 2) # not second-to-last
130 :            f[head,  'STOP', 'RA'] += (i_h == n - 1)     # last word
131 :            f[head, '-STOP', 'RA'] += (not i_h == n - 1) # not last
132 vs 
133 "all words take the same number of arguments" interpreted as
134 :for all heads:
135 :    p_STOP(head, 'STOP', 'LN') = 0.3
136 :    p_STOP(head, 'STOP', 'LA') = 0.5
137 :    p_STOP(head, 'STOP', 'RN') = 0.4
138 :    p_STOP(head, 'STOP', 'RA') = 0.7
139 (which we easily may tweak in init_zeros())
140 *** P_CHOOSE
141 Go through the corpus, counting distances between heads and
142 arguments. In [det nn vb nn], we give 
143 - f_{CHOOSE}(nn|det, R) +1/1 + C
144 - f_{CHOOSE}(vb|det, R) +1/2 + C
145 - f_{CHOOSE}(nn|det, R) +1/3 + C
146   - If this were the full corpus, P_{CHOOSE}(nn|det, R) would have
147     (1+1/3+2C) / sum_a f_{CHOOSE}(a|det, R)
149 The ROOT gets "each argument with equal probability", so in a sentence
150 of three words, 1/3 for each (in [nn vb nn], 'nn' gets 2/3). Basically
151 just a frequency count of the corpus...
152 * [#C] Deferred
153 ** TODO inner_dmv() should disregard rules with heads not in sent     :OPTIMIZE:
154 If the sentence is "nn vbd det nn", we should not even look at rules
155 where
156 : rule.head() not in "nn vbd det nn".split()
157 This is ruled out by getting rules from g.rules(LHS, sent).
159 Also, we optimize this further by saying we don't even recurse into
160 attachment rules where
161 : rule.head() not in sent[ s :r+1]
162 : rule.head() not in sent[r+1:t+1]
163 meaning, if we're looking at the span "vbd det", we only use
164 attachment rules where both daughters are members of ['vbd','det']
165 (although we don't (yet) care about removing rules that rewrite to the
166 same tag if there are no duplicate tags in the span, etc., that would
167 be a lot of trouble for little potential gain).
168 ** TODO when reestimating P_STOP etc, remove rules with p < epsilon   :OPTIMIZE:
169 ** TODO inner_dmv, short ranges and impossible attachment             :OPTIMIZE:
170 If s-t <= 2, there can be only one attachment below, so don't recurse
171 with both Lattach=True and Rattach=True.
173 If s-t <= 1, there can be no attachment below, so only recurse with
174 Lattach=False, Rattach=False.
176 Put this in the loop under rewrite rules (could also do it in the STOP
177 section, but that would only have an effect on very short sentences).
178 ** TODO clean up the module files                                     :PRETTIER:
179 Is there better way to divide dmv and harmonic? There's a two-way
180 dependency between the modules. Guess there could be a third file that
181 imports both the initialization and the actual EM stuff, while a file
182 containing constants and classes could be imported by all others:
183 : dmv.py imports dmv_EM.py imports dmv_classes.py
184 : dmv.py imports dmv_inits.py imports dmv_classes.py
186 ** TOGROK Some (tagged) sentences are bound to come twice             :OPTIMIZE:
187 Eg, first sort and count, so that the corpus
188 [['nn','vbd','det','nn'],
189  ['vbd','nn','det','nn'],
190  ['nn','vbd','det','nn']]
191 becomes
192 [(['nn','vbd','det','nn'],2),
193  (['vbd','nn','det','nn'],1)]
194 and then in each loop through sentences, make sure we handle the
195 frequency correctly.
196           
197 Is there much to gain here?
199 ** TOGROK tags as numbers or tags as strings?                         :OPTIMIZE:
200 Need to clean up the representation.
202 Stick with tag-strings in initialization then switch to numbers for
203 IO-algorithm perhaps? Can probably afford more string-matching in
204 initialization..
205 * Expectation Maximation in IO/DMV-terms
206 inner(s,t,LHS) calculates the expected number of trees headed by LHS
207 from s to t (sentence positions). This uses the P_STOP and P_CHOOSE
208 values, which have been conveniently distributed into CNF rules as
209 probN and probA (non-adjacent and adjacent probabilites).
211 When re-estimating, we use the expected values from inner() to get new
212 values for P_STOP and P_CHOOSE. When we've re-estimated for the entire
213 corpus, we distribute P_STOP and P_CHOOSE into the CNF rules again, so
214 that in the next round we use new probN and probA to find
215 inner-probabilites.
217 The distribution of P_STOP and P_CHOOSE into CNF rules also happens in
218 init_normalize() (here along with the creation of P_STOP and
219 P_CHOOSE); P_STOP is used to create CNF rules where one branch of the
220 rule is STOP, P_CHOOSE is used to create rules of the form 
221 : h  -> h  _a_
222 : h_ -> h_ _a_
224 Since "adjacency" is not captured in regular CNF rules, we need two
225 probabilites for each rule, and inner() has to know when to use which.
227 ** TODO Corpus access
228 ** TOGROK sentences or rules as the "outer loop"?                     :OPTIMIZE:
229 In regard to the E/M-step, finding P_{STOP}, P_{CHOOSE}.
232 * Python-stuff
233 - [[file:src/pseudo.py][pseudo.py]]
234 - http://nltk.org/doc/en/structured-programming.html recursive dynamic
235 - http://nltk.org/doc/en/advanced-parsing.html 
236 - http://jaynes.colorado.edu/PythonIdioms.html