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

news2024/12/25 23:42:07

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.coloradmin.cn/o/1434631.html

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

相关文章

Sui上TVL突破5亿美元,位列DeFi榜单前十名和最活跃链前五名

2023年Sui上DeFi协议迅速增长&#xff0c;2024年这一势头仍在继续&#xff0c;根据DeFiLlama报告Sui上TVL近期超过5亿美元。在不到一年的时间里就达到这个金额&#xff0c;得益于Sui的突破性指标&#xff0c;比如其峰值TPS接近6,000。 Sui TVL突破5亿美元&#xff0c;登上DeFi…

使用VASPKIT生成非整数倍扩胞结构

在计算某些任务时需要根据原胞进行非对称或者根号倍扩胞&#xff0c;使用vaspkit可以构建一个扩胞倍数矩阵&#xff0c;这样可以获得特定方向非整数倍的扩胞结构。 这里使用的命令为vaspkit的400模块 注意400的命令会让你连续输入三行&#xff0c;每行都需要三个数值&#xff0…

Android 数据恢复电脑版免费下载使用方法 [2024 更新]

“我一直在寻找一款可以下载的适用于 PC 的优秀 Android 数据恢复软件。有很多&#xff0c;但大部分都需要我付费。你能推荐一个好的让我免费下载吗&#xff1f;” 奇客数据恢复安卓版是恢复已删除或丢失的 Android 数据的最安全的工具。免费下载下面的奇客数据恢复安卓版来尝试…

开始使用 Cortex-M 微控制器:ARM Cortex-M 架构介绍

ARM Cortex-M 架构是一种专为微控制器设计的处理器架构&#xff0c;致力于提供低功耗、高性能和成本效益的解决方案。本文将介绍ARM Cortex-M 架构的特点、核心部件以及常见的应用场景&#xff0c;并提供示例代码来演示使用Cortex-M 微控制器。 ✅作者简介&#xff1a;热爱科研…

js中this对象的理解(深度解析)

文章目录 一、定义二、绑定规则默认绑定隐式绑定new绑定显示修改 三、箭头函数四、优先级隐式绑定 VS 显式绑定new绑定 VS 隐式绑定new绑定 VS 显式绑定 参考文献 一、定义 函数的 this 关键字在 JavaScript 中的表现略有不同&#xff0c;此外&#xff0c;在严格模式和非严格模…

板块零 IDEA编译器基础:第二节 创建JAVA WEB项目与IDEA基本设置 来自【汤米尼克的JAVAEE全套教程专栏】

板块零 IDEA编译器基础&#xff1a;第二节 创建JAVA WEB项目与IDEA基本设置 一、创建JAVA WEB项目&#xff08;1&#xff09;普通项目升级成WEB项目&#xff08;2&#xff09;创建JAVA包 二、IDEA 开荒基本设置&#xff08;1&#xff09;设置字体字号自动缩放 &#xff08;2&am…

如何对视频进行翻译

下载视频和翻译软件 视频和翻译软件点击下载就行了&#xff0c;下载之后解压&#xff0c;然后把两个exe点一下。接下来如果资金充裕或者要求比较高的可以使用各个api&#xff0c;网站里有视频介绍了。 经济适用视频翻译 原理简析 首先这个软件对视频的翻译的流程大致如下&a…

普通编程,机器学习与深度学习

普通编程&#xff1a;基于人手动设置规则&#xff0c;由输入产生输出经典机器学习&#xff1a;人手工指定需要的特征&#xff0c;通过一些数学原理对特征与输出的匹配模式进行学习&#xff0c;也就是更新相应的参数&#xff0c;从而使数学表达式能够更好的根据给定的特征得到准…

(二)hadoop搭建

1. 下载 访问https://hadoop.apache.org/releases.html查看hadoop最新下载地址 wget https://dlcdn.apache.org/hadoop/common/hadoop-3.3.4/hadoop-3.3.4.tar.gz 2.解压 tar zxvf hadoop-3.3.4.tar.gz mv hadoop-3.3.4 /usr/local 3.配置环境变量&#xff08;新建.sh文件&…

IDEA创建SpringBoot+Mybatis-Plus项目

