diff --git a/skluc/main/keras_/__init__.py b/skluc/main/keras_/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/skluc/main/keras_/kernel.py b/skluc/main/keras_/kernel.py
new file mode 100644
index 0000000000000000000000000000000000000000..653be6cc6a7e517558259e069ddcf847fcccc7bc
--- /dev/null
+++ b/skluc/main/keras_/kernel.py
@@ -0,0 +1,31 @@
+import tensorflow as tf
+
+from skluc.main.tensorflow_.utils import replace_nan
+
+
+def keras_linear_kernel(args):
+    X = args[0]
+    Y = args[1]
+    X = tf.nn.l2_normalize(X, axis=-1)
+    Y = tf.nn.l2_normalize(Y, axis=-1)
+    return tf.matmul(X, tf.transpose(Y))
+
+def keras_chi_square_CPD(args):
+    X = args[0]
+    Y = args[1]
+    # X = tf.nn.l2_normalize(X, axis=-1)
+    # Y = tf.nn.l2_normalize(Y, axis=-1)
+    # the drawing of the matrix X expanded looks like a wall
+    wall = tf.expand_dims(X, axis=1)
+    # the drawing of the matrix Y expanded looks like a floor
+    floor = tf.expand_dims(Y, axis=0)
+    numerator = tf.square((wall - floor))
+    denominator = wall + floor
+    quotient = numerator / denominator
+    quotient_without_nan = replace_nan(quotient)
+    K = - tf.reduce_sum(quotient_without_nan, axis=2)
+    return K
+
+
+if __name__ == '__main__':
+    a = tf.Constant(value=0)
\ No newline at end of file
diff --git a/skluc/main/keras_/kernel_approximation/__init__.py b/skluc/main/keras_/kernel_approximation/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..3a656798f67515eaeb6933a6163a6bcd18de0d0e
--- /dev/null
+++ b/skluc/main/keras_/kernel_approximation/__init__.py
@@ -0,0 +1,2 @@
+from skluc.main.tensorflow_.kernel_approximation import fastfood_layer as fastfood_layer
+from skluc.main.tensorflow_.kernel_approximation import  nystrom_layer as nystrom_layer
diff --git a/skluc/main/keras_/kernel_approximation/nystrom_layer.py b/skluc/main/keras_/kernel_approximation/nystrom_layer.py
new file mode 100644
index 0000000000000000000000000000000000000000..1c2458c32d19f30574e838a5fd18b95919ab9e77
--- /dev/null
+++ b/skluc/main/keras_/kernel_approximation/nystrom_layer.py
@@ -0,0 +1,137 @@
+"""
+Convnet with nystrom approximation of the feature map.
+
+"""
+import time as t
+
+import numpy as np
+import keras
+from sklearn.metrics.pairwise import rbf_kernel, linear_kernel, additive_chi2_kernel, chi2_kernel, laplacian_kernel
+
+import skluc.main.data.mldatasets as dataset
+from skluc.main.tensorflow_.kernel import tf_rbf_kernel, tf_chi_square_CPD, tf_chi_square_CPD_exp, tf_laplacian_kernel, tf_linear_kernel
+from skluc.main.utils import logger
+import tensorflow as tf
+
+class DeepstromLayerEndToEnd(keras.layers.Layer):
+    def __init__(self,
+                 subsample_size,
+                 kernel_name,
+                 out_dim=None,
+                 activation=None,
+                 sum_of_kernels=False,
+                 stack_of_kernels=False,
+                 kernel_dict={}
+                 ):
+
+        def init_kernel():
+            if kernel_name == "rbf":
+                kernel_fct = rbf_kernel
+                tf_kernel_fct = tf_rbf_kernel
+            elif kernel_name == "linear":
+                kernel_fct = linear_kernel
+                tf_kernel_fct = tf_linear_kernel
+            elif kernel_name == "chi2_cpd":
+                kernel_fct = additive_chi2_kernel
+                tf_kernel_fct = tf_chi_square_CPD
+            elif kernel_name == "chi2_exp_cpd":
+                kernel_fct = chi2_kernel
+                tf_kernel_fct = tf_chi_square_CPD_exp
+            elif kernel_name == "chi2_pd":
+                raise NotImplementedError("Bien verifier que ce code ne fait pas bordel")
+            elif kernel_name == "laplacian":
+                tf_kernel_fct = tf_laplacian_kernel
+                kernel_fct = laplacian_kernel
+            else:
+                raise ValueError("Unknown kernel name: {}".format(kernel_name))
+            return kernel_name, kernel_fct, tf_kernel_fct, kernel_dict
+
+        def init_output_dim(subsample_size):
+            if out_dim is not None and out_dim > subsample_size:
+                logger.debug("Output dim is greater than deepstrom subsample size. Aborting.")
+                exit()
+            elif out_dim is None:
+                return subsample_size
+            else:
+                return out_dim
+
+        def init_activation():
+            if activation == "tan":
+                activation_fct = tf.nn.tanh
+            elif activation == "relu":
+                activation_fct = tf.nn.relu
+            else:
+                activation_fct = activation
+
+            return activation_fct
+
+        super().__init__()
+
+        self.__subsample_size = subsample_size
+
+        self.__sum_of_kernels = sum_of_kernels
+        self.__stack_of_kernels = stack_of_kernels
+
+        self.__kernel_name, self.__kernel_fct, self.__tf_kernel_fct, self.__kernel_dict = init_kernel()
+        self.__output_dim = init_output_dim(self.__subsample_size)
+        self.__activation = init_activation()
+        self.__W_matrix = None
+
+        logger.info("Selecting deepstrom layer function with "
+                    "subsample size = {}, "
+                    "output_dim = {}, "
+                    "{} activation function "
+                    "and kernel = {}"
+                    .format(self.__subsample_size,
+                            self.__output_dim,
+                            "with" if self.__activation else "without",
+                            self.__kernel_name))
+
+    def build(self, input_shape):
+        if self.__output_dim != 0:
+            # outputdim == 0 means there is no W matrix and the kernel vector is directly added as input to
+            # the next layer
+            self.__W_matrix = self.add_weight(
+                name="W_nystrom",
+                shape=[self.__subsample_size, self.__output_dim],
+                initializer='glorot_uniform',
+                trainable=True
+            )
+        super(DeepstromLayerEndToEnd, self).build(input_shape)
+
+    @property
+    def output_dim(self):
+        return self.__output_dim
+
+    def call(self, inputs, **kwargs):
+        if type(inputs) is not list:
+            raise ValueError("Inputs of layer deepstrom should be a list")
+        if len(inputs[0].shape) != 2:
+            raise ValueError(f"Input x should be 2D but it is {len(inputs[0].shape)}D")
+        if len(inputs[1].shape) != 2:
+            raise ValueError(f"Input subsample should be 2D but it is {len(inputs[1].shape)}D")
+        if inputs[1].shape[0] != self.__subsample_size:
+            raise ValueError(f"Subsample should be of size {self.__subsample_size}")
+        if inputs[0][0].shape[0] != inputs[1][0].shape[0]:
+            raise ValueError(f"Input and subsample should have the same dimension")
+
+        input_x = inputs[0]
+        input_sub = inputs[1]
+        input_x = tf.nn.l2_normalize(input_x, axis=-1)
+        input_sub = tf.nn.l2_normalize(input_sub, axis=-1)
+        with tf.name_scope("NystromLayer"):
+            with tf.name_scope("kernel_vec"):
+                kernel_vector = self.__tf_kernel_fct(input_x, input_sub, **self.__kernel_dict)
+                tf.summary.histogram("kernel_vector", kernel_vector)
+
+            if self.__output_dim != 0:
+                out = tf.matmul(kernel_vector, self.__W_matrix)
+                tf.summary.histogram("W_matrix", self.__W_matrix)
+            else:
+                out = kernel_vector
+        if self.__activation is not None:
+            out = self.__activation(out)
+        return out
+
+    def compute_output_shape(self, input_shape):
+        return (input_shape[0], self.output_dim)
\ No newline at end of file