diff options
-rw-r--r-- | gi/pyp-topics/src/Makefile.am | 5 | ||||
-rw-r--r-- | gi/pyp-topics/src/contexts_corpus.cc | 56 | ||||
-rw-r--r-- | gi/pyp-topics/src/contexts_corpus.hh | 39 | ||||
-rw-r--r-- | gi/pyp-topics/src/contexts_lexer.h | 22 | ||||
-rw-r--r-- | gi/pyp-topics/src/contexts_lexer.l | 110 | ||||
-rw-r--r-- | gi/pyp-topics/src/corpus.cc | 7 | ||||
-rw-r--r-- | gi/pyp-topics/src/corpus.hh | 3 | ||||
-rw-r--r-- | gi/pyp-topics/src/pyp-topics.hh | 7 | ||||
-rw-r--r-- | gi/pyp-topics/src/train.cc | 43 |
9 files changed, 272 insertions, 20 deletions
diff --git a/gi/pyp-topics/src/Makefile.am b/gi/pyp-topics/src/Makefile.am index 51b47294..3d62a334 100644 --- a/gi/pyp-topics/src/Makefile.am +++ b/gi/pyp-topics/src/Makefile.am @@ -1,6 +1,9 @@ bin_PROGRAMS = pyp-topics-train -pyp_topics_train_SOURCES = corpus.cc gammadist.c gzstream.cc mt19937ar.c pyp-topics.cc train.cc +contexts_lexer.cc: contexts_lexer.l + $(LEX) -s -CF -8 -o$@ $< + +pyp_topics_train_SOURCES = corpus.cc gammadist.c gzstream.cc mt19937ar.c pyp-topics.cc train.cc contexts_lexer.cc contexts_corpus.cc pyp_topics_train_LDADD = -lz AM_CPPFLAGS = -W -Wall -Wno-sign-compare -funroll-loops diff --git a/gi/pyp-topics/src/contexts_corpus.cc b/gi/pyp-topics/src/contexts_corpus.cc new file mode 100644 index 00000000..0b3ec644 --- /dev/null +++ b/gi/pyp-topics/src/contexts_corpus.cc @@ -0,0 +1,56 @@ +#include <sstream> +#include <iostream> +#include <set> + +#include "contexts_corpus.hh" +#include "gzstream.hh" +#include "contexts_lexer.h" + +using namespace std; + +////////////////////////////////////////////////// +// ContextsCorpus +////////////////////////////////////////////////// + +void read_callback(const ContextsLexer::PhraseContextsType& new_contexts, void* extra) { + assert(new_contexts.contexts.size() == new_contexts.counts.size()); + + ContextsCorpus* corpus_ptr = static_cast<ContextsCorpus*>(extra); + Document* doc(new Document()); + + //std::cout << "READ: " << new_contexts.phrase << "\t"; + + for (int i=0; i < new_contexts.contexts.size(); ++i) { + std::string context_str = ""; + for (ContextsLexer::Context::const_iterator it=new_contexts.contexts[i].begin(); + it != new_contexts.contexts[i].end(); ++it) { + //std::cout << *it << " "; + if (it != new_contexts.contexts[i].begin()) + context_str += "__"; + context_str += *it; + } + + WordID id = corpus_ptr->m_dict.Convert(context_str); + int count = new_contexts.counts[i]; + for (int i=0; i<count; ++i) + doc->push_back(id); + corpus_ptr->m_num_terms += count; + + //std::cout << context_str << " (" << id << ") ||| C=" << count << " ||| "; + } + //std::cout << std::endl; + + corpus_ptr->m_documents.push_back(doc); +} + +unsigned ContextsCorpus::read_contexts(const std::string &filename) { + m_num_terms = 0; + m_num_types = 0; + + igzstream in(filename.c_str()); + ContextsLexer::ReadContexts(&in, read_callback, this); + + m_num_types = m_dict.max(); + + return m_documents.size(); +} diff --git a/gi/pyp-topics/src/contexts_corpus.hh b/gi/pyp-topics/src/contexts_corpus.hh new file mode 100644 index 00000000..e680cef5 --- /dev/null +++ b/gi/pyp-topics/src/contexts_corpus.hh @@ -0,0 +1,39 @@ +#ifndef _CONTEXTS_CORPUS_HH +#define _CONTEXTS_CORPUS_HH + +#include <vector> +#include <string> +#include <map> + +#include <boost/ptr_container/ptr_vector.hpp> + +#include "corpus.hh" +#include "contexts_lexer.h" +#include "../../../decoder/dict.h" + +//////////////////////////////////////////////////////////////// +// ContextsCorpus +//////////////////////////////////////////////////////////////// + +class ContextsCorpus : public Corpus { + friend void read_callback(const ContextsLexer::PhraseContextsType&, void*); + +public: + typedef boost::ptr_vector<Document>::const_iterator const_iterator; + +public: + ContextsCorpus() {} + virtual ~ContextsCorpus() {} + + unsigned read_contexts(const std::string &filename); + + TermBackoffPtr backoff_index() { + return m_backoff; + } + +private: + TermBackoffPtr m_backoff; + Dict m_dict; +}; + +#endif // _CONTEXTS_CORPUS_HH diff --git a/gi/pyp-topics/src/contexts_lexer.h b/gi/pyp-topics/src/contexts_lexer.h new file mode 100644 index 00000000..f9a1b21c --- /dev/null +++ b/gi/pyp-topics/src/contexts_lexer.h @@ -0,0 +1,22 @@ +#ifndef _CONTEXTS_LEXER_H_ +#define _CONTEXTS_LEXER_H_ + +#include <iostream> +#include <vector> +#include <string> + +#include "../../../decoder/dict.h" + +struct ContextsLexer { + typedef std::vector<std::string> Context; + struct PhraseContextsType { + std::string phrase; + std::vector<Context> contexts; + std::vector<int> counts; + }; + + typedef void (*ContextsCallback)(const PhraseContextsType& new_contexts, void* extra); + static void ReadContexts(std::istream* in, ContextsCallback func, void* extra); +}; + +#endif diff --git a/gi/pyp-topics/src/contexts_lexer.l b/gi/pyp-topics/src/contexts_lexer.l new file mode 100644 index 00000000..61189a73 --- /dev/null +++ b/gi/pyp-topics/src/contexts_lexer.l @@ -0,0 +1,110 @@ +%{ +#include "contexts_lexer.h" + +#include <string> +#include <iostream> +#include <sstream> +#include <cstring> +#include <cassert> + +int lex_line = 0; +std::istream* contextslex_stream = NULL; +ContextsLexer::ContextsCallback contexts_callback = NULL; +void* contexts_callback_extra = NULL; + +#undef YY_INPUT +#define YY_INPUT(buf, result, max_size) (result = contextslex_stream->read(buf, max_size).gcount()) + +#define YY_SKIP_YYWRAP 1 +int num_phrases = 0; +int yywrap() { return 1; } + +#define MAX_TOKEN_SIZE 255 +std::string contextslex_tmp_token(MAX_TOKEN_SIZE, '\0'); +ContextsLexer::PhraseContextsType current_contexts; + +#define MAX_CONTEXT_SIZE 255 +//std::string tmp_context[MAX_CONTEXT_SIZE]; +ContextsLexer::Context tmp_context; + + +void contextslex_reset() { + current_contexts.phrase.clear(); + current_contexts.contexts.clear(); + current_contexts.counts.clear(); + tmp_context.clear(); +} + +%} + +INT [\-+]?[0-9]+|inf|[\-+]inf + +%x CONTEXT COUNT COUNT_END +%% + +<INITIAL>[^\t]+ { + contextslex_reset(); + current_contexts.phrase.assign(yytext, yyleng); + BEGIN(CONTEXT); + } +<INITIAL>\t { + ; + } + +<INITIAL,CONTEXT,COUNT>\n { + std::cerr << "ERROR: contexts_lexer.l: unexpected newline while trying to read phrase|context|count." << std::endl; + abort(); + } + +<CONTEXT>\|\|\| { + current_contexts.contexts.push_back(tmp_context); + tmp_context.clear(); + BEGIN(COUNT); + } +<CONTEXT>[^ \t]+ { + contextslex_tmp_token.assign(yytext, yyleng); + tmp_context.push_back(contextslex_tmp_token); + } +<CONTEXT>[ \t]+ { ; } + +<COUNT>[ \t]+ { ; } +<COUNT>C={INT} { + current_contexts.counts.push_back(atoi(yytext+2)); + BEGIN(COUNT_END); + } +<COUNT>. { + std::cerr << "ERROR: contexts_lexer.l: unexpected content while reading count." << std::endl; + abort(); + } + +<COUNT_END>[ \t]+ { ; } +<COUNT_END>\|\|\| { + BEGIN(CONTEXT); + } +<COUNT_END>\n { + //std::cerr << "READ:" << current_contexts.phrase << " with " << current_contexts.contexts.size() + // << " contexts, and " << current_contexts.counts.size() << " counts." << std::endl; + contexts_callback(current_contexts, contexts_callback_extra); + current_contexts.phrase.clear(); + current_contexts.contexts.clear(); + current_contexts.counts.clear(); + BEGIN(INITIAL); + } +<COUNT_END>. { + contextslex_tmp_token.assign(yytext, yyleng); + std::cerr << "ERROR: contexts_lexer.l: unexpected content while looking for ||| closing count." << std::endl; + abort(); + } + +%% + +#include "../../../decoder/filelib.h" + +void ContextsLexer::ReadContexts(std::istream* in, ContextsLexer::ContextsCallback func, void* extra) { + lex_line = 1; + contextslex_stream = in; + contexts_callback_extra = extra, + contexts_callback = func; + yylex(); +} + diff --git a/gi/pyp-topics/src/corpus.cc b/gi/pyp-topics/src/corpus.cc index 93910ea3..24b93a03 100644 --- a/gi/pyp-topics/src/corpus.cc +++ b/gi/pyp-topics/src/corpus.cc @@ -22,7 +22,7 @@ unsigned Corpus::read(const std::string &filename) { string buf; int token; - unsigned count=0; + unsigned doc_count=0; while (getline(in, buf)) { Document* doc(new Document()); istringstream ss(buf); @@ -39,14 +39,15 @@ unsigned Corpus::read(const std::string &filename) { } m_documents.push_back(doc); - count++; + doc_count++; } m_num_types = seen_types.size(); - return count; + return doc_count; } + ////////////////////////////////////////////////// // TestCorpus ////////////////////////////////////////////////// diff --git a/gi/pyp-topics/src/corpus.hh b/gi/pyp-topics/src/corpus.hh index c1b0ceda..243f7e2c 100644 --- a/gi/pyp-topics/src/corpus.hh +++ b/gi/pyp-topics/src/corpus.hh @@ -5,6 +5,7 @@ #include <string> #include <map> +#include <boost/shared_ptr.hpp> #include <boost/ptr_container/ptr_vector.hpp> //////////////////////////////////////////////////////////////// @@ -100,4 +101,6 @@ protected: int m_backoff_order; std::vector<int> m_terms_at_order; }; +typedef boost::shared_ptr<TermBackoff> TermBackoffPtr; + #endif // _CORPUS_HH diff --git a/gi/pyp-topics/src/pyp-topics.hh b/gi/pyp-topics/src/pyp-topics.hh index 4036985a..92d6f292 100644 --- a/gi/pyp-topics/src/pyp-topics.hh +++ b/gi/pyp-topics/src/pyp-topics.hh @@ -15,7 +15,7 @@ public: typedef long double F; public: - PYPTopics(int num_topics) : m_num_topics(num_topics), m_word_pyps(1), m_backoff(0) {} + PYPTopics(int num_topics) : m_num_topics(num_topics), m_word_pyps(1) {} void sample(const Corpus& corpus, int samples); int sample(const DocumentId& doc, const Term& term); @@ -27,6 +27,9 @@ public: m_word_pyps.clear(); m_word_pyps.resize(m_backoff->order(), PYPs()); } + void set_backoff(TermBackoffPtr backoff) { + m_backoff = backoff; + } F prob(const Term& term, int topic, int level=0) const; void decrement(const Term& term, int topic, int level=0); @@ -46,7 +49,7 @@ private: PYPs m_document_pyps; std::vector<PYPs> m_word_pyps; - std::auto_ptr<TermBackoff> m_backoff; + TermBackoffPtr m_backoff; }; #endif // PYP_TOPICS_HH diff --git a/gi/pyp-topics/src/train.cc b/gi/pyp-topics/src/train.cc index 0d107f11..01ada182 100644 --- a/gi/pyp-topics/src/train.cc +++ b/gi/pyp-topics/src/train.cc @@ -10,6 +10,7 @@ // Local #include "pyp-topics.hh" #include "corpus.hh" +#include "contexts_corpus.hh" #include "gzstream.hh" #include "mt19937ar.h" @@ -38,6 +39,7 @@ int main(int argc, char **argv) options_description generic("Allowed options"); generic.add_options() ("documents,d", value<string>(), "file containing the documents") + ("contexts", value<string>(), "file containing the documents in phrase contexts format") ("topics,t", value<int>()->default_value(50), "number of topics") ("document-topics-out,o", value<string>(), "file to write the document topics to") ("topic-words-out,w", value<string>(), "file to write the topic word distribution to") @@ -56,42 +58,55 @@ int main(int argc, char **argv) } notify(vm); //////////////////////////////////////////////////////////////////////////////////////////// - - if (vm.count("help")) { - cout << cmdline_options << "\n"; + if (vm.count("contexts") > 0 && vm.count("documents") > 0) { + cerr << "Only one of --documents or --contexts must be specified." << std::endl; return 1; } - if (vm.count("documents") == 0) { + if (vm.count("documents") == 0 && vm.count("contexts") == 0) { cerr << "Please specify a file containing the documents." << endl; cout << cmdline_options << "\n"; return 1; } + if (vm.count("help")) { + cout << cmdline_options << "\n"; + return 1; + } + // seed the random number generator //mt_init_genrand(time(0)); + PYPTopics model(vm["topics"].as<int>()); + // read the data - Corpus corpus; - corpus.read(vm["documents"].as<string>()); + boost::shared_ptr<Corpus> corpus; + if (vm.count("documents") == 0 && vm.count("contexts") == 0) { + corpus.reset(new Corpus); + corpus->read(vm["documents"].as<string>()); - // run the sampler - PYPTopics model(vm["topics"].as<int>()); + // read the backoff dictionary + if (vm.count("backoff-paths")) + model.set_backoff(vm["backoff-paths"].as<string>()); - // read the backoff dictionary - if (vm.count("backoff-paths")) - model.set_backoff(vm["backoff-paths"].as<string>()); + } + else { + boost::shared_ptr<ContextsCorpus> contexts_corpus(new ContextsCorpus); + contexts_corpus->read_contexts(vm["contexts"].as<string>()); + corpus = contexts_corpus; + model.set_backoff(contexts_corpus->backoff_index()); + } // train the sampler - model.sample(corpus, vm["samples"].as<int>()); + model.sample(*corpus, vm["samples"].as<int>()); if (vm.count("document-topics-out")) { ogzstream documents_out(vm["document-topics-out"].as<string>().c_str()); //model.print_document_topics(documents_out); int document_id=0; - for (Corpus::const_iterator corpusIt=corpus.begin(); - corpusIt != corpus.end(); ++corpusIt, ++document_id) { + for (Corpus::const_iterator corpusIt=corpus->begin(); + corpusIt != corpus->end(); ++corpusIt, ++document_id) { std::vector<int> unique_terms; for (Document::const_iterator docIt=corpusIt->begin(); docIt != corpusIt->end(); ++docIt) { |