ITPub博客

首页 > 数据库 > 数据库开发技术 > 当NLP遇见PySpark

当NLP遇见PySpark

原创 数据库开发技术 作者:quincyqiang 时间:2021-05-19 10:15:00 0 删除 编辑


01
引言


    在本文,我们将给大家带来如何使用PySpark处理文本数据。文本数据可以是结构化或者非结构化的,我们必须进行多部处理,才能把数据处理成可供模型学习的输入数据。



2
分词


    NLP的底层任务由易到难大致可以分为词法分析、句法分析和语义分析。分词是词法分析(还包括词性标注和命名实体识别)中最基本的任务。首先需要创建spark对象,并创建一个spark的DataFrame。

from pyspark.sql import SparkSession
spark=SparkSession.builder.appName('nlp').getOrCreate()
df=spark.createDataFrame([(1,'I really liked this movie'),
                         (2,'I would recommend this movie to my friends'),
                         (3,'movie was alright but acting was horrible'),
                         (4,'I am never watching that movie ever again')],
                        ['user_id','review'])
df.show(5,False)
+-------+------------------------------------------+
|user_id|review                                    |
+-------+------------------------------------------+
|
1      |I really liked this movie                 |
|2      |I would recommend this movie to my friends|
|
3      |movie was alright but acting was horrible |
|4      |I am never watching that movie ever again |
+-------+------------------------------------------+

    下一步需要从Spark库中引入Tokenizer。然后必须传入输入列并对标记之后的输出列进行命名。这里使用transform函数,以便将标记应用到review列。可以看到tokens列就是已经分好词的文本。

from pyspark.ml.feature import Tokenizer
tokenization=Tokenizer(inputCol='review',outputCol='tokens')
tokenized_df=tokenization.transform(df)
tokenized_df.show(4,False)
+-------+------------------------------------------+---------------------------------------------------+
|user_id|review                                    |tokens                                             |
+-------+------------------------------------------+---------------------------------------------------+
|1      |I really liked this movie                 |[i, really, liked, this, movie]                    |
|2      |I would recommend this movie to my friends|[i, would, recommend, this, movie, to, my, friends]|
|3      |movie was alright but acting was horrible |[movie, was, alright, but, acting, was, horrible]  |
|4      |I am never watching that movie ever again |[i, am, never, watching, that, movie, ever, again] |
+-------+------------------------------------------+---------------------------------------------------+




3
去除停用词


    我们可以看到tokens中包含很多通用的单词,例如the、to等。这些单词被称为停用词,这些词对于分析没有多大的用处,并且会增大计算开销,因此我们在进行下游任务时,会考虑将这些词从文本中去除。这便用到Spark中的StopWordsRemover。

from pyspark.ml.feature import StopWordsRemover
stopword_removal=StopWordsRemover(inputCol='tokens',outputCol='refined_tokens')
refined_df=stopword_removal.transform(tokenized_df)
refined_df.select(['user_id','tokens','refined_tokens']).show(10,False)

+-------+---------------------------------------------------+----------------------------------+
|user_id|tokens                                             |refined_tokens                    |
+-------+---------------------------------------------------+----------------------------------+
|1      |[i, really, liked, this, movie]                    |[really, liked, movie]            |
|2      |[i, would, recommend, this, movie, to, my, friends]|[recommend, movie, friends]       |
|3      |[movie, was, alright, but, acting, was, horrible]  |[movie, alright, acting, horrible]|
|4      |[i, am, never, watching, that, movie, ever, again] |[never, watching, movie, ever]    |
+-------+---------------------------------------------------+----------------------------------+




4
词袋模型


    通过词袋可以数值化文本数据,以便将文本数据用于机器学习以及其它的任何分析。词袋允许我们将文本形式转换成数值向量形式,其中涉及单词在文本文档中出现的频率。通常有以下两种,一种是计数向量,一种是TF-IDF向量。

