cpuwdl 发布的文章

多分类LightGBM Cross Validation

使用LightGBM,Cross Validation进行多分类,multi log loss。

import lightgbm as lgb
import matplotlib.pyplot as plt
import numpy as np
import random
import os
from lightgbm import LGBMClassifier
from sklearn.metrics import roc_auc_score, roc_curve
from sklearn.metrics import log_loss
from sklearn.model_selection import KFold, StratifiedKFold
from sklearn.preprocessing import LabelEncoder
import seaborn as sns
import gc
def multi_log_loss(y_true, y_pred, num_classes):  # score function for CV
    esp = 1e-12
    y_pred += esp
    y_true = y_true.astype('int')
    # Handle all zeroes
    all_zeros = np.all(y_pred == 0, axis=1)
    y_pred[all_zeros] = 1/num_classes
    # Normalise sum of row probabilities to one
    row_sums = np.sum(y_pred, axis=1)
    y_pred /= row_sums.reshape((-1, 1))
    # Calculate score
    n_rows = y_true.size
    score_sum = 0
    for i in range(y_true.size):
        score_sum -= np.log(y_pred[i, y_true[i]])
    score = score_sum / n_rows
    return score
def kfold_lightgbm(train_df, test_df, y, num_folds, stratified = False, debug= False):
    if stratified:
        folds = StratifiedKFold(n_splits= num_folds, shuffle=True, random_state=SEED)
    else:
        folds = KFold(n_splits= num_folds, shuffle=True, random_state=SEED)
    num_classes = 6
    # Create arrays and dataframes to store results
    oof_preds = np.zeros((train_df.shape[0],num_classes))
    sub_preds = np.zeros((test_df.shape[0],num_classes))
    feature_importance_df = pd.DataFrame()
    feats = train_df.columns.tolist()
    cat_feats = 'auto'
    #feats = select
    for n_fold, (train_idx, valid_idx) in enumerate(folds.split(train_df[feats], y)):
        train_x, train_y = train_df[feats].iloc[train_idx], y.iloc[train_idx]
        valid_x, valid_y = train_df[feats].iloc[valid_idx], y.iloc[valid_idx]
        
        lgtrain = lgb.Dataset(train_x, train_y,
                        feature_name=feats,
                        categorical_feature = cat_feats)
        lgvalid = lgb.Dataset(valid_x, valid_y,
                        feature_name=feats,
                        categorical_feature = cat_feats)

        print('get lgb train valid dataset end')
        lgb_params =  {
            'task': 'train',
            'boosting_type': 'gbdt',
            'objective': 'multiclass',
            'num_class':num_classes,
            'metric': 'multi_logloss',
            #"n_estimators":10000,
            
            "learning_rate": 0.02,
            #"num_leaves": 200,
            #"feature_fraction": 0.50,
            #"bagging_fraction": 0.50,
            #'bagging_freq': 4,
            #"max_depth": -1,
            
            'num_leaves': 32,
            'max_depth': 8,
            'bagging_fraction': 0.7,
            'bagging_freq': 5,
            'feature_fraction': 0.7,
            
            "reg_alpha": 0.3,
            "reg_lambda": 0.1,
            'min_child_samples': 100,
            #'max_bin': 100,
            "min_split_gain":0.2,
            'nthread': 4,
            "min_child_weight":10,
        }
        clf = lgb.train(
            lgb_params,
            lgtrain,
            num_boost_round=3000,
            valid_sets=[lgtrain, lgvalid],
            valid_names=['train','valid'],
            early_stopping_rounds=100,
            verbose_eval=100
        )

        #clf.fit(train_x, train_y, eval_set=[(train_x, train_y), (valid_x, valid_y)], 
        #    eval_metric= 'auc', verbose= 100, early_stopping_rounds= 200)

        #oof_preds[valid_idx] = clf.predict_proba(valid_x, num_iteration=clf.best_iteration_)[:, 1]
        #sub_preds += clf.predict_proba(test_df[feats], num_iteration=clf.best_iteration_)[:, 1] / folds.n_splits
        
        oof_preds[valid_idx] = clf.predict(valid_x, num_iteration=clf.best_iteration)
        sub_preds += clf.predict(test_df, num_iteration=clf.best_iteration)/ folds.n_splits
        
        fold_importance_df = pd.DataFrame()
        fold_importance_df["feature"] = feats
        fold_importance_df["importance"] = clf.feature_importance()
        #fold_importance_df["importance"] = clf.feature_importances_
        fold_importance_df["fold"] = n_fold + 1
        
        feature_importance_df = pd.concat([feature_importance_df, fold_importance_df], axis=0)
        print('Fold %2d Multi Log Loss : %.6f' % (n_fold + 1, multi_log_loss(valid_y.values, oof_preds[valid_idx], num_classes)))
        del clf, train_x, train_y, valid_x, valid_y
        gc.collect()

    print('Full Multi Log Loss %.6f' % multi_log_loss(y.values, oof_preds, num_classes))
    display_importances(feature_importance_df)
    return feature_importance_df,sub_preds
