Code Monkey home page Code Monkey logo

simnets-tf's Introduction

simnets-tf

SimNets is a generalization of Convolutional Neural Networks that was first proposed by Cohen et al. (at NIPS 2014 DL Workshop and CVPR 2016), in whish they showed it to be well-suited for classification under limited computional resources. It was later discovered that under some settings SimNets realize Convolutional Arithmetic Circuits, where all computation is done in log-space instead of linear-space. We have previously released an implementation of SimNets in Caffe, and now we release our implementaiton in Tensorflow, with wrapper layers for Keras. You can read the documentation at https://huji-deep.github.io/simnets-tf/.

SimNets implementation in TensorFlow

Binary installation

Binary installation requires a cuda toolkit installation >= 7.5.
Download the .whl file from the GitHub release tab, then type:

python -m pip install <whl file>

all requirements should be installed automatically.

Building from Source

Building from source requires:

  1. A working c++ compiler with c++11 support (gcc >= 4.7)
  2. Cuda toolkit installed (for nvcc)
  3. CMake >= 3.0 (apt install cmake)
  4. TensorFlow installed for the Python interpreter you intend to use

Important: The following command should run without error:

python -c 'import tensorflow as tf'

To build the project type the following commands:
Python 2.7:

git clone --recursive https://github.com/HUJI-Deep/simnets-tf.git
cd simnets-tf
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DSIMNETS_PYTHON_VERSION=2.7 -DCMAKE_INSTALL_PREFIX=install
make -j simnet_ops

Python 3.5:

git clone --recursive https://github.com/HUJI-Deep/simnets-tf.git
cd simnets-tf
mkdir build
cd build
cmake .. -DCMAKE_BUILD_TYPE=Release -DSIMNETS_PYTHON_VERSION=3.5 -DCMAKE_INSTALL_PREFIX=install
make -j simnet_ops

To test the code you can now type:

make test_simnet_ops

This should run for about two minutes and return without any errors.
Now you can create a .whl file:

make create_wheel

Finally, to install the simnets-tf package type (remember to use the right interpreter):

cd install/dist
python -m pip install <whl file>

The installation is successful if the following runs (again, remember to use the right interpreter):

python -c 'import simnets'

Usage example

Keras

import simnets.keras as sk
import keras
from keras.datasets import mnist
from keras.models import Model
from keras.layers import Input, Flatten, AveragePooling2D, Lambda
from keras import backend as K
import numpy as np

batch_size = 64
num_classes = 10
sim_kernel = 2
sim_channels = 32
mex_channels = sim_channels
epochs = 3

# input image dimensions
img_rows, img_cols = 28, 28

# 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(x_train.shape[0], 1, img_rows, img_cols)
x_test = x_test.reshape(x_test.shape[0], 1, img_rows, img_cols)
input_shape = (1, img_rows, img_cols)

x_train = x_train.astype('float32')
x_test = x_test.astype('float32')
x_train = x_train / 255.0 - 0.5
x_test = x_test / 255.0 - 0.5

print(x_train.shape[0], 'train samples')
print(x_test.shape[0], 'test samples')

# convert class vectors to binary class matrices
y_train = keras.utils.to_categorical(y_train, num_classes)
y_test = keras.utils.to_categorical(y_test, num_classes)


def sum_pooling_layer(x, pool_size):
   x = AveragePooling2D(pool_size=pool_size, padding='valid')(x)
   x = Lambda(lambda x: x * pool_size[0] * pool_size[1])(x)
   return x


a = Input(shape=(1, img_rows, img_cols))
b = sk.Similarity(sim_channels,
                 ksize=[2, 2], strides=[2, 2], similarity_function='L2',
                 normalization_term=True, padding=[2, 2], out_of_bounds_value=np.nan, ignore_nan_input=True)(a)
while b.shape[-2:] != (1, 1):
   mex_channels *= 2
   b = sk.Mex(mex_channels,
              blocks=[int(b.shape[-3]), 1, 1], strides=[int(b.shape[-3]), 1, 1],
              softmax_mode=True, normalize_offsets=True,
              use_unshared_regions=True, unshared_offset_region=[2])(b)
   b = sum_pooling_layer(b, pool_size=(2, 2))

