Task5 食物声音识别之模型改进与优化

Task1 食物声音识别之Baseline学习
Task2 食物声音识别之赛题数据介绍与分析
Task3 食物声音识别之音频数据特征提取
Task4 食物声音识别之深度学习

根据上述4个Task的工作,我们已经完成了对于Baseline方案的整体分析,从数据准备,到特征提取,再到深度学习建模,也在Task1里得到了通过Baseline预测测试集的情况,得到了一个比较低的预测率。这说明模型有待改进,那么这节就是要说明在做完上述工作以后,如何进行优化,这可能是我们工作和研究中最经常面临的情况。如果用2/8定律来分析的话,那么20%的工作是在理解前人的算法/模型,而80%的工作都是在优化前人的算法/模型,或者根据新的变化,采用新的特征提取方式,来提高整体的预测率,实现商业可行性。

1 过拟合和欠拟合

1.1 过拟合和欠拟合现象

过拟合是指模型对于训练数据拟合呈过当的情况,反映到评估指标上,就是模型在训练集上的表现很好,但在测试集和新数据上的表现较差。
欠拟合指的是模型在训练和预测时表现都不好的情况。

在这里插入图片描述

可以看出,图(a)是欠拟合的情况,拟合的黄线没有很好地捕捉到数据的特征,不能够很好地拟合数据。图(c)则是过拟合的情况,模型过于复杂,把噪声数据的特征也学习到模型中,导致模型泛化能力下降,在后期应用过程中很容易输出错误的预测结果。

看到问题以后如何解决呢?那么就要从整个数据处理和建模路径的每个过程来分析可以改进的方式,并作全局考虑,非常典型的说易行难。

1.2 降低过拟合风险的方法

​ (1)数据,数据,数据,重要的事情说三遍,因为我们都听过这样的传说,采用相同的模型更大的数据量,预测率获得了大幅度的有提升。但是由于我们受到训练集的限制,所以很直观的考虑是增加训练集的大小,类似于李飞飞团队的贡献,人工来进行声音收集,并标注和标签的映射关系。但是这需要更长的时间和人力来完成,那么有没有其他取巧的办法?比如,在图像分类的问题上,可以通过图像的平移、旋转、缩放等方式扩充数据;更进一步地,可以使用生成式对抗网络来合成大量的新训练数据。在音频上我们可以对训练集做类似的处理,对音频叠加,增加噪声,相位平移等,可以参考:https://zhuanlan.zhihu.com/p/41679490。

​ (2)降低模型复杂度,化繁为简。在数据较少时,模型过于复杂是产生过拟合的主要因素,适当降低模型复杂度可以避免模型拟合过多的采样噪声。例如,在神经网络模型中减少网络层数、神经元个数等;在决策树模型中降低树的深度、进行剪枝等。在这个学习案例中就是对CNN网络的优化,CNN训练过程中使用dropout是在每次训练过程中随机将部分神经元的权重置为0,即让一些神经元失效,这样可以缩减参数量,避免过拟合。其实在Task1的1.4节代码函数分析中已经使用了dropout函数。

​ (3)正则化方法。给模型的参数加上一定的正则约束,比如将权值的大小加入到损失函数中。以L2正则化为例:在优化原来的目标函数C0的同时,也能避免权值过大带来的过拟合风险。可参考https://www.cnblogs.com/MrSaver/p/10217315.html,对Task1的1.4节进行优化。

​ (4)集成学习方法。集成学习是把多个模型集成在一起,来降低单一模型的过拟合风险,如Bagging方法。Task1的例子中我们使用了sklearn.ensemble库,其支持众多集成学习算法和模型。而实际应用的也是比较代表性的bagging继承算法RandomForestClassifier。

1.3 降低欠拟合风险的方法

​ (1)添加新特征。当特征不足或者现有特征与样本标签的相关性不强时,模型容易出现欠拟合。在深度学习潮流中,有很多模型可以帮助完成特征工程,如因子分解机、梯度提升决策树、Deep-crossing等都可以成为丰富特征的方法。在本案例中,也就是要重新分析我们Task3的工作,将可用的数据特征逐一进行观察和分析,看是否有其他的特征可以在案例中使用。除了增加特征,更好的提炼特征提升特征对标签的代表性也是一个思路。
​ (2)增加模型复杂度。简单模型的学习能力较差,通过增加模型的复杂度可以使模型拥有更强的拟合能力。例如,在线性模型中添加高次项,在神经网络模型中增加网络层数或神经元个数等。
​ (3)减小正则化系数。正则化是用来防止过拟合的,但当模型出现欠拟合现象时,则需要有针对性地减小正则化系数。

