2000字范文,分享全网优秀范文,学习好帮手!
2000字范文 > 天池算法大赛思路和代码分享

天池算法大赛思路和代码分享

时间:2020-04-18 01:31:53

相关推荐

天池算法大赛思路和代码分享

目录:

思路概述

1,数据探索

2,特征工程

3,机器学习模型的初步预测和特征工程

4,变量衍生和PCA降维

5,多模型融合预测

赛题介绍详见天池官网:UNiLAB智慧能源系统大数据分析赛 赛道2:不完整电压数据的电力系统稳定性评估,数据注册登录后可下载

思路概述

算法大赛赛题,数据量不大,训练数据为2500*3900,预测数据2536*1950,数据类型均为浮点数,无空缺值,数据友好。拿到数据后,反复阅读赛题,对赛题的理解,可以少走一些弯路。既然都是浮点数,在开赛阶段直接用现有数据套个模型提交试水,果然,效果感人。。。

后转换思路,从分位值和具体数据大小方面“构造”特征进行后续的分析。在特征工程方面,主要从特征和标签(下文的label)的相关性重要性、特征与特征之间的相关性、特征衍生和降维三方面展开。反复、多次修改(美其名曰:迭代),改到后来终于把代码变成了一座si山,连命名都乱了。。。

模型选用方面,一开始是非常看好逻辑回归模型的,但从实际结果看,单一的弱模型 和 “弱+强模型+模型融合”的半监督学习模型,差距还是挺大的。最终的预测模型共两层:第一层使用knn、逻辑回归、随机森林,将拟合结果作为新的“特征”并入原始数据集,作为第二层模型的特征集(类似stacking融合,但此处没有对训练集进行切分);第二层为LightGB模型,拟合出最终的提交结果

1,数据探索

导入需要用到的包、

import pandas as pdimport numpy as npimport matplotlib.pyplot as pltimport seaborn as snsfrom sklearn.model_selection import train_test_split,GridSearchCV,cross_val_scorefrom sklearn.preprocessing import MinMaxScaler,StandardScalerfrom sklearn.metrics import accuracy_score,mean_squared_errorfrom sklearn.neighbors import KNeighborsClassifierfrom sklearn.linear_model import LogisticRegressionfrom sklearn import treefrom sklearn.tree import DecisionTreeClassifier from sklearn.ensemble import RandomForestClassifierimport xgboost as xgbfrom lightgbm import LGBMClassifierfrom scipy import stats from sklearn.linear_model import RidgeClassifierfrom statsmodels.stats.outliers_influence import variance_inflation_factorfrom sklearn.decomposition import PCA

读入训练数据,并查看基本信息:

同时,可查看数据的最大值、最小值、空缺值等。查看可得: 数据共2500行、3900列,均为浮点数,无空缺值,columns为1-3900;

读取并查看预测集数据可知:预测集和训练集数据类似,均为浮点数,无空缺值,只是列数少一半,行数多36行,为2536*1950的数据。此处不再放代码

读取并查看训练集的label数据:

可见:该赛题为二分类问题,样本分布均匀。

2,特征工程

由数据探索可知,训练数据上了3900个特征,随机抽取两个特征进行比较,两者之间未发现明显的区别(从赛题中可以了解到,这些特征都是电压数据,保持在0-1.3波动,所有特征为同一类型的工业数据),考虑到特征过多,此处没有直接将这些特征入模,而是“构造”新的特征来进行探索分析。

新构造出的特征分三类,第一类是超过某个值的数量比例如电压大于0.6出现的次数比例,第二类是分位指标如0.1分位、0.25分位、0.75分位等,第三类是总体指标如极差、方差、峰度等。构造特征的函数如下:

