最终成绩:优胜奖(TOP 4)
比赛地址:https://gslab.qq.com/html/competition/2020/index.htm
源码:https://github.com/LogicJake/2020-gslab-ml-top4

赛题背景

在游戏中,有一些人或组织,通过非法手段获取大量游戏内金币物品等资源,这就是打金工作室(Gold Farming)。他们一般拥有大量帐号,使用外挂或挂机软件批量进行游戏行为,破坏游戏经济系统,影响玩家游戏体验。可自行搜索打金工作室相关资料,了解他们是如何运作,游戏方如何打击,他们又是如何逃避打击的。

竞赛目标

使用 2020年03月01日的数据(含标签) 来训练识别打金工作室的模型。用此模型, 预测2020年03月05日的打金工作室。取当日login或logout中出现过的帐号,判断这些帐号中哪些是打金工作室。

衡量标准

得分 = 4PR / (P+3R)

P为准确度, 即提交结果有多大比率是真正的工作室
R为覆盖率, 即提交结果覆盖了全体工作室的比率

数据说明

数据来自某MMORPG(大型多人在线角色扮演游戏), 并经过脱敏处理

  • 日期
    • 2020年03月01日的数据(含标签) 为训练集
    • 2020年03月05日的数据为测试集
  • 基础知识
    • uin: 唯一标识游戏内的一个用户, 比如你的qq或微信
    • roleid: 一个uin可能有多个角色
  • 存储格式
    • 文件名为: 年月日.txt
    • 以文本存储
    • 以竖线|分隔
    • 空 或 \N 表示数据缺失
  • 目录说明
    • label_black 黑标签: 打金工作室帐号
    • label_white 白标签: 非打金工作室帐号
    • role_login 角色登入游戏
    • role_logout 角色登出游戏
    • role_create 创建新角色
    • uin_chat 按天统计的帐号发言次数
    • 以下数据仅在决赛时提供
      • role_moneyflow 角色的详细金钱流水信息(当天按时间顺序前300条记录)
      • role_itemflow 角色的详细物品流水信息(当天按时间顺序前300条记录)

role_login 角色登入游戏

# 列名 类型 备注
1 dteventtime STRING 时间,格式YYYY-MM-DD HH:MM:SS
2 platid BIGINT ios=0/android=1
3 areaid BIGINT 微信=1/手Q=2/游客=3
4 worldid BIGINT 游戏小区(已加密)
5 uin STRING openid(已加密)
6 roleid STRING 角色id(已加密)
7 rolename STRING 角色名(已置空)
8 job STRING 职业
9 rolelevel BIGINT 等级
10 power BIGINT 战力
11 friendsnum BIGINT 好友数量
12 network STRING 3G/WIFI/2G/NULL
13 clientip STRING 客户端IP(已加密)
14 deviceid STRING 设备ID(已加密)

role_logout 角色登出游戏

# 列名 类型 备注
1 dteventtime STRING 时间,格式YYYY-MM-DD HH:MM:SS
2 platid BIGINT ios=0/android=1
3 areaid BIGINT 微信=1/手Q=2/游客=3
4 worldid BIGINT 游戏小区(已加密)
5 uin STRING openid(已加密)
6 roleid STRING 角色id(已加密)
7 rolename STRING 角色名(已置空)
8 job STRING 职业
9 rolelevel BIGINT 等级
10 power BIGINT 战力
11 friendsnum BIGINT 好友数量
12 network STRING 3G/WIFI/2G/NULL
13 clientip STRING 客户端IP(已加密)
14 deviceid STRING 设备ID(已加密)
15 onlinetime BIGINT 在线时长(秒)

role_create 创建新角色

# 列名 类型 备注
1 dteventtime STRING YYYY-MM-DD HH#MM#SS
2 platid BIGINT ios=0/android=1
3 areaid BIGINT 微信=1/手Q=2/游客=3
4 worldid BIGINT 游戏小区(已加密)
5 uin STRING openid(已加密)
6 roleid STRING 角色id(已加密)
7 rolename STRING 角色名(已置空)
8 job STRING 职业
9 regchannel STRING 注册渠道
10 network STRING 3G/WIFI/2G
11 clientip STRING 客户端IP(已加密)
12 deviceid STRING 设备ID(已加密)

uin_chat 按天统计的帐号发言次数

# 列名 类型 备注
1 uin STRING openid(已加密)
2 chat_cnt BIGINT 发言条数

role_moneyflow 帐号的详细金钱流水信息

