Kaggle - Home Depot Product Search Relevance关键词搜索

背景介绍

Home Depot 产品相关性预测 kaggle竞赛:https://www.kaggle.com/c/home-depot-product-search-relevance HomeDepot是美国一家家具建材商品网站,用户通过在搜索框中输入关键词,得到相关商品和服务,如输入floor,得到不同材料的地板商品、地板清洗商品、地板安装服务等。kaggle竞赛目的是通过设计一种模型,能够更好的匹配用户搜索关键词,得到相关性更高的产品和服务。

导入数据

import numpy as np
import pandas as pd
from sklearn.ensemble import RandomForestRegressor,BaggingRegressor
from nltk.stem.snowball import SnowballStemmer
#导入train,test,product_descriptions数据集
df_train=pd.read_csv(r'E:\python\kaggle-product-search-relevance-master\train.csv',encoding='ISO-8859-1')
df_test=pd.read_csv(r'E:\python\kaggle-product-search-relevance-master\test.csv',encoding='ISO-8859-1')

df_desc=pd.read_csv(r'E:\python\kaggle-product-search-relevance-master\product_descriptions.csv')

数据简介:

训练数据:
product_uid:产品的编号
product_title:产品标题描述
search_term:用户搜索的关键词
relevance: 用户搜索结果的评分。test数据集里没有这列,这个是要预测的Label。
在这里插入图片描述
产品信息:
product_uid:产品编号
product_description:产品详细描述
这个数据也挺重要的,可以并到训练数据里构建特征。
在这里插入图片描述
合并数据:

#合并训练数据及测试数据
df_all=pd.concat([df_train.assign(is_train=1),df_test.assign(is_train=0)],axis=0,ignore_index=True)
#把产品描述加进来
df_all=pd.merge(df_all,df_desc,how='left',on='product_uid')

文本预处理

stemmer=SnowballStemmer('english')
def str_stemmer(s):
    return ' '.join([stemmer.stem(word) for word in s.lower().split()])

#为了计算关键词的有效性,可以直接看出现了多少次
def str_common_word(str1,str2):
    return sum(int(str2.find(word)>0) for word in str1.split())

#数据处理
df_all['search_term']=df_all['search_term'].map(lambda x:str_stemmer(x))
df_all['product_title']=df_all['product_title'].map(lambda x:str_stemmer(x))
df_all['product_description']=df_all['product_description'].map(lambda x:str_stemmer(x))

文本特征

#关键词的长度
df_all['len_of_query']=df_all['search_term'].map(lambda x:len(x.split())).astype(np.int64)

#标题中有多少关键词重合
df_all['commons_in_title']=df_all.apply(lambda x:str_common_word(x['search_term'],x['product_title']),axis=1)

#描述中有多少关键词重合
df_all['commons_in_desc']=df_all.apply(lambda x:str_common_word(x['search_term'],x['product_description']),axis=1)

#Levenshtein文本相似度
import Levenshtein
df_all['dist_in_title']=df_all.apply(lambda x:Levenshtein.ratio(x['search_term'],x['product_title']),axis=1)
df_all['dist_in_desc']=df_all.apply(lambda x:Levenshtein.ratio(x['search_term'],x['product_description']),axis=1)

#TF-IDF
'''
我们首先搞一个新的column,叫all_texts, 里面是所有的texts。
(我并没有算上search term, 因为他们不是一个结构完整的句子,
可能会影响tfidf的学习)。为了防止句子格式不完整,我们也强制给他们
加上句号。

注意:这里我们最严谨的做法是把train/test先分开,
然后只在train上做tfidf的学习,并在test上直接转化。
但由于kaggle提前拿到test,这里姑且把文本内容汇总一起,
实际项目中,往往是没法提前拿到test的。
'''
df_all['all_texts']=df_all['product_title']+'.'+df_all['product_description']+'.'
#然后,我们取出所有的单字,做成一个我们的单词字典
from gensim.utils import tokenize
from gensim.corpora.dictionary import Dictionary
dictionary=Dictionary(list(tokenize(x,errors='ignore')) for x in df_all['all_texts'].values)
#print(dictionary)

#下面写一个类,扫便我们所有的语料,并且转化成简单的单词的个数计算(Bag-of-Words)
class MyCorpus(object):
    def __iter__(self):
        for x in df_all['all_texts'].values:
            yield dictionary.doc2bow(list(tokenize(x,errors='ignore')))

corpus=MyCorpus()

#有了标准形式的语料库,就可以把已经变成BoW向量的数组,做一次TFIDF计算
from gensim.models.tfidfmodel import TfidfModel
tfidf=TfidfModel(corpus)

