///////////////////////////////////////////////////////////////////////////////
// //
// This file is part of ModelBlocks. Copyright 2009, ModelBlocks developers. //
// //
// ModelBlocks is free software: you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation, either version 3 of the License, or //
// (at your option) any later version. //
// //
// ModelBlocks is distributed in the hope that it will be useful, //
// but WITHOUT ANY WARRANTY; without even the implied warranty of //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //
// GNU General Public License for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with ModelBlocks. If not, see . //
// //
// ModelBlocks developers designate this particular file as subject to //
// the "Moses" exception as provided by ModelBlocks developers in //
// the LICENSE file that accompanies this code. //
// //
///////////////////////////////////////////////////////////////////////////////
#ifndef _NL_MIXTURE__
#define _NL_MIXTURE__
#include "nl-randvar.h"
#include "nl-list.h"
////////////////////////////////////////////////////////////////////////////////
//
// Mixture Model
//
////////////////////////////////////////////////////////////////////////////////
template class M,class Y,class C>
class Mixture2DModel : public Generic1DModel {
private:
// Static data members...
static bool bModeled;
// Data members...
CPT1DModel mC;
SimpleHash > mY_giv_C;
public:
Mixture2DModel ( ) : mC() { }
Mixture2DModel ( const string& s ) : mC(s) { }
// Specification methods...
CPT1DModel& setMixingModel ( ) { return mC; }
Prob& setComponentProb ( const C& c ) { return mC.setProb(c); }
M& setComponentModel ( const C& c ) { return mY_giv_C.set(c); }
// Extraction methods...
Prob getComponentProb ( const C& c ) { return mC.getProb(c); }
const M& getComponentModel ( const C& c ) { return mY_giv_C.get(c); }
Prob getProb ( const Y& y ) const { Prob pr=0.0;
C c; for ( bool b=c.setFirst(); b; b=c.setNext() )
pr += ( mC.getProb(c) * mY_giv_C.get(c).getProb(y) );
return pr; }
// Input / output methods...
bool readFields ( char* as[], int numF ) { return ( mC.readFields(as,numF) || mY_giv_C.set(C(as[1])).readFields(as+1,numF-1) ); }
void writeFields ( FILE* pf, string sPref ) const { mC.writeFields(pf,sPref); C c; for ( bool b=c.setFirst(); b; b=c.setNext() )
mY_giv_C.get(c).writeFields(pf,sPref+" "+c.getString()); }
};
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
template class M,class Y,class X,class C>
class Mixture3DModel : public Generic2DModel {
private:
// Static data members...
static bool bModeled;
// Data members...
CPT2DModel mC_giv_X;
SimpleHash,M > mY_giv_X_C;
public:
Mixture3DModel ( ) : mC_giv_X() { }
Mixture3DModel ( const string& s ) : mC_giv_X(s) { }
// Specification methods...
Prob& setComponentProb ( const X& x, const C& c ) { return mC_giv_X.setProb(c,x); }
M& setComponentModel ( const X& x, const C& c ) { return mY_giv_X_C.set(Joint2DRV(x,c)); }
// Extraction methods...
Prob getProb ( const Y& y, const X& x ) const { Prob pr=0.0;
C c; for ( bool b=c.setFirst(); b; b=c.setNext() )
pr += ( mC_giv_X.getProb(c,x) * mY_giv_X_C.get(Joint2DRV(x,c)).getProb(y) );
return pr; }
// Input / output methods...
bool readFields ( char* as[], int numF ) { return ( mC_giv_X.readFields(as,numF) || mY_giv_X_C.set(Joint2DRV(X(as[1]),C(as[2]))).readFields(as+2,numF-2) ); }
void writeFields ( FILE* pf, string sPref ) const { mC_giv_X.writeFields(pf,sPref);
X x; for ( bool bx=x.setFirst(); bx; bx=x.setNext() ) {
C c; for ( bool b=c.setFirst(); b; b=c.setNext() ) {
mY_giv_X_C.get(Joint2DRV(x,c)).writeFields(pf,sPref+" "+c.getString()); } } }
};
////////////////////////////////////////////////////////////////////////////////
//
// Trainable Mixture Model
//
////////////////////////////////////////////////////////////////////////////////
template class M,class Y,class C>
class TrainableMixture2DModel : public Mixture2DModel {
// private:
// LogPDFVal logpdfPrevDataAvg;
public:
// // Constructor / destructor methods...
// TrainableMixture2DModel ( ) : logpdfPrevDataAvg(log(0.0)) { }
// Specification methods...
void updateFields ( const List >&, const PDFVal, bool& );
void train ( List >&, const int, const PDFVal );
};
////////////////////////////////////////
template class M,class Y,class C>
void TrainableMixture2DModel::updateFields ( const List >& lyp, const PDFVal WEIGHT_LIMIT, bool& bShouldStop ) {
LogPDFVal logpdfData = 0.0;
CPT1DModel mprPseudoEmpC; // pseudo-empirical prob marginal
SimpleHash > > alypPseudoEmpY_giv_C; // pseudo-empirical prob conditional
//// E step: create pseudo-annotated (expectation) corpus by re-distributing empirical probabilities over C, according to model parameters...
C c;
for ( bool b=c.setFirst(); b; b=c.setNext() )
setComponentModel(c).precomputeVarianceTerms();
// For each Y...
for ( const ListedObject >* pyp=lyp.getFirst(); pyp; pyp=lyp.getNext(pyp) ) {
const Y& y = pyp->getSub1(); // data value
const Prob& prEmpY = pyp->getSub2(); // empirical prob
CPT1DModel mpdfEstCY; // model-estimated pdf
PDFVal pdfEstY = 0.0; // model-estimated pdf marginal
// For each C, compute model-estimated pdfs...
C c; for ( bool b=c.setFirst(); b; b=c.setNext() ) {
PDFVal pdfEstCY = getComponentProb(c) * getComponentModel(c).getProb(y);
// fprintf(stderr," ???%g %d %g\n",(double)pdfEstCY,(int)c,getComponentModel(c).getProb(y)); ////y.write(stderr); fprintf(stderr,"\n");
mpdfEstCY.setProb(c) = pdfEstCY;
pdfEstY += pdfEstCY; // marginalize out C (marginal estimated pdf of each y)
}
// Update total...
logpdfData += log(pdfEstY); // product of each datum (joint estimated pdf of training corpus)
//fprintf(stderr,"..%g %g\n",pdfEstY,logpdfData);
// For each C, compute psuedo-empirical probs (model-estimated * empirical)...
for ( bool b=c.setFirst(); b; b=c.setNext() ) {
Prob prEstC_giv_Y = mpdfEstCY.getProb(c) / pdfEstY; // so these are normalized distribs over C
Prob prPseudoEmpCY = prEmpY * prEstC_giv_Y;
alypPseudoEmpY_giv_C.set(c).add() = pair ( &pyp->getSub1(), prPseudoEmpCY );
mprPseudoEmpC.setProb(c) += prPseudoEmpCY; // marginalize out Y
}
}
// Renormalize sub-lists...
for ( bool b=c.setFirst(); b; b=c.setNext() )
for ( ListedObject >* pyp=alypPseudoEmpY_giv_C.set(c).setFirst(); pyp; pyp=alypPseudoEmpY_giv_C.set(c).setNext(pyp) )
pyp->second /= mprPseudoEmpC.getProb(c);
//// M step: update model parameters using MLE (relative frequency estimation) on pseudo-annotated corpus...
for ( bool b=c.setFirst(); b; b=c.setNext() ) {
// If any change exceeds thresh, continue...
if ( bShouldStop && ( getComponentProb(c)-mprPseudoEmpC.getProb(c) > WEIGHT_LIMIT ||
getComponentProb(c)-mprPseudoEmpC.getProb(c) < -WEIGHT_LIMIT ) ) bShouldStop = false;
// Update probs...
setComponentProb(c) = mprPseudoEmpC.getProb(c);
setComponentModel(c).setFields ( alypPseudoEmpY_giv_C.get(c) );
//////getComponentModel(c).writeFields(stderr,c.getString());
}
//////Mixture2DModel::writeFields(stderr,"");
fprintf(stderr," log pdf data: %g\n",(double)logpdfData);
// // Quit if total prob doesn't improve enough...
// fprintf(stderr," log avg pdf: %g - %g < %g ORLY? %d\n",
// (double)logpdfDataAvg,(double)logpdfPrevDataAvg,(double)WEIGHT_LIMIT,
// ((logpdfDataAvg - logpdfPrevDataAvg) < WEIGHT_LIMIT) );
// bShouldStop = ( (logpdfDataAvg - logpdfPrevDataAvg) < WEIGHT_LIMIT );
// logpdfPrevDataAvg = logpdfDataAvg;
}
////////////////////////////////////////
template class M,class Y,class C>
void TrainableMixture2DModel::train ( List >& lyp, const int EPOCH_LIMIT, const PDFVal WEIGHT_LIMIT ) {
// Normalize model...
Mixture2DModel::setMixingModel().normalize(); // OUGHT NOT TO BE NECESSARY!!!
// Normalize input...
PDFVal pdfNorm = 0.0;
for ( const ListedObject >* pyp=lyp.getFirst(); pyp; pyp=lyp.getNext(pyp) ) pdfNorm += pyp->getSub2();
for ( ListedObject >* pyp=lyp.setFirst(); pyp; pyp=lyp.setNext(pyp) ) pyp->setSub2() /= pdfNorm;
// Iterate over each epoch...
bool bShouldStop = true;
for ( int epoch=0; epoch class M,class Y,class X,class C>
class TrainableMixture3DModel : public Generic2DModel {
private:
string sId;
SimpleHash > > alyp;
SimpleHash > am;
public:
// Constructor / destructor methods...
TrainableMixture3DModel ( string s ) : sId(s) { }
// Specification methods...
void train ( const int, const PDFVal );
void train ( const List >&, const int, const PDFVal );
// Extraction methods...
Prob getProb ( const Y& y, const X& x ) const { return am.get(x).getProb(y); }
// Input / output methods...
bool readData ( char* as[], int );
bool readFields ( char* as[], int numF ) { return ( am.set(X(as[1])).readFields(as+1,numF-1) ); }
void writeFields ( FILE*, string sPref );
};
////////////////////////////////////////
template class M,class Y,class X,class C>
void TrainableMixture3DModel::train ( const int EPOCH_LIMIT, const PDFVal WEIGHT_LIMIT ) {
// Update each subphone from list...
int ctr = 0;
X x; for ( bool b=x.setFirst(); b; b=x.setNext() ) {
if (OUTPUT_NOISY) fprintf(stderr,"==================== SUBMODEL %s (number %d) %d data pts ====================\n",
x.getString().c_str(),ctr++,alyp.get(x).getCard());
am.set(x).train ( alyp.set(x), EPOCH_LIMIT, WEIGHT_LIMIT );
}
}
////////////////////////////////////////
template class M,class Y,class X,class C>
void TrainableMixture3DModel::train ( const List >& lxyp, const int EPOCH_LIMIT, const PDFVal WEIGHT_LIMIT ) {
// Chop list into phone-specific sub-lists...
ListedObject >* pxyp;
for ( pxyp=lxyp.getFirst(); pxyp; pxyp=lxyp.getNext(pxyp) )
alyp.set(pxyp->getSub1()).add() = Joint2DRV ( pxyp->getSub2(), pxyp->getSub3() );
// Update each subphone from list...
train(EPOCH_LIMIT,WEIGHT_LIMIT);
}
////////////////////////////////////////
template class M,class Y,class X,class C>
bool TrainableMixture3DModel::readData ( char* as[], int numFields ) {
if ( /*as[0]!=sId+"dat" ||*/ numFields!=3 ) return false;
alyp.set(X(as[1])).add() = Joint2DRV(Y(as[2]),Prob(1.0));
return true;
}
////////////////////////////////////////
template class M,class Y,class X,class C>
void TrainableMixture3DModel::writeFields ( FILE* pf, string sPref ) {
X x; for ( bool b=x.setFirst(); b; b=x.setNext() ) {
am.get(x).writeFields(pf,sPref+" "+x.getString());
}
}
#endif /*_NL_MIXTURE__*/