【机器学习】某闯关类手游用户流失预测

Final Project: 某闯关类手游用户流失预测

1 案例简介

手游在当下的日常娱乐中占据着主导性地位,成为人们生活中放松身心的一种有效途径。近年来,各种类型的手游,尤其是闯关类的休闲手游,由于其对碎片化时间的利用取得了非常广泛的市场。然而在此类手游中,新用户流失是一个非常严峻的问题,有相当多的新用户在短暂尝试后会选择放弃,而如果能在用户还没有完全卸载游戏的时候针对流失可能性较大的用户施以干预(例如奖励道具、暖心短信),就可能挽回用户从而提升游戏的活跃度和公司的潜在收益,因此用户的流失预测成为一个重要且挑战性的问题。在毕业项目中我们将从真实游戏中非结构化的日志数据出发,构建用户流失预测模型,综合已有知识设计适合的算法解决实际问题。

✍作业说明:

  1. 根据给出的实际数据(包括用户游玩历史,关卡特征等),预测测试集中的用户是否为流失用户(二分类);
  2. 方法不限,使用学堂在线云平台进行评测,评价指标使用 AUC;
  3. 提交代码与实验报告,报告展示对数据的观察、分析、最后的解决方案以及不同尝试的对比等;
  4. 最终评分会参考达到的效果以及对所尝试方法的分析。

2 数据概览

本次使用的是一个休闲类闯关手游的数据,用户在游戏中不断闯关,每一关的基本任务是在限定步数内达到某个目标。每次闯关可能成功也可能失败,一般情况下用户只在完成一关后进入下一关,闯关过程中可以使用道具或提示等帮助。

对大多数手游来说,用户流失往往发生在早期,因此次周的留存情况是公司关注的一个重点。本次数据选取了 2020.2.1 注册的所有用户在 2.1-2.4 的交互数据,数据经过筛选保证这些注册用户在前四日至少有两日登录。流失的定义则参照次周(2.7-2.13)的登录情况,如果没有登录为流失。

本次的数据和以往结构化的形式不同,展现的是更原始的数据记录,更接近公司实际日志的形式,共包含 5 个文件:

2.1 训练集 train.csv

训练集用户,包括用户 id(从 1 开始)以及对应是否为流失用户的 label(1:流失,0:留存)。

训练集共 8158 个用户,其中流失用户大约占 1/3,需要注意的是为了匿名化,这里数据都经过一定的非均匀抽样处理,流失率并不反映实际游戏的情况,用户与关卡的 id 同样经过了重编号,但对于流失预测任务来说并没有影响。

import pandas as pd
import numpy as np
from tqdm import tqdmtrain_df = pd.read_csv('./data/train.csv', sep='\t')
train_df.T
0123456789...8148814981508151815281538154815581568157
user_id2774277527762777277827792780278127822783...10922109231092410925109261092710928109291093010931
label0010110001...0001111010

2 rows × 8158 columns

# 检查是否有空数据
train_df.isna().any().any()
False
train_df['label'].value_counts()
label
0    5428
1    2730
Name: count, dtype: int64

2.2 验证集 dev.csv

验证集格式和训练集相同,主要为了方便离线测试与模型选择。

dev_df = pd.read_csv('./data/dev.csv', sep='\t')
dev_df.T
0123456789...2648264926502651265226532654265526562657
user_id10932109331093410935109361093710938109391094010941...13580135811358213583135841358513586135871358813589
label0101000001...0110100010

2 rows × 2658 columns

# 检查是否有空数据
dev_df.isna().any().any()
False

2.3 测试集 test.csv

测试集只包含用户 id,任务就是要预测这些用户的流失概率。

test_df = pd.read_csv('./data/test.csv', sep='\t')
test_df.T
0123456789...2763276427652766276727682769277027712772
user_id12345678910...2764276527662767276827692770277127722773

1 rows × 2773 columns

# 检查是否有空数据
test_df.isna().any().any()
False

2.4 核心数据集 level_seq.csv

这个是核心的数据文件,包含用户游玩每个关卡的记录,每一条记录是对某个关卡的一次尝试,具体每列的含义如下:

  • user_id:用户 id,和训练、验证、测试集中的可以匹配;
  • level_id:关卡 id;
  • f_success:是否通关(1:通关,0:失败);
  • f_duration:此次尝试所用的时间(单位 s);
  • f_reststep:剩余步数与限定步数之比(失败为 0);
  • f_help:是否使用了道具、提示等额外帮助(1:使用,0:未使用);
  • time:时间戳。
seq_df = pd.read_csv('./data/level_seq.csv', sep='\t')
seq_df
user_idlevel_idf_successf_durationf_reststepf_helptime
01093211127.00.50000002020-02-01 00:05:51
1109322169.00.70370402020-02-01 00:08:01
2109323167.00.56000002020-02-01 00:09:50
3109324158.00.70000002020-02-01 00:11:16
4109325183.00.66666702020-02-01 00:13:12
........................
219434610931401111.00.25000012020-02-03 16:26:37
21943471093141176.00.27777802020-02-03 16:28:06
219434810931420121.00.00000012020-02-03 16:30:17
219434910931420115.00.00000002020-02-03 16:33:40
21943501093142191.00.18181802020-02-03 16:35:18

2194351 rows × 7 columns

# 检查是否有空数据
seq_df.isna().any().any()
False

2.5 关卡统计数据集 level_meta.csv

