Edinburgh Speech Tools  2.1-release
EST_ols.cc
Go to the documentation of this file.
1 /*************************************************************************/
2 /* */
3 /* Centre for Speech Technology Research */
4 /* University of Edinburgh, UK */
5 /* Copyright (c) 1998 */
6 /* All Rights Reserved. */
7 /* */
8 /* Permission is hereby granted, free of charge, to use and distribute */
9 /* this software and its documentation without restriction, including */
10 /* without limitation the rights to use, copy, modify, merge, publish, */
11 /* distribute, sublicense, and/or sell copies of this work, and to */
12 /* permit persons to whom this work is furnished to do so, subject to */
13 /* the following conditions: */
14 /* 1. The code must retain the above copyright notice, this list of */
15 /* conditions and the following disclaimer. */
16 /* 2. Any modifications must be clearly marked as such. */
17 /* 3. Original authors' names are not deleted. */
18 /* 4. The authors' names are not used to endorse or promote products */
19 /* derived from this software without specific prior written */
20 /* permission. */
21 /* */
22 /* THE UNIVERSITY OF EDINBURGH AND THE CONTRIBUTORS TO THIS WORK */
23 /* DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING */
24 /* ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT */
25 /* SHALL THE UNIVERSITY OF EDINBURGH NOR THE CONTRIBUTORS BE LIABLE */
26 /* FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES */
27 /* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN */
28 /* AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, */
29 /* ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF */
30 /* THIS SOFTWARE. */
31 /* */
32 /*************************************************************************/
33 /* Author : Alan W Black (and lots of books) */
34 /* Date : January 1998 */
35 /*-----------------------------------------------------------------------*/
36 /* Ordinary Least Squares/Linear regression */
37 /* */
38 /*=======================================================================*/
39 #include <cmath>
40 #include "EST_multistats.h"
41 #include "EST_simplestats.h"
42 
43 using namespace std;
44 
45 static void ols_load_selected_feats(const EST_FMatrix &X,
46  const EST_IVector &included,
47  EST_FMatrix &Xl);
48 static int ols_stepwise_find_best(const EST_FMatrix &X,
49  const EST_FMatrix &Y,
50  EST_IVector &included,
51  EST_FMatrix &coeffs,
52  float &bscore,
53  int &best_feat,
54  const EST_FMatrix &Xtest,
55  const EST_FMatrix &Ytest,
56  const EST_StrList &feat_names
57  );
58 
59 int ols(const EST_FMatrix &X,const EST_FMatrix &Y, EST_FMatrix &coeffs)
60 {
61  // Ordinary least squares, X contains the samples with 1 (for intercept)
62  // in column 0, Y contains the single values.
63  EST_FMatrix Xplus;
64 
65  if (!pseudo_inverse(X,Xplus))
66  return FALSE;
67 
68  multiply(Xplus,Y,coeffs);
69 
70  return TRUE;
71 }
72 
74  const EST_FMatrix &Y,
75  EST_FMatrix &coeffs)
76 {
77  EST_IVector included;
78  int i;
79 
80  included.resize(X.num_columns());
81  for (i=0; i<included.length(); i++)
82  included.a_no_check(i) = TRUE;
83 
84  return robust_ols(X,Y,included,coeffs);
85 }
86 
88  const EST_FMatrix &Y,
89  EST_IVector &included,
90  EST_FMatrix &coeffs)
91 {
92  // as with ols but if the pseudo inverse fails remove the offending
93  // features and try again until it works, this can be costly but
94  // its saves *you* from finding the singularity
95  // This expands the output and puts weights of 0 for omitted features
96  EST_FMatrix Xl;
97  EST_FMatrix coeffsl;
98  EST_FMatrix Xplus;
99  int i,j,singularity=-1;
100 
101  if (X.num_rows() <= X.num_columns())
102  {
103  cerr << "OLS: less rows than columns, so cannot find solution."
104  << endl;
105  return FALSE;
106  }
107  if (X.num_columns() != included.length())
108  {
109  cerr << "OLS: `included' list wrong size: internal error."
110  << endl;
111  return FALSE;
112  }
113 
114  while (TRUE)
115  {
116  ols_load_selected_feats(X,included,Xl);
117  if (pseudo_inverse(Xl,Xplus,singularity))
118  {
119  multiply(Xplus,Y,coeffsl);
120  break;
121  }
122  else
123  { // found a singularity so try again without that column
124  // remap singularity position back to X
125  int s;
126  for (s=i=0; i<singularity; i++)
127  {
128  s++;
129  while ((included(s) == FALSE) ||
130  (included(s) == OLS_IGNORE))
131  s++;
132  }
133  if (included(s) == FALSE)
134  { // oops
135  cerr << "OLS: found singularity twice, shouldn't happen"
136  << endl;
137  return FALSE;
138  }
139  else
140  {
141  cerr << "OLS: omitting singularity in column " << s << endl;
142  included[s] = FALSE;
143  }
144  }
145  }
146 
147  // Map coefficients back, making coefficient 0 for singular cols
148  coeffs.resize(X.num_columns(),1);
149  for (j=i=0; i<X.num_columns(); i++)
150  if (included(i))
151  {
152  coeffs.a_no_check(i,0) = coeffsl(j,0);
153  j++;
154  }
155  else
156  coeffs.a_no_check(i,0) = 0.0;
157 
158 
159  return TRUE;
160 
161 }
162 
163 static void ols_load_selected_feats(const EST_FMatrix &X,
164  const EST_IVector &included,
165  EST_FMatrix &Xl)
166 {
167  int i,j,k,width;
168 
169  for (width=i=0; i<included.length(); i++)
170  if (included(i) == TRUE)
171  width++;
172 
173  Xl.resize(X.num_rows(),width);
174 
175  for (i=0; i<X.num_rows(); i++)
176  for (k=j=0; j < X.num_columns(); j++)
177  if (included(j) == TRUE)
178  {
179  Xl.a_no_check(i,k) = X.a_no_check(i,j);
180  k++;
181  }
182 
183 }
184 
185 int ols_apply(const EST_FMatrix &samples,
186  const EST_FMatrix &coeffs,
187  EST_FMatrix &res)
188 {
189  // Apply coefficients to samples for res.
190 
191  if (samples.num_columns() != coeffs.num_rows())
192  return FALSE;
193 
194  multiply(samples,coeffs,res);
195 
196  return TRUE;
197 }
198 
200  const EST_FMatrix &Y,
201  const EST_StrList &feat_names,
202  float limit,
203  EST_FMatrix &coeffs,
204  const EST_FMatrix &Xtest,
205  const EST_FMatrix &Ytest,
206  EST_IVector &included)
207 {
208  // Find the features that contribute to the correlation using a
209  // a greedy algorithm
210 
211  EST_FMatrix coeffsl;
212  float best_score=0.0,bscore;
213  int i,best_feat;
214  int nf=1; // for nice printing of progress
215 
216  for (i=1; i < X.num_columns(); i++)
217  {
218  if (!ols_stepwise_find_best(X,Y,included,coeffsl,
219  bscore,best_feat,Xtest,Ytest,
220  feat_names))
221  {
222  cerr << "OLS: stepwise failed" << endl;
223  return FALSE;
224  }
225  if ((bscore - (bscore * (limit/100))) <= best_score)
226  break;
227  else
228  {
229  best_score = bscore;
230  coeffs = coeffsl;
231  included[best_feat] = TRUE;
232  printf("FEATURE %d %s: %2.4f\n",
233  nf,
234  (const char *)feat_names.nth(best_feat),
235  best_score);
236  fflush(stdout);
237  nf++;
238  }
239  }
240 
241  return TRUE;
242 }
243 
244 static int ols_stepwise_find_best(const EST_FMatrix &X,
245  const EST_FMatrix &Y,
246  EST_IVector &included,
247  EST_FMatrix &coeffs,
248  float &bscore,
249  int &best_feat,
250  const EST_FMatrix &Xtest,
251  const EST_FMatrix &Ytest,
252  const EST_StrList &feat_names
253  )
254 {
255  EST_FMatrix coeffsl;
256  bscore = 0;
257  best_feat = -1;
258  int i;
259 
260  for (i=0; i < included.length(); i++)
261  {
262  if (included.a_no_check(i) == FALSE)
263  {
264  float cor, rmse;
265  EST_FMatrix pred;
266  included.a_no_check(i) = TRUE;
267  if (!robust_ols(X,Y,included,coeffsl)) {
268  included.a_no_check(i) = FALSE; /* restore previous status */
269  return FALSE; // failed for some reason
270  }
271  ols_apply(Xtest,coeffsl,pred);
272  ols_test(Ytest,pred,cor,rmse);
273  printf("tested %d %s %f best %f\n",
274  i,(const char *)feat_names.nth(i),cor,bscore);
275  if (fabs(cor) > bscore)
276  {
277  bscore = fabs(cor);
278  coeffs = coeffsl;
279  best_feat = i;
280  }
281  included.a_no_check(i) = FALSE;
282  }
283  }
284 
285  return TRUE;
286 }
287 
288 int ols_test(const EST_FMatrix &real,
289  const EST_FMatrix &predicted,
290  float &correlation,
291  float &rmse)
292 {
293  // Others probably want this function too
294  // return correlation and RMSE for col 0 in real and predicted
295  int i;
296  float p,r;
297  EST_SuffStats x,y,xx,yy,xy,se,e;
298  double error;
299  double v1,v2,v3;
300 
301  if (real.num_rows() != predicted.num_rows())
302  {
303  correlation = 0;
304  return FALSE; // can't do this
305  }
306 
307  for (i=0; i < real.num_rows(); i++)
308  {
309  r = real(i,0);
310  p = predicted(i,0);
311  x += p;
312  y += r;
313  error = p-r;
314  se += error*error;
315  e += fabs(error);
316  xx += p*p;
317  yy += r*r;
318  xy += p*r;
319  }
320 
321  rmse = sqrt(se.mean());
322 
323  v1 = xx.mean()-(x.mean()*x.mean());
324  v2 = yy.mean()-(y.mean()*y.mean());
325 
326  v3 = v1*v2;
327 
328  if (v3 <= 0)
329  { // happens when there's very little variation in x
330  correlation = 0;
331  rmse = se.mean();
332  return FALSE;
333  }
334  // Pearson's product moment correlation coefficient
335  correlation = (xy.mean() - (x.mean()*y.mean()))/ sqrt(v3);
336 
337  // I hate to have to do this but it is necessary.
338  // When the the variation of X is very small v1*v2 approaches
339  // 0 (the negative and equals case is caught above) but that
340  // may not be enough when v1 or v2 are very small but positive.
341  // So I catch it here. If I knew more math I'd be able to describe
342  // this better but the code would remain the same.
343  if ((correlation <= 1.0) && (correlation >= -1.0))
344  return TRUE;
345  else
346  {
347  correlation = 0;
348  return FALSE;
349  }
350 }
ssize_t num_columns() const
return number of columns
Definition: EST_TMatrix.h:179
double mean(void) const
mean of currently cummulated values
INLINE const T & a_no_check(ssize_t n) const
read-only const access operator: without bounds checking
Definition: EST_TVector.h:254
ssize_t num_rows() const
return number of rows
Definition: EST_TMatrix.h:177
T & nth(int n)
return the Nth value
Definition: EST_TList.h:145
#define OLS_IGNORE
EST_Track error(EST_Track &ref, EST_Track &test, int relax=0)
int ols_apply(const EST_FMatrix &samples, const EST_FMatrix &coeffs, EST_FMatrix &res)
Definition: EST_ols.cc:185
INLINE ssize_t length() const
number of items in vector.
Definition: EST_TVector.h:249
int robust_ols(const EST_FMatrix &X, const EST_FMatrix &Y, EST_FMatrix &coeffs)
Definition: EST_ols.cc:73
int stepwise_ols(const EST_FMatrix &X, const EST_FMatrix &Y, const EST_StrList &feat_names, float limit, EST_FMatrix &coeffs, const EST_FMatrix &Xtest, const EST_FMatrix &Ytest, EST_IVector &included)
Definition: EST_ols.cc:199
void multiply(const EST_DMatrix &a, const EST_DMatrix &b, EST_DMatrix &ab)
Definition: EST_DMatrix.cc:293
#define FALSE
Definition: EST_bool.h:119
int pseudo_inverse(const EST_FMatrix &a, EST_FMatrix &inv)
pseudo inverse (for non-square matrices)
Definition: vec_mat_aux.cc:405
float correlation(EST_Track &a, EST_Track &b, ssize_t channel)
#define X
INLINE const T & a_no_check(ssize_t row, ssize_t col) const
const access with no bounds check, care recommend
Definition: EST_TMatrix.h:182
void resize(int rows, int cols, int set=1)
resize matrix
int ols_test(const EST_FMatrix &real, const EST_FMatrix &predicted, float &correlation, float &rmse)
Definition: EST_ols.cc:288
#define TRUE
Definition: EST_bool.h:118
int ols(const EST_FMatrix &X, const EST_FMatrix &Y, EST_FMatrix &coeffs)
Definition: EST_ols.cc:59
void resize(int n, int set=1)
resize vector