有很多技巧可以帮助我们在机器学习比赛中取得好成绩。因此,在本文中,我将重点介绍一些在机器学习比赛中常用但在教科书中很少提及的小技巧。因此,我希望通过撰写本文,机器学习领域的新手能够从这些有趣的技巧中获益。

数据预处理

  1. 缺失数据分析

    分析数据中缺失的数据是一种非常有用的方法,可以识别无用的特征,并在后续步骤中设计插补策略。为此,我们可以使用“missingno”包。

    import missingno as msno
    
    msno.bar(info)
    
  2. 删除丢失数据过多的列

    在分析丢失的数据之后,我们可以使用下面的函数来删除丢失值过多的特征。

    def filter_col_by_nan(df, ratio=0.05):
        cols = []
        for col in df.columns:
            if df[col].isna().mean() >= (1 - ratio):
                cols.append(col)
        return cols
    

特征构建

  1. 使用聚合函数创建聚合特征

    在某些情况下,训练数据包含多个表,必须在将这些表合并到一个表中,然后才能被输入机器学习系统。在大多数情况下,我们喜欢编写聚合函数来实现这一点。然而,如果我们使用Pandas函数,这将是一个更简单的过程。Pandas聚合函数是实现这一目标的一个非常有用的工具。

    如下面的示例所示,在使用一些聚合函数(如总和或平均)聚合薪资特征后,我们可以得到多个聚合的薪资特征,并将其合并到主表中。

    df = pd.DataFrame({"Job": ["Programmer", "Programmer", "Writer", "Writer"], "Salary": [1, 2, 3, 4]})
    df.groupby(["Job"]).agg({"Salary": ["sum", "mean"]})
    
    Salary         sum mean               
    Programmer      3  1.5
    Writer          7  3.5
    
  2. 使用Pivotal表构建聚合特征

    虽然聚合函数可以合并数字特征,但是处理分类特征仍然是一个难点。例如,我们可以使用平均函数来聚合“salary”特征。但是,我们不能直接使用该函数来聚合“job”特征。为了处理这种情况,我们可以使用Pivotal表来聚合这些特征。

    在下面的示例中,我们不仅基于职务,而且基于位置来获得总工资。我们可以通过这种方式基于分类特征和数字特征构建聚合特征。

    df = pd.DataFrame({"Job": ["Programmer", "Programmer", "Programmer", "Programmer", "Programmer",
                               "Writer", "Writer", "Writer", "Writer"],
                       "Location": ["Shanghai", "NewYork", "NewYork", "Shanghai",
                                    "Shanghai", "NewYork", "Shanghai", "Shanghai",
                                    "NewYork"],
                       "Salary": [1, 2, 2, 3, 3, 4, 5, 6, 7]})
    
    table = pd.pivot_table(df, values='Salary', index=['Job'],
                           columns=['Location'], aggfunc=np.sum)
    
    Location    NewYork  Shanghai                    
    Programmer        4         7
    Writer           11        11
    

    下面是一些常见聚合函数的示例。

    ['max', 'min', 'mean', 'count', 'nunique', 'sum']
    
  3. 创建多项式特征

    多项式特征是提高特征质量的一种简单有效的方法。例如,如果我们有两个收入来源,奖金和工资,我们可以将它们合并为一个项目作为总收入特征。

    df = pd.DataFrame({"Job": ["Programmer", "Programmer", "Writer", "Writer"],
                       "Salary": [1, 2, 3, 4], "Bonus": [1, 2, 3, 4]})
    df[["Salary", "Bonus"]].sum(1)
    

    除加法特征外,除法特征也很重要。例如,基于一个除法特征,我们可以计算平均每小时的工资。

    df = pd.DataFrame({"Job": ["Programmer", "Programmer", "Writer", "Writer"],
                       "Salary": [1, 2, 3, 4], "Working Time": [1, 1, 1, 0.5]})
    df["Salary"] / df["Working Time"]
    

    如果我们的数据集有时间特征,我们可以使用减法来创建一些时间间隔特征。

    df = pd.DataFrame({
        "StartDate": ["2020-01-01", "2020-01-01"],
        "EndDate": ["2020-01-05", "2020-01-10"],
    })
    days = (pd.to_datetime(df["EndDate"]) - pd.to_datetime(df["StartDate"])).dt.days
    
  4. 组合不常见级别

    组合稀有级别是提高基于决策树的学习算法性能的有效方法。尽管它看起来有悖常理,因为将不同的级别组合成一个值意味着信息丢失。然而,理解这一概念的一种直观方法是,决策树只会分割那些在分割后将导致高信息增益的特征。然而,对于那些罕见的级别来说,信息增益并没有那么大。因此,即使这些特征可能有用,它们也永远不会被使用。为了处理这种情况,我们可以将这些罕见级别的特征聚合到一个组中,如下面的代码所示。

    def combination(df):
        for col in ['Column']:
            df[col + '_COUNT'] = df[col].map(df[col].value_counts())
            col_idx = df[col].value_counts()
            for idx in col_idx[col_idx < 10].index:
                df[col] = df[col].replace(idx, -1)
    
  5. Label Encoding

    机器学习教科书通常涵盖Label Encoding和One-hot Encoding。因此,不再需要介绍这些基本概念。在本文中,我将讨论一种创建标签编码特征的简单方法。

    df = pd.DataFrame({"Job": ["Programmer", "Programmer", "Writer", "Writer"]})
    pd.factorize(df["Job"])[0]
    # array([0, 0, 1, 1], dtype=int64)
    

模型构造

  1. 如果我们使用XGBoost或LightGBM作为我们的学习模型,我们应该利用这些包提供的提前停止功能。然而,如果我们想使用这个特性,我们必须分割一个验证集,这可能导致训练数据的浪费。需要怎么来解决这个问题呢?交叉验证方案是一种聪明的解决方案。我们可以使用下面的函数对不同验证集的预测结果进行平均。这样,就可以在不浪费任何训练数据的情况下自动调优XGBoost的迭代参数。

    def k_fold_serachParmaters(model, train_val_data, train_val_kind, test_kind):
        mean_f1 = 0
        mean_f1Train = 0
        n_splits = 5
    
        cat_features = []
    
        sk = StratifiedKFold(n_splits=n_splits, shuffle=True, random_state=2020)
        pred_Test = np.zeros(len(test_kind))
        for train, test in sk.split(train_val_data, train_val_kind):
            x_train = train_val_data.iloc[train]
            y_train = train_val_kind.iloc[train]
            x_test = train_val_data.iloc[test]
            y_test = train_val_kind.iloc[test]
    
            model.fit(x_train, y_train,
                      eval_set=[(x_test, y_test)],
                      categorical_feature=cat_features,
                      early_stopping_rounds=100,
                      verbose=False)
    
            pred = model.predict(x_test)
            fper_class = f1_score(y_test, pred)
    
            pred_Train = model.predict(x_train)
            pred_Test += model.predict_proba(test_kind)[:, 1] / n_splits
            fper_class_train = f1_score(y_train, pred_Train)
    
            mean_f1 += fper_class / n_splits
            mean_f1Train += fper_class_train / n_splits
        return mean_f1, pred_Test
    

结论

本文仅介绍了在机器学习竞赛中提高模型性能的一些基本思路。然而,为了赢得比赛,我们必须仔细分析训练数据,并根据我们的领域知识创造一些神奇的特征。尽管如此,我希望本文能为机器学习新手提供一些新的见解,帮助他们在机器学习比赛中提高分数。

更多推荐