每个关卡的一些统计特征,可用于表示关卡,具体每列的含义如下:

  • f_avg_duration:平均每次尝试花费的时间(单位 s,包含成功与失败的尝试);
  • f_avg_passrate:平均通关率;
  • f_avg_win_duration:平均每次通关花费的时间(单位 s,只包含通关的尝试);
  • f_avg_retrytimes:平均重试次数(第二次玩同一关算第 1 次重试);
  • level_id:关卡 id,可以和 level_seq.csv 中的关卡匹配。
meta_df = pd.read_csv('./data/level_meta.csv', sep='\t')
meta_df
f_avg_durationf_avg_passratef_avg_win_durationf_avg_retrytimeslevel_id
039.8899400.94446735.5827570.0172251
160.6839750.99183656.7157060.0046382
276.9473550.99123271.7899430.0044803
358.1703470.99384354.8428820.0047614
4101.7845770.95417085.6505470.0273535
..................
1504594.8787880.453730133.6250003.1875001505
1505486.5625000.454180115.9062503.2187501506
1506325.9687500.57352586.2500002.6875001507
1507793.0967740.322684164.0000005.4193551508
1508423.4062500.461409106.8333332.2000001509

1509 rows × 5 columns

# 检查是否有空数据
meta_df.isna().any().any()
False

3 特征工程

在特征工程设计中,选择分为两部分进行:

  1. 根据 level_seq.csv 提取用户特征数据

    • 用户ID
    • 游戏次数
    • 通关率
    • 游戏总时间
    • 平均剩余步数
    • 总帮助次数
    • 登陆天数
  2. 根据 level_meta.csv 提取关卡特征数据

    📣在提取关卡特征数据之后,根据设计的公式,计算出不同分值,合并到用户特征中

    • 用户闯关时间分值:
      用户参与关卡中,通关时间小于平均时间 得0分 ,大于平均时间 得1分
    • 用户重试次数分值:
      用户参与关卡中,重试次数小于平均重试次数 得0分 ,大于平均重试次数 得1分
    • 用户通关率分值:
      用户参与关卡中,通关成功 得1-通关率的分值 ,通关失败 得0分
    • 用户通关时间分值:
      用户 通关 关卡中,通关时间小于平均时间 得0分 ,大于平均时间 得1分

3.1 提取 用户特征

  • 用户ID
  • 游戏次数
  • 通关率
  • 游戏总时间
  • 平均剩余步数
  • 总帮助次数
  • 登陆天数
'''
提取用户特征测试:根据训练集第一个用户的数据,进行特征提取
'''
user_features = []
user_id = train_df['user_id'][0]# 提取该用户的信息到user_df
user_df = seq_df[seq_df['user_id'] == user_id]# 添加用户id
user_features.append(user_id)# 添加用户游戏次数
user_features.append(len(user_df))# 添加用户通关率
user_df_succ = user_df[user_df['f_success']==1]
success_rate = round(len(user_df_succ) / len(user_df), 6)
user_features.append(success_rate)# 添加用户游戏总时间
duration_all = user_df['f_duration'].sum()
user_features.append(duration_all)# 添加平均剩余步数
reststep_mean = round(user_df['f_reststep'].mean(),6)
user_features.append(reststep_mean)# 添加累积帮助次数
times_help = user_df['f_help'].sum()
user_features.append(times_help)# 登陆天数
user_time = pd.to_datetime(user_df['time'])
days = user_time.dt.date.nunique()
user_features.append(days)user_features
[2774, 215, 0.632558, 25398.0, 0.189056, 18, 4]
# 打印单用户数据
user_df
user_idlevel_idf_successf_durationf_reststepf_helptime
61727741150.00.50000002020-02-01 00:02:21
61827742163.00.81481502020-02-01 00:05:22
61927743171.00.72000002020-02-01 00:07:25
62027744145.00.73333302020-02-01 00:09:39
62127745178.00.37500002020-02-01 00:11:36
........................
82727741340145.00.00000002020-02-04 22:53:41
82827741340129.00.00000002020-02-04 22:55:51
82927741340262.00.00000002020-02-04 23:00:15
83027741340164.00.00000002020-02-04 23:03:00
83127741340162.00.00000002020-02-04 23:05:43

215 rows × 7 columns

def Features_Contrust_User(df):features = []for user_id in tqdm(df['user_id'],desc='Processing Users'):user_features = []# 提取该用户的信息到user_dfuser_df = seq_df[seq_df['user_id'] == user_id]# 添加用户iduser_features.append(user_id)# 添加用户游戏次数user_features.append(len(user_df))# 添加用户通关率user_df_succ = user_df[user_df['f_success']==1]success_rate = round(len(user_df_succ) / len(user_df), 6)user_features.append(success_rate)# 添加用户游戏总时间duration_all = user_df['f_duration'].sum()user_features.append(duration_all)# 添加平均剩余步数reststep_mean = round(user_df['f_reststep'].mean(),6)user_features.append(reststep_mean)# 添加累积帮助次数times_help = user_df['f_help'].sum()user_features.append(times_help)# 登陆天数user_time = pd.to_datetime(user_df['time'], format="%Y-%m-%d %H:%M:%S")days = user_time.dt.date.nunique()user_features.append(days)features.append(user_features)features_df = pd.DataFrame(features)features_df.columns =['user_id','游戏次数','通关率','游戏总时间','平均剩余步数比','累积帮助次数','登陆天数']return features_dfFeatures_Contrust_User(dev_df)
Processing Users: 100%|██████████| 2658/2658 [00:05<00:00, 510.69it/s]
user_id游戏次数通关率游戏总时间平均剩余步数比累积帮助次数登陆天数
0109322220.50450541146.00.143857204
110933860.6046519482.00.18906943
210934970.73195910707.00.26544573
310935200.7500001811.00.33987923
4109362290.52401733288.00.115141224
........................
2653135852480.45161327237.00.119008123
2654135863110.44694538920.00.108392163
265513587200.7000002195.00.22879302
265613588280.9642862118.00.44120202
265713589210.8095241403.00.42183902

