点击左上方蓝字关注我们
【飞桨开发者说】
方案一开发者:唐志雄——目前就职于中国电信股份有限公司武汉分公司,工作方向为数据分析与挖掘及NLP方向的技术落地。
方案二开发者:(不愿透露)是一位低调的“Paddle爱好者”的 DeepFM应用方案。人可以低调,技术决不能低调!
方案三开发者:周兴伟——AI架构师,研究方向侧重底层硬件加速。
飞桨常规赛是面向广大开发者举办的月度刷榜赛,以真实的业务数据、详尽的基线教程和PPDE亲授的解题思路吸引了大量开发者,即便是刚刚入门深度学习的小白开发者也能轻松上手。
飞桨常规赛不仅在学习之余为大家提供了基于真实业务问题的炼丹场景,更为优胜者准备了GPU Tesla V100算力卡和飞桨周边小礼物,超过历史最高分的选手更有小度音响大奖等你来拿!
那么问题来了,想要霸榜到底该怎么魔改基线?常规赛就只能魔改基线了吗?
在2020年10月的“MarTech Challenge点击反欺诈”预测任务中,有三位选手脱颖而出,不仅霸榜而且霸的姿势优雅。一道看似简单的二分类问题,甚至可以用DeepFM和NLP的思想进行解题。
想知道他们是怎么脑洞大开的?快来一探究竟吧!
赛题回顾
广告欺诈是数字营销面临的重要挑战之一,用户点击欺诈不仅会浪费广告主大量金钱,而且会对点击分析产生误导作用。本次比赛的数据集提供了约50万次点击数据,需要参赛者预测用户的点击行为属于正常点击还是欺诈点击。点击欺诈预测适用于各种信息流广告投放,banner广告投放,以及百度网盟平台,可帮助商家鉴别点击欺诈行为,更精准地锁定目标用户。
下面,我们就来看看各位获胜选手如何玩转该赛的吧!
解题方案一
作为该赛题的新晋榜首,Mr唐从样本分析、特征处理、网络设计、参数优化四个方面对赛题进行了完整解读,这才是基线的正确打开方式!
1. 样本分析
对于标准的二分类问题,需要先检查训练集中的正负样本比例是否均衡。本赛题提供了一个正负样本分布平衡的数据集,所以不需要对数据做增强操作。在实际项目中,通常接触的数据集是不平衡的,这就需要在构建batch的时候,采取一些数据增强策略,如:over-sampling、under-sampling或SMOTE等,让每一个batch内的数据实现平衡分布。
接下来,可以根据baseline中的代码进行字典构建,Mr唐采取了训练集与测试集数据合并后构建字典的方式。为防止出现错位现象,第一次构建字典时选取了全部的sparse-feature,通过全面分析字典来筛选有效特征,进而构建训练模型。
import os def make_dict_file(data, save_path, dict_name):
"""
为离散数据生成字典
:param data: 数据列表
:param save_path: 保存路径
:param dict_name: 文件名 :return
字典大小
"""
data_hash = dict()
for sample in data:
sample = str(sample)
if sample not in data_hash:
data_hash[sample] = len(data_hash) + 1
with open(os.path.join(save_path, dict_name) + ".dict", "w", encoding="utf-8") as f:
f.write(str(data_hash))
print(dict_name, "字典生成完毕,共", len(data_hash), "个id")
return len(data_hash)TAGS = {'android_id': None,
'apptype': "emb",
'carrier': "emb",
'dev_height': "emb",
'dev_ppi': "emb",
'dev_width': "emb",
'lan': "emb",
'media_id': "emb",
'ntt': "emb",
'os': None,
'osv': "emb",
'package': "emb",
'sid': None,
'timestamp': "norm",
'version': "emb",
'fea_hash': None,
'location': "emb",
'fea1_hash': "emb",
'cus_type': None} TRAIN_PATH = "work/dataset/train.csv"
TEST_PATH = "work/dataset/test.csv"
SAVE_PATH = "work/emb_dicts"
df_train = pd.read_csv(TRAIN_PATH, index_col=0)
df_test = pd.read_csv(TEST_PATH, index_col=0)
df = pd.concat([df_train, df_test])
pack = dict()
for tag, tag_method in TAGS.items():
if tag_method != "emb":
continue
data = df.loc[:, tag]
dict_size = make_dict_file(data, SAVE_PATH,dict_name=tag)
pack[tag] = dict_size + 1
# +1是为了增加字典中不存在的情况,提供一个默认值
with open(os.path.join(SAVE_PATH, "size.dict"), "w", encoding="utf-8") as f: f.write(str(pack))print("字典生成完毕") 输出:
apptype 字典生成完毕,共 89 个id
carrier 字典生成完毕,共 5 个id
dev_height 字典生成完毕,共 864 个id
dev_ppi 字典生成完毕,共 105 个id
dev_width 字典生成完毕,共 382 个id
lan 字典生成完毕,共 25 个id
media_id 字典生成完毕,共 292 个id
ntt 字典生成完毕,共 8 个id
osv 字典生成完毕,共 165 个id
package 字典生成完毕,共 2102 个id
version 字典生成完毕,共 23 个id
location 字典生成完毕,共 332 个id
fea1_hash 字典生成完毕,共 6147 个id
字典生成完毕
通过字典的大小可知,对于fea_hash这个特征,生成的字典中包含了509473个id,与训练数据的大小相当,显然不适合做Embedding;而对于os字段,通过观察字典内容{'android': 1, 'Android': 2}可知,它包含的都是'android'类型数据,并不包含其他内容,因此也可以舍弃。于是,初步确定选取其余的sparse-feature进行embedding。
2. 特征处理
为什么要做embedding?且听Mr唐慢慢道来。针对sparse-feature,通常使用one-hot的方式来处理,但是one-hot有两个缺点:
维度太大,不便于计算:以package字段为例,通过生成的字典可知其中包含了2102个id,若通过one-hot的方式构建,就有2102个维度,显然不合适;
不同类别间正交,没有关联:以颜色为例,假设一个类别中包含了红,绿,橙三种颜色,那么其one-hot表示分别为[1,0,0],[0,1,0],[0,0,1],每两个类型都互斥。可直觉告诉我们,红色和橙色应该有一定关联,只是one-hot无法体现这种隐含的关联信息。
敲黑板!embedding大显身手的时间到了!
在飞桨零基础实践深度学习的课程中,介绍了词向量的概念(word2vec)。它是一种表示自然语言中单词的方法,即把每个单词表示为N维空间内的一个点,也即高维空间内的一个向量。通过这种方法,把自然语言计算转换为向量计算。如图所示,每个单词(如queen,king等)被转换成高维空间内的向量,这些向量在一定意义上可以代表这个词的语义信息。再通过计算这些向量之间的距离,就可以计算出词语之间的关联关系,从而达到让计算机像计算数值一样去计算自然语言的目的。
那么如何将词转换为词向量呢?自然语言单词是离散信号,比如“我”、“ 爱”、“人工智能”。要想把每个离散单词转换为一个向量,需要维护一个查询表,如图所示。表中每一行都存储了一个特定词语的向量值,每一列的第一个元素都代表这个词本身,便于我们进行词与向量间的映射(如“我”对应的向量值为 [0.3,0.5,0.7,0.9,-0.2,0.03] )。给定任何一个或者一组单词,我们都可以通过查询这个表格,把单词转换为向量,这个查询和替换过程就被称为Embedding Lookup。
上述过程也可以使用一个字典数据结构实现,而在实际场景中,通常使用张量运算的方式去获取单词的向量表示,如图所示。
接下来,我们举个例子。对于一个句子,首先要通过查询字典把句子中的单词(假设是“我”这个词)转换为一个id,接着把id再转换为一个固定长度的向量。假设词典中包含1万个词,“我”对应的id是1,那么这个向量就是[1,0…..0]。同样其他词也这样处理,那么“我爱飞桨”这句话就转换成了一个3*10000的向量。这个向量共有3行、10000列,从上到下,每一行分别代表了“我” “爱” “飞桨”三个单词的 one-hot-encoding。最后我们把这个张量V与一个稠密张量W相乘,其中W张量为10000*128(其中10000为词表的大小,128为词映射的维度)。经过张量乘法,我们就得到了3*128的张量,也就实现了单词到向量表示的转化。其实这个W张量就是我们通过模型学习得到的参数。借用word2vec的概念,我们可以用embedding来处理数据中的sparse特征,这样不但降低了维度,而且可以反映出不同类别的特征间的隐含关系。
重点来了!paddle中embedding使用非常简单,在动态图中直接引入:
然后使用embedding(size)就可以构建一个embedding层。这里的size必须是一个tuple或list,要包含两个元素:第一个元素是vocab_size(词表大小),第二个元素是emb_size(embedding层维度,可以理解为某个类别需要映射为一个多少维的向量)。
3. 网络设计
理解了Embedding的含义后,就可以构建模型了!基于推荐系统中的youtube-net网络结构(如下图),Mr唐对其做了简化。
将输出层替换为一个二分类的softmax,又将watch-vector,search-vector这些关联性的输入直接替换成数据中的sparse-feature,而dense-feature在经过一个fc层后,与embedding后的sparse-feature concat在一起,接着又经过两个fc层,最后经过softmax输出,从而形成最终的网络结构,网络示意图如下:
构建网络时,要注意以下两个问题:
embedding的维度大小如何定义。一般来说,embedding的维度大小是由字段包含信息的多少来决定的。例如对于性别这种类别较少的特征,我们定义的维度就可以小一些,而对于包含很多不同类别的特征,我们可以将其定义的大一些。
出现loss为NAN的问题。这里就要检查是否出现梯度爆炸的问题,如果是网络结构不合理,就要考虑重新网络结构,或者降低学习率,或者做数据归一化等。
就本题而言,对于fea_hash字段,曾考虑直接把这个编码当成dense-feature来处理,通过归一化后直接带入计算,然而效果并不理想,最后仅使用了timestamp这一个字段建模, 网络结构的定义如下:
class Model(dygraph.layers.Layer):
def __init__(self):
super(Model, self).__init__()
# 离散特征做Embedding
self.apptype_emb = Embedding([89 + 1, 32], is_sparse=False)
self.apptype_fc = Linear(32, 32) self.carrier_emb = Embedding([5 + 1, 32], is_sparse=False)
self.carrier_fc = Linear(32, 32) self.dev_height_emb = Embedding([864 + 1, 32], is_sparse=False)
self.dev_height_fc = Linear(32, 32) self.dev_ppi_emb = Embedding([105 + 1, 32], is_sparse=False)
self.dev_ppi_fc = Linear(32, 32) self.dev_width_emb = Embedding([382 + 1, 32], is_sparse=False)
self.dev_width_fc = Linear(32, 32) self.lan_emb = Embedding([25 + 1, 32], is_sparse=False)
self.lan_fc = Linear(32, 32) self.media_id_emb = Embedding([292 + 1, 32], is_sparse=False)
self.media_id_fc = Linear(32, 32) self.ntt_emb = Embedding([8 + 1, 32], is_sparse=False)
self.ntt_fc = Linear(32, 32) self.osv_emb = Embedding([165 + 1, 32], is_sparse=False)
self.osv_fc = Linear(32, 32) self.package_emb = Embedding([2102 + 1, 64], is_sparse=False)
self.package_fc = Linear(64, 64) self.version_emb = Embedding([23 + 1, 16], is_sparse=False)
self.version_fc = Linear(16, 16) self.location_emb = Embedding([332 + 1, 16], is_sparse=False)
self.location_fc = Linear(16, 16) self.fea1_hash_emb = Embedding([6147 + 1, 64], is_sparse=False)
self.fea1_hash_fc = Linear(64, 64) self.dense_fc = Linear(1, 32) self.fc1 = Linear(416 + 128-64 , 800, act='relu')
self.fc2 = Linear(800, 900, act='relu')
self.fc3 = Linear(900, 900, act='relu')
self.fc4 = Linear(900, 2, act='softmax')
4.参数优化
以nnt字段为例,在训练数据集中,我们可以得到当nnt字段为不同值时label=1的比例。在计算出测试结果后,我们同样可以计算出nnt字段为不同值时label=1的比例。本次模型使用Momentum优化器,学习率为0.0001,batch_size为256,epoch为3。在模型结构不变的基础上,进行了部分特征的调整,去掉一些影响因素较小或无意义的特征。对于特征融合,曾尝试过类似句向量的处理方法。结果发现,不管是对特征做sum或求平均,都不如直接做concat。这可能是因为不同类型的特征之间差异度较大,并不能通过简单的计算操作来处理。经过这些操作后,模型准确率由89.08提升至89.22。又经过了20000 step,loss的收敛逐渐放缓。模型最终选择dygraph进行预测,选取了不同训练阶段的三组模型,发现最终的评分差异非常小。
5.方法总结
就如飞桨公开课中老师所讲的那样,自从word2vec被证明行之有效后,embedding就火了起来。几年前大家都在探讨EveryThing2vec,这个赛题算是item2vec的一个小应用。本次赛题只有一张宽表,也不存在user与item的交互关系,因此不用考虑如何使用usr_fea与item_fea的交互。但是实际工作中,无论是不是做推荐系统,我们可能都会遇到这种交互问题。每次针对一个主题进行建模,预测完之后这个模型就被抛弃掉。能否对这种专题性质的模型进行再次利用呢?embedding可以为我们解决这类问题提供一些思路,基于item2vec中usr与item的交互关系构建双塔模型,构建usr和item的向量化表达,进而把它保存下来方便以后复用。
看完了榜首的解题思路,你是不是已经跃跃欲试了?别急,再来瞅瞅这道赛题的创新解法。没有你做不到的,只有你想不到的!
解题方案二
下面是一位低调的“Paddle爱好者”的 DeepFM应用方案。人可以低调,技术决不能低调!
1.离散特征提取
本赛题数据集,除时间戳之外,只有dev_height、dev_width、dev_ppi这三列(即设备的高度、宽度、分辨率)可以当作连续特征,其余特征全都是离散值。进一步地查看离散值的取值个数,发现很多特征的取值空间较大,也就不便于onehot形式处理特征。鉴于此,选取支持离散特征较好的模型提取离散特征。
一般地,我们可以按照以下方法进行特征提取:(1) 连续值分桶,转化成离散值。(2)离散值原样保留。(3) 从时间戳提取出具体特征,然后转为离散特征。
有了以上指导方法,赛题数据整起来!由于赛题给的连续数据全是整数,可以直接把每个数各自分为一个桶,直接转为str离散值即可。对于时间戳,我们提取 day,hour,month,weekday(在一周内的第几天)四个值,各自转为str离散值。通过以上方法,可以得到18个原始特征加4个时间特征,总共22个特征。
2.FM模型介绍
用于二分类的模型相当多,比如逻辑回归,支持向量机,XGBoost等枚不胜举,一般打比赛会常用LightGBM,针对传统的任务,在时间和性能上都比较有优势。
对于本次比赛,适合选择哪种模型呢?本任务数据集维度高而且较稀疏,使用支持向量机、随机森林等分类器会力不从心。首先由于空间维度相当高,数据点在空间内的分布会比较稀疏,导致非线性模型较难学到有效信息,而且高度依赖调参,容易过拟合。其次是时间复杂性相当高,数据的高度稀疏以及超大规模,制约着模型的运行时间。至于简单的lr模型,虽然没有以上两个问题,却由于过于简单而难以学得复杂模式,从而表现欠佳。
鉴于以上原因,我们需要找到一种在高维稀疏数据下表现优异的模型。因子分解机以及进化出来的各种模型,可以较好缓解上述问题。首先,在lr模型的基础上,FM模型通过设置隐变量,可以解决稀疏数据下的特征组合问题。其次,在FM模型的基础上,DeepFM模型利用深度神经网络对更高阶的特征进行组合,增强特征提取能力,从而达到更好的性能。接下来对DeepFM的前世今生进行简单梳理。
对于简单的线性模型,大家应该并不陌生。它有如下形式:
简易代码如下
import numpy as np
import paddle.fluid as fluidwith fluid.dygraph.guard():x = fluid.dygraph.to_variable(np.array([[1, 2, 3]]).astype(np.float32))y_linear = fluid.layers.fc(x, 1)
print(y_linear.numpy())
可以看到,模型仅用到了一阶特征,也就是x1, x2,..., 各自独立作为特征,并没有考虑进去如x1*x2, x1*x3等二阶运算。其实,这些二阶特征能提供更强大的特征表示,使得二阶交互运算对于离散特征尤为有用。
顺理成章的,可以列出如下式子:
其中,
这样一来,使用向量,之积(结果是标量),作为权重,去和, 做乘积的交互运算,便引入了二阶特征。这就是因子分解机(FM)模型,该模型可以在高维的稀疏特征下,做二阶特征组合,因此具有强大的表征能力。
在实际操作中,会将二阶项做一个变换,方便运算,公式如下。
大家是不是看的眼花缭乱?中间过程可以慢慢理解,我们直接使用Paddle代码实现最后的转化形式:
import numpy as np
import paddle.fluid as fluidwith fluid.dygraph.guard():x = fluid.dygraph.to_variable(np.array([[1, 2, 3]]).astype(np.float32))v_matrix = fluid.dygraph.to_variable(np.array([[0.1, 0.2], [0.3, 0.4], [0.5, 0.6]]).astype(np.float32))u = fluid.layers.t(x) * v_matrixsum_then_square = fluid.layers.square(fluid.layers.reduce_sum(u))square_then_sum = fluid.layers.reduce_sum(fluid.layers.square(u))two_order_part = 0.5*fluid.layers.reduce_sum(sum_then_square - square_then_sum, keep_dim=True)
print(two_order_part.numpy())
目前深度神经网络(DNN)比较流行,可以提取隐含的高阶特征。简单的DNN模型,就是若干个线性层和若干个激活层,像夹心饼干一样叠在一起。公式不再赘述,可以结合简易的Paddle代码理解如下。
import numpy as np
import paddle.fluid as fluidwith fluid.dygraph.guard():x = fluid.dygraph.to_variable(np.array([[1, 2, 3]]).astype(np.float32))a = fluid.layers.relu(fluid.layers.fc(x, 3))a = fluid.layers.relu(fluid.layers.fc(a, 3))a = fluid.layers.relu(fluid.layers.fc(a, 1))y_dnn = fluid.layers.sigmoid(a)
print(y_dnn.numpy())
上面讲了这三种模型,那把他们三者融为一体,是不是性能就会提高呢?这就是接下来介绍的DeepFM模型。DeepFM组合了FM与DNN,结合了广度和深度模型的优点,可以同时学习低阶特征组合和高阶特征组合。公式简单如下。
继续结合简易代码:
import numpy as np
import paddle.fluid as fluidwith fluid.dygraph.guard():y = fluid.layers.sigmoid(y_linear + two_order_part + y_dnn)
print(y.numpy())
3.MF方法总结
值得注意的是,对于极为稀疏的离散特征,使用embedding表,只查询非零部分的特征,就会有效地降低复杂性,同时可以设置离散特征对应向量的维度,来增强模型的表征能力。因此,对原来提到的形式做如下变换,读者在实现自己代码的时候需要注意。
(1) 对于线性部分而言,通过特征查1维的 embedding表,得到1个数值,可以认为是 w 向量。没有通过查表得到的位置,均默认为0,对求和而言是计算不到的。
(2) 对于FM的二阶项而言,通过查k维的 embedding 表,得到隐藏向量v。
(3) 对于DNN而言,通过查n维 embedding 表,得到若干维的连续值特征,增强表示能力。
在DeepFM中,会将FM和DNN的embedding矩阵权值进行共享,这样(2)(3)里面的k就等于n。
在实现方面,笔者运用了以下参数。配置比较中规中矩,并没有调参。切分训练集测试集的比例为9:1。模型所有的维度包括embedding和隐藏层,均为10,DNN为3层。优化器选用Adam,学习率0.001,l2正则为0.001。训练时,batch size为32,epoch数目为10。数据采用异步读取方式,在aistudio的GPU平台上,一个epoch的耗时约为一分半左右,整个流程比较快。一般在第3个epoch时,验证集达到分数最大值,往后就会过拟合。最后在预测的时候,选择0.5作为阈值。
大佬果然值得摩拜!接下来,还有NLP解法。是的,你没有听错!
解题方案三
1.灵感来源
NLP(自然语言处理)是人工智能领域中一个重要的研究方向,包含情感分析与文本分类等重要细分场景。语言可以认为是带有稀疏属性且有特定意义的序列组合,那么反过来,是否可以将带有稀疏属性且有特定意义的序列看作某种语言,从而利用NLP技术处理这类数据?本文就该思路进行简单探索与实践。
2.框架选择
这里所有的验证实践都是基于百度AI Stuido实训平台。使用的主要开发环境是PaddlePaddle提供的PALM。PALM (PArallel Learning from Multi-tasks) 是一个灵活,通用且易用的NLP大规模预训练和多任务学习框架,可以实现快速开发高性能NLP模型,其主要应用之一就是观点匹配或文本分类。PALM还提供了丰富的模型可供选择:
=> RoBERTa-zh-base
=> RoBERTa-zh-large
=> ERNIE-v2-en-base
=> ERNIE-v2-en-large
=> XLNet-cased-base
=> XLNet-cased-large
=> ERNIE-v1-zh-base
=> ERNIE-v1-zh-base-max-len-512
=> BERT-en-uncased-large-whole-word-masking
=> BERT-en-cased-large-whole-word-masking
=> BERT-en-uncased-base
=> BERT-en-uncased-large
=> BERT-en-cased-base
=> BERT-en-cased-large
=> BERT-multilingual-uncased-base
=> BERT-multilingual-cased-base
=> BERT-zh-base
基于这些模型可以进行各种快速而有效的想法验证。之所以考虑使用PALM也是基于这些考虑,可以节省前期模型搭建的时间,入门快且适合初学者,作为思路验证,PALM是一个足够友好且易用的环境。当然,如果从头设计模型并细调的话也是可以的,并且效果也会非常理想。
PALM repo路径:
https://github.com/PaddlePaddle/PALM
3.模型选择
点击反欺诈预测是一个分类问题,更详细说,是一个多字段或多属性的分类问题,这里有两个基本特点:
输入为多字段信息,且这些信息高度稀疏;
输出为分类结果,分类的种类很少,近似情感积极分类。
以上特点,是验证本文思路的绝佳环境。就类比来说,每个属性或字段类似NLP中的词,属性之间类似词之间是有依赖关系的,相似属性可以放在一起。
就NLP来说,需要将输入的特定字符embedding到稠密向量空间。由于本文只是验证思想,处理方式简单粗暴,将所有属性输入全部转为string,可以考虑剔除影响稀疏性质的属性(不剔除可以考虑稀疏化),直接作为输入进行ernie训练与预测。
就模型而言,PALM里面有很多模型,英文模型里面可以选择最基本的ERNIE-v2-en-base。这里模型的选择差异不大,经验证,使用PALM的其他模型效果也不错。使用ERNIE-v2-en-base仅仅依据多属性中无中文信息,且ERNIE-v2模型较新的直观原因。
具体到应用,本赛题可以看作是一个文本分类任务,这里有两个细化场景可供选择,输入为text_a,输出为label,类似文本情感积极分析;输入为text_a与text_b,输出为label,类似问答分析。考虑到本文输入包含数字与英文,可以套用第二种场景,将数字放于text_a字段,英文作为answer放于text_b字段,而分类结果即为label。text_b字段之间可以用分隔符隔开。训练数据与预测数据简单处理后可以用于后续的训练与实验。
安装palm:
!pip install paddlepalm -i https://pypi.tuna.tsinghua.edu.cn/simple
也可以从源码安装:
git clone https://github.com/PaddlePaddle/PALM.git
cd PALM && python setup.py install
查看支持模型:
from paddlepalm import downloader
downloader.ls('pretrain')
下载指定模型:
downloader.download('pretrain', 'ERNIE-v2-en-base', './pretrain_models')
处理数据,参照上述,灵活性很大,可以参照反欺诈其他文章的数据处理与过滤方式,最终只要转为string格式就可以了,实验效果都非常理想。
本文训练的完整代码:
max_seqlen = 128
batch_size = 16
num_epochs = 5
lr = 1e-6
weight_decay = 0.0001
num_classes = 2
random_seed = 1
dropout_prob = 0.002
save_path = './outputs/'
save_type = 'ckpt'
pred_model_path = './outputs/training_pred_model'
print_steps = 500
pred_output = './outputs/predict/'
pre_params = '/home/aistudio/pretrain_models/pretrain/ERNIE-v2-en-base/params'
task_name = 'Quora Question Pairs matching'
vocab_path = '~/pretrain/ERNIE-v2-en-base/vocab.txt'
train_file = '/home/aistudio/train.csv'
predict_file = '/home/aistudio/test.csv'
config = json.load(open('/home/aistudio/pretrain_models/pretrain/ERNIE-v2-en-base/ernie_config.json'))
input_dim = config['hidden_size']match_reader = palm.reader.MatchReader(vocab_path, max_seqlen, seed=random_seed)
# step 1-2: load the training data
match_reader.load_data(train_file, file_format='tsv', num_epochs=num_epochs, batch_size=batch_size)
# step 2: create a backbone of the model to extract text features
ernie = palm.backbone.ERNIE.from_config(config)
# step 3: register the backbone in reader
match_reader.register_with(ernie)
# step 4: create the task output head
match_head = palm.head.Match(num_classes, input_dim, dropout_prob)
# step 5-1: create a task trainer
trainer = palm.Trainer(task_name)
# step 5-2: build forward graph with backbone and task head
loss_var = trainer.build_forward(ernie, match_head)
# step 6-1*: use warmup
n_steps = match_reader.num_examples * num_epochs // batch_size
warmup_steps = int(0.1 * n_steps)
sched = palm.lr_sched.TriangularSchedualer(warmup_steps, n_steps)
# step 6-2: create a optimizer
adam = palm.optimizer.Adam(loss_var, lr, sched)
# step 6-3: build backward
trainer.build_backward(optimizer=adam, weight_decay=weight_decay)
# step 7: fit prepared reader and data
trainer.fit_reader(match_reader)
# step 8-1*: load pretrained parameters
trainer.load_pretrain(pre_params, False)
# step 8-2*: set saver to save model
save_steps = 15000
trainer.set_saver(save_path=save_path, save_steps=save_steps, save_type=save_type)
# step 8-3: start training
trainer.train(print_steps=print_steps)
预测部分代码,假设训练保存模型为./outputs/training_pred_model:
print('prepare to predict...')predict_match_reader = palm.reader.MatchReader(vocab_path, max_seqlen, seed=random_seed, phase='predict')
# step 1-2: load the training data
predict_match_reader.load_data(predict_file, batch_size)
# step 2: create a backbone of the model to extract text features
pred_ernie = palm.backbone.ERNIE.from_config(config, phase='predict')
# step 3: register the backbone in reader
predict_match_reader.register_with(pred_ernie)
# step 4: create the task output head
match_pred_head = palm.head.Match(num_classes, input_dim, phase='predict')
predicter=palm.Trainer("for predict")
# step 5: build forward graph with backbone and task head
predicter.build_predict_forward(pred_ernie, match_pred_head)# step 6: load pretrained model
pred_ckpt = predicter.load_ckpt(pred_model_path)
# step 7: fit prepared reader and data
predicter.fit_reader(predict_match_reader, phase='predict')# step 8: predict
print('predicting..')
predicter.predict(print_steps=print_steps, output_dir=pred_output)
里面具体的函数使用与定义可以参照PALM文档,其中介绍比较详细并有对应源码实现。
训练好的模型可以进行预测,先load_pretrain模型然后predict就可以了,具体参照PALM源码实现。需要注意的是,最后的输出为:
Predictions saved at ./outputs/predict/predictions.json
需要将其中的label对齐到submission输出,之后就可以提交验证了。
5.方法总结
虽然本文的预处理简单粗暴,但效果还是可以接受的。在点击反欺诈预测中,经过简单的调试分数就可以达到89以上。由于本文只是验证思路,没有花费太多时间在预处理与finetuning上,这个结果应该还有很大的提升空间。不同的属性排列组合对训练结果有一定的影响,如何以特征依赖来对属性进行更好的排序还需要进一步实验与研究。
飞桨常规赛致力于基于真实场景提供轻量级赛题,以赛促学,和开发者共成长。欢迎更多炼丹师艺术创想,使用创新方法解题。本次常规赛的三位魔改大师因为思路新颖,飞桨常规赛组委会特别送上了丰厚的周边礼包。首届榜首的Mr唐更是赢取了特别礼包:小度在家。
错过的小伙伴不要灰心,12月来啦!除了Martech系列赛题,飞桨常规赛全新开放“论文引用网络节点分类”和“中文场景文字识别”,对图学习和OCR感兴趣的小伙伴们千万不要错过啦!
比赛报名
常规赛:论文引用网络节点分类
https://aistudio.baidu.com/aistudio/competition/detail/59
常规赛:中文场景文字识别
https://aistudio.baidu.com/aistudio/competition/detail/20
常规赛:MarTech Challenge 用户购买预测
https://aistudio.baidu.com/aistudio/competition/detail/51
常规赛:MarTech Challenge 点击反欺诈预测
https://aistudio.baidu.com/aistudio/competition/detail/52
如在使用过程中有问题,可加入飞桨官方QQ群进行交流:1108045677。
飞桨(PaddlePaddle)以百度多年的深度学习技术研究和业务应用为基础,是中国首个开源开放、技术领先、功能完备的产业级深度学习平台,包括飞桨开源平台和飞桨企业版。飞桨开源平台包含核心框架、基础模型库、端到端开发套件与工具组件,持续开源核心能力,为产业、学术、科研创新提供基础底座。飞桨企业版基于飞桨开源平台,针对企业级需求增强了相应特性,包含零门槛AI开发平台EasyDL和全功能AI开发平台BML。EasyDL主要面向中小企业,提供零门槛、预置丰富网络和模型、便捷高效的开发平台;BML是为大型企业提供的功能全面、可灵活定制和被深度集成的开发平台。
扫描二维码 | 关注我们
微信号 : PaddleOpenSource
END
精彩活动