支持向量机
import numpy as np
from sklearn import datasets
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import StandardScaler
from sklearn.svm import LinearSVC
iris = datasets.load_iris()
X = iris["data"][:, (2, 3)] # petal length, petal width
y = (iris["target"] == 2).astype(np.float64) # Iris virginica
svm_clf = Pipeline([
("scaler", StandardScaler()), # SVM 对特征缩放敏感
("linear_svc", LinearSVC(C=1, loss="hinge", random_state=42)), # 这个损失函数就是 max(0, 1-x)
]) # C 越小模型越容忍错误,返回能力越强
svm_clf.fit(X, y)
svm_clf.predict([[5.5, 1.7]])
非线性SVM 分类
很多数据集不是线性可分的,处理这种的方法就是添加更多的特征,比如多项式特征。
from sklearn.datasets import make_moons # 二维的数据集
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures
polynomial_svm_clf = Pipeline([
("poly_features", PolynomialFeatures(degree=3)), # 添加个特征
("scaler", StandardScaler()),
("svm_clf", LinearSVC(C=10, loss="hinge", random_state=42))
])
polynomial_svm_clf.fit(X, y)
多项式内核
使用核技巧,与多项式的效果但实际上没有添加(防止特征太多)
from sklearn.svm import SVC
poly_kernel_svm_clf = Pipeline([
("scaler", StandardScaler()),
("svm_clf", SVC(kernel="poly", degree=3, coef0=1, C=5)) # 3阶多项式内核
]) # coef0 是控制模型受高阶多项式还是低阶多项式的影响程度
poly_kernel_svm_clf.fit(X, y)
相似特征
也是解决非线性的问题,通过相似函数(比如高斯RBF) 来计算相似特征,相似函数就是测量每个实例与一个特定的地标之间的相似度。
以 -2, 1 为地标,计算每个元素的相似度(相同为1),然后进行转换数据集,就可以发现可以线性分离了。地标的选择,最简单的方式就是每个实例的位置上建立一个地标,这样可以增加转换后数据集线性可分的概率。
rbf_kernel_svm_clf = Pipeline([
("scaler", StandardScaler()),
("svm_clf", SVC(kernel="rbf", gamma=5, C=0.001)) # 使用高斯RBF 核
]) # gamma变大,每个实例影响范围变小,决策边界变得不规则。模型过拟合降低,欠拟合提高(类似于C)
rbf_kernel_svm_clf.fit(X, y)
SVM 回归
与拟合两个类之间的边界不同,回归需要让尽可能多的实例位于边界上,边界的宽度由超参数 ε 控制
from sklearn.svm import LinearSVR # 使用这个来进行回归
svm_reg = LinearSVR(epsilon=1.5, random_state=42) # 数据需要缩放的
svm_reg.fit(X, y)
svm_poly_reg = SVR(kernel="poly", degree=2, C=100, epsilon=0.1, gamma="scale") # 二次多项式核的SVM 回归
svm_poly_reg.fit(X, y) # 这里的C 很大,几乎没有正则化的
决策树
可执行分离回归,甚至多输出任务。可以拟合复杂的数据。
训练
from sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier
iris = load_iris()
X = iris.data[:, 2:] # petal length and width
y = iris.target
tree_clf = DecisionTreeClassifier(max_depth=2, random_state=42) # 最大深度(层数)
tree_clf.fit(X, y) # 这个最大深度起到正则化的作用。减少深度有利于降低过拟合的风险。
决策数的特质之一就是不需要数据准备(特征缩放,居中之类的)。
模型解释:白盒子与黑盒子:
决策树是直观的,其决策也易于解释。这种模型通常称为白盒模
型。相反,正如我们将看到的,通常将随机森林或神经网络视为黑盒
模型。它们做出了很好的预测,你可以轻松地检查它们为做出这些预
测而执行的计算。但是,通常很难用简单的话语来解释为什么做出这
样的预测。例如,如果神经网络说某个人出现在图片上,那么很难知
道是什么因素促成了这一预测:该模型识别该人的眼睛、嘴、鼻子、
鞋子,甚至他们坐的沙发?相反,决策树提供了很好的、简单的分类
规则,如果需要的话,甚至可以手动应用这些规则(例如,用于花的
分类)。
估计类概率
决策树可以估算某个实例属于特定类的概率。就是该实例叶节点中占训练实例的比例。
tree_clf.predict_proba([[5, 1.5]])
array([[0. , 0.90740741, 0.09259259]]) # 三个类的概率
tree_clf.predict([[5, 1.5]])
array([1])
回归
每个节点不是预测一个类而是一个值(该区域内实例的平均值)。
from sklearn.tree import DecisionTreeRegressor
tree_reg = DecisionTreeRegressor(max_depth=2, random_state=42)
tree_reg.fit(X, y)
DecisionTreeClassifier类还有一些其他的参数,同样可以限制决策
树的形状:min_samples_split(分裂前节点必须有的最小样本数)、
min_samples_leaf(叶节点必须有的最小样本数量)、
min_weight_fraction_leaf(与min_samples_leaf一样,但表现为加权实例
总数的占比)、max_leaf_nodes(最大叶节点数量),以及
max_features(分裂每个节点评估的最大特征数量)。增大超参数
min_*或减小max_*将使模型正则化。
tree_reg1 = DecisionTreeRegressor(random_state=42)
tree_reg2 = DecisionTreeRegressor(random_state=42, min_samples_leaf=10)
集成学习和随机森林
聚合一组预测器的预测,得到的结果好于单个预测器。这就是集成学习,集成学习算法称为集成方法。例如一组决策树分类器,每棵树都是基于训练集的不同随机子集进行训练,进行预测时需要获得所有树各自的预测然后给出预测数目最高的类最为结果。这样就是随机森林了。这时最强的的机器学习算法之一。
投票分类器
一些分类器进行预测,聚合每个结果将得票最多的最为结果这种称为硬投票分类器。一般来说准确率比集成中最好的分类器还要高,即使每个分类器都是弱分类器(比随机分类好一些)。
但是需要各个分类器尽量独立(使用不同的算法),彼此的错误毫不相干(增加反不同错误的机会)。
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import VotingClassifier # 创建一个投票分类器
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
log_clf = LogisticRegression(solver="lbfgs", random_state=42)
rnd_clf = RandomForestClassifier(n_estimators=100, random_state=42)
svm_clf = SVC(gamma="scale", random_state=42)
voting_clf = VotingClassifier(
estimators=[('lr', log_clf), ('rf', rnd_clf), ('svc', svm_clf)],
voting='hard') # 硬投票 or soft
voting_clf.fit(X_train, y_train)
from sklearn.metrics import accuracy_score
for clf in (log_clf, rnd_clf, svm_clf, voting_clf): # 测试一下每个分类器的精度
clf.fit(X_train, y_train)
y_pred = clf.predict(X_test)
print(clf.__class__.__name__, accuracy_score(y_test, y_pred))
>>>
LogisticRegression 0.864
RandomForestClassifier 0.896
SVC 0.896
VotingClassifier 0.912 # 集成后的精度是更高的
如果分类器有 predict_proba() 方法可以估算出类别的概率,可以将概率在所有单个分类器上平均,然后计算出平均概率最高的类最为结果,这就是软投票法。一般来说比硬投票更好,可以给那些高度自信的投票更高的权重,只需要 voting = 'soft‘ 即可。并确保所有分类器都可以估算概率(SVC要调整,需要将超参数probability=True,进行交叉验证估计概率,添加 predict_proba() 方法)。
bagging and pasting
获得不同种类的分类器的方法除了使用不同算法还有:使用相同的算法,但是再不同的训练集的随机子集上训练。采样时如果将样本放回就叫 bagging ,不放回就是 pasting 。也就是说让训练实例再多个预测器中被多次采样,bagging允许训练实例可以让一个预测器多次采样。训练完成就采用聚合函数(统计法用于分类,平均法用于回归)。
每个预测器单独的偏差高于再原始数据上的训练偏差,但通过聚合,集成的偏差相近,但方差更低。
from sklearn.ensemble import BaggingClassifier # 使用简单的API 进行分类或 BaggingRegressor 回归
from sklearn.tree import DecisionTreeClassifier
bag_clf = BaggingClassifier(
DecisionTreeClassifier(random_state=42), n_estimators=500, # 训练一个包含500个决策数的分类器
max_samples=100, bootstrap=True, random_state=42) # 每次从训练集随机采样100个实例进行训练,然后放回
bag_clf.fit(X_train, y_train) # bootstrap=False 使用pasting,n_jobs=-1 使用虽有可用的CPU 内核
y_pred = bag_clf.predict(X_test) # 如果分类器有 predict_proba() 方法就自动进行软投票。
可用看到集成预测的泛化效果更好,二者偏差相近(训练集错误的数量差不多),方差更小(集成的边界更加规则)。
包外评估
对于给定的预测器,使用bagging,有些实例被多次采样,有些实例就不会被采样。大概有 37 % 37\% 37% 的实例不会被采样,这些就叫做 包外实例 (obb),预测器再训练中没有看过这些 Obb,所以可用再这些实例上进行评估,而不同单独的验证集了。
bag_clf = BaggingClassifier( # 只需要 oob_score= True,再训练结束后自动进行包外评估。
DecisionTreeClassifier(random_state=42), n_estimators=500,
bootstrap=True, oob_score=True, random_state=40) # 也支持特征采样 max_feature, bootstrap_feature 用法与 max_samples,bootstrap 相同
bag_clf.fit(X_train, y_train)
bag_clf.oob_score_
>>> 0.9013333333333333
bag_clf.oob_decision_function_ # 返回包外决策函数对每个实例的类别概率
# [0.31746032, 0.68253968], 比如这个,正类概率是68%
随机森林
是决策树的集成,用bagging 来训练,训练集的大小通过max_samples 设置。
bag_clf = BaggingClassifier(
DecisionTreeClassifier(splitter="random", max_leaf_nodes=16, random_state=42),
n_estimators=500, max_samples=1.0, bootstrap=True, random_state=42)
from sklearn.ensemble import RandomForestClassifier # 这种方法优化更多
# 还有 RandomForestRegressor 类
rnd_clf = RandomForestClassifier(n_estimators=500, max_leaf_nodes=16, random_state=42)
rnd_clf.fit(X_train, y_train) # 500棵树,每棵树最多16个叶子节点。
y_pred_rf = rnd_clf.predict(X_test) # 具有baggingClassifier and DescisionTreeClasifier 的超参数
极端随机树:随机森林里单棵树的生长中,每个节点的分裂仅考虑一个随机子集包含的特征,如果对每个特征使用随机阈值,而不是搜索得出的最佳阈值就可以让决策树生长的更加随机。使用更高的偏差换取更低的方差。训练起来也更快(寻找最佳阈值是最耗时间的)。
使用 ExtraTreesClassifier 类来构造一个极端随机森林,参数与普通的一样 。但一般来说这俩哪个好是不一定的,最好两个都试试,然后用交叉验证来比较一下。
特征重要性
随机森林的另一个特性就是可用测量每个特征的相对重要性。再训练后为为每个特征自动计算分数,对结果进行缩放让重要性的和为1 。
from sklearn.datasets import load_iris
iris = load_iris()
rnd_clf = RandomForestClassifier(n_estimators=500, random_state=42)
rnd_clf.fit(iris["data"], iris["target"])
for name, score in zip(iris["feature_names"], rnd_clf.feature_importances_): # 使用这个变量
print(name, score)
>>>
sepal length (cm) 0.11249225099876374
sepal width (cm) 0.023119288282510326
petal length (cm) 0.44103046436395765 # 长度和宽度是比较重要的
petal width (cm) 0.4233579963547681
这就有利于特征的选择了。
提升法
就是将几个弱学习器结合成一个强学习器的任意集成方法。大多数的总体思路就是:循环训练预测器,每次对上一次进行一些改进。
AdaBoost
训练这个分类器时,首先训练一个基础分类器(决策树之类的),然后对训练集进行预测,算法会根据分类错误的训练实例增加权重,使用更新后的权重训练第二个分类器,如此循环。
再数据集上5个连续的预测器的决策边界,大致时越来越好的。可用看出AdaBoost 与SGD 差不多,区别就是SGD 是调整单个预测器的参数,让代价函数最小化,这个是不断的再集成中加入调整后的预测器,让模型越来越好。
这种方法的缺陷就是无法并行,不如bagging 方法的扩展性,
from sklearn.ensemble import AdaBoostClassifier # 当然也有 AdaBoostRegressor 类
ada_clf = AdaBoostClassifier(
DecisionTreeClassifier(max_depth=1), n_estimators=200, # 200个单层决策树(一个节点两个叶节点)
algorithm="SAMME.R", learning_rate=0.5, random_state=42) # 是默认使用的基础估算器
ada_clf.fit(X_train, y_train) # algorithm 使用的算法。。如果结果过拟合,可用试试减少n_estimators,提高基础估算器的正则化程度。
梯度提升
不AdaBoost 差不多,差别就是不像AdaBoost 再每个迭代中调整实例权重,而是让新的预测器针对前一个预测器的残差进行拟合。
tree_reg1 = DecisionTreeRegressor(max_depth=2, random_state=42)
tree_reg1.fit(X, y)
y2 = y - tree_reg1.predict(X) # 这就是残差
tree_reg2 = DecisionTreeRegressor(max_depth=2, random_state=42)
tree_reg2.fit(X, y2)
from sklearn.ensemble import GradientBoostingRegressor
gbrt = GradientBoostingRegressor(max_depth=2, n_estimators=3, learning_rate=1.0, random_state=42) # 具有控制决策树生长的超参数(max_depth等),以及控制集成训练的超参数(n_estimators树的数量)
gbrt.fit(X, y) # learn_rate 对每棵树的贡献进行缩放,如果很低0.1 就需要更多的树来拟合训练集,但是泛化效果更好
#
图错了,学习率都是0.1 .。可以看到树的数量过多就出现了过拟合的情况。要找树的最佳数量,可以使用提前停止法。简单的就是使用 staged_predict() 方法,再训练的每个阶段(一个树,两个数。。)都对集成的预测返回一个迭代器。
import numpy as np
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
X_train, X_val, y_train, y_val = train_test_split(X, y, random_state=49)
gbrt = GradientBoostingRegressor(max_depth=2, n_estimators=120, random_state=42)
gbrt.fit(X_train, y_train) # 训练一个120棵树的集成
errors = [mean_squared_error(y_val, y_pred) for y_pred in gbrt.staged_predict(X_val)] # 逐个计算误差
bst_n_estimators = np.argmin(errors) + 1 # 最好的那个
gbrt_best = GradientBoostingRegressor(max_depth=2, n_estimators=bst_n_estimators, random_state=42)
gbrt_best.fit(X_train, y_train)
提前停止也不一定就需要训练大量的数,再回头找最优数字,还可以提前停止训练,设置warm_start=True,调用fit() 方法时,会保留现有的数,从而进行增量计算。
gbrt = GradientBoostingRegressor(max_depth=2, warm_start=True, random_state=42)
# 还有参数subsample=0.25 每棵树用25% 的随机选择的实例进行训练,用偏差换取更低的方差,加速训练(随机梯度提升)
min_val_error = float("inf")
error_going_up = 0
for n_estimators in range(1, 120): # 从一颗开始
gbrt.n_estimators = n_estimators # 外部调整超参数
gbrt.fit(X_train, y_train)
y_pred = gbrt.predict(X_val)
val_error = mean_squared_error(y_val, y_pred)
if val_error < min_val_error:
min_val_error = val_error
error_going_up = 0
else:
error_going_up += 1
if error_going_up == 5:
break # early stopping 再连续5次迭代没有改善时就停止了