之前讲过用商品协同过滤的方法来进行召回(https://blog.csdn.net/weixin_41332009/article/details/113550268),这里衡量商品的相似度的方法是通过购买两个商品的人群的相似度来衡量的。还有一种更直观的方法,就是直接根据商品的内容来判断两个商品的相似度。例如可以通过商品的名称、商品的固有属性(长宽高)来计算两个商品的相似度。这篇文章尝试了三种不同的词向量化方法,以及提出了一种自动化构建下游任务的方法,来评估词向量方法的优劣。
1. 词向量化方法
1.1 GloVe 预训练模型
GloVe预训练模型提供了400000个英文词语的词向量,在这里直接拿它们来表示不同的词语。
wv_from_bin=pickle.load(open("GloVe_50.pkl",'rb')) ###GLOVE
embedding = wv_from_bin.word_vec(word)
1.2 word2vec从零开始训练
sentences = list(word2vec.LineSentence('sku_names.txt'))
model = Word2Vec(size=50,min_count=1)
model.build_vocab(sentences)
model.train(sentences, total_examples=model.corpus_count, epochs=100)
model.save('word2vec.model')
1.3 GloVe预训练+skip-gram微调
使用数据库中的商品名称作为训练集,训练集格式如下:
就是一行一个商品名称。先用GloVe把那些GloVe词典中有的词语直接使用预训练的向量表示,那些没有的就随机初始化。之后用skip-gram算法对于每一条数据都进行梯度下降,迭代若干个epoch之后得到最终的词向量表示。(skip-gram:https://blog.csdn.net/weixin_41332009/article/details/112133265)
2. 构建下游任务的数据集
2.1 下游任务
上面这三种方法看上去都不错,那么如何评价这三种词向量化方法孰优孰劣呢?我采用了文本十分类问题做词向量的下游任务,就是用商品名称来预测它的三级品类。这种方法非常直观,可解释性强,而且的确能够把不同的词向量化方法拉开区分度,是一个合理的下游任务。
商品宽表adm_s03_glb_sku_profile_da中包含了商品的所有属性,选取商品英文名称、商品三级品类构成训练集。但是,数据库中总共有2000多个三级品类,要选择哪几个三级品类作为训练集呢?
考虑到有些商品可能会属于不同品类,虽然在数据库中商品的三级品类是唯一的,但是这个三级品类分类很可能是有歧义的。比如“车钥匙”可能属于“汽车配件类”,也可能属于“时尚”类。我们想要尽可能的减小这个歧义。一个显而易见的方法是直接通过肉眼观察,选择不太可能会有歧义的十个三级品类,比如“母婴类”和“电脑类”实在是想不出来有什么可以产生歧义的地方。但是这种肉眼观察的方法实在是太没有可信度了啊....而且也没办法推广应用。所以,要使用一种能自动构建数据集的方法。
2.2 自动构建数据集
首先,要考虑如何衡量两个三级品类的相似度。我使用的方法是,将三级品类A中所有的商品名称的token构成一个集合Set_A,将三级品类B中所有的商品名称的token构成一个集合Set_B, 然后计算Set_A和Set_B之间的Jaccard相似度,直接作为三级品类A和三级品类B的相似度(这样做的intuition就是,如果这两个品类名下的商品名称有很多重合的token,那么它们是相似的)。数据库中,名下超过3000个不同商品名称的三级品类总共有160个,那么三级品类的相似矩阵就是一个160*160的矩阵。
现在,问题变成了:如何从这160个品类中挑选出10个品类,使得这十个品类的两两相似度最低?
但是,这个问题是NP问题。如果真的要求两两相似度最低的十个品类比较复杂,所以我采用了贪心的算法。即,先选择相似度最低的两个品类A和B,作为cate_list的初始值,现在cate_list就是 [A,B];之后每次都选择那个到cate_list中每个品类相似度最大值最小的那个品类,加到cate_list中。例如,现在cate_list中有[A,B,C,D,E,F],就需要遍历其他的所有品类,计算similarity = max{sim(,A), sim(,B) ... sim(,F)},遍历所有品类之后,选择similarity最小的那个。
最终,选择了10个三级品类,为:
Fashion@@Women's Footwear@@Women's Slip Ons & Espadrilles,Computers & Office@@Office Equipment@@Other Office Equipment,.......Fashion@@Women's Jewelry@@Women's Bracelets, Computers & Office@@Computers & Laptops@@Laptops
这十个三级品类分别抽取3000条商品名称,总共30000条数据作为训练集。
3. 测试模型
这其实就是一个十分类问题,我采用双层 Bi - LSTM 作为模型,使用交叉熵损失函数。
class NeuralNet(nn.Module):
def __init__(self, embedding_matrix, num_aux_targets):
super(NeuralNet, self).__init__()
embed_size = embedding_matrix.shape[1]
self.embedding = nn.Embedding(max_features, embed_size)
self.embedding.weight = nn.Parameter(torch.tensor(embedding_matrix, dtype=torch.float32))
self.embedding.weight.requires_grad = False
self.embedding_dropout = SpatialDropout(0.3)
self.lstm1 = nn.LSTM(embed_size, LSTM_UNITS, bidirectional=True, batch_first=True)
self.lstm2 = nn.LSTM(LSTM_UNITS * 2, LSTM_UNITS, bidirectional=True, batch_first=True)
self.linear1 = nn.Linear(DENSE_HIDDEN_UNITS, DENSE_HIDDEN_UNITS)
self.linear2 = nn.Linear(DENSE_HIDDEN_UNITS, DENSE_HIDDEN_UNITS)
self.linear_aux_out = nn.Linear(DENSE_HIDDEN_UNITS, num_aux_targets)
def forward(self, x):
h_embedding = self.embedding(x) ###(batchsize, max_len, embedding_size) = (32,40,50)
h_embedding = self.embedding_dropout(h_embedding) #####(batchsize, max_len, embedding_size) = (32,40,50)
h_lstm1, _ = self.lstm1(h_embedding) ##(batchsize, max_len, hidden_size * 2) = (32,40,256), 因为是双向
h_lstm2, _ = self.lstm2(h_lstm1) ##(batchsize, max_len, hidden_size * 2) = (32,40,256),
print(h_lstm2.shape)
# global average pooling
avg_pool = torch.mean(h_lstm2, 1) ###(32, 256)
# global max pooling
max_pool, _ = torch.max(h_lstm2, 1) ##(32,256)
h_conc = torch.cat((max_pool, avg_pool), 1)
h_conc_linear1 = F.relu(self.linear1(h_conc))
h_conc_linear2 = F.relu(self.linear2(h_conc))
hidden = h_conc + h_conc_linear1 + h_conc_linear2
aux_result = self.linear_aux_out(hidden)
softmax_func=nn.Softmax()
out=softmax_func(aux_result)
return out
4. 测试结果
在这个训练集上,出乎意料的,word2vec从零开始训练的方法竟然表现最好。这可能是因为数据库中的商品名称毕竟不比平时说话或者wikipedia中的自然语言那么正常,会有很多乱七八糟的词语,而且一个词语在商品中可能会有特殊的、和它平时表达意思不同的含义,使用GloVe预训练会对其造成干扰。所以,使用word2vec从零训练的效果最好也是可以理解的了。
代码:https://github.com/hannawong/word_embedding_evaluation