机器学习算法的实现
1、随机森林RF
博客:
决策树(中)——Random Forest、Adaboost、GBDT (非常详细)
论文:随机森林算法优化研究
原理
随机森林是一种常见的机器学习模型,可以根据特征对样本进行分类预测,因此被广泛使用。随机森林是由多棵决策树构成的组合分类器,由Breiman于2001年提出,其算法思想是通对bootstrap样本进行决策树建模,再将多个决策树进行组合输出最终的预测结果。
随机森林算法还可以在分类的基础上进行回归分析,通过将样本分类的结果进行一定的运算可以获得各个特征重要性特征的重要性表示特征对预测结果影响程度,某一特征重要性越大,表明该特征对预测结果的影响越大,重要性越小,表明该特征对预测结果越小。随机森林算法中某一特征的重要性,是该特征在内部所有决策树重要性的平均值。
过程
随机森林算法主要有两个重要的随机过程,一个是随机选择每棵决策树的训练样本,另外一个是随机选择特征子集。随机森林是通过自助采样的方式有放回的选取对应数据集,并且在总的特征集合中随机选取指定数目的特征作为特征子集。具体如下:
训练随机森林的过程就是训练各个决策树的过程,由于各个决策树的训练是相互独立的,因此随机森林的训练可以通过并行处理来实现,这将大大提高生成模型的效率。
将以同样的方式训练得到 K 个决策树组合起来,就可以得到一个随机森林。当输入待分类的样本时,随机森林输出的分类结果由每个决策树的输出结果进行简单投票(分类取众数,回归取平均数)
优点(树模型共有的优点)
随机森林对噪声和异常值有较好的容忍性,能够在不需要降维的条件下处理具有高维特征的输入样本,而且能够评估各个特征在分类问题上的重要性,具有良好的可扩展性和并行性
2、梯 度 提 升 树 GBDT
梯 度 提 升 树 (Gradient Boosting Decision Tree, GBDT) 是 集 成 学 习 中Boosting 思 想 的 算 法 , 基 学 习 器 是 CART 回 归 树 [19], 它 实 际 上 是 提 升 树(Boosting Decision Tree) 的推广, 二者的区别在于损失函数, 提升树损失函数是特定的, 梯度提升树的损失函数更具一般性, 可以自定义, 它在刚开始被提出时就是公认的泛化能力较强的算法.
通俗理解: 假设我们希望预测一个人的年龄, 他的实际年龄为 30 岁, 首先用一个模型预测为 20 岁, 发现离真实值还有 10 岁差距, 为了不改变原有模型的参数, 于是想到在原有模型的基础上做一些改善来弥补差距, 建立一个新的模型来拟合未完全拟合的部分即这 10 岁的差距, 继续迭代下去, 最终将预测的岁数相加就是模型输出的结果 .
3、XGBoost
好的博客:
决策树(下)——XGBoost、LightGBM(非常详细)
Boosting算法4 --- LightGBM介绍与分布式
论文:
近似算法
结点分裂时, 传统算法的基本思想为: 遍历每一个特征, 根据特征值的大小对结点中样本进行升序排列, 分别将每个特征值作为分割阈值, 阈值左边的样本分到左结点, 右边的样本分到右结点; 计算增益, 若增益增大, 则更新目前的分割特征和阈值; 所有特征遍历完后, 输出最终的特征和阈值.
当数据量太大时, 传统算法将数据加载到内存或者分布式环境时会超出, 传统算法将不再适合. 于是 XGBoost 提出了一种近似算法 (Approximate Algorithm)解决这个问题. 具体思想为: 根据每个特征的分布, 找到候选分割点集合, 在结点分裂时, 遍历分割点集合中的值计算增益即可, 不需要遍历全部样本的值, 加快训练速度。
稀疏特征的分裂算法
若样本某个特征缺失, 结点分裂遍历该特征时, 没办法对样本进行左右结点的分配, 基于此, XGBoost 提出了Sparsity Aware Split Finding 算法, 通过该算法可以自动为样本分配一个使增益最大的分裂方向, 这加快了模型的训练速度。
4、LightGBM
同一系列算法(梯 度 提 升 树 GBDT,XGBoost)
那些老的算法,硕士博士学位论文一般讲的比较详细
好的博客:
决策树(下)——XGBoost、LightGBM(非常详细)
Boosting算法4 --- LightGBM介绍与分布式
论文:
三、代码实现
1、预处理
① 首先读取表格数据并合并(一般用pandas),获取变量df和标签y
import pandas as pd
# 读取表格,相对路径,默认第一个表
df1=pd.read_excel("ERa_activity.xlsx")
df2=pd.read_excel("Molecular_Descriptor.xlsx")
# 根据"SMILES"列合并表格
df=pd.merge(df1,df2,on="SMILES")
#弹出无用的列,获取标签y,剩下的df即为自变量
df.pop("SMILES")
df.pop("IC50_nM")
y=df.pop("pIC50")
② 划分训练集和验证集(当做测试集用)
from sklearn.model_selection import train_test_split
x_train, x_val, y_train, y_val = train_test_split(df, y, test_size = 0.3, random_state = 2021)</pre>
2、模型构建
LGB的实现
- lightbgm构建有两种方法:
一种用原生形式使用lightgbm,另一种用Sklearn接口形式使用lightgbm
第二种才能使用GridSearchCV搜索最优参数- 而且两种方法得到特征重要性权重的方式也不同,但只要说的有道理就好了
第一种方法实现
① 原生形式使用lightgbm构建模型与训练
from sklearn.metrics import mean_absolute_error
import lightgbm as lgb
# 参数设置
params = {
'boosting_type': 'gbdt',
'objective': 'regression', # 用于回归
'learning_rate': 0.1, # 选定一较高的值,通常是0.1
'num_leaves': 50, # 由于lightGBM是leaves_wise生长,官方说法是要小于2^max_depth
'max_depth': 10, # 由于数据集不大,所以选择一个适中的值,4-10都可以
'subsample': 0.8, # 数据采样
'colsample_bytree': 0.8, # 特征采样
}
dtrain = lgb.Dataset(x_train, y_train, silent=True)
dvalid = lgb.Dataset(x_val, y_val, silent=True)
# 模型训练
clf_first = lgb.train(
params=params,
train_set=dtrain,
num_boost_round=50000,
valid_sets=[dtrain, dvalid],
early_stopping_rounds=100, # 如果验证集效果100轮未提升就停止
verbose_eval=100
)
# 误差
print(mean_absolute_error(clf_first.predict(x_train),y_train))
print(mean_absolute_error(clf_first.predict(x_val),y_val))
② 作图(这里截取获得前20重要性特征的部分)
import matplotlib.pyplot as plt
import numpy as np
def plot_feature_importance(dataset, model_bst):
list_feature_name = list(dataset.columns[:])
list_feature_importance =model_bst.feature_importance()
dataframe_feature_importance = pd.DataFrame(
{'feature_name': list_feature_name, 'importance': list_feature_importance})
dataframe_feature_importance20 = dataframe_feature_importance.sort_values(by='importance', ascending=False)[:30]
第二种方法
用Sklearn接口形式使用lightgbm,并使用Gridsearchcv对lightgbm调参:网格搜索和交叉验证
- 网格搜索,搜索的是参数,即在指定的参数范围内,按步长依次调整参数,利用调整的参数训练学习器,从所有的参数中找到在验证集上精度最高的参数,这其实是一个循环和比较的过程。 GridSearchCV可以保证在指定的参数范围内找到精度最高的参数,但是这也是网格搜索的缺陷所在,它要求遍历所有可能参数的组合,在面对大数据集和多参数的情况下,非常耗时。
- 交叉验证的概念也很简单: 将训练数据集划分为K份,K一般为10(我个人取3到5比较多) ; 依次取其中一份为验证集,其余为训练集训练分类器,测试分类器在验证集上的精度 ; 取K次实验的平均精度为该分类器的平均精度
说实话,对回归模型进行网格搜索不是很靠谱,必须cv足够大比如10,才能找到真正在验证集上效果最好的参数。(也可能是因为这里我自己划分了验证集的原因),如果评估的时候采用交叉验证那肯定是没问题的
① 参数优化(这里只以优化max_depth和num_leaves为例,其它参数优化见代码)
from sklearn.model_selection import GridSearchCV
print('网格搜索,参数优化')
#step1 调节max_depth以及num_leaves,确定树的深度及复杂度
# 设置网格参数范围
parameters = {'max_depth': range(4, 12, 2),
'num_leaves': range(20, 60, 10)
}
# 用Sklearn接口形式使用lightgbm
estimator = lgb.LGBMRegressor(objective='regression',
learning_rate=0.1,
subsample=0.8,#数据采样
colsample_bytree=0.8,#特征采样
early_stopping_rounds=100,
importance_type="gain"
)
# 构建网格交叉验证搜索,评分函数:平均绝对误差,训练集划分为5份
gsearch = GridSearchCV(estimator, param_grid=parameters, scoring='neg_mean_absolute_error', cv=10)
gsearch.fit(x_train, y_train,
eval_set=[(x_val, y_val)], # 评估数据集
eval_names=['val'],
)
print('参数的最佳取值:{0}'.format(gsearch.best_params_))
print('最佳模型得分:{0}'.format(gsearch.best_score_))
print(gsearch.cv_results_['mean_test_score'])
print(gsearch.cv_results_['params'])
② 模型构建
estimator = lgb.LGBMRegressor(objective='regression',
learning_rate=0.1,
subsample=0.8,#数据采样
colsample_bytree=0.8,#特征采样
early_stopping_rounds=100,
num_leaves= 50, # 由于lightGBM是leaves_wise生长,官方说法是要小于2^max_depth
max_depth=10, # 由于数据集不大,所以选择一个适中的值,4-10都可以
importance_type="gain"
)
estimator.fit(x_train, y_train,
eval_set=[(x_val, y_val)], # 评估数据集
eval_names=['val'],
)
print(mean_absolute_error(estimator.predict(x_train),y_train))
print(mean_absolute_error(estimator.predict(x_val),y_val))
③ 打印前20重要性的分子和它的权重
def plot_feature_importance(dataset, model_bst):
list_feature_name = list(dataset.columns[:])
list_feature_importance =model_bst.feature_importances_
dataframe_feature_importance = pd.DataFrame(
{'feature_name': list_feature_name, 'importance': list_feature_importance})
dataframe_feature_importance20 = dataframe_feature_importance.sort_values(by='importance', ascending=False)[:30]
print(dataframe_feature_importance20)
x = range(len(dataframe_feature_importance20['feature_name']))
plt.xticks(x, dataframe_feature_importance20['feature_name'], rotation=90, fontsize=8)
plt.plot(x, dataframe_feature_importance20['importance'])
plt.xlabel("Feature name")
plt.ylabel("Importance")
plt.title("The importance of features")
plt.show()
return np.array(dataframe_feature_importance20['feature_name'])
plot_feature_importance(x_train,estimator)