#数量特征构造,大于某个阈值的电压出现的次数def n_feature(df):w = df.shape[1]a = pd.DataFrame( (df <0.50).sum(axis=1)/w,columns=["_05"])a1 = pd.DataFrame( (df <0.60).sum(axis=1)/w,columns=["_06"])a2 = pd.DataFrame( (df <0.70).sum(axis=1)/w,columns=["_07"])a3 = pd.DataFrame( (df <0.80).sum(axis=1)/w,columns=["_08"])a4 = pd.DataFrame( (df <0.90).sum(axis=1)/w,columns=["_09"])a2_a = pd.DataFrame( a2.values-a.values,columns=["n_1"])a3_a1 = pd.DataFrame( a3.values-a1.values,columns=["n_2"])n_feature = pd.concat([a,a1,a2,a3,a4,a2_a,a3_a1],axis=1)return(n_feature)#分位数据和总体数据def q_feature(df):df['f_min'] =df.min(axis=1)df['f_quan015'] = df.quantile(q = 0.0015 , axis=1)df['f_quan03'] = df.quantile(q = 0.003 , axis=1)df['f_quan061'] = df.quantile(q = 0.0061 , axis=1)df['f_quan12'] = df.quantile(q = 0.012 , axis=1)df['f_quan1'] = df.quantile(q = 0.02 , axis=1)df['f_quan2'] = df.quantile(q = 0.04 , axis=1)df['f_quan22']= df.quantile(q = 0.06, axis=1)df['f_quan23']= df.quantile(q = 0.08 , axis=1)df['f_quan3']= df.quantile(q = 0.1 , axis=1)df['f_quan4']= df.quantile(q = 0.25 , axis=1)df['f_quan5']= df.quantile(q = 0.50 , axis=1)df['f_quan6']= df.quantile(q = 0.75 , axis=1)df['f_quan7']= df.quantile(q = 0.90 , axis=1)df['f_quan8']= df.quantile(q = 0.95 , axis=1)df['r015'] =df['f_quan1'] - df['f_quan061']df['r015_2'] =df['f_quan3'] - df['f_quan061']df['r03'] =df['f_quan4'] - df['f_quan061']df['r015_3'] =df['f_quan3'] - df['f_quan1']df['r03_2'] =df['f_quan5'] - df['f_quan061']df['r03_3'] =df['f_quan1'] - df['f_quan03']df['r03_4'] =df['f_quan3'] - df['f_quan03']df['r1'] =df['f_quan6'] - df['f_quan1']df['r2'] =df['f_quan6'] - df['f_quan2']df['r23'] =df['f_quan6'] - df['f_quan23']df['r3'] =df['f_quan6'] - df['f_quan3']df['r4'] =df['f_quan6'] - df['f_quan4']df['f_max'] =df.max(axis=1) df['f_r'] = df['f_max'] - df['f_min']df['f_mean']= df.mean(axis =1)df['f_var'] = df.var( axis=1)df['f_kurt'] = df.kurt(axis=1)return(df)

好像。。命名不是很规范哈。。结合下面的步骤和3的步骤,此处经历了N次修改,有些凌乱。。。【手动抹泪】

调用函数,生成特征,并将label拼接入训练数据:

n_feature_ = n_feature(df_train)df_train = q_feature(df_train)df_train = pd.concat([df_train,n_feature_#,nf,lab],axis=1)df_train.head()

同时,此处可在预测集上调用函数,完成预测集特征的构造

构造好特征后,查看特征之间的关系和特征与label之间的关系。由于此处经历多次调整、修改,故:

1,此处仅将筛选好的、最终用于建模的特征进行展示。

#最后筛选出用于建模的17个特征如下:col_ys =['f_min','f_quan061', 'f_quan1','f_quan2','f_quan3','f_quan4','f_quan5', 'f_quan6','f_quan7','f_quan8','f_max','f_mean','f_quan22','f_quan23', '_07','_08','_09']df_ys = df_d[col_ys]#选择该17个变量的数据df_ys = df_ys.reset_index(drop=True)#重置索引df_ys

2,在步骤3用模型进行初步预测时,发现 f_min 这个特征与label相关性巨大:当 f_min >0.65时,label均为1,当f_min <0.3时,label均为-1,因此,在接下来的建模中,f_min特征直接当做“规则”先对数据进行处理,剩余的f_min在0.3-0.65之间的数据使用机器学习模型进行预测。

#f_min大于0.65时,label为1,f_min小于0.30时,label为-1;def min_select(df):df_d = df.loc[df["f_min"]<0.65]df_d = df_d.loc[df["f_min"]>0.30]#df_d = df_d.iloc[:,df_d.columns != "f_min"]#df = df.drop(["f_min"],axis=1)return(df_d)df_d = min_select(df_train)df_d.shape[0], df_d["label"].value_counts()

使用f_min“规则”后,2500行的数据剩余517行,label分布存在不均衡问题,在接下的建模中需要注意

查看特征分布:

column = df_ys.columnsfig = plt.figure(figsize=(80,60),dpi=50)for i in range(35) :plt.subplot(7,4,i+1)sns.boxplot(df_ys[column[i]],orient="v",width=0.5 )plt.ylabel(column[i],fontsize=36)plt.show()

部分结果:

查看训练集特征和测试机特征的分布情况。如某特征差异比较大,会影响模型在预测集上的表现,应考虑删除。

dist_cols =6dist_rows = len(test1.columns )plt.figure(figsize=(4*dist_cols,4*dist_rows ),dpi=100)i =1for col in test1.columns:ax = plt.subplot(dist_rows,dist_cols,i )ax = sns.kdeplot(df_ys[col],color="red",shade=True )ax = sns.kdeplot(test1[col],color="blue",shade=True )ax.set_xlabel(col)ax.set_ylabel("different")ax = ax.legend(["df_ys","test1"])i += 1plt.show

查看特征之间共线性:

train = df_ys.copy()scaler = MinMaxScaler() #vif之前先对数据进行去量纲处理scaler.fit(train)train_mms = scaler.transform(train)train_mms = pd.DataFrame(train_mms,columns=train.columns )vif_list = [variance_inflation_factor(x,i) for i in range(x.shape[1]) ]vif_list

特征之间的多重共线性还是比较严重的,尝试删除部分特征后进行预测(此处的特征已经是从最初的40个删除为17),但预测结果直接打脸,还不如保留这些特征。。。

查看特征和label分布关系:

#此处df1为df_ys合并label后的数据,倒腾了太多次。。嗯额嗯。。一开始命名还遵守驼峰规则,迭代次数多了就乱了啊哈哈哈plt.scatter(df1.f_min, df1.f_quan4, #此处可任意更换特征名称查看c=df1.label,cmap='rainbow')

查看特征和label之间的线性关系强弱:

pd.set_option('display.max_columns',10)pd.set_option('display.max_rows',10)df1 = df_ysdf1["label"]=labdf_ys_corr = df1.corr()df_ys_corr#此处没有画热力图,直接排序哈df_ys_corr["label"].sort_values(ascending=False )

特征与label之间的非线性关系可通过树模型进行查看

3,机器学习模型的初步预测和特征工程

数据划分:

train = df1.iloc[:,df1.columns != "label"]lab = df1.iloc[:,df1.columns == "label"]xtrain,xtest,ytrain,ytest=train_test_split(train,lab,test_size=0.3,random_state=90)

从步骤1可知,特征均为float数据,无任何分类或排序特征,故首先选用逻辑回归模型对数据进行初步的建模并查看相关系数:

sta = StandardScaler()sta =sta.fit(xtrain)xtrain_sta = sta.transform(xtrain)xtest_sta = sta.transform(xtest)train_sta = sta.transform(train)lr = LogisticRegression( C= 5,penalty = 'l2',class_weight ={-1:2,1:1})lr = lr.fit(train_sta,lab)lr.score(train_sta,lab), cross_val_score(lr,train_sta,lab,cv=4), lr.coef_

可用pd.concat将lr.coef_和columns拼接查看,更直观。此处可以结合步骤2,对特征进行筛选

使用随机森林进行预测并查看特征重要性:

rdf = RandomForestClassifier(max_depth= 3,criterion= "entropy",n_estimators=100) rdf.fit(train,lab)#rdf.score(xtrain,ytrain), rdf.score(xtest,ytest), cross_val_score(rdf,train,lab,cv=4), rdf.feature_importances_

使用KNN模型进行预测:

scaler = MinMaxScaler()scaler.fit(xtrain)train_mms = scaler.transform(train)xtrain_mms = scaler.transform(xtrain)xtest_mms = scaler.transform(xtest)knn_mms = KNeighborsClassifier(n_neighbors=5,weights = 'distance')knn_mms.fit(xtrain_mms,ytrain)#knn_mms.score(xtest_mms,ytest),cross_val_score(knn_mms,train_mms,lab,cv=4)

使用LightGB模型进行预测:

LGB = LGBMClassifier(objective= 'binary',metric = 'error',boosting_type = 'gbdt', ##num_leaves =32 , #max_depth =4,learning_rate = 0.05,n_estimators = 200,subsample = 0.7,colsample_bytree = 0.7,# subsample_freq =1,# reg_alpha =0.1,# reg_lambda =0.1,#silent =True,# min_split_gain = 0.1 ,# min_child_weight = 0.01)LGB = LGB.fit(train,lab)LGB.score(train,lab), cross_val_score(LGB,train,lab,cv=4)

对LGB 的参数进行网格搜索:

#第一次先搜索一部分参数,搜索出来后当成现成参数带入模型,再搜索另一部分参数,以此类推进行参数调优。#如果一次搜索太多参数,容易把电脑跑死。。。LGB1 = LGBMClassifier(objective= 'binary',metric = 'error',boosting_type = 'gbdt', ##num_leaves =32 , #n_estimators = 100,#max_depth = 1)p = {'max_depth' :[2,3,4,5],'learning_rate' : [0.05, 0.2, 0.3, 0.4,1],'subsample' :[0.7 ,0.8,1],'colsample_bytree': [0.7 ,0.8,1]}gri = GridSearchCV(LGB1,p,cv=4)gri.fit(train_use,lab)gri.best_score_, gri.best_params_

除LGB 模型外,其他模型均可进行网格搜索。训练好模型后,对预测集进行预测并提交。提交后成绩:逻辑回归≈ LGB > 随机森林 > K值邻近。此时成绩还有很大提升空间

4,特征衍生和PCA降维

直接使用加减乘(数据中有为0和趋近于0的数据,故没有用除法。当然把极值和0删除后也可进行除法衍生)进行暴力衍生:

#变量衍生加法def _add(df_d):"""变量衍生:加法"""feature_add = pd.DataFrame(df_d.index,columns=["_add"])colum = df_d.columnsfor ii in range(0,df_d.shape[1]-1):df_d.iloc[:,ii]for i in range(ii+1, df_d.shape[1]):v =df_d.iloc[:,ii] + df_d.iloc[:,i]a =colum[ii] b =colum[i]c = f'{a}_add_{b}'feature_add[c] = vfeature_add = feature_add.iloc [:,1:]feature_addreturn(feature_add) #变量衍生 减法def _sub(df_d):"""变量衍生:减法"""feature_sub = pd.DataFrame(df_d.index,columns=["_sub"])colum = df_d.columnsfor ii in range(0,df_d.shape[1]-1):df_d.iloc[:,ii]for i in range(ii+1, df_d.shape[1]):v =df_d.iloc[:,ii] - df_d.iloc[:,i]a =colum[ii] b =colum[i]c = f'{a}_sub_{b}'feature_sub[c] = vfeature_sub = feature_sub.iloc [:,1:]feature_subreturn(feature_sub) def _mul(df_d):"""乘法"""feature_mul = pd.DataFrame(df_d.index,columns=["_mul"])colum = df_d.columnsfor ii in range(0,df_d.shape[1]-1):df_d.iloc[:,ii]for i in range(ii+1, df_d.shape[1]):v =df_d.iloc[:,ii] * df_d.iloc[:,i]a =colum[ii] b =colum[i]c = f'{a}_mul_{b}'feature_mul[c] = vfeature_mul = feature_mul.iloc [:,1:]feature_mulreturn(feature_mul)#变量衍生 除法def _div(df_d):"""变量衍生:除法"""feature_div = pd.DataFrame(df_d.index,columns=["_div"])colum = df_d.columnsfor ii in range(0,df_d.shape[1]-1):df_d.iloc[:,ii]for i in range(ii+1, df_d.shape[1]):v =df_d.iloc[:,ii] / df_d.iloc[:,i]a =colum[ii] b =colum[i]c = f'{a}_div_{b}'feature_div[c] = vfeature_div = feature_div.iloc [:,1:]feature_divreturn(feature_div)#变量衍生函数:加减乘除的结果输出:def _ys(df_d):a = _add(df_d)b = _sub(df_d)c = _mul(df_d)#d = _div(df_d) #除法feature_ys = pd.concat ([a,b,c,#d],axis=1)feature_ysreturn(feature_ys)train2= _ys(train)train2

运行结果:

可见,衍生后,新增408个特征。同样的,调用函数,对测试集数据进行特征衍生:

test2 =_ys(test1)test2

衍生的训练集为train2,测试集为test2。

使用PCA对衍生特征进行降维,提取其中权重较大的特征,并和原来的17个特征进行合并。

#将训练集和测试集进行合并。如果是实际应用,这里好像有信息泄露的风险哈concat_ = pd.concat([train1,test1],axis=0)concat_#PCA之前记得标准化sta_ys = StandardScaler()sta_ys =sta_ys.fit(concat_)concat_sta = sta_ys.transform(concat_)#test2_sta = sta_ys.transform(test2)

pca = PCA().fit(concat_sta)#pca.explained_variance_plt.figure(figsize=[20,5] )plt.plot(pca.explained_variance_ratio_ )plt.show()

运行结果:

提取权重前10的特征,并将训练集和预测集重新分开:

#提取权重前10的因子pca = PCA(n_components=10 ).fit(concat_sta)concat_sta_10 = pca.transform(concat_sta)

train_pca = pd.DataFrame( concat_sta_10).loc[0:516,:]#训练集test_pca = pd.DataFrame( concat_sta_10).loc[517:1007,:] #测试集

将PCA得到的特征和原来的17个特征拼接:

#把pca数据和原始数据 拼接回去train_use = pd.concat([train,train_pca],axis=1)train_use test_use = pd.concat([test1,test_pca],axis=1)test_use

5,多模型融合预测

使用KNN、逻辑回归、随机森林作为模型第一层(可通过网格搜索对参数进行调优),对数据进行拟合,将拟合结果作为新的“特征”并入原始数据集(原始数据集为27个特征,并入后变成30个特征)形成新的数据集,再使用LGB作为模型第二层对新数据集进行拟合,形成最终的预测结果。

模型第一层:模型代码和步骤3类似,此处不再重复。参数调优后,输出dataframe格式的拟合结果,并将拟合结果和原始数据集合并。可先对单个模型进行参数调优,调优后将三个模型和数据合并的代码封装成函数,方便调用:

def model_level1(data):"""模型第一层,以下需先对单个模型进行参数调优"""xtrain,xtest,ytrain,ytest=train_test_split(train2,lab,test_size=0.3,random_state=90)#随机森林rdf = RandomForestClassifier(max_depth= 2,criterion= "entropy", min_samples_leaf = 2# ,min_impurity_decrease =0.001,n_estimators= 100) rdf.fit(train_use,lab)#逻辑回归sta = StandardScaler()sta =sta.fit(xtrain)xtrain_sta = sta.transform(xtrain)train_sta = sta.transform(train)lr = LogisticRegression( C= 1,penalty = 'l2',class_weight ={-1:1.2,1:1} )lr = lr.fit(train_sta,lab)#K值邻近scaler = MinMaxScaler()scaler.fit(xtrain)train_mms = scaler.transform(train)xtrain_mms = scaler.transform(xtrain)xtest_mms = scaler.transform(xtest)knn_mms = KNeighborsClassifier(n_neighbors=5,weights = 'distance')knn_mms.fit(xtrain_mms,ytrain)#对data进行变准化和归一化,此处容易遗漏data_sta = sta.transform(data)data_mms = scaler.transform(data)#将拟合结果转化成dataframe格式a = pd.DataFrame(rdf.predict(feature_p), columns=["rdf_pr"] )b = pd.DataFrame(lr.predict(data_sta), columns=["lr_pr"] ) c = pd.DataFrame(knn_mms.predict(data_mms),columns=["knn_pr"] )#合并数据,形成新的数据集data_newdata_new = pd.concat([data,a,b,c],axis=1)return(data_new)

模型第二程:使用LGBMClassifier对新数据集进行拟合,形成最终预测结果。模型代码和步骤3类似,此处不再重复。

提交结果后可知,490条记录中,预测正确480条,预测错误10条,加上f_min大于0.65和小于0.3的数据后,共2536条记录,最终结果:预测正确2526条,预测错误10条

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。