# 列名 类型 备注
1 dteventtime STRING 时间,格式YYYY-MM-DD HH:MM:SS
2 worldid BIGINT 游戏小区(已加密)
3 uin STRING openid(已加密)
4 roleid STRING 角色id(已加密)
5 rolelevel BIGINT 等级
6 iMoneyType STRING 货币类型
7 iMoney BIGINT 货币变化数
8 AfterMoney BIGINT 动作后的货币存量
9 AddOrReduce BIGINT 货币增加 0/减少 1
10 Reason STRING 货币流动一级原因
11 SubReason STRING 货币流动二级原因

role_itemflow 帐号的详细物品流水信息

# 列名 类型 备注
1 dteventtime STRING 时间,格式YYYY-MM-DD HH:MM:SS
2 worldid BIGINT 游戏小区(已加密)
3 uin STRING openid(已加密)
4 roleid STRING 角色id(已加密)
5 rolelevel BIGINT 等级
6 Itemtype STRING 道具类型
7 Itemid STRING 道具ID
8 Count BIGINT 道具变动数量
9 Aftercount BIGINT 动作后道具剩余数量
10 Addorreduce BIGINT 增加 0/减少 1
11 Reason STRING 道具流动一级原因
12 SubReason STRING 道具流动二级原因

简单 eda

训练集总账户数为74704,其中打金工作室账户10202,正常用户64502,正负样本分布不均匀。登入数据的离散数据统计信息如图1所示。

图1 登入数据的离散数据统计
图1 登入数据的离散数据统计

登出数据字段和登入数据差不多,仅多了在线时长(onlinetime)特征,所以在后续的处理中,将二者合并成 operation 表放在一起使用,使得代码更简洁。rolename 全为空没有使用价值。从业务上讲,deviceid 是一个很重要的特征,根据设备id我们可以判断多账号共享设备的情况。但在数据集中,每天仅有一个deviceid,3.1号的 deviceid 全为6259A4950D8B0CA5,3.5号的 deviceid 全为71A1315F1949F262,失去了使用价值。

数据集给出了按天统计的帐号发言次数,整体分布如图2所示,具有很明显的长尾分布。分是否是工作室观察发言次数分布,如图3和图4所示,可以明显看出工作室的发言次数相较于正常用户较多,推测是在游戏中进行叫卖或者交易聊天。

图2 整体帐号发言次数
图2 整体帐号发言次数
图3 工作室帐号发言次数
图3 工作室帐号发言次数
图4 正常用户帐号发言次数
图4 正常用户帐号发言次数

用户创建角色数据,主要需要关注regchannel(注册渠道),总共有76种不同的注册渠道,主要渠道数量分布如图5所示。

图5 主要注册渠道数量分布
图5 主要注册渠道数量分布

特征工程

数据集给出了操作时间,可以提取出小时信息进行进一步统计。ip 特征能够很好的反映是否存在同一 ip 下的多账户行为。ip 特征比较细化,根据基本的网络知识,我们可以将其分段,得到隐藏的地域信息。所以在本方案中,构造了clientip_3 和 clientip_2,分别取 clientip 的前三段和前两段。logout 表给出了在线时长特征,类似的,我们可以将 login 表按时间排序,登入时间减去上一次的登出时间可以得到离线时长特征。

统计特征

统计特征包括两方面,以uin为单位,分别采用不同的方法对数值特征和类别特征进行统计。对数值特征采取下列统计方法:

  • 在线时长:sum,median,mean;
  • 离线时长:mean,max
  • 角色等级:mean,max;
  • 战力:mean,max;
  • 好友数量:mean,max;
  • 用户操作时间(小时):mean,min,max。

对下列类别特征进行count统计:platid, worldid, roleid, job, network, clientip, clientip_3, hour, regchannel。

ip 特征

查资料可知,工作室往往会购买大量机器,利用自动化脚本进行打金,所以会出现多个账号共享 ip 的情况。我们计算每个 ip 下登录过多少个账户,再对某账户使用过的所有 ip 计算其登录过账户数的平均值,该值越大,说明该账户所处网段有大量账户登录,越有可能是工作室。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
for f in ['clientip', 'clientip_3']:
df_temp = operation[['uin', f]]
df_temp.drop_duplicates(inplace=True)
df_temp = df_temp.groupby([f])['uin'].nunique().reset_index()
df_temp.columns = [f, 'uin_count']

df_temp2 = operation[['uin', f]]
df_temp2.drop_duplicates(inplace=True)
df_temp = df_temp2.merge(df_temp, how='left')

