从非结构化文本中提取特定信息

用 Python 做文本分析

文本分析的本质是从给定文本中获取高质量、有用信息的自动化过程,其一般步骤为:数据采集、数据清洗、文本挖掘分析、可视化分析
从非结构化文本中提取特定信息,如:实体、情感分析、关键词、主题、知识管理和发现(按主题对文档进行整理和分类以便于发现,然后向读者推荐与同一主题相关的其他文章,以提供个性化的内容推荐)、聚类…
在公司实际工作中,最好的大数据挖掘工程师一定是最熟悉和理解业务的人。对于大数据挖掘的学习心得,作者认为学习数据挖掘一定要结合实际业务背景、案例背景来学习,这样才是以解决问题为导向的学习方法。

文本分析的步骤

常见数据挖掘方法

导包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import re
import os
import numpy as np
import codecs
from time import time
from tqdm import tqdm
import re
from wordCloud import doWordCloud
import jieba
import jieba.posseg # 需要另外加载一个词性标注模块
from jieba.analyse import *
from sklearn.feature_extraction.text import TfidfVectorizer, CountVectorizer
from sklearn.decomposition import LatentDirichletAllocation
from sklearn.cluster import MiniBatchKMeans, SpectralCoclustering
from sklearn.metrics.cluster import v_measure_score
import string
from zhon.hanzi import punctuation
import gensim
import gensim.corpora as corpora
from snownlp import SnowNLP
import matplotlib.pyplot as plt
from colorama import init, Fore, Back, Style

数据采集

读入我们的数据文件,显式指定编码类型,以免出现乱码错误

1
2
3
4
# 读取无结构文档
for txt_file in tqdm(os.listdir(path)):
if not os.path.isdir(txt_file):
with codecs.open(path + "/" + txt_file, "r", "utf-8") as f:

数据预处理

数据分析/挖掘领域有一条金科玉律:“Garbage in, Garbage out”,做好数据预处理,对于取得理想的分析结果来说是至关重要的。本文的数据规整主要是对文本数据进行清洗,处理的条目如下:

  1. 去除文本噪声信息
    处理编码问题 (codecs)
    去掉停用词(没有实际意义的符号)
     标点符号:, 。! /、*+-
     特殊符号:❤❥웃유♋☮✌☏☢☠✔☑♚▲♪等
     中文停用词
    
  2. 将文档分割成句子
  3. 中文分词 tokenize (jieba, 了避免歧义和切出符合预期效果的词汇,笔者采取的是精确(分词)模式)
  4. 词性标注。snownlp 是不二选择,还可以使用 pattern
  5. 计算Bigrams:如果两个词经常一起毗邻出现,那么这两个词可以结合成一个新词 (未使用)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
stop_words = []
with codecs.open(stopWord_path, "r", "utf-8") as f:
for line in f:
stop_words.append(line.strip("\n").strip())

...
with codecs.open(path + "/" + txt_file, "r", "utf-8") as f:
file_content = f.read() # 以列表形式读取日志数据
file_content = re.sub(r"^\s+$", "", file_content) # 去除换行符和空白字符
# 去除字符串中的所有数字和字母
file_content = re.sub("[0-9a-zA-Z_-]+", "", file_content)
# 去除特殊中文标点
file_content = re.sub(r"[%s]+" % punctuation, "", file_content)
# 去除特殊英文标点
file_content = re.sub(r"[%s]+" % string.punctuation, "", file_content)
# if word >= u'\u4e00' and word <= u'\u9fa5':#判断是否是汉字

# # 仅保留名词或特定POS (HMM)
# s = SnowNLP(file_content)
# pos_list = s.tags # [(u'这个', u'r'), (u'东西', u'n')]
# word_list = [
# w for w, pos in pos_list if pos in ["n", "a", "v", "d"]
# ] # 'NN', 'ADJ', 'VB', 'ADV'
# # 关键词 (TextRank算法)
# kw_list = s.keywords(3)
# # 文档摘要 (TextRank算法)
# summmary_list = s.summary(3)

# jieba分词
word_list = jieba.cut(file_content)
# 去除停用词
word_list = [w for w in word_list if w not in stop_words]
segments.append(" ".join(word_list))

单词之间都已经被空格区分开了。下面我们需要做一项重要工作,叫做文本的向量化

不要被这个名称吓跑。它的意思其实很简单。因为计算机不但不认识中文,甚至连英文也不认识,它只认得数字。我们需要做的,是把文章中的关键词转换为一个个特征(列),然后对每一篇文章数关键词出现个数。

特征抽取

我们希望获取到的词汇,既能保留文本的信息,同时又能反映它们的相对重要性。

特征选取的方式:

  1. 构建词向量空间:统计文本词频,生成文本的词向量空间
  2. 权重策略——TF-IDF:使用TF-IDF发现特征词,并抽取为反映文档主题的特征

