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

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 tqdm


train_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_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'], 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_df

Features_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 0
    scores_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 0
    scores_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 0
        scores_win_duration+=score_win_duration

scores_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 0
    scores_retrytimes+=score_retrytime

scores_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_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
        scores_retrytimes = 0
        for _,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 0
            scores_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 0
            scores_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 0
                scores_win_duration+=score_win_duration
        
        user_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 0
            scores_retrytimes+=score_retrytime
        user_scores.append(scores_retrytimes)
    
        features.append(user_scores)
    
    features_df = pd.DataFrame(features)
    features_df.columns =['user_id','用户闯关时间分值','用户通关率分值','用户通关时间分值','用户重试次数分值']
    return features_df

Features_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.mfbz.cn/a/372793.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HomeAssistant系统添加HACS插件商店与远程控制家中智能家居

文章目录 基本条件一、下载HACS源码二、添加HACS集成三、绑定米家设备 ​ 上文介绍了如何实现群晖Docker部署HomeAssistant&#xff0c;通过内网穿透在户外控制家庭中枢。本文将介绍如何安装HACS插件商店&#xff0c;将米家&#xff0c;果家设备接入 Home Assistant。 基本条件…

国图公考:公务员面试资格复审需要准备什么?

参加国考面试的考生在资格审核阶段需要准备以下材料&#xff1a; 1、本人身份证、学生证或工作证复印件。 2、公共科目笔试准考证复印件。 3、考试报名登记表。 4、本(专)科、研究生各阶段学历、学位证书(应届毕业生没有可以暂时不提供)。 5、报名资料上填写的各类证书材料…

YoloV8改进策略:Neck改进:HAM混合注意力机制改进YoloV8|多种改进,多种姿势涨点|代码注释详解

摘要 HAM通过快速一维卷积来缓解通道注意机制的负担,并引入通道分离技术自适应强调重要特征。HAM作为通用模块,在CIFAR-10、CIFAR-100和STL-10数据集上实现了SOTA级别的分类性能。 论文链接:https://www.sciencedirect.com/science/article/abs/pii/S0031320322002667?vi…

网工内推 | 网络工程师,需持有HCIE,最高16K,午餐补贴

01 科电数据 招聘岗位&#xff1a;网络工程师 职责描述&#xff1a; 1、负责网络及相关安全产品和技术&#xff0c;熟悉相关主流设备&#xff08;路由器、交换机、防火墙、IPS/IDS、WAF、VPN、负载均衡、网闸&#xff09;实施维护、安全加固及优化配置技术&#xff1b; 2、负责…

linux中的gdb调试

gdb是在程序运行的结果与预期不符合时&#xff0c;可以使用gdb进行调试 注意&#xff1a;使用gdb调试时要在编译上加-g参数 gcc -g -c hello.c 启动gdb调试&#xff1a; gdb file 对gdb进行调试 设置运行参数&#xff1a; set args 可指定运行参数 show args 可以查…

Spring5系列学习文章分享---第六篇(框架新功能系列+整合日志+ @Nullable注解 + JUnit5整合)

目录 **Spring5** 框架新功能系列一Spring 5.0 框架自带了通用的日志封装Spring5 **框架核心容器**支持Nullable **注解****Spring5** **核心容器支持函数式风格** GenericApplicationContext**Spring5** **支持整合** JUnit5感谢阅读 开篇: 欢迎再次来到 Spring 5 学习系列&am…

Windows如何安装Node.js环境并制作贪吃蛇小游戏分享好友远程访问

文章目录 前言1.安装Node.js环境2.创建node.js服务3. 访问node.js 服务4.内网穿透4.1 安装配置cpolar内网穿透4.2 创建隧道映射本地端口 5.固定公网地址 前言 Node.js 是能够在服务器端运行 JavaScript 的开放源代码、跨平台运行环境。Node.js 由 OpenJS Foundation&#xff0…

ICC2:polygon命令与复杂power plan

我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧? 拾陆楼知识星球入口 往期文章链接: ICC2&#x

全面解析:船运物流管理系统背后的技术

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

CSS:水平垂直居中

水平垂直居中效果如下&#xff1a; HTML: <div class"parent"><div class"child"></div> </div>公共的 CSS&#xff1a; .parent {width: 300px;height: 300px;background-color:#d0e4fe; }.child {width: 100px;height: 100px…