2658 rows × 7 columns

3.2 提取 关卡特征

🔔根据level_meta.csv包含的关卡数据,计算不同类型的用户分数,合并到用户特征中

  • 🔖用户闯关时间分值:

    用户参与关卡中,通关时间小于平均时间 得0分 ,大于平均时间 得1分

  • 🔖用户重试次数分值:

    用户参与关卡中,重试次数小于平均重试次数 得0分 ,大于平均重试次数 得1分

  • 🔖用户通关率分值:

    用户参与关卡中,通关成功 得1-通关率的分值 ,通关失败 得0分

  • 🔖用户通关时间分值:

    用户 通关 关卡中,通关时间小于平均时间 得0分 ,大于平均时间 得1分


user_scores = []
user_id = train_df['user_id'][0]# 提取该用户的信息到user_df
user_df = seq_df[seq_df['user_id'] == user_id]# 添加用户id
user_scores.append(user_id)scores_duration = 0
scores_win_duration = 0
scores_passrate = 0
for item,row in user_df.iterrows():level_id = row['level_id']level_df = (meta_df[meta_df['level_id'] == level_id]).iloc[0]# 用户闯关时间分值my_duration = row['f_duration']avg_duration = level_df['f_avg_duration']score_duration = 1 if my_duration> avg_duration else 0scores_duration+=score_duration# 用户通关率分值my_suc = row['f_success']avg_duration = level_df['f_avg_passrate']score_passrate = 1-avg_duration if my_suc==1 else 0scores_passrate += score_passrate# 用户通关时间分值if(my_suc == 1):my_win_duration = row['f_duration']avg_win_duration = level_df['f_avg_win_duration']score_win_duration = 1 if my_win_duration> avg_win_duration else 0scores_win_duration+=score_win_durationscores_retrytimes = 0
# 用户重试次数分值
my_retrytimes = user_df['level_id'].value_counts()
for level, count in my_retrytimes.items():level_df = (meta_df[meta_df['level_id'] == level]).iloc[0]avg_retrytime = level_df['f_avg_retrytimes']score_retrytime = 1 if count > avg_retrytime else 0scores_retrytimes+=score_retrytimescores_duration,scores_retrytimes,scores_passrate,scores_win_duration
(16, 112, 29.332141329431998, 60)

def Features_Contrust_Score(df):features = []for user_id in tqdm(df['user_id'], desc='Processing Users'):user_scores = []# 提取该用户的信息到user_dfuser_df = seq_df[seq_df['user_id'] == user_id]# 添加用户iduser_scores.append(user_id)scores_duration = 0scores_win_duration = 0scores_passrate = 0scores_retrytimes = 0for _,row in user_df.iterrows():level_id = row['level_id']level_df = (meta_df[meta_df['level_id'] == level_id]).iloc[0]# 用户闯关时间分值my_duration = row['f_duration']avg_duration = level_df['f_avg_duration']score_duration = 1 if my_duration> avg_duration else 0scores_duration+=score_duration# 用户通关率分值my_suc = row['f_success']avg_duration = level_df['f_avg_passrate']score_passrate = 1-avg_duration if my_suc==1 else 0scores_passrate += score_passrate# 用户通关时间分值if(my_suc == 1):my_win_duration = row['f_duration']avg_win_duration = level_df['f_avg_win_duration']score_win_duration = 1 if my_win_duration> avg_win_duration else 0scores_win_duration+=score_win_durationuser_scores.append(scores_duration)user_scores.append(scores_win_duration)user_scores.append(scores_passrate)# 用户重试次数分值my_retrytimes = user_df['level_id'].value_counts()for level, count in my_retrytimes.items():level_df = (meta_df[meta_df['level_id'] == level]).iloc[0]avg_retrytime = level_df['f_avg_retrytimes']score_retrytime = 1 if count > avg_retrytime else 0scores_retrytimes+=score_retrytimeuser_scores.append(scores_retrytimes)features.append(user_scores)features_df = pd.DataFrame(features)features_df.columns =['user_id','用户闯关时间分值','用户通关率分值','用户通关时间分值','用户重试次数分值']return features_dfFeatures_Contrust_Score(dev_df)
Processing Users: 100%|██████████| 2658/2658 [01:38<00:00, 26.99it/s]
user_id用户闯关时间分值用户通关率分值用户通关时间分值用户重试次数分值
010932519923.017459108
11093316245.81808553
2109345379.24051069
310935680.67539515
410936228625.570134113
..................
26531358532021.389009111
265413586148830.931455130
265513587055.0357688
265613588171.58773227
265713589330.81062018

2658 rows × 5 columns

4 数据集构建

4.1 训练集

import warnings# 训练集 用户数据
train_features_user_df = Features_Contrust_User(train_df)
train_features_user_df
Processing Users: 100%|██████████| 8158/8158 [00:14<00:00, 559.99it/s]
user_id游戏次数通关率游戏总时间平均剩余步数比累积帮助次数登陆天数
027742150.63255825398.00.189056184
127751110.73873918839.00.258456143
22776690.6376816119.00.18654313
327772860.50699340808.00.12424544
427781620.67284032045.00.29945093
........................
8153109273500.50571434697.00.166471104
815410928501.0000004073.00.47120332
8155109292430.46913628858.00.117959143
815610930390.9487186120.00.38018762
815710931560.7857146193.00.28460162

