自然语言处理(NLP)

1.文本建模:基于词袋模型的文章关键词提取、相似度分析等;

2.词汇处理:中文分词、用Word2vec寻找近义词等;

3.主题模型:比较NMF、LSA、PLSA、LDA技术,建立“文档-主题-单词”的三层模型。


 文本建模

处理对象——整段文本或整篇文章

问题:如何将自然语言文本输入机器学习模型中?

解决方法:文本数字向量化。

方法1:词袋模型——对于每一个训练文本,它只考虑每种词汇在该训练文本中出现的频率。它的输入时一整段文本,输出是一个词汇计数向量,输入多个文本则输出多个向量,也可构成一个矩阵。

方法2:TF-IDF——也是一种词袋模型,作用是在一个由多个文章组成的文集中计算出每个单词对其所在文本的重要程度。因此输入必须是整个文集,而输出是一个“文章-单词”矩阵。(计算方法:)


scikit-learn的sklearn.feature_extraction.text包中提供了文本特征提取的相关封装类:CountVectorizer类和TfidfTransformer类。

CountVectorizer类:生成词袋模型矩阵

import numpy as np
from sklearn.datasets import fetch_20newsgroups
from sklearn.feature_extraction.text import CountVectorizer

count_vect = CountVectorizer(stop_words='english',max_df=0.8, min_df=0.001)
texts=["dog cat fish","dog cat cat","fish bird", 'bird fly fly fly','cat bird eat']
X_train_counts = count_vect.fit_transform(texts)
dict_word = {count_vect.vocabulary_[key]: key for key in count_vect.vocabulary_.keys()}
print(dict_word)
#{2: 'dog', 1: 'cat', 4: 'fish', 0: 'bird', 5: 'fly', 3: 'eat'}

words = np.array([dict_word[idx] for idx in range(len(dict_word))])
print(words)
#['bird' 'cat' 'dog' 'eat' 'fish' 'fly']

print('count',type(X_train_counts), X_train_counts.shape)
#count <class 'scipy.sparse.csr.csr_matrix'> (5, 6)
#矩阵大小为5x6,文本有5个,出现的词汇共有6个。

print(X_train_counts)
#输出为列表形式
  (0, 4)	1
  (0, 1)	1
  (0, 2)	1
  (1, 1)	2
  (1, 2)	1
  (2, 0)	1
  (2, 4)	1
  (3, 5)	3
  (3, 0)	1
  (4, 3)	1
  (4, 0)	1
  (4, 1)	1

print(X_train_counts.toarray())
#输出为矩阵形式
[[0 1 1 0 1 0]
 [0 2 1 0 0 0]
 [1 0 0 0 1 0]
 [1 0 0 0 0 3]
 [1 1 0 1 0 0]]

其中max_df:一个0~1的数值,指定词汇出现的文章数上限。比如0.8定义“在80%”以上文章中出现过的单词不被放入词袋模型矩阵中。本参数用于自动过滤一些普遍性的词汇。min_d指定词汇出现的文章数下限。在一些文本和词汇数较大的训练中,如果去掉这个约束,X_train_counts的大小为会发生很大变化。

TfidfTransformer类:生成TF-IDF矩阵

from sklearn.feature_extraction.text import TfidfTransformer
X_train_tfidf = TfidfTransformer(use_idf=True).fit_transform(X_train_counts)
print('ftidf',type(X_train_tfidf), X_train_tfidf.shape)
print(X_train_tfidf.toarray())

 输出结果为:ftidf <class 'scipy.sparse.csr.csr_matrix'> (5, 6)
[[0.                  0.50620441  0.60981846             0.          0.60981846            0.        ]
 [0.                  0.85660579  0.51597143             0.                    0.                  0.        ]
 [0.63871058           0.                    0.                  0.          0.76944707            0.        ]
 [0.21787435           0.                    0.                  0.                    0.          0.97597683]
 [0.48624042  0.48624042             0.          0.72604443            0.                   0.        ]]


词汇处理:中文与相似词挖掘

1.中文分词:基于规则匹配--字典搜索系统

                    基于统计--机器学习(如HMM)

 实践:python中jieba分词

2.Word2ver(一种无监督学习)

a.词嵌入:将文本单词转换成数字或数字向量,如,普通词袋模型中的词汇表;

b.Word2ver意图:将语义相近的词编号到相近位置;

c.原理:分析样本中所有单词周围的其他单词(上下文),将具有类似上下文的单词映射为相近的向量值;

d.根据对样本特征与标签提取机制得不同,Word2ver有两种实现机制:Continuous Bag-of-Words(CBOW,用上下文预测中心词)、Continuous Skip-Gram(用中心词预测上下文)

 

特征

标签

CBOW

have,been,to,boost

shown

Skip-Gram

shown

have,been,to,boost

e.Skip-Gram样本抽取

   Word2ver的一种实现是以单隐藏层的神经网络作为学习模型的,Skip-Gram的训练时一个逐一扫描文本中的单词生成样本并投入神经网络的过程。

文本扫描

训练样本

word and phrase embedings have been...

word>and  word>phrase

word and phrase embedings have been...

and>word and>phrase and>embedings

word and phrase embedings have been...

phrase>word phrase>and phrase>embedings phrase>have