02 泛化误差、偏差和方差

​ 模型调优,第一步是要找准目标:我们要做什么?一般来说,这个目标是提升某个模型评估指标,比如对于随机森林来说,我们想要提升的是模型在未知数据上的准确率(由score或oob_score_来衡量)。找准了这个目标,我们就需要思考:模型在未知数据上的准确率受什么因素影响?常用来衡量模型在未知数据上的准确率的指标,叫做泛化误差(Genelization error)。

2.1 泛化误差

​ 当模型在未知数据(测试集或者袋外数据)上表现糟糕时,我们说模型的泛化程度不够,泛化误差大,模型的效果不好。泛化误差受到模型的结构(复杂度)影响。

在这里插入图片描述

​ 上面这张图,它准确地描绘了泛化误差与模型复杂度的关系,当模型太复杂,模型就会过拟合,泛化能力就不够,所以泛化误差大。当模型太简单,模型就会欠拟合,拟合能力就不够,所以误差也会大。只有当模型的复杂度刚刚好的才能够达到泛化误差最小的目标。那模型的复杂度与我们的参数有什么关系呢?对树模型来说,树越茂盛,深度越深,枝叶越多,模型就越复杂。所以树模型是天生位于图的右上角的模型,随机森林是以树模型为基础,所以随机森林也是天生复杂度高的模型。随机森林的参数,都是向着一个目标去:减少模型的复杂度,把模型往图像的左边移动,防止过拟合。

​ 当然了,调优没有绝对,也有天生处于图像左边的随机森林,所以调参之前,我们要先判断,模型现在究竟处于图像的哪一边。泛化误差的背后其实是“偏差-方差困境”,原理十分复杂。

有四点需要注意的:
1)模型太复杂或者太简单,都会让泛化误差高,我们追求的是位于中间的平衡点
2)模型太复杂就会过拟合,模型太简单就会欠拟合
3)对树模型和树的集成模型来说,树的深度越深,枝叶越多,模型越复杂
4)树模型和树的集成模型的目标,都是减少模型复杂度,把模型往图像的左边移动

2.2 偏差和方差

​ 用过拟合、欠拟合来定性地描述模型是否很好地解决了特定的问题。从定量的角度来说,可以用模型的偏差(Bias)与方差(Variance)来描述模型的性能。因为和通常意义的偏差和方差的定义是一样的,所以这里就不展开。

03 模型评估

3.1 Holdout检验

​ Holdout 检验是最简单也是最直接的验证方法,它将原始的样本集合随机划分成训练集和验证集两部分。比方说,对于一个预测模型,我们把样本按照70%~30% 的比例分成两部分,70% 的样本用于模型训练;30% 的样本用于模型验证,包括绘制ROC曲线、计算精确率和召回率等指标来评估模型性能。

​ Holdout 检验的缺点很明显,即在验证集上计算出来的最后评估指标与原始分组有很大关系。为了消除随机性,引入了“交叉检验”的思想。

3.2 交叉检验

k-fold交叉验证:首先将全部样本划分成k个大小相等的样本子集;依次遍历这k个子集,每次把当前子集作为验证集,其余所有子集作为训练集,进行模型的训练和评估;最后把k次评估指标的平均值作为最终的评估指标。在实际实验中,k经常取10。

在这里插入图片描述

交叉验证图

# 使用红酒数据集,验证10折交叉验证在随机森林和单个决策树效益的对比。
%matplotlib inline
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import load_wine

wine = load_wine()

from sklearn.model_selection import train_test_split
Xtrain, Xtest, Ytrain, Ytest = train_test_split(wine.data,wine.target,test_size=0.3)
clf = DecisionTreeClassifier(random_state=0)
rfc = RandomForestClassifier(random_state=0)
clf = clf.fit(Xtrain,Ytrain)
rfc = rfc.fit(Xtrain,Ytrain)
score_c = clf.score(Xtest,Ytest)
score_r = rfc.score(Xtest,Ytest)
print("Single Tree:{}".format(score_c),"Random Forest:{}".format(score_r))

Single Tree:0.8888888888888888
Random Forest:0.9629629629629629

#交叉验证:是数据集划分为n分,依次取每一份做测试集,每n-1份做训练集,多次训练模型以观测模型稳定性的方法
from sklearn.model_selection import cross_val_score
import matplotlib.pyplot as plt
rfc = RandomForestClassifier(n_estimators=25)
rfc_s = cross_val_score(rfc,wine.data,wine.target,cv=10)
clf = DecisionTreeClassifier()
clf_s = cross_val_score(clf,wine.data,wine.target,cv=10)
plt.plot(range(1,11),rfc_s,label = "RandomForest")
plt.plot(range(1,11),clf_s,label = "Decision Tree")
plt.legend()
plt.show()

