frprep 0.0.1.prealpha

Sign up to get free protection for your applications and to get access to all the features.
Files changed (138) hide show
  1. data/.yardopts +8 -0
  2. data/CHANGELOG.rdoc +0 -0
  3. data/LICENSE.rdoc +0 -0
  4. data/README.rdoc +0 -0
  5. data/lib/common/AbstractSynInterface.rb +1227 -0
  6. data/lib/common/BerkeleyInterface.rb +375 -0
  7. data/lib/common/CollinsInterface.rb +1165 -0
  8. data/lib/common/ConfigData.rb +694 -0
  9. data/lib/common/Counter.rb +18 -0
  10. data/lib/common/DBInterface.rb +48 -0
  11. data/lib/common/EnduserMode.rb +27 -0
  12. data/lib/common/Eval.rb +480 -0
  13. data/lib/common/FixSynSemMapping.rb +196 -0
  14. data/lib/common/FrPrepConfigData.rb +66 -0
  15. data/lib/common/FrprepHelper.rb +1324 -0
  16. data/lib/common/Graph.rb +345 -0
  17. data/lib/common/ISO-8859-1.rb +24 -0
  18. data/lib/common/ML.rb +186 -0
  19. data/lib/common/Maxent.rb +215 -0
  20. data/lib/common/MiniparInterface.rb +1388 -0
  21. data/lib/common/Optimise.rb +195 -0
  22. data/lib/common/Parser.rb +213 -0
  23. data/lib/common/RegXML.rb +269 -0
  24. data/lib/common/RosyConventions.rb +171 -0
  25. data/lib/common/SQLQuery.rb +243 -0
  26. data/lib/common/STXmlTerminalOrder.rb +194 -0
  27. data/lib/common/SalsaTigerRegXML.rb +2347 -0
  28. data/lib/common/SalsaTigerXMLHelper.rb +99 -0
  29. data/lib/common/SleepyInterface.rb +384 -0
  30. data/lib/common/SynInterfaces.rb +275 -0
  31. data/lib/common/TabFormat.rb +720 -0
  32. data/lib/common/Tiger.rb +1448 -0
  33. data/lib/common/TntInterface.rb +44 -0
  34. data/lib/common/Tree.rb +61 -0
  35. data/lib/common/TreetaggerInterface.rb +303 -0
  36. data/lib/common/headz.rb +338 -0
  37. data/lib/common/option_parser.rb +13 -0
  38. data/lib/common/ruby_class_extensions.rb +310 -0
  39. data/lib/fred/Baseline.rb +150 -0
  40. data/lib/fred/FileZipped.rb +31 -0
  41. data/lib/fred/FredBOWContext.rb +863 -0
  42. data/lib/fred/FredConfigData.rb +182 -0
  43. data/lib/fred/FredConventions.rb +232 -0
  44. data/lib/fred/FredDetermineTargets.rb +324 -0
  45. data/lib/fred/FredEval.rb +312 -0
  46. data/lib/fred/FredFeatureExtractors.rb +321 -0
  47. data/lib/fred/FredFeatures.rb +1061 -0
  48. data/lib/fred/FredFeaturize.rb +596 -0
  49. data/lib/fred/FredNumTrainingSenses.rb +27 -0
  50. data/lib/fred/FredParameters.rb +402 -0
  51. data/lib/fred/FredSplit.rb +84 -0
  52. data/lib/fred/FredSplitPkg.rb +180 -0
  53. data/lib/fred/FredTest.rb +607 -0
  54. data/lib/fred/FredTrain.rb +144 -0
  55. data/lib/fred/PlotAndREval.rb +480 -0
  56. data/lib/fred/fred.rb +45 -0
  57. data/lib/fred/md5.rb +23 -0
  58. data/lib/fred/opt_parser.rb +250 -0
  59. data/lib/frprep/AbstractSynInterface.rb +1227 -0
  60. data/lib/frprep/Ampersand.rb +37 -0
  61. data/lib/frprep/BerkeleyInterface.rb +375 -0
  62. data/lib/frprep/CollinsInterface.rb +1165 -0
  63. data/lib/frprep/ConfigData.rb +694 -0
  64. data/lib/frprep/Counter.rb +18 -0
  65. data/lib/frprep/FNCorpusXML.rb +643 -0
  66. data/lib/frprep/FNDatabase.rb +144 -0
  67. data/lib/frprep/FixSynSemMapping.rb +196 -0
  68. data/lib/frprep/FrPrepConfigData.rb +66 -0
  69. data/lib/frprep/FrameXML.rb +513 -0
  70. data/lib/frprep/FrprepHelper.rb +1324 -0
  71. data/lib/frprep/Graph.rb +345 -0
  72. data/lib/frprep/ISO-8859-1.rb +24 -0
  73. data/lib/frprep/MiniparInterface.rb +1388 -0
  74. data/lib/frprep/Parser.rb +213 -0
  75. data/lib/frprep/RegXML.rb +269 -0
  76. data/lib/frprep/STXmlTerminalOrder.rb +194 -0
  77. data/lib/frprep/SalsaTigerRegXML.rb +2347 -0
  78. data/lib/frprep/SalsaTigerXMLHelper.rb +99 -0
  79. data/lib/frprep/SleepyInterface.rb +384 -0
  80. data/lib/frprep/SynInterfaces.rb +275 -0
  81. data/lib/frprep/TabFormat.rb +720 -0
  82. data/lib/frprep/Tiger.rb +1448 -0
  83. data/lib/frprep/TntInterface.rb +44 -0
  84. data/lib/frprep/Tree.rb +61 -0
  85. data/lib/frprep/TreetaggerInterface.rb +303 -0
  86. data/lib/frprep/do_parses.rb +142 -0
  87. data/lib/frprep/frprep.rb +686 -0
  88. data/lib/frprep/headz.rb +338 -0
  89. data/lib/frprep/one_parsed_file.rb +28 -0
  90. data/lib/frprep/opt_parser.rb +94 -0
  91. data/lib/frprep/ruby_class_extensions.rb +310 -0
  92. data/lib/rosy/AbstractFeatureAndExternal.rb +240 -0
  93. data/lib/rosy/DBMySQL.rb +146 -0
  94. data/lib/rosy/DBSQLite.rb +280 -0
  95. data/lib/rosy/DBTable.rb +239 -0
  96. data/lib/rosy/DBWrapper.rb +176 -0
  97. data/lib/rosy/ExternalConfigData.rb +58 -0
  98. data/lib/rosy/FailedParses.rb +130 -0
  99. data/lib/rosy/FeatureInfo.rb +242 -0
  100. data/lib/rosy/GfInduce.rb +1115 -0
  101. data/lib/rosy/GfInduceFeature.rb +148 -0
  102. data/lib/rosy/InputData.rb +294 -0
  103. data/lib/rosy/RosyConfigData.rb +115 -0
  104. data/lib/rosy/RosyConfusability.rb +338 -0
  105. data/lib/rosy/RosyEval.rb +465 -0
  106. data/lib/rosy/RosyFeatureExtractors.rb +1609 -0
  107. data/lib/rosy/RosyFeaturize.rb +280 -0
  108. data/lib/rosy/RosyInspect.rb +336 -0
  109. data/lib/rosy/RosyIterator.rb +477 -0
  110. data/lib/rosy/RosyPhase2FeatureExtractors.rb +230 -0
  111. data/lib/rosy/RosyPruning.rb +165 -0
  112. data/lib/rosy/RosyServices.rb +744 -0
  113. data/lib/rosy/RosySplit.rb +232 -0
  114. data/lib/rosy/RosyTask.rb +19 -0
  115. data/lib/rosy/RosyTest.rb +826 -0
  116. data/lib/rosy/RosyTrain.rb +232 -0
  117. data/lib/rosy/RosyTrainingTestTable.rb +786 -0
  118. data/lib/rosy/TargetsMostFrequentFrame.rb +60 -0
  119. data/lib/rosy/View.rb +418 -0
  120. data/lib/rosy/opt_parser.rb +379 -0
  121. data/lib/rosy/rosy.rb +77 -0
  122. data/lib/shalmaneser/version.rb +3 -0
  123. data/test/frprep/test_opt_parser.rb +94 -0
  124. data/test/functional/functional_test_helper.rb +40 -0
  125. data/test/functional/sample_experiment_files/fred_test.salsa.erb +122 -0
  126. data/test/functional/sample_experiment_files/fred_train.salsa.erb +135 -0
  127. data/test/functional/sample_experiment_files/prp_test.salsa.erb +138 -0
  128. data/test/functional/sample_experiment_files/prp_test.salsa.fred.standalone.erb +120 -0
  129. data/test/functional/sample_experiment_files/prp_test.salsa.rosy.standalone.erb +120 -0
  130. data/test/functional/sample_experiment_files/prp_train.salsa.erb +138 -0
  131. data/test/functional/sample_experiment_files/prp_train.salsa.fred.standalone.erb +138 -0
  132. data/test/functional/sample_experiment_files/prp_train.salsa.rosy.standalone.erb +138 -0
  133. data/test/functional/sample_experiment_files/rosy_test.salsa.erb +257 -0
  134. data/test/functional/sample_experiment_files/rosy_train.salsa.erb +259 -0
  135. data/test/functional/test_fred.rb +47 -0
  136. data/test/functional/test_frprep.rb +52 -0
  137. data/test/functional/test_rosy.rb +20 -0
  138. metadata +270 -0
