ANN之手写数字识别
ANN之手写数字识别
MNIST数据集请查看上一篇博文:
本文在数据存储形式上与上一篇博文区别为,将target从单一的数字转化为了一个10*1的矩阵,或者说是一个10维向量,对应的target的维度值为1,其它均为0。
简介
ANN是人工神经网络的简称。如果你对比ANN简单的线性回归、逻辑回归和梯度下降法没有认识,推荐先学习一下再学习ANN。
ANN与回归的不同之处在于,它有网络结构,大概如下:
而对于回归,我们可以简单地理解为,只有输入层到输出层。而ANN有一个或多个隐藏层,隐藏层的每个节点称作神经元,每层之间依赖于一个权重系数矩阵。设计神经网络的主要工作在于设计隐藏层的结构和权重系数矩阵。
对于每层之间的传播过程,可以总结为如下:
表示系数矩阵的第 行,由 在该层的位置决定。
是偏置项。
是**函数。
该层的每一个节点的 构成该层的输出 。
权重系数矩阵
-
初始化
权重初始化一般采用Xavier权重初始化和MSRA Filler两种方式,如果**函数为sigmod或者tanh,使用前者;如果**函数为relu,则使用后者。
两种初始化方式的具体和区别,推荐博文:https://www.cnblogs.com/makefile/p/init-weight.html?utm_source=itdadao&utm_medium=referral
代码实现
#neurons是神经网络的结构表示,是一个数组,元素为每层的节点数,例如[784,40,10]。
#输出parameter是一个字典,键为Wi,值为系数矩阵。
def initialize_parameters(neurons):
inputneurons=neurons[0]
hideneurons=neurons[1:len(neurons)-1]
outputneurons=neurons[-1]
hidelayers=len(hideneurons)
parameter={}
np.random.seed(2);
#input->hide
W0=np.random.uniform(low=-np.sqrt(6)/np.sqrt(inputneurons+1+hideneurons[0]), high=np.sqrt(6)/np.sqrt(inputneurons+1+hideneurons[0]),size=(hideneurons[0],inputneurons+1))
parameter["W0"]=W0
#hide->hide
for l in range(0,hidelayers-1):
Wl=np.random.uniform(low=-np.sqrt(6)/np.sqrt(hideneurons[l]+1+hideneurons[l+1]), high=np.sqrt(6)/np.sqrt(hideneurons[l]+1+hideneurons[l+1]),size=(hideneurons[l+1],hideneurons[l]+1))
parameter["W"+str(l+1)]=Wl
#hide->output
Wo=np.random.uniform(low=-np.sqrt(6)/np.sqrt(hideneurons[hidelayers-1]+1+outputneurons), high=np.sqrt(6)/np.sqrt(hideneurons[hidelayers-1]+1+outputneurons),size=(outputneurons,hideneurons[hidelayers-1]+1))
parameter["W"+str(hidelayers)]=Wo
return parameter
偏置项
如果我们只看一维的 , 是斜率, 是截距。
如果 ,则图像经过原点,但并不是所有的数据都是关于原点线性可分的,如下图。因此需要一个偏置项来移动分割面。
而这里的 可以看做 , 则可以作为系数矩阵的一列,作为可训练的系数。
**函数
**函数常用的有sigmod、tanh和relu。
**函数的作用是使神经网络层的输出非线性化。假设没有**函数,我们知道 是线性函数,但是并不是所有的数据分类都是线性可分的,如下图。
左图线性可分,右图非线性可分。如果没有**函数,用线性结果去拟合右图的数据,则显得非常僵硬。
关于**函数的细节,推荐博文:
- 代码实现
def sigmod(x):
_x = -1*x
return 1.0/(1+np.exp(_x))
def tanh(x):
x_2 = 2*x
return 2*sigmod(x_2)-1
def relu(x):
mx = np.maximum(x, 0)
return mx
**函数的导数为:
代码实现:
def sigmod_prime(y):
return y*(1-y)
def tanh_prime(y):
return 1-y**2
def relu_prime(y):
if(y>0):
return 1
else:
return 0
神经网络的作用
为什么简单的回归的拟合效果没有神经网络好呢?神经网络各层的神经元究竟起到了什么样的作用?我们可以通过这个图来理解一下。
每层的一个神经元其实是对数据做了一次划分的表示,多个神经元通过系数矩阵的运算其实是在取逻辑与的过程,从而实现了对数据比较完整的划分。通过下图可以更加明确这个效果。
ANN学习过程
学习过程分为前向传播(forward_propagation)和反向传播(backward_propagation)两个过程。
- 前向传播
前向传播前面已经介绍过了,每层过程如下:
代码实现:
#hidelayers为隐藏层层数,outputneurons为输出层节点数,parameters为所有系数矩阵的字典,data为输入层的数据,target为输出的预期结果。
#output是输出层的结果,error是误差总和
def forward_propagation(hidelayers,outputneurons,parameters,data,target):
outputs=[]
outputs.append(data)
error=0.0
for l in range(0,hidelayers+1):
outputs[l]=np.vstack((outputs[l],np.ones((1,1))))
outputs.append(tanh(np.dot(parameters["W"+str(l)],outputs[l])))
for i in range(0, outputneurons):
error=error+0.5*((target[i][0]-outputs[-1][i][0])**2)
return outputs,error
-
反向传播
由于输出是有误差的,所以需要修正误差,于是就需要修正权重系数,我们可以根据误差对系数求偏导,以此来表示系数对误差的影响,来实现对误差的修正,本文误差计算采用二次代价函数。
计算每个输出神经元的误差:
修正系数:
求偏导可以使用链氏求导法则:
以上是隐藏层到输出层系数修正的求偏导示例,其他层之间的系数修正同理求导即可。我们把 ( 表示层与层之间的间隔序号,、 分别是矩阵的行和列的下标 ) 称为梯度, 称为学习速率。
我们的误差计算函数其实是一个开口向上的二次函数,所以修正系数是为了是二次函数取到最小值。
代码实现
#计算梯度
#hidelayers表示隐藏层层数,neurons是神经网络结构数组,outputs表示前向传播的输出,target表示数据本身的结果
#输出的Theta_W为梯度
def grad(hidelayers,neurons,parameters,outputs,target):
Theta_W=[]
t_w=np.zeros((neurons[-1],neurons[hidelayers]+1))
e_o=[]
#hide->out
for o in range(0,neurons[-1]):
e_o.append(-(target[o][0]-outputs[-1][o][0])*tanh_prime(outputs[-1][o][0]))
eta_out=e_o
W=parameters["W"+str(hidelayers)]
for o in range(0,neurons[-1]):
for k in range(0,neurons[hidelayers]+1):
t_w[o][k]=eta_out[o]*outputs[hidelayers][k]
Theta_W.insert(0,t_w)
e_o=[]
for k in range(0,neurons[hidelayers]+1):
eta_w=0
for o in range(0,neurons[-1]):
eta_w=eta_w+eta_out[o]*W[o][k]
e_o.append(eta_w*tanh_prime(outputs[hidelayers][k][0]))
eta_out=e_o
#hide->hide
for hl in range(hidelayers,0,-1):
W=parameters["W"+str(hl-1)]
t_w=np.zeros((neurons[hl],neurons[hl-1]+1))
for o in range(0,neurons[hl]):
for k in range(0,neurons[hl-1]+1):
t_w[o][k]=eta_out[o]*outputs[hl-1][k]
Theta_W.insert(0,t_w)
e_o=[]
for k in range(0,neurons[hl-1]+1):
eta_w=0
for o in range(0,neurons[hl]):
eta_w=eta_w+eta_out[o]*W[o][k]
e_o.append(eta_w*tanh_prime(outputs[hl-1][k][0]))
eta_out=e_o
return Theta_W
#反向传播过程
#hidelayers表示隐藏层层数,neurons是神经网络结构数组,parameters表示系数矩阵,eta表示学习速率,Theta_W表示要修正的梯度。
def backward_propagation(hidelayers,neurons,parameters,eta,Theta_W):
cache_param={}
for hl in range(0,hidelayers+1):
W=parameters["W"+str(hl)]
newW=parameters["W"+str(hl)]
for o in range(0,neurons[hl+1]):
for k in range(0,neurons[hl]+1):
newW[o][k]=W[o][k]-eta*Theta_W[hl][o][k]
cache_param["W"+str(hl)]=newW
return cache_param
整体代码
# -*- coding: utf-8 -*-
import pandas as pd
import numpy as np
import struct,os
from array import array as pyarray
from numpy import append, array, int8, uint8, zeros
pd.set_option('display.width',10000)
pd.set_option('display.max_colwidth',10000)
pd.set_option('display.max_columns',100)
pd.set_option('display.max_rows',100)
np.set_printoptions(threshold=np.nan)
def sigmod(x):
_x = -1*x
return 1.0/(1+np.exp(_x))
def tanh(x):
x_2 = 2*x
return 2*sigmod(x_2)-1
def relu(x):
mx = np.maximum(x, 0)
return mx
def sigmod_prime(y):
return y*(1-y)
def tanh_prime(y):
return 1-y**2
def relu_prime(y):
if(y>0):
return 1
else:
return 0
def softmax(x):
return np.argmax(x)
def normalized(ima, inputneurons):
ima_max=np.max(ima)
ima_min=np.min(ima)
newima=np.zeros((inputneurons,1))
for i in range(0,inputneurons):
newima[i][0]=(ima[i][0]-ima_min)/(ima_max-ima_min)
return newima
def initialize_parameters(neurons):
inputneurons=neurons[0]
hideneurons=neurons[1:len(neurons)-1]
outputneurons=neurons[-1]
hidelayers=len(hideneurons)
parameter={}
np.random.seed(2);
#inputneurons
W0=np.random.uniform(low=-np.sqrt(6)/np.sqrt(inputneurons+1+hideneurons[0]), high=np.sqrt(6)/np.sqrt(inputneurons+1+hideneurons[0]),size=(hideneurons[0],inputneurons+1))
parameter["W0"]=W0
#hidelayers
for l in range(0,hidelayers-1):
Wl=np.random.uniform(low=-np.sqrt(6)/np.sqrt(hideneurons[l]+1+hideneurons[l+1]), high=np.sqrt(6)/np.sqrt(hideneurons[l]+1+hideneurons[l+1]),size=(hideneurons[l+1],hideneurons[l]+1))
parameter["W"+str(l+1)]=Wl
#outputneurons
Wo=np.random.uniform(low=-np.sqrt(6)/np.sqrt(hideneurons[hidelayers-1]+1+outputneurons), high=np.sqrt(6)/np.sqrt(hideneurons[hidelayers-1]+1+outputneurons),size=(outputneurons,hideneurons[hidelayers-1]+1))
parameter["W"+str(hidelayers)]=Wo
return parameter
def forward_propagation(hidelayers,outputneurons,parameters,data,target):
outputs=[]
outputs.append(data)
error=0.0
for l in range(0,hidelayers+1):
outputs[l]=np.vstack((outputs[l],np.ones((1,1))))
outputs.append(sigmod(np.dot(parameters["W"+str(l)],outputs[l])))
for i in range(0, outputneurons):
error=error+0.5*((target[i][0]-outputs[-1][i][0])**2)
return outputs,error
def grad(hidelayers,neurons,parameters,outputs,target):
Theta_W=[]
t_w=np.zeros((neurons[-1],neurons[hidelayers]+1))
e_o=[]
#hide->out
for o in range(0,neurons[-1]):
e_o.append(-(target[o][0]-outputs[-1][o][0])*sigmod_prime(outputs[-1][o][0]))
eta_out=e_o
W=parameters["W"+str(hidelayers)]
for o in range(0,neurons[-1]):
for k in range(0,neurons[hidelayers]+1):
t_w[o][k]=eta_out[o]*outputs[hidelayers][k]
Theta_W.insert(0,t_w)
e_o=[]
for k in range(0,neurons[hidelayers]+1):
eta_w=0
for o in range(0,neurons[-1]):
eta_w=eta_w+eta_out[o]*W[o][k]
e_o.append(eta_w*sigmod_prime(outputs[hidelayers][k][0]))
eta_out=e_o
#hide->hide
for hl in range(hidelayers,0,-1):
W=parameters["W"+str(hl-1)]
t_w=np.zeros((neurons[hl],neurons[hl-1]+1))
for o in range(0,neurons[hl]):
for k in range(0,neurons[hl-1]+1):
t_w[o][k]=eta_out[o]*outputs[hl-1][k]
Theta_W.insert(0,t_w)
e_o=[]
for k in range(0,neurons[hl-1]+1):
eta_w=0
for o in range(0,neurons[hl]):
eta_w=eta_w+eta_out[o]*W[o][k]
e_o.append(eta_w*sigmod_prime(outputs[hl-1][k][0]))
eta_out=e_o
return Theta_W
def grad_average(batch_size,hidelayers,Theta_W_s):
Theta_W=[]
for hl in range(0,hidelayers+1):
t_w=np.zeros((Theta_W_s[0][hl].shape[0],Theta_W_s[0][hl].shape[1]))
for bs in range(0,batch_size):
t_w=t_w+Theta_W_s[bs][hl]
t_w=t_w/batch_size
Theta_W.append(t_w)
return Theta_W
def backward_propagation(hidelayers,neurons,parameters,theta,Theta_W):
cache_param={}
for hl in range(0,hidelayers+1):
W=parameters["W"+str(hl)]
newW=parameters["W"+str(hl)]
for o in range(0,neurons[hl+1]):
for k in range(0,neurons[hl]+1):
newW[o][k]=W[o][k]-theta*Theta_W[hl][o][k]
cache_param["W"+str(hl)]=newW
return cache_param
def load_mnist(dataset="training_data", digits=np.arange(10), path="."):
if dataset == "training_data":
fname_image = os.path.join(path, 'train-images.idx3-ubyte')
fname_label = os.path.join(path, 'train-labels.idx1-ubyte')
elif dataset == "testing_data":
fname_image = os.path.join(path, 't10k-images.idx3-ubyte')
fname_label = os.path.join(path, 't10k-labels.idx1-ubyte')
else:
raise ValueError("dataset must be 'training_data' or 'testing_data'")
flbl = open(fname_label, 'rb')
magic_nr, size = struct.unpack(">II", flbl.read(8))
lbl = pyarray("b", flbl.read())
flbl.close()
fimg = open(fname_image, 'rb')
magic_nr, size, rows, cols = struct.unpack(">IIII", fimg.read(16))
img = pyarray("B", fimg.read())
fimg.close()
ind = [ k for k in range(size) if lbl[k] in digits ]
N = len(ind)
images = zeros((N, rows, cols), dtype=uint8)
labels = []
for i in range(len(ind)):
images[i] = array(img[ ind[i]*rows*cols : (ind[i]+1)*rows*cols ]).reshape((rows, cols))
l=np.zeros((10,1))
l[lbl[ind[i]]]=1
labels.append(l)
return images, labels
def load_samples(dataset="training_data"):
image,label = load_mnist(dataset)
X = [np.reshape(x,(28*28, 1)) for x in image]
X = [x/255.0 for x in X] # 灰度值范围(0-255),转换为(0-1)
return X,label
if __name__ == "__main__":
hideneurons=[40]
hidelayers=len(hideneurons)
inputneurons=784
outputneurons=10
neurons=[inputneurons]+hideneurons+[outputneurons]
parameters = initialize_parameters(neurons)
eta = 1.0
numtraindata = 60000
numtestdata = 10000
traindata, imgtraintarget = load_samples(dataset='training_data')
testdata, imgtesttarget = load_samples(dataset='testing_data')
batch_size=20
epoch=10
Correct=[]
for e in range(0,epoch):
for ntd in range(0,numtraindata,batch_size):
Theta_W_s=[]
ER=[]
for bs in range(0,batch_size):
outputs,error = forward_propagation(hidelayers,outputneurons,parameters,traindata[ntd+bs],imgtraintarget[ntd+bs])
Theta_W_s.append(grad(hidelayers,neurons,parameters,outputs,imgtraintarget[ntd+bs]))
ER.append(error)
Theta_W=grad_average(batch_size,hidelayers,Theta_W_s)
parameters=backward_propagation(hidelayers,neurons,parameters,eta,Theta_W)
er=0
for e in ER:
er=er+e
error=er/len(ER)
#print(str(ntd)+"-"+str(ntd+batch_size)+":"+str(error))
preCorrect=0
prenum=[0]*10
targetnum=[0]*10
for ntd in range(0,numtestdata):
outputs,error = forward_propagation(hidelayers,outputneurons,parameters,testdata[ntd],imgtesttarget[ntd])
predict=softmax(outputs[-1])
target=softmax(imgtesttarget[ntd])
targetnum[target]=targetnum[target]+1
if(predict==target):
preCorrect=preCorrect+1
prenum[target]=prenum[target]+1
print(preCorrect/numtestdata)
Correct.append(preCorrect/numtestdata)
print(Correct)
for i in range(0,10):
if(targetnum[i]==0):
a=0.0
else:
a=prenum[i]/targetnum[i]
print(str(i)+": "+str(prenum[i])+"/"+str(targetnum[i])+" = "+str(a)+"\r\n")
print("All Testdata are predicted......")
由于一个一个数据计算过慢,于是选择一组大小为batch_size的数据以梯度平均值进行迭代。神经网络结构为[784,40,10],学习速率为1.0,**函数为sigmod。预测结果如图:
sklearn的ANN
sklearn也有人工神经网络的库
from sklearn.neural_network import MLPClassifier
clf = MLPClassifier(hidden_layer_sizes=(50,50),
activation ='relu',
solver ='sgd',
random_state =1,
learning_rate ='constant',
learning_rate_init=0.1,
max_iter =100,
alpha =1e-4,
verbose =True
)
clf.fit(imgtraindata,imgtraintarget)
pre = clf.predict(imgtestdata)
结果:
可见还是库写得好,瘫——
下一篇: leetcode 39. 组合总和