00001 /*!@file Learn/SOFM.C Self-Organizing Map network */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2005 // 00005 // by the University of Southern California (USC) and the iLab at USC. // 00006 // See http://iLab.usc.edu for information about this project. // 00007 // //////////////////////////////////////////////////////////////////// // 00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00010 // in Visual Environments, and Applications'' by Christof Koch and // 00011 // Laurent Itti, California Institute of Technology, 2001 (patent // 00012 // pending; application number 09/912,225 filed July 23, 2001; see // 00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00014 // //////////////////////////////////////////////////////////////////// // 00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00016 // // 00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00018 // redistribute it and/or modify it under the terms of the GNU General // 00019 // Public License as published by the Free Software Foundation; either // 00020 // version 2 of the License, or (at your option) any later version. // 00021 // // 00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00025 // PURPOSE. See the GNU General Public License for more details. // 00026 // // 00027 // You should have received a copy of the GNU General Public License // 00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00030 // Boston, MA 02111-1307 USA. // 00031 // //////////////////////////////////////////////////////////////////// // 00032 // 00033 // Primary maintainer for this file: Lior Elazary <elazary@usc.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Learn/SOFM.C $ 00035 // $Id: SOFM.C 14390 2011-01-13 20:17:22Z pez $ 00036 // 00037 00038 #ifndef LEARN_SOFM_C_DEFINED 00039 #define LEARN_SOFM_C_DEFINED 00040 00041 #include "Learn/SOFM.H" 00042 #include "Image/CutPaste.H" 00043 #include "GUI/DebugWin.H" 00044 #include <fcntl.h> 00045 #include <stdio.h> 00046 00047 SOFM::SOFM(const char *net_name, int InputSize, int x, int y){ 00048 00049 00050 name = net_name; 00051 MapSizeX = x; 00052 MapSizeY = y; 00053 00054 InputLayer = new Layer; 00055 KohonenLayer = new Layer; 00056 00057 //alocate input 00058 InputLayer->Units = InputSize; 00059 InputLayer->Output = new double[InputSize]; 00060 00061 //alocate map 00062 KohonenLayer->Units = x*y; 00063 KohonenLayer->Output = new double[x*y]; 00064 KohonenLayer->Weight = new float*[x*y]; 00065 KohonenLayer->StepSizeX = new double[x*y]; 00066 KohonenLayer->StepSizeY = new double[x*y]; 00067 KohonenLayer->dScoreMean = new double[x*y]; 00068 KohonenLayer->score = new double[x*y]; 00069 KohonenLayer->BadDim = new int[x*y]; 00070 KohonenLayer->Lambda = new double[x*y]; 00071 00072 for (int i=0; i<KohonenLayer->Units; i++){ 00073 KohonenLayer->Weight[i] = new float[InputLayer->Units]; 00074 } 00075 00076 00077 itsWinner = -1; //no winner 00078 itsLooser = -1; //no winner 00079 KohonenAlpha = 0; 00080 OutAlpha = 0 ; 00081 StepAlpha = 0; 00082 Gamma = 0; 00083 Sigma = 0.1; 00084 00085 itsLearningTime = 0; 00086 } 00087 00088 void SOFM::InitSofm(){ 00089 00090 for(int i=0; i<KohonenLayer->Units; i++){ 00091 KohonenLayer->StepSizeX[i] = -20; 00092 KohonenLayer->StepSizeY[i] = -20; 00093 KohonenLayer->dScoreMean[i] = 0; 00094 KohonenLayer->score[i] = 0; 00095 KohonenLayer->BadDim[i] = 0; 00096 KohonenLayer->Lambda[i] = 0; 00097 } 00098 } 00099 00100 double SOFM::RandomRange(double Low, double High){ 00101 return ( (double) rand() / RAND_MAX) * (High - Low) + Low; 00102 } 00103 00104 double SOFM::RandomBinaryRange(double Low, double High){ 00105 int i; 00106 i = (int)(1.0*rand()/(RAND_MAX+1.0)); 00107 if (i) 00108 return High; 00109 else 00110 return Low; 00111 } 00112 00113 00114 void SOFM::RandomWeights(int min, int max){ 00115 initRandomNumbers(); 00116 for (int i=0; i<KohonenLayer->Units; i++){ 00117 for (int j=0; j<InputLayer->Units; j++){ 00118 KohonenLayer->Weight[i][j] = (float)RandomRange(min, max); 00119 } 00120 } 00121 } 00122 00123 void SOFM::ZeroWeights(){ 00124 00125 for (int i=0; i<KohonenLayer->Units; i++){ 00126 for (int j=0; j<InputLayer->Units; j++){ 00127 KohonenLayer->Weight[i][j] = 0; 00128 } 00129 } 00130 } 00131 00132 00133 SOFM::~SOFM(){ 00134 delete InputLayer; 00135 delete KohonenLayer; 00136 } 00137 00138 00139 void SOFM::SetInput(float *in){ 00140 for (int i=0; i<InputLayer->Units; i++){ 00141 InputLayer->Output[i] = in[i]; 00142 } 00143 } 00144 00145 void SOFM::setInput(const std::vector<double> &in){ 00146 for (int i=0; i<InputLayer->Units; i++){ 00147 InputLayer->Output[i] = in[i]; 00148 } 00149 } 00150 00151 void SOFM::setInput(const Image<float> &in){ 00152 for (int i=0; i<InputLayer->Units; i++){ 00153 InputLayer->Output[i] = in[i]; 00154 } 00155 } 00156 00157 Image<float> SOFM::getMap(){ 00158 00159 Image<float> sofmOut(MapSizeX,MapSizeY,NO_INIT); 00160 00161 //convert the SOFM to image 00162 Image<float>::iterator dest_itr = sofmOut.beginw(); 00163 00164 int i=0; 00165 for (Image<float>::iterator itr = sofmOut.beginw(), stop = sofmOut.endw(); 00166 itr != stop; itr++, dest_itr++, i++) { 00167 *dest_itr = (float)KohonenLayer->Output[i]; 00168 } 00169 00170 return sofmOut; 00171 00172 } 00173 00174 Image<float> SOFM::getActMap(){ 00175 00176 Image<float> sofmOut(MapSizeX,MapSizeY,NO_INIT); 00177 00178 //convert the SOFM to image 00179 Image<float>::iterator dest_itr = sofmOut.beginw(); 00180 00181 int i=0; 00182 for (Image<float>::iterator itr = sofmOut.beginw(), stop = sofmOut.endw(); 00183 itr != stop; itr++, dest_itr++, i++) { 00184 *dest_itr = (float)KohonenLayer->Output[i]; 00185 } 00186 00187 return sofmOut; 00188 00189 } 00190 00191 Image<PixRGB<byte> > SOFM::getWeightsImage(){ 00192 00193 00194 int i=0; 00195 Image<PixRGB<byte> > sofmOut; 00196 00197 if (InputLayer->Units == 3) 00198 { 00199 sofmOut = Image<PixRGB<byte> >(MapSizeX,MapSizeY,NO_INIT); 00200 00201 //convert the SOFM to image 00202 Image<PixRGB<byte> >::iterator dest_itr = sofmOut.beginw(); 00203 00204 for (Image<PixRGB<byte> >::iterator itr = sofmOut.beginw(), stop = sofmOut.endw(); 00205 itr != stop; itr++, dest_itr++, i++) { 00206 *dest_itr = PixRGB<byte>(KohonenLayer->Weight[i][0], KohonenLayer->Weight[i][1], KohonenLayer->Weight[i][2]); 00207 } 00208 } else { 00209 int patchSize = (int)sqrt(InputLayer->Units); 00210 int w = MapSizeX*patchSize; 00211 int h = MapSizeY*patchSize; 00212 Image<float> tmp(w,h,NO_INIT); 00213 00214 for(int j=0; j<MapSizeY; j++) 00215 for(int i=0; i<MapSizeX; i++) 00216 { 00217 int weightIdx = j*MapSizeY + i; 00218 Image<float> patch(patchSize, patchSize, NO_INIT); 00219 for(uint pix=0; pix<patch.size(); pix++) 00220 patch[pix] = KohonenLayer->Weight[weightIdx][pix]; 00221 inplaceNormalize(patch, 0.0F, 255.0F); 00222 inplacePaste(tmp, patch, Point2D<int>(i*patchSize,j*patchSize)); 00223 } 00224 00225 sofmOut = toRGB(tmp); 00226 00227 } 00228 00229 00230 return sofmOut; 00231 00232 } 00233 00234 std::vector<float> SOFM::getWeights(const Point2D<int> loc) 00235 { 00236 std::vector<float> weights; 00237 00238 int weightIdx = loc.j*MapSizeY + loc.i; 00239 for(int i=0; i<InputLayer->Units; i++) 00240 weights.push_back(KohonenLayer->Weight[weightIdx][i]); 00241 00242 return weights; 00243 00244 } 00245 00246 00247 void SOFM::Propagate(DISTANCE_MEASURE dm){ 00248 00249 double minOut = +HUGE_VAL, maxOut = -HUGE_VAL; 00250 int winner = -1, looser = -1; 00251 //Caculate Output of all units 00252 00253 for (int i=0; i<KohonenLayer->Units; i++){ 00254 00255 switch (dm) 00256 { 00257 case EUCLIDEAN: 00258 { 00259 //find the sum of the input*wight^2 00260 double sum = 0; 00261 for(int j=0; j<InputLayer->Units; j++) 00262 { 00263 double out = InputLayer->Output[j]; 00264 double weight = KohonenLayer->Weight[i][j]; 00265 sum += (out - weight)*(out - weight); 00266 } 00267 KohonenLayer->Output[i] = sqrt(sum); 00268 } 00269 break; 00270 case KL: 00271 { 00272 //use the kl Distance 00273 double sum = 0; 00274 for(int j=0; j<InputLayer->Units; j++){ 00275 double out = InputLayer->Output[j]; 00276 double weight = KohonenLayer->Weight[i][j]; 00277 sum += weight*log(weight/out); 00278 } 00279 KohonenLayer->Output[i] = fabs(sum); 00280 } 00281 break; 00282 case L2GMM: 00283 { 00284 00285 00286 00287 } 00288 } 00289 00290 //find the winner 00291 if (KohonenLayer->Output[i] < minOut){ 00292 minOut = KohonenLayer->Output[i]; 00293 winner = i; 00294 } 00295 if (KohonenLayer->Output[i] > maxOut){ 00296 maxOut = KohonenLayer->Output[i]; 00297 looser = i; 00298 } 00299 00300 } 00301 00302 itsWinner = winner; 00303 itsWinnerValue = minOut; 00304 itsLooser = looser; 00305 itsLooserValue = maxOut; 00306 00307 } 00308 00309 double SOFM::Neighborhood(int i){ 00310 00311 double Distance; 00312 int iRow, iCol, jRow, jCol; 00313 00314 iRow = i / MapSizeX; jRow = itsWinner / MapSizeX; 00315 iCol = i % MapSizeX; jCol = itsWinner % MapSizeX; 00316 00317 Distance = sqrt(((iRow-jRow)*(iRow-jRow)) + ((iCol-jCol)*(iCol-jCol))); 00318 00319 return exp(-(Distance*Distance) / (2*(Sigma*Sigma))); 00320 } 00321 00322 Point2D<int> SOFM::getWinner(double& val) 00323 { 00324 int y = itsWinner/MapSizeX; 00325 int x = itsWinner - (y*MapSizeX); 00326 00327 val = itsWinnerValue; 00328 00329 return Point2D<int>(x,y); 00330 } 00331 00332 00333 void SOFM::Train(float *Input, float *Target, double score){ 00334 00335 double Out, Weight, Lambda=0; 00336 00337 //set the map to the input 00338 for(int i=0; i<KohonenLayer->Units; i++){ 00339 Lambda = Neighborhood(i); 00340 if (Lambda > 0.000001){ 00341 for(int j=0; j<InputLayer->Units; j++){ 00342 Out = Input[j]; 00343 Weight = KohonenLayer->Weight[i][j]; 00344 KohonenLayer->Weight[i][j] += KohonenAlpha * Lambda * (Out - Weight); 00345 } 00346 } 00347 KohonenLayer->Lambda[i] = Lambda; //set the Lambda for display 00348 //StepSize = KohonenLayer->StepSize[i]; 00349 //KohonenLayer->StepSize[i] += StepAlpha * Lambda * -StepSize; 00350 } 00351 00352 00353 /* 00354 //set the score 00355 00356 KohonenLayer->score[Winner] = score; 00357 for(int i=0; i<KohonenLayer->Units; i++){ 00358 Lambda = Neighborhood(i); 00359 if (Lambda > 0.1) //only update if not set from before 00360 if (KohonenLayer->score[i] == 0){ 00361 KohonenLayer->score[i] = score; 00362 } 00363 if (i != Winner){ 00364 //KohonenLayer->score[i] += 0.01 * (double)((int)(score/Lambda)%900); //score increses 00365 } 00366 if (Lambda > 0.1){ 00367 KohonenLayer->StepSizeX[i] = KohonenLayer->StepSizeX[Winner]; 00368 KohonenLayer->StepSizeY[i] = KohonenLayer->StepSizeY[Winner]; 00369 } 00370 if (Lambda > 0.5){ 00371 KohonenLayer->BadDim[i] = KohonenLayer->BadDim[Winner]; 00372 } 00373 00374 } */ 00375 00376 } 00377 00378 void SOFM::organize(std::vector<double> &input){ 00379 00380 double Out, Weight, Lambda=0; 00381 00382 //set the map to the input 00383 for(int i=0; i<KohonenLayer->Units; i++){ 00384 Lambda = Neighborhood(i); 00385 if (Lambda > 0.000001){ 00386 for(int j=0; j<InputLayer->Units; j++){ 00387 Out = input[j]; 00388 Weight = KohonenLayer->Weight[i][j]; 00389 KohonenLayer->Weight[i][j] += KohonenAlpha * Lambda * (Out - Weight); 00390 } 00391 } 00392 KohonenLayer->Lambda[i] = Lambda; //set the Lambda for display 00393 //StepSize = KohonenLayer->StepSize[i]; 00394 //KohonenLayer->StepSize[i] += StepAlpha * Lambda * -StepSize; 00395 } 00396 00397 } 00398 00399 void SOFM::organize(const Image<float>& input){ 00400 00401 double Out, Weight, Lambda=0; 00402 00403 //increment the learning time 00404 SetLearningRate(itsLearningTime++); 00405 00406 //set the map to the input 00407 for(int i=0; i<KohonenLayer->Units; i++){ 00408 Lambda = Neighborhood(i); 00409 if (Lambda > 0.000001){ 00410 for(int j=0; j<InputLayer->Units; j++){ 00411 Out = input[j]; 00412 Weight = KohonenLayer->Weight[i][j]; 00413 //KohonenLayer->Weight[i][j] += KohonenAlpha * Lambda * (Out - Weight); 00414 KohonenLayer->Weight[i][j] += 1 * Lambda * (Out - Weight); 00415 } 00416 } 00417 KohonenLayer->Lambda[i] = Lambda; //set the Lambda for display 00418 //StepSize = KohonenLayer->StepSize[i]; 00419 //KohonenLayer->StepSize[i] += StepAlpha * Lambda * -StepSize; 00420 } 00421 00422 } 00423 00424 void SOFM::SetLearningRate(unsigned long long learning_time){ 00425 00426 //set the net param 00427 KohonenAlpha = 0.1 * pow(0.001, (double) learning_time / 1000); 00428 OutAlpha = 1; //0.5 * pow(0.01, (double) learning_time / 1000); 00429 StepAlpha = 0.05; 00430 Gamma = 0.05; 00431 // after the leraning time, always update about 0.5 around the winner 00432 // this is why the 1.5 + 00433 Sigma = 0.5 + ( MapSizeX * pow(0.2, (double) learning_time / 1000)); 00434 00435 //LINFO("%llu: Sigma %f, Alpha %f", 00436 // learning_time, Sigma, KohonenAlpha); 00437 00438 } 00439 00440 void SOFM::ReadNet(const char *filename){ 00441 int fd; 00442 int in, sizeX, sizeY; 00443 00444 if ((fd = open(filename, 0, 0644)) == -1) return; 00445 00446 printf("Reading from %s\n", filename); 00447 //read the # of input, size of sofm, and output 00448 if(read(fd, (char *) &in, sizeof(int)) != sizeof(int)) LFATAL("fread error"); 00449 if(read(fd, (char *) &sizeX, sizeof(int)) != sizeof(int)) LFATAL("fread error"); 00450 if(read(fd, (char *) &sizeY, sizeof(int)) != sizeof(int)) LFATAL("fread error"); 00451 if(read(fd, (char *) &itsLearningTime, sizeof(itsLearningTime)) != sizeof(itsLearningTime)) LFATAL("fread error"); 00452 00453 printf("%i %i %i %llu\n", in, sizeX, sizeY, itsLearningTime); 00454 00455 //read the weights 00456 00457 for (int i=0; i<KohonenLayer->Units; i++){ 00458 int sz = sizeof(float)*InputLayer->Units; 00459 if(read(fd, KohonenLayer->Weight[i], sz) != sz) LFATAL("fread failed"); 00460 } 00461 00462 close(fd); 00463 00464 } 00465 00466 void SOFM::WriteNet(const char *filename){ 00467 int fd; 00468 00469 if ((fd = creat(filename, 0644)) == -1) { 00470 printf("Can not open %s for saving\n", filename); 00471 return; 00472 } 00473 00474 //write the # of input, size of sofm, and output 00475 if(write(fd, (char *) &InputLayer->Units, sizeof(int)) != sizeof(int)) LFATAL("write failed"); 00476 if(write(fd, (char *) &MapSizeX, sizeof(int)) != sizeof(int)) LFATAL("write failed"); 00477 if(write(fd, (char *) &MapSizeY, sizeof(int)) != sizeof(int)) LFATAL("write failed"); 00478 if(write(fd, (char *) &itsLearningTime, sizeof(itsLearningTime)) != sizeof(itsLearningTime)) LFATAL("write failed"); 00479 00480 //write the weights 00481 00482 for (int i=0; i<KohonenLayer->Units; i++){ 00483 int sz = sizeof(float)*InputLayer->Units; 00484 if(write(fd, KohonenLayer->Weight[i], sz) != sz) LFATAL("write failed"); 00485 } 00486 00487 close(fd); 00488 } 00489 00490 00491 00492 // ###################################################################### 00493 /* So things look consistent in everyone's emacs... */ 00494 /* Local Variables: */ 00495 /* indent-tabs-mode: nil */ 00496 /* End: */ 00497 00498 #endif // LEARN_SOFM_C_DEFINED