# Display/plot feature importance
def display_importances(feature_importance_df_):
    cols = feature_importance_df_[["feature", "importance"]].groupby("feature").mean().sort_values(by="importance", ascending=False)[:40].index
    best_features = feature_importance_df_.loc[feature_importance_df_.feature.isin(cols)]
    plt.figure(figsize=(8, 10))
    sns.barplot(x="importance", y="feature", data=best_features.sort_values(by="importance", ascending=False))
    plt.title('LightGBM Features (avg over folds)')
    plt.tight_layout()
    plt.savefig('lgb_importances.png')

one_hot_encoder和label_encoder

one_hot_encoder和label_encoder可以处理类别类型的特征。

import pandas as pd
from sklearn.preprocessing import LabelEncoder
def one_hot_encoder(df, nan_as_category = True):
    original_columns = list(df.columns)
    categorical_columns = [col for col in df.columns if df[col].dtype == 'object']
    df = pd.get_dummies(df, columns = categorical_columns, dummy_na = nan_as_category)
    new_columns = [c for c in df.columns if c not in original_columns]
    return df, new_columns
def label_encoder(df):
    original_columns = list(df.columns)
    categorical_columns = [col for col in df.columns if df[col].dtype == 'object']
    for col in categorical_columns:
        df[col] = LabelEncoder().fit_transform(df[col].astype('str'))
    new_columns = [c for c in df.columns if c not in original_columns]
    return df, categorical_columns

使用scipy.sparse保存稀疏矩阵

使用scipy.sparse保存稀疏矩阵可以省一些空间,将矩阵转化为稀疏矩阵进行计算可以省一些内存。

from scipy import sparse
from scipy.sparse import hstack, csr_matrix
import os
fname = 'df.npz'
if not os.path.exists(fname):
	df = get_df()
	df = hstack([csr_matrix(df), csr_matrix(df)])
	sparse.save_npz(fname, df, compressed=True)
else:
	df = sparse.load_npz(fname)

使用pandas保存h5文件

pandas读取的数据保存成h5文件,再次读取速度会快一些。

import pandas as pd
import os
fname = 'df.h5'
if not os.path.exists(fname):
    h5 = pd.HDFStore(fname,'w', complevel=4, complib='blosc')
    df = get_df()
    h5['df'] = df
    h5.close()
else:
    h5 = pd.HDFStore(fname,'r')
    df = h5['df']
    h5.close()

机器学习路径

我接触机器学习的入门书籍是:《机器学习实践指南:案例应用解析》,在这本书上学了python和R的基本语法,还有少量机器学习基础知识。然后是在学校图书馆借了几本AI相关书籍,这些书的最后一章是介绍决策树的,都比较老,具体是哪些书已经找不到了,看完就搁置了一段时间。

再次看机器学习是从Andrew Ng的视频开始,顺便看了台大林轩田机器学习基石的视频,看得云里雾里,顺便翻了一下李航的统计学习方法,无法确定看懂了多少,然后又搁置了很久。

再次看机器学习相关是第一次偶然看到CNN的时候,顺便了解了相关知识,比如CNN原理,用Keras框架CNN进行图像分类,看了很多遍Keras文档,使用预训练模型等。发现R-CNN、Fast R-CNN、Faster R-CNN系列,做了一个Faster R-CNN Caffe版本的PyQt5可视化界面,可以训练、测试、查看每张图像的标注和识别结果,然后又搁置了一段时间。

