Skip to content

Instantly share code, notes, and snippets.

@vbalnt
Last active March 1, 2019 01:50

Revisions

  1. Vassilis revised this gist Mar 18, 2016. 1 changed file with 0 additions and 6 deletions.
    6 changes: 0 additions & 6 deletions siamese.py
    Original file line number Diff line number Diff line change
    @@ -38,12 +38,6 @@
    from keras.layers import Dense, Dropout, Activation, Flatten
    from keras.layers import Convolution2D, MaxPooling2D
    import math
    from progressbar import AnimatedMarker, Bar, BouncingBar, Counter, ETA, \
    FileTransferSpeed, FormatLabel, Percentage, \
    ProgressBar, ReverseBar, RotatingMarker, \
    SimpleProgress, Timer
    from skimage.viewer import ImageViewer
    import matplotlib.pyplot as plt

    def euclidean_distance(inputs):
    assert len(inputs) == 2, ('Euclidean distance needs '
  2. Vassilis created this gist Mar 17, 2016.
    155 changes: 155 additions & 0 deletions siamese.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,155 @@
    '''Train a Siamese MLP on pairs of digits from the MNIST dataset.
    It follows Hadsell-et-al.'06 [1] by computing the Euclidean distance on the
    output of the shared network and by optimizing the contrastive loss (see paper
    for mode details).
    [1] "Dimensionality Reduction by Learning an Invariant Mapping"
    http://yann.lecun.com/exdb/publis/pdf/hadsell-chopra-lecun-06.pdf
    Run on GPU: THEANO_FLAGS=mode=FAST_RUN,device=gpu,floatX=float32 python mnist_siamese_graph.py
    Gets to 99.5% test accuracy after 20 epochs.
    3 seconds per epoch on a Titan X GPU
    '''
    from __future__ import absolute_import
    from __future__ import print_function
    import numpy as np
    np.random.seed(1337) # for reproducibility

    import random
    from keras.datasets import mnist
    from keras.models import Sequential, Graph
    from keras.layers.core import Dense, Dropout, Lambda
    from keras.optimizers import SGD, RMSprop
    from keras import backend as K

    import numpy as np
    np.random.seed(1337) # for reproducibility
    import random
    from keras.datasets import mnist
    from keras.models import Sequential, Graph
    from keras.layers.core import Dense, Dropout, Lambda
    from keras.optimizers import SGD, RMSprop,Adagrad,Adadelta
    from keras import backend as K
    from numpy import linalg as LA
    import utils
    import numpy.testing as npt
    from keras.layers import Dense, Dropout, Activation, Flatten
    from keras.layers import Convolution2D, MaxPooling2D
    import math
    from progressbar import AnimatedMarker, Bar, BouncingBar, Counter, ETA, \
    FileTransferSpeed, FormatLabel, Percentage, \
    ProgressBar, ReverseBar, RotatingMarker, \
    SimpleProgress, Timer
    from skimage.viewer import ImageViewer
    import matplotlib.pyplot as plt

    def euclidean_distance(inputs):
    assert len(inputs) == 2, ('Euclidean distance needs '
    '2 inputs, %d given' % len(inputs))
    u, v = inputs.values()
    return K.sqrt(K.sum(K.square(u - v), axis=1, keepdims=True))


    def contrastive_loss(y, d):
    '''Contrastive loss from Hadsell-et-al.'06
    http://yann.lecun.com/exdb/publis/pdf/hadsell-chopra-lecun-06.pdf
    '''
    margin = 1
    return K.mean(y * K.square(d) + (1 - y) * K.square(K.maximum(margin - d, 0)))


    def create_pairs(x, digit_indices):
    '''Positive and negative pair creation.
    Alternates between positive and negative pairs.
    '''
    pairs = []
    labels = []
    n = min([len(digit_indices[d]) for d in range(10)]) - 1
    for d in range(10):
    for i in range(n):
    z1, z2 = digit_indices[d][i], digit_indices[d][i+1]
    pairs += [[x[z1], x[z2]]]
    inc = random.randrange(1, 10)
    dn = (d + inc) % 10
    z1, z2 = digit_indices[d][i], digit_indices[dn][i]
    pairs += [[x[z1], x[z2]]]
    labels += [1, 0]
    return np.array(pairs), np.array(labels)


    def create_base_network(input_dim):
    '''Base network to be shared (eq. to feature extraction).
    '''
    seq = Sequential()
    seq.add(Dense(128, input_shape=(input_dim,), activation='relu'))
    seq.add(Dropout(0.1))
    seq.add(Dense(128, activation='relu'))
    seq.add(Dropout(0.1))
    seq.add(Dense(128, activation='relu'))
    return seq


    def compute_accuracy(predictions, labels):
    '''Compute classification accuracy with a fixed threshold on distances.
    '''
    return labels[predictions.ravel() < 0.5].mean()


    # the data, shuffled and split between train and test sets
    (X_train, y_train), (X_test, y_test) = mnist.load_data()
    X_train = X_train.reshape(60000, 784)
    X_test = X_test.reshape(10000, 784)
    X_train = X_train.astype('float32')
    X_test = X_test.astype('float32')
    X_train /= 255
    X_test /= 255
    input_dim = 784
    nb_epoch = 20

    # create training+test positive and negative pairs
    digit_indices = [np.where(y_train == i)[0] for i in range(10)]
    tr_pairs, tr_y = create_pairs(X_train, digit_indices)

    digit_indices = [np.where(y_test == i)[0] for i in range(10)]
    te_pairs, te_y = create_pairs(X_test, digit_indices)

    # network definition
    base_network = create_base_network(input_dim)

    g = Graph()
    g.add_input(name='input_a', input_shape=(input_dim,))
    g.add_input(name='input_b', input_shape=(input_dim,))
    g.add_shared_node(base_network, name='shared', inputs=['input_a', 'input_b'],
    merge_mode='join')
    g.add_node(Lambda(euclidean_distance), name='d', input='shared')
    g.add_output(name='output', input='d')

    # train
    rms = RMSprop()
    g.compile(loss={'output': contrastive_loss}, optimizer=rms)
    # g.fit({'input_a': tr_pairs[:, 0], 'input_b': tr_pairs[:, 1], 'output': tr_y},
    # validation_data={'input_a': te_pairs[:, 0], 'input_b': te_pairs[:, 1], 'output': te_y},
    # batch_size=128,
    # nb_epoch=nb_epoch)

    batch_size=128
    epochs = 10
    nsamples = 60000
    x = np.zeros((batch_size,784))
    y = np.zeros((batch_size,784))
    l = np.zeros((batch_size,1))

    for epoch in range(epochs):
    shuf = np.random.permutation(nsamples)
    nbatches = int(nsamples/batch_size)
    for k in range(nbatches):
    sh = shuf[k*batch_size:k*batch_size+batch_size]
    for s in range(batch_size):
    x[s] = X_train[sh[s],0]
    y[s] = X_train[sh[s],1]
    l[s] = y_train[sh[s]]
    #train here
    tt = g.train_on_batch({'input_a': x, 'input_b': y, 'output': l})
    print(tt[0])