@YELP NLP
为了做研究,进了Yelp这个大坑。自己选的题,哭着也得搞出来… 感觉边写代码边记录下心得体会、下一步的构想等等有的没的,是个很好的整理思路的过程。所以我接下来会长篇累牍地写自己在做这个项目的过程中所遇到的问题和可能的解决方法。
挑来挑去,还是觉得CSDN的写博界面最友好,所以就在这里扎根啦。
哦!我的NLP! I am coming!
我从Yelp 上收集了一些用户评论,现在希望从这些评论中提取出我想要的信息,特别是与用户的位置和社会网络相关的信息。目前,我设想的是训练一个分类模型,输入用户评论,输出0-1变量,告诉我这个评论中是否有我想要的信息。如果有的话,当然我可以继续深入下去,最终从模型中提取出这些信息。但是我现在想先从最简单的情况入手,只要模型能够告诉我输入项里是否存在我感兴趣的信息即可。目前,我还不知道具体怎么定义信息存在或不存在。我有的只是一个大致的想法。我知道我想要最终找到的句子是下面两类:
简单点说,我希望判断用户提交的评论中是否包含这样的信息,让我能够据此推测出他的住址/工作地址或社交情况。有可能这可以简化成一个关键词提取问题,但是我初步想了一下,感觉只是判断关键词是否存在似乎太简单了一点,模型的准确度可能很低,不过如果我最终想做一个分类模型的话,这无疑可以作为其中一个特征传进去。在关键词的选择上,我可能也需要多考虑一下。因为我的直觉告诉我,像apartment, home, work, coworker这样的关键词可能是比local, live, close等要更relevant一点。名词似乎更重要,但是只有这些名词也不行,因为我还需要知道这些名词的使用情景,只是告诉我他家的情况并不够,我希望他告诉我的是,他家住在附近,所以word embedding无疑是有必要的。但是more than embedding of any word, 也许我需要赋予我所关注的那些关键词的embedding更多的权重?我还需要再想一下细节,但是问题的关键应该就是关键词定位,并确定它们的使用情景。我之前有想过,用语言模型的方法来formalize使用情景,但是我看了相关文献,感觉也许embedding的想法更容易实现、效果也可能更好一些,毕竟网上已经有成型的实现embedding的工具了,而具体要定义什么样的语言模型、怎么定义,我还需要自己去摸索。后一种方式感觉吃力不讨好,工作量大不说,还存在不确定性,不知道效果怎么样。
我现在关注的虽然有两组信息,但是其实只要做出来其中一组的分类模型,第二组就会很容易了。我决定从位置信息入手,集中精力建一个能够work的位置分类模型。
建这个模型碰到的第一个问题是,到底是以句子为单位还是以整个评论文本为单位。其实我关注的应该是句子,但是如果要建以句子为单位的模型的话,我就需要有一个非常快的sentence tokenizer。而我初步试验了一下spaCy,它的performance完全跟不上我的需求。因为这个package里面附属了太多其他的东西,虽然我一把文本传进它的nlp model,就可以得到一个sentence list, 但是它同时也返给我很多其他我暂时不想要的东西,这些附属的功能现在看来就成了一个累赘,不必要的占用了资源。
事实上,我刚刚做了一下试验,估算了一下如果要用spacy给我所有的文本断句的话,需要大概10,000秒, 也就是166分钟,大概3个小时。这个时间倒也不是完全不能接受,但是转念一想,我现在还只有大概30万的用户评论。30万就已经需要这么久了,可以预见,如果我以后扩大样本量的话,光断句这个最basic的步骤就要花天长地久那么久,实在不合理。
所以我需要迅速的figure out 怎么能又快又好的实现断句功能。遇到这个瓶颈,是我之前没想到的。真正的战役还没开始,已然如临大敌了…
首先,我需要看一下除了spaCy之外,还有没有其他现成的performance 与 speed兼具的工具。简单google了一下,发现可用的python包有如下几个:
接下来我需要比较这几个包在sentence tokenization 方面的表现。
我利用batch_size = 500, num_batch = 5的小样本试验,估算不同程序完成对现有全部数据(342970条reviews)进行断句所需要的时间。以下是估算时间的均值和方差。
Table 1. Estimated Processing Time
mean(估算时间) | std(估算时间) | |
---|---|---|
spaCy | 9281.36s | 433.35 |
NLTK | 74.56s | 15.69 |
coreNLP | 2329.33s | 521.74 |
sentence_splitter 1.4 | 395.54s | 10.72 |
可以看到,最快的是NLTK,最慢的是spaCy. sentence_splitter和coreNLP属于中等。但是我估计NLTK的效果应该很差,因为我稍微追踪了一下断句数就发现,NLTK生成的句子数量明显少于spaCy。
这是我比较断句质量的其中一个指标 – 给定文本,不同包进行断句处理后,生成的句子数量。多数情况下,断句量大的效果更好,但也不绝对。很多时候,断句的难点就在于需要选择性地跳过某些形似断句点的地方,比如称呼和缩写 — Dr. Chen, M.D., U.S.A。这些短语中存在句点,但它并不是句子的终结,而只是一种表示省略的方式。如果错误的把它们当成句点,就会把本属于同一句的两个部分拆开。这种情况下,断句量会与断句效果成反比。
但是,这个问题算是断句问题中的经典难题了,大部分现有的模型都能够很好的规避它。在当前这个发展阶段,断句错误更多的集中在处理不规则的网络文本时,文本中可能出现一些奇奇怪怪的句点,甚至没有标点符号。人们在网络上打字,一方面比较随意,另外一方面也可能受手机键盘的影响,经常会省略标点符合,以空格代替。这种情况下,如何进行断句,是一个新问题。也正是由于这个问题的出现,我才特别关注断句量,因为不正确的断句模型会严重低估句子数量。从我所做的实验来看,这也正是很多现有工具的局限所在。
我所做的实验室,给定1000条reviews和任意两个断句模型A和B,计算有多大可能模型A给出的断句量会大于模型B,反之亦然。我现在先不管正确的断句结果应该是怎么样的,或者说哪个模型给出的判断更合理,这些都需要我们根据不同文本进行详细的分析。我现在只想知道,有没有哪个模型会系统性地断出更多或者更少的句子。对于这个比较问题,我对两个变量感兴趣。首先,我想知道,有多大可能,两个模型给出的断句量正好相等。一般来说,断句量相等就意味着断句结果一致,虽然也不总是如此。这是一个简化的假设。另外一方面,我也想知道,在那些不相等的情况下,哪个模型更有可能得到更多的断句。
Table 2. Count of Sentences: A Comparative Case
spaCy | NLTK | coreNLP | sentence splitter | |
---|---|---|---|---|
spaCy (>) | # | 290/322 | 265/288 | 315/315 |
NLTK (>) | 32/322 | # | 46/92 | 117/133 |
coreNLP (>) | 23/288 | 46/92 | # | 103/113 |
sentence splitter (>) | 0/315 | 16/133 | 10/113 | # |
最终的结果以二维矩阵的形式来展示。每一行中各格都有两个数字,前一个数字表示在1000次实验中,该格对应的行模型断句量大于该格对应的列模型所给出的断句量这一情况所出现的次数,后一个数字则表示这两个模型给出断句量不相等的次数。
从这个表,我们可以看出,在不同模型的两两对比中,有两个特征特别明显:
因此,从断句量的角度来看:
s p a C y > N L T K ≈ c o r e N L P > s e n t e n c e _ s p l i t t e r spaCy > NLTK \approx coreNLP > sentence\_splitter spaCy>NLTK≈coreNLP>sentence_splitter
之前说过,断句量只是一个粗略的指标,要想直接比较不同模型的断句质量,我们还必须直接审视断句结果,检查其中是否存在不合理断句的情况。
由于这一步比较花时间,我只选择性地比较两个模型的结果。我不再考虑sentence splitter, 因为这个程序不光表现不够好,在运行的时候还存在bug. 考虑到NLTK和coreNLP的结果高度一致,而NLTK显著快于coreNLP,所以在这二者之间,我选择了NLTK。
我所做的实验是:给定50条reviews,手动检查spaCy 和 NLTK 给出不同断句数的情况。
在这50条中,两个模型断句数不同的有17条。
一看具体的断句情况就能发现,之所以spaCy的断句量会显著高于其他模型,是因为它更偏向于shorter sentences。当句子出现可断可不断的时候,spaCy一般都会选择断,比如
原句是:
HORRIBLE … incredibly HORRIBLE .
spaCy就把它断成了两句,而NLTK则把它当成一句。
更明显的一个例子是:
worst service ever!!! the pizzas took 2 hrs to deliver and when i called the girl over the phone told me thats it was on its way but that it was taking long because of the rain but i live a block away … she was really rude and literally told me " can u just fucking wait" and hang up on me … they just dont give a fuck … and the delivery guy had the guts to ask why i didnt tip him … sticking with pizza hut
这个句子中没有句号,只有省略号,所以NLTK完全没有断句,而spaCy则断成了5句。
- worst service ever!!!
- the pizzas took 2 hrs to deliver and when i called the girl over the phone told me thats it was on its way but that it was taking long
because of the rain- but i live a block away …
- she was really rude and literally told me " can u just fucking wait" and hang up on me …
- they just dont give a fuck … and the delivery guy had the guts to ask why i didnt tip him … sticking with pizza hut
这个例子非常重要,因为我们在段落中发现了表示位置的句子,“but i live a block away…"。
这正是我想在成千上万的reviews中找到的关键性信息,而显然只有spaCy把它单独分割出来了。这样单独成句的好处是:
找到这个重要例子之后,我决定再拿它在sentence_splitter上试验一下。sentence_splitter的断句结果与NLTK一样,没有能够成功的断句。
这让我决定,目前还是用spaCy来断句。之后如果出现了performance太差的问题,再模仿spaCy,单独训练自己的sentence splitter。
这样一来,我预计需要花三个小时,将所有的reviews都断成sentences,另存为sentences.txt,之后才能进行正式的实验部分。这个断句程序写起来非常容易,但是我需要特别考虑如何更好地实现sentences与reviews之间的对应。给定一个sentence, 我如何能够迅速的确定,它来自哪一条review?
我在测试sentence_splitter的时候,发现这个包和spaCy有冲突。在同一个程序中调用这两个包中的函数,sentence_splitter会返回“_regex_core.error: unterminated character set at position 91”。这个问题,github有另外一个用户已经反映过了,不过好像并没有得到开发者的回答。[link]
为了解决这个问题,我不得不在测试的过程中将基于sentence_splitter的处理单独拎出来,通过文本在不同的程序之间传递结果。非常坑… 最后写了两个程序: “splitter_time_estimate.py”里包含了估算时间和单独处理sentence_splitter的函数,“splitter_sentence_length.py”里包含了基于其他几个模型比较断句量的函数。
虽然走了一大圈,比较了各种工具,最后还是决定用spaCy来断句,但我并不觉得之前做的这些工作是毫无意义的。这些工作让我更加确定了,断句模型在处理网络文本时的难题在哪里,这为我在未来开发自己的断句模型提供了新思路。也许最大的挑战不是像教材所说的,判断不同的标点符号是否标识着一个句子的结束,而是要在完全没有标点的情况下仍然能够正确地断句。这有点像是文言文了,文本本身没有句点,但是我们仍然可以人为地加入一些句点,让文本结构和逻辑更明确。解决了这个问题之后,我们能得到的将不仅仅是一个断句模型,更是一个加减标点模型。在网络空间中,保证标点的正确使用,不正是一个重要的挑战吗?特别是在使用手机的时候,从字母表键盘跳转到标点符号键盘再跳回来,本身就非常不方便。如果有一个程序,可以自动地给文本加标点,在手机上打字的用户体验就会更好。这也会提高语音识别文本的可读性。当然,很多现有的程序已经能够做到这一点了,比如输入法,到了恰当的地方都会自动推荐标点符号,但是准确度如何,就不好说了。至少,到目前为止,我还没有在NLP的文献中看到有任何论文明确地讨论过这个问题的。不过,也可能是我见识浅薄吧…
Anyway, STEP 1.1 正式告一段落。
下一步是,SENTENCE TOKENIZATION WITH spaCy。