背景介绍
- 鸢尾花数据集有150行,每行一个样本,样例如下,总共有三类,详见下表
搭建步骤
一、导入Keras模型库,创建模型对象,构建网络
from keras.models import Sequential
from keras.layers import Dense,Dropout
# 构建一个模型对象
model = Sequential()
# 下述为隐藏层1
# 首层必须指明每一层神经元的输入,这里指定每一个神经元接收四个数据,每一层是十六个神经元
model.add(Dense(16,activation='relu',input_shape=(4,)))
# 下述为隐藏层2
model.add(Dense(16,activation='relu'))
# 断开一些神经元的链接,防止过度拟合
model.add(Dropout(0.25))
# 增加输出层
# 多分类的输出层的激活函数要选择softmax,返回一个一个由多个概率值组成的数组
model.add(Dense(3,activation='softmax'))
# 配置网络
model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=["accuracy"])
Keras对象实例
- keras总共有两种模型
Keras中Sequential模型增加网络层
- 增加卷积层
model.add(Conv2D(64, (3, 3), activation='relu'))
- 增加最大池化层
model.add(MaxPooling2D(pool_size=(2, 2)))
- 增加全连接层
model.add(Dense(256, activation='relu'))
- 增加dropout层
model.add(Dropout(0.5))
- 增加展平层
model.add(Flatten())
每一层的神经元有什么数量上的限制吗?
- 输入层的神经元数量,等于待处理数据中输入变量的数量:几个输入,输入层就有几个神经元
- 输出层的神经元数量等于与每个输入关联的输出的数量:几个输出,输出层就有几个神经元
隐藏层的大文章
- 关于隐藏层层数
- 没有隐藏层,进能够表示线性可分的函数或者决策
- 隐藏层数为1,可以拟合从一个有限空间到另外一个有限空间的连续映射
- 隐藏层数为2,搭配适当的激活函数可以表示任意精度的任意决策边界,可以拟合任何精度的任何平滑映射
- 隐藏层数大于2:多出来的隐藏层可以学习复杂的描述(某种自动特征工程)
- 总结:
- 层数越深,拟合函数的能力增强,但是会出现过拟合的情况,增加训练难度,模型难以收敛
- 最好参照以优的表现有意的模型,实在没有,根据表格一个一个试。最好,尝试迁移和微调已有的预训练模型,能取得事半功倍的效果。
- 关于隐藏层的神经元数量
- 通常,对所有的隐藏层,使用相同数量的神经元就足够了。
- 与其添加每一层的神经元个数,不如添加总共的神经元的层数
- 具体原则:
- 隐藏神经元的数量应在输入层的大小和输出层的大小之间。
- 隐藏神经元的数量应为输入层大小的2/3加上输出层大小的2/3。
- 隐藏神经元的数量应小于输入层大小的两倍。
关于几个激活函数的理解
- relu函数:修正线性单元,斜坡函数的为代表的非线性函数
- 优势:更加有效率的梯度下降以及反向传播:避免了梯度爆炸和梯度消失问题,更像人脑。
- softmax函数:
- 补充:二分类通过逻辑回归算法实现,其关键步骤是将线性模型输出的实数域映射到由0到1表示的概率分布的有小时数空间
- 功能1:将预测结果转化为非负数,通过指数函数,将实数输出映射到零到正无穷
- 功能2:将各种预测结果的概率之和等于1,将所有结果相加,进行归一化
二、配置深度学习神经网络,并根据参数对网络进行编译
# 配置网络
model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=["accuracy"])
compile函数说明
看一下整个流程图,已经搭建了网络,训练还需要进行指定损失函数和优化器,从而反过来更新和优化权重
函数原型以及介绍
model.compile(loss,optimizer,metrics)
- loss损失函数,用来计算真实值和预测值之间的误差,以便优化器有效的调整权重。常见的损失函数如下极其作用
- 均方差损失:机器学习、深度学习回归任务中最常用的一种损失函数,又称为L2 loss。模型预测与真实值之间的误差服从标准高斯分布[公式]
- 平方绝对误差损失:L1 loss,模型预测与真实值之间的误差服从拉普拉斯分布 Laplace distribution
- 交叉熵损失:常用于分类问题,最常用的损失函数
- 针对二分类问题,常常使用的Sigmoid函数,将模型的输出压缩到(0,1)区间
- 多分类问题,常常使用Sigmax函数
- optimizer,优化器,参数学习算法,以梯度下降算法为基础的方法由SGD,Adam。这个有点难,没时间看了,大部分人推荐Adam比较快,看了一些,觉得这个大神写的很好,可以看一下,传送门
- metrics:监控指标列表,包含评估模型在训练和测试时的性能指标,分类一般只关注精度
三、准备数据
对数据进行的预处理,符合网络要求的类型,形状和数据分布
向量化
-
神经网络中的所有输入和目标都必须是浮点数或整数张量(即多维数据),在本例中,数据是1D张量
-
张量:张量可以看作是一个多维数组。标量可以看作是0维张量,向量可以看作1维张量,矩阵可以看作是二维张量。理解详见
-
实现代码:
import pandas as pd
from sklearn.model_selection import train_test_split
from keras.utils import np_utils
# 读入数据文件
# pandas库的读取文件,header指明引入的文档是没有的列的,自动编号
data = pd.read_csv('iris.data', header = None)
# 指定数据的各个列的代表的数据,花萼长度,花萼宽度,花瓣长度,花瓣宽度,种类,设置各个类的标签属性
data.columns = ['sepal length', 'sepal width','petal length','petal width','class']
# 查看5前五条数据
# loc是以列名和行名作为参数,进行返回的
# iloc是以索引作为参数进行返回地
print(data.iloc[0:5,:])
# 数据特征x取值于前四列
# 按照序号,自动的取出0列到第四列的所有的数据,并将对应的数据转成浮点类型
X = data.iloc[:,0:4].values.astype(float)
# 将类名转换为整数
# 利用loc函数,根据某个数据来提取数据所在的行
# 提取所有为class为对应类别的行,并将对应行的class数据提取出来置为对应的0到2
data.loc[data['class'] == 'Iris-setosa','class'] = 0
data.loc[data['class'] == 'Iris-versicolor','class'] = 1
data.loc[data['class'] == 'Iris-virginica','class'] = 2
# 标签y取值于第五列
# 将第四列取出来,作为int型
y = data.iloc[:,4].values.astype(int)
# 分割数据为训练集和测试集
# 八成为训练集,两成为测试集
train_x,test_x,train_y,test_y = train_test_split(X,y,train_size=0.8,test_size=0.2,random_state=0)
pands包的loc和iloc
- loc函数,通过index中的具体的值来取数据,你自己命名和定义的
data.loc[data['class'] == 'Iris-setosa','class'] = 0
* 将class为对应化名的所有行数据提取出来
* 然后将对应行数据class列的值全部置为0,1,2
- iloc是根据index索引值,默认标的序号进行取值的,data.iloc[:,:],写出来,前面是行数指定,后面是列数指定
X = data.iloc[:,0:4].values.astype(float)
* 将所有行的第0到第4列,左闭右开的数据全部提取出来,将之转成float型
- 具体的请看博客
标准化
- 将数据根据自身一定比例进行处理,使之落入一个小的特定的区间。因为取值范围差异大容易造成训练不收敛。
- 标准差标准化z-score:每个特征标准化为1,标准差为0的数据分布
- 标准差标准化z-score:每个特征标准化为1,标准差为0的数据分布
- 实现代码:
# 特征数据标准化,转换为均值0,标准差为1的分布
mean=train_x.mean(axis=0)
std=train_x.std (axis=0 )
train_x= (train_x-mean) /std
test_x=(test_x-mean) /std
print (train_x[0 : 5, : ])
类别标签独热编码
多分类使用损失函数categorical_crossentropy,标签必须是多类模式,即one-hot编码的向量,不是单个数值
- 离散特征的两种编码:
- 离散特征值之间没有意义,color[red,blue],就是用独热编码
- 离散特征值之间有意义,size[x,xx,xxxl],就使用数值的映射{X:1,XL:2,XXL:3}
- 什么时候使用独热编码
- 解决类别型数据的离散值问题时,使用独热编码
- 如果分类的类别数不是很多,建议使用独热编码
- 这里有一个讲的挺详细的,博客
- 实现代码:
# 类别标签独热编码
train_y_ohe = np_utils.to_categorical(train_y,3)
test_y_ohe = np_utils.to_categorical(test_y, 3)
print("前5条测试数据标签值:", test_y[0:5])
print('前五条测试数据标签的独热码:\n',test_y_ohe[0:5])
四、模型训练
实现源码:
model.fit(train_x, train_y_ohe, epochs=50, batch_size=1, verbose = 2, validation_data =(test_x,test_y_ohe))
- 函数原型:
model.fit( x, y,batch_size=32,epochs=10,verbose=1,callbacks=None,validation_split=0.0,
validation_data=None, shufflc=True, class_weight=None, sample_weight=None,initial_epoch=0)
- 参数说明:
- x:输入数据,如果模型只有一个输入,那么x的类型就是numpy array,如果模型由多个输入,那么x的类型就是list,list元素对应于各个输入的numpy array
- y:标签,numpy array元素
- batch_size:整数,指进行梯度下降时,每一个batch包含的样本数。训练时,一个batch的样本会被计算一次梯度下降,更新依次权重。这里不是很懂?
- epochs:整数,训练迭代的次数
- verbose:日志显示,0为不在标准输出流输出日志信息,1为输出进度记录,2为每一个epoch输出一行记录
- validation_data:指定验证集,是以元组的形式进行输入(验证集的输入,验证集的标签)
batch_size是什么?
-
一个完整的流程是输入并将计算loss,反向传播,修改误差。batch_size指这样一个完整的过程是多少个样例在进行,依次输入一个,还是一次输入五十个。
-
这里有一个不错的理解,直接复制过来了。
The batch size defines the number of samples that will be propagated through the network. For instance, let's say you have 1050 training samples and you want to set up a batch_size equal to 100. The algorithm takes the first 100 samples (from 1st to 100th) from the training dataset and trains the network. Next, it takes the second 100 samples (from 101st to 200th) and trains the network again. We can keep doing this procedure until we have propagated all samples through of the network. Problem might happen with the last set of samples. In our example, we've used 1050 which is not divisible by 100 without remainder. The simplest solution is just to get the final 50 samples and train the network.
五、模型的性能评价和预测分析
实现源码
# 模型评估
loss, accuracy = model.evaluate(prepareData.test_x, prepareData.test_y_ohe, verbose=2)
print("loss ={},accuracy={}".format(loss,accuracy))
函数模型
loss,accuracy = model.evaluate(X_test,Y_test,verbose);
- loss预测标签和目标标签之间的损失值
- accuracy:精确度
- X_test:测试集数据
- Y_test:测试集标签
- verbose:日志显示,0为不在标准输出流输出日志信息,1为输出进度条记录,2为每个epoch都输出一行记录
六、预测应用
# 模型预测
classes = modelConstruct.model.predict(prepareData.test_x, batch_size=1, verbose=2)
print("测试样本数: ",len(classes))
函数原型
model.predict(X_test,batch_size,verbose);
- X_test:测试集数据
- batch_size:整数,指定每一次训练时输入的样本数量
- verbose:日志显示,0为不在标准输出流输出日志信息,1为输出进度条记录,2为每个epoch都输出一行记录
自自我改进
查看当前两个隐藏层一个输出层的测试结果
三个隐藏层
四个隐藏层
五个隐藏层
八个隐藏层
结论
随着隐藏层的增加,损失值先上升在下降,最具会先聚合在分散,就是所谓的过拟合,最好隐藏层数应该是四个。这个是一个一个试出来,其实并不知道内部的具体的过程。
整个网络的搭建过程
完整的程序
- modelConstruct.py文件
from keras.models import Sequential
from keras.layers import Dense,Dropout
# 构建一个模型对象
model = Sequential()
# 堆叠若干网络层构建网络
# dense当前层为全连接层
# 16为当前层的节点数
# activate激活函数,之名为relu
# input_shape输入数据的维度,用元组表示,首层必须说明
# 下述为隐藏层1
model.add(Dense(16,activation='relu',input_shape=(4,)))
# 下述为隐藏层2
model.add(Dense(16,activation='relu'))
model.add(Dense(16,activation='relu'))
model.add(Dense(16,activation='relu'))
model.add(Dense(16,activation='relu'))
model.add(Dense(16,activation='relu'))
model.add(Dense(16,activation='relu'))
model.add(Dense(16,activation='relu'))
# 断开一些神经元的链接,防止过度拟合
# 隐藏层随机失活25%,为什么是随机失活,每一次都是随机的?
model.add(Dropout(0.25))
# 增加输出层
# 多类型的输出值,指定激活函数为softmax,返回一个由多个概率值组成的数组,每个概率值表示输出为某类的概率
# 三中类型的输出,
model.add(Dense(3,activation='softmax'))
# 配置网络
# 指定的损失函数是交叉熵损失函数
# 优化参数的方式是梯度下降发
# 监控指标列表
model.compile(loss='categorical_crossentropy',optimizer='adam',metrics=["accuracy"])
- prepareData.py文件
import pandas as pd
from sklearn.model_selection import train_test_split
from keras.utils import np_utils
# 读入数据文件
# pandas库的读取文件,header指明引入的文档是没有的列的,自动编号
data = pd.read_csv('iris.data', header = None)
# 指定数据的各个列的代表的数据,花萼长度,花萼宽度,花瓣长度,花瓣宽度,种类,设置各个类的标签属性
data.columns = ['sepal length', 'sepal width','petal length','petal width','class']
# 查看5前五条数据
# loc是以列名和行名作为参数,进行返回的
# iloc是以索引作为参数进行返回地
print(data.iloc[0:5,:])
# 数据特征x取值于前四列
X = data.iloc[:,0:4].values.astype(float)
# 将类名转换为整数
data.loc[data['class'] == 'Iris-setosa','class'] = 0
data.loc[data['class'] == 'Iris-versicolor','class'] = 1
data.loc[data['class'] == 'Iris-virginica','class'] = 2
# 标签y取值于第四列
# 将第四列取出来,作为int型
y = data.iloc[:,4].values.astype(int)
# 分割数据为训练集和测试集
# 八成为训练集,两成为测试集
train_x,test_x,train_y,test_y = train_test_split(X,y,train_size=0.8,test_size=0.2,random_state=0)
# 特征数据标准化,转换为均值0,标准差为1的分布
mean=train_x.mean(axis=0)
std=train_x.std (axis=0 )
train_x= (train_x-mean) /std
test_x=(test_x-mean) /std
print (train_x[0 : 5, : ])
# 类别标签独热编码
train_y_ohe = np_utils.to_categorical(train_y,3)
test_y_ohe = np_utils.to_categorical(test_y, 3)
print("前5条测试数据标签值:", test_y[0:5])
print('前五条测试数据标签的独热码:\n',test_y_ohe[0:5])
- test.py文件
import modelConstruct
import prepareData
modelConstruct.model.fit(prepareData.train_x, prepareData.train_y_ohe, epochs=50
, batch_size=1, verbose = 2, validation_data = (prepareData.test_x, prepareData.test_y_ohe))
# 返回记录的是各轮训练的情况,时间,训练集损失值,训练集精确度,验证集损失值,验证集精确率
# 模型评估
loss, accuracy = modelConstruct.model.evaluate(prepareData.test_x, prepareData.test_y_ohe, verbose=2)
print("loss ={},accuracy={}".format(loss,accuracy))
# 模型预测
classes = modelConstruct.model.predict(prepareData.test_x, batch_size=1, verbose=2)
print("测试样本数: ",len(classes))