00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00043
#include <time.h>
00044
#include "KernelProjection.h"
00045
#include <plearn/math/plapack.h>
00046
00047
namespace PLearn {
00048
using namespace std;
00049
00051
00053 KernelProjection::KernelProjection()
00054 : n_comp_kept(-1),
00055 first_output(true),
00056 compute_costs(false),
00057 free_extra_components(true),
00058 ignore_n_first(0),
00059 min_eigenvalue(-
REAL_MAX),
00060 n_comp(1),
00061 n_comp_for_cost(-1),
00062
normalize(0)
00063
00064 {
00065 }
00066
00067
PLEARN_IMPLEMENT_OBJECT(
KernelProjection,
00068
"Performs dimensionality reduction by learning eigenfunctions of a kernel.",
00069
""
00070 );
00071
00073
00075 void KernelProjection::declareOptions(
OptionList& ol)
00076 {
00077
00078
00079
00080
declareOption(ol,
"kernel", &KernelProjection::kernel, OptionBase::buildoption,
00081
"The kernel used to compute the Gram matrix.");
00082
00083
declareOption(ol,
"n_comp", &KernelProjection::n_comp, OptionBase::buildoption,
00084
"Number of components computed.");
00085
00086
declareOption(ol,
"normalize", &KernelProjection::normalize, OptionBase::buildoption,
00087
"The kind of normalization performed when computing the output\n"
00088
" - 0: none (classical projection on the eigenvectors)\n"
00089
" - 1: normalization to get unit variance on each coordinate\n"
00090
" - 2: ignore the eigenvalues and do as if they were all 1\n");
00091
00092
declareOption(ol,
"min_eigenvalue", &KernelProjection::min_eigenvalue, OptionBase::buildoption,
00093
"Any component associated with an eigenvalue <= min_eigenvalue will be discarded.");
00094
00095
declareOption(ol,
"compute_costs", &KernelProjection::compute_costs, OptionBase::buildoption,
00096
"Whether we should compute costs or not.");
00097
00098
declareOption(ol,
"n_comp_for_cost", &KernelProjection::n_comp_for_cost, OptionBase::buildoption,
00099
"The number of components considered when computing a cost (default = -1 means n_comp).");
00100
00101
declareOption(ol,
"free_extra_components", &KernelProjection::free_extra_components, OptionBase::buildoption,
00102
"If set to 1, components computed but not kept won't be available after training.");
00103
00104
declareOption(ol,
"ignore_n_first", &KernelProjection::ignore_n_first, OptionBase::buildoption,
00105
"Will ignore the first 'ignore_n_first' eigenvectors, if this option is > 0.");
00106
00107
00108
00109
declareOption(ol,
"eigenvalues", &KernelProjection::eigenvalues, OptionBase::learntoption,
00110
"The eigenvalues of the Gram matrix.");
00111
00112
declareOption(ol,
"eigenvectors", &KernelProjection::eigenvectors, OptionBase::learntoption,
00113
"The eigenvectors of the Gram matrix.");
00114
00115
declareOption(ol,
"n_comp_kept", &KernelProjection::n_comp_kept, OptionBase::learntoption,
00116
"The actual number of components actually kept in the output (we may discard\n"
00117
"some because of low eigenvalues).");
00118
00119
declareOption(ol,
"n_examples", &KernelProjection::n_examples, OptionBase::learntoption,
00120
"The number of points in the training set.");
00121
00122
00123 inherited::declareOptions(ol);
00124 }
00125
00127
00129 void KernelProjection::build()
00130 {
00131 inherited::build();
00132
build_();
00133 }
00134
00136
00138 void KernelProjection::build_()
00139 {
00140
if (
n_comp_kept == -1) {
00141
n_comp_kept =
n_comp;
00142 }
00143
00144
switch(
normalize) {
00145
case 0:
case 1:
case 2:
00146
break;
00147
default:
00148
PLERROR(
"In KernelProjection::build_ - Wrong value for 'normalize')");
00149 }
00150
first_output =
true;
00151
last_input.
resize(0);
00152 }
00153
00155
00157 void KernelProjection::computeCostsFromOutputs(
const Vec& input,
const Vec& output,
00158
const Vec& target,
Vec& costs)
const
00159
{
00160
if (!
compute_costs)
00161
return;
00162
00163
real k_x_x =
kernel->evaluate(input, input);
00164
real fs_norm;
00165
if (
n_comp_for_cost > 0) {
00166
00167 fs_norm =
pownorm(output.
subVec(0,
n_comp_for_cost));
00168 }
else {
00169 fs_norm =
pownorm(output);
00170 }
00171 costs.
resize(2);
00172
if (
last_input.
length() == 0) {
00173
last_input.
resize(input.
length());
00174
last_output.
resize(output.
length());
00175
last_input << input;
00176
last_output << output;
00177 costs[1] =
MISSING_VALUE;
00178 }
else {
00179
real k_x_y =
kernel->evaluate(input,
last_input);
00180
real fs_dotp;
00181
if (
n_comp_for_cost > 0) {
00182
00183 fs_dotp =
dot(output.
subVec(0,
n_comp_for_cost),
last_output.
subVec(0,
n_comp_for_cost));
00184 }
else {
00185 fs_dotp =
dot(output,
last_output);
00186 }
00187
last_input.
resize(0);
00188
real diff = k_x_y - fs_dotp;
00189 costs[1] = diff * diff;
00190 }
00191 costs[0] =
abs(k_x_x - fs_norm);
00192
if (k_x_x - fs_norm < -1e-5) {
00193
00194 cout <<
"Negative error: " << k_x_x - fs_norm <<
" (k_x_x = " << k_x_x <<
", fs_norm = " << fs_norm <<
")" <<
endl;
00195 }
00196 }
00197
00199
00201 void KernelProjection::computeOutput(
const Vec& input,
Vec& output)
const
00202
{
00203
static real* result_ptr;
00204
if (
first_output) {
00205
00206
k_x_xi.
resize(
n_examples);
00207
used_eigenvectors =
eigenvectors.
subMatRows(0,
n_comp_kept);
00208
result.
resize(
n_comp_kept,1);
00209
first_output =
false;
00210 }
00211
00212
kernel->evaluate_all_i_x(input,
k_x_xi);
00213
00214
rowSum(
used_eigenvectors *
k_x_xi,
result);
00215 output.
resize(
n_comp_kept);
00216 result_ptr =
result[0];
00217
if (
normalize == 1) {
00218
real norm_coeff =
sqrt(
real(
n_examples));
00219
for (
int i = 0; i <
n_comp_kept; i++) {
00220 output[i] = *(result_ptr++) /
eigenvalues[i] * norm_coeff;
00221 }
00222 }
else if (
normalize == 0) {
00223
for (
int i = 0; i <
n_comp_kept; i++) {
00224 output[i] = *(result_ptr++) /
sqrt(
eigenvalues[i]);
00225 }
00226 }
else if (
normalize == 2) {
00227 output << result;
00228 output *=
sqrt(
real(
n_examples));
00229 }
else {
00230
PLERROR(
"In KernelProjection::computeOutput - Wrong value for 'normalize')");
00231 }
00232 }
00233
00235
00237 void KernelProjection::forget()
00238 {
00239 stage = 0;
00240
n_comp_kept =
n_comp;
00241
n_examples = 0;
00242
first_output =
true;
00243
last_input.
resize(0);
00244
00245
eigenvectors =
Mat();
00246
eigenvalues =
Vec();
00247 }
00248
00250
00252 TVec<string> KernelProjection::getTestCostNames()
const
00253
{
00254
TVec<string> t;
00255
if (!
compute_costs)
00256
return t;
00257
00258
00259 t.
append(
"fs_squared_norm_reconstruction_error");
00260
00261
00262 t.
append(
"fs_dotp_reconstruction_squared_error");
00263
return t;
00264 }
00265
00267
00269 TVec<string> KernelProjection::getTrainCostNames()
const
00270
{
00271
return getTestCostNames();
00272 }
00273
00275
00277 void KernelProjection::makeDeepCopyFromShallowCopy(map<const void*, void*>& copies)
00278 {
00279 inherited::makeDeepCopyFromShallowCopy(copies);
00280
00281
00282
00283
00284
00285
00286
00287
00288
PLERROR(
"KernelProjection::makeDeepCopyFromShallowCopy not fully (correctly) implemented yet!");
00289 }
00290
00291
00293
00295 int KernelProjection::outputsize()
const
00296
{
00297
return n_comp_kept;
00298 }
00299
00301
00303 void KernelProjection::setTrainingSet(
VMat training_set,
bool call_forget) {
00304 inherited::setTrainingSet(training_set, call_forget);
00305
n_examples = training_set->
length();
00306
00307
00308
if (
kernel)
00309 {
00310
kernel->specify_dataset = training_set;
00311
kernel->build();
00312 }
00313
else
00314
PLERROR(
"KernelProjection::setTrainingSet: You cannot use setTrainingSet without a kernel set");
00315 }
00316
00318
00320 void KernelProjection::train()
00321 {
00322
if (stage == 1) {
00323
PLWARNING(
"In KernelProjection::train - Learner has already been trained");
00324
return;
00325 }
00326
Mat gram(
n_examples,
n_examples);
00327
00328
if (report_progress) {
00329
kernel->report_progress =
true;
00330 }
00331 clock_t time_for_gram = clock();
00332
kernel->computeGramMatrix(gram);
00333 time_for_gram = clock() - time_for_gram;
00334
if (verbosity >= 3) {
00335 cout <<
flush;
00336 cout <<
"Time to compute the Gram matrix: " <<
real(time_for_gram) / real(CLOCKS_PER_SEC) <<
endl;
00337 }
00338
00339
eigenVecOfSymmMat(gram,
n_comp +
ignore_n_first,
eigenvalues,
eigenvectors);
00340
if (ignore_n_first > 0) {
00341
eigenvalues =
eigenvalues.
subVec(ignore_n_first,
eigenvalues.
length() - ignore_n_first);
00342
eigenvectors =
eigenvectors.
subMatRows(ignore_n_first,
eigenvectors.
length() - ignore_n_first);
00343 }
00344
n_comp_kept =
eigenvalues.
length();
00345
00346
int p = 0;
00347
while (p < n_comp_kept && eigenvalues[p] >
min_eigenvalue)
00348 p++;
00349
n_comp_kept = p;
00350
00351
if (
free_extra_components) {
00352
eigenvalues.
resize(
n_comp_kept);
00353
eigenvectors.
resize(
n_comp_kept,
eigenvectors.
width());
00354 }
00355
00356
first_output =
true;
00357 stage = 1;
00358 }
00359
00360 }