const-fixing functions
[mldemos:mldemos.git] / _AlgorithmsPlugins / GMM / classifierGMM.cpp
1 /*********************************************************************\r
2 MLDemos: A User-Friendly visualization toolkit for machine learning\r
3 Copyright (C) 2010  Basilio Noris\r
4 Contact: mldemos@b4silio.com\r
5 \r
6 This library is free software; you can redistribute it and/or\r
7 modify it under the terms of the GNU Lesser General Public\r
8 License as published by the Free Software Foundation; either\r
9 version 2.1 of the License, or (at your option) any later version.\r
10 \r
11 This library is distributed in the hope that it will be useful,\r
12 but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\r
14 Library General Public License for more details.\r
15 \r
16 You should have received a copy of the GNU Lesser General Public\r
17 License along with this library; if not, write to the Free\r
18 Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\r
19 *********************************************************************/\r
20 #include "public.h"\r
21 #include "classifierGMM.h"\r
22 #include <map>\r
23 #include <QDebug>\r
24 #include <iostream>\r
25 #include <fstream>\r
26 \r
27 using namespace std;\r
28 \r
29 ClassifierGMM::ClassifierGMM()\r
30         : nbClusters(2), covarianceType(2), initType(1)\r
31 {\r
32         bSingleClass = false;\r
33         bMultiClass = true;\r
34 }\r
35 \r
36 ClassifierGMM::~ClassifierGMM()\r
37 {\r
38         FOR(i, gmms.size()) DEL(gmms[i]);\r
39         FOR(i, data.size()) KILL(data[i]);\r
40 }\r
41 \r
42 void ClassifierGMM::Train(std::vector< fvec > samples, ivec labels)\r
43 {\r
44         if(!samples.size()) return;\r
45     vector< fvec > positives, negatives;\r
46         classes.clear();\r
47     classMap.clear();\r
48     inverseMap.clear();\r
49 \r
50     int cnt=0;\r
51     FOR(i, labels.size()) if(!classMap.count(labels[i])) classMap[labels[i]] = cnt++;\r
52     bool bBinary = classMap.size() == 2;\r
53     if(bBinary)\r
54     {\r
55         int positive = INT_MIN;\r
56         int negative;\r
57         FOR(i, labels.size()) positive = max(positive, labels[i]);\r
58         FORIT(classMap, int, int)\r
59         {\r
60             if(it->first != positive)\r
61             {\r
62                 negative = it->first;\r
63                 break;\r
64             }\r
65         }\r
66         classMap[negative] = -1;\r
67     }\r
68     FORIT(classMap, int, int) inverseMap[it->second] = it->first;\r
69     ivec newLabels(labels.size());\r
70     FOR(i, labels.size()) newLabels[i] = classMap[labels[i]];\r
71 \r
72 //    for(map<int,int>::iterator it=inverseMap.begin(); it != inverseMap.end(); it++) qDebug() << "inverse: " << it->first << it->second;\r
73 //    for(map<int,int>::iterator it=classMap.begin(); it != classMap.end(); it++) qDebug() << "class: " << it->first << it->second;\r
74 \r
75     std::map<int, vector<fvec> > sampleMap;\r
76 \r
77     FOR(i, samples.size())\r
78         {\r
79         sampleMap[newLabels[i]].push_back(samples[i]);\r
80         if(newLabels[i] == 1) positives.push_back(samples[i]);\r
81                 else negatives.push_back(samples[i]);\r
82         }\r
83         int dim = samples[0].size();\r
84         nbClusters = min(nbClusters, (u32)samples.size());\r
85 \r
86         FOR(i, gmms.size()) DEL(gmms[i]);\r
87         FOR(i, data.size()) KILL(data[i]);\r
88         gmms.clear();\r
89         data.clear();\r
90     int i=0;\r
91     for(map<int,vector<fvec> >::iterator it=sampleMap.begin(); it != sampleMap.end(); it++, i++)\r
92         {\r
93         vector<fvec> &s = it->second;\r
94                 gmms.push_back(new Gmm(nbClusters, dim));\r
95                 data.push_back(new float[dim*s.size()]);\r
96                 FOR(j, s.size())\r
97                 {\r
98                         FOR(d, dim) data[i][j*dim + d] = s[j][d];\r
99                 }\r
100                 gmms[i]->init(data[i], s.size(), initType);\r
101                 gmms[i]->em(data[i], s.size(), 1e-4, (COVARIANCE_TYPE)covarianceType);\r
102         }\r
103 }\r
104 \r
105 fvec ClassifierGMM::TestMulti(const fvec &sample) const\r
106 {\r
107         fvec pdf(gmms.size());\r
108         FOR(i, gmms.size()) pdf[i] = gmms[i]->pdf((float*)&sample[0]);\r
109         if(gmms.size()==2)\r
110         {\r
111                 fvec res(1);\r
112 //        res[0] = pdf[1] - pdf[0];\r
113         double p1 = log((double)pdf[1]);\r
114         double p0 = log((double)pdf[0]);\r
115 //        if(fabs(p1) < 1e-6) p1 = 0;\r
116 //        if(fabs(p0) < 1e-6) p0 = 0;\r
117         res[0] = (float)(p1 - p0);\r
118         return res;\r
119         }\r
120 \r
121     float xmin=-100.f, xmax=100.f; // we clamp the value between these two\r
122         float sum = 0;\r
123         FOR(i, pdf.size())\r
124         {\r
125         float value = log(pdf[i]);\r
126         value = (min(xmax,max(xmin, value)) - xmin) / (xmax);\r
127         pdf[i] = value;\r
128         }\r
129         return pdf;\r
130 }\r
131 \r
132 float ClassifierGMM::Test( const fvec &sample) const\r
133 {\r
134         fvec pdf = TestMulti(sample);\r
135         if(pdf.size() < 2) return 0;\r
136         float res = log(pdf[1]) - log(pdf[0]);\r
137         return res;\r
138 }\r
139 \r
140 float ClassifierGMM::Test( const fVec &_sample) const\r
141 {\r
142         if(!gmms.size()) return 0;\r
143         fVec sample = _sample;\r
144         float v0 = gmms[0]->pdf(sample._);\r
145         float v1 = 0;\r
146         if(gmms.size() > 1)\r
147         {\r
148                 v1 = gmms[1]->pdf(sample._);\r
149         }\r
150         float res = log(v1) - log(v0);\r
151         return res;\r
152 }\r
153 \r
154 void ClassifierGMM::SetParams(u32 nbClusters, u32 covarianceType, u32 initType)\r
155 {\r
156         this->nbClusters = nbClusters;\r
157         this->covarianceType = covarianceType;\r
158         this->initType = initType;\r
159 }\r
160 \r
161 const char *ClassifierGMM::GetInfoString() const\r
162 {\r
163         char *text = new char[1024];\r
164         sprintf(text, "GMM\n");\r
165         sprintf(text, "%sMixture Components: %d\n", text, nbClusters);\r
166         sprintf(text, "%sCovariance Type: ", text);\r
167         switch(covarianceType)\r
168         {\r
169         case 2:\r
170                 sprintf(text, "%sSpherical\n", text);\r
171                 break;\r
172         case 1:\r
173                 sprintf(text, "%sDiagonal\n", text);\r
174                 break;\r
175         case 0:\r
176                 sprintf(text, "%sFull\n", text);\r
177                 break;\r
178         }\r
179         sprintf(text, "%sInitialization Type: ", text);\r
180         switch(initType)\r
181         {\r
182         case 0:\r
183                 sprintf(text, "%sRandom\n", text);\r
184                 break;\r
185         case 1:\r
186                 sprintf(text, "%sUniform\n", text);\r
187                 break;\r
188         case 2:\r
189                 sprintf(text, "%sK-Means\n", text);\r
190                 break;\r
191         }\r
192         return text;\r
193 }\r
194 \r
195 void ClassifierGMM::Update()\r
196 {\r
197 \r
198 }\r
199 \r
200 void ClassifierGMM::SaveModel(const std::string filename) const\r
201 {\r
202     std::cout << "saving GMM model";\r
203     if(!gmms.size())\r
204     {\r
205         std::cout << "Error: Nothing to save!" << std::endl;\r
206         return; // nothing to save!\r
207     }\r
208 \r
209     // Save the dataset to a file\r
210     std::ofstream file(filename.c_str());\r
211 \r
212     if(!file){\r
213         std::cout << "Error: Could not open the file!" << std::endl;\r
214         return;\r
215     }\r
216 \r
217     int dim = gmms[0]->dim;\r
218     int classCount = gmms.size();\r
219     file << dim << " " << classCount << endl;\r
220 \r
221     for(map<int,int>::const_iterator it=inverseMap.begin(); it != inverseMap.end(); it++)\r
222     {\r
223         file << it->first << " " << it->second << " ";\r
224     }\r
225     file << endl;\r
226     for(map<int,int>::const_iterator it=classMap.begin(); it != classMap.end(); it++)\r
227     {\r
228         file << it->first << " " << it->second << " ";\r
229     }\r
230     file << endl;\r
231 \r
232     file.precision(10); //setting the precision of writing\r
233     FOR(g, gmms.size())\r
234     {\r
235         Gmm* gmm = gmms[g];\r
236         file << gmm->dim << endl;\r
237         file << gmm->nstates << endl;\r
238         FOR(i, gmm->nstates)\r
239         {\r
240             float prior = gmm->getPrior(i);\r
241             file << prior << " ";\r
242         }\r
243         file << endl;\r
244         float *mu = new float[dim];\r
245         FOR(i, gmm->nstates)\r
246         {\r
247             gmm->getMean(i, mu);\r
248             FOR(d, dim)\r
249             {\r
250                 file << mu[d] << " ";\r
251             }\r
252             file << endl;\r
253         }\r
254         KILL(mu);\r
255         float *sigma = new float[dim*dim];\r
256         FOR(i, gmm->nstates)\r
257         {\r
258             gmm->getCovariance(i, sigma, false);\r
259             FOR(d, dim*dim)\r
260             {\r
261                 file << sigma[d] << " ";\r
262             }\r
263             file << endl;\r
264         }\r
265         KILL(sigma);\r
266     }\r
267 \r
268     file.close();\r
269 }\r
270 \r
271 bool ClassifierGMM::LoadModel(const string filename)\r
272 {\r
273     std::cout << "loading GMM model: " << filename;\r
274 \r
275     std::ifstream file(filename.c_str());\r
276 \r
277     if(!file.is_open()){\r
278         std::cout << "Error: Could not open the file!" << std::endl;\r
279         return false;\r
280     }\r
281 \r
282     FOR(i, gmms.size()) DEL(gmms[i]);\r
283     gmms.clear();\r
284 \r
285     int dim, classCount;\r
286     file >> dim >> classCount;\r
287     inverseMap.clear();\r
288     classMap.clear();\r
289 \r
290     FOR(i, classCount)\r
291     {\r
292         int first, second;\r
293         file >> first >> second;\r
294         inverseMap[first] = second;\r
295     }\r
296 \r
297     FOR(i, classCount)\r
298     {\r
299         int first, second;\r
300         file >> first >> second;\r
301         classMap[first] = second;\r
302     }\r
303 \r
304     FOR(g, classCount)\r
305     {\r
306         int dim, nstates;\r
307         file >> dim >> nstates;\r
308         Gmm *gmm = new Gmm(nstates, dim);\r
309         if(!g) nbClusters = gmm->nstates;\r
310         FOR(i, nstates)\r
311         {\r
312             float prior;\r
313             file >> prior;\r
314             gmm->setPrior(i, prior);\r
315         }\r
316 \r
317         float *mu = new float[dim];\r
318         FOR(i, nstates)\r
319         {\r
320             FOR(d, dim) file >> mu[d];\r
321             gmm->setMean(i, mu);\r
322         }\r
323         KILL(mu);\r
324 \r
325         float *sigma = new float[dim*dim];\r
326         FOR(i, nstates)\r
327         {\r
328             FOR(d, dim*dim) file >> sigma[d];\r
329             gmm->setCovariance(i, sigma, false);\r
330         }\r
331         KILL(sigma);\r
332         gmms.push_back(gmm);\r
333     }\r
334 \r
335     file.close();\r
336     return true;\r
337 }\r