Skip to content
Snippets Groups Projects
deepfriedConvnetMnist.py 8.34 KiB
Newer Older
  • Learn to ignore specific revisions
  • """
    Convolutional Neural Netwok implementation in tensorflow whith multiple representations possible after the convolution:
        - Fully connected layer
        - Random Fourier Features layer
        - Fast Food layer where Fast Hadamard Transform has been replaced by dot product with Hadamard matrix.
    
    
    See:
    "Deep Fried Convnets" by
    Zichao Yang, Marcin Moczulski, Misha Denil, Nando de Freitas, Alex Smola, Le Song, Ziyu Wang
    
    
    """
    
    import tensorflow as tf
    import numpy as np
    
    import skluc.mldatasets as dataset
    
    from skluc.neural_networks import bias_variable, weight_variable, conv2d, max_pool_2x2, get_next_batch
    
    
    tf.logging.set_verbosity(tf.logging.ERROR)
    
    from sklearn.preprocessing import LabelBinarizer
    
    enc = LabelBinarizer()
    mnist = dataset.MnistDataset()
    mnist = mnist.load()
    X_train, Y_train = mnist["train"]
    X_train = np.array(X_train / 255)
    enc.fit(Y_train)
    Y_train = np.array(enc.transform(Y_train))
    X_test, Y_test = mnist["test"]
    X_test = np.array(X_test / 255)
    Y_test = np.array(enc.transform(Y_test))
    
    X_train = X_train.astype(np.float32)
    
    permut = np.random.permutation(X_train.shape[0])
    val_size = 5000
    X_val = X_train[permut[:val_size]]
    X_train = X_train[permut[val_size:]]
    Y_val = Y_train[permut[:val_size]]
    Y_train = Y_train[permut[val_size:]]
    
    X_test = X_test.astype(np.float32)
    Y_train = Y_train.astype(np.float32)
    Y_test = Y_test.astype(np.float32)
    
    from fasfood_layer import fast_food
    
    # todo utiliser les fonctions adapate/definies pour ces couches de convolution
    
        with tf.name_scope("conv_pool_1"):
            # 32 is the number of filter we'll use. e.g. the number of different
            # shapes this layer is able to recognize
    
            tf.summary.histogram("weights conv1", W_conv1)
    
            tf.summary.histogram("biases conv1", b_conv1)
            # -1 is here to keep the total size constant (784)
            h_conv1 = tf.nn.relu(conv2d(input, W_conv1) + b_conv1)
            tf.summary.histogram("act conv1", h_conv1)
            h_pool1 = max_pool_2x2(h_conv1)
    
        with tf.name_scope("conv_pool_2"):
    
            tf.summary.histogram("weights conv2", W_conv2)
    
            tf.summary.histogram("biases conv2", b_conv2)
            h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2)
            tf.summary.histogram("act conv2", h_conv2)
            h_pool2 = max_pool_2x2(h_conv2)
    
        return h_pool2
    
    
    # --- Random Fourier Features --- #
    
    def random_variable(shape, sigma):
        W = np.random.normal(size=shape, scale=sigma).astype(np.float32)
        return tf.Variable(W, name="random_Weights", trainable=False)
    
    
    def random_biases(shape):
        b = np.random.uniform(0, 2 * np.pi, size=shape).astype(np.float32)
        return tf.Variable(b, name="random_biase", trainable=False)
    
    
    # --- Representation Layer --- #
    
    def random_features(conv_out, sigma):
        with tf.name_scope("random_features"):
            init_dim = np.prod([s.value for s in conv_out.shape if s.value is not None])
            conv_out2 = tf.reshape(conv_out, [-1, init_dim])
    
            W = random_variable((init_dim, init_dim), sigma)
            b = random_biases(init_dim)
            h1 = tf.matmul(conv_out2, W, name="Wx") + b
            h1_cos = tf.cos(h1)
            h1_final = tf.scalar_mul(np.sqrt(2.0 / init_dim).astype(np.float32), h1_cos)
            return h1_final
    
    
    def fully_connected(conv_out):
        with tf.name_scope("fc_1"):
    
            init_dim = np.prod([s.value for s in conv_out.shape if s.value is not None])
            h_pool2_flat = tf.reshape(conv_out, [-1, init_dim])
            W_fc1 = weight_variable([init_dim, 4096*2])
    
            b_fc1 = bias_variable([4096*2])
            h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
            tf.summary.histogram("weights", W_fc1)
            tf.summary.histogram("biases", b_fc1)
    
        return h_fc1
    
    
    if __name__ == '__main__':
    
        print("Sigma = {}".format(SIGMA))
    
        with tf.Graph().as_default():
    
            input_dim, output_dim = X_train.shape[1], Y_train.shape[1]
    
            x = tf.placeholder(tf.float32, shape=[None, input_dim], name="x")
            y_ = tf.placeholder(tf.float32, shape=[None, output_dim], name="labels")
    
    
            # side size is width or height of the images
            side_size = int(np.sqrt(input_dim))
    
            x_image = tf.reshape(x, [-1, side_size, side_size, 1])
            tf.summary.image("digit", x_image, max_outputs=3)
    
            # Representation layer
    
            # out_fc = fully_connected(h_conv)  # 95% accuracy
    
            # out_fc = tf.nn.relu(fast_food(h_conv, SIGMA, nbr_stack=1))  # 83% accuracy (conv) | 56% accuracy (noconv)
    
            # out_fc = tf.nn.relu(fast_food(h_conv, SIGMA, nbr_stack=2))
    
            # out_fc = tf.nn.relu(fast_food(h_conv, SIGMA, nbr_stack=2, trainable=True))
    
            out_fc = tf.nn.relu(fast_food(h_conv, SIGMA, trainable=True))  # 84% accuracy (conv) | 59% accuracy (noconv)
    
            # out_fc = fast_food(h_conv, SIGMA, diag=True, trainable=True)  # 84% accuracy (conv) | 59% accuracy (noconv)
            # out_fc = random_features(h_conv, SIGMA)  # 82% accuracy (conv) | 47% accuracy (noconv)
    
    
            # classification
            with tf.name_scope("fc_2"):
                keep_prob = tf.placeholder(tf.float32, name="keep_prob")
                h_fc1_drop = tf.nn.dropout(out_fc, keep_prob)
                dim = np.prod([s.value for s in h_fc1_drop.shape if s.value is not None])
    
                W_fc2 = weight_variable([dim, output_dim])
                b_fc2 = bias_variable([output_dim])
    
                tf.summary.histogram("weights", W_fc2)
                tf.summary.histogram("biases", b_fc2)
    
                y_conv = tf.matmul(h_fc1_drop, W_fc2) + b_fc2
    
            # calcul de la loss
            with tf.name_scope("xent"):
                cross_entropy = tf.reduce_mean(
                    tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y_conv, name="xentropy"),
                    name="xentropy_mean")
                tf.summary.scalar('loss-xent', cross_entropy)
    
            # calcul du gradient
            with tf.name_scope("train"):
                global_step = tf.Variable(0, name="global_step", trainable=False)
                train_optimizer = tf.train.AdamOptimizer(learning_rate=1e-4).minimize(cross_entropy, global_step=global_step)
    
            # calcul de l'accuracy
            with tf.name_scope("accuracy"):
                predictions = tf.argmax(y_conv, 1)
                correct_prediction = tf.equal(predictions, tf.argmax(y_, 1))
                accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
                tf.summary.scalar("accuracy", accuracy)
    
            merged_summary = tf.summary.merge_all()
    
            init = tf.global_variables_initializer()
            # Create a session for running Ops on the Graph.
            sess = tf.Session()
            # Instantiate a SummaryWriter to output summaries and the Graph.
    
            summary_writer = tf.summary.FileWriter("results_deepfried_stacked")
    
            summary_writer.add_graph(sess.graph)
            # Initialize all Variable objects
            sess.run(init)
            # actual learning
            started = t.time()
    
            feed_dict_val = {x: X_val, y_: Y_val, keep_prob: 1.0}
    
            for i in range(1100):
                X_batch = get_next_batch(X_train, i, 64)
                Y_batch = get_next_batch(Y_train, i, 64)
                feed_dict = {x: X_batch, y_: Y_batch, keep_prob: 0.5}
    
                # le _ est pour capturer le retour de "train_optimizer" qu'il faut appeler
                # pour calculer le gradient mais dont l'output ne nous interesse pas
    
                _, loss, y_result, x_exp = sess.run([train_optimizer, cross_entropy, y_conv, x_image], feed_dict=feed_dict)
    
                if i % 100 == 0:
                    print('step {}, loss {} (with dropout)'.format(i, loss))
    
                    r_accuracy = sess.run([accuracy], feed_dict=feed_dict_val)
                    print("accuracy: {} on validation set (without dropout).".format(r_accuracy))
    
                    summary_str = sess.run(merged_summary, feed_dict=feed_dict)
                    summary_writer.add_summary(summary_str, i)
    
    
            accuracy, preds = sess.run([accuracy, predictions], feed_dict={
    
                x: X_test, y_: Y_test, keep_prob: 1.0})
    
            print('test accuracy %g' % accuracy)
            np.set_printoptions(threshold=np.nan)
            print("Prediction sample: " + str(preds[:50]))
    
            print("Actual values: " + str(np.argmax(Y_test[:50], axis=1)))
    
            print("Elapsed time: %.4f s" % (stoped - started))