DNN(Deep Neural Nework):深层神经网络Python实践
完整代码:可参见我的github项目:https://github.com/RaySunWHUT/NeuralNetwork/blob/master/NerualNetwork/neural_network/week4/L_NN.py
欢迎star、fork。
此处,将结合吴恩达老师在Coursera上的Deep Learning and Neural Nework
课程以及MIT的IntroduceToDeeplearning的课程讲义,讲述基本DNN的实现过程。(拖了这么久,没时间,可奈何~ ~😜)
好,废话不多说,Let’s go:
在这篇Blog中,我们将利用Python Numpy设计实现一个针对图片分类(Image Classification)问题的深层神经网络(Deep Neural Network);
DNN实现的主要步骤如下:
以下将从这7个部分分别阐述:
-
加载数据集
catvnoncat.h5是一个包含各个种类的猫和其他事务的数据集。
明确任务,搭建的图片分类神经网络是要正确的将猫与其他图片分类出来。
首先查看数据维度,并对数据进行可视化。
数据维度:
可视化数据:
![]() Cat |
![]() Other |
加载数据(load_dataset):
# 加载数据集
def load_dataset():
train_dataset = h5py.File('../../datasets/train_catvnoncat.h5', "r")
# print(train_dataset.keys())
# 可以通过train_dataset.keys()查看键值的集合;
# [:]: 表示除当前维度以外的所有
train_set_x_orig = np.array(train_dataset["train_set_x"][:])
# print("train_set元素个数: " + str(len(train_set_x_orig)))
# print("train_set图片尺寸: " + str(train_set_x_orig[89].shape))
# 训练集标签
train_set_y_orig = np.array(train_dataset["train_set_y"][:])
test_dataset = h5py.File('../../datasets/test_catvnoncat.h5', "r")
# 测试集特征
test_set_x_orig = np.array(test_dataset["test_set_x"][:])
# print("test_set元素个数: " + str(len(test_set_x_orig)))
# print("test_set图片尺寸: " + str(test_set_x_orig[2].shape))
# 测试集标签
test_set_y_orig = np.array(test_dataset["test_set_y"][:])
# 类别
classes = np.array(test_dataset["list_classes"][:])
# 完善数据维度:由(209, ) ---> (1, 209)
train_set_y_orig = train_set_y_orig.reshape((1, train_set_y_orig.shape[0]))
test_set_y_orig = test_set_y_orig.reshape((1, test_set_y_orig.shape[0]))
return train_set_x_orig, train_set_y_orig, test_set_x_orig, test_set_y_orig, classes
# 预处理数据函数
def pre_process_data():
# 加载数据
train_set_x_orig, train_set_y, test_set_x_orig, test_set_y, classes = load_dataset()
# 数据扁平化
train_set_x_flatten = train_set_x_orig.reshape(train_set_x_orig.shape[0], -1).T
# test_set_x_flatten = test_set_x_orig.reshape(shapes[0], shapes[1] * shapes[2] * shapes[3]).T
# 与下面的形式等价, 但更贴近于理解, 即:examples_num * Vector; 转置后: 易于输入神经网络中
test_set_x_flatten = test_set_x_orig.reshape(test_set_x_orig.shape[0], -1).T
# 标准化
train_set_x = train_set_x_flatten / 255.0
test_set_x = test_set_x_flatten / 255.0
return train_set_x, train_set_y, test_set_x, test_set_y
设计神经网络架构:
神经网络结构大致如下:X
→
\rightarrow
→ [Linear
→
\rightarrow
→ ReLU] * (L - 1)
→
\rightarrow
→ Linear
→
\rightarrow
→ Sigmoid;
此处,设置layers_dims = [12288, 20, 7, 5, 1];
3. 随机初始化
# 随机初始化
def initialize_parameters_deep(layer_dims):
"""
layer_dims -- 网络维度
"""
# seed值固定, 则生成的随机数固定
np.random.seed(1)
parameters = {}
# 网络层数
layers = len(layer_dims)
for l in range(1, layers):
parameters['W' + str(l)] = np.random.randn(layer_dims[l], layer_dims[l - 1]) / np.sqrt(layer_dims[l - 1])
# 初始化为0的原因: 方便算法自我调节截距的移动方向
parameters['b' + str(l)] = np.zeros((layer_dims[l], 1))
return parameters
4. 前向传播
下图为感知机(Perceptron)的原理:
此处,需要说下 偏置单元(bias units) 的作用 / 实质:其实质是函数关于y轴的截距,若没有偏置单元,则函数将会始终经过原点(0, 0),将无法很好的拟合数据。
bias 在通常初始化为0,因为初始状态处于 原点(0, 0) 的bias可以更容易的结合数据,进行调节(上移/下移)。
以下为神经网络 前向传播(forward propagation) 的基本计算方法。
下图为,DNN的基础架构:
前向传播(forward propagation) 核心逻辑代码:
def L_model_forward(x, parameters):
"""
forward propagation for the [LINEAR -> RELU] * (layers - 1) -> LINEAR -> SIGMOID computation
X -- data (input size, number of examples)
parameters -- output of initialize_parameters_deep()
AL -- last post-activation value
caches -- : every cache of linear_relu_forward() (there are layers - 1 of them, indexed from 0 to layers - 2)
the cache of linear_sigmoid_forward() (there is one, indexed layers - 1)
"""
caches = []
a = x
# 网络层数: 由于有b和w, 所以, // 2
layers = len(parameters) // 2
# [LINEAR -> RELU] * (layers - 1)
for l in range(1, layers):
a_prev = a
a, cache = linear_activation_forward(a_prev, parameters['W' + str(l)], parameters['b' + str(l)],
activation="relu")
caches.append(cache)
# LINEAR -> SIGMOID
AL, cache = linear_activation_forward(a, parameters['W' + str(layers)], parameters['b' + str(layers)],
activation="sigmoid")
caches.append(cache)
return AL, caches
5. 计算代价损失(梯度)
首先,需要明确代价损失函数(loss function) 与 优化算法(optimization algorithm) 的概念。
代价损失函数(loss function): 代价损失函数是指用于计算标签值y和预测值 y ^ \hat{y} y^ 之间差异的函数。
例,下图为二元交叉熵损失函数(binary cross entropy loss); 目前,有很多种代价损失函数,具体使用哪种,需要结合具体问题和数据集的特点来决定。
优化算法(Optimization Algorithm):确定了代价损失函数后,接下来的工作就是求最优化的参数W,让代价损失函数达到最优值。即,
下图为代价损失函数(loss function)的二维空间图像:
可以采用诸如梯度下降(Gradient Descent)等优化算法寻找可以收敛到的loss最低点。
常见的 优化算法(Optimization Algorithm) 有很多,如:
下图为 梯度下降(Gradient Descent) 的一般执行过程:
def compute_cost(AL, y):
# 标签y数量
m = y.shape[1]
# 计算AL, y
cost = (1.0 / m) * (-np.dot(y, np.log(AL).T) - np.dot(1 - y, np.log(1 - AL).T))
# 将矩阵转化为数字
# (e.g. turns [[17]] into 17)
cost = np.squeeze(cost)
return cost
6. 反向传播
反向传播,即是应用链式法则(chain rule) 计算梯度(Gradient)。
下图为SGD算法的执行过程,step4进行反向传播,计算梯度。
反向传播核心逻辑:
def L_model_backward(AL, y, caches):
grads = {}
# 网络层数: 由于有b和w, 所以, // 2
layers = len(caches)
# 重塑y
y = y.reshape(AL.shape)
# 初始化反向传播
# 对应位置元素相除
dAL = - (np.divide(y, AL) - np.divide(1 - y, 1 - AL))
# Lth layer (SIGMOID -> LINEAR) gradients
# "AL, Y, caches"
current_cache = caches[layers - 1]
# "grads["dAL"], grads["dWL"], grads["dbL"]
grads["dA" + str(layers - 1)], grads["dW" + str(layers)], \
grads["db" + str(layers)] = linear_activation_backward(dAL, current_cache, activation="sigmoid")
for l in reversed(range(layers - 1)):
# lth layer: (RELU -> LINEAR) gradients
current_cache = caches[l]
dA_prev_temp, dW_temp, db_temp = linear_activation_backward(grads["dA" + str(l + 1)], current_cache,
activation="relu")
grads["dA" + str(l)] = dA_prev_temp
grads["dW" + str(l + 1)] = dW_temp
grads["db" + str(l + 1)] = db_temp
return grads
更新参数
def update_parameters(parameters, grads, learning_rate):
# 网络层数: 由于有b和w, 所以, // 2
layers = len(parameters) // 2
# 更新参数w, b
for l in range(layers):
parameters["W" + str(l + 1)] = parameters["W" + str(l + 1)] - learning_rate * grads["dW" + str(l + 1)]
parameters["b" + str(l + 1)] = parameters["b" + str(l + 1)] - learning_rate * grads["db" + str(l + 1)]
return parameters
集成网络结构
# L_layer_model
def L_layer_model(X, Y, layers_dims, learning_rate=0.0075, num_iterations=3000, print_cost=False): # lr was 0.009
np.random.seed(1)
costs = [] # keep track of cost
# 随机初始化
parameters = initialize_parameters_deep(layers_dims)
# 梯度下降
for i in range(0, num_iterations):
# 前向传播: [LINEAR -> RELU] * (L - 1) -> LINEAR -> SIGMOID
AL, caches = L_model_forward(X, parameters)
# 计算代价
cost = compute_cost(AL, Y)
# 反向传播
grads = L_model_backward(AL, Y, caches)
# 更新参数
parameters = update_parameters(parameters, grads, learning_rate)
# print
if print_cost and i % 100 == 0:
print("Cost after iteration %i: %f" % (i, cost))
if print_cost and i % 100 == 0:
costs.append(cost)
# 绘制学习曲线
plot.plot(np.squeeze(costs))
plot.ylabel('cost')
plot.xlabel('iterations (per hundreds)')
plot.title("Learning rate =" + str(learning_rate))
plot.show()
return parameters
7. 预测
def predict(x, y, parameters):
# X的数量
m = x.shape[1]
# 数据集X的对应的prediction的维度
p = np.zeros((1, m))
# 前向传播
probas, caches = L_model_forward(x, parameters)
# 将0 ~ 1 -- 映射 --> 0, 1
for i in range(0, probas.shape[1]):
if probas[0, i] > 0.5:
p[0, i] = 1
else:
p[0, i] = 0
print("Accuracy: " + str(np.sum((p == y) / m)))
Appendix
DNN的训练技巧(Training skills):
- 设置min-batches。
- 应对 过拟合(overfitting) 问题: 采用 正则化(regularization) 技术。
-
Dropout:在训练过程中,将一半的激活函数随机设置为0,使神经网络不依赖于任何一个单独的结点(Node)。
-
Early Stopping: 在模型对数据产生过拟合之前停止训练。
-