本文共 6807 字,大约阅读时间需要 22 分钟。
作为一个重度社恐的老萌新,决定单枪匹马的来参加这个练习赛,也是我参加的第一个此类比赛。
本次练习赛虽然最终成绩真的很普通,欢迎提提高意见,非常感谢。
不过收获真的很多,尝试了很多没接触过的东西。
希望有同学看到这段话,带我打打更大比赛!我可以
数据选自UCI机器学习库中的「银行营销数据集(Bank Marketing Data Set)」
这些数据与葡萄牙银行机构的营销活动相关。这些营销活动以电话为基础,一般,银行的客服人员需要联系客户至少一次,以此确认客户是否将认购该银行的产品(定期存款)。
因此,与该数据集对应的任务是「分类任务」,「分类目标」是预测客户是(' 1 ')或者否(' 0 ')购买该银行的产品。
字段说明
NO | 字段名称 | 数据类型 | 字段描述 |
---|---|---|---|
1 | ID | Int | 客户唯一标识 |
2 | age | Int | 客户年龄 |
3 | job | String | 客户的职业 |
4 | marital | String | 婚姻状况 |
5 | education | String | 受教育水平 |
6 | default | String | 是否有违约记录 |
7 | balance | Int | 每年账户的平均余额 |
8 | housing | String | 是否有住房贷款 |
9 | loan | String | 是否有个人贷款 |
10 | contact | String | 与客户联系的沟通方式 |
11 | day | Int | 最后一次联系的时间(几号) |
12 | month | String | 最后一次联系的时间(月份) |
13 | duration | Int | 最后一次联系的交流时长 |
14 | campaign | Int | 在本次活动中,与该客户交流过的次数 |
15 | pdays | Int | 距离上次活动最后一次联系该客户,过去了多久(999表示没有联系过) |
16 | previous | Int | 在本次活动之前,与该客户交流过的次数 |
17 | poutcome | String | 上一次活动的结果 |
18 | y | Int | 预测客户是否会订购定期存款业务 |
这部分内容比较冗长,虽然不是重点但仔细了解指标,有助于下一节的特征工程,对结果有很大帮助。
import numpy as npimport pandas as pd#导入数据集def load_data(): home_path = r"..." #输入数据所在位置 train_path = os.path.join(home_path, "train_set.csv") test_path = os.path.join(home_path, "test_set.csv") train_data = pd.read_csv(train_path) test_data = pd.read_csv(test_path) return train_data, test_datatrainData, testData = load_data()print('train Data:{} testData:{}'.format(trainData.shape, testData.shape))print(trainData.describe())
训练集(25317, 18),测试集(10852, 17)
age | balance | day | duration | campaign | pdays | previous | y | |
count | 25317.000000 | 25317 | 25317 | 25317 | 25317 | 25317 | 25317 | 25317 |
mean | 40.93538 | 1357.555 | 15.835289 | 257.7324 | 2.77205 | 40.24877 | 0.591737 | 0.116957 |
std | 10.63429 | 2999.823 | 8.31948 | 256.9752 | 3.136097 | 100.2135 | 2.568313 | 0.321375 |
min | 18 | -8019 | 1 | 0 | 1 | -1 | 0 | 0 |
25% | 33 | 73 | 8 | 103 | 1 | -1 | 0 | 0 |
50% | 39 | 448 | 16 | 181 | 2 | -1 | 0 | 0 |
75% | 48 | 1435 | 21 | 317 | 3 | -1 | 0 | 0 |
max | 95 | 102127 | 31 | 3881 | 55 | 854 | 275 | 1 |
#缺失#训练集trainData.isnull().sum()#测试集testData.isnull().sum()
结果表示 训练集与测试集均无缺失值
目标分布:
import matplotlib.pyplot as pltimport seaborn as sns#目标分布plt.subplots(figsize=[6,6])trainData['y'].value_counts().plot(kind='pie',autopct='%.1f%%')
88.3%的用户未办理业务,11.7%的用户办理了业务
下面来看看单指标对于目标y的分布情况
#单指标分布#首先看分类变量#jobf, ax = plt.subplots(1, 2, figsize=(10, 5))trainData[['y', 'job']].groupby(['job']).mean().plot(kind='bar', ax=ax[0])sns.countplot('job',hue='y',data=trainData,ax=ax[1])ax[0].set_title('Rate vs job')ax[1].set_title('number vs job')#其他分类变量套用即可
从以上的柱状图可以看出这些指标与能否成功办理业务的关系:
如无个人/住房贷款,无违约,显然更易成功订购定期存款业务
#再看连续变量sns.FacetGrid(trainData, hue='y', aspect=2, size=5).map(sns.kdeplot, 'age', shade=True).add_legend()
可见,大约25岁以下客户办理成功率更高,这与前面职业为学生/婚姻状况为单身的成功率更高结论的一致
貌似月初月末有一点影响。看不出什么,日这个指标需与月结合起来形成日期一起看,单看日指标没有很大意义。而月之间有存在的明显的差异,且后面有pdays这个指标表示距离上次活动最后一次联系该客户,过去了多久,因此这个指标可能意义不大。
显然交流时长与y之间的关联性较强。
说明联系紧密的成功办理的可能性更高。
尽量使用原特征,对二分类变量做序号编码,多分类变量做独热编码,连续变量尽量归一化,这里没有归一化。
from sklearn.model_selection import train_test_split#将测试集和训练集合并full = trainData.append(testData, ignore_index=True, sort=False)full.tail()#哑原处理data_columns=list(full.columns)categoricalCol=['ID','job','marital','education','default','housing','loan','contact','month' ,'poutcome']#分类变量名continuousCol=list(set(data_columns).difference(categoricalCol)) #连续变量continuousColcontinuousCol.pop(5)#删去y
#对分类变量分别进行序号编码和独热编码default = pd.Series(np.where(full['default']=='yes', 1, 0), name='default')housing = pd.Series(np.where(full['housing']=='yes', 1, 0), name='housing')loan = pd.Series(np.where(full['loan']=='yes', 1, 0), name='loan')job= pd.get_dummies(full['job'],prefix='job')marital= pd.get_dummies(full['marital'],prefix='marital')education= pd.get_dummies(full['education'],prefix='education')contact= pd.get_dummies(full['contact'],prefix='contact')month= pd.get_dummies(full['month'],prefix='month')poutcome= pd.get_dummies(full['poutcome'],prefix='poutcome')#拼回来full1 = pd.concat([job,marital,education,default,contact,housing,loan,month ,poutcome], axis=1)full2=full[continuousCol].applymap(float) ##连续变量的值变为float格式FULL=full1.join(full2)FULL=FULL.join(full['y'])train_set = FULL[0:25317]test_set = FULL[25317:]test_set = test_set.drop('y',axis=1)#划分训练集和验证集feature = train_set.drop('y',axis=1)label = train_set['y']X_train,X_test,y_train,y_test=train_test_split(feature,label,test_size=0.25,random_state=0)
在这里我尝试了各种模型,包括knn、逻辑回归、svm等等结果都很不好,然后开始尝试集成学习。
这里主要介绍我尝试gbdt时的过程
由于评分标准是auc,首先定义一个模型评估的函数
from sklearn.metrics import accuracy_score, roc_auc_score,roc_curve,auc# 性能评估def model_metrics(clf, X_train, X_test, y_train, y_test): # 预测训练集和测试集 y_train_pred = clf.predict(X_train) y_test_pred = clf.predict(X_test) y_train_proba = clf.predict_proba(X_train)[:,1] y_test_proba = clf.predict_proba(X_test)[:,1] # AUC取值 print('[auc值]', end = ' ') print('训练集:', '%.4f'%roc_auc_score(y_train, y_train_proba), end = ' ') print('测试集:', '%.4f'%roc_auc_score(y_test, y_test_proba)) # ROC曲线 fpr_train, tpr_train, thresholds_train = roc_curve(y_train, y_train_proba, pos_label = 1) fpr_test, tpr_test, thresholds_test = roc_curve(y_test, y_test_proba, pos_label = 1) label = ["Train - AUC:{:.4f}".format(auc(fpr_train, tpr_train)), "Test - AUC:{:.4f}".format(auc(fpr_test, tpr_test))] plt.plot(fpr_train,tpr_train) plt.plot(fpr_test,tpr_test) plt.plot([0, 1], [0, 1], 'd--') plt.xlabel('False Positive Rate') plt.ylabel('True Positive Rate') plt.legend(label, loc = 4) plt.title("ROC curve")
clf=ensemble.GradientBoostingClassifier()clf.fit(X_train,y_train)model_metrics(clf, X_train, X_test, y_train, y_test)
先是直接用gbdt的默认参数,auc等于
0.9249611515690814
然后进行调参
#大概是这样,前面发现learning_rate=0.29,n_estimators=96时得分最高X_train,X_test,y_train,y_test=train_test_split(feature,label,test_size=0.25,random_state=0)model = ensemble.GradientBoostingClassifier(learning_rate=0.29,n_estimators=96) #maxdepths=np.arange(1,20).tolist()param_grid= {'max_depth':list(range(3,14,2)), 'min_samples_split':list(range(100,801,200))}kflod = StratifiedKFold(n_splits=10, shuffle = True,random_state=7)grid_search = GridSearchCV(model,param_grid,scoring = 'roc_auc',n_jobs = -1,cv = kflod)grid_result = grid_search.fit(X_train, y_train) #运行网格搜索print("Best: %f using %s" % (grid_result.best_score_,grid_search.best_params_))#grid_scores_:给出不同参数情况下的评价结果。best_params_:描述了已取得最佳结果的参数的组合#best_score_:成员提供优化过程期间观察到的最好的评分#具有键作为列标题和值作为列的dict,可以导入到DataFrame中。#注意,“params”键用于存储所有参数候选项的参数设置列表。means = grid_result.cv_results_['mean_test_score']params = grid_result.cv_results_['params']for mean,param in zip(means,params): print("%f with: %r" % (mean,param))
尝试了很多次之后 将参数定为 learning_rate=0.29,n_estimators=96,max_depth=5,min_samples_split=700
此时
auc提高到了0.9326.
我也尝试其他的集成算法,lgb的得分最高,可是参数调整了半天好像默认参数最高。
后来又尝试了一下用StandardScaler融合模型,结果成绩几乎没有提升,模型选的不好甚至还下降了。
但是发现直接取不同算法结果的算数平均值 有效果。
我还尝试了构建了新的特征,比如有个人贷款或有房贷的令为有贷款,也通过特征重要度删去一些非常不重要的变量,但是效果甚微。
另外本文中代码有多处借鉴并调整,感谢各位前人的分享。
转载地址:http://kerii.baihongyu.com/