més
[apertium.git] / apertium-tagger-training-tools-unicode / src / apertium-tagger-tl-trainer.C
blobbc412f20d1aa1e4c029856bf5f460a798ac63658
1 /*
2  * Copyright (C) 2004-2006 Felipe Sánchez-Martínez
3  * Copyright (C) 2006 Universitat d'Alacant
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18  * 02111-1307, USA.
19  */
20 /**
21  * Main program of the TL-driven method used to train a HMM-based
22  * part-of-speech tagger.  (source file)
23  *
24  * For a deeper description on how the method works read the paper:
25  *
26  * Exploring the use of target-language information to train the
27  * part-of-speech tagger of machine translation systems.  By Felipe
28  * Sánchez-Martínez, Juan Antonio Pérez-Ortiz and Mikel L. Forcada.
29  * In Lecture Notes in Computer Science 3230 (Advances in Natural
30  * Language Processing, Proceedings of EsTAL - España for Natural
31  * Language Processing), p. 137-148, October 20-22, 2004, Alicante,
32  * Spain.  
33  * © Springer-Verlag Berling Heidelberg 2004
34  * http://www.dlsi.ua.es/~fsanchez/pub/pdf/sanchez04b.pdf
35  *
36  *  @author   Felipe Sánchez-Martínez - fsanchez@dlsi.ua.es
37  */
39 #include "HMM_TL_driven_trainer.H"
40 #include "Utils.H"
41 #include "PathsPruner.H"
42 #include "configure.H"
44 #include <apertium/tagger_utils.h>
45 #include <apertium/tagger_word.h>
46 #include <apertium/utf_converter.h>
48 #include <iostream>
49 #include <getopt.h>
50 #include <clocale>
52 #define MODE_UNKNOWN 0 
53 #define MODE_TRAIN   1
55 using namespace std;
57 void help(char *name) {
58   cerr<<"USAGE:\n"
59       <<name<<" --tsxfile tsxfile --train <n> [--prune <m> <l> <p> <c> --initprob init.prob]"
60       <<" --file file --tscript tscript --lscript lscript [--trules transferrules]"
61       <<" [--supforms superficialforms] [--genpaths pathsfile]"
62       <<" [--translations transfile [--likelihoods likefiles]]"
63       <<" [--save <n>] [--norules] [--debug]\n\n"
64       <<"ARGUMENTS:\n"
65       <<"   --tsxfile|-x: Specify the tagger specification file in XML format\n"
66       <<"   --train|-t: Train the HMM-based part-of-speech tagger using\n"
67       <<"               information from the target language.\n"
68       <<"               Up to <n> words are processed from the training corpus\n"
69       <<"   --file|-f:  Used in conjunction with --train to specify the files\n"
70       <<"               the method will work with\n"
71       <<"               Input files:\n"
72       <<"                  file.dic: Full expanded dictionary\n"
73       <<"                  file.crp: Training text corpus\n"
74       <<"               Output files generated:\n"
75       <<"                  file.prob: HMM parameters\n"
76       <<"   --tscript|-r: Specify the full path to the translation script\n"
77       <<"   --lscript|-l: Specify the full path to the likelihood-evaluation\n"
78       <<"               script\n"
79       <<"   --trules|-u: Specify the file with the transfer rules used when translating\n"
80       <<"               (see xtract_transfer_rules.sh)\n"
81       <<"   --save|-s:  Specify after how many words the HMM parameters must\n"
82       <<"               be calculated and stored (optional)\n"
83       <<"   --norules|-n: Forbidden and enforce rules will not be used to discard\n"
84       <<"               disambiguation paths during training (by default those \n"
85       <<"               rules are used)\n"      
86       <<"   --genpaths|-g: Specify a file in which all disambiguations paths\n"
87       <<"               for each segment are written This cause translations not\n"
88       <<"               to be performed, for batch training, 1st stage\n"
89       <<"  --translations|-a: Specify a file from which all translations of\n"
90       <<"               each segment are read.  Used for batch training, 2nd stage\n"
91       <<"  --likelihoods|-e: Specify a file from which the likelihood of each\n"
92       <<"               translation is read. Used for batch training, 2nd/3rd stage\n"
93       <<"   --supforms|-p: Specify a set of superficial forms (separated by '|') that\n"
94       <<"               will be tested during the source-language text segmentation \n"
95       <<"               to prevent the method from segmenting at those superficial forms\n"
96       <<"   --prune|-k: Tell the algorithm that a disambiguation path pruning must be\n"
97       <<"               performed. Meaning of the arguments to --prune:\n"
98       <<"                 <m> mode of prunning:\n"
99       <<"                    1: Consider only those disambiguation paths whose a priori\n"
100       <<"                       likelihood is within the <p> mass probability of all the\n"
101       <<"                       disambiguation paths\n"
102       <<"                 <l> latency: after how many words should the parameters used to\n"
103       <<"                    discard disambiguation paths be updated with new ones\n"
104       <<"                    if -1, no updated will be performed\n"
105       <<"                 <p> mass of probability: used as a threshold to discard \n"
106       <<"                    disambiguation paths. It only has sense if pruning mode is 1\n"
107       <<"                    Range of possible values: 0 < p <= 1.0\n"
108       <<"                 <c> mixing constant function: constant to be used when mixing\n"
109       <<"                     new parameters (weigh of the new model).  Range: c > 0 \n"
110       <<"   --initprob|-b:  Specify the file (.prob) with the initial parameters to be used\n"
111       <<"               when pruning techniques are used\n"
112       <<"   --debug|-d: Print debug information while operating\n"
113       <<"   --help|-h:  Prints this help message\n"
114       <<"   --version|-v: Print version and license information and exits\n\n";
117 int main(int argc, char *argv[]) {
118   int mode=MODE_UNKNOWN;
120   Utils::debug=false;
122   TaggerWord::show_ingnored_string=false;
123   TransferRules transfer_rules;
125   cerr<<"LOCALE: "<<setlocale(LC_ALL,"")<<"\n";
127   int corpus_length=0;
128   string filename="";
129   string tsxfile="";
131   string tscript="";
132   string lscript="";
133   string trules="";
134   wstring supforms=L"";
135   string initprob="";
136   bool use_tags_rules=true;
137   int prune_m=-1; //If greater than 0, path pruning techniques will
138                   //be used
139   int prune_l=-1;
140   double prune_p=1.0;
141   double mixing_c=-1.0;
143   string pathsfile="";
144   string transfile="";
145   string likefile="";
148   int save_after_nwords=0;
150   int c;
151   int option_index=0;
153   //cerr<<PACKAGE_STRING<<"\n";
154   cerr<<"Command line: ";
155   for(int i=0; i<argc; i++)
156     cerr<<argv[i]<<" ";
157   cerr<<"\n";
159   while (true) {
160     static struct option long_options[] =
161       {
162         {"tsxfile",    required_argument, 0, 'x'},
163         {"train",      required_argument, 0, 't'},
164         {"file",       required_argument, 0, 'f'},
165         {"tscript",    required_argument, 0, 'r'},
166         {"lscript",    required_argument, 0, 'l'},
167         {"trules",     required_argument, 0, 'u'},
168         {"supforms",   required_argument, 0, 'p'},
169         {"prune",      required_argument, 0, 'k'},
170         {"initprob",   required_argument, 0, 'b'},
171         {"genpaths",    required_argument, 0, 'g'},
172         {"translations",required_argument, 0, 'a'},
173         {"likelihoods", required_argument, 0, 'e'},
174         {"save",       required_argument, 0, 's'},
175         {"norules",    no_argument,       0, 'n'},
176         {"debug",      no_argument,       0, 'd'},
177         {"help",       no_argument,       0, 'h'},
178         {"version",    no_argument,       0, 'v'},
179         {0, 0, 0, 0}
180       };
182     c=getopt_long(argc, argv, "x:t:f:r:l:u:k:b:g:a:e:s:ndhv",long_options, &option_index);
183     if (c==-1)
184       break;
185       
186     switch (c) {
187     case 'v':
188       cerr<<PACKAGE_STRING<<"\n";
189       cerr<<"LICENSE:\n\n"
190           <<"   Copyright (C) 2004-2006 Felipe Sánchez Martínez\n"
191           <<"                 2006 Universitat d'Alacant\n\n"
192           <<"   This program is free software; you can redistribute it and/or\n"
193           <<"   modify it under the terms of the GNU General Public License as\n"
194           <<"   published by the Free Software Foundation; either version 2 of the\n"
195           <<"   License, or (at your option) any later version.\n"
196           <<"   This program is distributed in the hope that it will be useful, but\n"
197           <<"   WITHOUT ANY WARRANTY; without even the implied warranty of\n"
198           <<"   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n"
199           <<"   General Public License for more details.\n"
200           <<"\n"
201           <<"   You should have received a copy of the GNU General Public License\n"
202           <<"   along with this program; if not, write to the Free Software\n"
203           <<"   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA\n"
204           <<"   02111-1307, USA.\n";
206       exit(EXIT_SUCCESS);
207     case 'x':
208       tsxfile=optarg;
209       break;
210     case 'k':
211       if (mode==MODE_TRAIN) {
212         prune_m=atoi(optarg);
213         prune_l=atoi(argv[optind++]);
214         prune_p=atof(argv[optind++]);
215         mixing_c=atof(argv[optind++]);
216         if (prune_m!=1) {
217           cerr<<"Error: Unknown prunnig mode "<<prune_m<<"\n";
218           help(argv[0]);
219           exit(EXIT_FAILURE);
220         }
221         if ((prune_p<=0) || (prune_p>1.0)) {
222           cerr<<"Error: mass of probability parameter given to --prune option must be grater than 0.0 and less or equal to 1.0\n";
223           help(argv[0]);
224           exit(EXIT_FAILURE);
225         }
226         if (mixing_c<=0) {
227           cerr<<"Error: Mixing constant c given to --prune must be grater than 0.0\n";
228           help(argv[0]);
229           exit(EXIT_FAILURE);
230         }
231       } else {
232         cerr<<"Error: --prune argument can only be used in conjunction with --train\n";
233         help(argv[0]);
234         exit(EXIT_FAILURE);
235       }
236       break;
237     case 'b': 
238       initprob=optarg;
239       break;
240     case 't':  //Training
241       corpus_length = atoi(optarg);
242       if(corpus_length<=0) {
243         cerr<<"Error: mandatory --train argument <n> must be a positive integer\n";
244         help(argv[0]);
245         exit(EXIT_FAILURE);
246       }
247       mode=MODE_TRAIN;
248       break;
249     case 'f':
250       filename=optarg;
251       break;
252     case 'r':
253       tscript = optarg;
254       break;
255     case 'l':
256       lscript = optarg;
257       break;
258     case 'u':
259       trules = optarg;
260       break;
261     case 's':
262       save_after_nwords=atoi(optarg);
263       break;
264     case 'g':
265       pathsfile=optarg;
266       break;
267     case 'a':
268       transfile=optarg;
269       break;
270     case 'e':
271       likefile=optarg;
272       break;
273     case 'n':
274       use_tags_rules=false;
275       break;
276     case 'p': {
277       supforms=UtfConverter::fromUtf8(optarg);
278       break;
279     }
280     case 'd':
281       Utils::debug=true;
282       break;
283     case 'h':
284       help(argv[0]); 
285       exit(EXIT_SUCCESS);
286       break;     
287     default:
288       help(argv[0]);
289       exit(EXIT_FAILURE);
290       break;
291     }    
292   }
294   if(tsxfile=="") {
295     cerr<<"Error: No tagger specification file was given, Use --tsxfile argument to provide that file\n";
296     help(argv[0]);
297     exit(EXIT_FAILURE);
298   }
300   if (mode==MODE_UNKNOWN) {
301     help(argv[0]);
302     exit(EXIT_FAILURE);
303   }
305   HMM_TL_driven_trainer hmm_trainer(tsxfile, &transfer_rules);
306   hmm_trainer.set_use_tags_rules(use_tags_rules);
308   Utils::translation_script=tscript;
309   Utils::likelihood_script=lscript;
311   PathsPruner::mode=prune_m;
312   PathsPruner::latency=prune_l;
313   PathsPruner::probmass=prune_p;
315   if (mode==MODE_TRAIN) {
316     if (filename=="") {
317       cerr<<"Error: When using --train a file must be provided through the --file option\n";
318       help(argv[0]);
319       exit(EXIT_FAILURE);
320     }
321     if (tscript=="") {
322       cerr<<"Error: When using --train a translation script must be given through the --tscript option\n";
323       help(argv[0]);
324       exit(EXIT_FAILURE);
325     }
326     if (lscript=="") {
327       cerr<<"Error: When using --train a likelihood-evaluation script must be provided through the --lscript option\n";
328       help(argv[0]);
329       exit(EXIT_FAILURE);
330     }
331     if ((prune_m>0) && (initprob=="")) {
332       cerr<<"Error: When using --train <n> --prune <k> initial parameters file must be provided through the --initprob option\n";
333       help(argv[0]);
334       exit(EXIT_FAILURE);
335     }
336   }
338   if (trules!="") {
339     cerr<<"Reading transfer rules from file '"<<trules<<"' "<<flush;
340     transfer_rules.read_rules_from_file(trules);
341     cerr<<"done.\n";
342   }
343   transfer_rules.compile_regular_expressions();
345   if (supforms!=L"") {
346     cerr<<"Reading superficial forms to take into account when segmenting source-language text ... ";
347     transfer_rules.set_superficial_forms(supforms);
348     cerr<<"done.";
349   }
351   FILE *fdic, *fcrp, *fprob;
353   ofstream fpaths;
354   ifstream ftrans;
355   ifstream flike;
357   if (mode==MODE_TRAIN) {
358     if (prune_m>0) {
359       fprob = fopen(initprob.c_str(), "r");
360       if (!fprob) tagger_utils::file_name_error(initprob);
361       cerr<<"Reading apertium-tagger parameters from file '"<<initprob<<"'\n";
362       hmm_trainer.read_parameters(fprob);
363       fclose(fprob);
365       if(pathsfile!="") {
366         cerr<<"Error: --genpaths cannot be used when --prune.\n";
367         cerr<<"First use --genpaths without --prune (normal training) and the\n";
368         cerr<<"use --translations and --likelihood in conjuntion (or not) with --prune\n";
369         help(argv[0]);
370         exit(EXIT_FAILURE);
371       }
372     } else {
373       fdic = fopen((filename+".dic").c_str(), "r");
374       if (!fdic) tagger_utils::file_name_error(filename+".dic");
375       hmm_trainer.read_dictionary(fdic);
376       fclose(fdic);
377     }
379     fcrp = fopen((filename+".crp").c_str(), "r");
380     if (!fcrp) tagger_utils::file_name_error(filename+".crp");
382     fprob = fopen((filename+".prob").c_str(), "w");
383     if (!fprob) tagger_utils::file_name_error(filename+".prob");
385     if (pathsfile!="") {
386       fpaths.open(pathsfile.c_str(), ios::out | ios::trunc);
387       if(fpaths.fail()) tagger_utils::file_name_error(pathsfile);
388     }
390     if(transfile!="") {
391       ftrans.open(transfile.c_str(), ios::in);
392       if (ftrans.fail()) tagger_utils::file_name_error(transfile);
393     }
395     if(likefile!="") {
396       flike.open(likefile.c_str(), ios::in);
397       if(flike.fail()) tagger_utils::file_name_error(likefile);
398     }
399   }
401   if (mode==MODE_TRAIN) {
402     if (prune_m<=0)
403       hmm_trainer.train(fcrp, corpus_length, save_after_nwords, filename, fpaths, ftrans, flike);
404     else {
405       hmm_trainer.train_pruning(fcrp, corpus_length, save_after_nwords, filename, mixing_c, ftrans, flike);
406     }
407   } 
409   if (mode==MODE_TRAIN) {
410     fclose(fcrp);
412     hmm_trainer.write_parameters(fprob);
413     fclose(fprob);
414   }
416   exit(EXIT_SUCCESS);