导语:
今天是集成学习的第二次任务,本次任务的基本内容是熟悉机器学习建模的基本步骤,认识处理回归问题的常见模型,基于sklearn,使用默认参数训练模型,得到结果。当然,这其中涉及到模型的基础理论原理,由于时间和资源的限制,我其实没有全部手推公式,以后弥补起来吧,这里只记录一些模型基本使用和模型间的对比。首先,附上学习链接:
集成学习: EnsembleLearning项目-github.
1.机器学习建模流程与前期准备
1.1 机器学习项目的一般流程
一般来说,一个完整的机器学习项目分为以下步骤:
(1)明确项目任务:回归/分类
(2)收集数据集并选择合适的特征。
(3)选择度量模型性能的指标。
(4)选择具体的模型并进行训练以优化模型。
(5)评估模型的性能并调参。
本次任务,我们的数据集还是选择上期的boston房价预测数据,自然地,这是一个回归问题。关于特征,我们目前全部使用,看看效果,后期再调整。
1.2 回归模型的度量指标
在回归问题中,度量模型性能的指标一般有:
本文主要选择MSE和
R
2
R^2
R2来评价模型,更多的评价指标可参考sklearn的API:
sklearn回归评价指标
1.3 回归模型的选择
在这一部分,我只是简单介绍常见的用于回归的模型,对于其具体原理和公式推导就不再展示,需要查阅更多详尽的资料进行学习,推荐使用西瓜书和南瓜书!
(1)线性回归模型
通过最小二乘法来求解参数
w
w
w
注意:在线性回归中,我们假设因变量与特征之间的关系是线性关系,这样的假设使得模型很简单,但是缺点也是显然的,那就是当数据存在非线性关系时,我们使用线性回归模型进行预测会导致预测性能极其低下,因为模型的形式本身是线性的,无法表达数据中的非线性关系。我们一个很自然的想法就是去推广线性回归模型,使得推广后的模型更能表达非线性的关系。
推广:
(a)多项式回归
将线性回归的各项换成多项式,注意,对于多项式的阶数d不能取过大,一般不大于3或者4,因为d越大,多项式曲线就会越光滑,在X的边界处有异常的波动。
多项式回归实例
(b)广义可加模型
上式就是广义可加模型GAM的基本框架,GAM实际上是线性模型推广至非线性模型的一个框架,在这个框架中,每一个变量都用一个非线性函数来代替,但是模型本身保持整体可加性。GAM模型不仅仅可以用在线性回归的推广,还可以将线性分类模型进行推广。
GAM模型的优点与不足:
优点:简单容易操作,能够很自然地推广线性回归模型至非线性模型,使得模型的预测精度有所上升;由于模型本身是可加的,因此GAM还是能像线性回归模型一样把其他因素控制不变的情况下单独对某个变量进行推断,极大地保留了线性回归的易于推断的性质。
缺点:GAM模型会经常忽略一些有意义的交互作用,比如某两个特征共同影响因变量,不过GAM还是能像线性回归一样加入交互项 𝑥(𝑖)×𝑥(𝑗) 的形式进行建模;但是GAM模型本质上还是一个可加模型,如果我们能摆脱可加性模型形式,可能还会提升模型预测精度,比如接下来的算法。
GAM模型实例
(2)回归树
基于树的回归方法主要是依据分层和分割的方式将特征空间划分为一系列简单的区域。对某个给定的待预测的自变量,用他所属区域中训练集的平均数或者众数对其进行预测。由于划分特征空间的分裂规则可以用树的形式进行概括,因此这类方法称为决策树方法。决策树由结点(node)和有向边(diredcted edge)组成。结点有两种类型:内部结点(internal node)和叶结点(leaf node)。内部结点表示一个特征或属性,叶结点表示一个类别或者某个值。
树模型的优缺点:
树模型的解释性强,在解释性方面可能比线性回归还要方便。
树模型更接近人的决策方式。
树模型可以用图来表示,非专业人士也可以轻松解读。
树模型可以直接做定性的特征而不需要像线性回归一样哑元化。
树模型能很好处理缺失值和异常值,对异常值不敏感,但是这个对线性模型来说却是致命的。
树模型的预测准确性一般无法达到其他回归模型的水平,但是改进的方法很多。
sklearn回归树实例
sklearn.tree.DecisionTreeRegressor(*, criterion=‘mse’, splitter=‘best’, max_depth=None, min_samples_split=2, min_samples_leaf=1, min_weight_fraction_leaf=0.0, max_features=None, random_state=None, max_leaf_nodes=None, min_impurity_decrease=0.0, min_impurity_split=None, presort=‘deprecated’, ccp_alpha=0.0)
参数:(列举几个重要的,常用的,详情请看上面的官网)
criterion:{“ mse”,“ friedman_mse”,“ mae”},默认=“ mse”。衡量分割标准的函数 。
splitter:{“best”, “random”}, default=”best”。分割方式。
max_depth:树的最大深度。
min_samples_split:拆分内部节点所需的最少样本数,默认是2。
min_samples_leaf:在叶节点处需要的最小样本数。默认是1。
min_weight_fraction_leaf:在所有叶节点处(所有输入样本)的权重总和中的最小加权分数。如果未提供sample_weight,则样本的权重相等。默认是0。
(3)支持向量机回归(SVR)
SVR实例
sklearn.svm.SVR(*, kernel=‘rbf’, degree=3, gamma=‘scale’, coef0=0.0, tol=0.001, C=1.0, epsilon=0.1, shrinking=True, cache_size=200, verbose=False, max_iter=-1)
参数:
kernel:核函数,{‘linear’, ‘poly’, ‘rbf’, ‘sigmoid’, ‘precomputed’}, 默认=’rbf’。(后面会介绍)
degree:多项式核函数的阶数。默认 = 3。
C:正则化参数,默认=1.0。(后面会详细介绍)
epsilon:SVR模型允许的不计算误差的邻域大小。默认0.1。
(4)随机森林
集成学习的一种,将多棵决策树进行捆绑,用均值或者众数确定最终结果。
2.机器学习模型训练与对比
2.1 导入数据集
导入Boston数据集,并查看每个特征的含义,为方便后续模型的对比,这里先统一将数据集进行划分,75%的数据用于训练,25%的数据用于测试。
#数据导入与数据划分
from sklearn import datasets
import pandas as pd
from sklearn.model_selection import train_test_split
boston = datasets.load_boston() # 返回一个类似于字典的类
X = boston.data
y = boston.target
features = boston.feature_names
boston_data = pd.DataFrame(X,columns=features)
boston_data["Price"] = y
#boston_data.head()
#数据划分
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.25)
数据集前几行:
各个特征的相关解释:
CRIM:各城镇的人均犯罪率
ZN:规划地段超过25,000平方英尺的住宅用地比例
INDUS:城镇非零售商业用地比例
CHAS:是否在查尔斯河边(=1是)
NOX:一氧化氮浓度(/千万分之一)
RM:每个住宅的平均房间数
AGE:1940年以前建造的自住房屋的比例
DIS:到波士顿五个就业中心的加权距离
RAD:放射状公路的可达性指数
TAX:全部价值的房产税率(每1万美元)
PTRATIO:按城镇分配的学生与教师比例
B:
1000
(
B
k
−
0.63
)
2
1000(Bk - 0.63)^2
1000(Bk−0.63)2其中Bk是每个城镇的黑人比例
LSTAT:较低地位人口
Price:房价
2.2 模型训练,结果对比
这里针对同样的训练集和测试集,依次使用上面四种算法进行训练:
#使用各类回归模型进行建模预测
from sklearn.metrics import mean_squared_error
#定义列表,保存模型预测结果
test_score = [] #存放各模型测试集得分
test_mse = []
all_score = []
all_mse = []
#1.线性回归
from sklearn import linear_model # 引入线性回归方法
lin_reg = linear_model.LinearRegression() # 创建线性回归的类
lin_reg.fit(X_train,y_train) # 输入特征X和因变量y进行训练
lin_pred_test = lin_reg.predict(X_test)
lin_pred_all = lin_reg.predict(X)
#print("模型系数:",lin_reg.coef_) # 输出模型的系数
print("----1.线性回归----")
print("测试集得分:",lin_reg.score(X_test,y_test)) # 输出模型的决定系数R^2
print("测试集MSE:",mean_squared_error(lin_pred_test,y_test))
print("全集得分:",lin_reg.score(X,y))
print("全集MSE:",mean_squared_error(lin_pred_all,y))
test_score.append(lin_reg.score(X_test,y_test))
test_mse.append(mean_squared_error(lin_pred_test,y_test))
all_score.append(lin_reg.score(X,y))
all_mse.append(mean_squared_error(lin_pred_all,y))
#2.决策树回归
from sklearn.tree import DecisionTreeRegressor
reg_tree = DecisionTreeRegressor(criterion = "mse",min_samples_leaf = 5)
reg_tree.fit(X_train,y_train)
rt_pred_test = reg_tree.predict(X_test)
rt_pred_all = reg_tree.predict(X)
print("----2.决策树回归----")
print("测试集得分:",reg_tree.score(X_test,y_test)) # 输出模型的决定系数R^2
print("测试集MSE:",mean_squared_error(rt_pred_test,y_test))
print("全集得分:",reg_tree.score(X,y))
print("全集MSE:",mean_squared_error(rt_pred_all,y))
test_score.append(reg_tree.score(X_test,y_test))
test_mse.append(mean_squared_error(rt_pred_test,y_test))
all_score.append(reg_tree.score(X,y))
all_mse.append(mean_squared_error(rt_pred_all,y))
#3.支持向量机回归
from sklearn.svm import SVR
from sklearn.preprocessing import StandardScaler # 标准化数据
from sklearn.pipeline import make_pipeline # 使用管道,把预处理和模型形成一个流程
reg_svr = make_pipeline(StandardScaler(), SVR(C=1.0, epsilon=0.2))
reg_svr.fit(X_train,y_train)
svr_pred_test = reg_svr.predict(X_test)
svr_pred_all = reg_svr.predict(X)
print("----3.支持向量机回归----")
print("测试集得分:",reg_svr.score(X_test,y_test)) # 输出模型的决定系数R^2
print("测试集MSE:",mean_squared_error(svr_pred_test,y_test))
print("全集得分:",reg_svr.score(X,y))
print("全集MSE:",mean_squared_error(svr_pred_all,y))
test_score.append(reg_svr.score(X_test,y_test))
test_mse.append(mean_squared_error(svr_pred_test,y_test))
all_score.append(reg_svr.score(X,y))
all_mse.append(mean_squared_error(svr_pred_all,y))
#4.随机森林回归
from sklearn.ensemble import RandomForestRegressor
rfr = RandomForestRegressor()
rfr.fit(X_train,y_train)
rfr_pred_test = rfr.predict(X_test)
rfr_pred_all = rfr.predict(X)
print("----4.随机森林回归----")
print("测试集得分:",rfr.score(X_test,y_test)) # 输出模型的决定系数R^2
print("测试集MSE:",mean_squared_error(rfr_pred_test,y_test))
print("全集得分:",rfr.score(X,y))
print("全集MSE:",mean_squared_error(rfr_pred_all,y))
test_score.append(rfr.score(X_test,y_test))
test_mse.append(mean_squared_error(rfr_pred_test,y_test))
all_score.append(rfr.score(X,y))
all_mse.append(mean_squared_error(rfr_pred_all,y))
#可视化
import matplotlib
import matplotlib.pyplot as plt
plt.figure(1)
plt.plot(y_test,'g',label='real')
plt.plot(lin_pred_test,'r',label='lin_pred')
plt.plot(rt_pred_test,'b',label='rt_pred')
plt.plot(svr_pred_test,'black',label='svr_pred')
plt.plot(rfr_pred_test,'y',label='rfr_pred')
plt.xlabel("house", fontsize=12)
plt.ylabel("price", fontsize=12)
plt.title("测试集表现")
plt.legend()
plt.rcParams['font.sans-serif']=['SimHei']
plt.figure(2)
plt.plot(y,'g',label='real')
plt.plot(lin_pred_all,'r',label='lin_pred')
plt.plot(rt_pred_all,'b',label='rt_pred')
plt.plot(svr_pred_all,'black',label='svr_pred')
plt.plot(rfr_pred_all,'y',label='rfr_pred')
plt.xlabel("house", fontsize=12)
plt.ylabel("price", fontsize=12)
plt.title("全集表现")
plt.legend()
plt.rcParams['font.sans-serif']=['SimHei']
x_list = ["线性回归","决策树回归","支持向量回归","随机森林回归"]
plt.figure(3)
plt.plot(x_list,test_score,'g-o',label='test')
plt.plot(x_list,all_score,'b-o',label='all')
plt.xlabel("models", fontsize=12)
plt.ylabel("R_score", fontsize=12)
plt.title('R_score')
plt.legend()
plt.rcParams['font.sans-serif']=['SimHei']
matplotlib.rcParams['axes.unicode_minus'] =False
plt.figure(4)
plt.plot(x_list,test_mse,'g-o',label='test')
plt.plot(x_list,all_mse,'b-o',label='all')
plt.xlabel("models", fontsize=12)
plt.ylabel("MSE", fontsize=12)
plt.title('MSE')
plt.legend()
plt.rcParams['font.sans-serif']=['SimHei']
matplotlib.rcParams['axes.unicode_minus'] =False
训练结果与误差可视化:
从各模型的
R
2
R^2
R2得分来看,随机森林最高,决策树其次,然后是线性回归,最差的是支持向量机回归,模型的MSE也保持同样的结论。说明,树模型对于此数据集的预测效果较好,随机森林在全集上更是保持了0.93的
R
2
R^2
R2分数,相比于决策树,符合集成学习的常识认知,这也反映出数据集的特征可能更加倾向于非线性关系。而SVR的效果很差,可能它对模型参数比较敏感,也可能与本身处理回归问题的特质有关。注意,上面的模型基本都使用的是默认参数,没有经过任何优化,理论上后续优化处理可以进一步提升模型的精度。
我们将取得最好效果的随机森林单独展示一下:
可以看出,RF在全集的表现还是挺好的,但误差图中,明显存在一个极大的异常点,若出现这种情况,就得好好分析一下原始数据集,若存在异常值,在数据预处理过程中就应该去除,否则会影响后续模型的训练。
为探究特征对RF模型的影响程度,我们用随机森林自带的特征重要性来观察:
#特征重要性分布,按大到小排列
feat_labels = boston.feature_names
importances = rfr.feature_importances_
indices = np.argsort(importances)[::-1]
xl_list=[]
yl_list=[]
for f in range(len(boston.feature_names)):
print("%2d) %-*s %f" % (f + 1, 30, feat_labels[indices[f]], importances[indices[f]]))
xl_list.append(feat_labels[indices[f]])
yl_list.append(importances[indices[f]])
plt.figure(figsize=(15, 4)) #调整画布大小
sns.barplot(x=xl_list, y=yl_list)
plt.title('Random_forest_regressor feature_importances', fontsize=25)
plt.ylabel('importance', fontsize=20)
#plt.savefig('随机森林feature_importances.jpg', dpi=300)
结果及可视化:
从图中可以看出,特征LSTAT(较低地位人口)的重要性最高,RM(每个住宅的平均房间数)其次。而剩下的其他特征的重要性与之相比要弱很多,值得注意的是,LSTAT和RM两个特征的重要性之和0.84,理论上,就这两个特征就能表达数据的绝大部分信息,这里推测它们和房价的关系应该很明显,或是接近线性的强相关关系。经验上来说,较低地位人口越多,房价应该会越低,而每个住宅的平均房间数越多,房价则会越高,这样符合一般常识。
补充:利用随机森林的特征重要性分析,在某些特征杂乱和复杂的时候,可以带来数据降维的效果,去除一些重要性很低的特征,提升模型效率,节省训练时间。
2.3 数据特征分析
按道理,数据的特征分析应当属于数据预处理环节。这里为了对模型训练的特征进行解释,我们补充了这一环节,利用相关系数矩阵及热力图进行分析:
#pairplot多变量图与相关系数热力图
import matplotlib
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
# 利用数据分析工具可视化特征两两间的关系
sns.set(style='whitegrid', context='notebook')
sns.pairplot(boston_data, height=2.5)
#plt.savefig('Boston特征矩阵图.jpg', dpi=300)
plt.show()
# 可视化相关系数矩阵,理论:皮尔逊相关系数
plt.figure(figsize=(15,12))
cm = np.corrcoef(boston_data.values.T)
sns.set(font_scale=1.5)
hm = sns.heatmap(cm,
cbar=True,
annot=True,
square=True,
fmt='.2f',
annot_kws={'size':15},
yticklabels=boston.feature_names,
xticklabels=boston.feature_names)
#plt.savefig('Boston相关热力图.jpg', dpi=300)
plt.show()
可视化:
从热力图中可以看出,特征LSTAT(较低地位人口)和RM(每个住宅的平均房间数)与房价的相关系数分别为-0.74和0.70,从矩阵图中也可以看出它们与房价的线性关系明显,这也印证了我们上节末尾的推测,证明我们的数据分析和建模是有效的。
相关系数矩阵图能直观地看出每个特征与标签之间或每个两两特征之间的相关关系,而热力图可以量化这些关系,在进行模型训练之前,观察和提取出有用的特征,分析特征之间的规律,对数据建模很有必要,高质量的特征才能使模型更加有效。
3.后记
本次内容是对机器学习中常用的回归模型进行了解和训练,省去了模型的理论推导内容,为加深理解和应用,后续可以结合更加详尽的资料自己手推公式。
本次分析的四个模型的训练中,全部使用的是默认参数,没经过任何处理和优化,各模型实例的具体参数和用法可以在sklearn API中查找,为更加深入应用,这也是必须掌握的技能。
在分析完基本的模型之后,我们就可以选择一个具体的模型,并进行后续的训练优化,更重要的是,评估模型的性能并调参,直到实现满意的效果。
期待后续的模型优化更新吧!