b = sk.Mex(num_classes,
          blocks=[mex_channels, 1, 1], strides=[mex_channels, 1, 1],
          softmax_mode=True, normalize_offsets=True,
          use_unshared_regions=True, shared_offset_region=[1])(b)
b = Flatten()(b)
model = Model(inputs=[a], outputs=[b])

print(model.summary())

def softmax_loss(y_true, y_pred):
   return K.categorical_crossentropy(y_pred, y_true, True)

model.compile(loss=softmax_loss,
             optimizer=keras.optimizers.nadam(lr=1e-2, epsilon=1e-6),
             metrics=['accuracy'])

sk.perform_unsupervised_init(model, 'kmeans', layers=None, data=x_train, batch_size=100)

model.fit(x_train, y_train,
         batch_size=batch_size,
         epochs=epochs,
         verbose=1,
         validation_data=(x_test, y_test))
score = model.evaluate(x_test, y_test, verbose=0)
print('Test loss:', score[0])
print('Test accuracy:', score[1])

Low level

import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
from simnets import similarity
from simnets.unsupervised import similarity_unsupervised_init
import matplotlib.pyplot as plt

mnist = input_data.read_data_sets('MNIST_data', one_hot=True)
sess = tf.InteractiveSession()

x = tf.placeholder(tf.float32, shape=[None, 784])
xr = tf.reshape(x, [-1, 1, 28, 28])
y_ = tf.placeholder(tf.float32, shape=[None, 10])

shape = [10, 1, 28, 28]

templates = tf.Variable(tf.truncated_normal(shape, stddev=0.1))
weights_var = tf.Variable(tf.truncated_normal(shape, stddev=0.1))
weights = tf.abs(weights_var)

sim = similarity(xr, templates, weights, similarity_function='L2', ksize=[28,28], strides=[28,28], padding=[0,0])
y = tf.reshape(sim, [-1, 10])

kmo_init, kmo = similarity_unsupervised_init('kmeans', sim, templates, weights_var)

cross_entropy = tf.reduce_mean(
    tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y))

train_step = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entropy)

sess.run(tf.global_variables_initializer())


for idx in range(300):
    batch = mnist.train.next_batch(100)
    if idx == 0:
        kmo_init.run(feed_dict={x: batch[0], y_: batch[1]})
    kmo.run(feed_dict={x: batch[0], y_: batch[1]})
    if (idx + 1) % 100 == 0:
        print('kmeans', idx+1, '/', 1000)

def normalize(img):
    return (img - img.min()) / (img.max() - img.min())

templates_np = tf.get_default_session().run(templates)
plt.figure(1)
for i in range(10):
    plt.subplot(4,3, i+1)
    plt.imshow(normalize(templates_np[i,0,...]))
plt.show()

for idx in range(1000):
    batch = mnist.train.next_batch(100)
    train_step.run(feed_dict={x: batch[0], y_: batch[1]})
    if (idx + 1) % 100 == 0:
        print(idx+1, '/', 1000)

correct_prediction = tf.equal(tf.argmax(y,1), tf.argmax(y_,1))
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
print('Accuracy:', accuracy.eval(feed_dict={x: mnist.test.images, y_: mnist.test.labels}))

simnets-tf's People

Contributors

elhanan7 avatar orsharir avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

simnets-tf's Issues

Padding API conventions

Notice that in Tensorflow the padding api is specified in uppercase, i.e. 'SAME' and 'VALID', while in Keras it is in lowercase, i.e. 'same' and 'valid'. Our API should adhere to this convention. You could probably use this Keras's method to handle this change (or simply write your own).

Cannot reproduce the cifar10 result in Deep SimNet