在这里插入图片描述
留一验证:每次留下1个样本作为验证集,其余所有样本作为测试集。样本总数为n,依次对n个样本进行遍历,进行n次验证,再将评估指标求平均值得到最终的评估指标。在样本总数较多的情况下,留一验证法的时间开销极大。事实上,留一验证是留p验证的特例。留p验证是每次留下p个样本作为验证集,而从n个元素中选择p个元素有 C n p C_{n}^{p} Cnp种可能,因此它的时间开销更是远远高于留一验证,故而很少在实际工程中被应用。

3.3 自助法

​ 不管是Holdout检验还是交叉检验,都是基于划分训练集和测试集的方法进行模型评估的。然而,当样本规模比较小时,将样本集进行划分会让训练集进一步减小,这可能会影响模型训练效果。有没有能维持训练集样本规模的验证方法呢?自助法可以比较好地解决这个问题。
​ 自助法是基于自助采样法的检验方法。对于总数为n的样本集合,进行n次有放回的随机抽样,得到大小为n的训练集。n次采样过程中,有的样本会被重复采样,有的样本没有被抽出过,将这些没有被抽出的样本作为验集,进行模型验证,这就是自助法的验证过程。

04 集成学习

​ 面对一个机器学习问题,通常有两种策略。

  • 一种是研发人员尝试各种模型,选择其中表现最好的模型做重点调参优化。
  • 后一种策略的核心,是将多个分类器的结果统一成一个最终的决策。使用这类策略的机器学习方法统称为集成学习。其中的每个单独的分类器称为基分类器。

​ 俗语说“三个臭皮匠,顶一个诸葛亮”,基分类器就类似于“臭皮匠”,而如何将这些基分类器集成起来,就是本章要讨论的重点。
同质集成学习又有两大类:

  • 个体学习器之间存在强依赖关系,一系列个体学习器基本都需要串行生成,代表算法是boosting系列算法;
  • 个体学习器之间不存在强依赖关系,一系列个体学习器可以并行生成,代表算法是bagging和随机森林(Random Forest)系列算法。

在这里插入图片描述

在这里插入图片描述

(上述两图来自:https://www.biaodianfu.com/boosting.html)

4.1 Boosting

​ Boosting方法训练基分类器时采用串行的方式,各个基分类器之间有依赖。它的基本思路是将基分类器层层叠加,每一层在训练的时候,对前一层基分类器分错的样本,给予更高的权重。测试时,根据各层分类器的结果的加权得到最终结果。
​ Boosting的过程很类似于人类学习的过程,我们学习新知识的过程往往是迭代式的,第一遍学习的时候,我们会记住一部分知识,但往往也会犯一些错误,对于这些错误,我们的印象会很深。第二遍学习的时候,就会针对犯过错误的知识加强学习,以减少类似的错误发生。不断循环往复,直到犯错误的次数减少到很低的程度。

4.2 Bagging

​ Bagging与Boosting的串行训练方式不同,Bagging方法在训练过程中,各基分类器之间无强依赖,可以进行并行训练。其中很著名的算法之一是基于决策树基分类器的随机森林(Random Forest)。为了让基分类器之间互相独立,将训练集分为若干子集(当训练样本数量较少时,子集之间可能有交叠)。Bagging方法更像是一个集体决策的过程,每个个体都进行单独学习,学习的内容可以相同,也可以不同,也可以部分重叠。但由于个体之间存在差异性,最终做出的判断不会完全一致。在最终做决策时,每个个体单独作出判断,再通过投票的方式做出最后的集体决策。

​ 再从消除基分类器的偏差和方差的角度来理解Boosting和Bagging方法的差异。基分类器,有时又被称为弱分类器,因为基分类器的错误率要大于集成分类器。基分类器的错误,是偏差和方差两种错误之和。偏差主要是由于分类器的表达能力有限导致的系统性错误,表现在训练误差不收敛。方差是由于分类器对于样本分布过于敏感,导致在训练样本数较少时,产生过拟合。Boosting方法是通过逐步聚焦于基分类器分错的样本,减小集成分类器的偏差。Bagging方法则是采取分而治之的策略,通过对训练样本多次采样,并分别训练出多个不同模型,然后做综合,来减小集成分类器的方差。假设所有基分类器出错的概率是独立的,在某个测试样本上,用简单多数投票方法来集成结果,超过半数基分类器出错的概率会随着基分类器的数量增加而下降。

在这里插入图片描述
上图是Bagging算法的示意图,Model 1、Model 2、Model 3都是用训练集的一个子集训练出来的,单独来看,它们的决策边界都很曲折,有过拟合的倾向。集成之后的模型(红线所示)的决策边界就比各个独立的模型平滑了,这是由于集成的加权投票方法,减小了方差。

4.3 集成学习的步骤

​ 虽然集成学习的具体算法和策略各不相同,但都共享同样的基本步骤。

​ 集成学习一般可分为以下3个步骤。
​ (1)找到误差互相独立的基分类器。
​ (2)训练基分类器。
​ (3)合并基分类器的结果。

​ 合并基分类器的方法有voting和stacking两种。前者是用投票的方式,将获得最多选票的结果作为最终的结果。后者是用串行的方式,把前一个基分类器的结果输出到下一个分类器,将所有基分类器的输出结果相加(或者用更复杂的算法融合,比如把各基分类器的输出作为特征,使用逻辑回归作为融合模型进行最后的结果预测)作为最终的输出。

​ Boosting的思想,对分类正确的样本降低了权重,对分类错误的样本升高或者保持权重不变。在最后进行模型融合的过程中,也根据错误率对基分类器进行加权融合。错误率低的分类器拥有更大的“话语权”。

import numpy as np
import matplotlib.pyplot as plt

from sklearn import datasets
#make_moons()函数用来制作样本数据,产生的结果为一个简单的样本数据集,用于可视化聚类算法和分类算法。
X, y = datasets.make_moons(n_samples=500, noise=0.3, random_state=42)
#可视化产生的样本数据
plt.scatter(X[y==0, 0], X[y==0, 1])
plt.scatter(X[y==1, 0], X[y==1, 1])
plt.show()

在这里插入图片描述

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=666)

