twirls/MexFunc/GaussFit.cpp

1261 lines
28 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

#include <mex.h>
#include <mat.h>
#include <iostream>
#include <algorithm>
#include <vector>
#include <string>
#include <unordered_set>
#include <ctime>
#include <math.h>
#include <cassert>
#include <fstream>
#include <string.h>
#include <queue>
using std::cout;
using std::endl;
using namespace std;
#define M_PI 3.1415926535897932384626433832795
class KMeans
{
public:
enum InitMode
{
InitRandom,
InitManual,
InitUniform,
};
KMeans(int dimNum = 1, int clusterNum = 1);
~KMeans();
void SetMean(int i, const double* u) { memcpy(m_means[i], u, sizeof(double) * m_dimNum); }
void SetInitMode(int i) { m_initMode = i; }
void SetMaxIterNum(int i) { m_maxIterNum = i; }
void SetEndError(double f) { m_endError = f; }
double* GetMean(int i) { return m_means[i]; }
int GetInitMode() { return m_initMode; }
int GetMaxIterNum() { return m_maxIterNum; }
double GetEndError() { return m_endError; }
/* SampleFile: <size><dim><data>...
LabelFile: <size><label>...
*/
void Cluster(const char* sampleFileName, const char* labelFileName);
void Init(std::ifstream& sampleFile);
void Init(double* data, int N);
void Cluster(double* data, int N, int* Label);
friend std::ostream& operator<<(std::ostream& out, KMeans& kmeans);
private:
int m_dimNum;
int m_clusterNum;
double** m_means;
int m_initMode;
int m_maxIterNum; // The stopping criterion regarding the number of iterations
double m_endError; // The stopping criterion regarding the error
double GetLabel(const double* x, int* label);
double CalcDistance(const double* x, const double* u, int dimNum);
};
KMeans::KMeans(int dimNum, int clusterNum)
{
m_dimNum = dimNum;
m_clusterNum = clusterNum;
m_means = new double* [m_clusterNum];
for (int i = 0; i < m_clusterNum; i++)
{
m_means[i] = new double[m_dimNum];
memset(m_means[i], 0, sizeof(double) * m_dimNum);
}
m_initMode = InitRandom;
m_maxIterNum = 100;
m_endError = 0.001;
}
KMeans::~KMeans()
{
for (int i = 0; i < m_clusterNum; i++)
{
delete[] m_means[i];
}
delete[] m_means;
}
void KMeans::Cluster(const char* sampleFileName, const char* labelFileName)
{
// Check the sample file
ifstream sampleFile(sampleFileName, ios_base::binary);
assert(sampleFile);
int size = 0;
int dim = 0;
sampleFile.read((char*)&size, sizeof(int));
sampleFile.read((char*)&dim, sizeof(int));
assert(size >= m_clusterNum);
assert(dim == m_dimNum);
// Initialize model
Init(sampleFile);
// Recursion
double* x = new double[m_dimNum]; // Sample data
int label = -1; // Class index
double iterNum = 0;
double lastCost = 0;
double currCost = 0;
int unchanged = 0;
bool loop = true;
int* counts = new int[m_clusterNum];
double** next_means = new double* [m_clusterNum]; // New model for reestimation
for (int i = 0; i < m_clusterNum; i++)
{
next_means[i] = new double[m_dimNum];
}
while (loop)
{
memset(counts, 0, sizeof(int) * m_clusterNum);
for (int i = 0; i < m_clusterNum; i++)
{
memset(next_means[i], 0, sizeof(double) * m_dimNum);
}
lastCost = currCost;
currCost = 0;
sampleFile.clear();
sampleFile.seekg(sizeof(int) * 2, ios_base::beg);
// Classification
for (int i = 0; i < size; i++)
{
sampleFile.read((char*)x, sizeof(double) * m_dimNum);
currCost += GetLabel(x, &label);
counts[label]++;
for (int d = 0; d < m_dimNum; d++)
{
next_means[label][d] += x[d];
}
}
currCost /= size;
// Reestimation
for (int i = 0; i < m_clusterNum; i++)
{
if (counts[i] > 0)
{
for (int d = 0; d < m_dimNum; d++)
{
next_means[i][d] /= counts[i];
}
memcpy(m_means[i], next_means[i], sizeof(double) * m_dimNum);
}
}
// Terminal conditions
iterNum++;
if (fabs(lastCost - currCost) < m_endError * lastCost)
{
unchanged++;
}
if (iterNum >= m_maxIterNum || unchanged >= 3)
{
loop = false;
}
//DEBUG
//cout << "Iter: " << iterNum << ", Average Cost: " << currCost << endl;
}
// Output the label file
ofstream labelFile(labelFileName, ios_base::binary);
assert(labelFile);
labelFile.write((char*)&size, sizeof(int));
sampleFile.clear();
sampleFile.seekg(sizeof(int) * 2, ios_base::beg);
for (int i = 0; i < size; i++)
{
sampleFile.read((char*)x, sizeof(double) * m_dimNum);
GetLabel(x, &label);
labelFile.write((char*)&label, sizeof(int));
}
sampleFile.close();
labelFile.close();
delete[] counts;
delete[] x;
for (int i = 0; i < m_clusterNum; i++)
{
delete[] next_means[i];
}
delete[] next_means;
}
//
void KMeans::Cluster(double* data, int N, int* Label)
{
int size = 0;
size = N;
assert(size >= m_clusterNum);
// Initialize model
Init(data, N);
// Recursion
double* x = new double[m_dimNum]; // Sample data
int label = -1; // Class index
double iterNum = 0;
double lastCost = 0;
double currCost = 0;
int unchanged = 0;
bool loop = true;
int* counts = new int[m_clusterNum];
double** next_means = new double* [m_clusterNum]; // New model for reestimation
for (int i = 0; i < m_clusterNum; i++)
{
next_means[i] = new double[m_dimNum];
}
while (loop)
{
memset(counts, 0, sizeof(int) * m_clusterNum);
for (int i = 0; i < m_clusterNum; i++)
{
memset(next_means[i], 0, sizeof(double) * m_dimNum);
}
lastCost = currCost;
currCost = 0;
// Classification
for (int i = 0; i < size; i++)
{
for (int j = 0; j < m_dimNum; j++)
x[j] = data[i * m_dimNum + j];
currCost += GetLabel(x, &label);
counts[label]++;
for (int d = 0; d < m_dimNum; d++)
{
next_means[label][d] += x[d];
}
}
currCost /= size;
// Reestimation
for (int i = 0; i < m_clusterNum; i++)
{
if (counts[i] > 0)
{
for (int d = 0; d < m_dimNum; d++)
{
next_means[i][d] /= counts[i];
}
memcpy(m_means[i], next_means[i], sizeof(double) * m_dimNum);
}
}
// Terminal conditions
iterNum++;
if (fabs(lastCost - currCost) < m_endError * lastCost)
{
unchanged++;
}
if (iterNum >= m_maxIterNum || unchanged >= 3)
{
loop = false;
}
//DEBUG
//cout << "Iter: " << iterNum << ", Average Cost: " << currCost << endl;
}
// Output the label file
for (int i = 0; i < size; i++)
{
for (int j = 0; j < m_dimNum; j++)
x[j] = data[i * m_dimNum + j];
GetLabel(x, &label);
Label[i] = label;
}
delete[] counts;
delete[] x;
for (int i = 0; i < m_clusterNum; i++)
{
delete[] next_means[i];
}
delete[] next_means;
}
void KMeans::Init(double* data, int N)
{
int size = N;
if (m_initMode == InitRandom)
{
int inteval = size / m_clusterNum;
double* sample = new double[m_dimNum];
// Seed the random-number generator with current time
srand((unsigned)time(NULL));
for (int i = 0; i < m_clusterNum; i++)
{
int select = inteval * i + (inteval - 1) * rand() / RAND_MAX;
for (int j = 0; j < m_dimNum; j++)
sample[j] = data[select * m_dimNum + j];
memcpy(m_means[i], sample, sizeof(double) * m_dimNum);
}
delete[] sample;
}
else if (m_initMode == InitUniform)
{
double* sample = new double[m_dimNum];
for (int i = 0; i < m_clusterNum; i++)
{
int select = i * size / m_clusterNum;
for (int j = 0; j < m_dimNum; j++)
sample[j] = data[select * m_dimNum + j];
memcpy(m_means[i], sample, sizeof(double) * m_dimNum);
}
delete[] sample;
}
else if (m_initMode == InitManual)
{
// Do nothing
}
}
void KMeans::Init(ifstream& sampleFile)
{
int size = 0;
sampleFile.seekg(0, ios_base::beg);
sampleFile.read((char*)&size, sizeof(int));
if (m_initMode == InitRandom)
{
int inteval = size / m_clusterNum;
double* sample = new double[m_dimNum];
// Seed the random-number generator with current time
srand((unsigned)time(NULL));
for (int i = 0; i < m_clusterNum; i++)
{
int select = inteval * i + (inteval - 1) * rand() / RAND_MAX;
int offset = sizeof(int) * 2 + select * sizeof(double) * m_dimNum;
sampleFile.seekg(offset, ios_base::beg);
sampleFile.read((char*)sample, sizeof(double) * m_dimNum);
memcpy(m_means[i], sample, sizeof(double) * m_dimNum);
}
delete[] sample;
}
else if (m_initMode == InitUniform)
{
double* sample = new double[m_dimNum];
for (int i = 0; i < m_clusterNum; i++)
{
int select = i * size / m_clusterNum;
int offset = sizeof(int) * 2 + select * sizeof(double) * m_dimNum;
sampleFile.seekg(offset, ios_base::beg);
sampleFile.read((char*)sample, sizeof(double) * m_dimNum);
memcpy(m_means[i], sample, sizeof(double) * m_dimNum);
}
delete[] sample;
}
else if (m_initMode == InitManual)
{
// Do nothing
}
}
double KMeans::GetLabel(const double* sample, int* label)
{
double dist = -1;
for (int i = 0; i < m_clusterNum; i++)
{
double temp = CalcDistance(sample, m_means[i], m_dimNum);
if (temp < dist || dist == -1)
{
dist = temp;
*label = i;
}
}
return dist;
}
double KMeans::CalcDistance(const double* x, const double* u, int dimNum)
{
double temp = 0;
for (int d = 0; d < dimNum; d++)
{
temp += (x[d] - u[d]) * (x[d] - u[d]);
}
return sqrt(temp);
}
ostream& operator<<(ostream& out, KMeans& kmeans)
{
out << "<KMeans>" << endl;
out << "<DimNum> " << kmeans.m_dimNum << " </DimNum>" << endl;
out << "<ClusterNum> " << kmeans.m_clusterNum << " </CluterNum>" << endl;
out << "<Mean>" << endl;
for (int i = 0; i < kmeans.m_clusterNum; i++)
{
for (int d = 0; d < kmeans.m_dimNum; d++)
{
out << kmeans.m_means[i][d] << " ";
}
out << endl;
}
out << "</Mean>" << endl;
out << "</KMeans>" << endl;
return out;
}
class GMM
{
public:
GMM(int dimNum = 1, int mixNum = 1);
~GMM();
void Copy(GMM* gmm);
void SetMaxIterNum(int i) { m_maxIterNum = i; }
void SetEndError(double f) { m_endError = f; }
int GetDimNum() { return m_dimNum; }
int GetMixNum() { return m_mixNum; }
int GetMaxIterNum() { return m_maxIterNum; }
double GetEndError() { return m_endError; }
double& Prior(int i) { return m_priors[i]; }
double* Mean(int i) { return m_means[i]; }
double* Variance(int i) { return m_vars[i]; }
void setPrior(int i, double val) { m_priors[i] = val; }
void setMean(int i, double* val) { for (int j = 0;j < m_dimNum;j++) m_means[i][j] = val[j]; }
void setVariance(int i, double* val) { for (int j = 0;j < m_dimNum;j++) m_vars[i][j] = val[j]; }
double GetProbability(const double* sample);
/* SampleFile: <size><dim><data>...*/
void Init(const char* sampleFileName);
void Train(const char* sampleFileName);
void Init(double* data, int N);
void Train(double* data, int N);
void DumpSampleFile(const char* fileName);
friend std::ostream& operator<<(std::ostream& out, GMM& gmm);
friend std::istream& operator>>(std::istream& in, GMM& gmm);
private:
int m_dimNum; // <20><><EFBFBD><EFBFBD>ά<EFBFBD><CEAC>
int m_mixNum; // Gaussian<61><6E>Ŀ
double* m_priors; // GaussianȨ<6E><C8A8>
double** m_means; // Gaussian<61><6E>ֵ
double** m_vars; // Gaussian<61><6E><EFBFBD><EFBFBD>
// A minimum variance is required. Now, it is the overall variance * 0.01.
double* m_minVars;
int m_maxIterNum; // The stopping criterion regarding the number of iterations
double m_endError; // The stopping criterion regarding the error
private:
// Return the "j"th pdf, p(x|j).
double GetProbability(const double* x, int j);
void Allocate();
void Dispose();
};
GMM::GMM(int dimNum, int mixNum)
{
m_dimNum = dimNum;
m_mixNum = mixNum;
m_maxIterNum = 100;
m_endError = 0.001;
Allocate();
for (int i = 0; i < m_mixNum; i++)
{
m_priors[i] = 1.0 / m_mixNum;
for (int d = 0; d < m_dimNum; d++)
{
m_means[i][d] = 0;
m_vars[i][d] = 1;
}
}
}
GMM::~GMM()
{
Dispose();
}
void GMM::Allocate()
{
m_priors = new double[m_mixNum];
m_means = new double* [m_mixNum];
m_vars = new double* [m_mixNum];
for (int i = 0; i < m_mixNum; i++)
{
m_means[i] = new double[m_dimNum];
m_vars[i] = new double[m_dimNum];
}
m_minVars = new double[m_dimNum];
}
void GMM::Dispose()
{
delete[] m_priors;
for (int i = 0; i < m_mixNum; i++)
{
delete[] m_means[i];
delete[] m_vars[i];
}
delete[] m_means;
delete[] m_vars;
delete[] m_minVars;
}
void GMM::Copy(GMM* gmm)
{
assert(m_mixNum == gmm->m_mixNum && m_dimNum == gmm->m_dimNum);
for (int i = 0; i < m_mixNum; i++)
{
m_priors[i] = gmm->Prior(i);
memcpy(m_means[i], gmm->Mean(i), sizeof(double) * m_dimNum);
memcpy(m_vars[i], gmm->Variance(i), sizeof(double) * m_dimNum);
}
memcpy(m_minVars, gmm->m_minVars, sizeof(double) * m_dimNum);
}
double GMM::GetProbability(const double* sample)
{
double p = 0;
for (int i = 0; i < m_mixNum; i++)
{
p += m_priors[i] * GetProbability(sample, i);
}
return p;
}
double GMM::GetProbability(const double* x, int j)
{
double p = 1;
for (int d = 0; d < m_dimNum; d++)
{
p *= 1 / sqrt(2 * M_PI * m_vars[j][d]);
p *= exp(-0.5 * (x[d] - m_means[j][d]) * (x[d] - m_means[j][d]) / m_vars[j][d]);
}
return p;
}
void GMM::Train(const char* sampleFileName)
{
//DumpSampleFile(sampleFileName);
Init(sampleFileName);
ifstream sampleFile(sampleFileName, ios_base::binary);
assert(sampleFile);
int size = 0;
sampleFile.seekg(0, ios_base::beg);
sampleFile.read((char*)&size, sizeof(int));
// Reestimation
bool loop = true;
double iterNum = 0;
double lastL = 0;
double currL = 0;
int unchanged = 0;
double* x = new double[m_dimNum]; // Sample data
double* next_priors = new double[m_mixNum];
double** next_vars = new double* [m_mixNum];
double** next_means = new double* [m_mixNum];
for (int i = 0; i < m_mixNum; i++)
{
next_means[i] = new double[m_dimNum];
next_vars[i] = new double[m_dimNum];
}
while (loop)
{
// Clear buffer for reestimation
memset(next_priors, 0, sizeof(double) * m_mixNum);
for (int i = 0; i < m_mixNum; i++)
{
memset(next_vars[i], 0, sizeof(double) * m_dimNum);
memset(next_means[i], 0, sizeof(double) * m_dimNum);
}
lastL = currL;
currL = 0;
// Predict
sampleFile.seekg(2 * sizeof(int), ios_base::beg);
for (int k = 0; k < size; k++)
{
sampleFile.read((char*)x, sizeof(double) * m_dimNum);
double p = GetProbability(x);
for (int j = 0; j < m_mixNum; j++)
{
double pj = GetProbability(x, j) * m_priors[j] / p;
next_priors[j] += pj;
for (int d = 0; d < m_dimNum; d++)
{
next_means[j][d] += pj * x[d];
next_vars[j][d] += pj * x[d] * x[d];
}
}
currL += (p > 1E-20) ? log10(p) : -20;
}
currL /= size;
// Reestimation: generate new priors, means and variances.
for (int j = 0; j < m_mixNum; j++)
{
m_priors[j] = next_priors[j] / size;
if (m_priors[j] > 0)
{
for (int d = 0; d < m_dimNum; d++)
{
m_means[j][d] = next_means[j][d] / next_priors[j];
m_vars[j][d] = next_vars[j][d] / next_priors[j] - m_means[j][d] * m_means[j][d];
if (m_vars[j][d] < m_minVars[d])
{
m_vars[j][d] = m_minVars[d];
}
}
}
}
// Terminal conditions
iterNum++;
if (fabs(currL - lastL) < m_endError * fabs(lastL))
{
unchanged++;
}
if (iterNum >= m_maxIterNum || unchanged >= 3)
{
loop = false;
}
//--- Debug ---
//cout << "Iter: " << iterNum << ", Average Log-Probability: " << currL << endl;
}
sampleFile.close();
delete[] next_priors;
for (int i = 0; i < m_mixNum; i++)
{
delete[] next_means[i];
delete[] next_vars[i];
}
delete[] next_means;
delete[] next_vars;
delete[] x;
}
void GMM::Train(double* data, int N)
{
Init(data, N);
int size = N;
// Reestimation
bool loop = true;
double iterNum = 0;
double lastL = 0;
double currL = 0;
int unchanged = 0;
double* x = new double[m_dimNum]; // Sample data
double* next_priors = new double[m_mixNum];
double** next_vars = new double* [m_mixNum];
double** next_means = new double* [m_mixNum];
for (int i = 0; i < m_mixNum; i++)
{
next_means[i] = new double[m_dimNum];
next_vars[i] = new double[m_dimNum];
}
while (loop)
{
// Clear buffer for reestimation
memset(next_priors, 0, sizeof(double) * m_mixNum);
for (int i = 0; i < m_mixNum; i++)
{
memset(next_vars[i], 0, sizeof(double) * m_dimNum);
memset(next_means[i], 0, sizeof(double) * m_dimNum);
}
lastL = currL;
currL = 0;
// Predict
for (int k = 0; k < size; k++)
{
for (int j = 0;j < m_dimNum;j++)
x[j] = data[k * m_dimNum + j];
double p = GetProbability(x);
for (int j = 0; j < m_mixNum; j++)
{
double pj = GetProbability(x, j) * m_priors[j] / p;
next_priors[j] += pj;
for (int d = 0; d < m_dimNum; d++)
{
next_means[j][d] += pj * x[d];
next_vars[j][d] += pj * x[d] * x[d];
}
}
currL += (p > 1E-20) ? log10(p) : -20;
}
currL /= size;
// Reestimation: generate new priors, means and variances.
for (int j = 0; j < m_mixNum; j++)
{
m_priors[j] = next_priors[j] / size;
if (m_priors[j] > 0)
{
for (int d = 0; d < m_dimNum; d++)
{
m_means[j][d] = next_means[j][d] / next_priors[j];
m_vars[j][d] = next_vars[j][d] / next_priors[j] - m_means[j][d] * m_means[j][d];
if (m_vars[j][d] < m_minVars[d])
{
m_vars[j][d] = m_minVars[d];
}
}
}
}
// Terminal conditions
iterNum++;
if (fabs(currL - lastL) < m_endError * fabs(lastL))
{
unchanged++;
}
if (iterNum >= m_maxIterNum || unchanged >= 3)
{
loop = false;
}
//--- Debug ---
//cout << "Iter: " << iterNum << ", Average Log-Probability: " << currL << endl;
}
delete[] next_priors;
for (int i = 0; i < m_mixNum; i++)
{
delete[] next_means[i];
delete[] next_vars[i];
}
delete[] next_means;
delete[] next_vars;
delete[] x;
}
void GMM::Init(double* data, int N)
{
const double MIN_VAR = 1E-10;
KMeans* kmeans = new KMeans(m_dimNum, m_mixNum);
kmeans->SetInitMode(KMeans::InitUniform);
int* Label;
Label = new int[N];
kmeans->Cluster(data, N, Label);
int* counts = new int[m_mixNum];
double* overMeans = new double[m_dimNum]; // Overall mean of training data
for (int i = 0; i < m_mixNum; i++)
{
counts[i] = 0;
m_priors[i] = 0;
memcpy(m_means[i], kmeans->GetMean(i), sizeof(double) * m_dimNum);
memset(m_vars[i], 0, sizeof(double) * m_dimNum);
}
memset(overMeans, 0, sizeof(double) * m_dimNum);
memset(m_minVars, 0, sizeof(double) * m_dimNum);
int size = 0;
size = N;
double* x = new double[m_dimNum];
int label = -1;
for (int i = 0; i < size; i++)
{
for (int j = 0;j < m_dimNum;j++)
x[j] = data[i * m_dimNum + j];
label = Label[i];
// Count each Gaussian
counts[label]++;
double* m = kmeans->GetMean(label);
for (int d = 0; d < m_dimNum; d++)
{
m_vars[label][d] += (x[d] - m[d]) * (x[d] - m[d]);
}
// Count the overall mean and variance.
for (int d = 0; d < m_dimNum; d++)
{
overMeans[d] += x[d];
m_minVars[d] += x[d] * x[d];
}
}
// Compute the overall variance (* 0.01) as the minimum variance.
for (int d = 0; d < m_dimNum; d++)
{
overMeans[d] /= size;
m_minVars[d] = max(MIN_VAR, 0.01 * (m_minVars[d] / size - overMeans[d] * overMeans[d]));
}
// Initialize each Gaussian.
for (int i = 0; i < m_mixNum; i++)
{
m_priors[i] = 1.0 * counts[i] / size;
if (m_priors[i] > 0)
{
for (int d = 0; d < m_dimNum; d++)
{
m_vars[i][d] = m_vars[i][d] / counts[i];
// A minimum variance for each dimension is required.
if (m_vars[i][d] < m_minVars[d])
{
m_vars[i][d] = m_minVars[d];
}
}
}
else
{
memcpy(m_vars[i], m_minVars, sizeof(double) * m_dimNum);
cout << "[WARNING] Gaussian " << i << " of GMM is not used!\n";
}
}
delete kmeans;
delete[] x;
delete[] counts;
delete[] overMeans;
delete[] Label;
}
void GMM::Init(const char* sampleFileName)
{
const double MIN_VAR = 1E-10;
KMeans* kmeans = new KMeans(m_dimNum, m_mixNum);
kmeans->SetInitMode(KMeans::InitUniform);
kmeans->Cluster(sampleFileName, "gmm_init.tmp");
int* counts = new int[m_mixNum];
double* overMeans = new double[m_dimNum]; // Overall mean of training data
for (int i = 0; i < m_mixNum; i++)
{
counts[i] = 0;
m_priors[i] = 0;
memcpy(m_means[i], kmeans->GetMean(i), sizeof(double) * m_dimNum);
memset(m_vars[i], 0, sizeof(double) * m_dimNum);
}
memset(overMeans, 0, sizeof(double) * m_dimNum);
memset(m_minVars, 0, sizeof(double) * m_dimNum);
// Open the sample and label file to initialize the model
ifstream sampleFile(sampleFileName, ios_base::binary);
assert(sampleFile);
ifstream labelFile("gmm_init.tmp", ios_base::binary);
assert(labelFile);
int size = 0;
sampleFile.read((char*)&size, sizeof(int));
sampleFile.seekg(2 * sizeof(int), ios_base::beg);
labelFile.seekg(sizeof(int), ios_base::beg);
double* x = new double[m_dimNum];
int label = -1;
for (int i = 0; i < size; i++)
{
sampleFile.read((char*)x, sizeof(double) * m_dimNum);
labelFile.read((char*)&label, sizeof(int));
// Count each Gaussian
counts[label]++;
double* m = kmeans->GetMean(label);
for (int d = 0; d < m_dimNum; d++)
{
m_vars[label][d] += (x[d] - m[d]) * (x[d] - m[d]);
}
// Count the overall mean and variance.
for (int d = 0; d < m_dimNum; d++)
{
overMeans[d] += x[d];
m_minVars[d] += x[d] * x[d];
}
}
// Compute the overall variance (* 0.01) as the minimum variance.
for (int d = 0; d < m_dimNum; d++)
{
overMeans[d] /= size;
m_minVars[d] = max(MIN_VAR, 0.01 * (m_minVars[d] / size - overMeans[d] * overMeans[d]));
}
// Initialize each Gaussian.
for (int i = 0; i < m_mixNum; i++)
{
m_priors[i] = 1.0 * counts[i] / size;
if (m_priors[i] > 0)
{
for (int d = 0; d < m_dimNum; d++)
{
m_vars[i][d] = m_vars[i][d] / counts[i];
// A minimum variance for each dimension is required.
if (m_vars[i][d] < m_minVars[d])
{
m_vars[i][d] = m_minVars[d];
}
}
}
else
{
memcpy(m_vars[i], m_minVars, sizeof(double) * m_dimNum);
cout << "[WARNING] Gaussian " << i << " of GMM is not used!\n";
}
}
delete kmeans;
delete[] x;
delete[] counts;
delete[] overMeans;
sampleFile.close();
labelFile.close();
}
void GMM::DumpSampleFile(const char* fileName)
{
ifstream sampleFile(fileName, ios_base::binary);
assert(sampleFile);
int size = 0;
sampleFile.read((char*)&size, sizeof(int));
cout << size << endl;
int dim = 0;
sampleFile.read((char*)&dim, sizeof(int));
cout << dim << endl;
double* f = new double[dim];
for (int i = 0; i < size; i++)
{
sampleFile.read((char*)f, sizeof(double) * dim);
cout << i << ":";
for (int j = 0; j < dim; j++)
{
cout << " " << f[j];
}
cout << endl;
}
delete[] f;
sampleFile.close();
}
ostream& operator<<(ostream& out, GMM& gmm)
{
out << "<GMM>" << endl;
out << "<DimNum> " << gmm.m_dimNum << " </DimNum>" << endl;
out << "<MixNum> " << gmm.m_mixNum << " </MixNum>" << endl;
out << "<Prior> ";
for (int i = 0; i < gmm.m_mixNum; i++)
{
out << gmm.m_priors[i] << " ";
}
out << "</Prior>" << endl;
out << "<Mean>" << endl;
for (int i = 0; i < gmm.m_mixNum; i++)
{
for (int d = 0; d < gmm.m_dimNum; d++)
{
out << gmm.m_means[i][d] << " ";
}
out << endl;
}
out << "</Mean>" << endl;
out << "<Variance>" << endl;
for (int i = 0; i < gmm.m_mixNum; i++)
{
for (int d = 0; d < gmm.m_dimNum; d++)
{
out << gmm.m_vars[i][d] << " ";
}
out << endl;
}
out << "</Variance>" << endl;
out << "</GMM>" << endl;
return out;
}
istream& operator>>(istream& in, GMM& gmm)
{
char label[50];
in >> label; // "<GMM>"
assert(strcmp(label, "<GMM>") == 0);
gmm.Dispose();
in >> label >> gmm.m_dimNum >> label; // "<DimNum>"
in >> label >> gmm.m_mixNum >> label; // "<MixNum>"
gmm.Allocate();
in >> label; // "<Prior>"
for (int i = 0; i < gmm.m_mixNum; i++)
{
in >> gmm.m_priors[i];
}
in >> label;
in >> label; // "<Mean>"
for (int i = 0; i < gmm.m_mixNum; i++)
{
for (int d = 0; d < gmm.m_dimNum; d++)
{
in >> gmm.m_means[i][d];
}
}
in >> label;
in >> label; // "<Variance>"
for (int i = 0; i < gmm.m_mixNum; i++)
{
for (int d = 0; d < gmm.m_dimNum; d++)
{
in >> gmm.m_vars[i][d];
}
}
in >> label;
in >> label; // "</GMM>"
return in;
}
/* <20><><EFBFBD><EFBFBD>׼<EFBFBD><D7BC>˹ģ<CBB9><C4A3>ѵ<EFBFBD><D1B5><EFBFBD><EFBFBD><EFBFBD>IJ<EFBFBD><C4B2><EFBFBD>ת<EFBFBD><D7AA><EFBFBD><EFBFBD><EFBFBD>Զ<EFBFBD><D4B6><EFBFBD><EFBFBD><EFBFBD>ϵ<EFBFBD><CFB5>, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ϻ<EFBFBD><CFBA><EFBFBD><59><D6B5><EFBFBD><EFBFBD> */
struct cmpFunc {
bool operator()(const pair<double, double>& a, const pair<double, double>& b) { return a.first < b.first; }
};
void GMMToFactorEY(GMM& gmm, double binWidth, vector<double>& vYBin, vector<double>& vFactor, vector<double>& vEY) {
/* <20><>Ҫ<EFBFBD><D2AA><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ߵ<EFBFBD>Ȩ<EFBFBD>أ<EFBFBD><D8A3><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ϸ<EFBFBD>˹<EFBFBD><CBB9><EFBFBD>ߣ<EFBFBD><DFA3><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ø<EFBFBD><C3B8><EFBFBD><EFBFBD>ܶ<EFBFBD> */
double zoomFactorSum = 0.0;
vEY.resize(vYBin.size());
// int topM = 2; // = (int)(vYBin.size() / 4);
int topM = (int)(vYBin.size() / 4);
if (topM < 1) topM = 1;
/* <20>ö<EFBFBD><C3B6><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ķ<EFBFBD>ʽȡǰtopM<70><4D><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֵ, <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ų<EFBFBD><C5B2><EFBFBD>*/
priority_queue<pair<double, double>, vector<pair<double, double> >, cmpFunc> pqTopM;
for (int i = 0; i < vYBin.size(); ++i) {
double xVal = i * binWidth;
double probVal = gmm.GetProbability(&xVal);
vEY[i] = probVal;
pqTopM.push(make_pair(vYBin[i], probVal));
}
int realUsed = 1;
pair<double, double> topEle = pqTopM.top();
zoomFactorSum += topEle.first / topEle.second;
for (int i = 1; i < topM; ++i) {
topEle = pqTopM.top();
pqTopM.pop();
if (topEle.second > 0.05) { // <20><><EFBFBD>ʸ<EFBFBD><CAB8>ʳ<EFBFBD><CAB3><EFBFBD><EFBFBD>ض<EFBFBD>ֵ<EFBFBD>Ž<EFBFBD><C5BD>м<EFBFBD><D0BC><EFBFBD>
realUsed += 1;
zoomFactorSum += topEle.first / topEle.second;
}
}
double zoomFactor = zoomFactorSum / realUsed;
//zoomFactor = 0;
//for_each(vYBin.cbegin(), vYBin.cend(), [&](const double& val)->void { zoomFactor += val; });
for (int i = 0; i < vEY.size(); ++i) {
vEY[i] *= zoomFactor;
}
vFactor.clear();
vFactor.push_back(zoomFactor * gmm.Prior(0) / sqrt(2 * M_PI * *gmm.Variance(0)));
vFactor.push_back(*gmm.Mean(0));
vFactor.push_back(sqrt(2 * *gmm.Variance(0)));
vFactor.push_back(zoomFactor * gmm.Prior(1) / sqrt(2 * M_PI * *gmm.Variance(1)));
vFactor.push_back(*gmm.Mean(1));
vFactor.push_back(sqrt(2 * *gmm.Variance(1)));
}
/* <20><><EFBFBD><EFBFBD>ƽ<EFBFBD><C6BD><EFBFBD><EFBFBD> */
template <typename T>
T Average(vector<T>& vVal) {
T sumVal = T(0);
for (int i = 0; i < vVal.size(); ++i) {
sumVal += vVal[i];
}
return sumVal / vVal.size();
}
/* <20><><EFBFBD><EFBFBD>ƽ<EFBFBD><C6BD><EFBFBD>ľ<EFBFBD>ֵ */
template <typename T>
T SquareAverage(vector<T>& vVal) {
vector<T> vSquare(vVal.size());
for (int i = 0; i < vVal.size(); ++i) {
vSquare[i] = vVal[i] * vVal[i];
}
return Average(vSquare);
}
/* <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>x<EFBFBD><78>y<EFBFBD><79><EFBFBD><EFBFBD><EFBFBD>ؾ<EFBFBD><D8BE><EFBFBD>, <20><><EFBFBD><EFBFBD>ά<EFBFBD>ȱ<EFBFBD><C8B1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>*/
double CorrelationDistance(vector<double>& vX, vector<double>& vY) {
double xMean = Average(vX);
transform(vX.cbegin(), vX.cend(), vX.begin(), [&](double val) { return val - xMean; });
double yMean = Average(vY);
transform(vY.cbegin(), vY.cend(), vY.begin(), [&](double val) { return val - yMean; });
vector<double> vXY(vX.size());
for (int i = 0; i < vXY.size(); ++i) {
vXY[i] = vX[i] * vY[i];
}
double uv = Average(vXY);
double uu = SquareAverage(vX);
double vv = SquareAverage(vY);
double dist = 1.0 - uv / sqrt(uu * vv);
return abs(dist);
}
/*
nlhs<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ(Number Left - hand side)<29><><EFBFBD>Ⱥ<EFBFBD><C8BA><EFBFBD><EFBFBD><EFBFBD>
plhs<EFBFBD><EFBFBD>ָ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ָ<EFBFBD><EFBFBD>(Point Left - hand side)<29><><EFBFBD>Ⱥ<EFBFBD><C8BA><EFBFBD><EFBFBD><EFBFBD>
nrhs<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ(Number Right - hand side)<29><><EFBFBD>Ⱥ<EFBFBD><C8BA>ұ<EFBFBD>
prhs<EFBFBD><EFBFBD>ָ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ָ<EFBFBD><EFBFBD>(Point Right - hand side)<29><><EFBFBD>Ⱥ<EFBFBD><C8BA>ұߡ<D2B1>Ҫע<D2AA><D7A2>prhs<68><73>const<73><74>ָ<EFBFBD><D6B8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E9A3AC><EFBFBD><EFBFBD><EFBFBD>ܸı<DCB8><C4B1><EFBFBD>ָ<EFBFBD><D6B8><EFBFBD><EFBFBD><EFBFBD>ݡ<EFBFBD>
*/
void mexFunction(int nlhs, mxArray* plhs[], int nrhs, const mxArray* prhs[]) {
// cout << "GaussFit" << endl;
// cout << nlhs << '\t' << nrhs << endl;
if (nrhs != 2) {
cout << "2 arguments should be given for this function!" << endl;
return;
}
clock_t begin, finish;
begin = clock();
// <20><><EFBFBD><EFBFBD>x,y<><79><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>е<EFBFBD><D0B5>ĺ<EFBFBD><C4BA><EFBFBD><EFBFBD><EFBFBD><EAA3AC>ΪGMM<4D><4D><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
vector<double> vPoint;
vector<double> vYBin;
vPoint.reserve(1000);
int rowNum = (int)mxGetM(prhs[0]);
int colNum = (int)mxGetN(prhs[0]);
int dataSize = rowNum * colNum;
vYBin.resize(dataSize);
double* pX = (double*)mxGetData(prhs[0]);
double* pY = (double*)mxGetData(prhs[1]);
for (int i = 0; i < dataSize; ++i) {
double x = pX[i];
double y = pY[i];
vYBin[i] = y;
vPoint.reserve(vPoint.size() + (int)y);
for (int j = 0; j < (int)y; ++j) {
vPoint.push_back(x);
}
}
// ѵ<><D1B5><EFBFBD><EFBFBD>˹ģ<CBB9><C4A3>
GMM gmm(1, 2); // 1ά<31><CEAC> 2<><32><EFBFBD><EFBFBD>˹ģ<CBB9><C4A3>
gmm.Train(vPoint.data(), (int)vPoint.size());
vector<double>vEY;
vector<double>vFactor;
GMMToFactorEY(gmm, 0.2, vYBin, vFactor, vEY);
double correlationDist = CorrelationDistance(vYBin, vEY);
/* д<><D0B4><EFBFBD><EFBFBD><EFBFBD><EFBFBD> */
if (nlhs > 0) { // b
mxArray* pWriteArray = NULL;
//<2F><><EFBFBD><EFBFBD>һ<EFBFBD><D2BB>rowNum*colNum<75>ľ<EFBFBD><C4BE><EFBFBD>
pWriteArray = mxCreateDoubleMatrix(1, 6, mxREAL);
memcpy((void*)(mxGetPr(pWriteArray)), (void*)vFactor.data(), sizeof(double) * 6);
plhs[0] = pWriteArray; // <20><>ֵ<EFBFBD><D6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֵ
}
if (nlhs > 1) { // r
mxArray* pWriteArray = NULL;
//<2F><><EFBFBD><EFBFBD>һ<EFBFBD><D2BB>rowNum*colNum<75>ľ<EFBFBD><C4BE><EFBFBD>
pWriteArray = mxCreateDoubleMatrix(1, 1, mxREAL);
memcpy((void*)(mxGetPr(pWriteArray)), (void*)&correlationDist, sizeof(double) * 1);
plhs[1] = pWriteArray; // <20><>ֵ<EFBFBD><D6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֵ
}
if (nlhs > 2) { // c
mxArray* pWriteArray = NULL;
//<2F><><EFBFBD><EFBFBD>һ<EFBFBD><D2BB>rowNum*colNum<75>ľ<EFBFBD><C4BE><EFBFBD>
pWriteArray = mxCreateDoubleMatrix(1, vEY.size(), mxREAL);
memcpy((void*)(mxGetPr(pWriteArray)), (void*)vEY.data(), sizeof(double) * vEY.size());
plhs[2] = pWriteArray; // <20><>ֵ<EFBFBD><D6B5><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ֵ
}
// cout << "Point size:" << vPoint.size() << endl;
finish = clock();
// cout << "Gauss Fit Total time: " << (double)(finish - begin) / CLOCKS_PER_SEC << " s" << endl;
}