1"""
202. ์ ๊ฒฝ๋ง ๊ธฐ์ด - NumPy ๋ฒ์ (from scratch)
3
4NumPy๋ง์ผ๋ก MLP ์์ ํ๋ฅผ ๊ตฌํํฉ๋๋ค.
5PyTorch ๋ฒ์ (examples/pytorch/02_neural_network.py)๊ณผ ๋น๊ตํด ๋ณด์ธ์.
6
7ํต์ฌ: ์ญ์ ํ ์์ด ์์ ํ๋ง ๊ตฌํํฉ๋๋ค.
8 ์ญ์ ํ๋ 03_backprop_scratch.py์์ ๊ตฌํํฉ๋๋ค.
9"""
10
11import numpy as np
12import matplotlib.pyplot as plt
13
14print("=" * 60)
15print("NumPy ์ ๊ฒฝ๋ง ๊ธฐ์ด (from scratch)")
16print("=" * 60)
17
18
19# ============================================
20# 1. ํ์ฑํ ํจ์ ๊ตฌํ
21# ============================================
22print("\n[1] ํ์ฑํ ํจ์ ๊ตฌํ")
23print("-" * 40)
24
25def sigmoid(x):
26 """์๊ทธ๋ชจ์ด๋: ฯ(x) = 1 / (1 + e^(-x))"""
27 return 1 / (1 + np.exp(-np.clip(x, -500, 500)))
28
29def sigmoid_derivative(x):
30 """์๊ทธ๋ชจ์ด๋ ๋ฏธ๋ถ: ฯ'(x) = ฯ(x)(1 - ฯ(x))"""
31 s = sigmoid(x)
32 return s * (1 - s)
33
34def relu(x):
35 """ReLU: max(0, x)"""
36 return np.maximum(0, x)
37
38def relu_derivative(x):
39 """ReLU ๋ฏธ๋ถ: 1 if x > 0 else 0"""
40 return (x > 0).astype(float)
41
42def tanh(x):
43 """Tanh: (e^x - e^(-x)) / (e^x + e^(-x))"""
44 return np.tanh(x)
45
46def tanh_derivative(x):
47 """Tanh ๋ฏธ๋ถ: 1 - tanhยฒ(x)"""
48 return 1 - np.tanh(x)**2
49
50def softmax(x):
51 """Softmax: e^xi / ฮฃe^xj"""
52 exp_x = np.exp(x - np.max(x, axis=-1, keepdims=True))
53 return exp_x / np.sum(exp_x, axis=-1, keepdims=True)
54
55# ํ
์คํธ
56x_test = np.array([-2, -1, 0, 1, 2])
57print(f"์
๋ ฅ: {x_test}")
58print(f"sigmoid: {sigmoid(x_test)}")
59print(f"relu: {relu(x_test)}")
60print(f"tanh: {tanh(x_test)}")
61
62# ์๊ฐํ
63x = np.linspace(-5, 5, 100)
64
65fig, axes = plt.subplots(2, 2, figsize=(12, 8))
66
67axes[0, 0].plot(x, sigmoid(x), label='Sigmoid')
68axes[0, 0].plot(x, sigmoid_derivative(x), '--', label='Derivative')
69axes[0, 0].set_title('Sigmoid and Derivative')
70axes[0, 0].legend()
71axes[0, 0].grid(True, alpha=0.3)
72
73axes[0, 1].plot(x, tanh(x), label='Tanh')
74axes[0, 1].plot(x, tanh_derivative(x), '--', label='Derivative')
75axes[0, 1].set_title('Tanh and Derivative')
76axes[0, 1].legend()
77axes[0, 1].grid(True, alpha=0.3)
78
79axes[1, 0].plot(x, relu(x), label='ReLU')
80axes[1, 0].plot(x, relu_derivative(x), '--', label='Derivative')
81axes[1, 0].set_title('ReLU and Derivative')
82axes[1, 0].legend()
83axes[1, 0].grid(True, alpha=0.3)
84
85x_softmax = np.array([1, 2, 3, 4])
86axes[1, 1].bar(range(4), softmax(x_softmax))
87axes[1, 1].set_title(f'Softmax of {x_softmax}')
88axes[1, 1].set_ylabel('Probability')
89axes[1, 1].grid(True, alpha=0.3)
90
91plt.tight_layout()
92plt.savefig('numpy_activation_functions.png', dpi=100)
93plt.close()
94print("ํ์ฑํ ํจ์ ๊ทธ๋ํ ์ ์ฅ: numpy_activation_functions.png")
95
96
97# ============================================
98# 2. ํผ์
ํธ๋ก (๋จ์ผ ๋ด๋ฐ)
99# ============================================
100print("\n[2] ํผ์
ํธ๋ก ๊ตฌํ")
101print("-" * 40)
102
103class Perceptron:
104 """๋จ์ผ ํผ์
ํธ๋ก """
105
106 def __init__(self, n_inputs):
107 # ๊ฐ์ค์น ์ด๊ธฐํ (์์ ๋๋ค ๊ฐ)
108 self.weights = np.random.randn(n_inputs) * 0.1
109 self.bias = 0.0
110
111 def forward(self, x):
112 """์์ ํ: z = wx + b, y = activation(z)"""
113 z = np.dot(x, self.weights) + self.bias
114 return sigmoid(z)
115
116# ํ
์คํธ
117perceptron = Perceptron(n_inputs=3)
118x_input = np.array([1.0, 2.0, 3.0])
119output = perceptron.forward(x_input)
120
121print(f"์
๋ ฅ: {x_input}")
122print(f"๊ฐ์ค์น: {perceptron.weights}")
123print(f"ํธํฅ: {perceptron.bias}")
124print(f"์ถ๋ ฅ: {output:.4f}")
125
126
127# ============================================
128# 3. ๋ค์ธต ํผ์
ํธ๋ก (MLP) ์์ ํ
129# ============================================
130print("\n[3] MLP ์์ ํ ๊ตฌํ")
131print("-" * 40)
132
133class MLPNumpy:
134 """
135 NumPy๋ก ๊ตฌํํ ๋ค์ธต ํผ์
ํธ๋ก
136 ์์ ํ๋ง ๊ตฌํ (์ญ์ ํ๋ 03์์)
137 """
138
139 def __init__(self, layer_sizes):
140 """
141 layer_sizes: [์
๋ ฅ ์ฐจ์, ์๋์ธต1, ์๋์ธต2, ..., ์ถ๋ ฅ ์ฐจ์]
142 ์: [784, 256, 128, 10] โ ์
๋ ฅ 784, ์๋ 256/128, ์ถ๋ ฅ 10
143 """
144 self.num_layers = len(layer_sizes) - 1
145 self.weights = []
146 self.biases = []
147
148 # Xavier ์ด๊ธฐํ
149 for i in range(self.num_layers):
150 fan_in = layer_sizes[i]
151 fan_out = layer_sizes[i + 1]
152 # Xavier ์ด๊ธฐํ: std = sqrt(2 / (fan_in + fan_out))
153 std = np.sqrt(2.0 / (fan_in + fan_out))
154 W = np.random.randn(fan_in, fan_out) * std
155 b = np.zeros(fan_out)
156 self.weights.append(W)
157 self.biases.append(b)
158
159 print(f"MLP ์์ฑ: {layer_sizes}")
160 for i, (W, b) in enumerate(zip(self.weights, self.biases)):
161 print(f" Layer {i+1}: W{W.shape}, b{b.shape}")
162
163 def forward(self, x):
164 """์์ ํ"""
165 activations = [x]
166
167 for i in range(self.num_layers):
168 z = activations[-1] @ self.weights[i] + self.biases[i]
169
170 # ๋ง์ง๋ง ์ธต์ ํ์ฑํ ์์ (๋๋ softmax)
171 if i < self.num_layers - 1:
172 a = relu(z)
173 else:
174 a = z # ์ถ๋ ฅ์ธต
175
176 activations.append(a)
177
178 return activations[-1], activations
179
180 def predict_proba(self, x):
181 """๋ถ๋ฅ ํ๋ฅ (softmax)"""
182 output, _ = self.forward(x)
183 return softmax(output)
184
185 def predict(self, x):
186 """๋ถ๋ฅ ์์ธก"""
187 proba = self.predict_proba(x)
188 return np.argmax(proba, axis=-1)
189
190# MLP ํ
์คํธ
191mlp = MLPNumpy([10, 32, 16, 3])
192
193# ๋ฐฐ์น ์
๋ ฅ (4๊ฐ ์ํ, 10์ฐจ์)
194x_batch = np.random.randn(4, 10)
195output, activations = mlp.forward(x_batch)
196
197print(f"\n์
๋ ฅ shape: {x_batch.shape}")
198print(f"์ถ๋ ฅ shape: {output.shape}")
199print(f"์ถ๋ ฅ ์์:\n{output}")
200
201# ํ๋ฅ ๊ณผ ์์ธก
202proba = mlp.predict_proba(x_batch)
203pred = mlp.predict(x_batch)
204print(f"\nSoftmax ํ๋ฅ :\n{proba}")
205print(f"์์ธก ํด๋์ค: {pred}")
206
207
208# ============================================
209# 4. XOR ๋ฌธ์ - ์์ ํ๋ง
210# ============================================
211print("\n[4] XOR ๋ฌธ์ (์์ ํ๋ง)")
212print("-" * 40)
213
214# XOR ๋ฐ์ดํฐ
215X_xor = np.array([[0, 0], [0, 1], [1, 0], [1, 1]])
216y_xor = np.array([0, 1, 1, 0])
217
218# ์๋์ผ๋ก ๊ฐ์ค์น ์ค์ (ํ์ต ์์ด)
219# XOR์ ํด๊ฒฐํ๋ ์๋ ์ค์ ๊ฐ์ค์น
220class XORNetManual:
221 def __init__(self):
222 # ์๋์ธต: 2๊ฐ ๋ด๋ฐ
223 # ์ฒซ ๋ฒ์งธ ๋ด๋ฐ: AND์ฒ๋ผ ๋์ (๋ ๋ค 1์ผ ๋)
224 # ๋ ๋ฒ์งธ ๋ด๋ฐ: OR์ฒ๋ผ ๋์ (ํ๋๋ผ๋ 1์ผ ๋)
225 self.W1 = np.array([[ 20, 20], # x1์ ๋ํ ๊ฐ์ค์น
226 [ 20, 20]]) # x2์ ๋ํ ๊ฐ์ค์น
227 self.b1 = np.array([-30, -10]) # AND: -30, OR: -10
228
229 # ์ถ๋ ฅ์ธต: OR - AND = XOR
230 self.W2 = np.array([[-20], # AND ๋ด๋ฐ์ ์์
231 [ 20]]) # OR ๋ด๋ฐ์ ์์
232 self.b2 = np.array([-10])
233
234 def forward(self, x):
235 z1 = x @ self.W1 + self.b1
236 a1 = sigmoid(z1)
237
238 z2 = a1 @ self.W2 + self.b2
239 a2 = sigmoid(z2)
240
241 return a2
242
243xor_manual = XORNetManual()
244
245print("์๋ ์ค์ ๊ฐ์ค์น๋ก XOR ํด๊ฒฐ:")
246for i in range(4):
247 x = X_xor[i:i+1]
248 y_pred = xor_manual.forward(x)
249 print(f" {X_xor[i]} โ {y_pred[0, 0]:.4f} (์ ๋ต: {y_xor[i]})")
250
251
252# ============================================
253# 5. ์์ ํ ๊ณผ์ ์๊ฐํ
254# ============================================
255print("\n[5] ์์ ํ ๊ณผ์ ์๊ฐํ")
256print("-" * 40)
257
258def visualize_forward_pass(x, model):
259 """์์ ํ ๊ณผ์ ์ ๊ฐ ๋ณํ ์ถ๋ ฅ"""
260 print(f"์
๋ ฅ: {x}")
261
262 a = x
263 for i in range(model.num_layers):
264 z = a @ model.weights[i] + model.biases[i]
265 print(f"\nLayer {i+1}:")
266 print(f" z (์ ํ ๋ณํ): {z[:5]}...") # ์ฒ์ 5๊ฐ๋ง
267
268 if i < model.num_layers - 1:
269 a = relu(z)
270 print(f" a (ReLU ํ): {a[:5]}...")
271 else:
272 a = z
273 print(f" ์ถ๋ ฅ: {a}")
274
275 return a
276
277# ๋จ์ผ ์ํ๋ก ํ
์คํธ
278small_mlp = MLPNumpy([4, 8, 3])
279x_single = np.array([1.0, 2.0, 3.0, 4.0])
280output = visualize_forward_pass(x_single, small_mlp)
281
282
283# ============================================
284# 6. NumPy vs PyTorch ๋น๊ต
285# ============================================
286print("\n" + "=" * 60)
287print("NumPy vs PyTorch ๋น๊ต")
288print("=" * 60)
289
290comparison = """
291| ํญ๋ชฉ | NumPy (์ด ์ฝ๋) | PyTorch |
292|---------------|---------------------------|----------------------------|
293| ์์ ํ | x @ W + b ์ง์ ๊ณ์ฐ | model(x) ์๋ ๊ณ์ฐ |
294| ํ์ฑํ ํจ์ | np.maximum(0, x) | F.relu(x) |
295| ๊ฐ์ค์น ๊ด๋ฆฌ | ๋ฆฌ์คํธ๋ก ์ง์ ๊ด๋ฆฌ | model.parameters() |
296| ์ญ์ ํ | โ (๋ค์ ๋ ์จ์์ ๊ตฌํ) | loss.backward() ์๋ |
297| ๋ฐฐ์น ์ฒ๋ฆฌ | ํ๋ ฌ ๊ณฑ์
์ผ๋ก ์ง์ | DataLoader ์๋ |
298
299NumPy ๊ตฌํ์ ์ฅ์ :
3001. ์์ ํ์ ์ํ์ ์๋ฆฌ ์์ ์ดํด
3012. ํ๋ ฌ ์ฐ์ฐ์ ์๋ฏธ ํ์
3023. ํ์ฑํ ํจ์์ ๋์ ์ดํด
303
304๋ค์ ๋จ๊ณ (03_backprop_scratch.py):
305- ์ญ์ ํ ์๊ณ ๋ฆฌ์ฆ NumPy ๊ตฌํ
306- ๊ฒฝ์ฌ ํ๊ฐ๋ฒ์ผ๋ก ๊ฐ์ค์น ์
๋ฐ์ดํธ
307- XOR ๋ฌธ์ ํ์ต์ผ๋ก ํด๊ฒฐ
308"""
309print(comparison)
310
311print("NumPy ์ ๊ฒฝ๋ง ๊ธฐ์ด (์์ ํ) ์๋ฃ!")
312print("PyTorch ๋ฒ์ ๊ณผ ๋น๊ต: examples/pytorch/02_neural_network.py")
313print("=" * 60)