# ==========================>>
# 手动实现集成学习(这里单个模型未调优,可以自己调到最优,进行jicheng)
# 对于LogisticRegression()可以参考https://www.cnblogs.com/wj-1314/p/10181876.html
from sklearn.linear_model import LogisticRegression
log_clf = LogisticRegression()
log_clf.fit(X_train, y_train)
log_clf.score(X_test, y_test)
# 对于SVC()可以参考https://blog.csdn.net/qq_41577045/article/details/79859902
from sklearn.svm import SVC
svm_clf = SVC()
svm_clf.fit(X_train, y_train)
svm_clf.score(X_test, y_test)


from sklearn.tree import DecisionTreeClassifier
# 对于DecisionTreeClassifier()可以参考https://www.cnblogs.com/baby-lily/p/10646226.html
dt_clf = DecisionTreeClassifier()
dt_clf.fit(X_train, y_train)
dt_clf.score(X_test, y_test)


y_pre_log = log_clf.predict(X_test)
y_pre_svm = svm_clf.predict(X_test)
y_pre_dt = dt_clf.predict(X_test)

# 进行少数服从多数(投票)
# (>=2?)三个模型,当有2个或3个预测为1,就说其是1,否则就是0
y_pre = np.array((y_pre_log + y_pre_svm + y_pre_dt) >= 2, dtype='int')
y_pre

# 查看准确率
from sklearn.metrics import accuracy_score
accuracy_score(y_test, y_pre)

# =============>>
# 使用voting进行集成,VotingClassifier可以参考https://www.cnblogs.com/volcao/p/9483026.html

from sklearn.ensemble import VotingClassifier
voting_clf = VotingClassifier(estimators=[
    ('log_clf', LogisticRegression()),
    ('svm_clf', SVC()),
    ('dt_clf', DecisionTreeClassifier())
], voting='hard')

voting_clf.fit(X_train, y_train)

voting_clf.score(X_test, y_test)

可以从执行结果看出采用不同的函数进行预测准确率分别为0.824,0.88,0.808,采用少数服从多数的投票以后准确率预测提升到最高的预测模型的值0.88,采用hard voting的集成预测率提升到0.888。

4.4 从减小方差和偏差的角度解释Boosting和Bagging

​ Bagging能够提高弱分类器性能的原因是降低了方差,Boosting能够提升弱分类器性能的原因是降低了偏差。首先,Bagging 是 Bootstrap Aggregating 的简称,意思就是再抽样,然后在每个样本上训练出来的模型取平均。

在这里插入图片描述

泛化误差、偏差、方差和模型复杂度的关系

这里还只是从理论上了解对于模型优化的方式方法,具体在这个案例上如何将预测率从34.5提高到目前提交程序里的最高预测率99%以上,那么还会在这篇的基础上继续更新。

参考资料:

以上只是介绍了部分机器学习模型调优方法,以下是Task中推荐的其他优化方法,主旨就是了解:

Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