【聊天机器人】NLP知识

一、文本处理工具库:

1、NLTK

python上著名的自然语言处理库。自带语料库,词性分类看;自带分类,分词,等功能。有强大的社区支持,和N多的简单版wrapper(eg.TextBlob)

2、TextBlob 包–英文分析

TextBlob是一个用于处理文本数据的Python库。对NLTK的封装。它为常见的自然语言处理(NLP)任务提供了一个简单的API,例如词性标注,名词短语提取,情感分析,分类,翻译等。结果:(-1,1)GitHub链接:https://github.com/sloria/TextBlob

3、SnowNLP包–中文分析

SnowNLP是一个python写的类库,可以方便的处理中文文本内容,是受到了TextBlob的启发而写的,由于现在大部分的自然语言处理库基本都是针对英文的,于是写了一个方便处理中文的类库,并且和TextBlob不同的是,这里没有用NLTK,所有的算法都是自己实现的,并且自带了一些训练好的字典。SnowNLP用于情感分析方面(现在训练数据主要是买卖东西时的评价,所以对其他的一些可能效果不是很好),而且它的分词能力并没有jieba强大。

4、金融相关API:

彭博社Bloomberg API(30k一个月)、Thomson Reuters(15K)、 Wind、大智慧、同花顺数据API。

二、NLTK的介绍

1、模块

在这里插入图片描述

2、NLTK使用

from nltk.corpus import brown  #从nltk语料库中引入brown
brown.categories()  #brown中有哪些类型
print(len(brown.sents()))  #brown中的句子总数
print(len(brown.words()))  #brown中的单词总数

三、文本处理流程

在这里插入图片描述

1、分词:

分词方式:
(1)启发式分词:Heuristic。基于查字典进行分词。类比于relu_based
(2)机器学习/统计方法:HMM、CRF、nn、LSTM、RNN等。基于data进行分词(HMM、CRF传统模型是由公式计算是一个单词的概率是多少(概率P);nn、LSTM、RNN深度学习是黑盒子)。类比于Generative
中文的字(1000个覆盖约92%,2000字覆盖98%以上,3000字则已到99%)相当于英文的字母(26个)
(3)案例
英文分词:Tokenize

>>> import nltk
>>> sentence = “hello, world"
>>> tokens = nltk.word_tokenize(sentence)
>>> tokens
['hello', ',', 'world']

中文分词库:jieba

import jieba
seg_list = jieba.cut("我来到北北京清华⼤大学", cut_all=True)
print "Full Mode:", "/ ".join(seg_list) # 全模式
seg_list = jieba.cut("我来到北北京清华⼤大学", cut_all=False)
print "Default Mode:", "/ ".join(seg_list) # 精确模式
seg_list = jieba.cut("他来到了了⽹网易易杭研⼤大厦") # 默认是精确模式
print ", ".join(seg_list)
seg_list = jieba.cut_for_search("⼩小明硕⼠士毕业于中国科学院计算所,后在⽇日本京都⼤大学深造")
# 搜索引擎模式
print ", ".join(seg_list)

【全模式】: 我/ 来到/ 北北京/ 清华/ 清华⼤大学/ 华⼤大/ ⼤大学
【精确模式】: 我/ 来到/ 北北京/ 清华⼤大学
【新词识别】:他, 来到, 了了, ⽹网易易, 杭研, ⼤大厦
(此处,“杭研”并没有在词典中,但是也被Viterbi算法识别出来了了)
【搜索引擎模式】: 小明, 硕士, 毕业, 于, 中国, 科学, 学院, 科学院, 中国科学院, 计算,计算所, 后, 在, 日本, 京都, 大学, 日本京都大学, 深造
特殊符号:

from nltk.tokenize import word_tokenize
tweet = 'RT @angelababy: love you baby! :D http://ah.love #168cm' 
print(word_tokenize(tweet))