Hello, I am trying to involve SimNet in my project.
While there are some problems when I tried to reproduce the results listed in Deep SimNet
The structure of my one-layer SimNet is:

 model = Sequential()                                                                                                                                                                                       
 model.add(InputLayer(input_shape=(3,32,32)))                                                                                                                                                               
 model.add(Conv2D(filters=32, kernel_size=(5,5), padding='same', use_bias=None,\                                                                                                                            
                                  data_format='channels_first', strides=(1,1)))                                                                                                                                                          
 model.add(sk.Similarity(32, blocks=[1,1], strides=[1,1],\
                         similarity_function='L2',normalization_term=True, padding=[1,1],\                                                                                 
                         out_of_bounds_value=np.nan, ignore_nan_input=True ))                                                                                                                                                   
 model.add(sk.Mex(32,                                                                                                                                                                                       
               blocks=[1, 3, 3], strides=[32, 2, 2],                                                                                                                                                        
               softmax_mode=False, normalize_offsets=True,                                                                                                                                                  
               use_unshared_regions=True, unshared_offset_region=[2]))                                                                                                                                      
 model.add(sk.Mex(10,                                                                                                                                                                                       
               blocks=[1, 16, 16], strides=[32, 16, 16],                                                                                                                                                    
               softmax_mode=True, normalize_offsets=True,                                                                                                                                                   
               use_unshared_regions=True, unshared_offset_region=[2]))                                                                                                                                      
 model.add(Flatten(data_format='channels_first'))      

with

_________________________________________________________________
Layer (type)                 Output Shape              Param #   
=================================================================
input_1 (InputLayer)         (None, 3, 32, 32)         0         
_________________________________________________________________
conv2d_1 (Conv2D)            (None, 32, 32, 32)        2400      
_________________________________________________________________
similarity_1 (Similarity)    (None, 32, 34, 34)        2048      
_________________________________________________________________
mex_1 (Mex)                  (None, 32, 16, 16)        1152      
_________________________________________________________________
mex_2 (Mex)                  (None, 10, 1, 1)          2560      
_________________________________________________________________
flatten_1 (Flatten)          (None, 10)                0         
=================================================================
Total params: 8,160
Trainable params: 8,160
Non-trainable params: 0
_________________________________________________________________

But, the accuracy will always be 10%, which is a perfect random guess of CIFAR10. (At least, it converged to something.....)
Could you tell me what's wrong with my definition?
And Could you tell me the whole hyper-parameters in your work?

Thanks,
Neo

Issue with save/load model in Keras

I've stumble upon a usability bug with using our layers with Keras. The save/load model functions don't play well with our layers, even when I'm using the custom_objects optional argument. To reproduce simply build a model composed of our layers, followed by saving the model, and then loading it, e.g.:

import keras
import simnets as sk

model = # Create some model built with our layers
model.save_model('test.hdf5')

custom_objects= {
    'Similarity': sk.Similarity,
    'Mex': sk.Mex
}
loaded_model = keras.models.load_model('test.hdf5', custom_objects=custom_objects)

Despite the above, saving and loading just the weights does work well, so there is a temporary workaround.

More flexible MEX's blocks, strides, and padding syntax in Keras and Tensorflow

It could be that I've forgot to specify it, but the blocks syntax should allow for the user to specify that a dimension stretches to the size of the input. In Caffe we do this with negative numbers, e.g. if block_c = -1 is given by the user, then at runtime we replace it with block_c = <number of input channels>. I believe we should adopt the same syntax for Tensorflow, with a slight improvement: in python, the operator should allow lists of length 2, and automatically expand the list from [block_h block_w] to [-1 block_h block_w] -- this should be for both the low-level Tensorflow python's ops, as well as in Keras. The same logic should be applied to the padding and strides syntax as well.

Make gradient tests for similarity layer more general

In the current tests of the similarity layer, most cases are not covered. These tests should be generalize to all possible combinations of the different flags supported by the similarity layer, and using random input / parameters.

Failure to reproduce results of prior experiements

I've tried to reproduce our previous results of networks trained in Caffe, but I cannot get them to converge during training -- the loss function is either stuck or increasing. This seems to be some sort of bug in the current implementation, however, it's a bit difficult understanding where's the fault, given that the tests are running fine.

I'll upload my code later on, while I try to see if I get more specific information on the source of this issue.

Install wheel file not working

