Kaggle 实战:Quora Question Pairs

作者:郭小发

前言

这次比赛的题目是 Quora 公司举办的线上实际问题比赛。Quora 给出两个问题,然后参赛者判断这两个问题是否是重复的问题,即判断两个问题的意思是否一致。Quora 公司本身用了一个 Random Forest 模型来判断问题是否重复。

这次题目需要用到自然语言处理(natural language processing) NLP 的知识。而我对于 NLP 完全处于门外汉状态,本着学习的态度尝试一下这个题目。

题目地址:https://www.kaggle.com/c/quora-question-pairs#description

这个训练数据有大致 40W 条记录,而测试数据有 240W 之多,其中有很大一部分是为了防止参赛者人工标注而用电脑自动生成的题目。

评价标准

这次的评价标准用的是 LogLoss

详细公式见:https://www.kaggle.com/wiki/LogLoss

这个损失函数需要针对每行记录计算预测出问题重复的概率值,而不是 0-1 二分值。它的特点就是相比准确率这个指标来说能够更好标识预测的精度,这个损失函数值随着预测精度的提高而减少。

本文工作

由于这次数据量太大,并且涉及到自然语言处理,我自己一台 4G 内存的笔记本完全无法完成大计算量的处理。所以这次文章主要介绍几个简单特征的计算以及可视化,算是给自己这个半途而废的项目做个小总结。

特征计算

训练集的字段如下:

  • id - 训练集的记录 ID
  • qid1, qid2 - 每个问题的唯一 ID,只存在在训练集中
  • question1, question2 - 问题的文本内容,分别对应两个问题
  • is_duplicate - 目标值, 1 表示两个问题意思一致,0 则表示不一致

下图给出训练集的前 10 条记录:

文本基础处理

首先对每个问题进行文本处理,去除其中的数字标点以及停用词等同文本含义关系不大的部分,来获取各个问题的关键字。

这次特征计算使用了 R 包 tm。包 tm 即 text mining,是用来做文本挖掘的一个 R 包,是一个进行自然语言处理的基础包。 具体使用方法: http://www.bagualu.net/wordpress/archives/6112 以第八条记录的问题 1 为例:

# 记录 8 的问题 1
     q <- da$question1[8]
    [1] "How can I be a good geologist?"

    # 创建语料库
    library(tm)
    cp <- Corpus(VectorSource(q))

去除标点

cp <- tm_map(cp, removePunctuation)
    [1] How can I be a good geologist

去除数字

cp <- tm_map(cp, removeNumbers)
    [1] How can I be a good geologist

转化为小写

cp <- tm_map(cp, tolower)
    [1] how can i be a good geologist

去掉停用词

 cp <- tm_map(cp, removeWords, stopwords("english")) 
    [1]  can    good geologist

去掉空格

cp <- tm_map(cp, stripWhitespace) 
    [1]  can good geologist

去掉常用单词结尾,例如是 sesing 等结尾,来获取词根

cp <- tm_map(cp, stemDocument) 
    [1] can good geologist

经过这一番处理后,剩余的文本基本就是这个问题的关键字了。

整合成函数如下:

# 提取问题的关键字
    parse_key_word <- function(q)
    {
        library(tm)
        cp <- Corpus(VectorSource(q))

        cp <- tm_map(cp, removePunctuation)
        cp <- tm_map(cp, removeNumbers)
        cp <- tm_map(cp, tolower)
        cp <- tm_map(cp, removeWords, stopwords("english"))
        cp <- tm_map(cp, stripWhitespace)
        cp <- tm_map(cp, stemDocument) 
        # inspect(cp)

        td = TermDocumentMatrix(cp)
        return(td$dimnames$Terms)
    }

相同 Word 占比

wc1,wc2: 问题的关键字个数 diff_wc: 两个问题的关键字差异 comm_word_ratio:相同 Word 比例,两个问题相同的关键词所占整体的比例

 # 计算相同 Word 占比
    k1 <- parse_key_word( q1 )
    k2 <- parse_key_word( q2 )
    wc1 <- length(k1)
    wc2 <- length(k2)
    diff_wc <- abs(wc1 - wc2)
    if (wc1 wc2 == 0 ) {
        comm_word_ratio <- 0
    }else{
        comm_word_ratio <- ( sum(k1 %in% k2)   sum(k2 %in% k1) )/ (wc1 wc2)
    }