8158 rows × 7 columns

# 训练集 关卡数据
train_features_score_df = Features_Contrust_Score(train_df)
train_features_score_df
Processing Users: 100%|██████████| 8158/8158 [04:53<00:00, 27.78it/s]
user_id用户闯关时间分值用户通关率分值用户通关时间分值用户重试次数分值
02774166029.332141112
12775376712.70747181
22776177.60675546
32777147938.681589130
42778458720.94086790
..................
815310927123655.843120147
8154109282115.03429648
81551092993223.423420114
81561093021302.61252137
81571093113233.95915245

8158 rows × 5 columns

# 合并训练集
train_features_df = pd.merge(train_features_user_df,train_features_score_df,on='user_id')
train_features_df['label'] = train_df['label']
train_features_df
user_id游戏次数通关率游戏总时间平均剩余步数比累积帮助次数登陆天数用户闯关时间分值用户通关率分值用户通关时间分值用户重试次数分值label
027742150.63255825398.00.189056184166029.3321411120
127751110.73873918839.00.258456143376712.707471810
22776690.6376816119.00.18654313177.606755461
327772860.50699340808.00.12424544147938.6815891300
427781620.67284032045.00.29945093458720.940867901
.......................................
8153109273500.50571434697.00.166471104123655.8431201471
815410928501.0000004073.00.471203322115.034296481
8155109292430.46913628858.00.11795914393223.4234201140
815610930390.9487186120.00.3801876221302.612521371
815710931560.7857146193.00.2846016213233.959152450

8158 rows × 12 columns

4.2 验证集

# 验证集 用户数据
dev_features_user_df = Features_Contrust_User(dev_df)
dev_features_user_df
Processing Users: 100%|██████████| 2658/2658 [00:04<00:00, 554.20it/s]
user_id游戏次数通关率游戏总时间平均剩余步数比累积帮助次数登陆天数
0109322220.50450541146.00.143857204
110933860.6046519482.00.18906943
210934970.73195910707.00.26544573
310935200.7500001811.00.33987923
4109362290.52401733288.00.115141224
........................
2653135852480.45161327237.00.119008123
2654135863110.44694538920.00.108392163
265513587200.7000002195.00.22879302
265613588280.9642862118.00.44120202
265713589210.8095241403.00.42183902

2658 rows × 7 columns

# 验证集 关卡数据
dev_features_score_df = Features_Contrust_Score(dev_df)
dev_features_score_df
Processing Users: 100%|██████████| 2658/2658 [01:36<00:00, 27.51it/s]
user_id用户闯关时间分值用户通关率分值用户通关时间分值用户重试次数分值
010932519923.017459108
11093316245.81808553
2109345379.24051069
310935680.67539515
410936228625.570134113
..................
26531358532021.389009111
265413586148830.931455130
265513587055.0357688
265613588171.58773227
265713589330.81062018

2658 rows × 5 columns

# 合并验证集
dev_features_df = pd.merge(dev_features_user_df,dev_features_score_df,on='user_id')
dev_features_df['label'] = dev_df['label']
dev_features_df
user_id游戏次数通关率游戏总时间平均剩余步数比累积帮助次数登陆天数用户闯关时间分值用户通关率分值用户通关时间分值用户重试次数分值label
0109322220.50450541146.00.143857204519923.0174591080
110933860.6046519482.00.1890694316245.818085531
210934970.73195910707.00.265445735379.240510690
310935200.7500001811.00.33987923680.675395151
4109362290.52401733288.00.115141224228625.5701341130
.......................................
2653135852480.45161327237.00.11900812332021.3890091110
2654135863110.44694538920.00.108392163148830.9314551300
265513587200.7000002195.00.22879302055.03576880
265613588280.9642862118.00.44120202171.587732271
265713589210.8095241403.00.42183902330.810620180

2658 rows × 12 columns

4.3 测试集

# 测试集 用户数据
test_features_user_df = Features_Contrust_User(test_df)
test_features_user_df

Processing Users: 100%|██████████| 2773/2773 [00:05<00:00, 533.79it/s]

user_id游戏次数通关率游戏总时间平均剩余步数比累积帮助次数登陆天数
013950.26329138860.00.06052684
122370.51476820190.00.150546204
232300.60869622291.00.235325142
341070.53271013234.00.14374884
452380.42016829454.00.118816204
........................
27682769410.8292683294.00.32469633
276927704110.50121741576.00.147572184
277027712550.70196124327.00.198157154
27712772870.63218410432.00.21133612
277227732470.49797632303.00.11907722

2773 rows × 7 columns

# 测试集 关卡数据
test_features_score_df = Features_Contrust_Score(test_df)
test_features_score_df
Processing Users: 100%|██████████| 2773/2773 [01:38<00:00, 28.24it/s]
user_id用户闯关时间分值用户通关率分值用户通关时间分值用户重试次数分值
0192819.660024106
1211028.858450113
2343030.559213131
3422356.66946158
45106218.637362105
..................
27682769242.20889335
2769277013767.168588164
2770277124050.195715136
277127729336.19586857
27722773178826.838850118

2773 rows × 5 columns

# 合并测试集
test_features_df = pd.merge(test_features_user_df,test_features_score_df,on='user_id')
test_features_df
user_id游戏次数通关率游戏总时间平均剩余步数比累积帮助次数登陆天数用户闯关时间分值用户通关率分值用户通关时间分值用户重试次数分值
013950.26329138860.00.0605268492819.660024106
122370.51476820190.00.15054620411028.858450113
232300.60869622291.00.23532514243030.559213131
341070.53271013234.00.1437488422356.66946158
452380.42016829454.00.118816204106218.637362105
....................................
27682769410.8292683294.00.32469633242.20889335
276927704110.50121741576.00.14757218413767.168588164
277027712550.70196124327.00.19815715424050.195715136
27712772870.63218410432.00.211336129336.19586857
277227732470.49797632303.00.11907722178826.838850118