When I run the command python -m pip install simnets-0.0.1-py3-none-any.whl after a successful make create_wheel, I get the following error: simnets-0.0.1-py3-none-any.whl is not a supported wheel on this platform. Do you have any idea what's wrong?

Default initializers

Beyond the fact we need to fix issue #3, I believe the current default initializers for the parameters of the layers are not right.

For the weights, the default initializer should be ones, and not uniform. For the templates the default initializer should be normal gaussian. For the MEX layers, each offset vector (the rows of the weight matrix) should be drawn from a Dirichlet distribution of dimension equal to the vector and alpha = 1. You can implement the latter with numpy.random.dirichlet([1] * k), where k is the dimension of each vector.

Cannot change default initialization

It appears that the initialization of the weights of either the similarity layer or the mex layer in Keras are fixed to default values and cannot be changed. This is a very critical aspect of using networks in practice, and although the unsupervised initialization mitigate this problem (partially) for the similarity layer, it does not for the MEX layer.

Please add support for specifying the initializers of the parameters, according to Keras standard interface (as shown here).

Add support for "channels_last" dimensional ordering in Keras

While for our purposes using "channels_first" is preferred (better performance), "channels_last" is still the default most people use. Thus, we should add support in the Keras layers, and add a transpose operation before and after our op if "channels_last" is used by the user. However, in our docs we should still stress that "channels_first" is recommended.

I'll also note that you should add to the documentation of the Tensorflow ops that they expect data only in "channels_first" format (in Tensorflow docs they call it NCHW format).

Correct MAPS version

Switch to using the sub-module version of MAPS, and remove their actual sources (the ones under include) from this repo.

Dirichlet initializer API and usage

The Dirichlet initializer should have a public interface (including documentation), which will accept an alpha value, and return the respective private Dirichlet initializer. This could be something like so:

def dirichlet_init(alpha=1.0):
    def _dirichlet_init(shape, dtype=None):
        if dtype is None:
            dtype = K.floatx()
        num_regions, num_instances, block_c, block_h, block_w = shape
        k = block_c * block_h * block_w
        # when given s as a size argument dirichlet function return an array with shape s + [k]
        # then we reshape the output to be of the same shape as the variable
        init_np = np.random.dirichlet([alpha] * k, size=(num_regions, num_instances)).astype(dtype)
        init_np = np.log(init_np)
        init_np = init_np.reshape(shape)
        return tf.constant(init_np)
    return _dirichlet_init

Notice the above code is already in the corrected form as in #12.

Add unit tests for shape sizes and for correctly setting parameters

Given the cause of issue #8, I suggest you add unit tests for correct setting of input parameters and shapes. E.g. check that when you set unshared/shared weights the parameters size is correct -- do not use the functions you've written to handle this, but instead have hard coded values for a few cases. Do the same for other parameters (block, pooling, etc.).

Refactor ggemm_cpu.hpp

I've just noticed that you've put a lot of extra functions in ggemm_cpu.hpp which have nothing to do with it, e.g. the special ops used by certain layers or the split_patches function. Please put each of these functions in separate logical header instead. E.g. all the mex related functions in a mex_shared_utils.hpp, etc.

Complete documentation of source code

Many functions in the source code are left undocumented, and more importantly many "user-facing" api functions. The documentation should also follow best practices of each respective language, and document the role of each parameter. We should strive such that we can ran documentation generation tools on our code to have the API for using this library available online.

For python, you should follow the reStructuredText syntax, so that we would be able to use readthedocs.io (in the background, it is based on Sphinx). For C++, you should follow the Doxygen syntax.

Tests fail on school's computers

I've tried again to compile the project. Compilation was successful, but when running make test_simnet_ops, all of the tests fail. Here is the log of running this command: simnets-tf-errors.txt

It appears that the failed tests are all related to the gpu -- or the cpu isn't tested when running this command?

Could you me give more demos on simnets.

I use your keras example in README.md. And get only 0.9 acc in MNIST, which is equal to random guess.

What's the best way to train the plain MNIST classifier using SimNet?

Could you give more demos like experiments in.
In Deep SimNet.

BTW, the keras example has some mistakes, fixed in there

Thank you,
Neo

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.