Spaces:
No application file
No application file
File size: 7,691 Bytes
8cc5050 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 | import numpy as np
from abc import ABC, abstractmethod
class Layer(ABC):
def __init__(self):
self.input = None
self.output = None
@abstractmethod
def forward(self, x_train):
pass
@abstractmethod
def backward(self, e, eta=0.01):
pass
class Conv2D(Layer):
def __init__(self, num_filter, filter_size, rgb: bool = True, stride=(1,1), pad: str = 'valid'):
super().__init__()
self.num_filter = num_filter
self.filter_size = filter_size
self.stride = stride
self.pad = pad
self.input = None
self.padding = None
if rgb:
self.filter = np.random.randn(num_filter, 3, filter_size, filter_size)
else:
self.filter = np.random.randn(num_filter, 1, filter_size, filter_size)
self.bias = np.random.randn(num_filter)
def forward(self, x_train):
self.input = x_train
num_samples, h, w, c = self.input.shape
pad_h, pad_w = (0, 0)
if self.pad == 'same':
pad_h = ((h - 1)(self.stride[0] - 1) + self.filter[0] - 1)//2
pad_w = ((w - 1)(self.stride[1] - 1) + self.filter[1] - 1)//2
self.padding = (pad_h, pad_w)
self.input = np.pad(x_train, pad_width=((0, 0,), (pad_h, pad_h), (pad_w, pad_w), (0, 0)),mode='constant')
out_h = (h - self.filter_size + 1) // self.stride + 1
out_w = (w - self.filter_size + 1) // self.stride + 1
self.output = np.zeros((num_samples, self.num_filter, out_h, out_w))
for h in range(out_h):
for w in range(out_w):
h_s = h * self.stride
h_e = h_s + self.filter_size
w_s = w * self.stride
w_e = w_s + self.filter_size
self.output[:, :, h, w] = np.sum(self.input[:, :, h_s:h_e, w_s:w_e] * self.filter + self.bias)
return self.output
def backward(self, e, eta=0.01):
num_samples, channel, h, w = self.input.shape
input_error = np.zeros(self.input.shape)
filter_error = np.zeros(self.filter.shape)
# get error per stride
for i in range((h - self.filter_size) // self.stride + 1):
for j in range((w - self.filter_size) // self.stride + 1):
h_s = i * self.stride
w_s = j * self.stride
region = self.input[:, :, h_s:h_s+self.filter_size, w_s:w_s+self.filter_size]
for k in range(self.num_filter): # take loop if the image is rgb
filter_error += np.sum(region * e[:, k, i, j][:, None, None, None], axis=0)
for n in range(num_samples):
input_error[n, :, h_s:h_s + self.filter_size, w_s:w_s + self.filter_size] += np.sum(self.filter * e[n, :, i, j][:, None, None, None], axis=0)
self.filter -= eta * filter_error
if self.padding != (0, 0):
input_error = input_error[:, :, self.padding[0]: -self.padding[0], self.padding[1]:-self.padding[1]]
return input_error
class MaxPooling2D(Layer):
def __init__(self, pool_size=2, stride=2):
super().__init__()
self.pool_size = pool_size
self.stride = stride
self.max_indices = None
def forward(self, x_train):
batch, channel, h, w = x_train.shape
self.input = x_train
out_h = (h-self.pool_size)//self.stride + 1
out_w = (h-self.pool_size)//self.stride + 1
self.output = np.zeros((batch, channel, out_h, out_w))
self.max_indices = np.zeros_like(self.output, dtype=int)
for i in range(out_h):
for j in range(out_w):
h_s = i*self.stride
h_e = h_s+self.pool_size
w_s = j*self.stride
w_e = w_s+self.pool_size
region = input[:, :, h_s:h_e, w_s:w_e]
self.output[:, :, i, j] = np.max(region, axis=(2, 3))
self.max_indices[:, :, i, j] = np.argmax(region.reshape(batch, channel, -1), axis=1)
return self.output
def backward(self, e, eta=0.01):
input_error = np.zeros_like(self.input)
out_height, out_width = self.output.shape[2], self.output.shape[3]
for i in range(out_height):
for j in range(out_width):
h_s = i * self.stride
w_s = j * self.stride
region = self.input[:, :, h_s:h_s + self.pool_size, w_s:w_s + self.pool_size]
region_reshaped = region.reshape(region.shape[0], region.shape[1], -1)
for k in range(region_reshaped.shape[0]):
for l in range(region_reshaped.shape[1]):
max_index = self.max_indices[k, l, i, j]
input_error[k, l, h_s:h_s + self.pool_size, w_s:w_s + self.pool_size].reshape(-1)[max_index] = e[k, l, i, j]
return input_error
class Dense(Layer):
def __init__(self, input_size, output_size, activation=None):
super().__init__()
self.weights = np.random.randn(input_size, output_size) * 0.01
self.biases = np.zeros((1, output_size))
self.activation = activation
@staticmethod
def sigmoid(x):
return 1/(1 + np.exp(-x))
@staticmethod
def relu(x):
return np.maximum(0, x)
@staticmethod
def sigmoid_dev(x):
return x*(1-x)
@staticmethod
def relu_dev(x):
return np.where(x>1, 1, 0)
def forward(self, x_train):
self.input = x_train
z = np.dot(x_train, self.weights) + self.biases
if self.activation == 'sigmoid':
self.output = self.sigmoid(z)
elif self.activation == 'relu':
self.output = self.relu(z)
return self.output
def backward(self, e, eta=0.01):
activation_derivative = 0
if self.activation == 'relu':
activation_derivative = self.relu_dev(self.output)
else:
activation_derivative = self.sigmoid_dev(self.output)
input_error = np.dot(e * activation_derivative, self.weights.T)
weights_error = np.dot(self.input.T, e * activation_derivative)
# Update parameters
self.weights -= eta * weights_error
self.biases -= eta * np.sum(e * activation_derivative, axis=0, keepdims=True)
return input_error
class Flatten(Layer):
def __init__(self):
super().__init__()
def forward(self, x_train):
self.input = x_train
flatten = x_train.reshape((x_train.shape[0], -1))
return flatten
def backward(self, e, eta=0.01):
return e.reshape(self.input.shape)
class CNN:
def __init__(self):
self.layers = []
def append(self, layer):
self.layers.append(layer)
def predict(self, input):
for layer in self.layers:
input = layer.forward(input)
return input
def fit(self, x_train, y_train, batch_size=36, epochs=10):
num = x_train.shape[0]
for i in range(epochs):
indices = np.arange(num)
np.random.shuffle(indices)
x = x_train[indices]
y = y_train[indices]
for i in range(0, num, batch_size):
x_batch = x[i:i+batch_size]
y_batch = y[i:i+batch_size]
output = self.predict(x_batch)
error = output - y_batch
for layer in reversed(self.layers):
error = layer.backward(error, 0.01)
|