df_temp = df_temp.groupby(['uin'])['uin_count'].agg({
'{}_uin_count_mean'.format(f):
'mean'
}).reset_index()
df_feature = df_feature.merge(df_temp, how='left')
del df_temp, df_temp2
gc.collect()

标签五折交叉统计特征

在推荐任务中,经常使用点击率特征,即统计某个类别特征下点击商品的概率。同样作为二分类任务,我们也可以构造某个类别特征下是工作室的概率。该类特征利用了标签信息,容易出现标签泄露问题,所以在实际操作中,将训练集分为5份,每次使用4份做标签统计,得到的概率值给另外1份做特征。本方案构造了 chat_cnt 和 clientip_3 类别下的工作室概率。

embedding 特征

ip 字段能帮助我们很好的判断是否存在账户聚集行为,假如我们能够将无法定量计算的 ip 地址转成模型能够识别的数字特征,将会帮助模型更好地利用 ip 信息。我们整理出每个账户在哪些 ip 操作过,将这些 ip 列表作为 sentence 输入到 word2vec 模型,将 ip 映射为向量,经常出现在上下文中的 ip 在 embedding 空间中也相近。最后将账户下所有 ip 的向量取平均作为新特征。效仿对 ip 特征的处理,我们还对 worldid 和 job 做了类似的操作。

流水特征

决赛给出了角色的详细货币和物品流水信息(当天按时间顺序前300条记录)。结合业务考虑,工作室往往会进行大量的交易行为,实现物品或货币套利。所以对货币变化数,道具变动数量进行统计,包括求和,求平均值,求最大值和计数。货币变化可以详细到增加或减少,可以进一步在这两种情况下进行统计。此外数据集还给出了交易的理由,我们还可以统计每个理由下进行交易的货币数和物品数。货币类型,货币流动一级原因,道具流动一级原因,尤其是道具ID,这几个类别特征维度较大,分别进行统计会引入大量特征,而且数据普遍存在稀疏问题。以道具ID为例,ID 为342的物品只交易过一次,所以“物品342的交易数量”特征就会十分稀疏,只有一个账户下该特征有统计值。

最终,本方案总共使用704个特征,重要性比较高的特征如图6所示,可以看出账户的在线时长,离线时长特征很重要,因为工作室相较于正常用户会花费更多的时间进行游戏,追求利益的最大化。时段(hour)特征也比较重要,工作室不分白天黑夜进行游戏,而正常的用户不会在白天花费大量的时间进行游戏。加入流水特征后,Reason12下的货币交易尤为重要。

图6 特征重要性
图6 特征重要性

伪标签

在训练模型时,我们总希望输入的数据数据量尽可能大,数据标签尽可能分布均匀。在对测试集进行预测时,LightGBM 模型输出的是账户是打金工作室的概率,也可以理解为置信度,从图7可以看出,概率分布大多分布在两头,极有可能是和极有可能不是。我们完全可以将置信度较高的测试集数据作为标签为1的新训练集,从而达到扩增训练集且稍微平衡样本标签的目的。由于本次比赛没有榜单,所以这部分选择比较谨慎,只挑出概率值大于0.99的测试集数据添加到训练集,线下大概能提升0.006。

图7 概率值分布
图7 概率值分布

模型

利用 LightGBM 模型进行二分类任务,工作室账号标签设置为1,正常账号设置为0。采用五折交叉法,将3.1号的数据分为五份,每次使用4份训练,另外一份用来验证。由于样本标签分布不均衡,我们不能用0.5作为正负样本的分割点,而应该采用比例法。正样本大概占总样本的13.66%,所以将测试集的预测概率按从大到小排序,前13.66%挑选为工作室账号。在本地验证集上,精确率达到0.9700214132762313,召回率达到0.9324642227014311,比赛指标达到0.9603513111071851。

总结

由于是高校赛,主办方大概考虑到学生党的机器性能限制,所以数据集并不是很大,比赛时间也比较紧凑,初赛12个小时,决赛三天,整体体验比较好。赛后阅读第一大佬的方案,特征工程思路基本相同,但大佬在流水统计方面做的更细致,并没有像我直接一股脑的对所有分类进行统计。此外还利用对抗训练进行特征筛选,删去在训练集和测试集上分布差异较大的特征。

通过role_itemflow,构造了主要物品类型(类型20025, 20035, 20036, 20028)的流水次数和物品变动数量,主要流水原因(原因84, 12, 85, 5)的流水次数。

参考资料