生成的样本没有考虑上下文与中心词的前后顺序,单个中心词所产生的四个样本之间完全并列。将样本中的这些单词放入神经网络之前采用1-of-N对它们进行向量化编码。假设词汇表按顺序有word,and,phrase,embedings,have,been,那么word的编码为(1,0,0,0,0),第一个训练样本就是(1,1,0,0,0)。将所有样本以 1-of-N编码放入单隐藏层的神经网络训练后神经网络的隐藏层就成为了最终的Word2ver转换矩阵。

输入1-of-N向量X隐藏层权值=隐藏层向量X输出层权值=输出1-of-N向量

       这样在神经网络的隐藏层就形成了一个从输入层获得的映射,一般情况下隐藏层向量维度远小于输入与输出层维度数(也就是单词总数),因此达到了将单词转换为数字向量的目的。此外,由于含义相近的单词(输入层)会产生类似的上下文(输出层),进而产生相近的隐藏层向量。因此,直接将每个单词放入输入层,得到的隐藏层向量就成为了Word2vec嵌入。(代码2是一个实例)


几个例子

代码1:以CountVectorizer类和TfidfTransformer类自动提取“20newsgroups”中前20个邮件的5个最主要关键字

import numpy as np
from sklearn.datasets import fetch_20newsgroups
twenty_train = fetch_20newsgroups(subset='train')#读取邮件数据库

from sklearn.feature_extraction.text import CountVectorizer
count_vect = CountVectorizer(stop_words='english', max_df=0.8, min_df=0.001)#生成对象
email_data = ["\n".join(email.split("\n")[5:]) for email in twenty_train.data]
X_train_counts = count_vect.fit_transform(email_data) #词袋模型矩阵
dict_word = {count_vect.vocabulary_[key]: key for key in count_vect.vocabulary_.keys()}
words = np.array([dict_word[idx] for idx in range(len(dict_word))])
print('count',type(X_train_counts), X_train_counts.shape)

from sklearn.feature_extraction.text import TfidfTransformer
X_train_tfidf = TfidfTransformer(use_idf=True).fit_transform(X_train_counts)
print('ftidf',type(X_train_tfidf), X_train_tfidf.shape)


print("tf-idf created!")

def get_keywords(doc_index):提取单个邮件的关键字
    KEYWORD_COUNT = 5
    tfidf_row = X_train_tfidf.getrow(doc_index).toarray().flatten()
    maxn_index = np.argsort( tfidf_row)[-KEYWORD_COUNT:]
    maxn_index = np.flip(maxn_index, 0)
    maxn_value = tfidf_row[maxn_index]      #最高TF-IDF值
    maxn_word = words[maxn_index]
    for i in range(KEYWORD_COUNT):
        print("%s:%0.3f, "%(maxn_word[i], maxn_value[i]), end="")
    print("")

for i in range(20):      #输出前20个邮件的5个最主要关键词
    print("email %s: "%i, end="")
    get_keywords(i)

输出结果:

count <class 'scipy.sparse.csr.csr_matrix'> (11314, 12490)
 ftidf <class 'scipy.sparse.csr.csr_matrix'> (11314, 12490)
 tf-idf created!
 email 0: car:0.483, 60s:0.223, 70s:0.219, enlighten:0.211, bumper:0.209,
 email 1: poll:0.313, washington:0.292, experiences:0.257, clock:0.226, add:0.197,
 ......(不全部复制了)
 email 18: circuits:0.366, voltage:0.334, adc:0.214, able:0.201, acquisition:0.194,
 email 19: ncd:0.405, boots:0.292, tcp:0.276, ip:0.270, terminal:0.249,

这里的数据twenty_train.data是list格式,当用这个代码处理自己的文章数据时,将多个文章以list格式保存就行。

content=[]
for i in range(23): 
 text = open(r'D:\\python3.6.5\\pycharm\\main\\output\\txt\\'+str(i)+'.txt','r',encoding='utf-8').read()
 content.append(text)

 

代码2:scikit-learn中没有封装Word2ver的功能,而是以Gensim库作为开发工具。

import numpy as np
from sklearn.datasets import fetch_20newsgroups
twenty_train = fetch_20newsgroups(subset='train')
email_data = [" ".join(email.lower().split("\n")[5: -3]).split() for email in twenty_train.data]

sentences = []
for doc in email_data:
    sentence = [ word for word in doc if word.isdigit() or word.isalpha()]
    sentences.append(sentence)

from gensim.models.word2vec import Word2Vec

model = Word2Vec(sentences, size=100, window=10, min_count=4000, workers=4)
print(model.wv.vocab)
vocab = list(model.wv.vocab)
X = model[vocab]


from sklearn.manifold import TSNE

tsne = TSNE(n_components=2)
X_tsne = tsne.fit_transform(X)

import matplotlib.pyplot as plt
from adjustText import adjust_text

fig, ax = plt.subplots()
plt.plot(X_tsne[:, 0], X_tsne[:, 1], 'bo')
texts = []
for x, y, s in zip(X_tsne[:, 0], X_tsne[:, 1], vocab):
    texts.append(plt.text(x, y, s))
adjust_text(texts,arrowprops=dict(arrowstyle='->', color='red'))
plt.show()