秋招拿到了推荐相关的office,和未来老板沟通几次后也产生极大的兴趣,感觉以后要放弃Cv啦(快被卷死了~)。由于自己之前没做过推荐相关,害怕入职后会跟不上节奏(听说部门节奏很快。。) 最近正好没事,学习的同时找了个推荐相关的比赛练练手。
这是一个分类任务的比赛,主要要根据用户的个人信息,行为信息和订单信息来预测用户的下一个订单是否符合特定要求。由于最近正好看到了i2i召回相关的内容,于是尝试用了下word2vec 来生成embedding。我使用的方法是仅利用用户的行为信息,主要的思路是:将每个动作通过 word2vec 转化为 embedding 表示,然后将动作序列转化为 embedding 序列并作为 CNN/RNN 的输入。 下面依次介绍通过 word2vec 获得动作 embedding,将 embedding 作为CNN的输入和将embedding作为RNN的输入这三部分内容。哎,还是做什么首先想到用CNN。。
word2vec 获取动作 embedding
word2vec 是一个很著名的无监督算法了,这个算法最初在NLP领域提出,可以通过词语间的关系构建词向量,进而通过词向量可获取词语的语义信息,如词语意思相近度等。而将 word2vec 应用到动作序列中,主要是受到了知乎上这个答案的启发。因为 word2vec 能够挖掘序列中各个元素之间的关系信息,这里如果将每个动作看成是一个单词,然后通过 word2vec 得出每个动作的 embedding 表示,那么这些 embedding 之间会存在一定的关联程度,再将动作序列转为 embedding 序列,作为 CNN 或 RNN 的输入便可挖掘整个序列的信息。
这里训练动作 embedding 的方法跟训练 word embedding 的方法一致,将每个户的每个动作看做一个单词、动作序列看做一篇文章即可。训练时采用的是 gensim
, 训练的代码很简单,embedding 的维度设为 300, filter_texts
中每一行是一各用户的行为序列,行为之间用空格隔开。
1 2 3 4
| from gensim.models import word2vec vector_length = 300 model = word2vec.Word2Vec(filter_texts, size = vector_length, window=2, workers=4)
|
由于动作类型只有9种(1~9),也就是共有 9 个不同的单词,因此可将这 9 个动作的 embedding 存在一个 np.ndarray
中,然后作为后面 CNN/RNN 前的 embedding layer 的初始化权重。注意这里还添加了一个动作 0 ,原因是 CNN 的输入要求长度一致,因此对于长度达不到要求长度的序列,需要在前面补 0(补其他的不是已知的动作也可以)。代码如下
1 2 3 4
| import numpy as np embedding_matrix = np.zeros((10, vector_length)) for i in range(1, 10): embedding_matrix[i] = model.wv[str(i)]
|
CNN 对动作序列建模
CNN 采用的模型就是普通的两层卷积,一开始没想着CNN能有多大提升,搭建方式就看代码吧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| NUM_EPOCHS = 100 BATCH_SIZE = 64 DROP_PORB = (0.5, 0.8) NUM_FILTERS = (64, 32) FILTER_SIZES = (2, 3, 5, 8) HIDDEN_DIMS = 1024 FEATURE_DIMS = 256 ACTIVE_FUNC = 'relu'
sequence_input = Input(shape=(max_len, ), dtype='int32') embedded_seq = embedding_layer(sequence_input)
conv_blocks = [] for size in FILTER_SIZES: conv = Convolution1D(filters=NUM_FILTERS[0], kernel_size=size, padding="valid", activation=ACTIVE_FUNC, strides=1)(embedded_seq) conv = Convolution1D(filters=NUM_FILTERS[1], kernel_size=2, padding="valid", activation=ACTIVE_FUNC, strides=1)(conv) conv = Flatten()(conv) conv_blocks.append(conv)
model_tmp = Concatenate()(conv_blocks) if len(conv_blocks) > 1 else conv_blocks[0] model_tmp = Dropout(DROP_PORB[1])(model_tmp) model_tmp = Dense(HIDDEN_DIMS, activation=ACTIVE_FUNC)(model_tmp) model_tmp = Dropout(DROP_PORB[0])(model_tmp) model_tmp = Dense(FEATURE_DIMS, activation=ACTIVE_FUNC)(model_tmp) model_tmp = Dropout(DROP_PORB[0])(model_tmp) model_output = Dense(1, activation="sigmoid")(model_tmp) model = Model(sequence_input, model_output)
opti = optimizers.SGD(lr = 0.01, momentum=0.8, decay=0.0001)
model.compile(loss='binary_crossentropy', optimizer = opti, metrics=['binary_accuracy'])
model.fit(x_tra, y_tra, batch_size = BATCH_SIZE, validation_data = (x_val, y_val))
|
由于最后要求的是 auc 指标,但是 Keras 中并没有提供,而 accuracy 与 auc 还是存在一定差距的,因此可以在每个epoch后通过 sklearn 计算auc,具体代码如下
1 2 3 4 5 6
| from sklearn import metrics for i in range(NUM_EPOCHS): model.fit(x_tra, y_tra, batch_size = BATCH_SIZE, validation_data = (x_val, y_val)) y_pred = model.predict(x_val) val_auc = metrics.roc_auc_score(y_val, y_pred) print('val_auc:{0:5f}'.format(val_auc))
|
这种方法最终的准确率约为 0.86,auc 约为0.84。个人感觉还是不错了,名次在50左右- -!不过感觉在调下参应该会有一些提升。