Python手动搭建神经网络
神经网络
神经网络算法其实大体分为前向传播和反向传播两个部分,前向传播用于计算当前各参数(权重)下的预测值与实际值的误差(损失函数)。反向传播用于计算各参数的下降梯度,同时用这些梯度更新各参数值。这样反反复复训练直到最终损失函数收敛即不再下降。这里使用numpy库来手动搭建简单的单隐藏层神经网络。
参数调试
在这次验证码识别网络中,主要的参数有learning_rate,batch_size,iter_num 。当然对于隐藏层数和隐藏层单元数,这里仅仅是用于做简单识别,并不作很高的要求。就采用了单隐藏层,500单元数(略大于输入单元数486)作为神经网络的主要结构。输出单元为36个,对应26个字母和10个数字。
Learning_rate
learning_rate 控制各参数下降速度。当最终的cost不断上升下降或者上升时,learning_rate 过大,函数不能收敛,需要调小learning_rate。而当cost下降过慢,或者说下降幅度过小时,就需要适当提升learning_rate来提升训练速度(如果主机计算能力足够则可以忽略啦)。
Batch_size
对 batch_size,实际就是每次迭代中输入多少的数据,同样由于计算量原因可以适量减小batch_size。但当它过小,比如为1时,就成了随机梯度下降,有可能只能达到最终收敛点附近却不能达到最好。在这个网络中,我之前选用了1000大小的batch_size,但是最后预测效果不好。于是改为100大小,分批多次训练。不但降低了发生memory error的可能性,同时得到了性能的提升。
Iter_num
Iter_num其实就是某批训练数据的迭代次数,这个大小可以通过观察某批训练集每次迭代后cost下降情况来决定。比如说在第10代左右cost就不再明显下降的话,10可能就是一个可用的迭代数。如果这个值过大的话,当然会造成计算资源和时间的浪费,因为函数收敛后再优化已经没有多大意义了。过小呢则有可能出现尚未收敛,训练集没有充分使用上的情况。在确定上面两个参数后再确定这个参数也许可行。
各模块搭建
Y标签转one-hot码
神经网络的最终学习是要分出36类,而现有的标签值表示的是某类的index,所以需要将Y标签转为one-hot码来进行训练。举例来说,如果我只要分出4类,而某一个样本的真实值是第三类那么y=3。但是y=3需要转成[0,0,1,0]这一个数组来表示,下面是转码函数(输入数值y,返回one-hot数组)。
def tran_y(y):
y_ohe=np.zeros(36)
y_ohe[y]=1
return y_ohe
**函数sigmoid
神经网络是离不开**函数的,如果没有了非线性**函数,那么神经网络只是单一的线性变换,就像W*X+b一样。这里给出的sigmoid只是**函数中的一种,另外还有tanh,softmax,relu等等。注意**函数的编写需要满足向量化的要求,也就是说对与输入的数组**函数同样适用。下面是sigmoid函数。
def sigmoid(x):
s=1/(1+np.exp(-x))
return s
初始化参数
这里初始化的参数包括隐藏层的W1,b1和输出层的W2,b2。需要注意的是参数W的维度是本层单元数*前层单元数,参数b是本层单元数*1。为什么是这个尺度可以根据A1=W*A0+b来理解。A0是前一层输入,A1是这一层输出,各个参数维度需要满足矩阵相乘的条件。这里的n_x,n_h,n_y分别代表输入,隐藏层和输出层的单元数大小。W参数使用随机初始化,并用0.01缩小尺度,b参数则可以0-1随意,这里用的是0.01。
def initialize_parameters(n_x,n_h,n_y):
W1=np.random.randn(n_h,n_x)*0.01
b1=np.ones(shape=(n_h,1))*0.01
W2=np.random.randn(n_y,n_h)*0.01
b2=np.ones(shape=(n_y,1))*0.01
parameters={"W1":W1,"b1":b1,"W2":W2,"b2":b2}
return parameters
前向传播
前向传播就是一个将样本通过各层网络,并计算各层输出和最终输出的过程。计算公式总的可以用上述的A1=W*A0+b来表示,不过在计算完每层输出后需要套上**函数A1=sigmoid(A1)。由于里面的A,Z在后面的反向传播要用,所以同时返回了。
def forward_propagation(x,parameters):
W1=parameters["W1"]
b1=parameters["b1"]
W2=parameters["W2"]
b2=parameters["b2"]
Z1=np.dot(W1,x)+b1
A1=np.tanh(Z1)
Z2=np.dot(W2,A1)+b2
A2=sigmoid(Z2)
cache={"Z1":Z1,"A1":A1,"Z2":Z2,"A2":A2}
return A2,cache
误差计算
说是误差其实就是cost代价,用来表示当前预测值和实际值的偏差程度。具体为什么使用这个代价函数,一时不好讲清,我自己也不是懂。但用最小二乘是不行的好像,会陷入局部极小。
def compute_cost(A2,y,parameters):
m=y.shape[1]
W1 = parameters['W1']
W2 = parameters['W2']
cost=-1/m*np.sum(np.multiply(y,np.log(A2))+np.multiply((1-y),np.log(1-A2)))
cost=np.squeeze(cost)
return cost
反向传播
未完。。。明日更