输出:
[‘RT’, ‘@’, ‘angelababy’, ‘:’, ‘love’, ‘you’, ‘baby’, ‘!’, ‘:’, ’D’, ‘http’, ‘:’, ‘//ah.love’, ‘#’, ‘168cm’]
输出内容@+人名、网址等都是不需要的;:D表情文本没有表示出其含义。可以利用正则表达式re,对这些内容进行删除或转化。
(4)正则表达式对照表:
http://www.regexlab.com/zh/regref.htm

import re
#识别表情O(∩_∩)O
emoticons_str = r"""
        (?:
            [:=;] #眼睛
            [oO\-]? #鼻子
            [D\)\]\(\]/\\OpP] #嘴 
         )"""
#识别特殊符号
regex_str = [
    emticons_str,
    r'<[^>]+>', # HTML tags
    r'(?:@[\w_]+)', # @某人
    r"(?:\#+[\w_]+[\w\'_\-]*[\w_]+)", # 话题标签 
    r'http[s]?://(?:[a-z]|[0-9]|[$-_@.&amp;+]|[!*\(\),]|(?:%[0-9a-f][0-9a-f]))+', # URLs
    r'(?:(?:\d+,?)+(?:\.?\d+)?)', # 数字
    r"(?:[a-z][a-z'\-_]+[a-z])", # 含有-和'的单词
    r'(?:[\w_]+)', # 其他
    r'(?:\S)' # 其他
]

注:虚词:语义处理里没有用,但对于文本表面(如写作水平等)是有用的。分词可根据需要保留
(5)社交网络中的分词案例

tokens_re = re.compile(r'('+'|'.join(regex_str)+')', re.VERBOSE | re.IGNORECASE) 
emoticon_re = re.compile(r'^'+emoticons_str+'$', re.VERBOSE | re.IGNORECASE)
def tokenize(s):    
    return tokens_re.findall(s)
def preprocess(s, lowercase=False):    
    tokens = tokenize(s)    
    if lowercase:        
        tokens = [token if emoticon_re.search(token) else token.lower() for token in tokens]    
        return tokens
tweet = 'RT @angelababy: love you baby! :D http://ah.love #168cm' 
print(preprocess(tweet)) # ['RT', '@angelababy', ':', 'love', 'you', 'baby', # ’!', ':D', 'http://ah.love', '#168cm']

2、英文的词形还原:

(1)将不同的词性(但意思相同)规范为同一个表达式。如
Inflection时态变化(不要影响词性):walk ==> walking ==> walked
derivation引申(词性的变化):nation(noun) ==> national(adjective) ==> nationalize(veb)
(2)词形归一化方法
Stemming词干提取:一般是吧不影响词性的时态的小尾巴砍掉
walking ==> walk
walked ==> walk
Lemmatization词形归一:把各种类型的词的变形(查表),都归为一个形式
went ==> go
are ==> be
(3)代码实现

  • Stemming代码实现:
from nltk.stem.porter import PorterStemmer
porter_stemmer = PorterStemmer()
print(porter_stemmer.stem('maximum'))
print(porter_stemmer.stem('presumably'))
print(porter_stemmer.stem('multiply'))
print(porter_stemmer.stem('provision'))

输出:
maximum
presum
multipli
provis

from nltk.stem import SnowballStemmer
snowball_stemmer = SnowballStemmer("english")
print(snowball_stemmer.stem('maximum'))
print(snowball_stemmer.stem('presumably'))
print(snowball_stemmer.stem('multiply'))
print(snowball_stemmer.stem('provision'))

maximum
presum
multipli
provis

from nltk.stem.lancaster import LancasterStemmer 
lancaster_stemmer = LancasterStemmer()
print(lancaster_stemmer.stem('maximum'))
print(lancaster_stemmer.stem('presumably'))
print(lancaster_stemmer.stem('multiply'))
print(lancaster_stemmer.stem('provision'))

输出:
maxim
presum
multiply
provid

from nltk.stem.porter import PorterStemmer
p = PorterStemmer()
print(p.stem('went'))
print(p.stem('wenting'))

输出:
went
went

  • Lemma代码实现:
from nltk.stem import WordNetLemmatizer
wordnet_lemmatizer = WordNetLemmatizer()
print(wordnet_lemmatizer.lemmatize('dogs'))
print(wordnet_lemmatizer.lemmatize('churches'))
print(wordnet_lemmatizer.lemmatize('aardwolves'))
print(wordnet_lemmatizer.lemmatize('abaci'))
print(wordnet_lemmatizer.lemmatize('hardrock'))

输出:
dog
church
aardwolf
abacus
hardrock

一般先使用词性标注判断出词性,在使用Lemma进行词形还原。
1、对于Went:v.go的过去是;n.英文名:温特。直接使用Lemma无法正确还原
2、加入词性后即可准确的进行词形还原

#没有POS Tag,默认为NN 名词
wordnet_lemmatizer.lemmatize('are')
wordnet_lemmatizer.lemmatize('is')

#加上POS Tag
wordnet_lemmatizer.lemmatize('are',pos='v')
wordnet_lemmatizer.lemmatize('is',pos='v')
  • NLTK词性标注POS Tag:
import nltk
text = nltk.word_tokenize('what does the fox say')
print(text)
print(nltk.pos_tag(text))

输出:
[‘what’, ‘does’, ‘the’, ‘fox’, ‘say’]
[(‘what’, ‘WDT’), (‘does’, ‘VBZ’), (‘the’, ‘DT’), (‘fox’, ‘NNS’), (‘say’, ‘VBP’)]

3、Stopwprds停用词:

1、全体stopwords列表 http://www.ranks.nl/stopwords
2、nltk.download(‘stopwords’)
3、代码实现

from nltk.corpus import stopwords 
# 先token,得到一个word_list 
# ... 
# 然后filter
filtered_words = [word for word in word_list if word not in stopwords.words('english')]

4、总结

文本的一般预处理流程:
除了NLTK外,斯坦福的:CORENLP库指出中、英、西班牙文
在这里插入图片描述

四、文本表示----把句子按数字向量表达

机器学习:通过设定的规则去构造特征。

1、True-False

步骤:建立字典、句子单词出现即在字典对应记为1
缺点:没有考虑词的重要性和顺序关系

2、用元素频率形式表示:

(1)含义
词表位置需要固定,保持在一个空间内。文本表示相当于,每个词都进行one-hot,在累加。
(2)优点:

  • 所有句子的向量长度都是一样的,便于后期的机器学习
  • 向量的长度就是处理文本中不同的词的个数
    (3)缺点:
    没有考虑文本中词与词之间的顺序关系。
    (4)步骤:
  • 建立字典
  • 句子单词出现即在字典对应位置+1
  • 每个句子的维度变为1*字典维度。语料库字典是很重要的。
    如,
    he,he,he,we are happy
    he,he,he,we are
    you work
    每个句子对应的词向量:
    在这里插入图片描述
    (5)代码实现:
import nltk
from nltk import FreqDist
#先做一个词库
corpus = 'this is my sentence ' 'this is my life' 'this is the day'
tokens = nltk.word_tokenize(corpus)
print(tokens)

#使用NLTK的FreqDist统计一下文字出现的频率
fdist = FreqDist(tokens)
print(fdist)
print(fdist['is'])
#输出最常见的50个单词
standard_freq_vector = fdist.most_common(50)
size = len(standard_freq_vector)
print(size)
print(standard_freq_vector)
#按照词频大小,生成{词:位置索引}字典
word_to_count = {}
for term in standard_freq_vector:
    word_to_count[term[0]] = term[1]
print(word_to_count)
#对于任意句子
sentence = 'this is cool'
#分词
tokens = nltk.word_tokenize(sentence)
sentence_vector = []
for word in tokens:
    sentence_vector.append(word_to_count.get(word,0))

3、TF-IDF

单用频率表示,无法真实表达出词在该文本中的重要性,如某些公有词虽然在文本中出现的次数多,可能在整个语料库中都出现很多。
(1)TF: Term Frequency, 衡量⼀个term在⽂档中出现得有多频繁。
TF(t) = (t出现在⽂档中的次数) / (⽂档中的term总数)。
(2)IDF: Inverse Document Frequency, 衡量⼀个term有多重要。有些词出现的很多,但是明显不是很有卵⽤。⽐如’is’,’the‘,’and‘之类
的。为了平衡,我们把罕见的词的重要性(weight)搞⾼,把常见词的重要性搞低。
IDF(t) = log_e(⽂档总数 / 含有t的⽂档总数).
(3)TFIDF=TFIDFTF-IDF = TF * IDF
(4)例子:

现在有10M的文档,baby出现在其中的1000个文档中。
IDF(baby) = log(10000000/1000) = 4
那么来了一个文档有100个单词,其中单词baby出现3次。
TF(baby) = (3/100) = 0.03
所以,TF-IDF(baby) = TF(baby) * IDF(baby) = 0.03 * 4 = 0.12

(5)代码实现

from nltk.text import TextCollection
#首先, 把所有的⽂文档放到TextCollection类中。 
#这个类会⾃自动帮你断句句, 做统计, 做计算
corpus = TextCollection(['this is sentence one',
                        'this is sentence two',
                        'this is sentence three'])
#直接就能算出tfidf 
#(term:一句话中的某个term,text:这句话) 
print(corpus.tf_idf('this','this is sentence four'))

五、NLP经典案例

1、情感分析

案例一:简单的情感词字典

(1)介绍
一句话分词后通过情感词字典,对每个词进行打分,最后得出句子情感得分。例如:like:1,good:2,bad:-2,terrible:-3。这代表着like的情感正面程度分值为1,good的情感正面程度为2,bad的情感正面程度为-2,terrible的情感正面程度为-3。
AFINN-111就是一个典型的情感字典:http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6010
下载后如图:
在这里插入图片描述
(2)使用NLTK完成简单的情感分析代码实现:

#NLTK进行情感分析
import nltk


#建立情感字典
sentiment_dictionary ={}
for line in open('you_file\AFINN-111.txt'):
    word,score = line.split(' ')
    sentiment_dictionary[word] = int(score)  #构建{单词:得分}字典

sentence_1 ='i love you!'
sentence_2 ='i hate you!'  
#分词  
word_list1 = nltk.word_tokenize(sentence_1)
word_list2 = nltk.word_tokenize(sentence_2)
#遍历每个句子,把每个词的情感得分相加,不在情感字典中的词分数全部置0
s1_score = sum(sentiment_dictionary.get(word,0) for word in word_list1)
s2_score = sum(sentiment_dictionary.get(word,0) for word in word_list2)
print('我是句子'+sentence_1+'的正面情感得分:',s1_score)
print('我是句子'+sentence_2+'的正面情感得分:',s2_score)

输出结果:
我是句子i love you!的正面情感得分:3
我是句子i hate you!的正面情感得分:-3
(3)存在的问题:

  • 出现网络新词不在字典里怎么办?
  • 出现特殊词汇怎么办?
  • 更深层的语义怎么理解?

案例二:配上ML的情感分析

from nltk.classify import NaiveBayesClassifier
from nltk import word_tokenize
#简单手造的训练集
s1 = 'i am a good boy'
s2 = 'i am a handsome boy'
s3 = 'he is a bad boy'
s4 = 'he is a terrible boy'

#预处理后得到字典类型:
#key表示fname对应句子中出现的单词
#value表示每个文本单词对应的值
def preprocess(s):
    return {word:True for word in s.lower().split()}

#把训练集做成标准形式
training_data = [[preprocess(s1),'pos'],
                [preprocess(s2),'pos'],
                [preprocess(s3),'neg'],
                [preprocess(s4),'neg'],]

#采用朴素贝叶斯模型训练
model = NaiveBayesClassifier.train(training_data)
new_s1 = 'i am a good girl'
new_s2 = 'she is a terrible girl'
#输出预测结果
print('我在预测 '+new_s1+' 结果是:',model.classify(preprocess(new_s1)))
print('我在预测 '+new_s2+' 结果是:',model.classify(preprocess(new_s2)))

输出结果:
我在预测 i am a good girl 结果是: pos
我在预测 she is a terrible girl 结果是: neg

2、文本相似度

在句子被向量化后,我们根据余弦定理便可计算出句子的相似度
similarity=cos(θ)=ABAB\displaystyle similarity=cos( \theta ) =\frac{A\cdot B}{\| A\| \cdot \| B\| }

3、文本分类

文本表示称为向量后,通过及其学习模型,对分类任务进行预测。
在这里插入图片描述

五、深度学习文本表示:让机器去学习句子表达向量,而不是人为的规定特征

1、词向量表示工具:

gensim:word2vec、tensorflow

2、词编码:

英文、ASII编码(英文)、Unicode中文、自定义一个index编码、电报编码等,在进行深度学习时,输入的词约原始越好。

3、词向量表达方式

(1)Auto-Encoder
无监督学习,工业上主要用于降维。
在这里插入图片描述
**(2)Word2vec:**单词本身不是单独存在于句子中间的,而是由周围的词决定。

4、文本相似度的表示:

(1)Full document: TF-IDF
(2)Window: co-occurrence matrix + SVD(2个单词都出现的矩阵计算)不光关注在文章中出现的频率,重点关注词与词之间的相关性
a.贡献计数表
在这里插入图片描述
b.构建SVD矩阵:把任何形状的矩阵, 都转化为三个向量相乘的矩阵。个性特征:每一行代表自己的特有特征
在这里插入图片描述
c.将特征值取出,放在空间平面里。做词向量表达
在这里插入图片描述
应为SVD计算复杂度很高,所以想到用两边定义中间的形式,去表达。

5、Word2vec

(1)Skip-gram:数据量少,经常出现稀有单词时适合使用

(2)CBOW:比Skip-gram快,对于高频词的预测精度高

在这里插入图片描述
在这里插入图片描述

(3)步骤:

  • 文本预处理:去停用词、小写化、用正则表达式去除特殊符号或文本。进行分词。文本处理后的形式,输入到Word2Vec中:[[‘hello’, ‘how’], [‘fine’, ‘thank’]]
  • 文本表示
    1)使用from gensim.models import word2vec库;
    2)设定参数(num_features最多多少个不不同的features、min_word_count⼀一个word,最少出现多少次 才被计⼊入、多少thread⼀一起跑(快⼀一点⼉儿)、embeding_size、windom_size前后观察多⻓长的“语境)
    3)跑模型。生成每个次的词向量
