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 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏深度学习与数据挖掘实战

【今日热门&优质资源】深度学习经典论文&详解深度学习最热门的RNN网络

12920
来自专栏CreateAMind

浅析互信息与特征选择

那么什么是互信息呢?变量x与变量y之间的互信息,可以用来衡量已知变量x时变量y的不确定性减少的程度,同样的,也可以衡量已知变量y时变量x的不确定性减少的程度。

50220
来自专栏大数据文摘

手把手 | 用Python语言模型和LSTM做一个Drake饶舌歌词生成器

18140
来自专栏机器之心

教程 | 如何利用散点图矩阵进行数据可视化

22980
来自专栏专知

【干货】NLP中“词袋”模型和词嵌入模型的比较(附代码)

【导读】词袋模型和词向量表示是自然语言处理中最常用的特征表示方法,但这两种方法各适用于哪些不同的任务,有什么区别,作者Edward Ma详细讲解了这两类使用技巧...

11310
来自专栏崔庆才的专栏

从头开始了解PyTorch的简单实现

27550
来自专栏yw的数据分析

ONCOCNV软件思路分析之tumor处理

前期处理 perl脚本统计RC(RC(read counts)) 读入control baseline 和 sigma(最后baseline 预测的mad值) ...

430170
来自专栏机器学习算法工程师

机器翻译不可不知的Seq2Seq模型

Seq2Seq,全称Sequence to Sequence。它是一种通用的编码器——解码器框架,可用于机器翻译、文本摘要、会话建模、图...

24530
来自专栏PPV课数据科学社区

数据挖掘系列(6)决策树分类算法

 从这篇开始,我将介绍分类问题,主要介绍决策树算法、朴素贝叶斯、支持向量机、BP神经网络、懒惰学习算法、随机森林与自适应增强算法、分类模型选择和结果评价。总共7...

46640
来自专栏人工智能头条

实战 | 手把手教你搭一个机器翻译模型

62970

扫码关注云+社区

领取腾讯云代金券