#include #include #include #include #include #include #include "tdict.h" #include "filelib.h" #include "sampler.h" #include "ccrp_onetable.h" #include "array2d.h" using namespace std; using namespace std::tr1; namespace po = boost::program_options; void PrintTopCustomers(const CCRP_OneTable& crp) { for (CCRP_OneTable::const_iterator it = crp.begin(); it != crp.end(); ++it) { cerr << " " << TD::Convert(it->first) << " = " << it->second << endl; } } void PrintAlignment(const vector& src, const vector& trg, const vector& a) { cerr << TD::GetString(src) << endl << TD::GetString(trg) << endl; Array2D al(src.size(), trg.size()); for (int i = 0; i < a.size(); ++i) if (a[i] != 255) al(a[i], i) = true; cerr << al << endl; } void InitCommandLine(int argc, char** argv, po::variables_map* conf) { po::options_description opts("Configuration options"); opts.add_options() ("samples,s",po::value()->default_value(1000),"Number of samples") ("input,i",po::value(),"Read parallel data from") ("random_seed,S",po::value(), "Random seed"); po::options_description clo("Command line options"); clo.add_options() ("config", po::value(), "Configuration file") ("help,h", "Print this help message and exit"); po::options_description dconfig_options, dcmdline_options; dconfig_options.add(opts); dcmdline_options.add(opts).add(clo); po::store(parse_command_line(argc, argv, dcmdline_options), *conf); if (conf->count("config")) { ifstream config((*conf)["config"].as().c_str()); po::store(po::parse_config_file(config, dconfig_options), *conf); } po::notify(*conf); if (conf->count("help") || (conf->count("input") == 0)) { cerr << dcmdline_options << endl; exit(1); } } struct Unigram; struct Bigram { Bigram() : trg(), cond() {} Bigram(WordID prev, WordID cur, WordID t) : trg(t) { cond.first = prev; cond.second = cur; } const pair& ConditioningPair() const { return cond; } WordID& prev_src() { return cond.first; } WordID& cur_src() { return cond.second; } const WordID& prev_src() const { return cond.first; } const WordID& cur_src() const { return cond.second; } WordID trg; private: pair cond; }; struct Unigram { Unigram() : cur_src(), trg() {} Unigram(WordID s, WordID t) : cur_src(s), trg(t) {} WordID cur_src; WordID trg; }; ostream& operator<<(ostream& os, const Bigram& b) { os << "( " << TD::Convert(b.trg) << " | " << TD::Convert(b.prev_src()) << " , " << TD::Convert(b.cur_src()) << " )"; return os; } ostream& operator<<(ostream& os, const Unigram& u) { os << "( " << TD::Convert(u.trg) << " | " << TD::Convert(u.cur_src) << " )"; return os; } bool operator==(const Bigram& a, const Bigram& b) { return a.trg == b.trg && a.cur_src() == b.cur_src() && a.prev_src() == b.prev_src(); } bool operator==(const Unigram& a, const Unigram& b) { return a.trg == b.trg && a.cur_src == b.cur_src; } size_t hash_value(const Bigram& b) { size_t h = boost::hash_value(b.prev_src()); boost::hash_combine(h, boost::hash_value(b.cur_src())); boost::hash_combine(h, boost::hash_value(b.trg)); return h; } size_t hash_value(const Unigram& u) { size_t h = boost::hash_value(u.cur_src); boost::hash_combine(h, boost::hash_value(u.trg)); return h; } void ReadParallelCorpus(const string& filename, vector >* f, vector >* e, set* vocab_f, set* vocab_e) { f->clear(); e->clear(); vocab_f->clear(); vocab_e->clear(); istream* in; if (filename == "-") in = &cin; else in = new ifstream(filename.c_str()); assert(*in); string line; const WordID kDIV = TD::Convert("|||"); vector tmp; while(*in) { getline(*in, line); if (line.empty() && !*in) break; e->push_back(vector()); f->push_back(vector()); vector& le = e->back(); vector& lf = f->back(); tmp.clear(); TD::ConvertSentence(line, &tmp); bool isf = true; for (unsigned i = 0; i < tmp.size(); ++i) { const int cur = tmp[i]; if (isf) { if (kDIV == cur) { isf = false; } else { lf.push_back(cur); vocab_f->insert(cur); } } else { assert(cur != kDIV); le.push_back(cur); vocab_e->insert(cur); } } assert(isf == false); } if (in != &cin) delete in; } struct UnigramModel { UnigramModel(size_t src_voc_size, size_t trg_voc_size) : unigrams(TD::NumWords() + 1, CCRP_OneTable(1,1,1,1)), p0(1.0 / trg_voc_size) {} void increment(const Bigram& b) { unigrams[b.cur_src()].increment(b.trg); } void decrement(const Bigram& b) { unigrams[b.cur_src()].decrement(b.trg); } double prob(const Bigram& b) const { const double q0 = unigrams[b.cur_src()].prob(b.trg, p0); return q0; } double LogLikelihood() const { double llh = 0; for (unsigned i = 0; i < unigrams.size(); ++i) { const CCRP_OneTable& crp = unigrams[i]; if (crp.num_customers() > 0) { llh += crp.log_crp_prob(); llh += crp.num_tables() * log(p0); } } return llh; } void ResampleHyperparameters(MT19937* rng) { for (unsigned i = 0; i < unigrams.size(); ++i) unigrams[i].resample_hyperparameters(rng); } vector > unigrams; // unigrams[src].prob(trg, p0) = p(trg|src) const double p0; }; struct BigramModel { BigramModel(size_t src_voc_size, size_t trg_voc_size) : unigrams(TD::NumWords() + 1, CCRP_OneTable(1,1,1,1)), p0(1.0 / trg_voc_size) {} void increment(const Bigram& b) { BigramMap::iterator it = bigrams.find(b.ConditioningPair()); if (it == bigrams.end()) { it = bigrams.insert(make_pair(b.ConditioningPair(), CCRP_OneTable(1,1,1,1))).first; } if (it->second.increment(b.trg)) unigrams[b.cur_src()].increment(b.trg); } void decrement(const Bigram& b) { BigramMap::iterator it = bigrams.find(b.ConditioningPair()); assert(it != bigrams.end()); if (it->second.decrement(b.trg)) { unigrams[b.cur_src()].decrement(b.trg); if (it->second.num_customers() == 0) bigrams.erase(it); } } double prob(const Bigram& b) const { const double q0 = unigrams[b.cur_src()].prob(b.trg, p0); const BigramMap::const_iterator it = bigrams.find(b.ConditioningPair()); if (it == bigrams.end()) return q0; return it->second.prob(b.trg, q0); } double LogLikelihood() const { double llh = 0; for (unsigned i = 0; i < unigrams.size(); ++i) { const CCRP_OneTable& crp = unigrams[i]; if (crp.num_customers() > 0) { llh += crp.log_crp_prob(); llh += crp.num_tables() * log(p0); } } for (BigramMap::const_iterator it = bigrams.begin(); it != bigrams.end(); ++it) { const CCRP_OneTable& crp = it->second; const WordID cur_src = it->first.second; llh += crp.log_crp_prob(); for (CCRP_OneTable::const_iterator bit = crp.begin(); bit != crp.end(); ++bit) { llh += log(unigrams[cur_src].prob(bit->second, p0)); } } return llh; } void ResampleHyperparameters(MT19937* rng) { for (unsigned i = 0; i < unigrams.size(); ++i) unigrams[i].resample_hyperparameters(rng); for (BigramMap::iterator it = bigrams.begin(); it != bigrams.end(); ++it) it->second.resample_hyperparameters(rng); } typedef unordered_map, CCRP_OneTable, boost::hash > > BigramMap; BigramMap bigrams; // bigrams[(src-1,src)].prob(trg, q0) = p(trg|src,src-1) vector > unigrams; // unigrams[src].prob(trg, p0) = p(trg|src) const double p0; }; struct BigramAlignmentModel { BigramAlignmentModel(size_t src_voc_size, size_t trg_voc_size) : bigrams(TD::NumWords() + 1, CCRP_OneTable(1,1,1,1)), p0(1.0 / src_voc_size) {} void increment(WordID prev, WordID next) { bigrams[prev].increment(next); // hierarchy? } void decrement(WordID prev, WordID next) { bigrams[prev].decrement(next); // hierarchy? } double prob(WordID prev, WordID next) { return bigrams[prev].prob(next, p0); } double LogLikelihood() const { double llh = 0; for (unsigned i = 0; i < bigrams.size(); ++i) { const CCRP_OneTable& crp = bigrams[i]; if (crp.num_customers() > 0) { llh += crp.log_crp_prob(); llh += crp.num_tables() * log(p0); } } return llh; } vector > bigrams; // bigrams[prev].prob(next, p0) = p(next|prev) const double p0; }; struct Alignment { vector a; }; int main(int argc, char** argv) { po::variables_map conf; InitCommandLine(argc, argv, &conf); const unsigned samples = conf["samples"].as(); boost::shared_ptr prng; if (conf.count("random_seed")) prng.reset(new MT19937(conf["random_seed"].as())); else prng.reset(new MT19937); MT19937& rng = *prng; vector > corpuse, corpusf; set vocabe, vocabf; cerr << "Reading corpus...\n"; ReadParallelCorpus(conf["input"].as(), &corpusf, &corpuse, &vocabf, &vocabe); cerr << "F-corpus size: " << corpusf.size() << " sentences\t (" << vocabf.size() << " word types)\n"; cerr << "E-corpus size: " << corpuse.size() << " sentences\t (" << vocabe.size() << " word types)\n"; assert(corpusf.size() == corpuse.size()); const size_t corpus_len = corpusf.size(); const WordID kNULL = TD::Convert(""); const WordID kBOS = TD::Convert(""); const WordID kEOS = TD::Convert(""); Bigram TT(kBOS, TD::Convert("我"), TD::Convert("i")); Bigram TT2(kBOS, TD::Convert("要"), TD::Convert("i")); UnigramModel model(vocabf.size(), vocabe.size()); vector alignments(corpus_len); for (unsigned ci = 0; ci < corpus_len; ++ci) { const vector& src = corpusf[ci]; const vector& trg = corpuse[ci]; vector& alg = alignments[ci].a; alg.resize(trg.size()); int lenp1 = src.size() + 1; WordID prev_src = kBOS; for (int j = 0; j < trg.size(); ++j) { int samp = lenp1 * rng.next(); --samp; if (samp < 0) samp = 255; alg[j] = samp; WordID cur_src = (samp == 255 ? kNULL : src[alg[j]]); Bigram b(prev_src, cur_src, trg[j]); model.increment(b); prev_src = cur_src; } Bigram b(prev_src, kEOS, kEOS); model.increment(b); } cerr << "Initial LLH: " << model.LogLikelihood() << endl; SampleSet ss; for (unsigned si = 0; si < 50; ++si) { for (unsigned ci = 0; ci < corpus_len; ++ci) { const vector& src = corpusf[ci]; const vector& trg = corpuse[ci]; vector& alg = alignments[ci].a; WordID prev_src = kBOS; for (unsigned j = 0; j < trg.size(); ++j) { unsigned char& a_j = alg[j]; WordID cur_e_a_j = (a_j == 255 ? kNULL : src[a_j]); Bigram b(prev_src, cur_e_a_j, trg[j]); //cerr << "DEC: " << b << "\t" << nextb << endl; model.decrement(b); ss.clear(); for (unsigned i = 0; i <= src.size(); ++i) { const WordID cur_src = (i ? src[i-1] : kNULL); b.cur_src() = cur_src; ss.add(model.prob(b)); } int sampled_a_j = rng.SelectSample(ss); a_j = (sampled_a_j ? sampled_a_j - 1 : 255); cur_e_a_j = (a_j == 255 ? kNULL : src[a_j]); b.cur_src() = cur_e_a_j; //cerr << "INC: " << b << "\t" << nextb << endl; model.increment(b); prev_src = cur_e_a_j; } } cerr << '.' << flush; if (si % 10 == 9) { cerr << "[LLH prev=" << model.LogLikelihood(); //model.ResampleHyperparameters(&rng); cerr << " new=" << model.LogLikelihood() << "]\n"; //pair xx = make_pair(kBOS, TD::Convert("我")); //PrintTopCustomers(model.bigrams.find(xx)->second); cerr << "p(" << TT << ") = " << model.prob(TT) << endl; cerr << "p(" << TT2 << ") = " << model.prob(TT2) << endl; PrintAlignment(corpusf[0], corpuse[0], alignments[0].a); } } { // MODEL 2 BigramModel model(vocabf.size(), vocabe.size()); BigramAlignmentModel amodel(vocabf.size(), vocabe.size()); for (unsigned ci = 0; ci < corpus_len; ++ci) { const vector& src = corpusf[ci]; const vector& trg = corpuse[ci]; vector& alg = alignments[ci].a; WordID prev_src = kBOS; for (int j = 0; j < trg.size(); ++j) { WordID cur_src = (alg[j] == 255 ? kNULL : src[alg[j]]); Bigram b(prev_src, cur_src, trg[j]); model.increment(b); amodel.increment(prev_src, cur_src); prev_src = cur_src; } amodel.increment(prev_src, kEOS); Bigram b(prev_src, kEOS, kEOS); model.increment(b); } cerr << "Initial LLH: " << model.LogLikelihood() << " " << amodel.LogLikelihood() << endl; SampleSet ss; for (unsigned si = 0; si < samples; ++si) { for (unsigned ci = 0; ci < corpus_len; ++ci) { const vector& src = corpusf[ci]; const vector& trg = corpuse[ci]; vector& alg = alignments[ci].a; WordID prev_src = kBOS; for (unsigned j = 0; j < trg.size(); ++j) { unsigned char& a_j = alg[j]; WordID cur_e_a_j = (a_j == 255 ? kNULL : src[a_j]); Bigram b(prev_src, cur_e_a_j, trg[j]); WordID next_src = kEOS; WordID next_trg = kEOS; if (j < (trg.size() - 1)) { next_src = (alg[j+1] == 255 ? kNULL : src[alg[j + 1]]); next_trg = trg[j + 1]; } Bigram nextb(cur_e_a_j, next_src, next_trg); //cerr << "DEC: " << b << "\t" << nextb << endl; model.decrement(b); model.decrement(nextb); amodel.decrement(prev_src, cur_e_a_j); amodel.decrement(cur_e_a_j, next_src); ss.clear(); for (unsigned i = 0; i <= src.size(); ++i) { const WordID cur_src = (i ? src[i-1] : kNULL); b.cur_src() = cur_src; ss.add(model.prob(b) * model.prob(nextb) * amodel.prob(prev_src, cur_src) * amodel.prob(cur_src, next_src)); //cerr << log(ss[ss.size() - 1]) << "\t" << b << endl; } int sampled_a_j = rng.SelectSample(ss); a_j = (sampled_a_j ? sampled_a_j - 1 : 255); cur_e_a_j = (a_j == 255 ? kNULL : src[a_j]); b.cur_src() = cur_e_a_j; nextb.prev_src() = cur_e_a_j; //cerr << "INC: " << b << "\t" << nextb << endl; //exit(1); model.increment(b); model.increment(nextb); amodel.increment(prev_src, cur_e_a_j); amodel.increment(cur_e_a_j, next_src); prev_src = cur_e_a_j; } } cerr << '.' << flush; if (si % 10 == 9) { cerr << "[LLH prev=" << (model.LogLikelihood() + amodel.LogLikelihood()); //model.ResampleHyperparameters(&rng); cerr << " new=" << model.LogLikelihood() << "]\n"; pair xx = make_pair(kBOS, TD::Convert("我")); cerr << "p(" << TT << ") = " << model.prob(TT) << endl; cerr << "p(" << TT2 << ") = " << model.prob(TT2) << endl; pair xx2 = make_pair(kBOS, TD::Convert("要")); PrintTopCustomers(model.bigrams.find(xx)->second); //PrintTopCustomers(amodel.bigrams[TD::Convert("")]); //PrintTopCustomers(model.unigrams[TD::Convert("")]); PrintAlignment(corpusf[0], corpuse[0], alignments[0].a); } } } return 0; }