[机器学习]集成建模/集成学习:结合多种模型,实现更好的预测
1. 概述
1.1 什么是集成模型/集成学习
"模型集成"和"集成学习"是相同的概念。它们都指的是将多个机器学习模型组合在一起,以提高预测的准确性和稳定性的技术。通过结合多个模型的预测结果,集成学习可以减少单个模型的偏差和方差,并提供更可靠的预测结果。
集成学习原名为Classifier combination / ensemble learning,它是根据训练数据构造一组基分类器(base classifier),通过聚合每个基分类器的输出来进行分类。
基分类器,就是一个小的分类器,单个基分类器的性能取决于它选择的分类算法和训练集。 对于单个性能比较弱的基分类器,我们称为弱分类器。 对于单个性能比较强的基分类器,我们称为强分类器。
多个基分类器集成的思想源于下列直觉:
- 考虑到几个基分类器的分类意见,而不是仅仅依靠一个基分类器的意见 。
- 许多弱分类器的组合至少可以和一个强分类器一样好。
- 一些强分类器的组合(通常)至少和基分类器中最好的一个一样好。
集成是建立各种模型的过程,然后将它们混合以产生更好的预测。与单个模型相比,集成能够实现更精确的预测。在ML比赛中,利用集成通常会带来优势。你可以找到CrowdFlower winners的团队采访,他们用集成赢得了比赛:https://medium.com/kaggle-blog/crowdflower-winners-interview-3rd-place-team-quartet-cead438f8918
1.2 分类器集成的结果
多个分类器集成后的性能一定更好吗?我们带着这个疑问来看下面的例子:
分别代表了3个基分类器,
表示的是三个分类器的结合的最终结果:
由结果可以看出,多个基分类器的集合不一定犹豫单个基分类器的性能,那么:
什么时候选择集成呢? 基础分类器不会犯同样的错误。 每个基础分类器都是相当准确的。
1.3 构造基分类器的三种方法
实例操作:通过抽样产生多个训练集,并在每个数据集上训练一个基础分类器。
特征操作:通过不同的特征子集生成多个训练集,并在每个数据集上训练一个基础分类器。
算法操作:半随机地调整给定算法中的内部参数,在给定的数据集上生成多个基础分类器。
1.4 多个基分类器如何进行分类
在多个基分类器上进行分类的最简单手段是投票:
对于离散类,在测试集上运行多个基分类器,并选择由最多基分类器预测的类(少数服从多数)。例如一个二分类的数据集,,构造了5个基分类器,对于某个样本有三个基分类器的输出结果是1, 两个是0那么这个时候,总和来看结果就应该是1。
对于连续数值类,对我们基分类器预测的数字进行平均,将平均数作为最终的预测结果。
2. 单一模型存在的问题
单一模型是指只使用一个基础模型来进行预测或分类的方法,例如决策树、支持向量机、神经网络等。 单一模型的优点是简单、易于理解和实现,但也存在一些缺点,主要有以下几个方面:
- 泛化能力不足:单一模型容易受到数据噪声、异常值和过拟合的影响,导致在新的数据上表现不佳。
- 稳定性不高:单一模型对数据的分布和特征的选择非常敏感,稍微改变数据或特征就可能导致预测结果的变化。
- 表达能力有限:单一模型往往只能捕捉数据的某些方面的信息,难以表达数据的复杂性和多样性。
为了解决单一模型存在的问题,我们可以使用集成模型来组合多个基础模型,从而提高预测性能和泛化能力。
3. 简单的集成模型应用
集成模型背后的想法很简单:为什么不使用多个模型并结合它们的预测,而不是依赖一个模型?这样,我们就可以利用不同模型的多样性和互补性,获得更稳健、更准确的预测。
例如,假设我们想要根据客户的年龄、性别、收入和浏览历史来预测客户是否会购买产品。我们可以使用单一模型,例如逻辑回归或决策树,但它可能只能捕获数据中的一些细微差别和模式。或者,我们可以使用多种模型,例如逻辑回归、决策树、k 最近邻和支持向量机,并使用某种规则或算法组合它们的预测。这是集成模型的示例。 创建和组合多个模型的方法有多种。根据集成模型的实现方式,我们可以将集成模型分为五种主要类型:投票、平均、堆叠、装袋、提升。
3.1 投票
装袋的一个特殊情况是将模型用于分类而不是回归。例如,如果你有三个模型用于预测巴黎是否会下雨,可以进行多数投票,得到“是”作为最终预测结果。这也被称为多数投票集成或多数派集成。
多数投票的优点在于它降低了预测的错误率,意味着它们更有可能是正确的。缺点是它不考虑每个预测的置信度或概率,这意味着它可能忽略了一些有用的信息。
要在Python中使用scikit-learn实现多数投票集成,可以使用VotingClassifier类,将其voting参数设置为’hard’。这个类允许我们指定一个模型列表和一个投票方法(如’hard’或’soft’)来组合它们的预测。
以下是如何使用多数投票进行分类的示例:
from sklearn.ensemble import VotingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.svm import SVC
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
# 生成分类的随机数据集
X, y = make_classification(n_samples = 1000 , random_state= 42 )
# 将数据集分割为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size= 0.2 , random_state= 42 )
# 定义三个不同的模型
model1 = LogisticRegression(random_state= 42 )
model2 = DecisionTreeClassifier(random_state= 42 )
model3 = SVC(random_state= 42 ,probability= True )
# 使用多数投票集成组合模型
= VotingClassifier(estimators=[( 'lr' , model1), ( 'dt' , model2) , ( 'svc' , model3)], Voting= 'hard' )
# 在训练数据上拟合集成 ensemble.fit
(X_train, y_train)
# 在测试数据上评估集成的性能
print ( f"整体精准度:{ensemble.score(X_test, y_test)* 100 } %" )
3.2 平均
将多个模型的预测结果取平均值是组合多个模型的最简单方式。例如,如果你有三个模型分别预测巴黎的温度为15°C、18°C和20°C,你可以将它们的平均值计算出来,得到最终的预测值为17.67°C。这也被称为均值集成。
取平均的优点在于它降低了预测的方差,意味着它们不太可能偏离真实值太远。缺点在于它也降低了预测的偏差,意味着它们不太可能接近真实值。换句话说,取平均值使得预测更加一致,但也更加保守。
要在Python中使用scikit-learn实现平均集成,对于回归问题,我们可以使用VotingRegressor类,对于分类问题,我们可以使用VotingClassifier类。这些类允许我们指定一组模型和一个投票方法(如’hard’或’soft’)来组合它们的预测。
以下是如何在回归问题中使用平均集成的示例:
# 导入库
from sklearn.linear_model import LinearRegression
from sklearn.tree import DecisionTreeRegressor
from sklearn.neighbors import KNeighborsRegressor
from sklearn.ensemble import VotingRegressor
from sklearn.datasets import fetch_california_housing
from sklearn.metrics importmean_squared_error #
加载数据
california = fetch_california_housing (as_frame=真)
X = california.data
y = california.target
# 定义模型
lr = LinearRegression()
dt = DecisionTreeRegressor()
knn = KNeighborsRegressor()
# 创建平均集成
avg = VotingRegressor(estimators=[( 'lr' , lr), ( 'dt ' , dt), ( 'knn' , knn)])
# 对数据进行集成
avg.fit(X, y)
# 进行预测
y_pred = avg.predict(X)
# 评估性能
mse = Mean_squared_error(y, y_pred)
print ( f'MSE: {mse: .2 f} ' )
3.3 堆叠
另一种组合多个模型的方法是将它们用作另一个模型的输入。例如,如果你有三个模型分别预测巴黎的温度为15°C、18°C和20°C,你可以使用它们的预测作为第四个模型的特征,该模型学习如何加权它们并进行最终的预测。这也被称为元学习器或二级学习器。
堆叠的优点在于它可以从每个模型的优点和缺点中学习,从而做出更准确的预测。缺点在于它可能更加复杂,并容易过拟合,意味着它可能在训练数据上表现良好,但在新数据上表现不佳。
要在Python中使用scikit-learn实现堆叠集成,对于回归问题,我们可以使用StackingRegressor类,对于分类问题,我们可以使用StackingClassifier类。这些类允许我们指定一组模型作为基本估算器,以及另一个模型作为最终估算器。
以下是如何在分类问题中使用堆叠集成的示例:
# 导入库
from sklearn.linear_model import LogisticRegression
from sklearn.tree import DecisionTreeClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.ensemble import StackingClassifier
from sklearn.datasets import load_iris
from sklearn.metrics import precision_score
# 加载数据
X, y = load_iris(return_X_y= True )
# 定义模型
lr = LogisticRegression()
dt = DecisionTreeClassifier()
knn = KNeighborsClassifier()
# 创建堆叠集成
stack = StackingClassifier(estimators=[( 'lr' , lr), ( 'dt' , dt), ( ' knn' , knn)], Final_estimator=LogisticRegression())
# 对数据进行集成
stack.fit(X, y)
# 进行预测
y_pred = stack.predict(X)
# 评估性能
acc = precision_score(y, y_pred)
acc = acc* 100
print ( f'准确度: {acc: .2 f} %' )
3.4 装袋
第四种组合多个模型的方法是使用数据的不同子集来训练它们。例如,如果你有一个包含1000个观测值的数据集,你可以随机采样500个观测值(可以有重复的观测值),然后使用它们来训练一个模型。你可以多次重复这个过程,从而得到在数据不同子集上训练的不同模型。这也被称为自助聚合或自助法。
自助法的优点在于它降低了预测的方差,意味着它们不太可能偏离真实值太远。缺点在于它不降低预测的偏差,意味着它们仍然有可能接近真实值。换句话说,自助法使预测更加一致但不一定更准确。
要在Python中使用scikit-learn实现自助法集成,对于回归问题,我们可以使用BaggingRegressor类,对于分类问题,我们可以使用BaggingClassifier类。这些类允许我们指定一个基本估算器和创建的自助样本数量。
以下是如何在回归问题中使用bagging ensemble的示例:
# 导入库
from sklearn.tree import DecisionTreeClassifier
from sklearn.ensemble import BaggingClassifier
from sklearn.datasets import load_iris
from sklearn.metrics import precision_score
# 加载数据
X, y = load_iris(return_X_y= True )
# 定义基础模型
dt = DecisionTreeClassifier()
#创建装袋集成
bag = BaggingClassifier(base_estimator=dt, n_estimators= 10 )
# 在数据上拟合
集成 bag.fit(X, y)
# 进行预测
y_pred = bag.predict(X)
# 评估性能
acc = precision_score(y, y_pred )
acc = acc * 100
print ( f'准确度: {acc: .2 f} %' )
3.5 提升
组合多个模型的最后一种方法是以顺序和迭代的方式使用它们。例如,如果你有一个模型预测巴黎的温度为15°C,你可以使用其误差或残差作为另一个模型的输入,该模型试图纠正这些误差并做出更好的预测。你可以多次重复这个过程,得到相互从彼此错误中学习的不同模型。这也被称为自适应提升或AdaBoost。
提升的优点在于它降低了预测的方差和偏差,意味着它们更有可能接近并准确地反映真实值。缺点在于它可能对异常值和噪声更为敏感,意味着它可能对数据过拟合或欠拟合。
要在Python中使用scikit-learn实现提升集成,对于回归问题,我们可以使用AdaBoostRegressor类,对于分类问题,我们可以使用AdaBoostClassifier类。这些类允许我们指定一个基本估算器和提升迭代的次数。
以下是如何在回归问题中使用提升集成的示例:
# 导入库
from sklearn.tree import DecisionTreeRegressor
from sklearn.ensemble import AdaBoostRegressor
from sklearn.datasets import load_iris
from sklearn.metrics importmean_squared_error #
加载数据
X, y = load_iris(return_X_y= True )
# 定义基础模型
dt = DecisionTreeRegressor()
#创建增强集成
boost = AdaBoostRegressor(base_estimator=dt, n_estimators= 10 )
# 根据数据拟合集成
boost.fit(X, y)
# 进行预测
y_pred = boost.predict(X)
# 评估性能
mse = Mean_squared_error(y, y_pred)
打印(f'MSE:{mse:.2 f} ')
4. 分析预测模型的泛化误差
从模型角度看:
- Bias(偏置):衡量一个分类器进行错误预测的趋势。
- Variance(变异度):衡量一个分类器预测结果的偏离程度。
如果一个模型有更小的Bias和Variance 就代表这个模型的泛化性能很好。
5. 几何直观地理解集成学习的四大类型
5.1 装袋法 Bagging(实例操作)
- 将大小为
的原始数据
,进行Bootstrap抽样(有放回抽样),因此在各抽样数据集
中,存在部分元素重复;各抽样数据集的样本容量
- 将抽样后的数据集,分别独立进行模型学习
- 然后将
个模型进行聚合;分类:多数投票;回归:平均数、中位数
- 没有一个模型是在整个数据集上训练的
- 如果原始数据集部分元素发生异常变化,可能会影响子模型,而不影响聚合后的模型,
- 因此:“取偏差小 、方差大的基础模型,将其聚合,得到偏差小、方差小的模型”
Bagging = bootstrap aggregating(自举汇聚法)
装袋法思想源于数据越多,性能越好的直觉判断。
具体方法:
通过随机抽样与替换相结合的方式构建新的数据集 。
将原始数据集进行有放回的随机采样次,得到了个数据集,针对这些数据集一共产生个不同的基分类器。对于这个分类器,让他们采用投票法来决定最终的分类结果。
例子:
袋装法存在的问题:
因为袋装法是有放回的随机采样
次,那就有可能有些样本可能永远不会被随机到。因为
个样本,每个样本每次被取到的概率为
,那么一共取
次没取到的概率为
这个值在
很大的时候的极限值
。
装袋法的特点:
- 始终使用相同的基分类算法。
- 减少预测的
(通过接受一些
)。
- 对不稳定的分类器(训练集的微小变化会导致预测的巨大变化)有效。
- 可能会使稳定分类器的性能略有下降。
- 基于抽样(构造基分类器的三种方法中的实例操作)和投票的简单方法。
- 多个单独的基分类器可以同步并行进行计算。
- 可以有效的克服数据集中的噪声数据,因为异常值可能会消失(
)。
- 性能通常比基分类器要好得多,只是偶尔会比基分类器差。
5.2 随机森林法 Random Forest(特征操作)
具有良好深度的决策树就是 低偏差 和 高方差 的模型;因此用决策树做基础模型的Bagging(装袋算法),也称随机森林
- 对于原始数据集的 行:进行Bootstrap抽样(有放回抽样),大小为 m 的样本容量
- 对于原始数据集的 列:随机选择一个特征子集
- 在每个行抽样的数据集中,剩下的数据点(也称袋外点)可以用于相应子模型的交叉验证(以了解每个基础学习者的性能)
- 随着基础模型数量的增加 ,聚合的模型的方差减少
- 偏差(单个模型) ≈ 偏差(聚合后)
- 列采样率:
;行采样率:
;如果这两个比值减小,基础模型方差也减小,因此聚合模型的方差也会减少,一般情况固定,不需要认为优化
- 基础模型的个数 k ,为超参数,可以根据交叉验证进行确认最优
- 特征重要性:对每个基础模型的特征的信息增益求和,再在各特征中进行比较,较大信息增益为较重要特征
随机森林法的基分类器是随机树:一棵决策树,但每个节点只考虑一些可能的属性。
可以通过下图回忆什么是决策树:
也就是说随机树使用的特征空间不是训练集全部的特征空间
- 例如,采用一个固定的比例来选择每个决策树的特征空间大小。
- 随机森林中的每棵树的建立都比一个单独的决策树要简单和快速;但是这种方法增加了模型的 。
森林就是多个随机树的集合
- 每棵树都是用不同的袋装训练数据集建立的。
- 综合分类是通过投票进行的。
随机森林的超参数:
树的数量B,可以根据“out-of-bag”误差进行调整。
特征子样本大小:随着它的增加,分类器的强度和相关性都增加
。因为随机森林中的每棵树使用的特征越多,其与森林中其他树的特征重合度就可能越高,导致产生的随机数相似度越大。
可解释性:单个实例预测背后的逻辑可以通过多棵随机树共同决定。
随机森林的特点:
- 随机森林非常强大,可以高效地进行构建。
- 可以并行的进行。
- 对过拟合有很强的鲁棒性。
- 可解释性被牺牲了一部分,因为每个树的特征都是特征集合中随机选取的一部分。
5.3 演进法 Boosting(算法操作)
演进法的思想源于调整基础分类器,使其专注于难以分类的实例的直觉判断。
具体方法:
迭代地改变训练实例的分布和权重,以反映分类器在前一次迭代中的表现。
- 从初始训练集训练出一个基学习器;这时候每个样本的权重都为。
- 每个都会根据上一轮预测结果调整训练集样本的权重。
- 基于调整后的训练集训练一个新的基学习器。
- 重复进行,直到基学习器数量达到开始设置的值。
- 将个基学习器通过加权的投票方法(weighted voting)进行结合。
例子:
对于boosting方法,有两个问题需要解决:
- 每一轮学习应该如何改变数据的概率分布
- 如何将各个基分类器组合起来
Boosting集成方法的特点:
- 他的基分类器是决策树或者 OneR 方法。
- 数学过程复杂,但是计算的开销较小;整个过程建立在迭代的采样过程和加权的投票(voting)上。
- 通过迭代的方式不断的拟合残差信息,最终保证模型的精度。
- 比bagging方法的计算开销要大一些。
- 在实际的应用中,boosting的方法略有过拟合的倾向(但是不严重)。
- 可能是最佳的词分类器(gradient boosting)。
5.3.1 演进法实例:AdaBoost
Adaptive Boosting(自适应增强算法):是一种顺序的集成方法(随机森林和 Bagging 都属于并行的集成算法)。
具体方法: 有T个基分类器:
。 训练集表示为
。 初始化每个样本的权重都为
,即:
。
在每个iteration i 中,都按照下面的步骤进行:
计算错误率 error rate
是一个indicator函数,当函数的条件满足的时候函数值为1;即,当弱分类器
对样本
进行分类的时候如果分错了就会累积
。
使用
来计算每个基分类器
的重要程度(给这个基分类器分配权重
)
从这个公式也能看出来,当
判断错的样本量越多,得到的
就越大,相应的
就越小(越接近0) 根据
来更新每一个样本的权重参数,为了第i+1个iteration做准备:
样本j的权重由
变成
这个过程中发生的事情是:如果这个样本在第i个iteration中被判断正确了,他的权重就会在原本KaTeX parse error: Expected '}', got 'EOF' at end of input: w_{j}^{(i)}的基础上乘以
;根据上面的知识
因此
所以根据公式我们可以知道,那些被分类器预测错误的样本会有一个大的权重;而预测正确的样本则会有更小的权重。
是一个normalization项,为了保证所有的权重相加之和为1。
最终将所有的
按照权重进行集成
持续完成从i=2,…,T的迭代过程,但是当
的时候需要重新初始化样本的权重最终采用的集成模型进行分类的公式:
这个公式的意思大概是:例如我们现在已经得到了3个基分类器,他们的权重分别是0.3, 0.2, 0.1所以整个集成分类器可以表示为:
如果类别标签一共只有0, 1那就最终的C(x)对于0的值大还是对于1的值大了。 只要每一个基分类器都比随机预测的效果好,那么最终的集成模型就会收敛到一个强很多的模型。
5.3.2 装袋法/随机森林和演进法对比
装袋法和演进法的对比:
装袋法/随机森林 以及演进法对比
5.4 堆叠法 Stacking
堆叠法的思想源于在不同偏置的算法范围内平滑误差的直觉。
方法:采用多种算法,这些算法拥有不同的偏置
在基分类器(level-0 model) 的输出上训练一个元分类器(meta-classifier)也叫level-1 model 了解哪些分类器是可靠的,并组合基分类器的输出 使用交叉验证来减少偏置
Level-0:基分类器 给定一个数据集 ( X , y ) 可以是SVM, Naive Bayes, DT等
Level-1:集成分类器 在Level-0分类器的基础上构建新的attributes 每个Level-0分类器的预测输出都会加入作为新的attributes;如果有M个Level-0分离器最终就会加入M个attributes 删除或者保持原本的数据X 考虑其他可用的数据(NB概率分数,SVM权重) 训练meta-classifier来做最终的预测
可视化这个stacking过程:
stacking方法的特点:
- 结合多种不同的分类器
- 数学表达简单,但是实际操作耗费计算资源
- 通常与基分类器相比,stacking的结果一般好于最好的基分类器
PS:读到这里你大概已经了解集成学习了,如果你想更深入了解的话可以继续读下去
6. 深入介绍集成技术
6.1 集成学习介绍
我们通过一个例子来理解集成学习的概念。假设你是一名电影导演,你依据一个非常重要且有趣的话题创作了一部短片。现在,你想在公开发布前获得影片的初步反馈(评级)。有哪些可行的方法呢?
A:可以请一位朋友为电影打分。
于是完全有可能出现这种结果:你所选择的人由于非常爱你,并且不希望给你这部糟糕的影片打1星评级来伤害你脆弱的小心脏。
B:另一种方法是让你的5位同事评价这部电影。
这个办法应该更好,可能会为电影提供更客观诚实的评分。但问题依然存在。这5个人可能不是电影主题方面的“专家”。当然,他们可能懂电影摄制,镜头或音效,但他们可能并不是黑色幽默的最佳评判者。
C:让50个人评价这部电影呢?
其中一些可以是你的朋友,可以是你的同事,甚至是完完全全的陌生人。
在这种情况下,回应将更加普遍化和多样化,因为他们拥有不同的技能。事实证明,与我们之前看到的情况相比,这是获得诚实评级的更好方法。
通过这些例子,你可以推断,与个人相比,不同群体的人可能会做出更好的决策。与单一模型相比,各种不同模型也是这个道理。机器学习中的多样化是通过称为集成学习(Ensemble learning)的技术实现的。
现在你已经掌握了集成学习的要旨,接下来让我们看看集成学习中的各种技术及其实现。
6.2 简单集成技术
这一节中,我们会看一些简单但是强大的技术,比如:
- 最大投票法
- 平均法
- 加权平均法
6.2.1 最大投票法
最大投票方法通常用于分类问题。这种技术中使用多个模型来预测每个数据点。每个模型的预测都被视为一次“投票”。大多数模型得到的预测被用作最终预测结果。
例如,当你让5位同事评价你的电影时(最高5分); 我们假设其中三位将它评为4,而另外两位给它一个5。由于多数人评分为4,所以最终评分为4。你可以将此视为采用了所有预测的众数(mode)。
最大投票的结果有点像这样:
示例代码:
这里x_train由训练数据中的自变量组成,y_train是训练数据的目标变量。验证集是x_test(自变量)和y_test(目标变量)。
model1 = tree.DecisionTreeClassifier() model2 = KNeighborsClassifier() model3= LogisticRegression() model1.fit(x_train,y_train) model2.fit(x_train,y_train) model3.fit(x_train,y_train) pred1=model1.predict(x_test) pred2=model2.predict(x_test) pred3=model3.predict(x_test) final_pred = np.array([]) for i in range(0,len(x_test)): final_pred =np.append(final_pred, mode([pred1[i], pred2[i], pred3[i]]))
或者,你也可以在sklearn中使用“VotingClassifier”模块,如下所示:
from sklearn.ensemble import VotingClassifier model1 = LogisticRegression(random_state=1) model2 = tree.DecisionTreeClassifier(random_state=1) model = VotingClassifier(estimators=[(‘lr’, model1), (‘dt’, model2)], voting=‘hard’) model.fit(x_train,y_train) model.score(x_test,y_test)
6.2.2 平均法
类似于最大投票技术,这里对每个数据点的多次预测进行平均。在这种方法中,我们从所有模型中取平均值作为最终预测。平均法可用于在回归问题中进行预测或在计算分类问题的概率时使用。
例如,在下面的情况中,平均法将取所有值的平均值。
即(5 + 4 + 5 + 4 + 4)/ 5 = 4.4
示例代码:
model1 = tree.DecisionTreeClassifier() model2 = KNeighborsClassifier() model3= LogisticRegression() model1.fit(x_train,y_train) model2.fit(x_train,y_train) model3.fit(x_train,y_train) pred1=model1.predict_proba(x_test) pred2=model2.predict_proba(x_test) pred3=model3.predict_proba(x_test) finalpred=(pred1+pred2+pred3)/3
2.3 加权平均法
这是平均法的扩展。为所有模型分配不同的权重,定义每个模型的预测重要性。例如,如果你的两个同事是评论员,而其他人在这方面没有任何经验,那么与其他人相比,这两个朋友的答案就更加重要。
计算结果为[(5 * 0.23)+(4 * 0.23)+(5 * 0.18)+(4 * 0.18)+(4 * 0.18)] = 4.41。
示例代码:
model1 = tree.DecisionTreeClassifier() model2 = KNeighborsClassifier() model3= LogisticRegression() model1.fit(x_train,y_train) model2.fit(x_train,y_train) model3.fit(x_train,y_train) pred1=model1.predict_proba(x_test) pred2=model2.predict_proba(x_test) pred3=model3.predict_proba(x_test) finalpred=(pred1 0.3+pred20.3+pred3*0.4)
6.3 高级集成技术
我们已经介绍了基础的集成技术,让我们继续了解高级的技术。
6.3.1 堆叠(Stacking)
堆叠是一种集成学习技术,它使用多个模型(例如决策树,knn或svm)的预测来构建新模型。该新模型用于对测试集进行预测。以下是简单堆叠集成法的逐步解释:
第一步:把训练集分成10份
第二步:基础模型(假设是决策树)在其中9份上拟合,并对第10份进行预测。
第三步:对训练集上的每一份如此做一遍。
第四步:然后将基础模型(此处是决策树)拟合到整个训练集上。
第五步:使用此模型,在测试集上进行预测。
第六步:对另一个基本模型(比如knn)重复步骤2到4,产生对训练集和测试集的另一组预测。
第七步:训练集预测被用作构建新模型的特征。
第八步:该新模型用于对测试预测集(test prediction set,上图的右下角)进行最终预测。
示例代码:
我们首先定义一个函数来对n折的训练集和测试集进行预测。此函数返回每个模型对训练集和测试集的预测。
def Stacking(model,train,y,test,n_fold): folds=StratifiedKFold(n_splits=n_fold,random_state=1) test_pred=np.empty((test.shape[0],1),float) train_pred=np.empty((0,1),float) for train_indices,val_indices in folds.split(train,y.values): x_train,x_val=train.iloc[train_indices],train.iloc[val_indices] y_train,y_val=y.iloc[train_indices],y.iloc[val_indices] model.fit(X=x_train,y=y_train) train_pred=np.append(train_pred,model.predict(x_val)) test_pred=np.append(test_pred,model.predict(test)) return test_pred.reshape(-1,1),train_pred
现在我们将创建两个基本模型:决策树和knn。
model1 = tree.DecisionTreeClassifier(random_state=1) test_pred1 ,train_pred1=Stacking(model=model1,n_fold=10, train=x_train,test=x_test,y=y_train) train_pred1=pd.DataFrame(train_pred1) test_pred1=pd.DataFrame(test_pred1) model2 = KNeighborsClassifier() test_pred2 ,train_pred2=Stacking(model=model2,n_fold=10,train=x_train,test=x_test,y=y_train) train_pred2=pd.DataFrame(train_pred2) test_pred2=pd.DataFrame(test_pred2)
创建第三个模型,逻辑回归,在决策树和knn模型的预测之上。
df = pd.concat([train_pred1, train_pred2], axis=1) df_test = pd.concat([test_pred1, test_pred2], axis=1) model = LogisticRegression(random_state=1) model.fit(df,y_train) model.score(df_test, y_test)
为了简化上面的解释,我们创建的堆叠模型只有两层。决策树和knn模型建立在零级,而逻辑回归模型建立在第一级。其实可以随意的在堆叠模型中创建多个层次。
6.3.2 混合(Stacking)
混合遵循与堆叠相同的方法,但仅使用来自训练集的一个留出(holdout)/验证集来进行预测。换句话说,与堆叠不同,预测仅在留出集上进行。留出集和预测用于构建在测试集上运行的模型。以下是混合过程的详细说明:
第一步:原始训练数据被分为训练集合验证集。
第二步:在训练集上拟合模型。
第三步:在验证集和测试集上进行预测。
第四步:验证集及其预测用作构建新模型的特征。
第五步:该新模型用于对测试集和元特征(meta-features)进行最终预测。
示例代码:
我们将在训练集上建立两个模型,决策树和knn,以便对验证集进行预测。
model1 = tree.DecisionTreeClassifier() model1.fit(x_train, y_train) val_pred1=model1.predict(x_val) test_pred1=model1.predict(x_test) val_pred1=pd.DataFrame(val_pred1) test_pred1=pd.DataFrame(test_pred1) model2 = KNeighborsClassifier() model2.fit(x_train,y_train) val_pred2=model2.predict(x_val) test_pred2=model2.predict(x_test) val_pred2=pd.DataFrame(val_pred2) test_pred2=pd.DataFrame(test_pred2)
结合元特征和验证集,构建逻辑回归模型以对测试集进行预测。
df_val=pd.concat([x_val, val_pred1,val_pred2],axis=1) df_test=pd.concat([x_test, test_pred1,test_pred2],axis=1) model = LogisticRegression() model.fit(df_val,y_val) model.score(df_test,y_test)
6.3.3 Bagging
Bagging背后的想法是结合多个模型的结果(例如,所有决策树)来获得泛化的结果。这有一个问题:如果在同样一组数据上创建所有模型并将其组合起来,它会有用吗?这些模型极大可能会得到相同的结果,因为它们获得的输入相同。那我们该如何解决这个问题呢?其中一种技术是自举(bootstrapping)。
Bootstrapping是一种采样技术,我们有放回的从原始数据集上创建观察子集,子集的大小与原始集的大小相同。
Bagging(或Bootstrap Aggregating)技术使用这些子集(包)来获得分布的完整概念(完备集)。为bagging创建的子集的大小也可能小于原始集。
第一步:从原始数据集有放回的选择观测值来创建多个子集。
第二步:在每一个子集上创建一个基础模型(弱模型)。
第三步:这些模型同时运行,彼此独立。
第四步:通过组合所有模型的预测来确定最终预测。
6.3.4 Boosting
在我们进一步讨论之前,这里有另一个问题:如果第一个模型错误地预测了某一个数据点,然后接下来的模型(可能是所有模型),将预测组合起来会提供更好的结果吗?Boosting就是来处理这种情况的。
Boosting是一个顺序过程,每个后续模型都会尝试纠正先前模型的错误。后续的模型依赖于之前的模型。接下来一起看看boosting的工作方式:
第一步:从原始数据集创建一个子集。
第二步:最初,所有数据点都具有相同的权重。
第三步:在此子集上创建基础模型。
第四步:该模型用于对整个数据集进行预测。
第五步:使用实际值和预测值计算误差。
第六步:预测错误的点获得更高的权重。(这里,三个错误分类的蓝色加号点将被赋予更高的权重)
第七步:创建另一个模型并对数据集进行预测(此模型尝试更正先前模型中的错误)。
第八步:类似地,创建多个模型,每个模型校正先前模型的错误。
第九步:最终模型(强学习器)是所有模型(弱学习器)的加权平均值。
因此,boosting算法结合了许多弱学习器来形成一个强
推荐阅读
-
SSM三大框架基础面试题-一、Spring篇 什么是Spring框架? Spring是一种轻量级框架,提高开发人员的开发效率以及系统的可维护性。 我们一般说的Spring框架就是Spring Framework,它是很多模块的集合,使用这些模块可以很方便地协助我们进行开发。这些模块是核心容器、数据访问/集成、Web、AOP(面向切面编程)、工具、消息和测试模块。比如Core Container中的Core组件是Spring所有组件的核心,Beans组件和Context组件是实现IOC和DI的基础,AOP组件用来实现面向切面编程。 Spring的6个特征: 核心技术:依赖注入(DI),AOP,事件(Events),资源,i18n,验证,数据绑定,类型转换,SpEL。 测试:模拟对象,TestContext框架,Spring MVC测试,WebTestClient。 数据访问:事务,DAO支持,JDBC,ORM,编组XML。 Web支持:Spring MVC和Spring WebFlux Web框架。 集成:远程处理,JMS,JCA,JMX,电子邮件,任务,调度,缓存。 语言:Kotlin,Groovy,动态语言。 列举一些重要的Spring模块? Spring Core:核心,可以说Spring其他所有的功能都依赖于该类库。主要提供IOC和DI功能。 Spring Aspects:该模块为与AspectJ的集成提供支持。 Spring AOP:提供面向切面的编程实现。 Spring JDBC:Java数据库连接。 Spring JMS:Java消息服务。 Spring ORM:用于支持Hibernate等ORM工具。 Spring Web:为创建Web应用程序提供支持。 Spring Test:提供了对JUnit和TestNG测试的支持。 谈谈自己对于Spring IOC和AOP的理解 IOC(Inversion Of Controll,控制反转)是一种设计思想: 在程序中手动创建对象的控制权,交由给Spring框架来管理。IOC在其他语言中也有应用,并非Spring特有。IOC容器实际上就是一个Map(key, value),Map中存放的是各种对象。 将对象之间的相互依赖关系交给IOC容器来管理,并由IOC容器完成对象的注入。这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。IOC容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。在实际项目中一个Service类可能由几百甚至上千个类作为它的底层,假如我们需要实例化这个Service,可能要每次都搞清楚这个Service所有底层类的构造函数,这可能会把人逼疯。如果利用IOC的话,你只需要配置好,然后在需要的地方引用就行了,大大增加了项目的可维护性且降低了开发难度。 Spring中的bean的作用域有哪些? 1.singleton:该bean实例为单例 2.prototype:每次请求都会创建一个新的bean实例(多例)。 3.request:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP request内有效。 4.session:每一次HTTP请求都会产生一个新的bean,该bean仅在当前HTTP session内有效。 5.global-session:全局session作用域,仅仅在基于Portlet的Web应用中才有意义,Spring5中已经没有了。Portlet是能够生成语义代码(例如HTML)片段的小型Java Web插件。它们基于Portlet容器,可以像Servlet一样处理HTTP请求。但是与Servlet不同,每个Portlet都有不同的会话。 Spring中的单例bean的线程安全问题了解吗? 概念用于理解:大部分时候我们并没有在系统中使用多线程,所以很少有人会关注这个问题。单例bean存在线程问题,主要是因为当多个线程操作同一个对象的时候,对这个对象的非静态成员变量的写操作会存在线程安全问题。 有两种常见的解决方案(用于回答的点): 1.在bean对象中尽量避免定义可变的成员变量(不太现实)。 2.在类中定义一个ThreadLocal成员变量,将需要的可变成员变量保存在ThreadLocal(线程本地化对象)中(推荐的一种方式)。 ThreadLocal解决多线程变量共享问题(参考博客):https://segmentfault.com/a/1190000009236777 Spring中Bean的生命周期: 1.Bean容器找到配置文件中Spring Bean的定义。 2.Bean容器利用Java Reflection API创建一个Bean的实例。 3.如果涉及到一些属性值,利用set方法设置一些属性值。 4.如果Bean实现了BeanNameAware接口,调用setBeanName方法,传入Bean的名字。 5.如果Bean实现了BeanClassLoaderAware接口,调用setBeanClassLoader方法,传入ClassLoader对象的实例。 6.如果Bean实现了BeanFactoryAware接口,调用setBeanClassFacotory方法,传入ClassLoader对象的实例。 7.与上面的类似,如果实现了其他*Aware接口,就调用相应的方法。 8.如果有和加载这个Bean的Spring容器相关的BeanPostProcessor对象,执postProcessBeforeInitialization方法。 9.如果Bean实现了InitializingBean接口,执行afeterPropertiesSet方法。 10.如果Bean在配置文件中的定义包含init-method属性,执行指定的方法。 11.如果有和加载这个Bean的Spring容器相关的BeanPostProcess对象,执行postProcessAfterInitialization方法。 12.当要销毁Bean的时候,如果Bean实现了DisposableBean接口,执行destroy方法。 13.当要销毁Bean的时候,如果Bean在配置文件中的定义包含destroy-method属性,执行指定的方法。 Spring框架中用到了哪些设计模式? 1.工厂设计模式:Spring使用工厂模式通过BeanFactory和ApplicationContext创建bean对象。 2.代理设计模式:Spring AOP功能的实现。 3.单例设计模式:Spring中的bean默认都是单例的。 4.模板方法模式:Spring中的jdbcTemplate、hibernateTemplate等以Template结尾的对数据库操作的类,它们就使用到了模板模式。 5.包装器设计模式:我们的项目需要连接多个数据库,而且不同的客户在每次访问中根据需要会去访问不同的数据库。这种模式让我们可以根据客户的需求能够动态切换不同的数据源。 6.观察者模式:Spring事件驱动模型就是观察者模式很经典的一个应用。 7.适配器模式:Spring AOP的增强或通知(Advice)使用到了适配器模式、Spring MVC中也是用到了适配器模式适配Controller。 还有很多。。。。。。。 @Component和@Bean的区别是什么 1.作用对象不同。@Component注解作用于类,而@Bean注解作用于方法。 2.@Component注解通常是通过类路径扫描来自动侦测以及自动装配到Spring容器中(我们可以使用@ComponentScan注解定义要扫描的路径)。@Bean注解通常是在标有该注解的方法中定义产生这个bean,告诉Spring这是某个类的实例,当我需要用它的时候还给我。 3.@Bean注解比@Component注解的自定义性更强,而且很多地方只能通过@Bean注解来注册bean。比如当引用第三方库的类需要装配到Spring容器的时候,就只能通过@Bean注解来实现。 @Configuration public class AppConfig { @Bean public TransferService transferService { return new TransferServiceImpl; } } <beans> <bean id="transferService" class="com.kk.TransferServiceImpl"/> </beans> @Bean public OneService getService(status) { case (status) { when 1: return new serviceImpl1; when 2: return new serviceImpl2; when 3: return new serviceImpl3; } } 将一个类声明为Spring的bean的注解有哪些? 声明bean的注解: @Component 组件,没有明确的角色 @Service 在业务逻辑层使用(service层) @Repository 在数据访问层使用(dao层) @Controller 在展现层使用,控制器的声明 注入bean的注解: @Autowired:由Spring提供 @Inject:由JSR-330提供 @Resource:由JSR-250提供 *扩:JSR 是 java 规范标准 Spring事务管理的方式有几种? 1.编程式事务:在代码中硬编码(不推荐使用)。 2.声明式事务:在配置文件中配置(推荐使用),分为基于XML的声明式事务和基于注解的声明式事务。 Spring事务中的隔离级别有哪几种? 在TransactionDefinition接口中定义了五个表示隔离级别的常量:ISOLATION_DEFAULT:使用后端数据库默认的隔离级别,Mysql默认采用的REPEATABLE_READ隔离级别;Oracle默认采用的READ_COMMITTED隔离级别。ISOLATION_READ_UNCOMMITTED:最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。ISOLATION_READ_COMMITTED:允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生ISOLATION_REPEATABLE_READ:对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。ISOLATION_SERIALIZABLE:最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。 Spring事务中有哪几种事务传播行为? 在TransactionDefinition接口中定义了八个表示事务传播行为的常量。 支持当前事务的情况:PROPAGATION_REQUIRED:如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。PROPAGATION_MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)。 不支持当前事务的情况:PROPAGATION_REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。 其他情况:PROPAGATION_NESTED: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于PROPAGATION_REQUIRED。 二、SpringMVC篇 什么是Spring MVC ?简单介绍下你对springMVC的理解? Spring MVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,通过把Model,View,Controller分离,将web层进行职责解耦,把复杂的web应用分成逻辑清晰的几部分,简化开发,减少出错,方便组内开发人员之间的配合。 Spring MVC的工作原理了解嘛? image.png Springmvc的优点: (1)可以支持各种视图技术,而不仅仅局限于JSP; (2)与Spring框架集成(如IoC容器、AOP等); (3)清晰的角色分配:前端控制器(dispatcherServlet) , 请求到处理器映射(handlerMapping), 处理器适配器(HandlerAdapter), 视图解析器(ViewResolver)。 (4) 支持各种请求资源的映射策略。 Spring MVC的主要组件? (1)前端控制器 DispatcherServlet(不需要程序员开发) 作用:接收请求、响应结果,相当于转发器,有了DispatcherServlet 就减少了其它组件之间的耦合度。 (2)处理器映射器HandlerMapping(不需要程序员开发) 作用:根据请求的URL来查找Handler (3)处理器适配器HandlerAdapter 注意:在编写Handler的时候要按照HandlerAdapter要求的规则去编写,这样适配器HandlerAdapter才可以正确的去执行Handler。 (4)处理器Handler(需要程序员开发) (5)视图解析器 ViewResolver(不需要程序员开发) 作用:进行视图的解析,根据视图逻辑名解析成真正的视图(view) (6)视图View(需要程序员开发jsp) View是一个接口, 它的实现类支持不同的视图类型(jsp,freemarker,pdf等等) springMVC和struts2的区别有哪些? (1)springmvc的入口是一个servlet即前端控制器(DispatchServlet),而struts2入口是一个filter过虑器(StrutsPrepareAndExecuteFilter)。 (2)springmvc是基于方法开发(一个url对应一个方法),请求参数传递到方法的形参,可以设计为单例或多例(建议单例),struts2是基于类开发,传递参数是通过类的属性,只能设计为多例。 (3)Struts采用值栈存储请求和响应的数据,通过OGNL存取数据,springmvc通过参数解析器是将request请求内容解析,并给方法形参赋值,将数据和视图封装成ModelAndView对象,最后又将ModelAndView中的模型数据通过reques域传输到页面。Jsp视图解析器默认使用jstl。 SpringMVC怎么样设定重定向和转发的? (1)转发:在返回值前面加"forward:",譬如"forward:user.do?name=method4" (2)重定向:在返回值前面加"redirect:",譬如"redirect:http://www.baidu.com" SpringMvc怎么和AJAX相互调用的? 通过Jackson框架就可以把Java里面的对象直接转化成Js可以识别的Json对象。具体步骤如下 : (1)加入Jackson.jar (2)在配置文件中配置json的映射 (3)在接受Ajax方法里面可以直接返回Object,List等,但方法前面要加上@ResponseBody注解。 如何解决POST请求中文乱码问题,GET的又如何处理呢? (1)解决post请求乱码问题: 在web.xml中配置一个CharacterEncodingFilter过滤器,设置成utf-8; <filter> <filter-name>CharacterEncodingFilter</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>CharacterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> (2)get请求中文参数出现乱码解决方法有两个: ①修改tomcat配置文件添加编码与工程编码一致,如下: <ConnectorURIEncoding="utf-8" connectionTimeout="20000" port="8080" protocol="HTTP/1.1" redirectPort="8443"/> ②另外一种方法对参数进行重新编码: String userName = new String(request.getParamter("userName").getBytes("ISO8859-1"),"utf-8") ISO8859-1是tomcat默认编码,需要将tomcat编码后的内容按utf-8编码。 Spring MVC的异常处理 ? 统一异常处理: Spring MVC处理异常有3种方式: (1)使用Spring MVC提供的简单异常处理器SimpleMappingExceptionResolver; (2)实现Spring的异常处理接口HandlerExceptionResolver 自定义自己的异常处理器; (3)使用@ExceptionHandler注解实现异常处理; 统一异常处理的博客:https://blog.csdn.net/ctwy291314/article/details/81983103 SpringMVC的控制器是不是单例模式,如果是,有什么问题,怎么解决? 是单例模式,所以在多线程访问的时候有线程安全问题,不要用同步,会影响性能的,解决方案是在控制器里面不能写成员变量。(此题目类似于上面Spring 中 第5题 有两种解决方案) SpringMVC常用的注解有哪些? @RequestMapping:用于处理请求 url 映射的注解,可用于类或方法上。用于类上,则表示类中的所有响应请求的方法都是以该地址作为父路径。 @RequestBody:注解实现接收http请求的json数据,将json转换为java对象。 @ResponseBody:注解实现将conreoller方法返回对象转化为json对象响应给客户。 SpingMvc中的控制器的注解一般用那个,有没有别的注解可以替代? 一般用@Controller注解,也可以使用@RestController,@RestController注解相当于@ResponseBody + @Controller,表示是表现层,除此之外,一般不用别的注解代替。 如果在拦截请求中,我想拦截get方式提交的方法,怎么配置? 可以在@RequestMapping注解里面加上method=RequestMethod.GET。 怎样在方法里面得到Request,或者Session? 直接在方法的形参中声明request,SpringMVC就自动把request对象传入。 如果想在拦截的方法里面得到从前台传入的参数,怎么得到? 直接在形参里面声明这个参数就可以,但必须名字和传过来的参数一样。 如果前台有很多个参数传入,并且这些参数都是一个对象的,那么怎么样快速得到这个对象? 直接在方法中声明这个对象,SpringMVC就自动会把属性赋值到这个对象里面。 SpringMVC中函数的返回值是什么? 返回值可以有很多类型,有String, ModelAndView。ModelAndView类把视图和数据都合并的一起的。 SpringMVC用什么对象从后台向前台传递数据的? 通过ModelMap对象,可以在这个对象里面调用put方法,把对象加到里面,前台就可以拿到数据。 怎么样把ModelMap里面的数据放入Session里面? 可以在类上面加上@SessionAttributes注解,里面包含的字符串就是要放入session里面的key。 SpringMvc里面拦截器是怎么写的: 有两种写法,一种是实现HandlerInterceptor接口,另外一种是继承适配器类,接着在接口方法当中,实现处理逻辑;然后在SpringMvc的配置文件中配置拦截器即可: <!-- 配置SpringMvc的拦截器 --> <mvc:interceptors> <!-- 配置一个拦截器的Bean就可以了 默认是对所有请求都拦截 --> <bean id="myInterceptor" class="com.zwp.action.MyHandlerInterceptor"></bean> <!-- 只针对部分请求拦截 --> <mvc:interceptor> <mvc:mapping path="/modelMap.do" /> <bean class="com.zwp.action.MyHandlerInterceptorAdapter" /> </mvc:interceptor> </mvc:interceptors> 注解原理: 注解本质是一个继承了Annotation的特殊接口,其具体实现类是Java运行时生成的动态代理类。我们通过反射获取注解时,返回的是Java运行时生成的动态代理对象。通过代理对象调用自定义注解的方法,会最终调用AnnotationInvocationHandler的invoke方法。该方法会从memberValues这个Map中索引出对应的值。而memberValues的来源是Java常量池 三、Mybatis篇 什么是MyBatis? MyBatis是一个可以自定义SQL、存储过程和高级映射的持久层框架。 讲下MyBatis的缓存 MyBatis的缓存分为一级缓存和二级缓存,一级缓存放在session里面,默认就有, 二级缓存放在它的命名空间里,默认是不打开的,使用二级缓存属性类需要实现Serializable序列化接口, 可在它的映射文件中配置<cache/> Mybatis是如何进行分页的?分页插件的原理是什么? 1)Mybatis使用RowBounds对象进行分页,也可以直接编写sql实现分页,也可以使用Mybatis的分页插件。 2)分页插件的原理:实现Mybatis提供的接口,实现自定义插件,在插件的拦截方法内拦截待执行的sql,然后重写sql。 举例:select * from student,拦截sql后重写为:select t.* from (select * from student)t limit 0,10 简述Mybatis的插件运行原理,以及如何编写一个插件? 1)Mybatis仅可以编写针对ParameterHandler、ResultSetHandler、StatementHandler、 Executor这4种接口的插件,Mybatis通过动态代理, 为需要拦截的接口生成代理对象以实现接口方法拦截功能, 每当执行这4种接口对象的方法时,就会进入拦截方法, 具体就是InvocationHandler的invoke方法,当然, 只会拦截那些你指定需要拦截的方法。 2)实现Mybatis的Interceptor接口并复写intercept方法, 然后在给插件编写注解,指定要拦截哪一个接口的哪些方法即可, 记住,别忘了在配置文件中配置你编写的插件。 Mybatis动态sql是做什么的?都有哪些动态sql?能简述一下动态sql的执行原理不? 1)Mybatis动态sql可以让我们在Xml映射文件内, 以标签的形式编写动态sql,完成逻辑判断和动态拼接sql的功能。 2)Mybatis提供了9种动态sql标签:trim|where|set|foreach|if|choose|when|otherwise|bind。 3)其执行原理为,使用OGNL从sql参数对象中计算表达式的值, 根据表达式的值动态拼接sql,以此来完成动态sql的功能。 #{}和${}的区别是什么? 1)#{}是预编译处理,${}是字符串替换。 2)Mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值(有效的防止SQL注入); 3)Mybatis在处理${}时,就是把${}替换成变量的值。 为什么说Mybatis是半自动ORM映射工具?它与全自动的区别在哪里? Hibernate属于全自动ORM映射工具, 使用Hibernate查询关联对象或者关联集合对象时, 可以根据对象关系模型直接获取,所以它是全自动的。 而Mybatis在查询关联对象或关联集合对象时, 需要手动编写sql来完成,所以,称之为半自动ORM映射工具。 Mybatis是否支持延迟加载?如果支持,它的实现原理是什么? 1)Mybatis仅支持association关联对象和collection关联集合对象的延迟加载, association指的就是一对一,collection指的就是一对多查询。 在Mybatis配置文件中, 可以配置是否启用延迟加载lazyLoadingEnabled=true|false。 2)它的原理是,使用CGLIB创建目标对象的代理对象, 当调用目标方法时,进入拦截器方法, 比如调用a.getB.getName, 拦截器invoke方法发现a.getB是null值, 那么就会单独发送事先保存好的查询关联B对象的sql, 把B查询上来,然后调用a.setB(b), 于是a的对象b属性就有值了, 接着完成a.getB.getName方法的调用。 这就是延迟加载的基本原理。 MyBatis与Hibernate有哪些不同? 1)Mybatis和hibernate不同,它不完全是一个ORM框架, 因为MyBatis需要程序员自己编写Sql语句, 不过mybatis可以通过XML或注解方式灵活配置要运行的sql语句, 并将java对象和sql语句映射生成最终执行的sql, 最后将sql执行的结果再映射生成java对象。 2)Mybatis学习门槛低,简单易学,程序员直接编写原生态sql, 可严格控制sql执行性能,灵活度高,非常适合对关系数据模型要求不高的软件开发, 例如互联网软件、企业运营类软件等,因为这类软件需求变化频繁, 一但需求变化要求成果输出迅速。但是灵活的前提是mybatis无法做到数据库无关性, 如果需要实现支持多种数据库的软件则需要自定义多套sql映射文件,工作量大。 3)Hibernate对象/关系映射能力强,数据库无关性好, 对于关系模型要求高的软件(例如需求固定的定制化软件) 如果用hibernate开发可以节省很多代码,提高效率。 但是Hibernate的缺点是学习门槛高,要精通门槛更高, 而且怎么设计O/R映射,在性能和对象模型之间如何权衡, 以及怎样用好Hibernate需要具有很强的经验和能力才行。 总之,按照用户的需求在有限的资源环境下只要能做出维护性、 扩展性良好的软件架构都是好架构,所以框架只有适合才是最好。 MyBatis的好处是什么? 1)MyBatis把sql语句从Java源程序中独立出来,放在单独的XML文件中编写, 给程序的维护带来了很大便利。 2)MyBatis封装了底层JDBC API的调用细节,并能自动将结果集转换成Java Bean对象, 大大简化了Java数据库编程的重复工作。 3)因为MyBatis需要程序员自己去编写sql语句, 程序员可以结合数据库自身的特点灵活控制sql语句, 因此能够实现比Hibernate等全自动orm框架更高的查询效率,能够完成复杂查询。 简述Mybatis的Xml映射文件和Mybatis内部数据结构之间的映射关系? Mybatis将所有Xml配置信息都封装到All-In-One重量级对象Configuration内部。 在Xml映射文件中,<parameterMap>标签会被解析为ParameterMap对象, 其每个子元素会被解析为ParameterMapping对象。 <resultMap>标签会被解析为ResultMap对象, 其每个子元素会被解析为ResultMapping对象。 每一个<select>、<insert>、<update>、<delete> 标签均会被解析为MappedStatement对象, 标签内的sql会被解析为BoundSql对象。 什么是MyBatis的接口绑定,有什么好处? 接口映射就是在MyBatis中任意定义接口,然后把接口里面的方法和SQL语句绑定, 我们直接调用接口方法就可以,这样比起原来了SqlSession提供的方法我们可以有更加灵活的选择和设置. 接口绑定有几种实现方式,分别是怎么实现的? 接口绑定有两种实现方式,一种是通过注解绑定,就是在接口的方法上面加 上@Select@Update等注解里面包含Sql语句来绑定, 另外一种就是通过xml里面写SQL来绑定,在这种情况下, 要指定xml映射文件里面的namespace必须为接口的全路径名. 什么情况下用注解绑定,什么情况下用xml绑定? 当Sql语句比较简单时候,用注解绑定;当SQL语句比较复杂时候,用xml绑定,一般用xml绑定的比较多 MyBatis实现一对一有几种方式?具体怎么操作的? 有联合查询和嵌套查询,联合查询是几个表联合查询,只查询一次, 通过在resultMap里面配置association节点配置一对一的类就可以完成; 嵌套查询是先查一个表,根据这个表里面的结果的外键id, 去再另外一个表里面查询数据,也是通过association配置, 但另外一个表的查询通过select属性配置。 Mybatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别? 能,Mybatis不仅可以执行一对一、一对多的关联查询, 还可以执行多对一,多对多的关联查询,多对一查询, 其实就是一对一查询,只需要把selectOne修改为selectList即可; 多对多查询,其实就是一对多查询,只需要把selectOne修改为selectList即可。 关联对象查询,有两种实现方式,一种是单独发送一个sql去查询关联对象, 赋给主对象,然后返回主对象。另一种是使用嵌套查询,嵌套查询的含义为使用join查询, 一部分列是A对象的属性值,另外一部分列是关联对象B的属性值, 好处是只发一个sql查询,就可以把主对象和其关联对象查出来。 MyBatis里面的动态Sql是怎么设定的?用什么语法? MyBatis里面的动态Sql一般是通过if节点来实现,通过OGNL语法来实现, 但是如果要写的完整,必须配合where,trim节点,where节点是判断包含节点有 内容就插入where,否则不插入,trim节点是用来判断如果动态语句是以and 或or 开始,那么会自动把这个and或者or取掉。 Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式? 第一种是使用<resultMap>标签,逐一定义列名和对象属性名之间的映射关系。 第二种是使用sql列的别名功能,将列别名书写为对象属性名, 比如T_NAME AS NAME,对象属性名一般是name,小写, 但是列名不区分大小写,Mybatis会忽略列名大小写,
-
随着人工智能和机器学习的发展,如何在 C# 中有效地集成深度学习框架,以实现复杂的模型训练和预测功能,并且能够在不同的平台上进行部署和优化?
-
[机器学习]集成建模/集成学习:结合多种模型,实现更好的预测
-
综合运用:如何巧妙结合多种模型的集成学习与模型融合方法