在机器学习中,特征选择是选择对您的预测最有用的特征的过程。尽管听起来很简单,但这是创建新的机器学习模型时最复杂的问题之一。
在本文中,我将与您分享我在Fiverr领导的上一个项目期间研究的一些方法。
您将获得有关我尝试的基本方法以及更复杂的方法的一些想法,该方法获得了最佳效果-删除了60%以上的功能,同时保持了准确性并为我们的模型实现了更高的稳定性。我还将分享我们对该算法的改进。

为什么进行特征选择如此重要?

如果您构建了机器学习模型,您就会知道识别哪些功能很重要而哪些仅仅是噪音是多么困难。

删除嘈杂的功能将有助于内存,计算成本和模型准确性。
此外,通过删除功能部件,将有助于避免模型的过拟合。

有时,您具有一项具有业务意义的功能,但这并不意味着此功能将帮助您进行预测。
您需要记住,功能在一种算法(例如决策树)中可能有用,而在另一种算法中的代表性可能不足(例如回归模型),并非所有功能都是一样的:)

不相关或部分相关的特征可能会对模型性能产生负面影响。功能选择和数据清理应该是设计模型的第一步,也是最重要的一步。

特征选择方法:

尽管有很多用于特征选择的技术,例如向后消除,套索回归。在本文中,我将分享3种方法,这些方法被发现对完成更好的特征选择最有用,每种方法都有其自身的优势。

“除了X”

在Fiverr中,将这种技术命名为“ All But X”。此技术很简单,但很有用。

  1. 您可以反复训练和评估
  2. 在每次迭代中,都将删除一个功能。
    如果您有大量功能,则可以删除功能的“系列”,在Fiverr,我们通常会汇总不同时间,30天点击次数,60天点击次数等功能。这是一系列功能。
  3. 根据基准检查评估指标。

该技术的目标是查看功能族中的哪些不影响评估,或者甚至删除它也可以改善评估。

这种方法的问题在于,一次删除一个要素,不会使要素彼此产生效果(非线性效果)。也许特征X和特征Y的组合正在产生噪声,而不仅仅是特征X。
运行全部流程的“除X之外的所有图” —在运行所有迭代之后,我们进行了比较以检查哪个迭代不影响模型的准确性。

这种方法的问题在于,一次删除一个要素,不会使要素彼此产生效果(非线性效果)。也许特征X和特征Y的组合正在产生噪声,而不仅仅是特征X。

特征重要性+随机特征

我们尝试的另一种方法是使用大多数机器学习模型API具有的功能重要性。

我们所做的不仅仅是从功能重要性中获取前N个功能。我们向数据添加了3个随机特征:

  1. 二进制随机特征(0或1)
  2. 在0至1个随机特征之间均匀
  3. 整数随机特征

在重要功能列表之后,我们仅选择了高于随机特征的功能。

重要的是采用随机特征的不同分布,因为每种分布都会产生不同的影响。

在树木中,模型“喜欢”连续要素(由于分割),因此这些要素将位于层次结构中的较高位置。因此,您需要将每个功能与其均布的随机功能进行比较。

Boruta

Boruta是一种由华沙大学开发的功能分级和选择算法。该算法基于随机森林,但也可以用于XGBoost和不同的树算法。

在Fiverr,我使用了该算法,并对XGBoost排名和分类器模型进行了一些改进,我将对此进行简要介绍。

该算法是我上面提到的两种方法的一种组合。

  1. 为数据集中的每个要素创建一个“阴影”要素,具有相同的要素值,但仅在各行之间随机排列
  2. 循环运行,直到停止条件之一:
    2.1。我们不会删除任何其他功能
    2.2。我们删除了足够的功能-可以说我们要删除60%的功能
    2.3。我们进行了N次迭代-我们限制了迭代次数以免陷入无限循环
  3. 运行X次迭代-我们使用5来消除模式
    3.1 的随机性。使用常规特征和阴影特征训练模型
    3.2。保存每个特征的平均特征重要性得分
    3.3删除所有低于其阴影特征的特征
def _create_shadow(x):
    """
    Take all X variables, creating copies and randomly shuffling them
    :param x: the dataframe to create shadow features on
    :return: dataframe 2x width and the names of the shadows for removing later
    """
    x_shadow = x.copy()
    for c in x_shadow.columns:
        np.random.shuffle(x_shadow[c].values) # shuffle the values of each feature to all the features
    # rename the shadow
    shadow_names = ["shadow_feature_" + str(i + 1) for i in range(x.shape[1])]
    x_shadow.columns = shadow_names
    # Combine to make one new dataframe
    x_new = pd.concat([x, x_shadow], axis=1)
    return x_new, shadow_names


# Set up the parameters for running the model in XGBoost
param = booster_params
df = pd.DataFrame() # initial empty dataframe

for i in range(1, n_iterations + 1):
    # Create the shadow variables and run the model to obtain importances
    new_x, shadow_names = _create_shadow(x)
    bst, df = _run_model(new_x, y, group, weights, param, num_boost_round, early_stopping_rounds, i == 1, df)
    df = _check_feature_importance(bst, df, i, importance_type)

df[MEAN_COLUMN] = df.mean(axis=1)
# Split them back out
real_vars = df[~df['feature'].isin(shadow_names)]
shadow_vars = df[df['feature'].isin(
)]

# Get mean value from the shadows
mean_shadow = shadow_vars[MEAN_COLUMN].mean() * (perc / 100)
real_vars = real_vars[(real_vars[MEAN_COLUMN] > mean_shadow)]

criteria = _check_stopping_crietria(delta, real_vars, x)

return criteria, real_vars['feature']
从创建阴影-训练-比较-删除特征再返回的Boruta运行图。
从创建阴影-训练-比较-删除特征再返回的Boruta运行图。

Boruta 2.0

这是本文的最佳部分,是对Boruta的改进。

我们使用原始模型的“简短版本”运行了Boruta。通过获取数据样本和较少数量的树(我们使用XGBoost),我们在不降低准确性的情况下改善了原始Boruta的运行时间。

另一个改进是,我们使用前面提到的随机特征运行了该算法。可以看出我们已经从数据集中删除了所有随机特征,这是一个很好的条件。

有了改进,我们看不到模型准确性的任何变化,但是看到了运行时的改进。通过删除,我们能够将200多个要素转换为少于70个要素。我们看到了该模型在树木数量和训练的不同阶段的稳定性。

我们还看到训练损失与验证集之间的距离有所改善。

改进和Boruta的优势在于您正在运行模型。在这种情况下,发现的有问题的特征对您的模型有问题,而不是不同的算法。

总结

在本文中,您了解了3种不同的技术,这些技术如何对数据集进行特征选择以及如何建立有效的预测模型。您看到了我们对Boruta的实现,运行时的改进以及添加了随机功能以帮助进行健全性检查。

通过这些改进,我们的模型仅以原始功能的35%就能运行得更快,更稳定并保持了一定的准确性。

选择最适合您的技术。请记住,功能选择可以帮助提高准确性,稳定性和运行时间,并避免过度拟合。更重要的是,更少的功能使调试和解释性变得更加容易。

本文转自 medium,原文地址