一.反向传播(BP算法)
虽然名字听起来高大上,但是意思其实就是从后往前,通过链式求导来求得梯度,之后再用梯度下降等方法来进行参数更新而已。
如图所示,就是一层函数套一层函数就行,那这个式子再带入损失函数去参数
w
w
w和
b
b
b求导,也都不是难事了。具体的推导老师讲的很详细,这里就不细讲了。
二.简单全连接神经网络的构建
笔者以异或问题为例,建立一个如下的二层的神经网络。代码格式和风格参考了其他的课程。
首先我们可以整理一下训练神经网络的步骤:首先定义好神经网络的结构,确定好损失函数以及优化方式;之后的流程图如下图所示。因此我们利用模块化的思想来构建代码。
#以异或问题举例,两层神经网络
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.colors import ListedColormap
#带标签的数据
x_data = np.array([[1,1],[1,0],[0,1],[0,0]])
y_data = np.array([[1],[0],[0],[1]])
plt.scatter(x_data[:,1],x_data[:,0],c=y_data)
plt.show()
#使用k+1个神经元,其中第一层k个,第二层1个,用于构造全部参数
def initial(k):
W1=np.random.randn(2,k)
b1=np.zeros((1,k))
W2=np.random.randn(k,1)
b2=np.zeros((1,1))
parameters = {"W1": W1,"b1": b1,"W2": W2,"b2": b2}
return parameters
#sigmoid函数
def sigmoid(x):
return 1/(1+np.exp(-x))
#使用交叉熵损失函数
def loss(Y_pre,Y):
return -np.sum(Y*np.log(Y_pre)+(1-Y)*np.log(1-Y_pre)) #numpy自带广播机制
#前向传播计算预测值
def forward_propagation(parameters,X):
#每两行是一层,激活函数全使用sigmoid函数
Z1 = np.dot(X,parameters["W1"])+parameters["b1"]
A1 = sigmoid(Z1)
Z2 = np.dot(A1,parameters["W2"])+parameters["b2"]
A2 = sigmoid(Z2) #其实A2就是预测值
cache = {"Z1": Z1, "A1": A1, "Z2": Z2, "A2": A2}
return cache
#用于计算梯度值
def backward_propagation(parameters,X,Y,cache,loss):
#先判断输入的个数(注意不是维数)
m = X.shape[0]
#先计算最后一层,对W和b使用单层的公式,推导略,参数列表与前向传播对应
dZ2 = cache["A2"]-Y #链导,损失值先对y求导,再对sigmiod函数求导
dW2 = 1/m * np.dot(cache["A1"].T,dZ2)
db2 = 1/m * np.sum(dZ2)
#再向前计算一层。同样对W和b使用单层公式
dZ1 = np.dot(dZ2,parameters["W2"].T) * (1-np.power(cache["A1"],2)) #从后面一层的Z中向前链导,经过sigmiod函数
dW1 = 1/m * np.dot(X.T,dZ1)
db1 = 1 / m * np.sum(dZ1, axis=0)
grads = {"dW1": dW1,"db1": db1,"dW2": dW2,"db2": db2}
return grads
#更新梯度,注意必须要全部计算完一起更新,不能传播一层更新一层
def update(parameters,grads,alpha):
parameters["W1"] -= alpha * grads["dW1"]
parameters["b1"] -= alpha * grads["db1"]
parameters["W2"] -= alpha * grads["dW2"]
parameters["b2"] -= alpha * grads["db2"]
return parameters
#用于画图
def plot_decision_boundary(parameters):
#对网格状的点进行预测
xx, yy = np.meshgrid(np.arange(-0.1, 1.1, 0.01), np.arange(-0.1, 1.1, 0.01))
X = np.c_[xx.ravel(),yy.ravel()]
Z = forward_propagation(parameters,X)["A2"]
Z = Z.reshape(xx.shape)
# 然后画出等高线图
custom_cmap = ListedColormap(['#EF9A9A', '#FFF59D'])
plt.contourf(xx, yy, Z, linewidth=5,cmap=custom_cmap)
将这些模块按照流程图中的结构组合起来,就可以构建出全连接神经网络了。
#将功能组合起来,即为全连接神经网络
def DNN(learning_rate,iteration_times,k):
parameters = initial(k)
for i in range(iteration_times):
cache = forward_propagation(parameters,x_data)
Loss = loss(cache["A2"],y_data)
grads = backward_propagation(parameters,x_data,y_data,cache,Loss)
parameters = update(parameters,grads,learning_rate)
if i % 1000 == 0:
print("第"+str(i)+"轮:损失值为"+str(Loss))
plot_decision_boundary(parameters)
plt.scatter(x_data[:,1],x_data[:,0],c=y_data)
plt.show()
#参数可任选
DNN(0.005,200000,40)
最后的结果,总之一般来说,神经元越多越稳定、训练的次数越多越稳定、学习率越小越稳定;当神经元少的时候,尤其是只有3个的时候,经常会很不稳定。最后的分类结果如下图所示,可见我们通过多层的神经网络,已经实现了非线性边界。