前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Kaggle 实战:Quora Question Pairs

Kaggle 实战:Quora Question Pairs

原创
作者头像
serena
修改2021-08-03 14:56:05
1.8K1
修改2021-08-03 14:56:05
举报
文章被收录于专栏:社区的朋友们社区的朋友们

作者:郭小发

前言

这次比赛的题目是 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 为例:

代码语言:javascript
复制
# 记录 8 的问题 1
     q <- da$question1[8]
    [1] "How can I be a good geologist?"

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

去除标点

代码语言:javascript
复制
cp <- tm_map(cp, removePunctuation)
    [1] How can I be a good geologist

去除数字

代码语言:javascript
复制
cp <- tm_map(cp, removeNumbers)
    [1] How can I be a good geologist

转化为小写

代码语言:javascript
复制
cp <- tm_map(cp, tolower)
    [1] how can i be a good geologist

去掉停用词

代码语言:javascript
复制
 cp <- tm_map(cp, removeWords, stopwords("english")) 
    [1]  can    good geologist

去掉空格

代码语言:javascript
复制
cp <- tm_map(cp, stripWhitespace) 
    [1]  can good geologist

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

代码语言:javascript
复制
cp <- tm_map(cp, stemDocument) 
    [1] can good geologist

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

整合成函数如下:

代码语言:javascript
复制
# 提取问题的关键字
    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 比例,两个问题相同的关键词所占整体的比例

代码语言:javascript
复制
 # 计算相同 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 的分布状况:

代码语言:javascript
复制
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: 问题字符长度之差所占比例

代码语言:javascript
复制
# 问题的字符长度
    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)以及两种分类(正面情绪和负面情绪)
代码语言:javascript
复制
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)的一种。指两个字串之间,由一个转成另一个所需的最少编辑操作次数。许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。
代码语言:javascript
复制
# 使用 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: 两个问题的疑问词是否一致

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

代码语言:javascript
复制
# 计算两个问题的疑问词是否一致
    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

代码语言:javascript
复制
# 加载包
    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,基本和随机猜测没有什么区别。

相关系数

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

代码语言:javascript
复制
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 等,主要由于这些特征计算量都比较大而没有实施。

参考文献

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
    • 评价标准
      • 本文工作
      • 特征计算
        • 文本基础处理
          • 相同 Word 占比
            • 问题字符长度
              • 情绪分析
                • 字符串相似度
                  • 疑问词是否一致
                    • 相关系数
                    • 更多特征
                    • 参考文献
                    相关产品与服务
                    大数据
                    全栈大数据产品,面向海量数据场景,帮助您 “智理无数,心中有数”!
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档