特征的转换_01-自然语言相关特征转换

SparkML中的自然语言处理:分词与n-gram
本文介绍了SparkML库中的自然语言处理方法,包括Tokenizer的分词功能,StopWordsRemover去除停用词,以及n-gram的概念与应用。通过实例展示了如何使用这些工具进行文本预处理,为后续的特征提取和建模打下基础。

笔记整理时间:2017年1月11日
笔记整理者:王小草


SparkML 2.1.0的官方文档给出了21类特征转换的方法,其中关于自然语言处理领域的有3类,分别是:Tokenizer分词,StopWordsRemover去停用词,n-gram。本文主要介绍这三种方法的使用。

1. Tokenizer分词

在对文本做数据挖掘的时候,分词往往是第一步:将一个句子拆分成单词的集合。
在文本特征的提取笔记中,讲到可以用TF-IDF文档矩阵,word2vec词嵌入矩阵来做词或文档的向量表征,这些都是基于分词的基础上的。准确的分词是接下来特征提取与模型建立的成功垫脚石。

英文的分词很简单,只需按照标点与空格就可以提取word,而中文的分词就有点让人头痛了呢。sparkml此处提供的只是基于英文的分词方法。关于中文分词,我们还需要对其在巨大的语料库上进行训练而得到,常见的模型有隐马尔科夫模型HMM,条件随机场模型CRF等,这些模型的介绍请参见我的机器学习笔记哦~

Sparkml的分词提供了两种方法:

1.Tokenization,将英文句子分成单个的term。

    // 创建一个dataframe,第二列是句子
    val sentenceDataFrame = spark.createDataFrame(Seq(
      (0, "Hi I heard about Spark"),
      (1, "I wish Java could use case classes"),
      (2, "Logistic,regression,models,are,neat")
    )).toDF("id", "sentence")


    // 默认的分词(自动转成小写)
    val tokenizer = new Tokenizer().setInputCol("sentence").setOutputCol("words")

    val tokenized = tokenizer.transform(sentenceDataFrame)
    tokenized.show(false)

输出结果:

+---+-----------------------------------+------------------------------------------+
|id |sentence                           |words                                     |
+---+-----------------------------------+------------------------------------------+
|0  |Hi I heard about Spark             |[hi, i, heard, about, spark]              |
|1  |I wish Java could use case classes |[i, wish, java, could, use, case, classes]|
|2  |Logistic,regression,models,are,neat|[logistic,regression,models,are,neat]     |
+---+-----------------------------------+------------------------------------------+

2.RegexTokenizer ,根据正则表达式去匹配单词。
默认情况下是pattern的参数是(regex, default: “\ \s+”) ,即按照正则表达式匹配的词进行分割。
\ \s表示 空格,回车,换行等空白符,
+号表示一个或多个的意思

// 利用正则表达式分词
    val regexTokenizer = new RegexTokenizer()
      .setInputCol("sentence")
      .setOutputCol("words")

    val regexTokenized = regexTokenizer.transform(sentenceDataFrame)
    regexTokenized.show(false)

输出结果:

+---+-----------------------------------+------------------------------------------+
|id |sentence                           |words                                     |
+---+-----------------------------------+------------------------------------------+
|0  |Hi I heard about Spark             |[hi, i, heard, about, spark]              |
|1  |I wish Java could use case classes |[i, wish, java, could, use, case, classes]|
|2  |Logistic,regression,models,are,neat|[logistic,regression,models,are,neat]     |
+---+-----------------------------------+------------------------------------------+

另外,还可以这样做:在pattern中传入要取出的东东的正则表达式,比如说想得到一句话中的word,就可以设置.setPattern(“\ \W”),如果光这样,表示是按照词去分割,输出结果是酱紫的:

+---+-----------------------------------+------------------+
|id |sentence                           |words             |
+---+-----------------------------------+------------------+
|0  |Hi I heard about Spark             |[ ,  ,  ,  ]      |
|1  |I wish Java could use case classes |[ ,  ,  ,  ,  ,  ]|
|2  |Logistic,regression,models,are,neat|[,, ,, ,, ,]      |
+---+-----------------------------------+------------------+

需要再设置.setGaps(false),表示把正则表达式匹配的东东取出来而不是用其去分割。

    val regexTokenizer = new RegexTokenizer()
      .setInputCol("sentence")
      .setOutputCol("words")
      .setPattern("\\w+").setGaps(false)

    val regexTokenized = regexTokenizer.transform(sentenceDataFrame)
    regexTokenized.show(false)

