bool SelfOrganizingMap::train_( MatrixFloat &data ){ //Clear any previous models clear(); const UINT M = data.getNumRows(); const UINT N = data.getNumCols(); numInputDimensions = N; numOutputDimensions = numClusters*numClusters; Random rand; //Setup the neurons neurons.resize( numClusters, numClusters ); if( neurons.getSize() != numClusters*numClusters ){ errorLog << "train_( MatrixFloat &data ) - Failed to resize neurons matrix, there might not be enough memory!" << std::endl; return false; } //Init the neurons for(UINT i=0; i<numClusters; i++){ for(UINT j=0; j<numClusters; j++){ neurons[i][j].init( N, 0.5, SOM_MIN_TARGET, SOM_MAX_TARGET ); } } //Scale the data if needed ranges = data.getRanges(); if( useScaling ){ for(UINT i=0; i<M; i++){ for(UINT j=0; j<numInputDimensions; j++){ data[i][j] = scale(data[i][j],ranges[j].minValue,ranges[j].maxValue,SOM_MIN_TARGET,SOM_MAX_TARGET); } } } Float error = 0; Float lastError = 0; Float trainingSampleError = 0; Float delta = 0; Float minChange = 0; Float weightUpdate = 0; Float alpha = 1.0; Float neuronDiff = 0; Float neuronWeightFunction = 0; Float gamma = 0; UINT iter = 0; bool keepTraining = true; VectorFloat trainingSample; Vector< UINT > randomTrainingOrder(M); //In most cases, the training data is grouped into classes (100 samples for class 1, followed by 100 samples for class 2, etc.) //This can cause a problem for stochastic gradient descent algorithm. To avoid this issue, we randomly shuffle the order of the //training samples. This random order is then used at each epoch. for(UINT i=0; i<M; i++){ randomTrainingOrder[i] = i; } std::random_shuffle(randomTrainingOrder.begin(), randomTrainingOrder.end()); //Enter the main training loop while( keepTraining ){ //Update alpha based on the current iteration alpha = Util::scale(iter,0,maxNumEpochs,alphaStart,alphaEnd); //Run one epoch of training using the online best-matching-unit algorithm error = 0; for(UINT m=0; m<M; m++){ trainingSampleError = 0; //Get the i'th random training sample trainingSample = data.getRowVector( randomTrainingOrder[m] ); //Find the best matching unit Float dist = 0; Float bestDist = grt_numeric_limits< Float >::max(); UINT bestIndexRow = 0; UINT bestIndexCol = 0; for(UINT i=0; i<numClusters; i++){ for(UINT j=0; j<numClusters; j++){ dist = neurons[i][j].getSquaredWeightDistance( trainingSample ); if( dist < bestDist ){ bestDist = dist; bestIndexRow = i; bestIndexCol = j; } } } error += bestDist; //Update the weights based on the distance to the winning neuron //Neurons closer to the winning neuron will have their weights update more const Float bir = bestIndexRow; const Float bic = bestIndexCol; for(UINT i=0; i<numClusters; i++){ for(UINT j=0; j<numClusters; j++){ //Update the weights for all the neurons, pulling them a little closer to the input example neuronDiff = 0; gamma = 2.0 * grt_sqr( numClusters * sigmaWeight ); neuronWeightFunction = exp( -grt_sqr(bir-i)/gamma ) * exp( -grt_sqr(bic-j)/gamma ); //std::cout << "best index: " << bestIndexRow << " " << bestIndexCol << " bestDist: " << bestDist << " pos: " << i << " " << j << " neuronWeightFunction: " << neuronWeightFunction << std::endl; for(UINT n=0; n<N; n++){ neuronDiff = trainingSample[n] - neurons[i][j][n]; weightUpdate = neuronWeightFunction * alpha * neuronDiff; neurons[i][j][n] += weightUpdate; } } } } error = error / M; trainingLog << "iter: " << iter << " average error: " << error << std::endl; //Compute the error delta = fabs( error-lastError ); lastError = error; //Check to see if we should stop if( delta <= minChange && false ){ converged = true; keepTraining = false; } if( grt_isinf( error ) ){ errorLog << "train_(MatrixFloat &data) - Training failed! Error is NAN!" << std::endl; return false; } if( ++iter >= maxNumEpochs ){ keepTraining = false; } trainingLog << "Epoch: " << iter << " Squared Error: " << error << " Delta: " << delta << " Alpha: " << alpha << std::endl; } numTrainingIterationsToConverge = iter; trained = true; return true; }
bool SelfOrganizingMap::train_( MatrixFloat &data ){ //Clear any previous models clear(); const UINT M = data.getNumRows(); const UINT N = data.getNumCols(); numInputDimensions = N; numOutputDimensions = numClusters; Random rand; //Setup the neurons neurons.resize( numClusters ); if( neurons.size() != numClusters ){ errorLog << "train_( MatrixFloat &data ) - Failed to resize neurons Vector, there might not be enough memory!" << std::endl; return false; } for(UINT j=0; j<numClusters; j++){ //Init the neuron neurons[j].init( N, 0.5 ); //Set the weights as a random training example neurons[j].weights = data.getRowVector( rand.getRandomNumberInt(0, M) ); } //Setup the network weights switch( networkTypology ){ case RANDOM_NETWORK: networkWeights.resize(numClusters, numClusters); //Set the diagonal weights as 1 (as i==j) for(UINT i=0; i<numClusters; i++){ networkWeights[i][i] = 1; } //Randomize the other weights UINT indexA = 0; UINT indexB = 0; Float weight = 0; for(UINT i=0; i<numClusters*numClusters; i++){ indexA = rand.getRandomNumberInt(0, numClusters); indexB = rand.getRandomNumberInt(0, numClusters); //Make sure the two random indexs are the same (as this is a diagonal and should be 1) if( indexA != indexB ){ //Pick a random weight between these two neurons weight = rand.getRandomNumberUniform(0,1); //The weight betwen neurons a and b is the mirrored networkWeights[indexA][indexB] = weight; networkWeights[indexB][indexA] = weight; } } break; } //Scale the data if needed ranges = data.getRanges(); if( useScaling ){ for(UINT i=0; i<M; i++){ for(UINT j=0; j<numInputDimensions; j++){ data[i][j] = scale(data[i][j],ranges[j].minValue,ranges[j].maxValue,0,1); } } } Float error = 0; Float lastError = 0; Float trainingSampleError = 0; Float delta = 0; Float minChange = 0; Float weightUpdate = 0; Float weightUpdateSum = 0; Float alpha = 1.0; Float neuronDiff = 0; UINT iter = 0; bool keepTraining = true; VectorFloat trainingSample; Vector< UINT > randomTrainingOrder(M); //In most cases, the training data is grouped into classes (100 samples for class 1, followed by 100 samples for class 2, etc.) //This can cause a problem for stochastic gradient descent algorithm. To avoid this issue, we randomly shuffle the order of the //training samples. This random order is then used at each epoch. for(UINT i=0; i<M; i++){ randomTrainingOrder[i] = i; } std::random_shuffle(randomTrainingOrder.begin(), randomTrainingOrder.end()); //Enter the main training loop while( keepTraining ){ //Update alpha based on the current iteration alpha = Util::scale(iter,0,maxNumEpochs,alphaStart,alphaEnd); //Run one epoch of training using the online best-matching-unit algorithm error = 0; for(UINT i=0; i<M; i++){ trainingSampleError = 0; //Get the i'th random training sample trainingSample = data.getRowVector( randomTrainingOrder[i] ); //Find the best matching unit Float dist = 0; Float bestDist = grt_numeric_limits< Float >::max(); UINT bestIndex = 0; for(UINT j=0; j<numClusters; j++){ dist = neurons[j].getSquaredWeightDistance( trainingSample ); if( dist < bestDist ){ bestDist = dist; bestIndex = j; } } //Update the weights based on the distance to the winning neuron //Neurons closer to the winning neuron will have their weights update more for(UINT j=0; j<numClusters; j++){ //Update the weights for the j'th neuron weightUpdateSum = 0; neuronDiff = 0; for(UINT n=0; n<N; n++){ neuronDiff = trainingSample[n] - neurons[j][n]; weightUpdate = networkWeights[bestIndex][j] * alpha * neuronDiff; neurons[j][n] += weightUpdate; weightUpdateSum += neuronDiff; } trainingSampleError += grt_sqr( weightUpdateSum ); } error += grt_sqrt( trainingSampleError / numClusters ); } //Compute the error delta = fabs( error-lastError ); lastError = error; //Check to see if we should stop if( delta <= minChange ){ converged = true; keepTraining = false; } if( grt_isinf( error ) ){ errorLog << "train_(MatrixFloat &data) - Training failed! Error is NAN!" << std::endl; return false; } if( ++iter >= maxNumEpochs ){ keepTraining = false; } trainingLog << "Epoch: " << iter << " Squared Error: " << error << " Delta: " << delta << " Alpha: " << alpha << std::endl; } numTrainingIterationsToConverge = iter; trained = true; return true; }