gihakkk commited on
Commit
d731ef0
ยท
verified ยท
1 Parent(s): 5a6e45e

Upload app.py

Browse files
Files changed (1) hide show
  1. app.py +276 -0
app.py ADDED
@@ -0,0 +1,276 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ # ๋ฐ์ดํ„ฐ ๋กœ๋”ฉ์„ ์œ„ํ•ด์„œ๋งŒ tensorflow.keras.datasets๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
3
+ # ์‹ค์ œ ๋ชจ๋ธ ์•„ํ‚คํ…์ฒ˜์™€ ํ•™์Šต ๋กœ์ง์—๋Š” ์ „ํ˜€ ์‚ฌ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.
4
+ from tensorflow.keras.datasets import mnist
5
+ from tensorflow.keras.utils import to_categorical
6
+
7
+ # =================================================================
8
+ # --- 1. ๋ฐ์ดํ„ฐ ์ค€๋น„ ---
9
+ # =================================================================
10
+
11
+ def load_mnist_data():
12
+ """MNIST ๋ฐ์ดํ„ฐ์…‹์„ ๋ถˆ๋Ÿฌ์˜ค๊ณ  ์ „์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค."""
13
+ (x_train, y_train), (x_test, y_test) = mnist.load_data()
14
+
15
+ # ํ”ฝ์…€ ๊ฐ’์„ 0๊ณผ 1 ์‚ฌ์ด๋กœ ์ •๊ทœํ™”
16
+ x_train = x_train.astype('float32') / 255.0
17
+ x_test = x_test.astype('float32') / 255.0
18
+
19
+ # ์ฑ„๋„ ์ฐจ์› ์ถ”๊ฐ€ (N, 28, 28) -> (N, 28, 28, 1)
20
+ x_train = np.expand_dims(x_train, -1)
21
+ x_test = np.expand_dims(x_test, -1)
22
+
23
+ # ๋ ˆ์ด๋ธ”์„ ์›-ํ•ซ ์ธ์ฝ”๋”ฉ
24
+ y_train = to_categorical(y_train, 10)
25
+ y_test = to_categorical(y_test, 10)
26
+
27
+ return x_train, y_train, x_test, y_test
28
+
29
+ # =================================================================
30
+ # --- 2. ๊ณ„์ธต(Layer) ๊ตฌํ˜„ ---
31
+ # =================================================================
32
+
33
+ class ConvLayer:
34
+ """ํ•ฉ์„ฑ๊ณฑ ๊ณ„์ธต (๋‹ค์ค‘ ์ฑ„๋„ ์ž…๋ ฅ ์ง€์›์œผ๋กœ ์ˆ˜์ •๋จ)"""
35
+ def __init__(self, num_filters, filter_size, input_channels):
36
+ self.num_filters = num_filters
37
+ self.filter_size = filter_size
38
+ self.input_channels = input_channels
39
+ # Xavier/Glorot ์ดˆ๊ธฐํ™” (๋‹ค์ค‘ ์ฑ„๋„ ๊ณ ๋ ค)
40
+ self.filters = np.random.randn(filter_size, filter_size, input_channels, num_filters) * np.sqrt(2. / (filter_size * filter_size * input_channels))
41
+ self.biases = np.zeros(self.num_filters)
42
+ self.last_input = None
43
+
44
+ def forward(self, input_image):
45
+ self.last_input = input_image
46
+ batch_size, input_height, input_width, _ = input_image.shape
47
+ output_height = input_height - self.filter_size + 1
48
+ output_width = input_width - self.filter_size + 1
49
+
50
+ output = np.zeros((batch_size, output_height, output_width, self.num_filters))
51
+
52
+ for b in range(batch_size):
53
+ for i in range(output_height):
54
+ for j in range(output_width):
55
+ region = input_image[b, i:(i + self.filter_size), j:(j + self.filter_size), :]
56
+ for f in range(self.num_filters):
57
+ output[b, i, j, f] = np.sum(region * self.filters[:, :, :, f]) + self.biases[f]
58
+ return output
59
+
60
+ def backward(self, d_loss_d_output, learning_rate):
61
+ """์—ญ์ „ํŒŒ ๋ฉ”์„œ๋“œ (์ž…๋ ฅ ๊ทธ๋ž˜๋””์–ธํŠธ ๊ณ„์‚ฐ ์ถ”๊ฐ€)"""
62
+ batch_size, _, _, _ = self.last_input.shape
63
+ d_loss_d_filters = np.zeros_like(self.filters)
64
+ d_loss_d_input = np.zeros_like(self.last_input)
65
+
66
+ # ํ•„ํ„ฐ์™€ ์ž…๋ ฅ์— ๋Œ€ํ•œ ๊ทธ๋ž˜๋””์–ธํŠธ ๊ณ„์‚ฐ
67
+ for b in range(batch_size):
68
+ for i in range(d_loss_d_output.shape[1]): # output height
69
+ for j in range(d_loss_d_output.shape[2]): # output width
70
+ region = self.last_input[b, i:(i + self.filter_size), j:(j + self.filter_size), :]
71
+ for f in range(self.num_filters):
72
+ # ํ•„ํ„ฐ ๊ทธ๋ž˜๋””์–ธํŠธ ๋ˆ„์ 
73
+ d_loss_d_filters[:, :, :, f] += d_loss_d_output[b, i, j, f] * region
74
+ # ์ž…๋ ฅ ๊ทธ๋ž˜๋””์–ธํŠธ ๋ˆ„์  (Chain Rule)
75
+ d_loss_d_input[b, i:i+self.filter_size, j:j+self.filter_size, :] += d_loss_d_output[b, i, j, f] * self.filters[:, :, :, f]
76
+
77
+ # ํŽธํ–ฅ์˜ ๊ทธ๋ž˜๋””์–ธํŠธ
78
+ d_loss_d_biases = np.sum(d_loss_d_output, axis=(0, 1, 2))
79
+
80
+ # ๊ฐ€์ค‘์น˜ ๋ฐ ํŽธํ–ฅ ์—…๋ฐ์ดํŠธ
81
+ self.filters -= learning_rate * d_loss_d_filters / batch_size
82
+ self.biases -= learning_rate * d_loss_d_biases / batch_size
83
+
84
+ # ์ด์ „ ๊ณ„์ธต์œผ๋กœ ์ „๋‹ฌํ•  ๊ทธ๋ž˜๋””์–ธํŠธ ๋ฐ˜ํ™˜
85
+ return d_loss_d_input
86
+
87
+ class ReLULayer:
88
+ """ReLU ํ™œ์„ฑํ™” ํ•จ์ˆ˜"""
89
+ def __init__(self):
90
+ self.last_input = None
91
+
92
+ def forward(self, input_data):
93
+ self.last_input = input_data
94
+ return np.maximum(0, input_data)
95
+
96
+ def backward(self, d_loss_d_output):
97
+ d_relu = (self.last_input > 0).astype(int)
98
+ return d_loss_d_output * d_relu
99
+
100
+ class MaxPoolingLayer:
101
+ """๋งฅ์Šค ํ’€๋ง ๊ณ„์ธต"""
102
+ def __init__(self, pool_size):
103
+ self.pool_size = pool_size
104
+ self.last_input = None
105
+
106
+ def forward(self, input_image):
107
+ self.last_input = input_image
108
+ batch_size, input_height, input_width, num_filters = input_image.shape
109
+ output_height = input_height // self.pool_size
110
+ output_width = input_width // self.pool_size
111
+
112
+ output = np.zeros((batch_size, output_height, output_width, num_filters))
113
+
114
+ for b in range(batch_size):
115
+ for i in range(output_height):
116
+ for j in range(output_width):
117
+ for f in range(num_filters):
118
+ region = input_image[b, (i*self.pool_size):(i*self.pool_size + self.pool_size),
119
+ (j*self.pool_size):(j*self.pool_size + self.pool_size), f]
120
+ output[b, i, j, f] = np.max(region)
121
+ return output
122
+
123
+ def backward(self, d_loss_d_output):
124
+ d_loss_d_input = np.zeros_like(self.last_input)
125
+
126
+ for b in range(d_loss_d_output.shape[0]):
127
+ for i in range(d_loss_d_output.shape[1]):
128
+ for j in range(d_loss_d_output.shape[2]):
129
+ for f in range(d_loss_d_output.shape[3]):
130
+ region = self.last_input[b, (i*self.pool_size):(i*self.pool_size + self.pool_size),
131
+ (j*self.pool_size):(j*self.pool_size + self.pool_size), f]
132
+ max_val = np.max(region)
133
+ mask = (region == max_val)
134
+ d_loss_d_input[b, (i*self.pool_size):(i*self.pool_size + self.pool_size),
135
+ (j*self.pool_size):(j*self.pool_size + self.pool_size), f] += \
136
+ mask * d_loss_d_output[b, i, j, f]
137
+ return d_loss_d_input
138
+
139
+ class FlattenLayer:
140
+ """ํ‰ํƒ„ํ™” ๊ณ„์ธต"""
141
+ def __init__(self):
142
+ self.last_input_shape = None
143
+
144
+ def forward(self, input_data):
145
+ self.last_input_shape = input_data.shape
146
+ batch_size = input_data.shape[0]
147
+ return input_data.reshape(batch_size, -1)
148
+
149
+ def backward(self, d_loss_d_output):
150
+ return d_loss_d_output.reshape(self.last_input_shape)
151
+
152
+ class DenseLayer:
153
+ """์™„์ „ ์—ฐ๊ฒฐ ๊ณ„์ธต"""
154
+ def __init__(self, input_size, output_size):
155
+ self.weights = np.random.randn(input_size, output_size) * np.sqrt(2. / input_size)
156
+ self.biases = np.zeros(output_size)
157
+ self.last_input = None
158
+ self.last_input_shape = None
159
+
160
+ def forward(self, input_data):
161
+ self.last_input_shape = input_data.shape
162
+ self.last_input = input_data
163
+ return np.dot(input_data, self.weights) + self.biases
164
+
165
+ def backward(self, d_loss_d_output, learning_rate):
166
+ batch_size = self.last_input.shape[0]
167
+ d_loss_d_input = np.dot(d_loss_d_output, self.weights.T)
168
+ d_loss_d_weights = np.dot(self.last_input.T, d_loss_d_output)
169
+ d_loss_d_biases = np.sum(d_loss_d_output, axis=0)
170
+ self.weights -= learning_rate * d_loss_d_weights / batch_size
171
+ self.biases -= learning_rate * d_loss_d_biases / batch_size
172
+ return d_loss_d_input
173
+
174
+ # =================================================================
175
+ # --- 3. Softmax ๋ฐ ์†์‹ค ํ•จ์ˆ˜ ---
176
+ # =================================================================
177
+
178
+ def softmax(logits):
179
+ exps = np.exp(logits - np.max(logits, axis=1, keepdims=True))
180
+ return exps / np.sum(exps, axis=1, keepdims=True)
181
+
182
+ def cross_entropy_loss(y_pred, y_true):
183
+ samples = y_true.shape[0]
184
+ epsilon = 1e-12
185
+ y_pred_clipped = np.clip(y_pred, epsilon, 1. - epsilon)
186
+ loss = -np.sum(y_true * np.log(y_pred_clipped)) / samples
187
+ return loss
188
+
189
+ # =================================================================
190
+ # --- 4. CNN ๋ชจ๋ธ ํด๋ž˜์Šค ---
191
+ # =================================================================
192
+ class SimpleCNN:
193
+ def __init__(self):
194
+ # ์ž…๋ ฅ: 28x28x1
195
+ self.conv1 = ConvLayer(num_filters=8, filter_size=3, input_channels=1) # -> 26x26x8
196
+ self.relu1 = ReLULayer()
197
+ self.pool1 = MaxPoolingLayer(pool_size=2) # -> 13x13x8
198
+
199
+ self.conv2 = ConvLayer(num_filters=16, filter_size=3, input_channels=8)# -> 11x11x16
200
+ self.relu2 = ReLULayer()
201
+ self.pool2 = MaxPoolingLayer(pool_size=2) # -> 5x5x16
202
+
203
+ self.flatten = FlattenLayer() # -> 400
204
+ self.dense = DenseLayer(5 * 5 * 16, 10) # -> 10
205
+
206
+ def forward(self, image):
207
+ out = self.conv1.forward(image)
208
+ out = self.relu1.forward(out)
209
+ out = self.pool1.forward(out)
210
+ out = self.conv2.forward(out)
211
+ out = self.relu2.forward(out)
212
+ out = self.pool2.forward(out)
213
+ out = self.flatten.forward(out)
214
+ out = self.dense.forward(out)
215
+ return out
216
+
217
+ def backward(self, d_loss_d_out, learning_rate):
218
+ """์—ญ์ „ํŒŒ ๋ฉ”์„œ๋“œ (๊ทธ๋ž˜๋””์–ธํŠธ ํ๋ฆ„ ์ˆ˜์ •)"""
219
+ # ์—ญ์ „ํŒŒ๋Š” ์ˆœ์ „ํŒŒ์˜ ์—ญ์ˆœ์œผ๋กœ ์ง„ํ–‰
220
+ gradient = self.dense.backward(d_loss_d_out, learning_rate)
221
+ gradient = self.flatten.backward(gradient)
222
+
223
+ # 2๋‹จ๊ณ„ ์—ญ์ „ํŒŒ
224
+ gradient = self.pool2.backward(gradient)
225
+ gradient = self.relu2.backward(gradient)
226
+ gradient = self.conv2.backward(gradient, learning_rate)
227
+
228
+ # 1๋‹จ๊ณ„ ์—ญ์ „ํŒŒ
229
+ gradient = self.pool1.backward(gradient)
230
+ gradient = self.relu1.backward(gradient)
231
+ self.conv1.backward(gradient, learning_rate)
232
+
233
+ # =================================================================
234
+ # --- 5. ํ•™์Šต ๋ฃจํ”„ ---
235
+ # =================================================================
236
+
237
+ if __name__ == '__main__':
238
+ x_train, y_train, x_test, y_test = load_mnist_data()
239
+ x_train_small, y_train_small = x_train[:1000], y_train[:1000]
240
+ x_test_small, y_test_small = x_test[:500], y_test[:500]
241
+
242
+ model = SimpleCNN()
243
+
244
+ learning_rate = 0.01
245
+ epochs = 5
246
+ batch_size = 32
247
+
248
+ print("ํ•™์Šต ์‹œ์ž‘...")
249
+ for epoch in range(epochs):
250
+ epoch_loss = 0
251
+
252
+ for i in range(0, x_train_small.shape[0], batch_size):
253
+ x_batch = x_train_small[i:i+batch_size]
254
+ y_batch = y_train_small[i:i+batch_size]
255
+
256
+ logits = model.forward(x_batch)
257
+ predictions = softmax(logits)
258
+
259
+ loss = cross_entropy_loss(predictions, y_batch)
260
+ epoch_loss += loss
261
+
262
+ d_loss_d_out = (predictions - y_batch)
263
+ model.backward(d_loss_d_out, learning_rate)
264
+
265
+ avg_loss = epoch_loss / (len(x_train_small) / batch_size)
266
+ print(f"Epoch {epoch + 1}/{epochs}, Loss: {avg_loss:.4f}")
267
+
268
+ print("\nํ…Œ์ŠคํŠธ ์‹œ์ž‘...")
269
+ test_logits = model.forward(x_test_small)
270
+ test_predictions = softmax(test_logits)
271
+
272
+ predicted_labels = np.argmax(test_predictions, axis=1)
273
+ true_labels = np.argmax(y_test_small, axis=1)
274
+ accuracy = np.mean(predicted_labels == true_labels)
275
+
276
+ print(f"ํ…Œ์ŠคํŠธ ์ •ํ™•๋„: {accuracy * 100:.2f}%")