欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Python手动搭建神经网络

程序员文章站 2022-07-13 11:42:42
...

神经网络

神经网络算法其实大体分为前向传播和反向传播两个部分,前向传播用于计算当前各参数(权重)下的预测值与实际值的误差(损失函数)。反向传播用于计算各参数的下降梯度,同时用这些梯度更新各参数值。这样反反复复训练直到最终损失函数收敛即不再下降。这里使用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

反向传播

未完。。。明日更