1. 为什么要使用NLP?
根据业界估计,仅仅只有21%的数据是以结构化的形式展现的。数据由说话,发微博,发消息等各种方式产生。数据主要是以文本形式存在,而这种方式却是高度无结构化的。使用这些文本消息的例子包括:社交网络上的发言,聊天记录,新闻,博客,文章等等。尽管我们会有一些高维的数据,但是它所表达的信息我们很难直接获取到,除非它们已经被我们人工地做了处理(例如:我们阅读并理解了它们)。或者,我们可以通过自动化系统来对他进行分析。为了从文本数据里得到有意义并且可行的深层信息,我们需要深入理解自然语言的技术与原理。
2. NLP简介
NLP是数据科学里的一个分支,它的主要覆盖的内容是:以一种智能与高效的方式,对文本数据进行系统化分析、理解与信息提取的过程。通过使用NLP以及它的组件,我们可以管理非常大块的文本数据,或者执行大量的自动化任务,并且解决各式各样的问题,如自动摘要,机器翻译,命名实体识别,关系提取,情感分析,语音识别,以及主题分割等等。
3. 文本预处理
因为文本数据在可用的数据中是非常无结构的,它内部会包含很多不同类型的噪点。所以在对文本进行预处理之前,它暂时是不适合被用于做直接分析的。文本预处理过程主要是对 文本数据进行清洗与标准化。这个过程会让我们的数据没有噪声,并可以对它直接做分析。数据预处理的过程主要包括以下三个部分:
(1)噪声移除
任何与数据上下文无关的文本片段以及end-output均可被认为是噪音。例如,语言停顿词(一般是在语言里常用的单词,如:is, am, the, of, in 等等),URL或链接,社交媒体里的实体(如@符号,#标签等),标点符号,以及工业特有词汇等。这个步骤就是为了移除文本里所有类型的噪音实体。在噪音移除里,一个常见的方法是:准备一个噪音实体的字典,然后对text object(一个句子、短语、单词或文章)进行迭代(以token或单词),去除掉那些存在于噪音字典里的tokens(单词或实体)。
例如:
# sample code to remove noisy words from a textnoise_list = ['is', 'a', 'this', '...']def _remove_noise(input_text):words = input_text.split()noise_free_words = [word for word in words if word not in noise_list]noise_free_text = ' '.join(noise_free_words)return noise_free_textprint(_remove_noise("this is a sample text"))>>>sample text
另外一个方法是使用正则表达式处理一些特定模式的噪音。下面是一个使用正则表达式移除噪音的例子:
def _remove_regex(input_text, regex_pattern):urls = re.finditer(regex_pattern, input_text)for i in urls:input_text = re.sub(i.group().strip(), '', input_text)return input_textregex_pattern = '#[\w]*'result = _remove_regex("remove this #hashtag from analytics vidhya", regex_pattern)print(result)>>> remove this from analytics vidhya
(2)词汇规范化
另外一种文本型的噪音与一个词语的多种表达形式有关。例如,“play”,“player”,“played”,“plays”和“playing”都是单词“play”的变种。尽管它们有不同的意思,但是根据上下文来看,它们是意思是相似的。这个步骤是将一个单词的所有不同形式转换为它的规范形式(也被称为词条(lemma))。规范化在特征工程里,是对文本处理的一个关键步骤。因为它将高维的特征(N个不同的特征)转换到了低维空间(1个特征),这对于机器学习模型来说是非常完美的。最常见的词汇规范化的实践有:Ⅰ 词干提取(Stemming):词干提取是一个初级的、基于规则的脱去后缀(如“ing”,“ly”,“es”,“s”等等)的过程。Ⅱ 词元化(Lemmatization):另一方面,词元化,是一个组织好的、一步一步的获取词根的过程。并使用了词汇表(单词在字典里的重要性)和形态学分析(单词结构与语法关系)。
下面这个例子使用了NLTK来做Stemming与Lemmatization:
>>> from nltk.stem.wordnet import WordNetLemmatizer>>> lem = WordNetLemmatizer()>>> from nltk.stem.porter import PorterStemmer>>> stem = PorterStemmer()>>> word = "multiplying">>> lem.lemmatize(word, "v")'multiply'>>> stem.stem(word)'multipli'>>> word2 = 'playing'>>> stem.stem(word2)'play'
(3)对象标准化
文本数据经常包含一些不存在于标准词汇字典里的单词或短语。这些部分是无法被搜索引擎和模型所识别的。一些例子如:首字母缩略词,井字标签与它后面的词汇,以及口语俚语等。对此我们可以使用正则表达式和人工准备的数据字典,来修正这些噪音。
下面的代码使用了查找字典的方法来修复社交媒体里文本数据的俚语:
lookup_dict = {'rt':'Retweet', 'dm':'direct message', 'awsm':'awesome','luv':'love'}def _lookup_words(input_text):words = input_text.split()new_words = []for word in words:if word.lower() in lookup_dict:word = lookup_dict[word.lower()]new_words.append(word)new_words = ' '.join(new_words)return new_wordsresult = _lookup_words('RT this is a retweeted tweet by Shivam Bansal')print(result)>>>Retweet this is a retweeted tweet by Shivam Bansal
除了以上的三个步骤以外,其他还有一些文本预处理的技术,如:encoding-decoding噪音,语法检查,以及拼写纠正等。
4. 将文本转化为特征
为了分析一个已经做了预处理的数据,我们需要将它转化为特征。根据使用用途不同,文本特征可以根据各种技术建立而成。如:句法分析(Syntactical Parsing),实体(entities) / N元语法(N-grams) / 基于单词(word-based)特征,统计学(Statistical)特征,以及词向量(word embeddings)。
(1)句法分析
句法分析包括:分析单词在句子里的语法,以及,它们在句子中的位置显示的,在某种程度上,与其他单词之间的关系 。依赖性文法(Depenency Grammar)和词性标注(Part of Speech tags),是文本句法里最重要的属性。**依赖关系树** – 句子是由一些单词组成。在句子里,单词之间的关系是由基本的依赖性文法决定的。依赖性文法是一类句法文本分析,它用于处理两个词项(单词)之间的非对称二元关系。每个关系可以以一个三元项(relation(关系),governor(管理者),dependent(依赖))来表示。
例如这个句子:“Bills on ports and immigration were submitted by Senator Brownback, Republican of Kansas”。在这个句子里,单词之间的关系可以用下面一个树来表示:

从这个树里我们可以看到,“submitted”是这个句子里的根部,并且它由两个子树连接起来(subject-主语子树和object-宾语子树)。每个子树都是它自己的依赖关系树,它们之间的关系例如:(“Bills”<-> “ports” “介词(preposition)”关系),(“ports”<-> “immigration” “连词(conjunction)”关系)。这种类型的树,当以自上到下的方式迭代的进行语法分析时,我们可以得到语法关系三元组作为输出。这些输出可以被当作特征应用于非常多的nlp问题中,如实体情感分析,actor & entity identification,以及文本分类。在python中,StanfordCoreNLP以及NLTK依赖性文法,都可以用于生成依赖关系树。**词性标注** – 除了语法关系,在句子中,每个单词还与它的词性(名次,动词,形容词,副词,等)相关。词性的标签定义了一个单词在句子里的用法以及功能。下面的代码展示了如何用NLTK对输入的文本做词性标注注解。(NLTK提供了多种实现,默认使用的是perceptron tagger)。
>>> from nltk import word_tokenize, pos_tag>>> text = "Hello, I am Zack, I am learning Natural Language Processing online">>> tokens = word_tokenize(text)>>> tokens['Hello', ',', 'I', 'am', 'Zack', ',', 'I', 'am', 'learning', 'Natural', 'Language', 'Processing', 'online']>>> pos_tag(tokens)[('Hello', 'NNP'), (',', ','), ('I', 'PRP'), ('am', 'VBP'), ('Zack', 'RB'), (',', ','), ('I', 'PRP'), ('am', 'VBP'), ('learning', 'VBG'), ('Natural', 'NNP'), ('Language', 'NNP'), ('Processing', 'NNP'), ('online', 'NN')]
词性标注在NLP里有很多重要的通途:
**A.** 词义消歧:一些单词有多重意思,根据它们的使用不同,会有不同意思。例如,下面两个句子:1. “Please book my flight for Delhi”2. “I am going to read this book in the flight”这里 book的单词根据上下文的不同,含义也不同。而词性标注对两个book的词性标注也应不同。在句子1里,book单词是一个动词,而在句子2里是一个名次。(Lesk算法也可以用于相同的目的)。**B**. 提升基于单词的特征(Improving word-based features):当单词被当作特征使用时,一个学习模型会学习到一个单词的不同上下文。然而如果词性标注被关联进去时,上下文即被保护了,这样便可构造出更优秀的特征。例如:Sentence – “book my flight, I will read this book”Tokens – (“book”, 2), (“my”, 1), (“flight”, 1), (“I”, 1), (“will”, 1), (“read”, 1), (“this”, 1)Tokens with POS – (“book_VB”, 1), (“my_PRP$”, 1), (“flight_NN”, 1), (“I_PRP”, 1), (“will_MD”, 1), (“read_VB”, 1), (“this_DT”, 1), (“book_NN”, 1)**C**. 标准化与词元化:在转换一个词到它的基本形式时,词性标签是词元化过程的基础。**D.** 高效的停止词移除(Efficient stopword removal):在高效的停止词移除中,词性标注也非常有用。
(2)实体抽取(实体作为特征)
实体被定义为一个句子里最重要的部分,它们一般是名词短语,动词短语或者两者均有。实体检测算法一般是由基于规则的句法分析、词典查找、词性标注以及依存句法分析结合起来的组合模型。实体抽取的应用一般可以在自动聊天机器人、上下文分析器以及消费者洞察(consumer insights)里见到。
![%4B}_79]{SKVMK@Z}}]LEEC.png](/uploads/projects/icode-cqut@rg0s9x/d89cf3354a5aa9936c5b1b6ac00c9ce2.png)
主题建模(topic modelling)和命名实体识别(named entity recognition)在NLP里是两个非常关键的实体抽取方法。**A**. 命名实体识别(NER)从一个文本里检测命名的实体(如:人名、地点名、公司名等)的过程叫做NER,例如:Sentence – Sergey Brin, the manager of Google Inc. is walking in the streets of New YorkNamed Entities – (“person” : “Sergey Brin”), (“org” : “Google Inc.”), (“location” : “New York”)_一个典型的NER模型包括以下三个部分:_**名词短语识别**:这个步骤处理的是,使用依存句法分析和词性标记的方法,从一个文本里抽取出所有的名词短语。**短语分类**:这个步骤是一个分类操作,它将所有提取出来的名词短语分类到对应的类别中(如地点,名字,等等)。Google Maps API提供了一个很好的办法消除地点的歧义。然后,一般的开源数据库,如dbpedia,Wikipedia等都可以用于识别人名与公司名。除了这以外,我们也可以自己创建一个查找表和字典,将不同来源的数据整合起来。**实体消歧**:可能有些时候实体会被错误的分类,所以在输出层之上创建一个验证层将会大有帮助。使用知识图可以实现这个目标。比较流行的知识图有:Google Knowledge Graph,IBM Watson和Wikipedia。**B**. 主题建模主题建模是一个从文本语料库里自动化识别主题的过程。它以一种非监督的方式,得到语料库里单词中的隐含模式。主题被定义为“a repeating pattern of co-occurring terms in a corpus”。一个好的主题模型可以得到:“heath”, “doctor”, “patient”, “hospital” 为Healthcare的主题,“farm”, “crops”, “wheat” 对应于“Farming”的主题。_隐含狄利克雷分布_(Latent Dirichlet Allocation)(LDA)是最流行的主题建模技术。下面的代码使用了LDA实现主题建模。更多与此有关的说明与实现,可以参考这个文档:
https://www.analyticsvidhya.com/blog/2016/08/beginners-guide-to-topic-modeling-in-python/
import gensimfrom gensim import corpora# Creating the term dictionary of our corpus, where every term is assigned an indexdictionary = corpora.Dictionary(doc_clean)# Converting list of documents (corpus) into Document Term Matrix using dictionary prepared abovedoc_term_matrix = [dictionary.doc2bow(doc) for doc in doc_clean]# Creating the object for LDA model using gensim libLda = gensim.models.ldamodel.LdaModel# Running and Training LDA model on the document term matrixldamodel = Lda(doc_term_matrix, num_topics=3, id2word=dictionary, passes=50)# Resultsprint(ldamodel.print_topics())
[(0, '0.030*"driving" + 0.030*"a" + 0.030*"practice" + 0.030*"dance" + 0.030*"lot" + 0.030*"time" + 0.030*"of" + 0.030*"spends" + 0.030*"around" + 0.030*"consume."'), (1, '0.083*"to" + 0.058*"My" + 0.058*"sister" + 0.058*"my" + 0.058*"father" + 0.033*"is" + 0.033*"not" + 0.033*"Sugar" + 0.033*"likes" + 0.033*"have"'), (2, '0.061*"driving" + 0.061*"and" + 0.061*"suggest" + 0.061*"stress" + 0.061*"pressure" + 0.061*"may" + 0.061*"that" + 0.061*"increase" + 0.061*"Doctors" + 0.061*"blood"')]
**C**. N元语法(N-Grams)作为特征将N个单词结合在一起叫做N元语法。在作为特征时,N元语法(N>1)一般会比单元语法提供更多的消息。同样,二元语法(bigrams)被认为是其中最重要的特征。下面的代码为一个文本生成了他的二元语法:
def generate_ngrams(text, n):words = text.split()output = []for i in range(len(words)-n+1):output.append(words[i:i+n])return outputprint(generate_ngrams('this is a sample text', 2))[['this', 'is'], ['is', 'a'], ['a', 'sample'], ['sample', 'text']]
(3)统计特征(Statistical Features)
通过使用以下提到的几个技术,文本数据可以被直接量化为数量值:**A**. 词频 – 逆文档频率(Term Frequency – Inverse Document Frequency)(TF– IDF)TF-IDF是一个有权重的模型,它常被用于信息检索(information retrieval)问题。它的目的是在于:基于单词在文档里出现的频率(不考虑严格的排序),将文档转化为向量模型。例如:假设这里有个数据库,里面有N个文档。对于每个文档“D”来说,TF和IDF被定义为:Term Frequency(TF):对于一个术语(term)“t”来说,它在“D”里的TF为“t”在“D”里出现的次数。Inverse Document Frequency(IDF):IDF对于一个术语(term)来说,它被定义为:在语料库里所有文档数,与,包含术语T文档的数,的一个比率的对数。TF . IDF:TF IDF的公式(如下所示)给出了一个术语(term)在语料库(文档的集合)里的相对重要性。
![9[GIGAF7QA]G~~U4[@7QPV.png](https://cdn.nlark.com/yuque/0/2019/png/637008/1576398778184-fe6a2d19-16ba-4b21-9096-46c30d0472b3.png#align=left&display=inline&height=319&name=9%5BGIGAF7QA%5DG~~U4%5B%407QPV.png&originHeight=319&originWidth=518&size=68171&status=done&style=none&width=518)
下面的代码展示了使用python的scikit learn包将一个文本转化为tf-idf向量:
from sklearn.feature_extraction.text import TfidfVectorizerobj = TfidfVectorizer()corpus = ['This is a sample document.', 'another random document.', 'third sample document text']X = obj.fit_transform(corpus)
(0, 7) 0.58448290102(0, 2) 0.58448290102(0, 4) 0.444514311537(0, 1) 0.345205016865(1, 1) 0.385371627466(1, 0) 0.652490884513(1, 3) 0.652490884513(2, 4) 0.444514311537(2, 1) 0.345205016865(2, 6) 0.58448290102(2, 5) 0.58448290102
这个模型创建了一个词汇字典,并且给每个单词分配了一个索引。在输出结果里,每一行包含一个元祖(i, j),以及index为j的单词在文档i里的tf-idf值。**B**. 计数 / 密度 / 可读性特征(Count / Density / Readability Features)基于特征的计数与密度特征也可以被用于模型与分析。这些特征可能看起来是不重要的,但是在训练模型时显示出了巨大的影响。其中一些特征包括:单词数(Word Count),句子数(Sentence Count),标点数(Punctuation Counts)和行业特定词数(Industry specific word)。其他类型的衡量方法(包括可读性衡量)如音节数,smog index和易读性指数。可以参考以下文档创建这些特征:
https://github.com/shivam5992/textstat
(4)词向量(Word Embedding or Text vectors)
Word embedding是一种非常现代的用向量表示单词的方式。它的目标是为了将高维的词特征重新定义为低维的特征向量,主要通过保留语料库里的上下文相关性完成。它已经被广泛应用于如卷积神经网络,循环神经网络等深度学习模型中。Word2Vec 和 GloVe是两个非常流行的为文本创建词向量的模型。这些模型使用文本语料库作为输入,并生成词向量作为输出。Word2Vec模型由预处理模块、被称为连续词袋(Continuous Bag of Words)的一个浅神经网络模块以及另一个名叫skip-gram的浅神经网络模型组成。这些模型已经被广泛的用于其他各种nlp问题。它首先从训练语料库建立一个词汇表,然后学习词向量的表现方式。下面的代码使用了gensim包实现词向量:
from gensim.models import Word2Vecsentences = [['data', 'science'], ['online', 'science', 'data', 'analytics'],['machine', 'learning'], ['deep', 'learning']]# train the model on your corpusmodel = Word2Vec(sentences, min_count=1)print(model.similarity('data', 'science'))>>>-0.204396587715print(model['learning'])
[ 4.16955026e-03 -2.37376429e-03 -2.78551341e-03 3.92724760e-037.43532903e-04 7.26812170e-04 4.44857636e-03 3.75635154e-03-1.43383816e-03 4.24796832e-04 -3.50184389e-03 4.13319934e-03-2.44115014e-03 1.11951970e-03 1.82436092e-03 -4.80600866e-034.72195214e-03 -7.03078258e-05 1.24959322e-03 -4.79448866e-03…… ]
它们可以作为特征向量使用在机器学习模型中,也可以用于衡量文本相似度(使用余弦相似技术),还可以适用于词聚类以及文本分类等技术中。
