本文主要讲述如何通过 word2vec 和 CNN/RNN 对动作序列建模,在最近的一个比赛中验证了这个思路,的确有一定效果,在二分类的准确率上能达到0.87.本文主要介绍这个方法的具体步骤,并以比赛和代码为例进行说明。
这里提到的比赛是目前正在进行的精品旅行服务成单预测, 该比赛就是要根据用户的个人信息,行为信息和订单信息来预测用户的下一个订单是否是精品服务。本文提到的方法是仅利用用户的行为信息,主要的思路是:将每个动作通过 word2vec 转化为 embedding 表示,然后将动作序列转化为 embedding 序列并作为 CNN/RNN 的输入。 下面依次介绍通过 word2vec 获得动作 embedding,将 embedding 作为CNN的输入和将embedding作为RNN的输入这三部分内容。
word2vec 获取动作 embedding
word2vec 是一个很著名的无监督算法了,这个算法最初在NLP领域提出,可以通过词语间的关系构建词向量,进而通过词向量可获取词语的语义信息,如词语意思相近度等。而将 word2vec 应用到动作序列中,主要是受到了知乎上这个答案的启发。因为 word2vec 能够挖掘序列中各个元素之间的关系信息,这里如果将每个动作看成是一个单词,然后通过 word2vec 得出每个动作的 embedding 表示,那么这些 embedding 之间会存在一定的关联程度,再将动作序列转为 embedding 序列,作为 CNN 或 RNN 的输入便可挖掘整个序列的信息。
这里训练动作 embedding 的方法跟训练 word embedding 的方法一致,将每个户的每个动作看做一个单词、动作序列看做一篇文章即可。训练时采用的是 gensim
, 训练的代码很简单,embedding 的维度设为 300, filter_texts
中每一行是一各用户的行为序列,行为之间用空格隔开。
1 | from gensim.models import word2vec |
由于动作类型只有9种(1~9),也就是共有 9 个不同的单词,因此可将这 9 个动作的 embedding 存在一个 np.ndarray
中,然后作为后面 CNN/RNN 前的 embedding layer 的初始化权重。注意这里还添加了一个动作 0 ,原因是 CNN 的输入要求长度一致,因此对于长度达不到要求长度的序列,需要在前面补 0(补其他的不是已知的动作也可以)。代码如下
1 | import numpy as np |
CNN 对动作序列建模
CNN 采用的模型是经典的 TextCNN, 模型结构如下图所示
这里通过 Keras 实现,具体代码如下
首先需要处理序列,使得所有序列长度一致,这里选择的长度是 50,具体代码如下,代码中的 x_original
是一个 list[list[int]]
类型,表示所有用户的所有动作序列,对于长度比 max_len
长的,从后往前截取50个最近时间的动作,而短的则在前面补0.
1 | from keras.preprocessing import sequence |
然后通过前面得到的 embedding_matrix
初始化 embedding 层
1 | from keras.models import Sequential, Model |
然后建立模型并训练, 这里用了四种不同步长的卷积核,分别是 2、3、5、8,比起原始的 TextCNN, 用了两层的卷积层(在这个任务上经过测试比一层的要好), 后面的全连接层也拓展到了三层,具体代码如下
1 | NUM_EPOCHS = 100 |
由于最后要求的是 auc 指标,但是 Keras 中并没有提供,而 accuracy 与 auc 还是存在一定差距的,因此可以在每个epoch后通过 sklearn 计算auc,具体代码如下
1 | from sklearn import metrics |
这种方法最终的准确率约为 0.86,auc 约为0.84
RNN 对动作序列建模
通过 RNN 进行建模与 CNN 类似,不同的是 RNN 可接受不同长度的输入,但是根据这里的说明,对于输入也需要 padding 的操作,只是RNN 会将其自动忽略。
因此,数据的预处理和构建 embedding 层的代码与 CNN 中基本一致,这里只给出建立模型的代码,模型比较简单,首先是将输入通过 embedding 层的映射后,作为以 LSMT/GRU 为基础单元构建的 RNN 的输入, 最后通过 sigmoid 进行分类,具体代码如下
1 | # RNNs are tricky. Choice of batch size is important, |
通过 RNN 得出的最终效果比 CNN 要好一点,准确率约为 0.87,auc 约为0.85。但是训练起来非常慢,且参数非常的 tricky,需要精调,这里我没有细调参数,模型也没有搞得很复杂,应该还有提升空间。
综上,本文提供了一种对动态序列建模的思路:将动作序列通过 word2vec,得到每个动作的 embedding 表示,然后将动作序列转化为 embedding 序列并作为 CNN/RNN 的输入。希望能够起到抛砖引玉的作用,如果您有更好的想法,欢迎交流。