零基础如何入门渗透测试2024年最新版,保姆级教程,小白必看!

转眼间&#xff0c;从大三开始学安全&#xff0c;到现在也有五年了&#xff0c;也算是对渗透测试有一定理解&#xff0c;今天也是出一篇入门教程&#xff0c;以实操为主&#xff0c;希望可以帮助到想入门渗透测试的小白。如果觉得有用&#xff0c;可以给我点个赞和收藏&#xf…

[C语言] 代码调试, 快捷键说明,assert函数,const函数

一、Debug 和 Release 区别 1.Debug 为调试版本 包含调试信息,不做任何优化,便于程序员调试程序,产生Debug.exe 可以调试,因为文件中包含了调试信息,文件比release大 2.Release 为发布版本 进行了各种优化,是程序在代码大小和运行速度是最优,产生Release.exe 不能调试,可以以…

下载已编译的 OpenCV 包在 Visual Studio 下实现快速配置

自己编译 OpenCV 挺麻烦的&#xff0c;配置需要耗费很长时间&#xff0c;编译也需要很长时间&#xff0c;而且无法保证能全部编译通过。利用 OpenCV 官网提供的已编译的 OpenCV 库可以节省很多时间。下面介绍安装配置方法。 1. OpenCV 官网 地址是&#xff1a;https://opencv…

IT行业证书的获取与价值:提升职业竞争力的关键

目录 IT行业证书的价值和作用 1. Cisco&#xff08;思科&#xff09;认证&#xff08;如CCNA、CCNP、CCIE&#xff09;&#xff1a; 2. 微软认证&#xff08;如MCSA、MCSE、MCSD&#xff09;&#xff1a; 3. 计算机网络技术&#xff08;CompTIA Network、CompTIA Security&a…

二叉树(2)——堆的实现

堆表面是数组&#xff0c;内核是完全二叉树/满二叉树 在插入删除的时候要注意操作过后堆是否还是一个堆&#xff0c;要进行交换等操作。&#xff08;向上调整&#xff09; 逻辑上控制二叉树&#xff0c;物理上控制数组&#xff01;&#xff01;&#xff01; 接下来我们用【小…

小白水平理解面试经典题目LeetCode 21. Merge Two Sorted Lists【Linked List类】

21. 将两个有序列表融合 Linked List 数据结构也在面试中经常出现&#xff0c;作为很好处理客户信息存储的结构很方便&#xff0c;也是重点必会项目之一&#xff0c;看看我们如何教懂白月光&#xff0c;成功邀约看电影吧。 小白渣翻译 你将获得两个排序链表 list1 和 list2 …

Hudi学习 6:Hudi使用

准备工作&#xff1a; 1.安装hdfs https://mp.csdn.net/mp_blog/creation/editor/109689143 2.安装spark spark学习4&#xff1a;spark安装_hzp666的博客-CSDN博客 3.安装Scala Hudi学习6&#xff1a;安装和基本操作_hzp666的博客-CSDN博客 spark-shell 写入和读取hudi 2.…

【Rust】——rust前言与安装rust

&#x1f383;个人专栏&#xff1a; &#x1f42c; 算法设计与分析&#xff1a;算法设计与分析_IT闫的博客-CSDN博客 &#x1f433;Java基础&#xff1a;Java基础_IT闫的博客-CSDN博客 &#x1f40b;c语言&#xff1a;c语言_IT闫的博客-CSDN博客 &#x1f41f;MySQL&#xff1a…

力扣热门100题刷题笔记 - 10. 正则表达式匹配

力扣热门100题 - 10. 正则表达式匹配 题目链接&#xff1a;10. 正则表达式匹配 题目描述&#xff1a; 给你一个字符串 s 和一个字符规律 p&#xff0c;请你来实现一个支持 . 和 * 的正则表达式匹配。 . 匹配任意单个字符 * 匹配零个或多个前面的那一个元素 所谓匹配&#xff…

数据库管理-第144期 深入使用EMCC-01(20240204)

数据库管理144期 2024-02-04 数据库管理-第144期 深入使用EMCC-01&#xff08;20240204&#xff09;1 用户管理2 配置告警动作3 配置意外事件规则总结 数据库管理-第144期 深入使用EMCC-01&#xff08;20240204&#xff09; 作者&#xff1a;胖头鱼的鱼缸&#xff08;尹海文&am…
最新文章