@@ -0,0 +1,338 @@
1
+ # RosyConfusability
2
+ # KE May 05
3
+ #
4
+ # Access instance database created by the Rosy role assignment system
5
+ # and compute the confusability of target categories
6
+ # for the data in the (training) database there.
7
+ #
8
+ # We define confusability as follows:
9
+ # Given a frame fr, let
10
+ # - fes(fr) the FEs of fr (a set)
11
+ # - gfs(fe) the grammatical functions realizing the FE fe in the data
12
+ # - gfs(fr) = U_{fe \in fes(fr)} gfs(fe) the grammatical functions realizing roles of fr
13
+ #
14
+ # Then the entropy of a grammatical function gf within fr is
15
+ #
16
+ # gfe_{fr}(gf) = \sum_{fe \in fes(fr)} -p(fe|gf) log p(fe|gf)
17
+ #
18
+ # where p(fe|gf) = f(gf, fe) / f(gf)
19
+ #
20
+ # And the confusability of a frame element fe of fr is
21
+ #
22
+ # c_{fr}(fe) = \sum_{gf \in gfs(fr)} p(gf|fe) gfe_{fr}(gf)
23
+ #
24
+ # where p(gf|fe) = f(gf, fe) / f(fe)
25
+
26
+ require "RosyConfigData"
27
+ require "RosyIterator"
28
+ require "RosyConventions"
29
+ require "TargetsMostFrequentFrame"
30
+
31
+ require "mysql"
32
+
33
+ class RosyConfusability
34
+ include TargetsMostFrequentSc
35
+
36
+ attr_reader :confusability, :counts_fe_glob, :frame_confusability, :overall_confusability
37
+
38
+ def initialize(exp) # RosyConfigData object
39
+ @exp = exp
40
+
41
+ @confusability = Hash.new(0.0)
42
+ @counts_fe_glob = Hash.new(0)
43
+ @counts_gffe_glob = Hash.new(0)
44
+ @frame_confusability = Hash.new(0.0)
45
+ @overall_confusability = 0.0
46
+
47
+ @frequent_gframes = [
48
+ # NO DUPLICATES
49
+ "Ext_Comp", "Mod", "Comp", "Gen",
50
+ "Ext_Obj", "Ext", "Ext_Obj_Comp", "Head",
51
+ "Ext_Mod", "Gen_Mod", "Mod_Comp", "Comp_Ext",
52
+ "Gen_Comp", "Ext_Gen", "Ext_Mod_Comp", "Head_Comp",
53
+ "Obj_Comp", "Obj", "Mod_Head", "Ext_Comp_Obj",
54
+ "Gen_Head", "Ext_Gen_Mod"
55
+ # with duplicates
56
+ # "Ext_Comp", "Mod", "Comp", "Gen",
57
+ # "Ext_Obj", "Ext", "", "Ext_Obj_Comp",
58
+ # "Ext_Comp_Comp", "Head", "Mod_Mod", "Gen_Mod",
59
+ # "Ext_Mod", "Comp_Comp", "Mod_Comp", "Ext_Gen",
60
+ # "Gen_Comp", "Head_Head", "Ext_Comp_Comp_Comp", "Head_Comp",
61
+ # # "Ext_Ext_Comp",
62
+ # # "Ext_Obj_Comp_Comp", "Obj_Comp",
63
+ # # "Ext_Mod_Mod", "Comp_Comp_Comp",
64
+ # # "Ext_Ext_Obj", "Ext_Mod_Comp", "Comp_Ext", "Obj",
65
+ # # "Ext_Ext", "Ext_Obj_Obj", "Mod_Mod_Mod", "Gen_Mod_Mod",
66
+ # # "Ext_Comp_Comp_Comp_Comp", "Gen_Head", "Mod_Head",
67
+ # # "Ext_Ext_Ext_Comp"
68
+ ].map { |string|
69
+ string.split("_")
70
+ }
71
+ end
72
+
73
+ def compute(splitID, # string: split ID, may be nil
74
+ additionals) # array:string: "target", "target_pos", "gframe", "fgframe"
75
+ ###
76
+ # open and initialize stuff:
77
+
78
+ # open database
79
+ database = Mysql.real_connect(@exp.get('host'), @exp.get('user'),
80
+ @exp.get('passwd'), @exp.get('dbname'))
81
+ # make an object that creates views.
82
+ # read one frame at a time.
83
+ iterator = RosyIterator.new(database, @exp, "train",
84
+ "splitID" => splitID,
85
+ "xwise" => "frame")
86
+ # get value for "no val"
87
+ noval = @exp.get("noval")
88
+
89
+ counts_frame = Hash.new(0)
90
+
91
+ # iterate through all frames and compute confusability of each FE
92
+ iterator.each_group { |group_descr_hash, frame|
93
+
94
+ $stderr.puts "Computing confusability for #{frame}"
95
+
96
+ # read all instances of the frame, columns: FE and GF
97
+ view = iterator.get_a_view_for_current_group(["sentid","gold", "fn_gf",
98
+ "target","target_pos", "frame"])
99
+
100
+ if additionals.include? "tmfframe"
101
+ # find most frequent gframe for each target
102
+ tmfframe = determine_target_most_frequent_sc(view, noval)
103
+ end
104
+
105
+ # count occurrences
106
+ counts_gf = Hash.new(0)
107
+ counts_fe = Hash.new(0)
108
+ counts_gffe = Hash.new(0)
109
+
110
+ view.each_sentence { |sentence|
111
+
112
+ # make string consisting of all FN GFs of this sentence
113
+ allgfs = Array.new()
114
+ sentence.each { |inst|
115
+ if inst["fn_gf"] != noval
116
+ allgfs << inst["fn_gf"]
117
+ end
118
+ }
119
+
120
+ # assume uniqueness of GFs
121
+ # design decision, could also be done differently.
122
+ # rationale: if a GF occurs more than once,
123
+ # it's probable that this is because we get more than
124
+ # one constituent for this GF, not because
125
+ # it actually occurred more than once in the
126
+ # original FrameNet annotation.
127
+ allgfs.uniq!
128
+
129
+ # now count each instance
130
+ sentence.each { |row|
131
+ if row["gold"] == "target"
132
+ # don't count target among the FEs
133
+ next
134
+ end
135
+
136
+ if row["gold"] != noval
137
+ counts_fe[row["gold"]] += 1
138
+ end
139
+ if row["fn_gf"] != noval and row["fn_gf"] != "target"
140
+ gf = row["fn_gf"]
141
+
142
+ additionals.each { |additional|
143
+ case additional
144
+ when "target"
145
+ gf << "_" + row["target"]
146
+ when "target_pos"
147
+ gf << "_" + row["target_pos"]
148
+ when "gframe"
149
+ gf << "_" + allgfs.join("_")
150
+
151
+ when "fgframe"
152
+ # find the maximal frequent frame subsuming allgfs
153
+ maxfgf = nil
154
+ @frequent_gframes.each { |fgframe|
155
+ if fgframe.subsumed_by?(allgfs)
156
+ # fgframe is a subset of allgfs
157
+ if maxfgf.nil? or fgframe.length() > maxfgf.length()
158
+ maxfgf = fgframe
159
+ end
160
+ end
161
+ }
162
+ if maxfgf.nil?
163
+ # nothing there that fits
164
+ # leave GF as is
165
+ else
166
+ gf << "_" + maxfgf.join("_")
167
+ end
168
+
169
+ when "tmfframe"
170
+ gf << "_" + tmfframe[tmf_target_key(row)]
171
+
172
+ else
173
+ raise "Don't know how to compute #{additional}"
174
+ end
175
+ }
176
+
177
+ counts_gf[gf] += 1
178
+ end
179
+
180
+ if row["gold"] != noval and gf
181
+ counts_gffe[gf + " " + row["gold"]] += 1
182
+ end
183
+ } # each row of sentence
184
+ } # each sentence of view
185
+
186
+ # compute gf entropy
187
+ # gfe_{fr}(gf) = \sum_{fe \in fes(fr)} -p(fe|gf) log_2 p(fe|gf)
188
+ #
189
+ # where p(fe|gf) = f(gf, fe) / f(gf)
190
+ gf_entropy = Hash.new
191
+
192
+ counts_gf.keys.each { |gf|
193
+ gf_entropy[gf] = 0.0
194
+
195
+ counts_fe.keys.each { |fe|
196
+ if counts_gf[gf] > 0
197
+ p_gf_fe = counts_gffe[gf + " " + fe].to_f / counts_gf[gf].to_f
198
+
199
+ # get log_2 via log_10
200
+ if p_gf_fe > 0.0
201
+ gf_entropy[gf] -= p_gf_fe * Math.log10(p_gf_fe) * 3.32193
202
+ end
203
+ end
204
+ } # each FE for this GF
205
+ } # each GF (gf entropy)
206
+
207
+ # compute FE confusability
208
+ # c_{fr}(fe) = \sum_{gf \in gfs(fr)} p(gf|fe) gfe_{fr}(gf)
209
+ #
210
+ # where p(gf|fe) = f(gf, fe) / f(fe)
211
+ counts_fe.keys.each { |fe|
212
+ @confusability[frame + " " + fe] = 0.0
213
+
214
+ counts_gf.keys.each { |gf|
215
+ if counts_fe[fe] > 0
216
+ p_fe_gf = counts_gffe[gf + " " + fe].to_f / counts_fe[fe].to_f
217
+
218
+ @confusability[frame + " " + fe] += p_fe_gf * gf_entropy[gf]
219
+ end
220
+ } # each GF for this FE
221
+ } # each FE (fe confusability)
222
+
223
+
224
+ # remember counts for FEs and GF/FE pairs
225
+ counts_fe.keys.each { |fe|
226
+ @counts_fe_glob[frame + " " + fe] = counts_fe[fe]
227
+ }
228
+ counts_gffe.each_pair {|event,freq|
229
+ @counts_gffe_glob[frame+" " +event] = freq
230
+ }
231
+
232
+ # omit rare FEs:
233
+ # anything below 5 occurrences
234
+ counts_fe.each_key { |fe|
235
+ if counts_fe[fe] < 5
236
+ @confusability.delete(frame + " " + fe)
237
+ end
238
+ }
239
+
240
+ # compute overall frame confusability
241
+ # omitting rare FEs with below 5 occurrences:
242
+ #
243
+ # c(fr) = sum_{fe \in fes(fr)} f(fe)/f(fr) * c_{fr}(fe)
244
+ # = \sum_{gf \in gfs(fr)} p(gf|fr) gfe_{fr}(gf)
245
+ #
246
+ # where p(gf|fr) = (sum_{fe\in fes(fr)} f(gf, fe)) / f(fr)
247
+ counts_frame[frame] = 0
248
+ counts_fe.each_value { |count|
249
+ if count >= 5
250
+ counts_frame[frame] += count
251
+ end
252
+ }
253
+ @frame_confusability[frame] = 0.0
254
+ counts_fe.each_pair { |fe, count|
255
+ if count >= 5
256
+ @frame_confusability[frame] += (count.to_f / counts_frame[frame].to_f) * @confusability[frame + " " + fe]
257
+ end
258
+ }
259
+ } # each frame
260
+
261
+ # compute overall confusability
262
+ # c = \sum{fr \in frames} f(fr)/N * c(fr)
263
+ #
264
+ # where N is the number of FE occurrences overall
265
+ counts_overall = 0
266
+ counts_frame.each_value { |count|
267
+ counts_overall += count
268
+ }
269
+ @overall_confusability = 0.0
270
+ counts_frame.each_pair { |frame, count|
271
+ @overall_confusability += (count.to_f / counts_overall.to_f) * @frame_confusability[frame]
272
+ }
273
+ end
274
+
275
+
276
+ # return a copy of @counts_fe_glob, from which all fes with less than 5 occurrences are deleted
277
+ def get_global_counts
278
+ global_counts = @counts_fe_glob.clone
279
+ global_counts.delete_if {|key, value| value < 5}
280
+ return global_counts
281
+ end
282
+
283
+ ###
284
+ #
285
+ # compute sparseness statistics over the set of
286
+ # base events used for computing the confusability
287
+ # returns an array of length 4:
288
+ # - number of events with freq 1
289
+ # - number of events with freq 2
290
+ # - number of events with freq 3-5
291
+ # - number of events with freq > 5
292
+
293
+ def counts()
294
+ counts = [0,0,0,0]
295
+ @counts_gffe_glob.each_value {|freq|
296
+ case freq
297
+ when 1
298
+ counts[0] += 1
299
+ when 2
300
+ counts[1] += 1
301
+ when 3..5
302
+ counts[2] += 1
303
+ else
304
+ counts[3] += 1
305
+ end
306
+ }
307
+ return counts
308
+ end
309
+
310
+ def to_file(filename)
311
+ begin
312
+ file = File.new(filename,"w")
313
+ rescue
314
+ raise "Couldn't open file #{filename} for writing."
315
+ end
316
+ Marshal.dump({"confusability" => @confusability,
317
+ "counts_fe_glob" => @counts_fe_glob,
318
+ "counts_gffe_glob" => @counts_gffe_glob,
319
+ "frame_confusability" => @frame_confusability,
320
+ "overall_confusability" => @overall_confusability
321
+ },
322
+ file)
323
+ end
324
+
325
+ def from_file(filename)
326
+ begin
327
+ file = File.new(filename)
328
+ rescue
329
+ raise "Couldn't open file #{filename} for reading."
330
+ end
331
+ hash = Marshal.load(file)
332
+ @confusability = hash["confusability"]
333
+ @counts_fe_glob = hash["counts_fe_glob"]
334
+ @counts_gffe_glob = hash["counts_gffe_glob"]
335
+ @frame_confusability = hash["frame_confusability"]
336
+ @overall_confusability = hash["overall_confusability"]
337
+ end
338
+ end
@@ -0,0 +1,465 @@
1
+ # RosyEval
2
+ # KE May 05
3
+ #
4
+ # Evaluation for Rosy:
5
+ # Precision, Recall, F-score
6
+ # Output to evaluation file,
7
+ # plus optional output of evaluation log file.
8
+ #
9
+ # Builds on the general Salsa Eval package
10
+
11
+ # Salsa packages
12
+ require "common/Eval"
13
+ require "common/ruby_class_extensions"
14
+
15
+ # Rosy packages
16
+ require "rosy/RosyIterator"
17
+ require "rosy/RosySplit"
18
+ require "rosy/RosyTask"
19
+ require "rosy/RosyPruning"
20
+
21
+ # Frprep packages
22
+ require "common/FrPrepConfigData"
23
+
24
+ #######################################################################
25
+ # This class is a subclass of the general evaluation class
26
+ # Eval, which makes evaluation results readable via
27
+ # readable object variables
28
+ #
29
+ # step: can be argrec, arglab, onestep, as usual, but also
30
+ # - "all":
31
+ # evaluate argrec and arglab together.
32
+ # When argrec == NONE, use the argrec value, else use the arglab value
33
+ # - "prune":
34
+ # evaluate the pruning column as if it were an argrec assignment
35
+ #
36
+ # When step == argrec or prune, evaluate _only_ the target class FE
37
+ # Otherwise, evaluate all target classes
38
+ class RosyEval < Eval
39
+ def initialize(exp, # RosyConfigData object: experiment file
40
+ ttt_obj, # RosyTrainingTestTable object
41
+ step, # string: argrec, arglab, onestep, all, prune
42
+ splitID, # string: splitlog ID, or nil
43
+ testID, # string: test ID, or nil
44
+ outfilename, # string: name of file to print output to
45
+ logfilename, # string: name of file to print eval log to (may be nil)
46
+ dont_adjoin_frprep_exp) # string: if non-nil, don't re-adjoin frprep experiment obj
47
+ @exp = exp
48
+ @step = step
49
+
50
+ if outfilename
51
+ $stderr.puts "Rosy evaluation: printing results to " + outfilename
52
+ end
53
+ if logfilename
54
+ $stderr.puts "and printing an evaluation log to " + logfilename
55
+ end
56
+
57
+ ##
58
+ # add preprocessing information to the experiment file object
59
+ unless dont_adjoin_frprep_exp
60
+ if splitID
61
+ # use split data
62
+ preproc_expname = @exp.get("preproc_descr_file_train")
63
+ else
64
+ # use test data
65
+ preproc_expname = @exp.get("preproc_descr_file_test")
66
+ end
67
+ if not(preproc_expname)
68
+ $stderr.puts "Please set the name of the preprocessing exp. file name"
69
+ $stderr.puts "in the experiment file."
70
+ exit 1
71
+ elsif not(File.readable?(preproc_expname))
72
+ $stderr.puts "Error in the experiment file:"
73
+ $stderr.puts "Parameter preproc_descr_file_train has to be a readable file."
74
+ exit 1
75
+ end
76
+ preproc_exp = FrPrepConfigData.new(preproc_expname)
77
+ @exp.adjoin(preproc_exp)
78
+ end
79
+
80
+ ##
81
+ # evaluate which labels?
82
+ if ["argrec", "prune"].include? @step
83
+ # evaluate only the label "FE"
84
+ super(outfilename, logfilename, "FE")
85
+ else
86
+ # evaluate all target classes
87
+ super(outfilename, logfilename)
88
+ end
89
+
90
+ ##
91
+ # what are classifier columns?
92
+ case @step
93
+ when "all"
94
+ # read one argrec and one arglab classifier run column
95
+ @classif_column_argrec = ttt_obj.existing_runlog("argrec", "test", testID,splitID)
96
+ @classif_column_arglab = ttt_obj.existing_runlog("arglab", "test", testID,splitID)
97
+ @columns = ["gold", @classif_column_argrec, @classif_column_arglab]
98
+
99
+ if @classif_column_argrec.nil? or @classif_column_arglab.nil?
100
+ # no run found for the given specifications
101
+ $stderr.puts "Couldn't determine the run to evaluate."
102
+ $stderr.puts "There were either none or too many possible runs given your specification.\n"
103
+ $stderr.puts "Here is a list of all runs the system knows for this experiment ID:\n\n"
104
+ $stderr.puts ttt_obj.runlog_to_s("test", testID, splitID)
105
+ exit 1
106
+ end
107
+
108
+ when "prune"
109
+ # read pruning column, evaluate as a kind of argrec assignment
110
+ unless Pruning.prune?(@exp)
111
+ raise "Error: Pruning evaluation without pruning column. Skipping."
112
+ end
113
+ @classif_column = Pruning.colname(@exp)
114
+ @columns = ["gold", @classif_column]
115
+
116
+ else
117
+ # read the classifier run column for the current step
118
+ @classif_column = ttt_obj.existing_runlog(@step, "test", testID,splitID)
119
+ @columns = ["gold", @classif_column]
120
+
121
+ if @classif_column.nil?
122
+ # no run found for the given specifications
123
+ $stderr.puts "Couldn't determine the run to evaluate."
124
+ $stderr.puts "There were either none or too many possible runs given your specification.\n"
125
+ $stderr.puts "Here is a list of all runs the system knows for this experiment ID:\n\n"
126
+ $stderr.puts ttt_obj.runlog_to_s("test", testID, splitID)
127
+ exit 1
128
+ end
129
+ end
130
+
131
+ ##
132
+ # make object for iterating through groups and making views
133
+ case @step
134
+ when "all"
135
+ # all: no step in particular
136
+ @iterator = RosyIterator.new(ttt_obj, exp, "test",
137
+ "step" => nil,
138
+ "testID" => testID,
139
+ "splitID" => splitID,
140
+ "xwise" => "frame")
141
+ when "prune"
142
+ # prune: use argrec
143
+ @iterator = RosyIterator.new(ttt_obj, exp, "test",
144
+ "step" => "argrec",
145
+ "testID" => testID,
146
+ "splitID" => splitID)
147
+
148
+ else
149
+ # use the given step
150
+ @iterator = RosyIterator.new(ttt_obj, exp, "test",
151
+ "step" => @step,
152
+ "testID" => testID,
153
+ "splitID" => splitID)
154
+ end
155
+
156
+ ##
157
+ # xwise
158
+ if @step == "all"
159
+ # argrec and arglab may have different xwises,
160
+ # which would create trouble.
161
+ # just use "frame" instead
162
+ @xwise = ["frame"]
163
+ else
164
+ # evaluate as you have trained and tested
165
+ @xwise = @iterator.get_xwise_column_names()
166
+ end
167
+
168
+ ##
169
+ # split? then include FE labels from unparsed sentences
170
+ # in count of gold labels
171
+ if splitID
172
+ # get a FailedParses object for this split
173
+ @failed_parses_split = FailedParses.new()
174
+ fp_filename = File.new_filename(@exp.instantiate("rosy_dir",
175
+ "exp_ID" => @exp.get("experiment_ID")),
176
+ @exp.instantiate("failed_file",
177
+ "exp_ID" => @exp.get("experiment_ID"),
178
+ "split_ID" => splitID,
179
+ "dataset" => "test"))
180
+ @failed_parses_split.load(fp_filename)
181
+ end
182
+
183
+ # announce the task
184
+ $stderr.puts "---------"
185
+ $stderr.print "Rosy experiment #{@exp.get("experiment_ID")}: Evaluating "
186
+ if splitID
187
+ $stderr.puts "on split dataset #{splitID}"
188
+ else
189
+ $stderr.puts "on test dataset #{testID}"
190
+ end
191
+ $stderr.puts "---------"
192
+ end
193
+
194
+ ###
195
+ protected
196
+
197
+ ###
198
+ # each_group
199
+ #
200
+ # yield each group name in turn
201
+ def each_group()
202
+
203
+ @view = nil
204
+
205
+ # for the sake of the failed parses module:
206
+ # it can split the failed parses by frame, target and target_pos,
207
+ # but if our "xwise" splits the data along any further columns,
208
+ # the failed parses module cannot know how to split up its failed parses.
209
+ # so see whether we've got any column names besides the three named above
210
+ # in our xwise,
211
+ # and if so, count the groups and split the failed parses evenly between them
212
+ normal_xwise_cols = ["frame", "target", "target_pos"] & @xwise
213
+ extra_xwise_cols = @xwise - normal_xwise_cols
214
+
215
+ # num_groups_for_normalxwise: hash: normal_xwise_values(string) -> num. of
216
+ # groups with these normal xwise values(integer)
217
+ # where the key normal_xwise_values is a conjunction of
218
+ # strings <col_name>=<value> joined by commas,
219
+ # and the column names are in alphabetical order
220
+ num_groups_for_normalxwise = Hash.new(0)
221
+
222
+ unless extra_xwise_cols.empty?
223
+ # we do have extra columns
224
+
225
+ # for each value sequence for normal_xwise_cols: find out how many values
226
+ # of extra xwise col.s there are
227
+ @iterator.each_group() { |group_descr_hash, group_name|
228
+
229
+ # make the hash key
230
+ key = normal_xwise_cols.sort.map { |col_name|
231
+ col_name + "=" + group_descr_hash[col_name]
232
+ }.join(",")
233
+
234
+ # record one occurrence of this hash key
235
+ num_groups_for_normalxwise[key] += 1
236
+ }
237
+ end
238
+
239
+ @iterator.each_group() { |group_descr_hash, group_name|
240
+
241
+ if @exp.get("verbose")
242
+ $stderr.puts group_name
243
+ end
244
+
245
+ # construct view for the current group
246
+ @view = @iterator.get_a_view_for_current_group(@columns)
247
+
248
+ ##
249
+ # get counts of FE labels from unparsed sentences:
250
+
251
+ # first take apart the group label to find
252
+ # the frame name, target name, target POS name in this group
253
+ # (all but one may be nil)
254
+ frame = target = target_pos = nil
255
+
256
+ # get a description of this group, array of pairs [column name, value]
257
+ # where column name is the name of one database column
258
+ @xwise.interleave(group_name.split()).each { |col_name, col_value|
259
+ case col_name
260
+ when "frame"
261
+ frame = col_value
262
+ when "target"
263
+ target = col_value
264
+ when "target_pos"
265
+ target_pos = col_value
266
+ else
267
+ # additional database columns: handled below
268
+ end
269
+ }
270
+
271
+ # do we have additional column names in "xwise", besides 'frame', 'target', 'target_pos'?
272
+ if extra_xwise_cols.empty?
273
+ split_between_groups = 1
274
+ else
275
+ key = normal_xwise_cols.sort.map { |col_name|
276
+ col_name + "=" + group_descr_hash[col_name]
277
+ }.join(",")
278
+ split_between_groups = num_groups_for_normalxwise[key]
279
+
280
+ # sanity check
281
+ if split_between_groups == 0
282
+ raise "shouldn't be here"
283
+ end
284
+ end
285
+
286
+ # failed_fes returns: hash that maps FE names [String] onto numbers of failed FEs [Int]
287
+ if @failed_parses_split
288
+ @failed_parses_split.failed_fes(frame, target, target_pos).each_pair { |fe, count|
289
+ # add this number of gold labels we failed to find
290
+ # to the number of gold labels that Eval counts
291
+
292
+ # if argrec, map all non-NONE FEs to "FE"
293
+ if @step == "argrec" and fe != @exp.get("noval")
294
+ fe = "FE"
295
+ end
296
+ inject_gold_counts(group_name, fe, (count.to_f / split_between_groups.to_f).round)
297
+ }
298
+ end
299
+
300
+ # yield the name of the group to the Eval object for evaluation
301
+ yield group_name
302
+ @view.close()
303
+ }
304
+ end
305
+
306
+ ###
307
+ # each_instance
308
+ #
309
+ # given a group name, yield each instance of this group in turn,
310
+ # or rather: yield pairs [gold_class(string), assigned_class(string)]
311
+ #
312
+ # this method depends on each_group() having been called before and
313
+ # having initialized @view to the right view object
314
+ def each_instance(group) # string: group name
315
+ case @step
316
+ when "all"
317
+ # step "all":
318
+ # if the argrec label is "NONE", use that as the assigned label.
319
+ # else use the arglab-label
320
+ @view.each_hash { |row|
321
+ if row[@classif_column_argrec] == @exp.get("noval")
322
+ yield [ row["gold"], row[@classif_column_argrec] ]
323
+ else
324
+ yield [ row["gold"], row[@classif_column_arglab] ]
325
+ end
326
+ }
327
+
328
+ when "prune"
329
+ # step "prune":
330
+ # if the pruning column has entry 1, regard as assignment "FE",
331
+ # else regard as assignment "NONE".
332
+ @view.each_hash { |row|
333
+ if row[@classif_column] == "1"
334
+ yield [ row["gold"], "FE" ]
335
+ else
336
+ yield [ row["gold"], @exp.get("noval") ]
337
+ end
338
+ }
339
+
340
+ else
341
+ # argrec, arglab, onestep:
342
+ # just yield pairs [goldlabel, classif_column_label]
343
+ # as given in the view
344
+
345
+ @view.each_hash { |row|
346
+ yield [row["gold"], row[@classif_column]]
347
+ }
348
+ end
349
+
350
+ end
351
+ end
352
+
353
+ ###########################################################33
354
+ # This is the class to be called from rosy.rb
355
+ ###########################################################33
356
+ class RosyEvalTask < RosyTask
357
+
358
+ def initialize(exp, # RosyConfigData object: experiment description
359
+ opts, # hash: runtime argument option (string) -> value (string)
360
+ ttt_obj) # RosyTrainingTestTable object
361
+
362
+ #####
363
+ # In enduser mode, this whole task is unavailable
364
+ in_enduser_mode_unavailable()
365
+
366
+ @exp = exp
367
+ @ttt_obj = ttt_obj
368
+
369
+ ##
370
+ # check runtime options
371
+ @step = "both"
372
+ @splitID = nil
373
+ @testID = default_test_ID()
374
+
375
+ opts.each do |opt,arg|
376
+ case opt
377
+ when "--step"
378
+ unless ["argrec", "arglab", "both", "onestep"].include? arg
379
+ raise "Classification step must be one of: argrec, arglab, both, onestep. I got: " + arg.to_s
380
+ end
381
+ @step = arg
382
+ when "--logID"
383
+ @splitID = arg
384
+ when "--testID"
385
+ @testID = arg
386
+ else
387
+ # this is an option that is okay but has already been read and used by rosy.rb
388
+ end
389
+ end
390
+ end
391
+
392
+ def perform()
393
+ dont_adjoin_frprep_exp = nil
394
+ original_step = @step
395
+
396
+ if ["both", "argrec", "onestep"].include? original_step and
397
+ Pruning.prune?(@exp)
398
+ # evaluate pruning
399
+ $stderr.puts "Rosy evaluating pruning"
400
+ @step = "prune"
401
+ perform_aux()
402
+ dont_adjoin_frprep_exp = "dont_adjoin_frprep_exp"
403
+ end
404
+
405
+ if original_step == "both"
406
+ # both? then do first argrec, then arglab
407
+ $stderr.puts "Rosy evaluating step argrec"
408
+ @step = "argrec"
409
+ perform_aux(dont_adjoin_frprep_exp)
410
+
411
+
412
+ $stderr.puts "Rosy evaluating step arglab"
413
+ @step = "arglab"
414
+ perform_aux("dont_adjoin_frprep_exp")
415
+
416
+ # KE Jan 30, 2006: evaluation "all" deactivated until we've
417
+ # figured out how to evaluate accuracy for the NONE class
418
+ # $stderr.puts "Rosy overall evaluation"
419
+ # @step = "all"
420
+ # perform_aux("dont_adjoin_frprep_exp")
421
+
422
+ else
423
+ # not both? then just do one
424
+ @step = original_step
425
+ perform_aux(dont_adjoin_frprep_exp)
426
+ end
427
+ end
428
+
429
+ ###############3
430
+ private
431
+
432
+ # perform_aux: do the actual work of the perform() method
433
+ # moved here because of the possibility of having @step=="both",
434
+ # which makes it necessary to perform two eval steps one after the other
435
+ def perform_aux(dont_adjoin_frprep_exp = nil) # string passed on to RosyEval initialize method
436
+ # construct names for evaluation output file
437
+ # and evaluation log file (which classifies each instances as correct/incorrect/unassigned)
438
+ if @splitID
439
+ outfilename_id = "split" + @splitID
440
+ else
441
+ outfilename_id = "test" + @testID
442
+ end
443
+ @outfilename = File.new_filename(@exp.instantiate("rosy_dir",
444
+ "exp_ID" => @exp.get("experiment_ID")),
445
+ @exp.instantiate("eval_file",
446
+ "exp_ID" => @exp.get("experiment_ID"),
447
+ "test_ID" => outfilename_id,
448
+ "step" => @step))
449
+
450
+ if @exp.get("print_eval_log")
451
+ @logfilename = File.new_filename(@exp.instantiate("rosy_dir",
452
+ "exp_ID" => @exp.get("experiment_ID")),
453
+ @exp.instantiate("log_file",
454
+ "exp_ID" => @exp.get("experiment_ID"),
455
+ "test_ID" => outfilename_id,
456
+ "step" => @step))
457
+ else
458
+ @logfilename = nil
459
+ end
460
+ @eval_obj = RosyEval.new(@exp, @ttt_obj, @step, @splitID, @testID,
461
+ @outfilename, @logfilename,
462
+ dont_adjoin_frprep_exp)
463
+ @eval_obj.compute()
464
+ end
465
+ end