经过上面的步骤之后,我们就可以把文本集转化成一个矩阵。

TF-IDF向量化

1
2
3
4
5
6
7
vectorizer = TfidfVectorizer(stop_words=stop_words, sublinear_tf=True, max_df=0.5)
print("\nvectorizing...")
vector = vectorizer.fit_transform(segments)
# 查看结果
# print(vectorizer.vocabulary_) # 词表索引
# print(vectorizer.idf_) # 词表中每个词的IDF权重
# print(vector.toarray()) # 向量化结果 [[0. 0. 0.07097325 ... 0. 0. 0. ]...]

文本挖掘

关于文本挖掘方面的相关知识,请参看《数据运营|数据分析中,文本分析远比数值型分析重要!(上)》《在运营中,为什么文本分析远比数值型分析重要?一个实际案例,五点分析(下)》

本文的文本挖掘部分主要涉及:

1
2
3
4
5
6
7
8
9
高频词统计/关键词提取/关键词云
TF-IDF的关键词提取方法
文章标题聚类
文章内容聚类
文章内容LDA主题模型分析
词向量/关联词分析
ATM模型
词汇分散图
词聚类分析

用Python提取中文关键词

对于关键词提取,笔者没有采取词频统计的方法,因为词频统计的逻辑是:一个词在文章中出现的次数越多,则它就越重要。因而,笔者采用的是TF-IDF(termfrequency–inverse document frequency)的关键词提取方法:

  • 关键字抽取步骤:

1
2
3
4
5
6
7
8
9
10
11
12
# TextRank关键词提取
print(Fore.RED + "关键词提取结果如下:")
topk_list = textrank("\n".join(segments), withWeight=True, topK=100)
topk_keyword = []
with codecs.open('result/TextRank算法提取的 TOP100 关键词.txt', 'w', 'utf-8') as f:
for keyword, weight in topk_list:
f.write("{}\t{}\n".format(keyword, weight))
topk_keyword.append(keyword)
print(Fore.GREEN + "%s\t%s" % (keyword, weight))

# 选取TOP100关键词来绘制关键词云
doWordCloud(' '.join(topk_keyword), stop_words)

下面是笔者利用jieba在经预处理后的、近70MB的语料中抽取出的TOP100关键词(略)

统计性分析

只从文本中提取1000个最重要的特征关键词,然后停止

1
2
3
4
5
6
7
8
9
10
11
12
# CountVectorizer统计词频
n_features = 1000 # 提取1000个最重要的特征关键词
vectorizer = CountVectorizer(
max_df=0.95,
min_df=2,
max_features=n_features,
strip_accents="unicode",
stop_words=stop_words,
)
vector = vectorizer.fit_transform(segments)
vocab = vectorizer.vocabulary_
cipin = vector.toarray() # 词频统计 [[1 1 1 1 1 1 1 2]]

利用LDA进行主题抽取

刚才针对关键词的分类较为粗略,且人为划分,难免有失偏颇,达不到全面的效果。因此,笔者采用LDA主题模型来发现该语料中的潜在主题。关于LDA主题模型的相关原理,请参看《【干货】用大数据文本挖掘,来洞察“共享单车”的行业现状及走势》的第4部分。

LDA是一种典型的无监督(也就是说,我们事先不知道每段文本里面说的是啥,每个文本没有啥标签)、基于统计学习的词袋模型,即它认为一篇文档是由一组词构成的一个集合,词与词之间没有顺序以及先后的关系。一篇文档可以包含多个主题,文档中每一个词都由其中的一个主题生成。主题模型通过分析文本中的词来发现文档中的主题、主题之间的联系方式和主题的发展,通过主题模型可以使我们组织和总结无法人工标注的海量电子文档。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
def trainLDA(vector):
# 主题分析
n_topics = [5]
perplexityLst = [1.0]*len(n_topics)

#训练LDA并打印训练时间
lda_models = []
for idx, n_topic in enumerate(n_topics):
lda = LatentDirichletAllocation(n_components=n_topic,
max_iter=100,
learning_method='batch', # 'batch' 'online'
evaluate_every=200,
learning_offset=50.0,
random_state=0)
t0 = time()
lda.fit(vector)
# 收敛效果(perplexity)
perplexityLst[idx] = lda.perplexity(vector)
lda_models.append(lda)
print("# of Topic: %d, " % n_topics[idx])
print("done in %0.3fs, N_iter %d, " % ((time() - t0), lda.n_iter_))
print("收敛效果 Perplexity Score (越小越好) %0.3f" % perplexityLst[idx])

#打印最佳模型
best_index = perplexityLst.index(min(perplexityLst))
best_n_topic = n_topics[best_index]
best_model = lda_models[best_index]
print(Fore.RED + "Best # of Topic: ", best_n_topic)
print('\n')