2773 rows × 11 columns

4.4 数据归一化

  • 归一化:Min-Max Normalization
    • x i − m i n ( x i ) m a x ( x i ) − m i n ( x i ) \frac{x_{i}-min(x_i)}{max(x_i)-min(x_i)} max(xi)min(xi)ximin(xi)
''' 
函数说明:对数据进行归一化
Parameters:dataSet - 特征矩阵
Returns:normDataSet - 归一化后的特征矩阵
'''
def autoNorm(dataSet):# 获得数据的最小值minVals = dataSet.min(0)maxVals = dataSet.max(0)# 最大值和最小值的范围ranges = maxVals - minVals# shape(dataSet)返回dataSet的矩阵行列数normDataSet = np.zeros(np.shape(dataSet))# 返回dataSet的行数m = dataSet.shape[0]# 原始值减去最小值normDataSet = dataSet - np.tile(minVals, (m, 1))# 除以最大和最小值的差,得到归一化数据normDataSet = normDataSet / np.tile(ranges, (m, 1))# 返回归一化数据结果,数据范围,最小值return normDataSet
# 训练集
train_features = np.array(train_features_df.iloc[:, 1:11])
train_features = autoNorm(train_features) # 归一化
train_labels = train_features_df.iloc[:, -1].values
# 验证集
dev_features = np.array(dev_features_df.iloc[:, 1:11])
dev_features = autoNorm(dev_features) # 归一化
dev_labels = dev_features_df.iloc[:, -1].values
train_features.shape,train_labels.shape,dev_features.shape,dev_labels.shape
((8158, 10), (8158,), (2658, 10), (2658,))
# 测试集
test_features = np.array(test_features_df.iloc[:, 1:11])
test_features = autoNorm(test_features) # 归一化
test_features.shape
(2773, 10)

5 模型构建

from sklearn import tree
from sklearn.ensemble import AdaBoostClassifier
from sklearn.naive_bayes import MultinomialNB, BernoulliNB, ComplementNB
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score, precision_score, recall_score
from sklearn.metrics import roc_auc_score
from sklearn.model_selection import GridSearchCV
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import confusion_matrix, ConfusionMatrixDisplay
import matplotlib.pyplot as plt
p_list = [] # 记录模型预测结果(0/1)
pro_list = [] # 记录模型预测结果(probability)

5.1 决策树

parameters = {'criterion':['entropy', 'gini'], 'max_depth': range(1, 6), 'min_samples_split': [10, 50, 100, 200, 500, 1000]} # 定义需要遍历的参数clf = tree.DecisionTreeClassifier()
grid_search = GridSearchCV(clf, parameters, scoring='accuracy', cv=5, verbose=100, n_jobs=4) # 传入模型和要遍历的参数
grid_search.fit(train_features,train_labels) # 在所有数据上搜索参数
print(grid_search.best_score_, grid_search.best_params_) # 输出最佳指标和最佳参数
Fitting 5 folds for each of 60 candidates, totalling 300 fits
0.7391579056515309 {'criterion': 'entropy', 'max_depth': 2, 'min_samples_split': 10}
clf = tree.DecisionTreeClassifier(**grid_search.best_params_)
clf.fit(train_features,train_labels) # 在训练集上训练
p_test = clf.predict(dev_features) # 在测试集上预测,获得预测值
print(p_test) # 输出预测值
pro_test = clf.predict_proba(dev_features)test_acc = accuracy_score(p_test, dev_labels) # 将测试预测值与测试集标签对比获得准确率
test_prec = precision_score(p_test, dev_labels)
test_rec = recall_score(p_test, dev_labels)
test_AUC = roc_auc_score(dev_labels,pro_test[:,1])
print('>> accuracy: {:.4f}, precision: {:.4f}, recall: {:.4f}'.format(test_acc, test_prec, test_rec)) # 输出评价指标
print('>> AUC: {:.4f}'.format(test_AUC))p_list.append(p_test)
pro_list.append(pro_test)
[0 0 0 ... 1 1 1]
>> accuracy: 0.7397, precision: 0.5239, recall: 0.6422
>> AUC: 0.7663

5.2 贝叶斯学习

for NB in [BernoulliNB(), MultinomialNB(), ComplementNB()]: # 测试三种类型的朴素贝叶斯NB.fit(train_features,train_labels) # 在训练集上训练p_test = NB.predict(dev_features) # 在测试集上预测,获得预测值pro_test = NB.predict_proba(dev_features)test_acc = accuracy_score(dev_labels, p_test) # 准确率test_prec = precision_score(dev_labels, p_test) # 精准率test_rec = recall_score(dev_labels, p_test) # 召回率test_AUC = roc_auc_score(dev_labels,pro_test[:,1])print(NB)print(p_test)print('>> accuracy: {:.4f}, precision: {:.4f}, recall: {:.4f}'.format(test_acc, test_prec, test_rec)) # 输出评价指标print('>> AUC: {:.4f}'.format(test_AUC))
BernoulliNB()
[0 0 0 ... 1 1 1]
>> accuracy: 0.7385, precision: 0.6366, recall: 0.5327
>> AUC: 0.7073
MultinomialNB()
[0 0 0 ... 0 0 0]
>> accuracy: 0.6731, precision: 0.7162, recall: 0.0588
>> AUC: 0.7786
ComplementNB()
[0 0 0 ... 1 1 1]
>> accuracy: 0.7400, precision: 0.6067, recall: 0.6626
>> AUC: 0.7786
NB = ComplementNB()
NB.fit(train_features,train_labels)
p_test = NB.predict(dev_features)
pro_test = NB.predict_proba(dev_features)p_list.append(p_test)
pro_list.append(pro_test)

