题目:MLP实现图像多分类(手写数字识别)
实验目的与环境
目的
基于mnist数据集,建立MLP模型
使用模型实现0-9数字的十分类
环境
Python3.6
Numpy
Matplotlib
Keras
Pandas
理论
多层感知机(MLP)原理
多层感知机(MLP,Multilayer Perceptron)也叫人工神经网络(ANN,Artificial Neural Network),除了输入输出层,它中间可以有多个隐层,最简单的MLP只含一个隐层,即三层的结构,如下图:
从上图可以看到,多层感知机层与层之间是全连接的。多层感知机最底层是输入层,中间是隐藏层,最后是输出层。
隐藏层的神经元怎么得来?首先它与输入层是全连接的,假设输入层用向量X表示,则隐藏层的输出就是 f (W1X+b1),W1是权重(也叫连接系数),b1是偏置,函数f 可以是常用的sigmoid函数或者tanh函数:
注:神经网络中的Sigmoid型激活函数:
导数
sigmoid函数也叫logistic函数,用于隐藏层神经元输出,取值范围为(0,1),他可以将一个实数映射到(0,1)区间,用来做二分类
在特征相差比较复杂或是相差不是特别大时效果比较好
sigmoid缺点:
- 激活函数计算量大,反向传播求误差梯度时,求导涉及除法
- 反向传播很容易出现梯度消失的情况从而无法完成深层网络的训练
- sigmoid收敛缓慢
- sigmoid函数饱和且kill掉梯度
Tanh 函数
取值范围为[-1,1]
tanh在特征相差明显时的效果会很好,在循环过程中会不断扩大特征效果。
与sigmod的区别是 tanh 是0 的均值,因此在实际应用中tanh会比sigmod更好。
在具体应用中,tanh函数相比于Sigmoid函数往往更具有优越性,这主要是因为Sigmoid函数在输入处于 [-1,1]之间时,函数值变 化敏感,一旦接近或者超出区间就失去敏感性,处于饱和状态。
-
为嘛使用激活函数?
- 不使用激活函数,每一层输出都是上层输入的线性函数,无论神经网络有多少层,输出都是输入的线性组合。
- 使用激活函数,能够给神经元引入非线性因素,使得神经网络可以任意逼近任何非线性函数,这样神经网络就可以利用到更多的非线性模型中。
-
激活函数需要具备以下几点性质:
- 连续并可导(允许少数点上不可导)的非线性函数。可导的激活函数可以直接利用数值优化的方法来学习网络参数。
- 激活函数及其导函数要尽可能的简单,有利于提高网络计算效率。
- 激活函数的导函数的值域要在一个合适的区间内,不能太大也不能太小,否则会影响训练的效率和稳定性。
-
最后就是输出层,输出层与隐藏层是什么关系?
其实隐藏层到输出层可以看成是一个多类别的逻辑回归,也即softmax回归,所以输出层的输出就是softmax(W2X1+b2),X1表示隐藏层的输出f(W1X+b1)。MLP整个模型就是这样子的,上面说的这个三层的MLP用公式总结起来就是,函数G是softmax。
因此,MLP所有的参数就是各个层之间的连接权重以及偏置,包括W1、b1、W2、b2。对于一个具体的问题,怎么确定这些参数?求解最佳的参数是一个最优化问题,解决最优化问题,最简单的就是梯度下降法了(SGD):首先随机初始化所有参数,然后迭代地训练,不断地计算梯度和更新参数,直到满足某个条件为止(比如误差足够小、迭代次数足够多时)。这个过程涉及到代价函数、规则(Regularization)、学习速率(learning rate)、梯度计算等
实现
获取数据及可视化
-
获取数据集
from keras.datasets import mnist #导入数据集 #数据集分训练集(图片和对应的值)和测试集(图片和对应的值) (x_train, y_train), (x_test, y_test) = mnist.load_data()
-
查看数据
x_train.shape
-
部分数据的可视化
#导入可视化库 import matplotlib.pyplot as plt img1 = x_train[0] #设置显示框的大小 fig = plt.figure(figsize=(3, 3)) #显示img1这个图片 plt.imshow(img1) #可视框的标题设置成对应图片的数字 plt.title(y_train[0]) #显示可视化窗口 plt.show()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mTkgRvXJ-1639243943389)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20211211214628557.png)]
数据预处理
# 显示图片的大小
print(img1.shape)
# 维度转换对图片的维度进行转换
feature_size = img1.shape[0] * img1.shape[1]
# 填写
'''
reshape()维度转化函数
'''
x_train_format = x_train.reshape(x_train.shape[0], feature_size)
x_test_format = x_test.reshape(x_test.shape[0], feature_size)
print(x_train_format.shape)
# 对图像进行归一化处理
x_train_normal = x_train_format / 255
# 待填写
x_test_normal = x_test_format / 255
# 对输出结果进行格式转换
from keras.utils import to_categorical
#from tensorflow.keras.utils import to_categorical
#将类别向量转换为二进制(只有0和1)的矩阵类型表示。
#对测试集和训练集的y都进行转化
y_train_format = to_categorical(y_train)
# 待填写
y_test_format = to_categorical(y_test)
print(y_train[0])
print(y_train_format[0])
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ke4PMGfb-1639243943390)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20211212010747549.png)]
归一化处理
-
为什么要进行数据预处理
- 任何收集而来的庞大数据往往是不可能一拿到就可以立马用得上的,比如一些数值大的数据,计算量复杂度高,不容易收敛,很难进行统计处理。
- 数据不符合正态分布,无法做一些符合正态分布的数学分析。
所以为了对数据进行更好的利用,我们需要使数据标准化。
-
数据标准化
数据无量纲化处理主要解决数据的可比性。数据标准化的方法有很多种,常用的有“最小—最大标准化”、“Z-score标准化”和“按小数定标标准化”等。经过上述标准化处理,原始数据均转换为无量纲化指标测评值,即各指标值都处于同一个数量级别上,可以进行综合测评分析。最常用的数据归一化处理,即将数据统一映射到[0,1]区间上。
-
归一化的目标
- 把数据转换为(0,1)区间的小数, 主要是为了数据处理方便提出来的,把数据映射到0~1范围之内处理,更加便捷快速。
- 把有量纲表达式变为无量纲表达式,解决数据的可比性。
-
归一化的优点
-
归一化后加快了梯度下降求最优解的速度,如果机器学习模型使用梯度下降法求最优解时,归一化往往非常有必要,否则很难收敛甚至不能收敛。
-
归一化有可能提高精度,一些分类器需要计算样本之间的距离(如欧氏距离),例如KNN。如果一个特征值域范围非常大,那么距离计算就主要取决于这个特征,从而与实际情况相悖(比如这时实际情况是值域范围小的特征更重要)。
-
哪些算法并不需要归一化
概率模型(树形模型)不需要归一化,因为它们不关心变量的值,而是关心变量的分布和变量之间的条件概率,如决策树、RF。而像Adaboost、SVM、LR、Knn、KMeans之类的最优化问题就需要归一化。
-
-
建立模型
# 导入所需要的库
from keras.models import Sequential
from keras.layers import Dense, Activation
'''
选择模型
Keras有两种类型的模型,序贯模型(Sequential)和函数式模型(Model),函数式模型应用更为广泛,序贯模型是函数式模型的一种特殊情况。
a)序贯模型(Sequential):单输入单输出,一条路通到底,层与层之间只有相邻关系,没有跨层连接。这种模型编译速度快,操作也比较简单
b)函数式模型(Model):多输入多输出,层与层之间任意连接。这种模型编译速度慢。
'''
mlp = Sequential()
# 待填写
'''
构建神经网络
共3层神经网络
输入层:输入:图片的维度大小 输出:392个 (下一层的输入) 激活函数:relu
隐藏层:输入392个 输出392个 激活函数:relu
输出层:输入392个 输出10个 激活函数softmax
'''
mlp.add(Dense(units=392, activation='relu', input_dim=feature_size))
mlp.add(Dense(units=392, activation='relu'))
mlp.add(Dense(units=10, activation='softmax'))
# 编译 损失函数 mae(平均绝对误差)优化器:sgd
mlp.compile(loss='mean_absolute_error', optimizer='sgd')
# 输出模型各层的参数状况
mlp.summary()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xF1KKPQU-1639243943392)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20211212011009147.png)]
注:最后一层输出的十个数是0~9这十个数可能的概率他们相加1
模型训练
# epochs执行次数一个迭代器 一般来说数值越高准确率会高一点
mlp.fit(x_train_format, y_train_format, epochs=50)
训练集
# 导入numpy库
import numpy as np
# 输入训练集x的值对y进行预测
y_train_predict = mlp.predict(x_train_normal)
# 找出0~9十个概率之中最大的那一个数
y_train_predict = np.argmax(y_train_predict, axis=1)
#打印输出预测结果
print(y_train_predict)
# 计算准确率
from sklearn.metrics import accuracy_score
# 待填写
# 调用三方库计算准确率(参数真实的y值和预测y值)
accuracy_train = accuracy_score(y_train, y_train_predict)
# 打印输出
print(accuracy_train)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N9Wv9c4O-1639243943393)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20211212011534318.png)]
测试集
# 输入测试集x的值对y进行预测
y_test_predict = mlp.predict(x_test_normal)
# 找出0~9十个概率之中最大的那一个数
y_test_predict = np.argmax(y_test_predict, axis=1)
# 调用三方库计算准确率(参数真实的y值和预测y值)
accuracy_test = accuracy_score(y_test, y_test_predict)
# 打印输出
print(accuracy_test)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cnryz857-1639243943395)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20211212011558467.png)]
可视化
# mg2图片为x_test[10]
mg2 = x_test[10]
# 可视化窗口大小3*3
fig = plt.figure(figsize=(3, 3))
# 显示图片
plt.imshow(mg2)
# 可视化窗口的title为图片的预测值
plt.title(y_test_predict[10])
# 显示可视化窗口
plt.show()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GE7cGeqS-1639243943396)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20211212012019138.png)]
小结
实验最后我预测出来的结果是86%(epoch设为5 ),在epochs=5000的时候正确率能达到96%97%的样子但模型跑的时间会很久早上去上课的时候开始训练中午回去的时候已经训练完了
任何一个模型的准确率都达不到100%,就像空想社会那样想法很好,不可能实现,我们能做的是选择比较好的激活函数和损失函数使得loss足够小(预测值和真实值的差距)