通过图形来看下 comm_word_ratio 的分布状况:

ggplot(train,aes(comm_word_ratio))   geom_histogram(position = 'identity', alpha=0.5, aes(y = ..density.., fill = is_duplicate))

上图可以看到 comm_word_ratio 比例高的问题对更倾向于意思一致,但是区分度并不高。

问题字符长度

cc1、cc2: 问题的字符长度 diff_cc: 问题的字符长度之差 diff_cc_r: 问题字符长度之差所占比例

# 问题的字符长度
    cc1 <- nchar(q1)
    cc2 <- nchar(q2)
    diff_cc <- abs(cc1 - cc2)
    # 转化为 0-1 范围
    diff_cc_r <-  1 -  ( diff_cc / (cc1   cc2) )
    # 画图
    ggplot(train, aes(x = is_duplicate, y = diff_cc_r, fill = is_duplicate) )   geom_boxplot()   guides(fill = FALSE)

从上图可以看到两个问题之间的字符差异对于结果基本毫无区分度。

情绪分析

语义的情绪分析。如果两个问题的情绪背景不同,那么其含义不同的概率也会大一些。 这里用到了 R 包 syuzhet,具体使用方法见:https://cran.rstudio.com/web/packages/syuzhet/vignettes/syuzhet-vignette.html

  • R 包 syuzhet 提供了四个情绪相关的字典。我们所使用的函数 get_nrc_sentiment 调用的是 Saif Mohammad's NRC Emotion lexicon。 这个字典将词语附上 8 种情绪(anger, fear, anticipation, trust, surprise, sadness, joy, and disgust)以及两种分类(正面情绪和负面情绪)