IDEA创建SpringBootMybatis-Plus项目 一、配置Maven apache-maven-3.6.3的下载与安装&#xff08;详细教程&#xff09; 二、创建SpringBoot项目 在菜单栏选择File->new->project->Spring Initializr&#xff0c;然后修改Server URL为start.aliyun.com&#xff0c…

TOP100-二叉数

1.94. 二叉树的中序遍历 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,3,2]示例 2&#xff1a; 输入&#xff1a;root [] 输出&#xff1a;[]示例 3&#xff1a; 输入&#xf…

提升你的PHP开发效率:探索JetBrains PhpStorm 2022的全新特性

在当今快速发展的软件开发领域&#xff0c;选择一个强大且高效的开发工具对于提升开发效率、保证代码质量至关重要。对于PHP开发者来说&#xff0c;JetBrains PhpStorm一直是市场上最受欢迎的IDE之一。随着JetBrains PhpStorm 2022的发布&#xff0c;这款工具带来了一系列创新功…

springboot 集成 nacos (demo 版)

环境要求&#xff1a;本地安装 nacos&#xff08;windows版本&#xff09;&#xff0c;jdk 是 8&#xff0c;安装完还得在 nacos 上面配置一个 yaml 类型的配置文件&#xff0c;方便项目演示读取。 提示&#xff1a;如标题所示&#xff0c;本文只介绍如何从零到一的去集成 naco…

进程状态、排队

进程状态 1. 进程排队2. 进程各个状态3. 查看一个进程的状态4. linux内核描述进程状态5. 孤儿进程 1. 进程排队 进程为什么要排队呢&#xff1f;答案就是资源不够。需要等待某个软硬件资源&#xff0c;就像我们常用的scanf函数就是等待键盘资源。 之前的文章我们有个结论&#…

【SpringBoot】策略和模板模式的思考与实践

一、应用场景 之所以会将策略和模板模式放在一起&#xff0c;是因为这两种模式用的最多最广泛&#xff0c;而且基本都是联合使用的。在开始之前&#xff0c;先复习一下模式的定义&#xff1a; 模板模式&#xff08;Template Pattern&#xff09; 模板模式是在一个抽象类中定…

OpenGL 入门(八)— 标准光照模型

标准光照模型 前言颜色创建一个光照场景 标准光照模型环境光照漫反射光照法向量计算漫反射光照完整源码 高光反射完整源码 Gouraud着色 前言 冯氏光照模型(Phong Lighting Model)&#xff1a; 一个通过计算环境光&#xff0c;漫反射&#xff0c;和镜面光分量的值来估计真实光照…

大模型运行成本对比:GPT-3.5/4 vs. 开源托管

在过去的几个月里&#xff0c;生成式人工智能领域出现了许多令人兴奋的新进展。 ChatGPT 于 2022 年底发布&#xff0c;席卷了人工智能世界。 作为回应&#xff0c;各行业开始研究大型语言模型以及如何将其纳入其业务中。 然而&#xff0c;在医疗保健、金融和法律行业等敏感应用…

MATLAB实现高通滤波(附完整代码)

1.MATLAB实现高通滤波器 以下是一个使用MATLAB实现高通滤波器的例子。在这个例子中&#xff0c;我们将设计一个简单的数字高通滤波器&#xff0c;然后将其应用到一个包含低频和高频成分的信号上。 clc;close all;clear all;warning off;%清除变量 rand(seed, 500); randn(s…

算法学习——LeetCode力扣哈希表篇2

算法学习——LeetCode力扣哈希表篇2 454. 四数相加 II 454. 四数相加 II - 力扣&#xff08;LeetCode&#xff09; 描述 给你四个整数数组 nums1、nums2、nums3 和 nums4 &#xff0c;数组长度都是 n &#xff0c;请你计算有多少个元组 (i, j, k, l) 能满足&#xff1a; 0 …

PiflowX新增Apache Beam引擎支持

参考资料&#xff1a; Apache Beam 架构原理及应用实践-腾讯云开发者社区-腾讯云 (tencent.com) 在之前的文章中有介绍过&#xff0c;PiflowX是支持spark和flink计算引擎&#xff0c;其架构图如下所示&#xff1a; 在piflow高度抽象的流水线组件的支持下&#xff0c;我们可以…