# 基于NumPy手写神经网络

Keras、TensorFlow、PyTorch等高层框架让我们可以快速搭建复杂模型。然而，花一点时间了解下底层概念是值得的。前不久我发过一篇文章，以简单的方式解释了神经网络是如何工作的。但这是一篇高度理论性的文章，主要以数学为主（数学是神经网络超能力的来源）。我打算以实践的方式继续这一主题，在这篇文章中，我们将尝试仅仅基于NumPy创建一个可以工作的神经网络。最后，我们将测试一下创建的模型——用它来解决简单的分类问题，并和基于Keras搭建的神经网络比较一下。

## 初始化神经网络层

```nn_architecture = [
{"input_dim": 2, "output_dim": 4, "activation": "relu"},
{"input_dim": 4, "output_dim": 6, "activation": "relu"},
{"input_dim": 6, "output_dim": 6, "activation": "relu"},
{"input_dim": 6, "output_dim": 4, "activation": "relu"},
{"input_dim": 4, "output_dim": 1, "activation": "sigmoid"},
]```

```def init_layers(nn_architecture, seed = 99):
np.random.seed(seed)
number_of_layers = len(nn_architecture)
params_values = {}

for idx, layer in enumerate(nn_architecture):
layer_idx = idx + 1
layer_input_size = layer["input_dim"]
layer_output_size = layer["output_dim"]

params_values['W' + str(layer_idx)] = np.random.randn(
layer_output_size, layer_input_size) * 0.1
params_values['b' + str(layer_idx)] = np.random.randn(
layer_output_size, 1) * 0.1

return params_values```

## 激活函数

```def sigmoid(Z):
return 1/(1+np.exp(-Z))

def relu(Z):
return np.maximum(0,Z)

def sigmoid_backward(dA, Z):
sig = sigmoid(Z)
return dA * sig * (1 - sig)

def relu_backward(dA, Z):
dZ = np.array(dA, copy = True)
dZ[Z <= 0] = 0;
return dZ```

## 前向传播

```def single_layer_forward_propagation(A_prev, W_curr, b_curr, activation="relu"):
Z_curr = np.dot(W_curr, A_prev) + b_curr

if activation is "relu":
activation_func = relu
elif activation is "sigmoid":
activation_func = sigmoid
else:
raise Exception('Non-supported activation function')

return activation_func(Z_curr), Z_curr```

```def full_forward_propagation(X, params_values, nn_architecture):
memory = {}
A_curr = X

for idx, layer in enumerate(nn_architecture):
layer_idx = idx + 1
A_prev = A_curr

activ_function_curr = layer["activation"]
W_curr = params_values["W" + str(layer_idx)]
b_curr = params_values["b" + str(layer_idx)]
A_curr, Z_curr = single_layer_forward_propagation(A_prev, W_curr, b_curr, activ_function_curr)

memory["A" + str(idx)] = A_prev
memory["Z" + str(layer_idx)] = Z_curr

return A_curr, memory```

## 损失函数

```def get_cost_value(Y_hat, Y):
m = Y_hat.shape[1]
cost = -1 / m * (np.dot(Y, np.log(Y_hat).T) + np.dot(1 - Y, np.log(1 - Y_hat).T))
return np.squeeze(cost)

def get_accuracy_value(Y_hat, Y):
Y_hat_ = convert_prob_into_class(Y_hat)
return (Y_hat_ == Y).all(axis=0).mean()```

## 反向传播

```def single_layer_backward_propagation(dA_curr, W_curr, b_curr, Z_curr, A_prev, activation="relu"):
m = A_prev.shape[1]

if activation is "relu":
backward_activation_func = relu_backward
elif activation is "sigmoid":
backward_activation_func = sigmoid_backward
else:
raise Exception('Non-supported activation function')

dZ_curr = backward_activation_func(dA_curr, Z_curr)
dW_curr = np.dot(dZ_curr, A_prev.T) / m
db_curr = np.sum(dZ_curr, axis=1, keepdims=True) / m
dA_prev = np.dot(W_curr.T, dZ_curr)

return dA_prev, dW_curr, db_curr```

