twirls/MexFunc/GaussFit.cpp

1261 lines
28 KiB
C++
Raw Normal View History

2023-10-05 10:38:21 +08:00
#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; });
2023-10-05 10:38:21 +08:00
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)<EFBFBD><EFBFBD><EFBFBD>Ⱥ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
plhs<EFBFBD><EFBFBD>ָ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ָ<EFBFBD><EFBFBD>(Point Left - hand side)<EFBFBD><EFBFBD><EFBFBD>Ⱥ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
nrhs<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ŀ(Number Right - hand side)<EFBFBD><EFBFBD><EFBFBD>Ⱥ<EFBFBD><EFBFBD>ұ<EFBFBD>
prhs<EFBFBD><EFBFBD>ָ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ָ<EFBFBD><EFBFBD>(Point Right - hand side)<EFBFBD><EFBFBD><EFBFBD>Ⱥ<EFBFBD><EFBFBD>ұߡ<EFBFBD>Ҫע<EFBFBD><EFBFBD>prhs<EFBFBD><EFBFBD>const<EFBFBD><EFBFBD>ָ<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ܸı<EFBFBD><EFBFBD><EFBFBD>ָ<EFBFBD><EFBFBD><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;
}