一、神经网络原理 线性回归(Linear Regression)和逻辑回归(Logistic Regression)通常用来处理线性模型,如果利用线性回归或逻辑回归对多特征的非线性问题进行分类,则涉及太多特征组合的计算,往往导致计算负荷增大,并不适合解决这类问题。
假设我们需要训练一个模型用来判断一张图片中是否出现汽车,可能有很多用来训练模型的数据,这些图片有的包含小汽车,有的没有,利用这些图片的一个个像素值作为特征,训练一个满足这样功能的模型。训练过程需要处理可能百万级别甚至更多的数据,对于这样问题通常采用神经网络(Neural Networks)解决。
1.1 模型
这个一个简单的3层神经网络,第一层为输入层(Input Layers),最后一层为输出层(Output Layers),中间层称为隐藏层(Hidden Layers)
1.2 前向传播 1.2.1 激活函数 (1)Sigmoid函数
一个常见的激活函数,其数学表达式为 。
Python实现代码为:
1 2 3 import numpy as npdef sigmoid (x) : return 1 /(1 +np.exp(-x))
(2)ReLU函数
计算速度更快,是目前的主流激活函数。数学表达式为:
Python实现代码为:
1 2 3 import numpy as npdef relu (x) : return np.maximum(0 ,x)
1.2.2 前向传播过程 除输入层外,每一层神经元都有前一层的神经元作为本层神经元的输入,本层神经元的输出又可以作为下一层神经元的输入,依次向前传播最终得到一个输出值,这个由输入值经输入层经过一系列处理最终到达输出层得到输出值的过程称为前向传播。
其中x1, x2, x3是输入单元,即原始的输入数据,a1, a2, a3是中间单元,负责将输入的数据处理然后传递到下一层,最后是输出单元,负责计算 。计算的过程中为每一层都添加了一个偏置(bias unit)。
上图中 , 分别代表输入层到隐藏层的权重和隐藏层到输出层的权重,对于上图的网络模型激活单元和输出分别表达为:
如此,从左到右的算法称为前向传播算法,实际应用中为了计算方便通常是以矩阵方式计算的。
Python实现示例(示例不能直接运行):
1 2 3 4 5 6 7 8 9 10 def predict (self,X) : self.a1 = X.T self.z2 = np.dot(self.W1,self.a1)+self.b1 self.a2 = relu(self.z2) self.z3 = np.dot(self.W2,self.a2)+self.b2 self.a3 = relu(self.z3) out = self.a3 p = np.argmax(out, axis=0 ) return p
1.3 代价函数
通过代价函数观察预测的结果与真实情况的误差有多大。
1.4 反向传播算法 一般的训练算法可以分为两个阶段:
(1)求解代价函数关于权值(参数)的导数。(BP)
(2)用得到的导数进一步计算权值的调整量。(梯度下降等优化算法)
反向传播(BP)算法主要应用第一阶段,非常高效的计算这些导数。
1.4.1 推导过程 假设有一个四层的神经网络,其相关参数为: Sl=4, L=4(其中L表示网络层数,Sl表示l层有多少个神经元),用 表示误差,结合前面介绍的前向传播过程,则:
前一层误差为:
其中 是Sig函数的导数。
接着第二层的误差为:
第一层是输入变量,不存在误差,此时不考虑正则项,则有:
要求的导数 = 权值输出端单元的误差项 * 权值输入端单元的激活值。
Python实现示例(示例不能直接运行):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 def backward (self, dAL) : m=60000 dZ3=np.multiply(dAL,relu_derivative(self.z3)) dW2=np.dot(dZ3, self.a2.T)/m db2=np.mean(dZ3,axis=1 ) dAL_1 = np.dot(self.W2.T, dZ3) dZ2 = np.multiply(dAL_1, relu_derivative(self.z2)) dW1 = np.dot(dZ2, self.a1.T) / m db1 = np.mean(dZ2, axis=1 ) self.W2-=self.lr*dW2 self.b2-=self.lr*db2 self.W1 -= self.lr * dW1 self.b1 -= self.lr * db1
二、数据集解析 数据集来源:http://yann.lecun.com/exdb/mnist,选用MNIST手写字数据集训练神经网络,数据集使用Python模块Struct解析二进制文件。手写字特征为28*28=784个像素点,输出为手写字值。解析过程不做详细介绍,其数据解析的一种Python实现为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 import numpy as npimport matplotlib.pyplot as pltimport structfile1="./MNIST_data/train-images.idx3-ubyte" file2="./MNIST_data/train-labels.idx1-ubyte" def train_images_ana (filepath) : """解析图片数据集 .idx3-ubyte格式""" with open(filepath,'rb' ) as fbj: bin_data=fbj.read() offset=0 magic_num,image_num,rows_num,column_num=struct.unpack_from('>iiii' ,bin_data,offset) offset+=struct.calcsize('>iiii' ) imgsize=image_num*rows_num*column_num fmt_image='>' +str(imgsize)+'B' images=struct.unpack_from(fmt_image,bin_data,offset) img=np.reshape(images,(image_num,rows_num*column_num)) return img def train_labels_ana (filepath) : """解析特征数据集 .idx1-ubyte格式""" with open(filepath,'rb' ) as fbj: bin_data=fbj.read() offset=0 magic_num,items_num=struct.unpack_from('>ii' ,bin_data,offset) offset+=struct.calcsize('>ii' ) fmt_label='>' +str(items_num)+'B' labels=struct.unpack_from(fmt_label,bin_data,offset) label=np.reshape(labels,[items_num]) return label if __name__=='__main__' : imgs=train_images_ana(file1) print(np.shape(imgs[1 ])) labels = train_labels_ana(file2) print(labels) print(np.shape(labels[1 ])) for i in range(10 ): img=np.reshape(imgs[i],[28 ,28 ]) plt.imshow(img,cmap='gray' ) print(labels[i]) plt.show()
三、神经网络搭建 搭建一个三层神经网络,输入层、隐藏层、输出层节点分别为:784,100,10。
四、附录 network.py文件,构建神经网络类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 import numpy as npfrom activation_func import *from loss import *class Network : def __init__ (self,inputnodes,hidnodes,outputnodes,learning_rate) : self.innodes = inputnodes self.hidnodes = hidnodes self.outnodes = outputnodes self.lr = learning_rate self.W1 = np.random.randn(self.hidnodes, self.innodes) * 0.01 self.W2 = np.random.randn(self.outnodes, self.hidnodes) * 0.01 self.b1 = np.random.randn(self.hidnodes, 1 ) * 0.01 self.b2 = np.random.randn(self.outnodes, 1 ) * 0.01 def predict (self,X) : self.a1 = X.T self.z2 = np.dot(self.W1,self.a1)+self.b1 self.a2 = relu(self.z2) self.z3 = np.dot(self.W2,self.a2)+self.b2 self.a3 = relu(self.z3) out = self.a3 p = np.argmax(out, axis=0 ) return p def backward (self, dAL) : m=60000 dZ3=np.multiply(dAL,relu_derivative(self.z3)) dW2=np.dot(dZ3, self.a2.T)/m db2=np.mean(dZ3,axis=1 ) dAL_1 = np.dot(self.W2.T, dZ3) dZ2 = np.multiply(dAL_1, relu_derivative(self.z2)) dW1 = np.dot(dZ2, self.a1.T) / m db1 = np.mean(dZ2, axis=1 ) self.W2-=self.lr*dW2 self.b2-=self.lr*db2 self.W1 -= self.lr * dW1 self.b1 -= self.lr * db1
loss.py文件,损失函数计算代价。
1 2 3 4 5 6 7 8 9 10 import numpy as npdef cross_entropy (y, y_predict) : y_predict = np.clip(y_predict,1e-10 ,1 -1e-10 ) return -(y * np.log(y_predict) + (1 - y) * np.log(1 - y_predict)) def cross_entropy_der (y,y_predict) : return -y/y_predict+(1 -y)/(1 -y_predict)
activation.py文件,激活函数及其导数实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 import numpy as np""" 激活函数 选择非线性的激活函数处理非线性假设 常用激活函数relu、sigmoid """ def sigmoid (x) : return 1 /(1 +np.exp(-x)) def relu (x) : return np.maximum(0 ,x) def sig_derivative (x) : fx=sigmoid(x) return fx*(1 -fx) def relu_derivative (x) : return (x>=0 ).astype(np.float64)
load_data.py文件,加载数据集。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 import numpy as npimport matplotlib.pyplot as pltimport structfile1="./MNIST_data/train-images.idx3-ubyte" file2="./MNIST_data/train-labels.idx1-ubyte" def train_images_ana (filepath) : """解析图片数据集 .idx3-ubyte格式""" with open(filepath,'rb' ) as fbj: bin_data=fbj.read() offset=0 magic_num,image_num,rows_num,column_num=struct.unpack_from('>iiii' ,bin_data,offset) offset+=struct.calcsize('>iiii' ) imgsize=image_num*rows_num*column_num fmt_image='>' +str(imgsize)+'B' images=struct.unpack_from(fmt_image,bin_data,offset) img=np.reshape(images,(image_num,rows_num*column_num)) return img def train_labels_ana (filepath) : """解析特征数据集 .idx1-ubyte格式""" with open(filepath,'rb' ) as fbj: bin_data=fbj.read() offset=0 magic_num,items_num=struct.unpack_from('>ii' ,bin_data,offset) offset+=struct.calcsize('>ii' ) fmt_label='>' +str(items_num)+'B' labels=struct.unpack_from(fmt_label,bin_data,offset) label=np.reshape(labels,[items_num]) return label
train.py文件,训练模型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 from network import *from load_data import *input_nodes = 784 hidden_nodes = 100 output_nodes = 10 learning_rate = 0.1 n = Network(input_nodes, hidden_nodes, output_nodes, learning_rate) X=train_images_ana(file1) Y=train_labels_ana(file2) for epoch in range(10 ): cnt = 0 for i in range(60000 ): Y_predict = n.predict(np.mat(X[i])) if (Y[i]==Y_predict): cnt+=1 dA = cross_entropy_der(np.mat(Y[i]),Y_predict) n.backward(dA) print('epoch %d:accurac=%f' %(epoch,cnt/60000 ))