# 定义以下的函数,把每个主题里面的前若干个关键词显示出来
def print_top_words(model, feature_names, n_top_words):
with codecs.open("result/主题簇.txt", "w", "utf-8") as f:
for topic_idx, topic in enumerate(model.components_):
f.write("Topic #%d:\n" % topic_idx)
f.write(
"\n".join(
[feature_names[i] for i in topic.argsort()[: -n_top_words - 1 : -1]]
)
)
f.write("\n")
print(Fore.RED + "Topic #%d:" % topic_idx)
print(
Fore.GREEN
+ " ".join(
[feature_names[i] for i in topic.argsort()[: -n_top_words - 1 : -1]]
)
)
print("\n")

# 打印主题建模的结果
n_top_words = 10 # 主题数量
tf_feature_names = vectorizer.get_feature_names()
print_top_words(lda, tf_feature_names, n_top_words)
# 将doc转换为话题分布的函数
doc_topic_dist = lda.transform(vector)

trainLDA(vector)

一般情况下,笔者将主题的数量设定为10个,经过数小时的运行,得到如下结果:

1
2
3
4
5
6
7
8
9
10
Topic #0:
学习 模型 使用 算法 方法 机器 可视化 神经网络 特征 处理 计算 系统 不同 数据库 训练 分类 基于 工具 一种 深度
Topic #1:
这个 就是 可能 如果 他们 没有 自己 很多 什么 不是 但是 这样 因为 一些 时候 现在 用户 所以 非常 已经
Topic #2:
企业 平台 服务 管理 互联网 公司 行业 数据分析 业务 用户 产品 金融 创新 客户 实现 系统 能力 产业 工作 价值
Topic #3:
中国 2016 电子 增长 10 市场 城市 2015 关注 人口 检索 30 或者 其中 阅读 应当 美国 全国 同比 20
Topic #4:
人工智能 学习 领域 智能 机器人 机器 人类 公司 深度 研究 未来 识别 已经 医疗 系统 计算机 目前 语音 百度 方面
  • 利用LDA做了主题抽取,不论是5个还是10个主题,可能都不是最优的数量选择。你可以根据程序反馈的结果不断尝试。

ATM模型

ATM模型(author-topic model)也是“概率主题模型”家族的一员,是LDA主题模型(Latent Dirichlet Allocation )的拓展,它能对某个语料库中作者的写作主题进行分析,找出某个作家的写作主题倾向,以及找到具有同样写作倾向的作家,它是一种新颖的主题探索方式。

词向量/关联词分析

词聚类

文档聚类

每一条评论都围绕一个或几个核心主旨进行阐述,这个核心主旨就是评论的中心思想。基于主旨话题分析技术获取文本主旨,每一条评论可能包含多个主旨话题,每一个话题包含多个主题词,通过评论的隶属度确定评论所属的主旨话题,通过主题词的分布确定主旨的含义。评论话题的具体分析流程如下,此过程要依次进行词法分析、文本过滤、主旨话题分析:

接下来采用的是基于谱联合聚类算法(Spectral Co-clustering algorithm)的文档聚类,这部分的原理涉及到艰深的数学和算法知识,可能会引起小伙伴们的阅读不适感,如果是这样,请快速跳过,直接看后面的操作和结果。

先将待分析的文本经TF-IDF向量化构成了词频矩阵,然后使用Dhillon的谱联合聚类算法(Spectral Co-clustering algorithm)进行双重聚类(Biclusters)。所得到的“文档-词汇”双聚类(Biclusters)会把某些文档子集中的常用词汇聚集在一起,由若干个关键词构成某个主题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 将聚类结果写入文件
def writeClusterResult(y):
result_dict = {}
for i in range(len(y)):
if y[i] not in result_dict:
result_dict[y[i]] = []
result_dict[y[i]].append(os.listdir(path)[:15][i].strip())

# 按value长度逆序排序,写入
result_list = sorted(result_dict.items(), key=lambda d: len(d[1]), reverse=True)
with codecs.open("result/prediction_cluster.txt", "w", encoding='utf-8') as target:
for item in result_list:
target.write("{}\n{}\n\n".format(item[0], "\n".join(item[1])))
print('聚类结果写入文件完成!!!')

# 文本聚类
n_clusters = 5
kmeans = MiniBatchKMeans(n_clusters=n_clusters, batch_size=20000, random_state=0)

print("MiniBatchKMeans...")
y_kmeans = kmeans.fit_predict(vector).tolist()
y_kmeans = np.array(y_kmeans)
writeClusterResult(y_kmeans)

参考

如何用Python从海量文本抽取主题?

如何用Python做词云

如何从 “用户评论”中挖掘业务价值

【干货】用大数据文本挖掘,来洞察“共享单车”的行业现状及走势

以虎嗅网4W+文章的文本挖掘为例,展现数据分析的一整套流程


Python文本挖掘
http://example.com/2019/09/10/2019-09-10-Python文本挖掘/
作者
NSX
发布于
2019年9月10日
许可协议