model = word2vec.Word2Vec(sentences, size=size, workers=num_workers, size=num_features, min_count = min_word_count, window = window)

(4)应用:

  • 输出单词在空间中的位置向量。词向量表示
model['computer']

输出:
array([-0.00449447, -0.00310097, 0.02421786, …], dtype=float32)

  • 求两个词的senmatics相似度
model.similarity('woman', 'man')

输出:0.73723527

  • 通过语义,利用空间距离计算。单词和概率。
#woman + king - man = queen
model.most_similar(positive=['woman', 'king'], negative=['man'])

输出:
[(‘queen’, 0.50882536), …]

六、机器人应用:intents

在这里插入图片描述
“你好吗?”——>向量,“你最近怎么样”——>向量
通过word2vec、tf-idf等形式,把句子把句子表达为向量形式。
无监督学习:KNN做聚类,计算两个句子的距离是否小于某个阈值,如果小於则认为属于一类,可以使用同一个回答
有监督学习:分类问题。
根据网上的语料库,输入词向量,确定它属于那个标签。
在这里插入图片描述

七、思考:

这个时候你会发现,我们的vec是针对每个word的。而我们的训练集是sen和label互相对应的,
工业上,到了这一步,有三种解决方案:
1、平均化一个句子里所有词的vec。
sen_vec = [vec, vec, vec, …] / n
2、排成一个大matrix (M * N),等着CNN进行特征提取。
[ vec | vec | vec | vec | … ]
3、用Doc2Vec。这是基于句子的vec,跟word2vec差不多思路路,用起来也差不多。
只对长篇 大文章效果好。对头条新闻,twitter这种的东西,就不行了。每一篇的就太少。
具体可以看gensim。

参考文献:文本为七月在线《自动聊天机器人项目班》学习笔记

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章