1.神经网络的原理和非线性矫正
1.神经网络的原理:
在介绍神经网络的原理之前先回顾一下线性回归模型的一般公式:
其中,表示y的估计值,x[0],到x[p]是样本特征值,w表示每个特征值的权重,y_hat可以看成是所有特征值的加权求和,可以用如下图所示表示这个过程:
分析:在图中, 输入的特征和预测的结果用节点进行表示,系数w用来连接这些节点。
再来说神经网络的原理,神经网络的原理,就是在上述算法过程里添加了隐藏层(Hidden Layers),然后在隐藏层重复进行上述加权求和计算,最后再把隐藏层所计算的结果用来生成最终结果。如下图所示:
这样一来,模型要学习的特征系数,或者说权重,就会有很多了。
可以看到,在第一个输入的特征和隐藏单元之间,都有一个系数,这一步也是为了生成隐藏单元。而每个隐藏单元到最终结果之间,也都有一个系数。而计算一系列的加权求和和计算单一的加权求和。
2.神经网络的非线性矫正:
非线性矫正处理方法:
在生成隐藏层之后,我们要对结果进行非线性矫正,简称relu或者进行双曲正切处理,简称tanh。通过这两种方法处理后的结果用来计算最终结果y。
用图像直观展示:
#导入numpy
import numpy as np
#导入画图工具
import matplotlib.pyplot as plt
#生成一个等差数列
line = np.linspace(-5,5,200)
#画出非线性矫正的图形表示
plt.plot(line,np.tanh(line),label='tanh')
plt.plot(line,np.maximum(line,0),label='relu')
#设置图注位置
plt.legend(loc='best')
plt.xlabel('x')
plt.ylabel('relu(x) and tanh(x)')
plt.show()
运行代码,如下图所示:
结果分析:
从图中可以看出,tanh函数把特征x的值压缩到-1到1区间内,-1代表的是x中较小的值,而1代表的是x中较大的值。relu函数则索性把小于0的x值全部去掉,用0代替。这两种非线性处理的方法,都是为了降低样本特征进行简化,从而使神经网络可以对复杂的非线性模型数据集进行学习。
那么这样一来,我们刚才看到的公式:
经过tanh处理后,就会变成下面的样子:
在权重系数w之外,我们又多了一个权重系数v,用来通过隐藏层h来计算y-hat的结果。在模型中,w和v都是通过对数据的学习所得出的。而用户所要设置的参数,就是隐藏层中节点的数量。
如何设置节点数量:
一般来讲,对于小规模数据集或者简单数据集,节点数量设置为10就已经足够了,但是对于大规模数据集或者复杂数据集来说,有两种方式可供选择:
1.增加隐藏层中的节点数量,比如增加到1万个
2.添加更多隐藏层,如下图:
在大型神经网络当中,往往有很多这样的隐藏层,这也是“深度学习”中“深度”二字的来源。
2.神经网络的模型参数调节
下面我们就以MLP算法中的MLP分类器为例,研究一下MLP分类器模型的使用方法。这次还是使用熟悉的酒的数据集。
#导入MLP神经网络
from sklearn.neural_network import MLPClassifier
#导入红酒数据集
from sklearn.datasets import load_wine
#导入数据集拆分工具
from sklearn.model_selection import train_test_split
wine = load_wine()
X = wine.data[:,:2]
y = wine.target
#下面我们拆分数据集
X_train,X_test,y_train,y_test = train_test_split(X,y,random_state=0)
#接下来定义分类器
mlp = MLPClassifier(solver='lbfgs')
mlp.fit(X_train,y_train)
运行代码,得到如下图所示:
参数含义:
activation:将隐藏单元进行非线性化的方法,一共四种:
“identity” “logistic” “tanh” “relu”,默认值是“relu”
alpha:和线性模型的alpha值是一样的,是一个L2惩罚项,用来控制正则化的程度,默认0.0001
hidden_layer_sizes:着重讲一下这个参数,默认情况下,hidden_layer_sizes的值是[100,]这意味着模型中只有一个隐藏层,而隐藏层中的节点数是100.如果我们给hidden_layer_sizes定义为[10,10],那就意味着模型中有两个隐藏层,每层有10个节点。
用图像展示MLP分类情况:
#导入画图工具
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
#使用不同色块表示不同分类
cmap_light = ListedColormap(['#FFAAAA','#AAFFAA','#AAAAFF'])
cmap_bold = ListedColormap(['#FF0000','#00FF00','#0000FF'])
x_min,x_max = X_train[:,0].min()-1,X_train[:,0].max()+1
y_min,y_max = X_train[:,1].min()-1,X_train[:,1].max()+1
xx,yy = np.meshgrid(np.arange(x_min,x_max,.02),np.arange(y_min,y_max,.02))
Z = mlp.predict(np.c_[xx.ravel(),yy.ravel()])
Z = Z.reshape(xx.shape)
plt.figure()
plt.pcolormesh(xx, yy, Z, cmap=cmap_light)
#将数据特征用散点图表示出来
plt.scatter(X[:,0],X[:,1],c=y,edgecolor='k',s=60)
plt.xlim(xx.min(),xx.max())
plt.ylim(yy.min(),yy.max())
#设定图题
plt.title("MLPClassifier:solver=lbfgs")
#显示图形
plt.show()
运行代码,如下图所示:
调节参数:将隐藏层的节点数变少,减少至10个:
#设定隐藏层中的节点数为10
mlp_20 = MLPClassifier(solver='lbfgs',hidden_layer_sizes=[10])
mlp_20.fit(X_train,y_train)
Z1 = mlp_20.predict(np.c_[xx.ravel(),yy.ravel()])
Z1 = Z1.reshape(xx.shape)
plt.figure()
plt.pcolormesh(xx,yy,Z1,cmap=cmap_light)
#使用散点图画出X
plt.scatter(X[:,0],X[:,1],c=y,edgecolor='k',s=60)
plt.xlim(xx.min(),xx.max())
plt.ylim(yy.min(),yy.max())
plt.title('MLPClassifier:nodes=10')
plt.show()
运行代码,如下图所示:
结果分析:
将前两张图进行对比,发现分类器生成的决定边界看起来很不一样了:
节点数为10的时候,决定边界丢失了很多细节。我们可以这样理解,在每一个隐藏层当中,节点数就代表了决定边界中最大的直线数,这个数值越大,则决定边界看起来越平滑。
当然,除了增加单个隐藏层中的节点数之外,还有两种方法可以让决定边界更细腻:
一个是增加隐藏层的数量;另一个是把activation参数改为tanh。
调节参数:增加隐藏层的数量
#设定隐藏层中的节点数为10
mlp_2L = MLPClassifier(solver='lbfgs',hidden_layer_sizes=[10,10])
mlp_2L.fit(X_train,y_train)
Z1 = mlp_2L.predict(np.c_[xx.ravel(),yy.ravel()])
Z1 = Z1.reshape(xx.shape)
plt.figure()
plt.pcolormesh(xx,yy,Z1,cmap=cmap_light)
#使用散点图画出X
plt.scatter(X[:,0],X[:,1],c=y,edgecolor='k',s=60)
plt.xlim(xx.min(),xx.max())
plt.ylim(yy.min(),yy.max())
plt.title('MLPClassifier : 2 layers')
plt.show()
结果分析:
和上一个图对比,能够看到隐藏层带来的结果就是决定边界看起来更加细腻。
调节参数:把activation参数改为tanh。
#设定隐藏层中的节点数为10
mlp_tanh = MLPClassifier(solver='lbfgs',hidden_layer_sizes=[10,10],activation='tanh')
mlp_tanh.fit(X_train,y_train)
Z2 = mlp_tanh.predict(np.c_[xx.ravel(),yy.ravel()])
Z2 = Z2.reshape(xx.shape)
plt.figure()
plt.pcolormesh(xx,yy,Z2,cmap=cmap_light)
#使用散点图画出X
plt.scatter(X[:,0],X[:,1],c=y,edgecolor='k',s=60)
plt.xlim(xx.min(),xx.max())
plt.ylim(yy.min(),yy.max())
plt.title('MLPClassifier : 2 layers with tanh')
plt.show()
结果分析:将activation参数修改为tanh后,分类器的决定边界完全变成了平滑的曲线。这就是我们对样本特征进行双曲线正切化后的结果。
除了上述方法,还可以通过调节alpha值来进行模型复杂度控制,默认的alpha值是0.0001。
调节参数:alpha增加到1
#修改模型的alpha参数
mlp_alpha = MLPClassifier(solver='lbfgs',hidden_layer_sizes=[10,10],activation='tanh',alpha=1)
mlp_alpha.fit(X_train,y_train)
Z3 = mlp_alpha.predict(np.c_[xx.ravel(),yy.ravel()])
Z3 = Z3.reshape(xx.shape)
plt.figure()
plt.pcolormesh(xx,yy,Z3,cmap=cmap_light)
#使用散点图画出X
plt.scatter(X[:,0],X[:,1],c=y,edgecolor='k',s=60)
plt.xlim(xx.min(),xx.max())
plt.ylim(yy.min(),yy.max())
plt.title('MLPClassifier : 2 layers with tanh')
plt.show()
jie
结果分析:
从图中可以看出,增加alpha参数的值,会加大模型正则化的程度,让模型更加简单。目前四种方法调整参数:
1.隐藏层的节点数
2.隐藏层的层数
3.activation
4.alpha
3.使用神经网络训练手写数字识别模型
数据集准备
使用MNIST数据集:
MNIST数据集是一个专门用来训练各种图像处理系统的庞大数据集,包含70000多个手写数字图像,60000个训练数据,10000个测试数据。改数据被广泛应用于魔性的训练和测试。目前有大量的学术论文都在试图把模型对MNIST数据集的识别错误率不断降低,目前识别错误率最低的一篇论文使用的是卷积神经网络,成功地把错误率降到了0.23%。而最早创造这个数据集的学者,在他们最早的论文中使用了支持向量机算法,使模型的错误率到达了0.8%。
获取MNIST数据集:
from sklearn.datasets import fetch_mldata
mnist = fetch_mldata('MNIST original',data_home='./datasets')
mnist
运行代码,得到如下图所示:
结果分析:从结果看出,MNIST数据集包含两个部分:
一个是分类标签;另一个是数据本身。
data的类型是无符号的8位整型np数组,而target是从0-9
接下来,检查一下数据集中的样本数量和样本特征数量:
from sklearn.model_selection import train_test_split
X = mnist.data/255
y = mnist.target
X_train,X_test,y_train,y_test = train_test_split(X,y,train_size=5000,test_size=1000,random_state=0)
运行代码,如下:
样本数量:70000,样本特征数:784
从结果看出,数据集有70000个样本,每个样本784个特征。这是因为,数据集中存储的样本是2828像素的手写数字图片的像素信息,因此特征数为2828=784个。
在开始训练MLP之前,需要对数据进行预处理,由于样本特征是从0~255的灰度值,为了让特征的数值更利于建模,我们把特征向量的值全部除以255,这样全部数值都会在0和1之间,再用我们非常熟悉的train_test_split函数将数据集分为训练集合测试集并训练神经网络:
from sklearn.model_selection import train_test_split
X = mnist.data/255
y = mnist.target
X_train,X_test,y_train,y_test = train_test_split(X,y,train_size=5000,test_size=1000,random_state=0)
#设置神经网络有2个100节点的隐藏层
mlp_hw = MLPClassifier(solver='lbfgs',hidden_layer_sizes=[100,100],activation='relu',alpha=1e-5,random_state=62)
#训练模型
mlp_hw.fit(X_train,y_train)
print('测试数据集得分:{:.2f}%'.format(mlp_hw.score(X_test,y_test)*100))
运行代码,得到如下结果:
测试数据集得分:93.90%
结果分析:从结果中可以看到,可以说是一个非常不错的分数了。
使用模型进行数字识别
准备图片:
将上面的测试图像转化为模型可以读取的numpy数组并进行测试:
#导入图像处理工具
from PIL import Image
#打开图像
image = Image.open('mlp_test.jpg').convert('F')
#调整图像的大小
image = image.resize((28,28))
arr = []
#将图像中的像素作为预测数据点的特征
for i in range(28):
for j in range(28):
pixel = 1.0 - float(image.getpixel((j,i)))/255
arr.append(pixel)
#由于只有一个样本,所以需要进行reshape
arr1 = np.array(arr).reshape(1,-1)
#进行图像识别
print('图片中的数字是:{:.0f}'.format(mlp_hw.predict(arr1)[0]))
运行代码,得到如下结果:
图片中的数字是:4
结果分析:从结果中可以看到,神经网络正确地识别出了图片中的数字4,效果还是很不错的。