from pyspark.ml.feature import CountVectorizer
count_vec=CountVectorizer(inputCol='refined_tokens',outputCol='features')
cv_df=count_vec.fit(refined_df).transform(refined_df)
cv_df.select(['user_id','refined_tokens','features']).show(4,False)
count_vec.fit(refined_df).vocabulary
+-------+----------------------------------+--------------------------------+
|user_id|refined_tokens                    |features                        |
+-------+----------------------------------+--------------------------------+
|1      |[really, liked, movie]            |(11,[0,4,9],[1.0,1.0,1.0])      |
|2      |[recommend, movie, friends]       |(11,[0,6,10],[1.0,1.0,1.0])     |
|3      |[movie, alright, acting, horrible]|(11,[0,2,3,5],[1.0,1.0,1.0,1.0])|
|4      |[never, watching, movie, ever]    |(11,[0,1,7,8],[1.0,1.0,1.0,1.0])|
+-------+----------------------------------+--------------------------------+
['movie',
 'horrible',
 'really',
 'alright',
 'liked',
 'friends',
 'recommend',
 'never',
 'ever',
 'acting',
 'watching']
from pyspark.ml.feature import HashingTF,IDF
hashing_vec=HashingTF(inputCol='refined_tokens',outputCol='tf_features')
hashing_df=hashing_vec.transform(refined_df)
hashing_df.select(['user_id','refined_tokens','tf_features']).show(4,False)
+-------+----------------------------------+-------------------------------------------------------+
|user_id|refined_tokens                    |tf_features                                            |
+-------+----------------------------------+-------------------------------------------------------+
|1      |[really, liked, movie]            |(262144,[14,32675,155321],[1.0,1.0,1.0])               |
|2      |[recommend, movie, friends]       |(262144,[129613,155321,222394],[1.0,1.0,1.0])          |
|3      |[movie, alright, acting, horrible]|(262144,[80824,155321,236263,240286],[1.0,1.0,1.0,1.0])|
|4      |[never, watching, movie, ever]    |(262144,[63139,155321,203802,245806],[1.0,1.0,1.0,1.0])|
+-------+----------------------------------+-------------------------------------------------------+


tf_idf_vec=IDF(inputCol='tf_features',outputCol='tf_idf_features')
tf_idf_df=tf_idf_vec.fit(hashing_df).transform(hashing_df)
tf_idf_df.select(['user_id','tf_idf_features']).show(4,False)
+-------+----------------------------------------------------------------------------------------------------+
|user_id|tf_idf_features                                                                                     |
+-------+----------------------------------------------------------------------------------------------------+
|
1      |(262144,[14,32675,155321],[0.9162907318741551,0.9162907318741551,0.0])                              |
|2      |(262144,[129613,155321,222394],[0.9162907318741551,0.0,0.9162907318741551])                         |
|
3      |(262144,[80824,155321,236263,240286],[0.9162907318741551,0.0,0.9162907318741551,0.9162907318741551])|
|4      |(262144,[63139,155321,203802,245806],[0.9162907318741551,0.0,0.9162907318741551,0.9162907318741551])|
+-------+----------------------------------------------------------------------------------------------------+




5
文本分类


    下面我们构建一个简单的文本分类模型,使用的数据及是开源的已标记的电影镜头评论数据,并且我们打算预测任意指定评论的情绪分类。整体流程如下程序所示。

from pyspark.sql.functions import rand 
from pyspark.sql.functions import length
from pyspark.sql.functions import udf
from pyspark.sql.types import IntegerType
from pyspark.sql.functions import *
from pyspark.ml.feature import VectorAssembler
from pyspark.ml.classification import LogisticRegression
from pyspark.ml.evaluation import BinaryClassificationEvaluator

text_df=spark.read.csv('Movie_reviews.csv',inferSchema=True,header=True,sep=',')