输出结果:

+---+-----------------------------------+------------------------------------------+
|id |sentence                           |words                                     |
+---+-----------------------------------+------------------------------------------+
|0  |Hi I heard about Spark             |[hi, i, heard, about, spark]              |
|1  |I wish Java could use case classes |[i, wish, java, could, use, case, classes]|
|2  |Logistic,regression,models,are,neat|[logistic, regression, models, are, neat] |
+---+-----------------------------------+------------------------------------------+

2. StopWordsRemover去停用词

分词之后,句子变成了单词的集合,但是并非每一个单词都有意义,比如冠词the, 人称代词I,等等出现频率高单又缺乏意义的词。在很多需求中去掉这些无意义的停用词可以使得建模更加准确。

StopWordsRemover这个方法,输入一个字符串组成的列表,输出一个去掉了停用词的列表。

不同需求中的停用词有不同的定义,这个我们可以自己出传入参数设置。若要使用默认的停用词可以调用StopWordsRemover.loadDefaultStopWords(language),目前有的language有: “danish”, “dutch”, “english”, “finnish”, “french”, “german”, “hungarian”, “italian”, “norwegian”, “portuguese”, “russian”, “spanish”, “swedish” and “turkish”.(恩,看了好几遍的确没有中文。。再见,朋友。。。)

还有一个布尔类型的参数 caseSensitive,表示是都对大小写敏感,默认是false.

看一个例子:
比如有这样2句话:

idraw
0[I, saw, the, red, baloon]
1[Mary, had, a, little, lamb]

然后去停后输出了新的过滤后的分词组合:

idrawfiltered
0[I, saw, the, red, baloon][saw, red, baloon]
1[Mary, had, a, little, lamb][Mary, little, lamb]

停用词“I”, “the”, “had”, and “a” 被过滤掉了。

代码:

   // 创建一个DataFrame
    val dataSet = spark.createDataFrame(Seq(
      (0, Seq("I", "saw", "the", "red", "balloon")),
      (1, Seq("Mary", "had", "a", "little", "lamb"))
    )).toDF("id", "raw")

    // 创建一个新的对象,并设置参数
    val remover = new StopWordsRemover()
      .setInputCol("raw")
      .setOutputCol("filtered")

    // 转换
    remover.transform(dataSet).show(false)

输出:

+---+----------------------------+--------------------+
|id |raw                         |filtered            |
+---+----------------------------+--------------------+
|0  |[I, saw, the, red, balloon] |[saw, red, balloon] |
|1  |[Mary, had, a, little, lamb]|[Mary, little, lamb]|
+---+----------------------------+--------------------+

3. n-gram

经常有这样的需求,需要将词进行窗口为n的遍历,说多了更混乱,一个例子一目了然:
输入一句分好词的句子:

[Hi, I, heard, about, Spark]

如果窗口大小设置为2,那么输出的是:

[Hi I, I heard, heard about, about Spark] 

可见,每个词都在窗口为2的组合中被遍历了一次。

这个在中文分词中其实可以应用,将中文按字拆分,如果某个组合出现的次数很多,是不是可以换衣这个组合的词是一个真正的词语呢。

来看一下代码的实现:

   //创建dataframe
   val wordDataFrame = spark.createDataFrame(Seq(
      (0, Array("Hi", "I", "heard", "about", "Spark")),
      (1, Array("I", "wish", "Java", "could", "use", "case", "classes")),
      (2, Array("Logistic", "regression", "models", "are", "neat"))
    )).toDF("id", "words")

    // 创建新对象,并设置参数,窗口长度为3
    val ngram = new NGram().setN(3).setInputCol("words").setOutputCol("ngrame")

    //转换
    val ngramDataFrame = ngram.transform(wordDataFrame)
    ngramDataFrame.show(false)

输出结果:

+---+------------------------------------------+---------------------------------+
|id |words                                     |ngrame                                                            |
+---+------------------------------------------+---------------------------------+
|0  |[Hi, I, heard, about, Spark]              |[Hi I, I heard, heard about, about                                                       Spark]                         |
|1  |[I, wish, Java, could, use, case, classes]|[I wish, wish Java, Java could, 
                                             could use, use case, case classes]|
|2  |[Logistic, regression, models, are, neat] |[Logistic regression, regression 
                                               models, models are, are neat]    |
+---+------------------------------------------+---------------------------------+
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值