前言
这是一篇Scikit-Learn的学习笔记,主要有环境配置、基本模块和实际案例等内容。这篇文章中我会介绍一下我用机器学习做城市分析任务的基本思路。Scikit-Learn是一个用于构建机器学习模型的Python库,不仅提供了数十种基础机器学习算法,而且配备了数据预处理和模型评估工具。
Scikit-Learn介绍
安装方法
Scikit-Learn的安装十分简单,用pip和anaconda均可,下面是pip的示例:
pip install scikit-learn
主要特性
在正式介绍之前,先简要介绍一下sklearn的一些特点:(1)提供有监督、无监督和半监督学习算法等多种机器学习算法;(2)包括数据预处理、特征选择与提取、超参数调优、模型评估等工具;(3)API清晰一致;(4)说明文档:https://scikit-learn.org;(5)运行性能良好,与Numpy、Pandas、Scipy等很好地集成
API
Sklearn的api定义的三个主要对象是估计器(estimators)、预测器(predictors)和转换器(transformers)。
(1)估计器是从数据中学习的任何对象,比如分类器、回归器、变换器。
每个估计器都有_init_()方法,接受估计器行为的参数,这些通常是底层机器学习模型的超参数(如神经网络中的学习率)或与模型运行环境相关的参数(如用于训练的CPU核心数)等。
每个估计器的fit()方法根据给定的数据集构造模型,如:
estimator.fit(X,y)
X是形状为(n_samples, n_features)的类数组对象,即行代表样本,列代表特征,X可以是列表、元组、Numpy数组或Dataframe。y是形状为(n_samples,)的类数组对象,包含X中样本的标签。在无监督学习模型中,此参数被忽略(默认值为None)。调用fit()后,学习得到的参数存储在估计器属性中:
estimator.learned_param_
# DecisionTreeClassifier中的底层决策树存储
DecisionTreeClassifier.tree_
每个估计器都有get_params()和set_params()方法,get_params()不接受任何参数,返回估计器的所有参数(传递给其构造函数的参数)及其值的字典,set_params()允许覆盖估计器构造函数中设置的参数。它以关键字参数的形式接受参数,并更新估计器中相应的属性,如:
estimator.set_params(max_depth=5)
(2)预测器是一种可以进行预测的估计器,能够为新样本分配新的标签:
y_pred = estimator.predict(X)
下图是预测器的基本工作流程:
(3)转换器是一种过滤或修改数据的对象,通常用于为构建机器学习模型做准备。sklearn提供了归一化、缩放、离散化、填充缺失值、编码分类特征、特征提取等的转换器。每个转换器都提供fit()方法,该方法从训练数据中估计相关模型参数。例如,StandardScaler是一种对数据进行归一化的转换器,在其fit()方法中计算训练集中每个特征的均值和标准差。
需要注意:转换器的transfrom方法应该用在训练集的测试集上,因为在对测试集进行归一化时,我们希望使用与训练集估计的一样的均值和方差,所以fit()方法只能在训练集上调用一次。以下是转换器的基本工作流程:
案例实践
我用随机森林模型演示一下完成一个完整的分析,项目内容是关于城乡一体化综合绩效的影响因素探测。首先,导入所需要的库:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
第二步,加载数据,得到样本特征X和样本标签y:
df = pd.read_csv("1520.csv",encoding="gbk")
df.head()
X = df.iloc[:,6:]
y = df.iloc[:,5]
第三步,进行探索性数据分析(EDA)
# 检查数据类型及是否有缺失值
X.info()
检查数据之间、特征与目标之间的相关型,目的是:(1)与目标高度相关的特征是良好的预测因子,我们应该保留它们。(2)与目标弱相关的特征可以合并成一个更能表示目标的新特征。(3)彼此之间强相关的特征可能会导致某些算法的不稳定性问题,并使模型的可解释性降低(这是一种称为多重共线性的问题)。在这种情况下,我们可以考虑使用特征选择来去除一些冗余特征。
df_cor = pd.concat([X,y],axis=1) # 将两个dataframe按列方向连接,以便进行进一步的分析建模
sns.heatmap(df_cor.corr(),annot=True,cmap="coolwarm",annot_kws={'size':6})
# df.corr(): 这部分计算了DataFrame df 中各列之间的相关系数
# corr()函数用于计算列之间的相关性,返回一个相关性矩阵,其中的值表示各列之间的相关性程度
# annot=True: 这个参数设置为True表示在热图上显示相关性系数的数值。这样可以更清晰地了解各列之间的相关性
# cmap="coolwarm": 这个参数指定了用于着色热图的颜色映射。
检查特征及目标标签的分布,以相互之间的散点关系(可以通过散点图粗略判断相互关系):
sns.pairplot(df_cor.iloc[:,:5])
如果变量太多,可以分批次运行。不分批次的话也可以保存为高质量图片查看。
最后再看看有没有异常值,这里用到seaborn的箱线图。同样的,太多变量建议分批次查看,而且此时还没有归一化,量纲不同就会出现下面图中的情况:
plt.rcParams["font.size"] = 5
sns.boxplot(data=df_cor.iloc[:,1:])
第四步,拆分数据集,在对数据集进行任何处理之前,应该将一部分数据用于测试,并将其与训练过程分开。可以使用train_test_split()函数。由于我们不过分追求模型的泛化能力,就不设置验证集了。在做深度学习的时候或者需要多次调整超参数的时候,还是设置好验证集,以免污染测试集。
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X,y,random_state=0)
# random_state参数是在拆分之前用于洗牌数据的随机种子,从而使输出在多次函数调用中具有可重现性
print(X_train.shape) #查看训练集和测试集的形状
print(X_test.shape)
'''
(60, 14)
(20, 14)
'''
第五步,清理和预处理训练集。因为都是数值类型且没有缺失值,只需要进行归一化即可。创建一个StandardScaler转换器,通过去除平均值并将特征缩放为单位方差来对特征进行归一化,然后在训练集上调用其fit_transform()方法。
from sklearn.preprocessing import StandardScaler(归一化后的训练样本特征)
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train) # 归一化
# 注意此时已经将X_train的dataframe格式转化为Numpy数组了
X_train_scaled[:5]
第六步,选择机器学习模型,训练模型。我选择了随机森林回归模型,实例化一个RandomForestRegressor类的对象。在此模型中,超参数n_estimators设置为100,其他超参数设置为默认值。
from sklearn.ensemble import RandomForestRegressor
rf_regressor = RandomForestRegressor(n_estimators=100, random_state=30)
rf_regressor.fit(X_train_scaled,y_train)
第七步,在训练集上评估模型。
train_acc = rf_regressor.score(X_train_scaled,y_train)
print(f"Train accuracy:{train_acc:.4f}")
Train accuracy:0.9399,说明我们的模型在训练集上获得了93.99%的准确率。
第八步,在测试集上评估模型,首先对测试集也要进行归一化,调用创建的StandardScaler实例的transform()方法来实现。请记住要调用
transform()方法而不是fit_transform()。
from sklearn.metrics import mean_squared_error, r2_score
X_test_scaled = scaler.transform(X_test)
#计算精确度
test_acc = rf_regressor.score(X_test_scaled, y_test)
y_pred = rf_regressor.predict(X_test_scaled)
# 计算均方误差(MSE)
mse = mean_squared_error(y_test, y_pred)
#计算伪R方
r2 = r2_score(y_test,y_pred)
print(f"Test accuracy:{test_acc:.4f}")
print(f"Mean Squared Error (MSE): {mse:.4f}")
print(f"R-squared (R2): {r2:.4f}")
得到:Test accuracy:0.7881 Mean Squared Error (MSE): 0.0020 R-squared (R2): 0.7881
嗯...这结果还不错。
第九步,深入分析,进行预测或者利用可解释性机器学习方法。我这里对影响因子的重要性进行评估。首先要获取特征名称列表,创建一个包含特征名和其对应重要性分数的DataFrame。利用feature_importances_得到重要性分数,并进行降序排列和可视化。
feature_name = X.columns.tolist()
feature_importance = rf_regressor.feature_importances_
importance_df = pd.DataFrame({"Feature":feature_name,"Importance":feature_importance})
# 根据重要性分数降序排序
importance_df = importance_df.sort_values(by="Importance",ascending=False)
print(importance_df)
# 可视化
plt.figure(figsize=(10, 6))
plt.barh(importance_df['Feature'], importance_df['Importance'], color='skyblue')
plt.xlabel('Importance')
plt.ylabel('Feature')
plt.title('Feature Importance')
plt.gca().invert_yaxis() # 反转y轴以使最重要的特征在顶部显示
plt.savefig("result.jpg",dpi=300)
至此,一个用机器学习分析问题的基本流程就结束了。不过这只是最基础的流程,想要把问题分析的透彻还是需要在过程中思考数据的特征、
结合实际意义、利用更加丰富的方法。
基本流程总结
以下是对构建一个有监督机器学习模型的基本步骤的总结:
1、加载数据集,构建特征矩阵X和目标向量y
2、对数据集进行探索性数据分析(EDA)。EDA的目标是了解数据的统计属性,例如特征的分布和它们之间的相关性,并识别数据中的任何问题,例如缺失值、非数值特征、异常值、类别不平衡等。这个对数据集的初步调查将帮助您决定需要进行哪些特征工程,并考虑用于建模的机器学习算法。在Python中进行EDA时,通常使用Pandas进行数据分析,使用Matplotlib进行数据可视化。
3、分割数据集:训练集、(验证集)、测试集,可以用train_test_split()函数
4、预处理和清晰训练集如填充缺失值、归一化、编码分类等,从而为建模做准备。对于每个选择的转换器,调用其fit_transform()方法,并将其结果传递给下一个转换器的fit_transform()。注意,变换的顺序很重要,例如,只有在填充缺失值之后才应进行归一化处理。
5、选择适合当前学习任务的估计器。创建其对应类的实例,并在构造函数中设置其超参数。如果对于超参数不确定要选择哪些值,可以最初使用它们的默认值,然后稍后进行调整。
6、训练,通过调用估计器的fit()方法将其拟合到训练集。
7、通过调用估计器的score()方法评估其在训练集上的性能。如果得分过低,则意味着训练数据存在问题(训练集过小、数据太嘈杂等)或模型本身存在问题(模型过于简单、模型的超参数未调整等)。在这种情况下,根据问题返回到步骤4或5。
8、在训练集上获得令人满意的得分后,可以在测试集上评估模型。为此,需要通过在训练集上使用相同的转换器调用transform()方法来对测试集进行转换。
9、通过调用估计器的score()方法评估其在测试集上的性能。如果测试集上的得分明显低于训练集上的得分,则意味着模型过拟合。返回到步骤5来调整模型的超参数(例如,添加正则化),如果这没有帮助,则选择更换模型或尝试获取更多的训练数据。
10、在超参数调优中,最好使用训练集的另一个子集(称为验证集),以避免污染测试集。
11、根据需要重复步骤4至9,直到获得足够好的解决方案。
参考:
[1] scikit-leran: https://mp.weixin.qq.com/s/HLzWKJqNQNJGvJIg9ejfMw
[2] 正则化方法(regularization):https://zhuanlan.zhihu.com/p/44363288