// Evaluate t-SNE cost function (approximately) double TSNE::evaluateError(int* row_P, int* col_P, double* val_P, double* Y, int N, int D, double theta) { // Get estimate of normalization term SPTree* tree = new SPTree(D, Y, N); double* buff = (double*) calloc(D, sizeof(double)); double sum_Q = .0; for(int n = 0; n < N; n++) tree->computeNonEdgeForces(n, theta, buff, &sum_Q); // Loop over all edges to compute t-SNE error int ind1, ind2; double C = .0, Q; for(int n = 0; n < N; n++) { ind1 = n * D; for(int i = row_P[n]; i < row_P[n + 1]; i++) { Q = .0; ind2 = col_P[i] * D; for(int d = 0; d < D; d++) buff[d] = Y[ind1 + d]; for(int d = 0; d < D; d++) buff[d] -= Y[ind2 + d]; for(int d = 0; d < D; d++) Q += buff[d] * buff[d]; Q = (1.0 / (1.0 + Q)) / sum_Q; C += val_P[i] * log((val_P[i] + FLT_MIN) / (Q + FLT_MIN)); } } // Clean up memory free(buff); delete tree; return C; }
void TSNE<NDims>::computeGradient(double* P, unsigned int* inp_row_P, unsigned int* inp_col_P, double* inp_val_P, double* Y, unsigned int N, int D, double* dC, double theta) { // Construct space-partitioning tree on current map SPTree<NDims>* tree = new SPTree<NDims>(Y, N); // Compute all terms required for t-SNE gradient double* pos_f = (double*) calloc(N * D, sizeof(double)); double* neg_f = (double*) calloc(N * D, sizeof(double)); if(pos_f == NULL || neg_f == NULL) { Rcpp::stop("Memory allocation failed!\n"); } tree->computeEdgeForces(inp_row_P, inp_col_P, inp_val_P, N, pos_f, num_threads); // Storing the output to sum in single-threaded mode; avoid randomness in rounding errors. std::vector<double> output(N); #pragma omp parallel for schedule(guided) num_threads(num_threads) for (unsigned int n = 0; n < N; n++) { output[n]=tree->computeNonEdgeForces(n, theta, neg_f + n * D); } double sum_Q = .0; for (unsigned int n=0; n<N; ++n) { sum_Q += output[n]; } // Compute final t-SNE gradient for(unsigned int i = 0; i < N * D; i++) { dC[i] = pos_f[i] - (neg_f[i] / sum_Q); } free(pos_f); free(neg_f); delete tree; }
void TSNE<NDims>::getCost(unsigned int* row_P, unsigned int* col_P, double* val_P, double* Y, unsigned int N, int D, double theta, double* costs) { // Get estimate of normalization term SPTree<NDims>* tree = new SPTree<NDims>(Y, N); double* buff = (double*) calloc(D, sizeof(double)); double sum_Q = .0; for(unsigned int n = 0; n < N; n++) sum_Q += tree->computeNonEdgeForces(n, theta, buff); // Loop over all edges to compute t-SNE error int ind1, ind2; double Q; for(unsigned int n = 0; n < N; n++) { ind1 = n * D; costs[n] = 0.0; for(unsigned int i = row_P[n]; i < row_P[n + 1]; i++) { Q = .0; ind2 = col_P[i] * D; for(int d = 0; d < D; d++) buff[d] = Y[ind1 + d]; for(int d = 0; d < D; d++) buff[d] -= Y[ind2 + d]; for(int d = 0; d < D; d++) Q += buff[d] * buff[d]; Q = (1.0 / (1.0 + Q)) / sum_Q; costs[n] += val_P[i] * log((val_P[i] + FLT_MIN) / (Q + FLT_MIN)); } } // Clean up memory free(buff); delete tree; }
// Compute gradient of the t-SNE cost function (using Barnes-Hut algorithm) void TSNE::computeGradient(double* P, int* inp_row_P, int* inp_col_P, double* inp_val_P, double* Y, int N, int D, double* dC, double theta) { // Construct space-partitioning tree on current map SPTree* tree = new SPTree(D, Y, N); // Compute all terms required for t-SNE gradient double sum_Q = .0; double* pos_f = (double*) calloc(N * D, sizeof(double)); double* neg_f = (double*) calloc(N * D, sizeof(double)); if(pos_f == NULL || neg_f == NULL) { cout<<"Memory allocation failed!\n"; } tree->computeEdgeForces(inp_row_P, inp_col_P, inp_val_P, N, pos_f); for(int n = 0; n < N; n++) tree->computeNonEdgeForces(n, theta, neg_f + n * D, &sum_Q); // Compute final t-SNE gradient for(int i = 0; i < N * D; i++) { dC[i] = pos_f[i] - (neg_f[i] / sum_Q); } free(pos_f); free(neg_f); delete tree; }
int main(int argc, char** argv) { std::ifstream imageFile; std::ifstream labelFile; if(argc != 5) { std::cerr << "Invalid command." << std::endl; return -1; } // Open training images and their labels for reading. imageFile.open(argv[1], std::ios::binary); labelFile.open(argv[2], std::ios::binary); labelFile.seekg(8); BitInputStream* input = new BitInputStream(imageFile); BitInputStream* label = new BitInputStream(labelFile); // Get the magic number, the rows, and the columns of each training image. int magic = input->readInt(); int total = input->readInt(); std::cerr << "Loading training images" << std::endl; std::cerr << "Total image: " << total << std::endl; int rows = input->readInt(); std::cerr << "Each image contains " << rows << " rows." << std::endl; int columns = input->readInt(); std::cerr << "Each image contains " << columns << " columns." << std::endl; // Load the training data to memory. Training* training = new Training(total, rows * columns); for(int i = 0; i < total; i++) { int lbl = (int)(label->readChar()); TRPoint* point = new TRPoint(lbl, rows * columns, i); for(int j = 0; j < (rows * columns); j++) { int pixel = (int)(input->readChar()); point->addPixel(pixel); } training->addElement(point); } imageFile.close(); labelFile.close(); std::ifstream testImageFile; std::ifstream testLabelFile; // Open testing images and their actual labels for reading. testImageFile.open(argv[3], std::ios::binary); testLabelFile.open(argv[4], std::ios::binary); testLabelFile.seekg(8); input = new BitInputStream(testImageFile); label = new BitInputStream(testLabelFile); magic = input->readInt(); total = input->readInt(); std::cerr << "Loading testing images" << std::endl; std::cerr << "Total image: " << total << std::endl; rows = input->readInt(); std::cerr << "Each image contains " << rows << " rows." << std::endl; columns = input->readInt(); std::cerr << "Each image contains " << columns << " columns." << std::endl; // Construct the K-D for nearest neighbor search. std::cerr << "Please enter the number of elements in each leaf for your K-D tree: "; std::string numImages; getline(std::cin, numImages); std::cerr << "Ok, at least " << atoi(numImages.c_str()) << " images." << std::endl; std::cerr << "Constructing K-D tree for training set." << std::endl; srand(time(0)); SPTree* tree = new SPTree(); tree->root = tree->build(tree->root, training, training->size(), atoi(numImages.c_str())); // Load the testing data to memory. Training* testing = new Training(total, rows * columns); for(int i = 0; i < total; i++) { int lbl = (int)(label->readChar()); TRPoint* point = new TRPoint(lbl, rows * columns); for(int j = 0; j < (rows * columns); j++) { int pixel = (int)(input->readChar()); point->addPixel(pixel); } testing->addElement(point); } testImageFile.close(); testLabelFile.close(); // Loading the actual true nearest neighbor of each testing images to memory. std::ifstream actualLabels; actualLabels.open("actual"); std::string in; int* trueLabels = new int[total]; int index = 0; for(int i = 0; getline(actualLabels, in); i++) trueLabels[i] = atoi(in.c_str()); int errors = 0; int notTrueNN = 0; // Perform the nearest neighbor search. for(int o = 0; o < testing->size(); o++) { std::cerr << "classifying image " << o+1 << std::endl; int currentDist = INT_MAX; int currentLabel = -1; int imageNumber = -1; // Find the leaf that contains the elements closest to the test point. TRPoint* testPoint = testing->getElement(o); Training* set = tree->find(testing->getElement(o))->set; // Compute the shortest distance from training images, update if needed. for(int i = 0; i < set->size(); i++) { TRPoint* trainPoint = set->getElement(i); int distance = 0; for(int j = 0; j < trainPoint->size; j++) { int difference = trainPoint->feature[j] - testPoint->feature[j]; if(difference < 0) difference = (-1) * difference; distance = distance + difference; } if(distance < currentDist) { currentDist = distance; currentLabel = trainPoint->label; imageNumber = trainPoint->index; } } std::cerr << "classified label: " << currentLabel << std::endl; std::cerr << "actual label: " << testPoint->label << std::endl; // There's an error if this image is not classified correctly. if(currentLabel != testPoint->label) { std::cerr << "image " << o+1 << " has an error" << std::endl; //std::cout << "image " << o+1 << " has an error" << std::endl; std::cerr << "classified with label " << currentLabel << std::endl; //std::cout << "classified with label " << currentLabel << std::endl; //printImage(training->getElement(imageNumber), columns, rows); std::cerr << "actual label is " << testPoint->label << std::endl; //std::cout << "actual label is " << testPoint->label << std::endl; //printImage(testPoint, columns, rows); errors++; } // Not a true nearest neighbor. if(imageNumber != trueLabels[o]) notTrueNN++; } // Print out the result here. std::cerr << "errors: " << errors << std::endl; std::cerr << "not true nearest neighbors: " << notTrueNN << std::endl; return 0; }