diff --git a/2_CNN/Pooling.py b/2_CNN/Pooling.py
new file mode 100644
index 0000000000000000000000000000000000000000..39b5f1c966ba100e9db1d01c69367b185718dd70
--- /dev/null
+++ b/2_CNN/Pooling.py
@@ -0,0 +1,51 @@
+from .Base import BaseLayer
+import numpy as np
+
+
+class Pooling(BaseLayer):
+    kernels = None
+    weights = None
+
+    def __init__(self, stride_shape, pooling_shape):
+        super().__init__()
+        self.stride_shape = stride_shape
+        self.pooling_shape = pooling_shape
+        self.input_tensor = None
+        self.input_tensor_shape = None
+        self.error_tensor = None
+        self.max_indices = None
+
+    def forward(self, input_tensor):
+        self.input_tensor = input_tensor
+        self.input_tensor_shape = input_tensor.shape
+
+        op_tensor_num_rows = int(np.floor((input_tensor.shape[2] - self.pooling_shape[0]) / self.stride_shape[0] + 1))
+        op_tensor_num_cols = int(np.floor((input_tensor.shape[3] - self.pooling_shape[1]) / self.stride_shape[1] + 1))
+
+        output_tensor = np.empty((input_tensor.shape[0], input_tensor.shape[1], op_tensor_num_rows, op_tensor_num_cols))
+        max_indices = []
+        for i in np.arange(input_tensor.shape[0]):  # iterate through batch
+            for j in np.arange(input_tensor.shape[1]):  # iterate through channels
+                for k in np.arange(output_tensor.shape[2]):
+                    for l in np.arange(output_tensor.shape[3]):
+                        subblock_mat = input_tensor[i][j][k * self.stride_shape[0] : k * self.stride_shape[0] + self.pooling_shape[0], l * self.stride_shape[1] : l * self.stride_shape[1] + self.pooling_shape[1]]
+                        output_tensor[i][j][k][l] = np.max(subblock_mat)
+                        c, d = np.unravel_index(subblock_mat.argmax(), subblock_mat.shape)
+                        max_indices.append([i, j, k * self.stride_shape[0] + c, l * self.stride_shape[1] + d])
+
+        self.max_indices = max_indices
+        return output_tensor
+
+    def backward(self, error_tensor):
+        self.error_tensor = error_tensor
+        output_error_tensor = np.zeros(self.input_tensor_shape)
+        for i in np.arange(error_tensor.shape[0]):  # iterate through batch
+            for j in np.arange(error_tensor.shape[1]):  # iterate through channels
+                for k in np.arange(error_tensor.shape[2]):
+                    for l in np.arange(error_tensor.shape[3]):
+                        index = self.max_indices[0]
+                        del(self.max_indices[0])
+                        output_error_tensor[index[0]][index[1]][index[2]][index[3]] += error_tensor[i][j][k][l]
+
+        return output_error_tensor
+