library(syuzhet)
    r1 <- get_nrc_sentiment(q1)
    r2 <- get_nrc_sentiment(q2)

    # 计算正面情绪和负面情绪的 word 数
    p1 <- r1$positive
    p2 <- r2$positive
    n1 <- r1$negative
    n2 <- r2$negative

    # 来判断是否两个问题的情绪是否一致
    np_xor <- !xor(p1,p2))   (!xor(n1,n2)

    ggplot(data=train,aes(x=np_xor,fill=is_duplicate))   geom_bar(position='fill')

从图上看出,随着情绪背景的一致状况的增高,问题意思一致的比例也在增高。

字符串相似度

衡量字符串相似度的计算方式有很多,主要分为基于字符串的相似度和基于语义的相似度。 详细情况参见:http://wetest.qq.com/lab/view/276.html 由于基于语义的相似度计算量太大,我们从基于字符串的相似度度量中获取我们的特征变量。

  • 莱文斯坦距离,又称 Levenshtein 距离,是编辑距离(edit distance)的一种。指两个字串之间,由一个转成另一个所需的最少编辑操作次数。许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。
# 使用 stringdist 包
    library(stringdist)
    # 余弦相似度
    dist_cos = stringdist(q1, q2, method="cosine")
    # 莱文斯坦距离
    dist_lv = stringdist(q1, q2, method="lv")
    # 转化为 0-1 范围 
    dist_lv_r <- 1 - ( dist_lv / ((cc1   cc2)) )

疑问词是否一致

same_what: 两个问题的疑问词是否一致

如果两个问题的疑问词不同,那么他们相同的几率会小一些。

# 计算两个问题的疑问词是否一致
    q <- c(q1, q2)
    same_what = 1 
    for ( term in c("what","why","who","when","how","which") ){
        same_num <- sum( grepl(term, q, ignore.case=TRUE) )
        if ( same_num == 1){
            same_what = 0
            break
        }else if (same_num == 2){
            same_what = 1
            break
        }
    }

由于变量 same_what 是二分值,可以直接查看它和 is_duplicate 的 confusionMatrix

# 加载包
    require(caret)
    confusionMatrix(train$same_what, train$is_duplicate, positive = "1")

    ## 结果 
    Confusion Matrix and Statistics

              Reference
    Prediction      0      1
             0 121769  43830
             1 133258 105433

                   Accuracy : 0.562           
                     95% CI : (0.5604, 0.5635)
        No Information Rate : 0.6308          
        P-Value [Acc > NIR] : 1               

                      Kappa : 0.1635          
     Mcnemars Test P-Value : <2e-16          

                Sensitivity : 0.7064          
                Specificity : 0.4775          
             Pos Pred Value : 0.4417          
             Neg Pred Value : 0.7353          
                 Prevalence : 0.3692          
             Detection Rate : 0.2608          
       Detection Prevalence : 0.5904          
          Balanced Accuracy : 0.5919          

            Positive Class  : 1

准确率只有 0.56,基本和随机猜测没有什么区别。

相关系数

我们看一下几类参数的相关系数

library(corrplot)
    corr <- cor(train[, c("comm_word_ratio","dist_cos","same_what","dist_lv_r","np_xor")])
    # 画图
    corrplot(corr = corr,order="AOE",type="upper",tl.pos="d")
    corrplot(corr = corr,add=TRUE, type="lower", method="number",order="AOE",diag=FALSE,tl.pos="n", cl.pos="n")

从上图可以看到几个特征之间的相关性比较低,都相互独立。

更多特征

前面所有的特征都是基于将问题本身作为字符串来看待,特征只都是来衡量两个字符串之间的相似度。而预测目标其实是语义上的相似,所以上述的特征只能近似的来衡量两个问题的相似度。

更多的关于语义的相似度特征,例如同义词、 wordnet、 word2vec 等,主要由于这些特征计算量都比较大而没有实施。

参考文献

原创声明,本文系作者授权云+社区-专栏发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

1 条评论
登录 后参与评论

相关文章

来自专栏企鹅号快讯

机器学习优化算法——梯度下降

在机器学习算法中,优化算法有很多,其中梯度下降法是个重头戏,如果说理解不到梯度下降法的原理,那很多算法的核心都难以掌握,今天我们就来谈谈何为“梯度下降法”。 我...

1949
来自专栏人工智能LeadAI

最全算法工程师面试题目整理(一)

1 基于每日用户搜索内容,假设只有少量已知商品的情况下,如何根据用户搜索内容获取平台内没有的新商品? ? ? 答案:这是一条类似于分词“新词获取问题”,答案是...

3476
来自专栏AI研习社

深度学习如何入门?

关于深度学习,网上的资料很多,不过貌似大部分都不太适合初学者。 这里有几个原因: 1. 深度学习确实需要一定的数学基础。如果不用深入浅出地方法讲,有些读者就会有...

3016
来自专栏量子位

干货分享 | 详解特征工程与推荐系统及其实践(附PPT)

云脑科技机器学习训练营11月13日正式开始啦!量子位作为合作媒体独家为大家分享课程干货内容。 本期内容简介 主题:亿级用户电商平台推荐系统挑战 主讲人:张本宇(...

3274
来自专栏前沿技墅

揭开计算机识别人类语言的神秘面纱——词向量

1113
来自专栏机器学习、深度学习

群体运动度量--Measuring Crowd Collectiveness

Measuring Crowd Collectiveness CVPR2013 http://mmlab.ie.cuhk.edu.hk/projects...

2049
来自专栏人工智能LeadAI

通俗易懂丨深度学习如何入门

? 作者:Jacky Yang(知乎) 关于深度学习,网上的资料很多,不过貌似大部分都不太适合初学者。 这里有几个原因: 1.深度学习确实需要一定的数学基础...

29510
来自专栏大数据挖掘DT机器学习

R语言实现混合模型

普通的线性回归只包含两项影响因素,即固定效应(fixed-effect)和噪声(noise)。噪声是我们模型中没有考虑的随机因素。而固定效应是那些可预测因素,而...

5196
来自专栏人工智能

以撩妹为例,5分钟让你秒懂深度学习!

深度学习是一个不断磨合的过程 就像谈恋爱一样 如果在这个人工智能的时代,作为一个有理想抱负的程序员,或者学生、爱好者,不懂深度学习这个超热的话题,似乎已经跟时代...

1807
来自专栏大数据挖掘DT机器学习

文本挖掘:社交网络、社群划分

作者:Matt 自然语言处理实习生 http://blog.csdn.net/sinat_26917383/article/details/5144453...

2996

扫码关注云+社区