再次看机器学习相关是参加Kaggle竞赛,第一个参加的竞赛可以不用机器学习,可以转化为最小费用最大流,是在论坛里面学到的,学到之前是采用暴力搜索加优化做的。接下来做了很久的Kaggle竞赛,比如第一个图像分类的竞赛,当时写了一个融合常见预训练模型的框架,可惜那时候除了全连接层的参数都冻结了,试了各种预处理方法,统统效果很差。在接下来的图像分类竞赛,学到了Random Crop,多进程预处理,类别平衡,Test Time Augmentation等方法,离顶尖队伍还有很大的距离,想要提高技巧得查看很多图像竞赛的前几名的解题思路,例如模型的修改和损失函数自定义可能也会有用。然后是一个比较少见的文字分类的竞赛,学习方法就是Kernels里面他人的代码和Discussion里他人提到的思路,需要研究一些预处理的方法,还有word2vec,预训练词向量模型和训练词向量的方法,SpatialDropout1D、BiLSTM、BiGRU,N-Fold平均。

这里有个问题是我比较好奇的,就是假设我自己得到一个还可以的结果,但是公开的Kernels里也有一些还不错的结果,他采用的方法和我不同,分数差不多,这种情况只要和他平均,就可以得到更高的分数,而且过拟合的概率也很小。从规则上来说,这是允许的,私下分享但不组队才是违反规则。假设某人A凭借融合公开的结果,得到某个分数,某人只靠自己得到和A相同的分数,另外一个团队分工合作使用各个方法得到和A相同的分数,这三种情况的数量不同会导致成绩的含金量不同,不过一般公开的结果最终都不会进前10%,据我猜测使用公开Kernel的情况是比较普遍的,不过事实是怎样呢?

在一个竞赛里体验了一下Mask R-CNN,顺便体验了一下2阶段比赛的风险,1阶段小测试集,结束时要上传代码,然后发放大测试集,1星期后2阶段结束。假设在1阶段时经验不足,一味提高分数,没有考虑测试集变化的一些情况(图像尺寸、识别目标大小、图像颜色分布等等),就有可能跌落几百名。

在一个竞赛里发现了LightGBM和它的前身,曾经我以为很弱的决策树竟然可以这么强。文档里参数好多,数据集好大,作为新手依然在公开Kernels里学会了不少东西。可惜有人在最后12小时发布了一个分数和我差不多的Kernel,然后有人用它和一些公开Kernels融合了一下发布了,分数就比我高了一些,导致排名下降了300名,嘿嘿这次我就没有融合公开Kernel,所以说邪恶无法阻止,除非能碾压邪恶。

到这里基本上常见比赛都见过了,后面看到的比赛很多都可以直接用以前的代码来跑了,不过还是有一些新型的比赛出没。比如一个可以用DBSCAN的比赛,预测粒子轨迹,我一开始看到这个比赛的时候第一反应就是不做这个比赛,可惜有一些多余的时间,我就看了它好几次,作为学习聚类的一个途径了解一下,这题的最优解不是聚类。大概就是瞎构造一些已有变量的组合,用贝叶斯优化找线性组合的参数,有时手工调整一下参数看看效果,多看Discussion找方法,时间不多,自己没想出什么思路。还有比如说目标检测的竞赛,刚好是个大数据集,谷歌还提供了一笔赠金跑谷歌云,体验了一下多卡训练的感觉。

我总觉得我在面临重要选择的时候,都会做出错误的决定,就比如说我两个月多前面临做一些准备去找工作的问题,当时有一些备选:1.打比赛得好成绩去找,2.刷leetcode中等难度的题,3.用以前学过的知识做一个靠谱小应用,4.复习和学习机器学习基本知识。只能说我水平确实有限,基础知识不牢,没有办法取得前十的成绩,表面Kaggle银牌已经没啥用了,硬实力才是关键,似乎现在大家要求都挺高。我花了10天去试了一下天池的比赛,感觉有一批人很强,不过实际认真参与的大概是300人,说明很多人不需要参加竞赛,直接就可以去实际应用,想靠竞赛找到工作得进天池决赛。很多公司喜欢考算法题,听说leetcode刷完中等难度就行,可惜我没有选择全刷leetcode,这应该就是一个错误,leetcode我只试了前50题中等难度的,不参考资料会45题,间隔的做花了10天,不过不是一整天都在刷,比较难的一些知识我现在肯定不会,如果多刷一些可能才能看到更多的不足,补完应该会强一些。想做一个完整的小应用至少得花1个月,风险不小,做了不够好人家肯定看不上。机器学习基础知识感觉挺难,想在短时间内灌输进去也不知道有没有用,不过这些基础知识学起来挺快的,暂时不清楚学会了有多大用,可惜了少了论文的积累,估计只能做一些边角的工作。