2 * mlp-mnist is an implementation of a multi-layer neural network.
4 * Copyright (c) 2006 École Polytechnique Fédérale de Lausanne,
7 * Written by Francois Fleuret <francois@fleuret.org>
9 * This file is part of mlp-mnist.
11 * mlp-mnist is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 3 as
13 * published by the Free Software Foundation.
15 * mlp-mnist is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with mlp-mnist. If not, see <http://www.gnu.org/licenses/>.
27 // 2-layer NN, 300 hidden units, mean square error 4.70%
28 // 2-layer NN, 1000 hidden units 4.50%
29 // 3-layer NN, 300+100 hidden units 3.05%
30 // 3-layer NN, 500+150 hidden units 2.95%
32 /*********************************************************************
34 This program, trained on 20,000 (+ 20,000 for the stopping
35 criterion), tested on the 10,000 of the MNIST test set 100 hidden
36 neurons, basic network, 3.48%
40 ./ann --nb-training-examples 20000 --nb-validation-examples 20000 \
41 --mlp-structure 784,200,10 \
42 --data-files ${DATA_DIR}/train-images-idx3-ubyte ${DATA_DIR}/train-labels-idx1-ubyte \
47 ./ann --load-mlp simple.mlp \
48 --data-files ${DATA_DIR}/t10k-images-idx3-ubyte ${DATA_DIR}/t10k-labels-idx1-ubyte \
49 --nb-test-examples 10000
51 *********************************************************************/
66 #define SMALL_BUFFER_SIZE 1024
68 //////////////////////////////////////////////////////////////////////
70 //////////////////////////////////////////////////////////////////////
72 int nb_experiment = 0;
73 int nb_training_examples = 0;
74 int nb_validation_examples = 0;
75 int nb_test_examples = 0;
76 bool save_data = false;
78 char images_filename[SMALL_BUFFER_SIZE] = "\0";
79 char labels_filename[SMALL_BUFFER_SIZE] = "\0";
80 char opt_load_filename[SMALL_BUFFER_SIZE] = "\0";
81 char opt_save_filename[SMALL_BUFFER_SIZE] = "\0";
82 char opt_layer_sizes[SMALL_BUFFER_SIZE] = "\0";
84 char *next_word(char *buffer, char *r, int buffer_size) {
91 while((*r != '"') && (*r != '\0') &&
92 (s<buffer+buffer_size-1))
96 while((*r != '\r') && (*r != '\n') && (*r != '\0') &&
97 (*r != '\t') && (*r != ' ') && (*r != ',') &&
98 (s<buffer+buffer_size-1))
102 while((*r == ' ') || (*r == '\t') || (*r == ',')) r++;
103 if((*r == '\0') || (*r=='\r') || (*r=='\n')) r = NULL;
109 //////////////////////////////////////////////////////////////////////
110 // Simple routine to check we have enough parameters
111 //////////////////////////////////////////////////////////////////////
113 void check_opt(int argc, char **argv, int n_opt, int n, const char *help) {
114 if(n_opt + n >= argc) {
115 cerr << "Missing argument for " << argv[n_opt] << ".\n";
116 cerr << "Expecting " << help << ".\n";
121 void print_help_and_exit(int e) {
122 cout << "ANN. Written by François Fleuret.\n";
123 cout << "$Id: ann.cc,v 1.1 2005-12-13 17:19:11 fleuret Exp $\n";
128 int main(int argc, char **argv) {
130 if(argc == 1) print_help_and_exit(1);
134 // Parsing the command line parameters ///////////////////////////////
140 if(argc == 1 || strcmp(argv[i], "--help") == 0) print_help_and_exit(0);
142 else if(strcmp(argv[i], "--data-files") == 0) {
143 check_opt(argc, argv, i, 2, "<string: pixel filename> <string: label filename>");
144 strncpy(images_filename, argv[i+1], SMALL_BUFFER_SIZE);
145 strncpy(labels_filename, argv[i+2], SMALL_BUFFER_SIZE);
149 else if(strcmp(argv[i], "--load-mlp") == 0) {
150 check_opt(argc, argv, i, 1, "<string: mlp filename>");
151 strncpy(opt_load_filename, argv[i+1], SMALL_BUFFER_SIZE);
155 else if(strcmp(argv[i], "--mlp-structure") == 0) {
156 check_opt(argc, argv, i, 1, "<int: input layer size>,<int: first hidden layer size>,[...,]<int: output layer size>");
157 strncpy(opt_layer_sizes, argv[i+1], SMALL_BUFFER_SIZE);
161 else if(strcmp(argv[i], "--save-mlp") == 0) {
162 check_opt(argc, argv, i, 1, "<string: mlp filename>");
163 strncpy(opt_save_filename, argv[i+1], SMALL_BUFFER_SIZE);
167 else if(strcmp(argv[i], "--nb-experiment") == 0) {
168 check_opt(argc, argv, i, 1, "<int: number of the experiment>");
169 nb_experiment = atoi(argv[i+1]);
173 else if(strcmp(argv[i], "--nb-training-examples") == 0) {
174 check_opt(argc, argv, i, 1, "<int: number of examples for the training>");
175 nb_training_examples = atoi(argv[i+1]);
179 else if(strcmp(argv[i], "--nb-validation-examples") == 0) {
180 check_opt(argc, argv, i, 1, "<int: number of examples for the validation>");
181 nb_validation_examples = atoi(argv[i+1]);
185 else if(strcmp(argv[i], "--nb-test-examples") == 0) {
186 check_opt(argc, argv, i, 1, "<int: number of examples for the test>");
187 nb_test_examples = atoi(argv[i+1]);
191 else if(strcmp(argv[i], "--save-data") == 0) {
197 cerr << "Unknown option " << argv[i] << "\n";
198 print_help_and_exit(1);
203 cout << "Loading the data file ..."; cout.flush();
204 image_set.load_mnist_format(images_filename, labels_filename);
205 cout << " done.\n"; cout.flush();
207 cout << "Database contains " << image_set.nb_pics()
208 << " images of resolution " << image_set.width() << "x" << image_set.height()
209 << " divided into " << image_set.nb_obj() << " objects.\n";
211 srand48(nb_experiment);
214 int *layer_sizes = 0;
216 if(opt_layer_sizes[0]) {
217 char *s = opt_layer_sizes;
218 char token[SMALL_BUFFER_SIZE];
219 while(s) { s = next_word(token, s, SMALL_BUFFER_SIZE); nb_layers++; }
222 cerr << "Need at least two layers.\n";
226 layer_sizes = new int[nb_layers];
229 while(s) { s = next_word(token, s, SMALL_BUFFER_SIZE); layer_sizes[n++] = atoi(token); }
232 // Loading or creating a perceptron from scratch /////////////////////
234 MultiLayerPerceptron *mlp = 0;
236 if(opt_load_filename[0]) {
238 ifstream stream(opt_load_filename);
240 cerr << "Can not read " << opt_load_filename << ".\n";
244 cout << "Loading network " << opt_load_filename << " ... "; cout.flush();
245 mlp = new MultiLayerPerceptron(stream);
246 cout << "done (layers of sizes";
247 for(int l = 0; l < mlp->nb_layers(); l++) cout << " " << mlp->layer_size(l);
248 cout << ")\n"; cout.flush();
250 } else if(nb_layers > 0) {
252 if(layer_sizes[0] != image_set.width() * image_set.height() ||
253 layer_sizes[nb_layers-1] != image_set.nb_obj()) {
254 cerr << "For this data set, the input layer has to be of size " << image_set.width() * image_set.height() << ",\n";
255 cerr << "and the output has to be of size " << image_set.nb_obj() << ".\n";
259 cout << "Creating a new network (layers of sizes";
260 for(int i = 0; i < nb_layers; i++) cout << " " << layer_sizes[i];
263 mlp = new MultiLayerPerceptron(nb_layers, layer_sizes);
264 mlp->init_random_weights(1e-1);
267 // Training the perceptron ///////////////////////////////////////////
269 ImageSet training_set, validation_set, test_set;
271 if(nb_training_examples > 0)
272 training_set.sample_among_unused_pictures(image_set, nb_training_examples);
274 if(nb_validation_examples > 0)
275 validation_set.sample_among_unused_pictures(image_set, nb_validation_examples);
277 if(save_data && mlp) mlp->save_data();
279 if(nb_training_examples > 0) {
280 if(validation_set.nb_pics() == 0) {
281 cerr << "We need validation pictures for training.\n";
284 cout << "Training the network with " << nb_training_examples << " training and " << nb_validation_examples << " validation examples.\n"; cout.flush();
285 mlp->train(&training_set, &validation_set);
288 // Saving the perceptron /////////////////////////////////////////////
290 if(opt_save_filename[0]) {
292 cerr << "No perceptron to save.\n";
296 ofstream stream(opt_save_filename);
298 cerr << "Can not write " << opt_save_filename << ".\n";
302 cout << "Saving network " << opt_save_filename << " ... "; cout.flush();
304 cout << "done.\n"; cout.flush();
307 // Testing the perceptron ////////////////////////////////////////////
309 if(nb_test_examples > 0) {
310 test_set.sample_among_unused_pictures(image_set, nb_test_examples);
311 cout << "Error rate " << mlp->error(&test_set) << " (" << mlp->classification_error(&test_set)*100 << "%)\n";
313 // This is to test the analytical gradient
314 // scalar_t gradient[mlp->nb_weights()], numerical_gradient[mlp->nb_weights()];
315 // mlp->compute_gradient(&test_set, gradient);
316 // mlp->compute_numerical_gradient(&test_set, numerical_gradient);
317 // for(int i = 0; i < mlp->nb_weights(); i++) cout << "TEST " << gradient[i] << " " << numerical_gradient[i] << "\n";
320 // Flushing the log //////////////////////////////////////////////////
322 delete[] layer_sizes;