5.3 k-邻近

parameters = {'n_neighbors': range(1, 30),'weights':['uniform', 'distance'],'metric':['euclidean', 'manhattan', 'chebyshev', 'minkowski']}
KNN = KNeighborsClassifier()
grid_search = GridSearchCV(KNN, parameters, scoring='accuracy', cv=5, verbose=100, n_jobs=4)
grid_search.fit(train_features,train_labels) # 在所有数据上搜索参数
print(grid_search.best_score_, grid_search.best_params_) # 输出最佳指标和最佳参数
Fitting 5 folds for each of 232 candidates, totalling 1160 fits
0.7329049001574879 {'metric': 'chebyshev', 'n_neighbors': 29, 'weights': 'uniform'}
KNN = KNeighborsClassifier(**grid_search.best_params_)  # 取最佳参数
KNN.fit(train_features,train_labels) # 在训练集上训练
p_test = KNN.predict(dev_features) # 在测试集上预测,获得预测值
print(p_test) # 输出预测值
pro_test = KNN.predict_proba(dev_features)
test_acc = accuracy_score(p_test, dev_labels) # 将测试预测值与测试集标签对比获得准确率
test_prec = precision_score(p_test, dev_labels)
test_rec = recall_score(p_test, dev_labels)
test_AUC = roc_auc_score(dev_labels,pro_test[:,1])print('>> accuracy: {:.4f}, precision: {:.4f}, recall: {:.4f}'.format(test_acc, test_prec, test_rec)) # 输出评价指标
print('>> AUC: {:.4f}'.format(test_AUC))p_list.append(p_test)
pro_list.append(pro_test)
[0 0 0 ... 1 1 1]
>> accuracy: 0.7348, precision: 0.4917, recall: 0.6420
>> AUC: 0.7712

5.4 SVM

param_grid = {'C': [0.1,1, 10, 100], 'gamma': [1,0.1,0.01,0.001]} 
grid_search  = GridSearchCV(SVC(),param_grid,scoring='accuracy', cv=5, verbose=100, n_jobs=4)
grid_search.fit(train_features,train_labels)
print(grid_search.best_score_, grid_search.best_params_)
Fitting 5 folds for each of 16 candidates, totalling 80 fits
0.7401390491819045 {'C': 1, 'gamma': 0.01}
SVM = SVC(probability=True,**grid_search.best_params_)  # 取最佳参数
SVM.fit(train_features,train_labels) # 在训练集上训练
p_test = SVM.predict(dev_features) # 在测试集上预测,获得预测值
print(p_test) # 输出预测值
pro_test = SVM.predict_proba(dev_features)
test_acc = accuracy_score(p_test, dev_labels) 
test_prec = precision_score(p_test, dev_labels)
test_rec = recall_score(p_test, dev_labels)
test_AUC = roc_auc_score(dev_labels,pro_test[:,1])print('>> accuracy: {:.4f}, precision: {:.4f}, recall: {:.4f}'.format(test_acc, test_prec, test_rec)) # 输出评价指标
print('>> AUC: {:.4f}'.format(test_AUC))p_list.append(p_test)
pro_list.append(pro_test)
[0 0 0 ... 1 1 1]
>> accuracy: 0.7397, precision: 0.5250, recall: 0.6418
>> AUC: 0.7801
from itertools import cycle
import matplotlib.pyplot as plt
from sklearn.metrics import roc_curve, auc
fpr = dict()
tpr = dict()
roc_auc = dict()
for i in range(4):fpr[i], tpr[i], _ = roc_curve(dev_labels, pro_list[i][:,1])roc_auc[i] = auc(fpr[i], tpr[i])
plt.figure()
colors = cycle(["aqua", "darkorange", "cornflowerblue","deeppink"])
models = cycle(["DT","BN","KNN","SVM"])
for i, color,model in zip(range(4), colors, models):plt.plot(fpr[i],tpr[i],color=color,label="ROC curve {0} (auc = {1:0.4f})".format(model, roc_auc[i]),)
plt.plot([0, 1], [0, 1], color="black", linestyle="--")
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel("False Positive Rate")
plt.ylabel("True Positive Rate")
plt.title("ROC curve")
plt.legend(loc="lower right")
plt.grid("open")
plt.show()


请添加图片描述

5.5 集成学习

