目录
步骤
SpaCy
Textacy——Text Analysis for Cybersecurity
Networkx
Dateparser
导入库
写出页面的名称
编辑
自然语言处理
词性标注
可能标记的完整列表
依存句法分析(Dependency Parsing,DEP)
可能的标签完整列表
实例理解POS与DEP
可视化注释
Spacy还可执行命名实体识别
可能的所有标签的完整列表
Spacy图形工具
实体和关系抽取
构建图表
网络图
使用Python和自然语言处理构建知识图谱。
知识图谱被视为自然语言处理领域的一部分,因为要构建“知识”,需要进行“语义增强”过程。由于没有人想要手动执行此任务,因此我们需要使用机器和自然语言处理算法来完成此任务。
我们将解析维基百科并提取一个页面,用作本数据集。
俄乌战争-维基百科
"spaCy" 这个名称是从 "Space"(空间) 这个词汇中来的,它代表了 spaCy 设计的初衷,即为了提供一个轻量级、高性能的自然语言处理(NLP)库。
SpaCy是一个自然语言处理(NLP)库和工具包,用于处理和分析文本数据。它被设计成高效、快速且易用的工具,具有许多功能,包括分词、命名实体识别、依存关系分析、文本分类等。SpaCy支持多种语言,并提供了预训练的词向量模型。它广泛用于文本挖掘、信息检索、自动化文本分类、情感分析、实体识别、机器翻译等领域。
Textacy的名称来源于"Text Analysis for Cybersecurity"(网络安全文本分析),这个名称强调了该库最初的用途,即在网络安全领域中对文本数据进行分析。然而,随着时间的推移,Textacy的功能扩展到了更广泛的自然语言处理和文本挖掘任务,包括情感分析、实体识别、主题建模等,因此它的名称也逐渐演化成了更通用的文本分析工具。
NetworkX是一个用于创建、操作和研究复杂网络(图)的Python库。它提供了丰富的功能和工具,使用户能够轻松地构建、分析和可视化各种类型的网络,包括社交网络、网络拓扑、生物网络、交通网络等。
"dateparser" 是一个Python库,用于解析日期和时间字符串。它的主要功能是将各种格式的日期和时间字符串转换成Python的datetime对象,以便在程序中进行日期和时间的处理和计算。
## for data
import pandas as pd #1.1.5
import numpy as np #1.21.0
## for plotting
import matplotlib.pyplot as plt #3.3.2
## for text
import wikipediaapi #0.5.8
import nltk #3.8.1
import re
## for nlp
import spacy #3.5.0
from spacy import displacy
import textacy #0.12.0
## for graph
import networkx as nx #3.0 (also pygraphviz==1.10)
## for timeline
import dateparser #1.1.7
Wikipedia-api是一个Python库,可轻松解析Wikipedia页面。我们将使用这个库来提取所需的页面,但会排除页面底部的所有“注释”和“参考文献”内容。
topic = "Russo-Ukrainian War"
wiki = wikipediaapi.Wikipedia('en')
page = wiki.page(topic)
txt = page.text[:page.text.find("See also")]
txt[0:500] + " ..."
topic = "Russo-Ukrainian War"
:在这一行中,定义了一个名为 topic
的变量,其中存储了要查询的维基百科主题,即 "Russo-Ukrainian War"(俄乌战争)。wiki = wikipediaapi.Wikipedia('en')
:在这一行中,创建了一个名为 wiki
的维基百科API的实例,使用了英语语言版('en'表示英语)。page = wiki.page(topic)
:这一行使用 wiki
实例的 page
方法来获取与主题 topic
相关的维基百科页面。这将返回一个包含页面内容的对象,存储在名为 page
的变量中。txt = page.text[:page.text.find("See also")]
:这一行代码从获取的维基百科页面文本中提取了感兴趣的部分。它使用了字符串切片和 .find()
方法,首先查找文本中 "See also"(通常表示相关链接的部分)的位置,然后将文本截断到这个位置之前,从而得到了页面的一部分文本。这部分文本存储在名为 txt
的变量中。txt[0:500] + " ..."
:最后一行代码将前500个字符的文本内容提取出来,然后附加了 " ...",以表示文本的截断。这个结果存储在 txt
变量中,它包含了从维基百科页面提取的前500个字符的内容。#python -m spacy download en_core_web_sm
nlp = spacy.load("en_core_web_sm")
doc = nlp(txt)
#python -m spacy download en_core_web_sm
:这是一个注释行,用于表示在终端或命令行中执行的操作。它指示用户下载spaCy的英语语言模型"en_core_web_sm"。这个模型包括了一些用于处理英语文本的语言数据和算法。nlp = spacy.load("en_core_web_sm")
:在这一行代码中,首先导入了spaCy库(前提是已经安装了spaCy库)。然后,使用spacy.load()
函数加载了之前下载的英语语言模型"en_core_web_sm"。加载后的模型被存储在名为nlp
的变量中,以便后续对文本数据进行处理。doc = nlp(txt)
:在这一行代码中,使用已加载的模型nlp
对文本数据txt
进行处理。nlp(txt)
将文本数据传递给已加载的模型,返回一个Doc
对象,其中包含了对文本进行了分词、词性标注、命名实体识别等自然语言处理任务的结果。这个Doc
对象存储了文本的各种信息,可以用于进一步的文本分析和处理。看SpaCy将文本分成了多少个句子:
lst_docs = [sent for sent in doc.sents]
print("tot sentences:", len(lst_docs))
lst_docs = [sent for sent in doc.sents]
:这一行代码使用了列表推导式(List Comprehension)来遍历doc
对象中的每个句子,并将它们存储在一个名为lst_docs
的列表中。列表推导式的语法是[expression for item in iterable]
,在这里,expression
是用于生成列表元素的表达式,item
是迭代的每个元素,iterable
是要迭代的对象。因此,这行代码遍历doc.sents
,它是doc
对象中句子的一个生成器(generator),并将每个句子添加到lst_docs
列表中。
即用适当的语法标签标记句子中的每个单词的过程
模型还会尝试理解单词对之间的关系。
i = 3
list_docs[3]
检查 NLP 模型预测的 POS 和 DEP 标签
for token in lst_docs[i]:
print(token.text, "-->", "pos: "+token.pos_, "|", "dep: "+token.dep_, "")
token.text
:token
对象的text
属性表示词汇的原始文本内容,即单词或标点符号的字符串。"-->"
:这部分代码只是一个字符串,用于分隔词汇信息的不同部分,以便输出更易读。"pos: "+token.pos_
:token
对象的pos_
属性表示词汇的词性(Part-of-Speech,POS)。该部分将词汇的词性标签添加到输出中,例如:"pos: NOUN" 表示名词。"|"
:这部分代码只是一个字符串,用于分隔不同词汇信息。"dep: "+token.dep_
:token
对象的dep_
属性表示词汇与句子中其他词汇的依存关系。该部分将词汇的依存关系标签添加到输出中,例如:"dep: nsubj" 表示名词主语。SpaCy提供了一个图形工具来可视化这些注释
from spacy import displacy
displacy.render(lst_docs[i], style="dep", options={"distance":100})
displacy.render(lst_docs[i], style="dep", options={"distance":100})
:这是用于渲染句子依存关系图的函数调用。它包括以下参数:
lst_docs[i]
:这是要可视化的文本数据,通常是一个Doc
对象,或者在这里是句子的Doc
对象,表示要可视化的句子。
style="dep"
:这个参数指定了可视化的样式。在这里,我们选择了"dep",表示依存关系图。
options={"distance":100}
:这是一个字典参数,用于配置可视化选项。在这里,我们设置了"distance"参数,以控制词汇之间的水平距离。较大的距离可以使图更易于阅读。您可以根据需要自定义其他可视化选项。
for ent in lst_docs[i].ents:
print(tag.text, f"({tag.label_})")
print(tag.text, f"({tag.label_})")
:在每次迭代中,使用 print()
函数打印每个实体的文本内容和实体类型标签。
tag.text
:这是实体对象的 text
属性,表示实体的原始文本内容。
f"({tag.label_})"
:这是一个格式化字符串,用于将实体的类型标签添加到输出中。在字符串中使用了f
开头的字符串字面值,它允许在字符串中插入表达式,这里插入了实体的类型标签,标签位于括号中。
花括号 {}
在格式化字符串中用于表示占位符,可以在运行时将变量或表达式的值插入到字符串中。
在spaCy中,实体(命名实体)对象通常包含两个重要的属性:ent.text
和 ent.label_
,它们分别表示实体的文本内容和实体类型标签。
displacy.render(lst_docs[i], style="ent")
对于每个句子,我们将提取主语和宾语以及它们的修饰语、复合词和它们之间的标点符号。
## extract entities and relations
dic = {"id":[], "text":[], "entity":[], "relation":[], "object":[]}
for n,sentence in enumerate(lst_docs):
lst_generators = list(textacy.extract.subject_verb_object_triples(sentence))
for sent in lst_generators:
subj = "_".join(map(str, sent.subject))
obj = "_".join(map(str, sent.object))
relation = "_".join(map(str, sent.verb))
dic["id"].append(n)
dic["text"].append(sentence.text)
dic["entity"].append(subj)
dic["object"].append(obj)
dic["relation"].append(relation)
## create dataframe
dtf = pd.DataFrame(dic)
## example
dtf[dtf["id"]==i]
Python标准库中用于创建和操作图网络的是NetworkX。我们可以从整个数据集开始创建图形,但如果节点太多,可视化将变得混乱:
## create full graph
G = nx.from_pandas_edgelist(dtf, source="entity", target="object",
edge_attr="relation",
create_using=nx.DiGraph())
## plot
plt.figure(figsize=(15,10))
pos = nx.spring_layout(G, k=1)
node_color = "skyblue"
edge_color = "black"
nx.draw(G, pos=pos, with_labels=True, node_color=node_color,
edge_color=edge_color, cmap=plt.cm.Dark2,
node_size=2000, connectionstyle='arc3,rad=0.1')
nx.draw_networkx_edge_labels(G, pos=pos, label_pos=0.5,
edge_labels=nx.get_edge_attributes(G,'relation'),
font_size=12, font_color='black', alpha=0.6)
plt.show()
G = nx.from_pandas_edgelist(dtf, source="entity", target="object", edge_attr="relation", create_using=nx.DiGraph())
:这行代码使用 NetworkX 库创建了一个有向图(DiGraph)。具体解释如下:nx.from_pandas_edgelist(dtf, source="entity", target="object", edge_attr="relation", create_using=nx.DiGraph())
:这个函数将 Pandas 数据帧 dtf
转换为一个有向图。在有向图中,实体作为节点,关系作为有向边,而 "entity" 列和 "object" 列包含了节点之间的连接,"relation" 列包含了边的属性(关系)。plt.figure(figsize=(15,10))
:这行代码创建一个新的图形画布,指定了画布的大小为 15x10 像素。pos = nx.spring_layout(G, k=1)
:这行代码使用 NetworkX 的 spring_layout
函数布局图形中的节点位置,其中 G
是创建的有向图。k=1
控制了节点之间的相互排斥力,影响图形的布局。node_color
和 edge_color
:这两行代码定义了节点和边的颜色。nx.draw(...)
:这个函数用于绘制图形。以下是参数的含义:G
:要绘制的图形。pos=pos
:节点位置的布局。with_labels=True
:是否显示节点的标签。node_color=node_color
:节点的颜色。edge_color=edge_color
:边的颜色。cmap=plt.cm.Dark2
:用于定义节点颜色映射的颜色映射。nx.draw_networkx_edge_labels(...)
:这个函数用于在图形上绘制边的标签。以下是参数的含义:pos=pos
:节点位置的布局。label_pos=0.5
:标签相对于边的位置。edge_labels=nx.get_edge_attributes(G,'relation')
:从图中获取边的属性(关系)作为标签。font_size=12
:标签的字体大小。font_color='black'
:标签的字体颜色。alpha=0.6
:标签的透明度。plt.show()
:这行代码用于显示绘制好的图形。知识图谱可以让我们从大局的角度看到所有事物的相关性,但是如果直接看整张图就没有什么用处。因此,最好根据我们所需的信息应用一些过滤器。对于这个例子,我将只选择涉及最常见实体的部分(基本上是最多连接的节点):
先找出最多连接的节点
dtf["entity"].value_counts().head()
然后进行过滤操作并进行可视化
## filter
f = "Russia"
tmp = dtf[(dtf["entity"]==f) | (dtf["object"]==f)]
## create small graph
G = nx.from_pandas_edgelist(tmp, source="entity", target="object",
edge_attr="relation",
create_using=nx.DiGraph())
## plot
plt.figure(figsize=(15,10))
pos = nx.spring_layout(G, k=0.5)
node_color = ["red" if node==f else "skyblue" for node in G.nodes]
edge_color = ["red" if edge[0]==f else "black" for edge in G.edges]
nx.draw(G, pos=pos, with_labels=True, node_color=node_color,
edge_color=edge_color, cmap=plt.cm.Dark2,
node_size=800, node_shape="o", width=1.0, connectionstyle='arc3,rad=0.1', font_size=8)
nx.draw_networkx_edge_labels(G, pos=pos, label_pos=0.5,
edge_labels=nx.get_edge_attributes(G,'relation'),
font_size=8, font_color='black', alpha=0.6)
plt.show()
对于Ukraine的效果图