#判断两个句子的相似度:把其中一个作为index,扩展开全部的matrixsize,另一个带入,就可以计算
from gensim.similarities import MatrixSimilarity

def to_tfidf(text):
    res=tfidf[dictionary.doc2bow(list(tokenize(text,errors='ignore')))]
    return res
#然后,创造一个cosine similarity的比较方法
def cos_sim(text1,text2):
    tfidf1=to_tfidf(text1)
    tfidf2=to_tfidf(text2)
    index=MatrixSimilarity([tfidf1],num_features=len(dictionary))
    sim=index[tfidf2]
    #sim输出的是一个array,我们只需要数值
    return float(sim[0])

#计算TFIDF相似度
df_all['tfidf_cos_sim_in_title']=df_all.apply(lambda x:cos_sim(x['search_term'],x['product_title']),axis=1)
df_all['tfidf_cos_sim_in_desc']=df_all.apply(lambda x:cos_sim(x['search_term'],x['product_description']),axis=1)

#Word2Vec
#w2v和tfidf不同,对tfidf而言,只需要知道一整段text中包含了哪些word元素就行了。
#而w2v要考虑到句子层级的split,以及语境前后的考虑.
import nltk
#句子分割,把长文本分割成list of 句子,再把句子变成list of 单词
tokenizer=nltk.data.load('tokenizers/punkt/english.pickle')
sentences=[tokenizer.tokenize(x) for x in df_all['all_texts'].values]
#其实这些sentences不需要层级关系,我们把list of lists给flatten了
sentences=[y for x in sentences for y in x]

#把单词分好
from nltk.tokenize import word_tokenize
w2v_corpus=[word_tokenize(x) for x in sentences]

#训练model
from gensim.models.word2vec import Word2Vec
model=Word2Vec(w2v_corpus,size=128,window=5,min_count=5,workers=4)

#TFIDF是针对每个句子都可以有的,而w2v是针对每个单词的
#这里我们平均化一个句子的w2v向量,算作整个text的平均vector

#先拿到全部的vocabulary
vocab=model.wv.vocab

#得到任意text的vector
def get_vector(text):
    res=np.zeros([128])
    count=0
    for word in word_tokenize(text):
        if word in vocab:
            res+=model[word]
            count+=1
    return res/count

#计算两个text的平均w2v的cosine similarity
from scipy import spatial

def w2v_cos_sim(text1,text2):
        w2v1=get_vector(text1)
        w2v2=get_vector(text2)
        sim=1-spatial.distance.cosine(w2v1,w2v2)
        if str(sim)=='nan':
            sim=1
        else:
            pass
        return float(sim)

#构建w2v相似度特征
df_all['w2v_cos_sim_in_title']=df_all.apply(lambda x:w2v_cos_sim(x['search_term'],x['product_title']),axis=1)
df_all['w2v_cos_sim_in_desc']=df_all.apply(lambda x:w2v_cos_sim(x['search_term'],x['product_description']),axis=1)

重塑训练/测试数据集

#删除相关特征
df_all=df_all.drop(['search_term','product_title','product_description','all_texts'],axis=1)
#重塑训练/测试集
df_train=df_all.loc[df_all['is_train']==1]
df_test=df_all.loc[df_all['is_train']==0]

#记录下测试集的id
test_ids=df_test['id']
#分离出y_train
y_train=df_train['relevance'].values
#把原集中的label删去
X_train=df_train.drop(['id','relevance','is_train'],axis=1).values
X_test=df_test.drop(['id','relevance','is_train'],axis=1).values

建立模型

这里用个简单的随机森林模型,参数也没有细调。可以再拿其它模型试试,并更细致的调整参数。

#建立模型
from sklearn.ensemble import RandomForestRegressor
from sklearn.model_selection import cross_val_score

params=[1,3,5,6,7,8,9,10]
test_scores=[]
for param in params:
    clf=RandomForestRegressor(n_estimators=30,max_depth=param)
    test_score=np.sqrt(-cross_val_score(clf,X_train,y_train,cv=5,scoring='neg_mean_squared_error'))
    test_scores.append(np.mean(test_score))

import matplotlib.pyplot as plt
%matplotlib inline
plt.plot(params,test_scores)
plt.title('Param vs CV Error')

在这里插入图片描述

上传结果

rf=RandomForestRegressor(n_estimators=30,max_depth=9)
rf.fit(X_train,y_train)
y_pred=rf.predict(X_test)
pd.DataFrame({'id':test_ids,'relevance':y_pred}).to_csv('submission.csv',index=False)
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值