```def full_backward_propagation(Y_hat, Y, memory, params_values, nn_architecture):
m = Y.shape[1]
Y = Y.reshape(Y_hat.shape)

dA_prev = - (np.divide(Y, Y_hat) - np.divide(1 - Y, 1 - Y_hat));

for layer_idx_prev, layer in reversed(list(enumerate(nn_architecture))):
layer_idx_curr = layer_idx_prev + 1
activ_function_curr = layer["activation"]

dA_curr = dA_prev

A_prev = memory["A" + str(layer_idx_prev)]
Z_curr = memory["Z" + str(layer_idx_curr)]
W_curr = params_values["W" + str(layer_idx_curr)]
b_curr = params_values["b" + str(layer_idx_curr)]

dA_prev, dW_curr, db_curr = single_layer_backward_propagation(
dA_curr, W_curr, b_curr, Z_curr, A_prev, activ_function_curr)

## 更新参数值

```def update(params_values, grads_values, nn_architecture, learning_rate):
for idx, layer in enumerate(nn_architecture):
layer_idx = idx + 1
params_values["W" + str(layer_idx)] -= learning_rate * grads_values["dW" + str(layer_idx)]
params_values["b" + str(layer_idx)] -= learning_rate * grads_values["db" + str(layer_idx)]

return params_values```

## 整合一切

```def train(X, Y, nn_architecture, epochs, learning_rate):
params_values = init_layers(nn_architecture, 2)
cost_history = []
accuracy_history = []

for i in range(epochs):
Y_hat, cashe = full_forward_propagation(X, params_values, nn_architecture)
cost = get_cost_value(Y_hat, Y)
cost_history.append(cost)
accuracy = get_accuracy_value(Y_hat, Y)
accuracy_history.append(accuracy)

grads_values = full_backward_propagation(Y_hat, Y, cashe, params_values, nn_architecture)
params_values = update(params_values, grads_values, nn_architecture, learning_rate)

return params_values, cost_history, accuracy_history```

## 再见

IELTS a bit

hazard n. 危险；有危险的事物；风险

incline v. 倾向于

junction n. 公路或铁路的交叉点；交叉路口

0 条评论

• ### 【NLP】自然语言处理中词性、短语、短语关系标签的具体含义列表

ROOT：要处理文本的语句 IP：简单从句 NP：名词短语 VP：动词短语 PU：断句符，通常是句号、问号、感叹号等标点符号 LCP：方位词短语 PP：介词短语...

• ### Python学习——collections系列

一 ,计数器（counter) Counter是对字典类型的补充，用于追踪值得出现次数 ps:具备字典的所有功能 + 自己的功能 例： >>> from ...

• ### 【ML系列】手把手教你用Numpy构建神经网络！（附代码）

比如使用Keras，TensorFlow或PyTorch这样的高级框架，我们可以快速构建非常复杂的模型。但是，需要花时间去了解其内部结构并理解基本原理。今天，将...

• ### 用wxPython打造Python图形界面（上）

有许多图形用户界面(GUI)工具包可以与Python编程语言一起使用。其中三巨头是Tkinter、wxPython和PyQt。这些工具包中的每一个都将与Wind...

• ### PCL常见错误集锦

我刚刚开始接触PCL，懂的东西也很少，所以总是出现各种各样的问题，每次遇见问题的时候要查找各种各样的资料，很费时间。所以，今天我把我遇见的常见问题分享给大家，讲...

近日，记者从国家知识产权局了解到，腾讯公司正式向国家知识产权局提交了一份关于图片编码技术的专利申请。此项专利被命名为TPG（Tiny Portable Grap...

腾讯公司正式向国家知识产权局提交了一份关于图片编码技术的专利申请，此项专利被命名为TPG（Tiny Portable Graphics），在数据上TPG图片格式...

近日，记者从国家知识产权局了解到，腾讯公司正式向国家知识产权局提交了一份关于图片编码技术的专利申请。此项专利被命名为TPG（Tiny Portable Grap...

• ### 混合云存储：大数据应用的上云之道

? 企业数字化转型过程中，数据价值被显著放大，大数据应用成为不少企业探索的重点。 从技术上看，大数据业务由于数据体量大，且数据量很多时候呈急速膨胀状态；在进...