p_list_Adaboost = [] # 记录Adaboost+模型预测结果(0/1)
pro_list_Adaboost = [] # 记录Adaboost+模型预测结果(probability)
for baseclf in [tree.DecisionTreeClassifier(criterion='entropy', max_depth=2, min_samples_split=50),BernoulliNB(),SVC(C=1,  gamma=0.01,probability=True)]:Adaboost_ = AdaBoostClassifier(baseclf,algorithm='SAMME')Adaboost_.fit(train_features,train_labels)p_test = Adaboost_.predict(dev_features)p_list_Adaboost.append(p_test)pro_test = Adaboost_.predict_proba(dev_features)pro_list_Adaboost.append(pro_test)test_acc = accuracy_score(p_test, dev_labels)test_prec = precision_score(p_test, dev_labels) test_rec = recall_score(p_test, dev_labels)test_AUC = roc_auc_score(dev_labels,pro_test[:,1])print(baseclf)print('>> accuracy: {:.4f}, precision: {:.4f}, recall: {:.4f}'.format(test_acc, test_prec, test_rec)) # 输出评价指标print('>> AUC: {:.4f}'.format(test_AUC))
DecisionTreeClassifier(criterion='entropy', max_depth=2, min_samples_split=50)
>> accuracy: 0.7216, precision: 0.3929, recall: 0.6472
>> AUC: 0.7811
BernoulliNB()
>> accuracy: 0.7397, precision: 0.5239, recall: 0.6422
>> AUC: 0.7072
SVC(C=1, gamma=0.01, probability=True)
>> accuracy: 0.6610, precision: 0.0000, recall: 0.0000
>> AUC: 0.5000
for i,model in enumerate(["Adaboost + D_T","Adaboost + B_N","Adaboost + SVM"]):auc = roc_auc_score(dev_labels,pro_list_Adaboost[i][:,1])print("{0} model: \n>> AUC = {1:.4f}".format(model,auc))
Adaboost + D_T model: 
>> AUC = 0.7811
Adaboost + B_N model: 
>> AUC = 0.7072
Adaboost + SVM model: 
>> AUC = 0.5000

6 提交测试

提交文件需要对测试集中每一个用户给出预测流失的概率,每行包括一个ID(和 test.csv 中的user_id对应)以及预测的概率Prediction(0-1的浮点数),用逗号分隔。示例提交格式如下:

ID,Prediction  
1,0.9  
2,0.45  
3,0.78  
...  
base = tree.DecisionTreeClassifier(criterion='entropy', max_depth=2, min_samples_split=50)
TEST = Adaboost_ = AdaBoostClassifier(base,algorithm='SAMME')
TEST.fit(train_features,train_labels)
pro_test = TEST.predict_proba(test_features)test_df['Prediction'] = pro_test[:,1]
test_df.rename(columns={'user_id':'ID'},inplace=True)
test_df.T
0123456789...2763276427652766276727682769277027712772
ID1.0000002.000003.0000004.0000005.0000006.000007.0000008.0000009.00000010.000000...2764.0000002765.0000002766.0000002767.0000002768.0000002769.0000002770.0000002771.000002772.0000002773.000000
Prediction0.2976980.382730.4850570.3593370.3249840.371530.5201230.3088080.2894940.504076...0.3762910.4425270.3971510.5698640.5080340.4989190.3550910.366250.4642520.477353

2 rows × 2773 columns

# DataFrame 转 .csv
test_df.to_csv(r'./result.csv',index=False)

Tips

  • 一个基本的思路可以是:根据游玩关卡的记录为每个用户提取特征 → 结合 label 构建表格式的数据集 → 使用不同模型训练与测试;
  • 还可以借助其他模型(如循环神经网络)直接对用户历史序列建模;
  • 数据量太大运行时间过长的话,可以先在一个采样的小训练集上调参;
  • 集成多种模型往往能达到更优的效果;
  • 可以使用各种开源工具。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/661503.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

服务器未启动而端口进程仍在运行如何查看并杀死

首先登录服务器然后查看当前监听的端口&#xff1a; sudo netstat -tuln比如这里的8080&#xff0c;我们此时并未启动服务器&#xff0c;但是它却正在运行&#xff0c;这会导致服务器刚启动就秒挂。如果没有日志的话会让人有点疑惑&#xff0c;这种情况可能是之前运行了该进程…

代码随想录day17--二叉树的应用5

LeetCode654.最大二叉树 题目描述&#xff1a; 给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建: 创建一个根节点&#xff0c;其值为 nums 中的最大值。递归地在最大值 左边 的 子数组前缀上 构建左子树。递归地在最大值 右边 的 子数组后…

鸿蒙开发-UI-页面路由

鸿蒙开发-UI-组件 鸿蒙开发-UI-组件2 鸿蒙开发-UI-组件3 鸿蒙开发-UI-气泡/菜单 文章目录 一、基本概念 二、页面跳转 1.router基本概念 2.使用场景 3.页面跳转参数传递 三、页面返回 1.普通页面返回 2.页面返回前增加一个询问框 1.系统默认询问框 2.自定义询问框 总…

EtherCAT FP介绍系列文章—UDP gateway

EtherCAT主站上的Mailbox Gateway功能&#xff0c;可以用于将EtherCAT mailbox相关协议从外部设备的工具通过邮箱网关路由到EtherCAT从站设备。在EtherCAT规范中定义的所有邮箱协议在此功能中都可用&#xff0c;例如CoE, FoE, VoE, SoE。 但是&#xff0c;这里特别注意的是Mai…

雨云游戏云VPS服务器用Linux搭建MCSM面板和Minecraft Mohist 1.20.2服务器教程,我的世界MOD和插件服开服教程。

雨云游戏云VPS服务器用Linux搭建MCSM面板和Minecraft Mohist 1.20.2服务器教程&#xff0c;我的世界MOD和插件服开服教程。 本教程演示安装的MC服是Mohist 1.20.2版&#xff0c;其他版本也可以参考本教程&#xff0c;差别不大。 本教程使用Docker来运行mc服&#xff0c;可以方…

UserWarning: Glyph 39640 missing from current font问题

是因为不支持中文字体导致的&#xff0c;设置为一个支持中文的字体就行了。 另外&#xff0c;上面的改动会引起负号显示为方块&#xff0c;需要额外再加一条设置。 在中文系统上 import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei] # 设置为一个支持…

C#,斯特林数(Stirling Number)的算法与源代码

