赞
踩
大部分数据都是以结构化形式存在的,而文本数据是非结构化数据,比如推文,帖子,聊天对话,新闻,博客文章,产品等。尽管具有高维数据,但需要手动处理才能直接访问。
NLP是数据科学的一个分支,包括以智能和有效的方式分析,理解和从文本数据中获取信息的系统过程。通过利用NLP及其组件,可以组织大量的文本数据,执行大量自动化任务并解决各种问题,如自动摘要,机器翻译,命名实体识别,关系提取,情感分析,语音识别,和主题细分等。
安装NLTK及其数据的步骤:
安装Pip:在终端中运行:
sudo easy_install pip
安装NLTK:在终端中运行:
sudo pip install -U nltk
下载NLTK数据:运行python shell(在终端中)并编写以下代码:
import nltk nltk.download()
由于文本是所有可用数据中最无结构的形式,因此其中存在各种类型的噪声,并且在没有任何预处理的情况下数据不易分析。文本清理和标准化的整个过程,使其无噪音并准备好进行分析,称为文本预处理。
主要包括三个步骤:
任何上下文中和最终输出无关的文本都可以指定为噪声。例如,语言公共词(is, am, the, of, etc),URLs, 等。这一步消除文本中所有的噪声实体。用于噪声消除的一般方法是准备噪声实体的字典,并通过迭代文本对象,消除存在于噪声字典中的那些噪声实体。
下面是这一部分的代码:
# Sample code to remove noisy words from a text
noise_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_text
_remove_noise("this is a sample text")
>>> "sample text"
另一种方法是在处理特殊噪声时使用正则表达式。我们在之前的一篇文章中详细解释了正则表达式。以下python代码从输入文本中删除正则表达式模式:
# Sample code to remove a regex pattern
import re
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_text
regex_pattern = "#[\w]*" #文本中特殊的几个符号
_remove_regex("remove this #hashtag from analytics vidhya", regex_pattern)
>>> "remove this from analytics vidhya"
另一种类型是一个单词的多种形态,如动词过去分词第三人称等。例如,‘play’, ‘player’, ‘played’, 'plays’等都是单词play的不同形式,虽然意思不同但是结构相似。规范化是使用文本进行特征工程的关键步骤,因为它将高维特征(N个不同特征)转换为低维空间(1特征),这是任何ML模型的理想要求。
最常见的词典规范化实践是:
下面是使用python的流行库–NLTK执行词形还原和词干化的示例代码:
from nltk.stem.wordnet import WordNetLemmatizer
lem = WordNetLemmatizer()
from nltk.stem.porter import PorterStemmer
stem = PorterStemmer()
word = "multiplying"
lem.lemmatize(word, "v")
文本数据经常包含一些不存在于标准词汇字典里的单词或短语。这些部分是无法被搜索引擎和模型所识别的。一些例子如:首字母缩略词,井字标签与它后面的词汇,以及口语俚语等。对此我们可以使用正则表达式和人工准备的数据字典,来修正这些噪音。下面的代码使用了查找字典的方法来修复社交媒体里文本数据的俚语:
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_words
result = _lookup_words(‘RT this is a retweeted tweet by Shivam Bansal’)
print(result)
Retweet this is a retweeted tweet by Shivam Bansal
除了以上讨论的三个步骤以外,其他还有一些文本预处理的技术,如:encoding-decoding噪音,语法检查,以及拼写纠正等。
为了分析一个已经做了预处理的数据,我们需要将它转化为特征。根据使用用途不同,文本特征可以根据各种技术建立而成。如:句法分析(Syntactical Parsing),实体(entities) / N-grams / 基于单词(word-based)特征,统计学(Statistical)特征,以及词向量(word embeddings)。接下来我们会对它们进行逐一介绍。
句法分析包括:分析单词在句子里的语法,以及,它们在句子中的位置显示的,在某种程度上,与其他单词之间的关系 。依赖性文法(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)
print(tokens)
print(pos_tag(tokens)
‘[‘Hello’, ‘,’, ‘I’, ‘am’, ‘Zack’, ‘,’, ‘I’, ‘am’, ‘learning’, ‘Natural’, ‘Language’, ‘Processing’, ‘online’]’
[(‘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):在高效的停止词移除中,词性标注也非常有用
实体被定义为一个句子里最重要的部分,它们一般是名词短语,动词短语或者两者均有。实体检测算法一般是由基于规则的句法分析、词典查找、词性标注以及依存句法分析结合起来的组合模型。实体抽取的应用一般可以在自动聊天机器人、上下文分析器以及消费者洞察(consumer insights)里见到。
主题建模(topic modelling)和命名实体识别(named entity recognition)在NLP里是两个非常关键的实体抽取方法。
A. 命名实体识别(NER)
从一个文本里检测命名的实体(如:人名、地点名、公司名等)的过程叫做NER,例如:
Sentence – Sergey Brin, the manager of Google Inc. is walking in the streets of New York
Named 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/
doc1 = "Sugar is bad to consume. My sister likes to have sugar, but not my father." doc2 = "My father spends a lot of time driving my sister around to dance practice." doc3 = "Doctors suggest that driving may cause increased stress and blood pressure." doc_complete = [doc1, doc2, doc3] doc_clean = [doc.split() for doc in doc_complete] #通过指定分隔符对字符串进行切片,如果参数 num 有指定值,则分隔 num+1 个子字符串 import gensim from gensim import corpora # Creating the term dictionary of our corpus, where every unique term is assigned an index. dictionary = corpora.Dictionary(doc_clean) # Converting list of documents (corpus) into Document Term Matrix using dictionary prepared above. doc_term_matrix = [dictionary.doc2bow(doc) for doc in doc_clean] # Creating the object for LDA model using gensim library Lda = gensim.models.ldamodel.LdaModel # Running and Training LDA model on the document term matrix ldamodel = Lda(doc_term_matrix, num_topics=3, id2word = dictionary, passes=50) # Results print(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 output
generate_ngrams(‘this is a sample text’, 2)
' [['this', 'is'], ['is', 'a'], ['a', 'sample'], , ['sample', 'text']] '
通过使用以下提到的几个技术,文本数据可以被直接量化为数量值:
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)在语料库(文档的集合)里的相对重要性:
下面的代码展示了使用python的scikit learn包将一个文本转化为tf-idf向量:
from sklearn.feature_extraction.text import TfidfVectorizer
obj = 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
Word embedding是一种非常现代的用向量表示单词的方式。它的目标是为了将高维的词特征重新定义为低维的特征向量,主要通过保留语料库里的上下文相关性完成。它已经被广泛应用于如卷积神经网络,循环神经网络等深度学习模型中
Word2Vec 和 GloVe是两个非常流行的为文本创建词向量的模型。这些模型使用文本语料库作为输入,并生成词向量作为输出。
Word2Vec模型由预处理模块、被称为连续词袋(Continuous Bag of Words)的一个浅神经网络模块以及另一个名叫skip-gram的浅神经网络模型组成。这些模型已经被广泛的用于其他各种nlp问题。它首先从训练语料库建立一个词汇表,然后学习词向量的表现方式。下面的代码使用了gensim包实现词向量:
from gensim.models import Word2Vec
sentences = [['data', 'science'], ['vidhya', 'science', 'data', 'analytics'],['machine', 'learning'], ['deep', 'learning']]
# train the model on your corpus
model = Word2Vec(sentences, min_count = 1)
print model.similarity('data', 'science')
0.11222489293
print model['learning']
array([ 0.00459356 0.00303564 -0.00467622 0.00209638, ...])
它们可以作为特征向量使用在机器学习模型中,也可以用于衡量文本相似度(使用余弦相似技术),还可以适用于词聚类以及文本分类等技术中。
这个板块会讨论在自然语言处理领域下的不同的应用场景与问题
文本分类经典的NLP问题之一。众所周知的例子包括:垃圾邮件识别,新闻主题分类,情感分析,以及搜索引擎的页面组织。
文本分类,简单来说,它就是一种将文本对象(文档或句子)分类到一个固定的类别的技术。当数据量非常大时,它在数据的组织、信息过滤,以及数据存储等方面起到非常大的作用。
一个典型的自然语言分类器包含两部分:1. 训练;2. 预测。如下图所示:
首先,文本在输入后,它的特征会被创建。然后机器学习算法从这些特征学习一组参数。之后使用学习到的机器学习模型对新文本做预测。
下面是一个朴素贝叶斯分类器的例子(使用了textblob库):
from textblob.classifiers import NaiveBayesClassifier as NBC from textblob import TextBlob training_corpus = [('I am exhausted of this work.', 'Class_B'), ("I can't cooperate with this", 'Class_B'), ('He is my badest enemy!', 'Class_B'), ('My management is poor.', 'Class_B'), ('I love this burger.', 'Class_A'), ('This is an brilliant place!', 'Class_A'), ('I feel very good about these dates.', 'Class_A'), ('This is my best work.', 'Class_A'), ("What an awesome view", 'Class_A'), ('I do not like this dish', 'Class_B')] test_corpus = [("I am not feeling well today.", 'Class_B'), ("I feel brilliant!", 'Class_A'), ('Gary is a friend of mine.', 'Class_A'), ("I can't believe I'm doing this.", 'Class_B'), ('The date was good.', 'Class_A'), ('I do not enjoy my job', 'Class_B')] model = NBC(training_corpus)
print(model.classify("Their codes are amazing."))
Class_A
print(model.classify("I don't like their computer."))
Class_B
print(model.accuracy(test_corpus))
0.833
Scikit.Learn也提供了做文本分类的管道框架:
from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.metrics import classification_report from sklearn import svm # preparing data for SVM model (using the same training_corpus, test_corpus from naive bayes example) train_data = [] train_labels = [] for row in training_corpus: train_data.append(row[0]) train_labels.append(row[1]) test_data = [] test_labels = [] for row in test_corpus: test_data.append(row[0]) test_labels.append(row[1]) # Create feature vectors vectorizer = TfidfVectorizer(min_df=4, max_df=0.9) # Train the feature vectors train_vectors = vectorizer.fit_transform(train_data) # Apply model on test data test_vectors = vectorizer.transform(test_data) # Perform classification with SVM, kernel=linear model = svm.SVC(kernel='linear') model.fit(train_vectors, train_labels) prediction = model.predict(test_vectors)
['Class_A' 'Class_A' 'Class_B' 'Class_B' 'Class_A' 'Class_A']
precision recall f1-score support
Class_A 0.50 0.67 0.57 3
Class_B 0.50 0.33 0.40 3
avg / total 0.50 0.50 0.49 6
文本分类很大程度上依赖于特征的质量与数量。当然,在使用任何机器学习训练模型时,一般来说,引入越多的训练数据总会是一个比较好的事。
在NLP中,一个很重要的领域是通过匹配文本对象找到相似体。它的主要应用有:自动拼写修正,重复数据删除,以及基因组分析,等等。
根据需求,我们有若干个文本匹配技术可供选择。这个部分会详细的描述比较重要的技术:
A. 莱文斯坦距离(Levenshtein Distance)—— 两个字符串之间的莱文斯坦距离可以被定义为:将一个字符串转换为另一个字符串时,所需的最小编辑次数。可允许的编辑操作有插入,删除,或者替换一个单字符。下面是对此算法的一个实现(高效的内存计算):
def levenshtein(s1,s2):
if len(s1) > len(s2):
s1,s2 = s2,s1
distances = range(len(s1) + 1)
for index2,char2 in enumerate(s2):
newDistances = [index2+1]
for index1,char1 in enumerate(s1):
if char1 == char2:
newDistances.append(distances[index1])
else:
newDistances.append(1 + min((distances[index1], distances[index1+1], newDistances[-1])))
distances = newDistances
return distances[-1]
print(levenshtein("analyze","analyse"))
1
B. 语音匹配(Phonetic Matching)—— 语音匹配的算法以一个关键词作为输入(如人名,地名等),然后产生出一个字符串,这个字符串与一组语音上(大致)相似的单词有关。这个技术在搜索超大文本语料库、修正拼写错误以及匹配相关名字时非常有帮助。Soundex和Metaphone是其中两个组主要的语音算法。Python里的Fuzzy模块可以用来为不同的单词计算soundex字符串,如:
import fuzzy
soundex = fuzzy.Soundex(4)
print soundex('ankit')
>>> “A523”
print soundex('aunkit')
>>> “A523”
C. 灵活的字符串匹配(Flexible String Matching)—— 一个完整的文本匹配系统里包括多种不同的算法,它们通过管道的方式组合起来,计算文本变化的种类(compute variety of text variations)。正则表达式对于这个任务也非常有用。其他常见的技术包括:精准字符串匹配,lemmatized matching,以及紧凑匹配(处理空格,标点,俚语等)
D. 余弦相似度—— 当文本以向量表示时,一个余弦相似度也可以用于衡量向量相似度。下面的代码将文本转化为向量(使用词频的方式),并且使用了余弦相似度来计算两个文本之间的相似度
import math from collections import Counter def get_cosine(vec1, vec2): common = set(vec1.keys()) & set(vec2.keys()) numerator = sum([vec1[x] * vec2[x] for x in common]) sum1 = sum([vec1[x]**2 for x in vec1.keys()]) sum2 = sum([vec2[x]**2 for x in vec2.keys()]) denominator = math.sqrt(sum1) * math.sqrt(sum2) if not denominator: return 0.0 else: return float(numerator) / denominator def text_to_vector(text): words = text.split() return Counter(words) text1 = 'This is an article on analytics vidhya' text2 = 'article on analytics vidhya is about natural language processing' vector1 = text_to_vector(text1) vector2 = text_to_vector(text2) cosine = get_cosine(vector1, vector2) >>> 0.62
指代消解是一个在句子里寻找单词(或短语)之间关系连接的过程。考虑这个句子“Donald went to John’s office to see the new table. He looked at it for an hour.”
人们可以很快的指出“he”指代的是Donald(而不是John),并且“it”指代的是table(而不是John’s office)。指代消解是NLP的一个组成部分,它会自动的完成这个工作。这个技术常被用于文件摘要,问答系统,以及信息提取。Stanford CoreNLP
提供了一个python wrapper:
https://github.com/Wordseer/stanford-corenlp-python
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。