目录
绘制训练集和检验集f1_score随隐藏层神经元数量变化的趋势
机器算法分类
感知器图示
- 输入相当于神经元的树突
- 计算和输出相当于轴突
- 加权求和相当于神经元中接收信号的累积
- 非线性变化相当于产生输出信号的阈值函数,被称作激活函数
- 输入与输出之间有一条加权链接,相当于突触
链接是神经元中最重要的部分,一个神经网络的训练算法就是不断调整链接的权重,以使得整个网络的预测效果最好
多层人工神经网络
- 多层人工神经网络由输入层、输出层、隐藏层构成
- 多层神经网络的结构
输入层
- 输入层的每个神经元与一个属性相连
- 作用是将属性值映射到合理的范围,例如[−1,1]
隐藏层(hidden layer)
- 输入层与输出层之间包含的中间层
- 引入隐藏层使得人工神经网络,相对于简单的感知器,能够处理更加复杂的分类、预测问题
- 隐藏层中的每个神经元通常与输入层的所有神经元相连
- 可以有任意多个隐藏层,但是一般一个隐藏层就可以
- 隐藏层越宽(包含更多的神经元),模式识别的能力越强,但是越容易出现过拟合,因此隐藏层不应当太宽
隐藏层就像是我们大脑中的处理中心,它们接收来自输入层的信号,对这些信号进行加工和处理,然后再将处理后的信号传递给输出层。隐藏层可以提取输入数据的某些特征或模式,并将其组合成更高层次的信息,这样可以让神经网络学会更复杂的任务。
隐藏层的作用是在输入数据和最终输出之间建立一种复杂的映射关系。通过这种映射,神经网络可以学习和理解数据中的复杂结构和模式,从而完成诸如图像识别、语言处理等复杂的任务。
输出层
- 输出层的每个神经元与隐藏层的所有神经元相连
- 输出层的作用:将神经网络计算的结果转换为能够理解的结果
- 输出层一般有一个神经元(对应一个计算值),但也可以有多个神经元
人工神经网络示例
例子1:房地产评估
- 根据房屋的特征,估计房屋的价值
- 建立的三层神经网络模型(所有数值是虚拟,为了展示)
- 假设有一个隐藏层,包含两个隐藏神经元
每个神经元可以对应一个输入特征(例如,如果我们有两个输入特征,就可以有两个神经元)。每个神经元会接收来自输入层的信号,并通过对这些信号进行加权求和再加上一个偏置值,然后通过一个非线性激活函数(如ReLU或Sigmoid)来处理这些信号。
隐藏层中的每个神经元都会学习如何将输入特征组合起来,以产生对房价有用的特征表示。这些特征表示可以捕捉到输入数据中的复杂关系,从而使网络能够更准确地预测房价。
最后,隐藏层的所有输出会汇总到输出层的一个神经元中,该神经元会生成最终的房价预测。通过训练过程中不断调整网络的权重和偏置值,神经网络最终能够学会如何根据输入特征来预测房价。
例子2:输出层含有多个神经元
一家百货公司想要预测顾客购买不同部门商品的可能性:服装、家具、电子产品,以便利用这些信息进行促销
- 每个部门对应一个输出,反映从该部门购买的可能性
Titanic生存的人工神经网络模型的实现
- 读入数据,titanic生存分类
titDfRaw = pd.read_csv('./data/analysis/train.csv')
titDfRaw.head()
数据预处理
titDfRaw.set_index('PassengerId',inplace=True)
titDf = titDfRaw.loc[:,['Survived','Pclass','Sex','Age','SibSp','Parch',
'Fare','Embarked']]
titDf.head()
titDf.shape
删除含有缺失值的行
titDf.dropna(axis=0,how='any',inplace=True)
划分预测属性与分类属性
titX = titDf.iloc[:,1:]
titX.head()
titY = titDf['Survived']
titY.head()
1:
表示选择从第一列(索引为1,因为Python中索引从0开始)开始的所有列,不包括第一列本身,只包含原DataFrame中索引从1到最后一列的数据。
对于分类预测属性进行one-hot
编码
titXOH = pd.get_dummies(titX,columns=['Sex','Embarked'])
titXOH.head()
建立神经网络模型
-
用反向传播算法实现的多层神经网络
from sklearn.neural_network import MLPClassifier
MLPClassifier(hidden_layer_sizes=(100, ), activation='relu', solver='adam',
learning_rate='constant')
hidden_layer_sizes
:元组tuple
类型,隐藏层设置,元组的元素个数代表隐藏层的数量,每一个元素值代表对应隐藏层中的神经元数量- 例如,
(100,50)
表示有2个隐藏层,第一个隐藏层含有100个神经元,第二个隐藏层含有50个神经元
- 例如,
-
activation:
str
类型,激活函数,决定了神经元是如何处理输入的。常见的激活函数包括
solver
:str
类型,权重优化的算法,定义了优化问题的求解器,它用于在训练过程中最小化损失函数。可选的优化算法包括,adam(
随机梯度下降,带有动量和平滑项):对于大规模数据集(训练集包含的样本数量达到几千或者上万条)效果好,默认值lbfgs
:对于小规模数据集效果好
learning_rate
:str
类型,学习率,在优化过程中控制算法更新模型参数的步长大小的一个值,决定了每一步更新是如何改变模型中的权重(weights)和偏置(biases)的。constant
:恒定学习率,默认值invscaling
:逐步降低学习率
titAnn = MLPClassifier(hidden_layer_sizes=(2,),solver='lbfgs')
titAnn
# 一个隐藏层,包含2个隐藏神经元
训练神经网络模型
划分训练集与检验集
titTrainX,titTestX,titTrainY,titTestY = train_test_split(titXOH,titY,test_size=0.3,random_state=10)
在训练集上训练模型
titAnn.fit(titTrainX,titTrainY)
查看MLPClassifier的属性
titAnn.classes_
classes_
:输出的类标签,形状是[n_classes]
titAnn.coefs_
coefs_
:list
类型,第i个元素代表从第i层到第i+1层的权重
titAnn.n_layers_
n_layers_
:int
类型,神经网络包含的层数量
titAnn.n_outputs_
n_outputs_
:int
类型,输出的个数
titAnn.out_activation_
out_activation_
:str
类型,输出神经元的激活函数
检验神经网络在训练集上的分类性能
titTrainYPre = titAnn.predict(titTrainX) #对训练集样本的类别预测
print(f'神经网络在训练集上的准确率是
{metrics.accuracy_score(titTrainY,titTrainYPre):.3f}')
print(f'神经网络在训练集上对于未生还(类标签是0)的召回率是
{metrics.recall_score(titTrainY,titTrainYPre,pos_label=0):.3f}')
print(f'神经网络在训练集上对于生还(类标签是1)的召回率是
{metrics.recall_score(titTrainY,titTrainYPre,pos_label=1):.3f}')
print(f'神经网络在训练集上对于未生还(类标签是0)的精确率是
{metrics.precision_score(titTrainY,titTrainYPre,pos_label=0):.3f}')
print(f'神经网络在训练集上对于生还(类标签是1)的精确率是
{metrics.precision_score(titTrainY,titTrainYPre,pos_label=1):.3f}')
print(f'神经网络在训练集上对于未生还(类标签是0)的f1_score是
{metrics.f1_score(titTrainY,titTrainYPre,pos_label=0):.3f}')
print(f'神经网络在训练集上对于生还(类标签是1)的f1_score是
{metrics.f1_score(titTrainY,titTrainYPre,pos_label=1):.3f}')
检验神经网络在检验集上的分类性能
titTestYPre = titAnn.predict(titTestX) #预测检验集样本类别
print(f'神经网络在检验集上的准确率是
{metrics.accuracy_score(titTestY,titTestYPre):.3f}')
print(f'神经网络在检验集上对于未生还(类标签是0)的f1_score是
{metrics.f1_score(titTestY,titTestYPre,pos_label=0):.3f}')
print(f'神经网络在检验集上对于生还(类标签是1)的f1_score是
{metrics.f1_score(titTestY,titTestYPre,pos_label=1):.3f}')
在训练之前对预测属性进行标准化
在机器学习和数据分析中,标准化(Standardization)是一种重要的数据预处理技术,它用于将特征缩放到一个统一的尺度,以便于模型训练和提高模型的性能。
标准化预测属性通常指的是对用于预测的属性(特征)进行规范化处理,以确保这些属性在模型训练和预测过程中具有可比性和可解释性,让所有预测属性的数值范围可比
在训练之前标准化预测属性,对于神经网络建模非常重要
from sklearn import preprocessing
preprocessing.scale(X,copy=True)
X
:需要标准化的数据copy
:是否直接标准化原始数据
preprocessing.scale(titTrainX)
- 虽然可以实现对训练集预测属性的标准化,但是这种标准化规则不能轻易应用到检验集上
在训练集上计算均值和标准差,然后将其应用到检验集上
scaler = preprocessing.StandardScaler(copy=True, with_mean=True,
with_std=True) # 建立标准化模型
scaler.fit(data) # 训练模型
scaler.transform(data) # 转变原始数据
这段代码创建了一个StandardScaler
对象,该对象可以对数据集的特征进行标准化处理,即缩放每个特征到具有零均值和单位方差的分布
-
copy=True
: 这个参数指示在缩放数据时是否复制原始数据。如果设置为True
(默认值),那么在缩放过程中会创建数据的一个副本,从而不会更改原始数据。如果设置为False
,则直接在原始数据上进行缩放,这可能会节省内存,但原始数据会因此被改变。 -
with_mean=True
: 这个参数控制是否在缩放过程中使用数据的平均值。如果设置为True
(默认值),则每个特征都会除以它的平均值。这样做的目的是去除数据的均值,使数据具有零均值特性,这在某些机器学习算法中是很有用的,比如在神经网络中。 -
with_std=True
: 这个参数控制是否在缩放过程中使用数据的标准差。如果设置为True
(默认值),则每个特征都会除以它的标准差。这样做的目的是不仅去除数据的均值,还去除数据的单位方差,使数据在数值上更加一致,有助于某些算法(如线性模型)的准确性。
titScaler = preprocessing.StandardScaler()
titScaler.fit(titTrainX)
titTrainXScale = titScaler.transform(titTrainX)
#简洁的用法:先训练,然后转变原始数据
#titScaler.fit_transform(titTrainX)
重新训练神经网络模型
在训练集上训练模型
titAnn.fit(titTrainXScale,titTrainY)
预测训练集样本类别
titTrainYPreNew = titAnn.predict(titTrainXScale)
pd.Series(titTrainYPreNew).value_counts()
# 训练集上真实类别分布
pd.Series(titTrainY).value_counts()
神经网络在训练集上的分类性能
print(f'神经网络在训练集上的准确率是
{metrics.accuracy_score(titTrainY,titTrainYPreNew):.3f}')
print(f'神经网络在训练集上对于未生还(类标签是0)的f1_score是
{metrics.f1_score(titTrainY,titTrainYPreNew,pos_label=0):.3f}')
print(f'神经网络在训练集上对于生还(类标签是1)的f1_score是
{metrics.f1_score(titTrainY,titTrainYPreNew,pos_label=1):.3f}')
神经网络在检验集上的分类性能
titTestXScale = titScaler.transform(titTestX) #标准化检验集数据
titTestYPreNew = titAnn.predict(titTestXScale) #预测检验集类别
print(f'神经网络在检验集上的准确率是
{metrics.accuracy_score(titTestY,titTestYPreNew):.3f}')
print(f'神经网络在检验集上对于未生还(类标签是0)的f1_score是
{metrics.f1_score(titTestY,titTestYPreNew,pos_label=0):.3f}')
print(f'神经网络在检验集上对于生还(类标签是1)的f1_score是
{metrics.f1_score(titTestY,titTestYPreNew,pos_label=1):.3f}')
metrics.plot_precision_recall_curve(titAnn,titTestXScale,titTestY)
寻找合适的隐藏层设置
增加隐藏层神经元数量的效果
-
利用训练集和检验集的f1_score寻找隐藏层的设置
trainf1Lst = [] # 记录训练集上准确率的列表
testf1Lst = [] # 记录检验集上准确率的列表
for each in range(1,51):
# 隐藏层包含的神经元数量从1以1为步长增加到50
tmpAnn = MLPClassifier(hidden_layer_sizes=(each,),solver='lbfgs',
activation='relu',learning_rate='constant',max_iter=1000)
tmpAnn.fit(titTrainXScale,titTrainY) # 在训练集上训练神经网络
trainYPre = tmpAnn.predict(titTrainXScale) # 预测训练集上每个样本的类别
trainf1Lst.append(metrics.f1_score(titTrainY,trainYPre))
testYPre = tmpAnn.predict(titTestXScale) # 预测检验集上每个样本的类别
testf1Lst.append(metrics.f1_score(titTestY,testY Pre))
-
绘制训练集和检验集f1_score随隐藏层神经元数量变化的趋势
titAnnDf = pd.DataFrame({'hiddenNo':range(1,51),'train':trainf1Lst,'test':testf1Lst})
ax = titAnnDf.plot('hiddenNo','train',kind='line',marker='o',figsize=(12,6))
_ = titAnnDf.plot('hiddenNo','test',kind='line',marker='d',ax=ax)
_ = ax.set(title='隐藏层神经元数量 v.s. f1_score',ylabel='f1_score',xlabel='隐藏层神经元数量')
# 显示f1_score在训练集和检验集上均合适的隐藏层数量
titAnnDf.loc[abs(titAnnDf['train']-titAnnDf['test'])==
min(abs(titAnnDf['train']-titAnnDf['test'])),:]
重新训练模型
titAnn8 = MLPClassifier(hidden_layer_sizes=(8,),solver='lbfgs',
activation='relu',learning_rate='constant',max_iter=3000)
titAnn8.fit(titTrainXScale,titTrainY)
titAnnTrainDisp = metrics.plot_roc_curve(titAnn,titTrainXScale,titTrainY,
label='original')
metrics.plot_roc_curve(titAnn8,titTrainXScale,titTrainY,
label='hidden no is 8',ax=titAnnTrainDisp.ax_)
titAnnTestDisp = metrics.plot_roc_curve(titAnn,titTestXScale,titTestY,
label='original')
metrics.plot_roc_curve(titAnn8,titTestXScale,titTestY,
label='hidden no is 8',ax=titAnnTestDisp.ax_)
增加隐藏层数量的效果
trainf1LstNew = [] # 记录训练集上准确率的列表
testf1LstNew = [] # 记录检验集上准确率的列表
for each in range(1,11):
# 第二个隐藏层包含的神经元数量从1以1为步长增加到10
tmpAnn = MLPClassifier(hidden_layer_sizes=(8,each),solver='lbfgs',
activation='relu',learning_rate='constant',max_iter=3000)
tmpAnn.fit(titTrainXScale,titTrainY) # 在训练集上训练神经网络
trainYPre = tmpAnn.predict(titTrainXScale) # 预测训练集上每个样本的类别
trainf1LstNew.append(metrics.f1_score(titTrainY,trainYPre))
testYPre = tmpAnn.predict(titTestXScale) # 预测检验集上每个样本的类别
testf1LstNew.append(metrics.f1_score(titTestY,testYPre))
titAnn2HiddenDf =
pd.DataFrame({'hiddenNo':range(1,11),'train':trainf1LstNew,'test':testf1LstNew})
ax = titAnn2HiddenDf.plot('hiddenNo','train',kind='line',marker='o',figsize=(12,6))
_ = titAnn2HiddenDf.plot('hiddenNo','test',kind='line',marker='d',ax=ax)
_ = ax.set(title='第二个隐藏层神经元数量 v.s. f1_score',
ylabel='f1_score',xlabel='第二个隐藏层神经元数量')
经验总结
k折交叉验证
k折交叉验证(k-fold cross-validation)是一种评估机器学习模型性能的方法,它通过将训练集分成 k 个大小相等的子集(折叠或折数),然后选择 k-1 个子集作为训练集,剩下的一个子集作为验证集(或测试集),对模型进行 k 次这样的迭代。每次迭代都会评估模型的性能,并使用所有 k 次评估的平均性能来代表模型的整体性能。
训练集的每条记录用于训练的次数相同,并且恰好被检验一次
利用k折交叉验证筛选模型参数
- 步骤
- 原始数据划分成训练集(完整训练集)与检验集
- 将训练集划分成k折
- 设定模型参数,进行k折交叉验证
- 将获得的最优参数在完整训练集上重新训练
- 在检验集上进行模型性能的最终评估
重新建立titanic的神经网络模型
建立神经网络模型
titAnnNew = MLPClassifier(hidden_layer_sizes=(2,),solver='lbfgs',
activation='relu',learning_rate='constant',max_iter=3000)
进行k折交叉验证
from sklearn.model_selection import cross_val_score
cross_val_score(estimator, X, y=None, scoring=None, cv=None)
estimator
:构建的分类模型
X
:训练集的预测属性y
:训练集的类别标签
cv
:k折的k值,int
类型,表示具体的折数,默认是5折
scoring
:性能指标,可选的值包括(str
类型)- 'accuracy':
metrics.accuracy_score
- 'recall':
metrics.recall_score
- 'precision':
metrics.precision_score
- 'f1':
metrics.f1_score
- 'accuracy':
- 返回值:每一次交叉验证的性能,是一个含有k个值的
Numpy
数组
scoreLst = cross_val_score(titAnnNew,titTrainXScale,titTrainY,scoring='f1',cv=10)
# 计算准确率,分成10折
计算f1_score的均值和标准差
print(f'10折交叉验证的f1_score的均值是
{scoreLst.mean():.3f},标准差是{scoreLst.std():.3f}')
筛选隐藏层参数
hiddenNodes = range(1,21)
f1MeanLst = []
f1StdLst = []
for each in hiddenNodes:
annTmp = MLPClassifier(hidden_layer_sizes=(each,),solver='lbfgs',
activation='relu',learning_rate='invscaling',max_iter=3000)
resTmp = cross_val_score(annTmp,titTrainXScale,titTrainY,scoring='f1',cv=10)
f1MeanLst.append(resTmp.mean())
f1StdLst.append(resTmp.std())
compDf = pd.DataFrame({'hiddenNodeNum':hiddenNodes,'f1Mean':f1MeanLst,'f1Std':f1StdLst})
compDf.plot(x='hiddenNodeNum',y='f1Mean',kind='line',marker='o',figsize=(12,6))
- 隐藏层含有4个神经元效果最好
在完整训练集上重新训练模型
titAnnNew4 = MLPClassifier(hidden_layer_sizes=(4,),solver='lbfgs',
activation='relu',learning_rate='invscaling',max_iter=3000)
titAnnNew4.fit(titTrainXScale,titTrainY)
在检验集上检验模型分类效果
titTestYPreNew = titAnnNew4.predict(titTestXScale)
print(f'训练的神经网络模型的准确率是
{metrics.accuracy_score(titTestY,titTestYPreNew):.3f}')
print(f'训练的神经网络模型关于未生还的f1_score是
{metrics.f1_score(titTestY,titTestYPreNew,pos_label=0):.3f}')
print(f'训练的神经网络模型关于生还的f1_score是
{metrics.f1_score(titTestY,titTestYPreNew,pos_label=1):.3f}')
隐藏层不同神经元数量比较
titAnnTrainDisp = metrics.plot_roc_curve(titAnn,titTrainXScale,titTrainY,label='original')
metrics.plot_roc_curve(titAnn8,titTrainXScale,titTrainY,
label='hidden no is 8',ax=titAnnTrainDisp.ax_)
metrics.plot_roc_curve(titAnnNew4,titTrainXScale,titTrainY,
label='hidden no is 4',ax=titAnnTrainDisp.ax_)
titAnnTestDisp = metrics.plot_roc_curve(titAnn,titTestXScale,titTestY,label='original')
metrics.plot_roc_curve(titAnn8,titTestXScale,titTestY,
label='hidden no is 8',ax=titAnnTestDisp.ax_)
metrics.plot_roc_curve(titAnnNew4,titTestXScale,titTestY,
label='hidden no is 4',ax=titAnnTestDisp.ax_)