1 斯特林数 在组合数学&#xff0c;斯特林数可指两类数&#xff0c;第一类斯特林数和第二类斯特林数&#xff0c;都是由18世纪数学家James Stirling提出的。它们自18世纪以来一直吸引许多数学家的兴趣&#xff0c;如欧拉、柯西、西尔沃斯特和凯莱等。后来哥本哈根&#xff08;…

Docker容器引擎私有仓库的搭建和管理

目录 一、私有仓库搭建和管理 &#xff08;一&#xff09;搭建本地私有仓库 1.拉取registry镜像 2.修改docker配置文件并重启 3.运行registry容器 4.给镜像打标签 5.上传到私有仓库 6.查看私有仓库的所有镜像 7.从私有仓库拉取镜像 &#xff08;1&#xff09;删除cen…

网络协议 UDP协议

网络协议 UDP协议 在之前的文章中有对UDP协议套接字的使用进行讲解&#xff0c;本文主要对UDP协议进行一些理论补充。 文章目录 网络协议 UDP协议1. 概念2. UDP协议格式2.1 数据报长度2.2 校验和/检验和2.2.1 CRC校验2.2.2 MD5算法 1. 概念 UDP&#xff0c;即User Datagram P…

gitlab操作手册

git操作篇 1. 项目克隆 git clone gitgitlab.test.cn:pro/project1.git2. 项目的提交 注&#xff1a;如果要查看文件的状态可以用git status命令&#xff1a; 如上图所示&#xff0c;文件已经修改了。 3. 项目的推送 git push origin feature/test01注&#xff1a;如果要查…

nodejs+vue+ElementUi宠物领养救助网站0w6wc

宠物领养救助平台采用B/S架构&#xff0c;数据库是MySQL。网站的搭建与开发采用了先进的nodejs进行编写&#xff0c;使用了vue框架。该系统从两个对象&#xff1a;由管理员和用户来对系统进行设计构建。主要功能包括&#xff1a;个人信息修改&#xff0c;对用户、宠物类别、宠物…

深信服技术认证“SCSA-S”划重点:安全事件管理处置

为帮助大家更加系统化地学习网络安全知识&#xff0c;以及更高效地通过深信服安全服务认证工程师考核&#xff0c;深信服特别推出“SCSA-S认证备考秘笈”共十期内容&#xff0c;“考试重点”内容框架&#xff0c;帮助大家快速get重点知识~ 划重点来啦 *点击图片放大展示 深信…

MySQL-运维-主从复制

一、概述 二、原理 三、搭建 1、服务器准备 2、主库配置 &#xff08;1&#xff09;、修改配置文件/etc/my.cnf &#xff08;2&#xff09;、重启MySQL服务器 &#xff08;3&#xff09;、登录mysql&#xff0c;创建远程链接的账号&#xff0c;并授予主从复制权限 &#xff0…

shell脚本-免交互

一、Here Document免交互&#xff1a; 1.交互概述&#xff1a; 交互&#xff1a;当计算机播放某多媒体程序的时候&#xff0c;编程人员可以发出指令控制该程序的运行&#xff0c;而不是程序单方面执行下去&#xff0c;程序在接受到编程人员相应的指令后而相应地做出反应。 对于…

铁轨语义分割(Unet结合resnet系列)

数据介绍 一类是图片&#xff0c;一类是图像标签。 引入库&#xff0c;处理数据 import torch.nn as nn import torch import torch.nn.functional as F import os from PIL import Image import torch from torch.utils.data import Dataset import torchvision.transfor…

Unity SRP 管线【第七讲:URP LOD实现以及Reflections反射探针】

目录 一、URP LOD 组件1、LOD Group的使用2、LOD切换原理Cross Fade(淡入淡出)模式Animated Cross-Fading如果未设置Clip&#xff0c;并且Fade Transition Width不为0LOD物体烘培 SpeedTree 模式 二、反射探针1. 获取反射探针数据2. 环境光照明 IBL3. 反射探针&#xff08;Refl…

部署前后端分离项目详细教程

部署前后端分离项目详细教程 1、准备工作 首先你需要一台服务器&#xff0c;然后在服务器上安装好你所需要的环境&#xff0c;我这里用的宝塔界面来安装环境。 如果有人不知道怎么安装宝塔界面&#xff0c;可参考这篇文章&#xff0c;如果不知道怎么买服务器&#xff0c;可以参…

菜鸡后端的前端学习记录-2

前言 记录一下看视频学习前端的的一些笔记&#xff0c;以前对Html、Js、CSS有一定的基础&#xff08;都认得&#xff0c;没用过&#xff09;&#xff0c;现在不想从头再来了&#xff0c;学学Vue框架&#xff0c;不定时更新&#xff0c;指不定什么时候就鸽了。。。。 忘了记一下…

前端工程化基础(四):Git代码版本控制工具详解

Git版本控制工具详解 认识版本控制&#xff08;版本控制&#xff09; 是维护 工程蓝图的标准做法&#xff0c;能追踪工程蓝图从诞生一直到定案的过程版本控制也是 一种软件工程技巧&#xff0c;借此能在软件开发的过程中&#xff0c;确保不同的人所编辑的同一程序都能得到同步…

tcp/ip模型中,帧是第几层的数据单元?

在网络通信的世界中&#xff0c;TCP/IP模型以其高效和可靠性而著称。这个模型是现代互联网通信的基石&#xff0c;它定义了数据在网络中如何被传输和接收。其中&#xff0c;一个核心的概念是数据单元的层级&#xff0c;特别是“帧”在这个模型中的位置。今天&#xff0c;我们就…