#创建标签列
text_df = text_df.withColumn("Label", text_df.Sentiment.cast('float')).drop('Sentiment')
#增加一个额外的length列来捕获评论的长度
text_df=text_df.withColumn('length',length(text_df['Review']))
#分词、去除停用词
tokenization=Tokenizer(inputCol='Review',outputCol='tokens')
tokenized_df=tokenization.transform(text_df)
stopword_removal=StopWordsRemover(inputCol='tokens',outputCol='refined_tokens')
refined_text_df=stopword_removal.transform(tokenized_df)


len_udf = udf(lambda s: len(s), IntegerType())
refined_text_df = refined_text_df.withColumn("token_count", len_udf(col('refined_tokens')))
refined_text_df.orderBy(rand()).show(10)
+--------------------+-----+------+--------------------+--------------------+-----------+
|              Review|Label|length|              tokens|      refined_tokens|token_count|
+--------------------+-----+------+--------------------+--------------------+-----------+
|da vinci code was...|  1.0|    37|[da, vinci, code,...|[da, vinci, code,...|          5|
|Not because I hat...|  0.0|    72|[not, because, i,...|[hate, harry, pot...|          6|
|I love Harry Potter.|  1.0|    20|[i, love, harry, ...|[love, harry, pot...|          3|
|and I love Da Vin...|  1.0|    71|[and, i, love, da...|[love, da, vinci,...|          7|
|Da Vinci Code = U...|  0.0|    72|[da, vinci, code,...|[da, vinci, code,...|         15|
|Brokeback Mountai...|  1.0|    34|[brokeback, mount...|[brokeback, mount...|          3|
|I think I hate Ha...|  0.0|    72|[i, think, i, hat...|[think, hate, har...|          9|
|Harry Potter is b...|  1.0|    26|[harry, potter, i...|[harry, potter, b...|          3|
|The Da Vinci Code...|  1.0|    30|[the, da, vinci, ...|[da, vinci, code,...|          4|
|Combining the opi...|  0.0|    71|[combining, the, ...|[combining, opini...|         10|
+--------------------+-----+------+--------------------+--------------------+-----------+

#将文本数据转换成数值特征
count_vec=CountVectorizer(inputCol='refined_tokens',outputCol='features')
cv_text_df=count_vec.fit(refined_text_df).transform(refined_text_df)

#select data for building model
model_text_df=cv_text_df.select(['features','token_count','Label'])

#创建输入特征
df_assembler = VectorAssembler(inputCols=['features','token_count'],outputCol='features_vec')
model_text_df = df_assembler.transform(model_text_df)

#划分训练、验证集
training_df,test_df=model_text_df.randomSplit([0.75,0.25])
#训练模型
log_reg=LogisticRegression(featuresCol='features_vec',labelCol='Label').fit(training_df)
#验证评估
results=log_reg.evaluate(test_df).predictions

#confusion matrix
true_postives = results[(results.Label == 1) & (results.prediction == 1)].count()
true_negatives = results[(results.Label == 0) & (results.prediction == 0)].count()
false_positives = results[(results.Label == 0) & (results.prediction == 1)].count()
false_negatives = results[(results.Label == 1) & (results.prediction == 0)].count()

recall = float(true_postives)/(true_postives + false_negatives)
print(recall)

precision = float(true_postives) / (true_postives + false_positives)
print(precision)

accuracy=float((true_postives+true_negatives) /(results.count()))
print(accuracy)




6
总结


    本文主要使用PySpark来实现NLP(自然语言处理)的文本处理以及简单的文本分类,在工业级实践中,使用pyspark来处理文本的场景是较多的。感兴趣的小伙伴点赞收藏,关注公众号获取第一手资源哦!!!

分享,点赞,在看,
都在这儿,点我不香吗?
扫描下方的二维码进入ChallengeHub粉丝群,也可以添加管理员微信拉您入群。
与爱好AI的朋友们共同成长
ChallengeHub粉丝群
管理员微信




原文作者:ChallengeHub;如有侵权,请联系管理员删除。

请登录后发表评论 登录
全部评论

注册时间:2019-01-07

  • 博文量
    350
  • 访问量
    161006