chemdataextractor ---- Chemistry Articles NLP Library
文章目录
参考
chemdataextractor2 documentation
chemdataextractor2 repo
tabledataextractor
参考文献和链接
jsr 385:
ref:https://jcp.org/en/jsr/detail?id=385 unit of measurement api 2.0
JSR 363:
ref:https://jcp.org/en/jsr/detail?id=363 unit of measurement api 1.0
物理量 纯文本表示方法
ref:UCUM ref:The Unified Code for Units of Measure 规范解说 作用:物理量的编写规范(在 unicode 领域),机器间物理量传输工具
名词
专业术语
| 英语 | 汉语 | 备注 |
|---|---|---|
| document-level processing | ||
| data interdependencies | 数据相互依赖 | 前后文本数据的相互关联性 |
文件解析
解析工具
- chemdataextractor.reader.*
eg:
| |
注意
- 读取模式必须是 'rb'
解析结果 Document 对象
- 解析结果存储在 Document 对象中
Document 属性
- paragraphs
- cems
abbreviation_definitions注意
- 没有的属性
- tokens
- sentences
段落
doc.elements
- 一个列表
- 段落获取 para = doc.elements[i]
存储信息
para.sentences: List[Sentence]- 句子列表
para.tokens
- token 列表
- eg:
[[Token('1,4-Dibromoanthracene', 0, 21), Token('was', 22, 25),]
para.cems
- chemical 列表
- Chemical Entity Mension(化学物质实体提到)
para.abbreviation_definitions()
- 缩写
解析器添加流程
新建 Model 类
- 用于添加到 Document.models: List[BaseModel]
- model 中 添加 Parser
model
QuantityModel
用来解析物理量 (property, value, unit) 三元组
字段:
- raw_value
- raw_unites
value
- 提取后的(可以直接使用的)值
units
- 真正的值
specifier
- Optional 可选
与 autoparsers 配合使用
StringType(updatable=True) 时,
- 通过 model.definitions, BaseModel.update 实现
- 缩写代称等,更新 parse_expression
- 或运算,加入 specifier(缩写代称,如:希腊字母、Tc、Tn 等)
- StringType(required=True), 过滤不合法匹配
StringType(parse_expression), 需要 add_action(join)
eg:
1specifier_expr = ((I('Glass') + I('transition') + I('temperature')) | I('Tg')).add_action(join)
compound
- 需要关联 化合物时
Dimension 类
用途
- 表示物理尺寸,physical dimension
字段
constituent_dimensions
用来创建复合量纲
eg:
1 2class Speed(Dimension): constituent_dimensions = Length() / Time()
_dimensions
- 用来展示符合量纲
eg:
1Speed._dimensions = {Length(): 1.0, Time(): -1.0}注意
- 不用用户手动设置
units_dict = {}
- 用来提取这些复合量纲
类型
1Dict[chemdataextractor.parse.element, Union[chemdataextractor.model.units.unit.Unit, None]]eg:
1 2 3 4 5# 温度的提取 units_dict = {R('°?(((K|k)elvin(s)?)|K)\.?', group=0): Kelvin, R('(°C|((C|c)elsius))\.?', group=0): Celsius, R('°?((F|f)ahrenheit|F)\.?', group=0): Fahrenheit, R('°|C', group=0): None}注意
- 使用 regex 时,不要带 $, 避免停止后续匹配
安装 installation
chemdataextractor2
日期: 2021.11
python 版本要求
- python 3.7
依赖安装
- pip install /path/to/tableextractor
- pip install /path/to/chemdataextractor2
组件
测试数据集
开放 chemical ner
- CHEMDNER
论文
chemdataextractor 1
论文名称:
ChemDataExtractor: A Toolkit for Automated Extraction of Chemical Information from the Scientific Literature
抽取对象
自动提取
- chemical entities
- associated properties
- measures
- procedures
- relations
特性
- extensible
- chemistry-aware
- natural language processing pipeline (nlp pipeline)
额外功能
table parser: 提取半结构化的信息
a table parser for extracting information from semi-structured tabulated data
document-level 处理后算法(post-processing)
document-level post-processing algorithms to resolve data interdependencies between information extracted from different parts of a document
解析 interdepencies 信息,解决数据互依赖性
流程 pipeline
- tokennization
part of speech tagging (POS tagging)
- chemical aware POS
named entity recognition (NER taggging)
- combine CRF model and dictionary
phrase parsing
- rule-based grammar
chemical entity recognition (chemical NER)
使用
非监督学习聚类(unsupervised word clustering)以提升机器学习性能
to improve performance of machine learning methods through unsupervised training
- 基于大化学文献语料库 (based on a massive corpus chemistry articles)
Phrase Parsing and Information Extraction
实现原理:多种基于规则的语法分析
the novel use of multiple rule-based grammars that are tailored for interpreting specific document domains such as textual paragraphs, captions, tables.
即:对于不同的文本类型(document domains),段落、标题、表格等,编写特定的规则, 针对性解析处理
document level processing
用途:
- resolve data interdepencies 解决数据的互依赖性
重要性:
标题和表格常常包含化学识别符(chemical identifier)和引用- 化学标识符和引用常常在别的地方被定义
性能评估 performance evaluation
| F1 score | item | |
| 93.4% | chemical identifiers | 化学标识符 |
| 86.8% | spectroscopic attributes | 光谱属性 |
| 91.5% | chemical property attributes | 化学性质属性 |
大语料 CHEMDNER 评估 —- 化学式结构式 NER 语料
语料地址
语料论文
挑战评估结果(CHEMDNER challenge)
- F1 score: 87.8%
材料基因项目
Materials Genome Initiative
large-scale data-mining for materials discovery 大规模数据挖掘和材料发现
Harvard Clean Energy Project
materials for organic photovoltaics 有机光伏材料
The Materials Project
论文:A Materials Genome Approach to Accelerating Materials Innovation focuses on battery materials
电池材料
项目类型
利用计算资源预测化学性质
- 用到机器可理解的实验性质数据库
that would be well complemented by wider availability of machine-readable databases of experimental properties
通用型材料数据抽取工具
通用的数据库制作和材料性质提取工具
a generic method that can automatically generate a database for any type of material property
chemdataextractor 开发目的和软件项目类型
工具
LeadMine
在文本中,识别和标记 化学材料、靶蛋白、基因、命名反应、公司名称等等
NextMove Software's LeadMine product is a text mining tool for the identification and annotation of chemicals, protein targets, genes, diseases, species, named reactions, company names, cell lines, etc. in the text of documents.
v2.0 可以转换中文和日语的化学材料名称 2015 年论文:LeadMine: a grammar and dictionary driven approach to entity recognition
ChemicalTagger
抽取反应,给物质打上角色标记(chemical roles)
which parses experimental synthesis sections of documents to determine chemical roles (e.g. reactant, solvent) and relationships with experimental actions (e.g. heated, stirred)
可以与 LeadMine 一起使用抽取熔点 melting pints
使用工具:
ANTLR grammar
- rule-based 文本解析
OSCAR
- chemical NER
- repo: OSCAR4
- 底层使用 OSPIN
ChemEx
ChemicalTagger 的扩展版本
新功能:
- 识别 biomedical entity
- 2D 化学结构识别 + 底层使用 OSRA
论文:ChemEx: information extraction system for chemical data curation | BMC Bioinf…
model.units.dimension 包
Dimension 类
字段
constituent_dimessions
- type: Dimension
用途
新的复合量纲实例
- 由子量纲运算得来
构建方法
- dimension 实例对象间的运算
eg:
DimensionA / DimensionB
1 2class Speed(Dimension): constituent_dimensions = Length() / Time()
_dimensions
- type: Dict[Dimension, float]
用途
- 存储
子量纲之间的运算关系
- 存储
model
字段
required
相对于上级 model(outer model)当前子 model 是否强制要求必须存在
- type: bool
contextual
是否允许在文章的其他部分(相对于 outer model),获取当前 model 内容 (在 interdepency 解析过程中)
- type: bool
updatable
引用文献
chemdataextractor
项目论文
chemdataextractor2
- ChemDataExtractor 2.0: Auto-Populated Ontologies for Materials Science
chemdataextractor1
- ChemDataExtractor: A Toolkit for Automated Extraction of Chemical Information from the Scientific Literature
应用
本团队应用论文
- A Design-to-Device Pipeline for Data-Driven Materials Discovery
grobid-quantity
项目论文
- Automatic Identification and Normalisation of Physical Measurements in Scientific Literature
引用文献
Processing of quantitative expressions with units of measurement in scientific texts as applied to Belarusian and Russian text-to-speech synthesis
- 白俄人写的论文
unit-api
项目名称
- units of measurements
项目论文
How to Extract Unit of Measure in Scientific Documents?
- 被 grobid-quantity 引用
解析流程分析
Document.from_file(f)
类: chemdataextractor.doc.document.Document
调用顺序:
- Document.from_file(f)
- Document.from_string(bytes)
Reader.detect
- 被重写的方法
Reader.readstring(bytes)
- bytes 是整个文档
Reader.parse(bytes)
- 被重写的方法
具体处理
Reader._make_tree
- 产生 root ElementTree
Reader._css() 获取 root element
- eg: <html> 标签
Cleaner 调用
- 清理和正规化
特殊类型,分类别抽取,结果类型 List[element]
- titles
- headings
- figures
- tables
citations
- 参考文献
references
- 链接
- ignores
- metadata
结果存储
refs:
List[parentElement, str]- 链接信息
specials: Dict[element, list]
- element 可以 Hash
- 链接以外的东西
Reader._parse_element(root, specials, refs) –> Document
- 从 root 开始解析
- Reader._parse_element_r(elem, specials, refs, element_class)
正规化
- Cleaner 类
SnowBall 算法
训练
- file
- Document object
- Sentence object
规定
- 句子长度 <= 300
位置
- chemdataextractor.relex
类
Entity
- chemdataextractor.relex.entity.Entity
用途
- 关系抽取的基础实体
- 实体容器
实体的基本信息
- 位置
- 标签
- 解析表达式 parse_expression
属性
text
- 文本内容
tag
- 实体名称 or list
位置
- start: int
- end: int
- parse_expression
特殊功能
tag
- 嵌套 tag,
__双下划线分割 eg:
- normal_tag
- root_tag__sub_tag
- 嵌套 tag,
Cluster
- chemdataextractor.relex.cluster.Cluster
用途
- 聚类
Pattern
- chemdataextractor.relex.Pattern
用途
- snowball 算法中的
pattern制作
- snowball 算法中的
属性
snowball pattern
各组成部分
self.elements: Dict[str, Dict]
- {'prefix': {'tokens': [….], …}, …}
Phrase
- chemdataextractor.relex.phrase.Phrase
用途
- 句子容器
- 句子解析
- 关系解析
- 用于
snowball pattern的聚类和抽取 - 关系汇总工具
属性
self.cluster_assignments: Set
- 归属的 簇, 可以是多个
self.entities
- 实体列表
- self.tokens: List[str] | Any?
eg:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16class TestPhrase(unittest.TestCase): maxDiff = None training_corpus = 'tests/data/relex/curie_training_set/' def test_phrase_create(self): """Test that Candidate Relation objects are correctly created """ tokens = ['this', 'is', 'a', 'test', 'phrase'] entities = [ Entity('this', 'who', I('this'), 0, 1), Entity('phrase', 'what', I('phrase'), 4, 5)] relations = [Relation(entities, confidence=1.0)] phrase = Phrase(tokens, relations, 1, 1) expected = '<Blank> (who) is a test (what) <Blank>' self.assertEqual(phrase.to_string(), expected)
Relation
- chemdataextractor.relex.relation.Relation
用途
关系定义
- 关系模式,容纳具体的关系例子
- 关系的容器
- 在
Phrase类中被调用
属性
self.entities: List[Entity]
- 关系中的各个实体
self.confidence: float
- 这个关系的置信度
方法
self.is_valid()
- 同一个实体,拥有两个 tag,显然打 tag 出错,不合法
Snowball Element 解说
| |
解释: 一个 element 就是 prefix, middle_n, suffix 之中的一个
组分:
给定 Phrase 内部:
- 相应的各个单词
存储工具
Phrase.elements
- 给定句子的多个 element
给定 Cluster 内部
- (cluster 包含多个 Phrase)
存储工具
Cluster.dictionaries
Cluster.dictionaries[element]
- 多个句子的 给定位置 element, 汇总
- 统计所有给定 token 的权重 和 其他统计数据
eg:
1 2 3 4 5 6 7 8 9 10 11 12{ "token_dict": { "That": [ // token text 2, // freq 0.1 // weight = current_freq / sum(all_freq) ], "is": [ 3, 0.2 ] } }
Cluster.update_pattern 函数内部
local.vectors
- 类型: Dict[List[List[float]]], Dict[element_name, List[Phrase_List[token_weight]]]
元素:单个 element 的处理
- self.dictionaries[element]['token_dict'] 包含了所有 Phrase 中的 token 信息
- 建立 定长数组
- 遍历所有 Phrase (self.phrases)
- 给定 Phrase 中不包含 同样的 token, weight = 0
eg:
1 2 3 4 5[ [0.1, 0.2, 0], // 第一个 Phrase, token 顺序 That is [0, 0.2, 0], // 第二个 Phrase, token 顺序 is ]- 通过 self.dictionaries[element]['token_dict'].keys() token 保证遍历顺序
definitions
参见: chemdataextractor.doc.text.BaseText::definitions 行号: 690 行
样式:
| |
关联代码
Model.update(definitions) 方法
- 更新 model 中的 updateable 字段
tagged_tokens
Sentence.tagged_tokens: List[Tuple[token, tag]]
tags:
tag = pos_tag if ner_tag is None else ner_tag
句子文本处理流程
- Sentence(text) 对象
- token 化
- 打标签 pos_tag, ner_tag
token 处理
definition 识别
- 缩写
- 代称
- 化学名称 代称
命名实体识别
- 化学名称 cem
model 匹配抽取 和 序列化(Sentence.records 方法实现)
model 匹配
- 返回结果: List[Model]
records 处理
- 序列化
去重、去空值、去信息重叠
普通 Model
- 去重、空值
Compound Model
- 去信息重叠
- label 判断重复问题
打标签流程
在 chemdataextractor.doc.text.Sentence 中
- self.pos_tagger 打标签
- self.ner_togger 打标签
打完后
- self.pos_tags, self.ner_tags
- self.tagged_tokens
- self.pos_tagged_tokens
- self.ner_tagged_tokens
Model 抽取流程
使用 AutoSentenceParser 重组各个 parse_expression
AutoSentenceParser 继承关系:
- BaseAutoParser(BaseParser).interpret(result, start, end)
- BaseSentenceParser(BaseParser).parse_sentence(tokens)
抽取流程:
AutoSentenceParser.root() 组建
- 把各个字段的 parse_expression 组合在一起
- 针对 self.model.dimensions (量纲) 特殊处理
- 必须有 self.model.compound 字段
- BaseSentenceParser.parse_sentence(tokens)
BaseAutoParser
- BaseAutoParser.parse_
物理量(数值 + 单位处理流程)
粗文本处理
chemdataextractor.parse.quantity
extract_unit(text_str)
- split_(text_str)
extract_value(text_str)
- "150 to 160" –> [150, 160]
调用 extract_unit
parser.extract_unit:
<— parser.interpret(result, start, end)
BaseParser:=> yield BaseModel- 粗结果 与 对应字段规则比较
- 精准匹配抽取转换
<— parser.parse_sentence(tagged_tokens)
BaseSentenceParser:=> yield BaseModelparser.root.scan(tagged_tokens)
BaseSentenceParser=:=> yield [etree.Element, start, end]- 数据匹配和粗抽取
- xml 结构结果
- model 精准结果
<— Sentence.records()
Sentence:=> List[Model]- 结果清洗:去重、去空、合并、化合物去重特殊规则
- model 的 遍历
- Parser 的遍历
<— Text.records()
Text:=> List[Model]- 其他非 Text 类,调用 Sentence.records 实现 model 抽取
<— Document.records
Document:=> List[Model]- definitions 处理
- interdepencies 互依赖的处理
互依赖性
通过下面三个
BaseType字段规定required
- 是否必须有
binding
- 是否 在子 Model 中,该字段必须是相同的一个
contextual
- 是否允许是上下文出现也可以
merge 问题
- BaseModel::merge_all 引入
相关函数
- BaseModel::binding_properties
BaseModel::_binding_compatable(other) –> bool
- self 和 other 类型不同 –> True
- 绑定字段值 全相同 ,不冲突 –> True,可以合并
self 字段是 other 的子集(other 有不被 self 字段值包含的元素,即 self 字段值元素量小) –> False
- BaseModel::is_superset() 和 BaseModel::is_subset()
BaseModel::_compatible(self, other)
- self, other 类型一致
字段值一致(相等),满足下列调节的字段
- not field.ignore_when_merging
- self[field_name] != None
- 即,待比较 self 有的字段 other 字段不为 None,必须相等,否则 not compatible
BaseModel::binding_properties() –> Dict[name_str, value]
- 找到的 field.binding = True 字段
- 当前级别 level Model 中的字段
- 不包含 sub model 中字段
例子:
参考:Getting Started — Step1: Defining a new property
| |
例子修改: 官方使用 Altitude 我认为是错误的
解说:
- 两个 model 共同字段 compound
BoilingPoint::compound 字段
ModelType(Compound, binding=True)binding父Model 和子Model 中给定字段的值必须相同 这里 binding 规定了 当前 root_model(BoilingPoint) 中的 compound 字段 和 sub_model(Pressure) 中的 compound 字段的值必须相同
BoilingPoint::pressure 字段
contextual- 允许在上下文中找该字段的值
BoilingPoint::pressure 字段
required- 是否必选
相关函数
chemdataextractor.model.base.BaseModel::binding_properties
绑定的属性
类型:Dict[name_str, value]
chemdataextractor.model.base.BaseModel::merge_all
parser 与 句法模式
句法组分
specifier 和 缩写
(缩写)
Optional(lbrct) + W('Tm') + Optional(rbrct)
归属词
Optional(lbrct + W('Tm') + rbrct) + Optional(W('=') | I('of') | I('was') | I('is') | I('at'))
=, of, was, is, at
范围
in the range of about
api 接口
chemdataextractor.parse.elements
功能类
针对单个 expr
内部存储的一个解析规则 self.expr
Any
匹配任意 token
OneOrMore(ParseElementEnchenced)
单个 expr 第一个必须出现,后续出不出现都可
ZeroOrMore
针对多个 expr 组合
内部存储的时多个解析规则 self.exprs
And(ParseExpression)
多个 exprs 顺序出现,允许元素缺失,每个 expr 出不出现,根据对应 expr 的内部规则
内部规则
- 是否强制出现
- 是否捕获 ParseException 异常
Or
多个 exprs 匹配最长的那一个
基类 BaseParserElement
BaseParserElement.scan(tagged_tokens)
返回类型:
- Tuple[List[etree.Element], matched, next_index]
- Tuple[etree.Element, matched, next_index]
逻辑:
逐个 token 匹配
匹配失败
- 下一个
i += 1
- 下一个
匹配成功,
额外要求: next_i > i
满足
单个 Element
- yield single_element, i, next_i
多个 Element
- yield list_element, i, next_i
是否允许重叠 overlap?
- True: i += 1
- False: i = next_i
不满足
- i += 1
- 遍历结束
任务(功能): 逐个 yield 匹配的 (token, i, next_i)
BaseParserElement.with_condition(condition_function)
作用: 接受一个回调函数
参数类型: condition_function: CallableTuple[List[Element], int, bool]
condition_function 的用途
- 在 BaseParserElement.parse() 中被调用
- 接收 BaseParserElement._parse_tokens 返回结果
- 作用:判断 self._parse_tokens 结果 List[Element] 是否满足条件
BaseParserElement.parse(tagged_tokens, start_index)
异常: ParseException
返回值: (List[Element], next_index)
- next_index = start_index + 1
BaseParserElement.actions 属性
设置:
- self.add_actions(*actions)
- self.set_actions(*actions)
单个 action
- 类型: 回调函数,
Callable[[tagged_tokens, index, List[etree.Element]]] - 作用 修饰 result (List[etree.Element])
BaseParserElement._parse_tokens(tagged_tokens, start_parse_index, actions=True)
- 重载函数
- 执行解析
结果: Tuple[List[Element], index]
chemdataextractor.parse.base
用于解析给定的 Document(Text) 文档,调用 parse_expression 遍历 tagged_tokens
chemdataextractor.parse.base.BaseParser
作用: 解析器的基类
属性:
self.trigger_phrase: BaseParserElement
过滤器
- 判断给定句子是否是要处理的
- 提升解析速度
类型
- 单个匹配规则
self.root: BaseParserElement
- 给定 Model 的所有规则处理后制作的匹配规则
方法:
self.interpret(result, start, end)
作用: 把 self.root.scan(tagged_tokens) 解析的结果 [List[etree.Element], start, end] 再解析成对应到 Model 的字段
特点:
- 嵌套 Model 的处理,通过 self._get_data() 实现
- ListType 字段处理,通过 self._get_data() 实现
self.model.dimenssions 处理
- 量纲一处理
- 包含量纲时处理
- 无 self.model.dimensions 处理
返回: yield Model, 解析好的 Model 结果
self._get_data(field_name, field, result: etree.Element)
作用: 通过给定 field_name, field 从 etree.Element 中提取响应 字段的值,并加以检验
特点:
- 嵌套模型处理(字段是模型)
- required 字段处理
- ListType 字段处理
chemdataextractor.parse.auto
AutoSentenceParser
功能: 处理句子 tagged_tokens
结果: 抽取到的 model
属性
self.model
- 注意: 一个 parse 只能有一个关联 model
AutoSentenceParser.root(self)
作用:
把 model 下的 BaseType 包裹的字段 遍历一遍
- 获取 parse_expression
给 parse_expression 添加名字
通过 parse_expression(name) 实现
- 背后逻辑 BaseParserElement.set_name(name) 间接调用
通过遍历下面的字段实现
- model.compound.labels
- model.dimension
- model.specifier
其他字段
- model.fields: dict
制作 entites 列表
- compound: labels
- specifier: specifier
value_phase(物理量 值+单位), 嵌套的 parse_expression
- raw_value
- raw_units
结构, And 运算
- (raw_value + raw_unites)
跳过的
- value
- error
原因猜测
- 它们是结果存储
- 不是要直接解析的对象
- 制作 combined_entity_list 列表
- 通过 OneOrMore(…)('root_phase') 合在一起
注意:
- 要求 self.model 必须有 compound 属性
chemdataextractor.model.base
chemdataextractor.model.base.ModelList
功能:
存储结果 Model 对象列表
- self.models: List[Model]
序列化结果
json 形式
- self.serialize()
dict 形式
- self.to_json()
实现方法
- 通过 Model.serialize() 实现给定 Model 序列化
- 通过 BaseType.serialize() 实现给定字段序列化
chemdataextractor.doc 包
类
chemdataextractor.doc.text.BaseText
功能:
token 化
- 指定分隔符,分割
缩写识别
- 通过缩写词典
- 通过括号等特殊符号
别称
- self.definitions
- 打标签 tagger (pos 和 ner)
- 化学材料识别
物理量解析?
- self.quantity_re
- chemdataextractor.parse.quantity.construct_quantity_re 函数
规则抽取
- 使用 各个 model 字段规则抽取数据
- 序列化成 List[Model]
属性:
tagger
属性方法:
- self.pos_tags
- self.ner_tags
属性:
- self.pos_tagger
- self.ner_tagger
- self.tags
缩写
属性方法
- self.abbreviation_definitions
属性
- self.abbreviation_detector
化学材料
- self.cems()
别称
- self.definitions()
- self.chemical_definitions()
物理量 quantity
self.quantity_re()
- 一个 model_list 的 dimensions.units_dict 规则叠加
抽取结果
self.records()
- 遍历 self._streamed_lined_models 中的 model
- 遍历 model 下的 parsers
- 得到多个 record
多个 records 消减合并(结果非单个)
- 空值删除
- 重复值删除
Compound Model 问题
- 删除重复: labels 集合判断 是否已经搜集过
更新信息重叠: 新 record 与 旧 record, names 有交集 或者 labels 有交集
- 把 新 record 的信息 (names, labels, roles) 更新(并集 union)到旧 record 中
返回类型 List[Model]
- 一个 Model 对应 一个 Record
chemdataextractor.doc.document.Document
属性:
- self.elements: List[Text]
作用:
- 注册 file 对象
- 读取二进制内容
- 传递给 Document.from_string
接受类型: Union[file_object, str]
作用:
- 准备 Reader,选择合适的 Reader, 通过 Reader.detect() 实现
- 调用 Reader.readstring(bytes) 开始解析
接受类型: ByteString(bytes)
chemdataextractor.doc.text.Sentence
句子的文本容器和解析工具
字段:
self.definitions: List[Dict]
1 2 3 4 5 6[{ 'definition': first(definition.xpath('./phrase/text()')), 'specifier': first(definition.xpath('./specifier/text()')), 'tokens': tagged_tokens[start:end], 'start': start, 'end': end}, ...]
chemdataextractor.reader 包
继承关系
抽象类
- BaseReader –> LxmlReader
LxmlReader
- 使用 css 选择器
具体类
需要实现的方法:
def detect(self, fstring, fname) –> bool
- 判断扩展名等等
def _make_tree(self, fstring: bytes) –> lxml.etree.ElementTree
- 产生 root
def parse(self, fstring: bytes) –> chemdataextractor.doc.Document
- 执行解析
XmlReader
HtmlReader
chemdataextractor.reader.markup.LxmlReader
入口方法: self.parse(fstring)
完成功能:
xml 解析
- 构建一个 etree Element root
- 解析 etree.Element root, 制作 List[Text]
- 使用 List[Text] 构建 Document 对象
数据清洗
- 丢弃空元素 Text('')
Text.text.strip()
- strip 掉 空白符
- self._xpath(…) –> List[etree.Element]
- self._css(…) –> List[etree.Element]
self._parse_element_r(etree.Element, …) –> List[Text]
- Text, base class of Title, Paragraph, Heading, Sentence
- self._parse_element(etee.Element, …) –> List[Text]
self._parse_text(etree.Element, …) –> List[Text]
- return single Text object,
[Text]
- return single Text object,
代码
| |
chemdataextractor.scrape 包
chemdataextractor.scrape.clean.Cleaner 类
作用:
- 删除无用标签,eg: <script>, style, …
- 用 text 替代整个标签
chemdataextractor.relex 包
chemdataextractor.relex.Snowball
方法
Snowball.candidates(tagged_tokens)
功能: 处理一个句子的 tagged_tokens 抽取关系,可以是多个
entities 制作
- 类型: chemdataextractor.relex.Entity 类
- AutoSentenceParser.root.scan(tagged_tokens) 扫描
self.retrieve_entities(…) 制作粗 entities 原料
(text, (model.__name__.lower(), tag), field.expression)
- tag: field name in model
- KnuthMorrisPratt 产生 开始位置等,index 参数
tag 转换
- (model.__name__.lower(), tag) –> model_name__field_name
Entity类对象创建- entity_dic: Dict[tag, List[Entity]] 创建
重复删除: 多余删除,一个 (model_name, field_name) 多个 entity 选择性抛弃
准则: 同一个 tag, index 位置交叠判断
- start 相同时
to_pop.append([i, j][np.argmin([entities[i].end, entities[j].end])])- 舍弃长度短的
组分不完整删除:
- 针对 root_model
self.filter_incomplete(model, entities_dict)
- 支持嵌套 model 处理
给定 field 是 required, 并且 相应 model_name__field 不在 entities_dict.keys() 中
- 出删除 该 model 所有 entities
即:
- 一个子 model (是否 required,未定)
- 这个子 model 如果含有 required 字段 field, 没有获取到
- 整体删除这个 子 model 的所有字段 start_with(model_name + '__')
解说
因为 entities 里面存储的是 model__name__field_name
- 所以是遍历的各个
子model 的普通字段完整性和 当前root model 普通字段的完整行
- 所以是遍历的各个
没有遍历 model 是 required 这种嵌套情况
- 这种通过 self.required_fulfilled(model, entities_dict) 覆盖
校验 嵌套 model, 检验 field 完整性
- self.required_fulfilled(model, entities_dict) –> bool
- 校验不通过,当前句子 直接失败, self.candidates(tagged_tokens) –> []
排序(all_entities)
- 给定 tag (model_name__field_name), 列表内, 按 List[Entity] 按 entity.start 排序
笛卡尔积制作
product(*all_entities)- 展平后的笛卡尔积
用处,model 解析结果列表形式,转换成 各字段都是单个的形式
- 可以产生针对
多对多潜在形式的枚举
- 可以产生针对
结果: List[List[Entity]]
- 实体的笛卡尔积结果
- 一个各个 tag 都存在的 实体列表
- List[Candidate]
- Candidate: List[Entity]
关系构建
- 单个 condidate 内部 实体排序
- chemdataextractor.relex.Relation 类
置信度初始化
confidence=0r = Relation(candidate, confidence=0)
关系有效性判断
不同 tag(model_name__field_name), 相同 index 非法
- Relation 内部实际上都是不同 tag,天然满足 tag 不同
e1.tag != e2.tag and e1.start == e2.start and e1.end == e2.end- index 重叠非法
Snowball.train_from_sentence(Sentence)
Snowball.parse_sentence(tokens)
功能:
- 仿造 chemda
逻辑:
self.candidates(tagged_tokens) ==> List[Relation(entities, confidence=0)] cde 抽取结果
- 未标注初始化 Relation.confidence = 0 不可信
unique_names
- 不重复的化合物名称 compound name
用来做阈值判断
num_candicates * num_of_unique_names <= self.max_candidate_combination
- 制作 candiate 组合: all_combs
candidate_phrase 创建
- 通过 单个 combination 和 not_tagged_tokens 一起创建
- candidate_phrase = Phrase([t[0] for t in tokens], new_rels, self.prefix_length, self.suffix_length)
作用:?
- 通过化合物 compound 数量,来创建 Relation 组合
- compound 数量决定
最大组合元素数量
标注
- 可以是多个,eg: "1,3,5"
初始化: chosen_relation.condidence = 1.0
置信度修正
给定句子 Snowball 关系更新
self.update(sent.raw_tokens, chosen_relations)
- 聚类使用
self.cluster(Phase(sent.raw_tokens, Relation, self.prefix_length, self.suffix_length))
- 聚类使用
Snowball.update(sent_text_tokens, chosen_condidates)
功能:
new_phrase 创建:
- 把当前句子解析到的 Relations 包装成 Phrase
聚类:
- 放入 self.cluster(new_phrase) 方法中聚类
Snowball.cluster(phrase)
功能:
- 聚类
- 把 新 phrase 添加如
self.clusters属性内
Snowball.classify(phrase)
功能: 给 phrase 分类到对应的 cluster, 或这新建一个 cluster 逻辑:
- 逐个 cluster 对比,获取相似度 similarity
- 检验标准:
if similarity >= self.minimum_cluster_similarity_score: 比较结果
- 相似: 添加到给定 cluster
- 否则: 新建一个 cluster
chemdataextractor.relex.Phrase
功能:
- suffix_tokens、middle_tokens、suffix_tokens 制作
- 一个句子的多个 Relation 容器
- 用于 cluster 和 抽取 pattern: <prefix> Tag_Entity <middle> Tag_Entity <suffix>
接口:
__init__(self, sent_raw_tokens, list_phases, prefix_length, suffix_length)
- 句子信息, 解析的关系, 前缀长度限制,后缀长度限制
属性:
- sent_raw_tokens
- full_sentence 句子文本
self.relations
- 一个句子的所有 chosen_relations
order
- 排序的 tag
entities
- 排序的 Entity
self.elements
- 类型: Dict[str, List[{"tokens": List[text_token] }]]
- 例子: {"prefix": [{"tokens": ["That", "is"]}]}
方法:
self.create()
- 按 entity.tag(model_name__field_name) 计数
- 所有 relations 展开到 combined_entity_list
- combined_entity_list 排序
- 排序的 tag : self.order
prefix_tokens 制作
- 通过 sorted_entity_list[0].start 制作
- 空 –> ["<Blank>"]
suffix_tokens 制作
- 通过 sorted_entity_list[-1].end 制作
- 空 –> ["<Blank>"]
middle_tokens
- 便利 sorted_entity_list 制作
prefix_tokens、midlde_tokens、suffix_tokens 存储
self.elements: Dict[fix_name_str, Dict['tokens', List[str_token]]]
- fix_name_str: eg: "prefix", "suffix", "middle_1", "middle_2", …
Dict['tokens', List[str_token]]:
- {"tokens": ["<BLANK>"]}
- {"tokens": ["that", "is"]}
- …
chemdataextractor.relex.Relation
功能:
- 一个句子的 不同 tag 实体的结合
- 即,给定 model 的不同 field 的抽取结果容器
接口: Relation(entities: List[Entity], confidence: float)
方法:
is_valid
- 不允许两个 entity 重叠
if e1.tag != e2.tag and e1.start == e2.start and e1.end == e2.end:
chemdataextractor.relex.Cluster
功能:
- 单个聚类的簇
- 相似 Phrase 收集容器
属性:
self.dictionaries
存储给定 (prefix, middle_1, middle_2, suffix) 的 token 信息
- 指的是 element
类型:
1 2 3 4self.dictionaries[element] = {'token dict': OrderedDict(), # 给定 element 各个 token 的 Dict[token_str, [frequeny, weight]] 参数统计 'unique words': [], # Which words appear once 'total words': 0, # counter 给定 element 的 所有 token 总数 sum(token_frequeny) 'total recurring words': 0} # counter- frequeny: int
weight: float 分数
- frequeny / total_words
添加 和 更新 tokens 参数
1 2 3 4 5 6 7 8 9 10@staticmethod def add_tokens(dictionary, tokens): for token in tokens: if token not in dictionary['token dict'].keys(): dictionary['total words'] += 1 dictionary['token dict'][token] = [1.0, 0] # [frequeny, weight] else: dictionary['total words'] += 1 dictionary['token dict'][token][0] += 1 return
方法:
add_phrase()
- 把 phrase 放入到 self.dictionaries
- 更新计算 element 对应的 token 的
频数和权重frequeny, weight
update_pattern()
制作 vectors: List[{element_key: List[List[token_weight]]}]
- 作用: 统计给定 element 的 各个 token 的权重 weight
针对 self.phrases 制作 vectors
- 遍历 self.phrases
- 遍历 phrase.elemens.keys()
- 遍历 self.dictionaries[element], 内部 token_weight
单个 element, 多个 phrase, 多个 tokens
- 因此 vectors[element] 对应的是
给定 element, 不同 phrase, 不同 token 内部的 weight 二维阵列(2d array)
- 行: phrase
- 列: token 的 weight 权重
形心(centroid)查找
单个形心:
- 给定 vector[element]: 2d array
- 频率最高的行计算
element_mode = mode_rows(2d_array) - KDtree.query 查找 形心 Phrase(即 row_id)
- 提取 element,
self.phrases[medoid_idx].elements[element]
存储
结果形式: 找到给定 phrase
pattern_elements[element] = self.phrases[medoid_idx].elements[element]- 单个形心 List[token_str]
- 整体 Dict[element_name, List[token_str]]
结果存放
- pattern_elements
self.pattern = Pattern(pattern_elements, self.entities, ....)
update_pattern_confidence()
功能:
- 计算
形心置信度confidence new_confidence = len(centroid_found_relations) / sum(len(all_phrases.relations))- (遍历所有 phrases, 形心发现的 relation 总和) / (原来所有 phrases 已经发现的 relations 总和)
置信度更新,与 self.learnning_rate 有关
1self.pattern.confidence = self.learning_rate*new_pattern_confidence + (1.0 - self.learning_rate)*self.old_pattern_confidence
- 计算
get_relations(sent_tagged_tokens)
遍历给定 Pattern.parse_expression.scan(tokens) 抽取结果
- 理论上,应当是只有一个
给定 match, 遍历 Pattern.relations
给定 relation, 遍历 Entities
把 Entity 放入 entity_type_indexes: Dict[str, List[Entity]], Dict['entity_tag', List[Entity]]
作用:
- 多个 Relation 的 Entity 存在一起
- Entity.tag 分类, 保证了 同一个句子, Entity.tag 一致,List[Entity] 内部顺序就是 Pattern.relation 顺序
使用 Entity.tag 做 xpath, 在
match(etree 类型)中查找匹配结果- 注意:多结果性
- 因为 一个句子(Pattern) 允许多个 Relation, 相同的 Entity.tag 可能存在多个结果 在 match 中
使用 Pattern 中 Entity 的 index 在 Phrase(即 res[0]) 的 match_xpath_found_results 中 查找中 对应 Entity
- Pattern entity 查找位置
- Phrase 匹配 xpath 结果 对应 真实的 entity
找到 relation 中 Entity 对应的实际 句子中 Entity(重头构建) –>
found_entities原因
- Pattern.relations.relation.entity ,因为 多个 Relation 可能有共享的 Entity
最终结果
- 给定 self.Pattern.relations 重新查找的 Relation 对应的 Entity
- 这里 self.Pattern 是形心,不是原来的 Phrase 对应的句子了
chemdataextractor.relex.Pattern
属性:
self.parse_expression
- 解释: 形心的 Element tokens 和 Entity parse_expression 合在一起创建
方法:
generate_cde_parse_expression
逻辑
- 把 prefix, middle_1, middle_2, …, suffix token
逐个使用 IWord(token_str) 包起来
- 跳过 "<BLANK>"
注意:
中间 穿插 Entity.parse_expression
通过 Model.field.parse_expression 传递过来
- 在 Snowball.candiate
- 在 Entity 初始化时, Entity.parse_expression name 被重新设置, 参见 Entity
- 使用 And 连接
- 设定名称 "phrase", 完成处理
句法相似度 chemdataextractor.relex.utils
作用:
- 当前句法模式 Phrase 和一个给定句法簇 cluster 的相似度
- 比较 Phrase 和 cluster 所有子 Phrase 相似的总体情况
向量化 chemdataextractor.relex.utils.vectorise()
逻辑:
按 element 统计
获取所有 token 的频数:
- cluster 中给定 element 所有 token 的频数 frequeny
- 加上当前对应 phrase 对应的 element 的 token 的 频数
计算 phrase 和 pattern 的 token 正规化向量
频数向量
- 按 整个 cluster 中的 所有 token 为元素数量和排序基准
- 分别统计 phrase 和 pattern 的 对应 token 频数
frequency_vector / np.linalg.norm(frequency_vector)- 即: 频率向量 = 频数向量 / 二阶范数(频数向量)
得到结果
- Dict[element_name, List[token_frequeny]]
List[token_frequeny]
- 这是一个稀疏向量
相似度 chemdataextractor.relex.utils.match_score()
逻辑:
按 element 分组
- suffix 一组,prefix 一组,middle 一组
给定 element
- token 频率向量做 点积训练
特殊情况
长度问题
- 都为零 –> 1
- 单个为零 –> 0
- 正常 –> 点积运算
- 按分组乘上对应比重后加和
Snowball.retrieve_entities(model, sent_parser.scan_result)
返回 generator: yield –> List[(text, (model_name, field_name), field.parse_expression)]
entities: List[(text, (model_name, field_name), field.parse_expression), index, index + token_count]
- text: 空格分割的多个单词(token)
- index: KnuthMorrisPratt 算法差生产生的开始位置
单位解析
工具汇总
chemdataextractor.parse.auto.construct_unit_element
- 作用: 创建
(单位)规则匹配工具(root 中用到的 parse_expression) - 注意: 这里
只是单位规则,不包含数值规则 - 函数接口: construct_unit_element(dimensions: Dimension)
- 作用: 创建
chemdataextractor.parse.quantity.value_element_plain
- 作用: 创建
纯数值匹配规则
- 作用: 创建
chemdataextractor.parse.quantity.value_element
- 作用: 创建
(数值 + 单位)匹配规则 - 函数接口: value_element(units: BaseParserElement) -> BaseParserElement
- 作用: 创建
chemdataextractor.parse.quantity.construct_quantity_re
作用: 在 句子 token 化中使用
线索:
- Sentence::quantity_re
- Sentence.word_tokenizer = ChemWordTokenizer
- ChemWordTokenizer.get_additional_regex(sentence)
函数接口:
- construct_quantity_re(*models)
- models: List[BaseModel]
- 有用数据: List[QuantityModel]
单位匹配规则 construct_unit_element
参考: chemdataextractor.parse.auto
| |
解析 regex2
- 总体
| |
匹配
mmol c-mol centimol centimmol mol/3 mol/3.1 molmolmol molmol mol3.1mol3.2 mol-3 centi-mol milli-mol1.2 )mol (mol (mol3 (mol3) mol/centi-mol replace. Tab to end.
数量级部分
| |
单位和指数部分
括号(),[]; -; 单位本身; 分数 /; 指数 -\d\.\d+
| |
规则分立
- 分别说明,括号、单位、分数、指数等
- 使用 | 连接
- 各个部分作为组建
| |
数值解析 value_element_plain
参考: chemdataextractor.parse.quantity
- 不包含单位
| |
number 数字匹配
| |
pure_number 纯数字
| |
+12.3 -12,3 12·3
fraction 分数
不允许空格版本
1R('^[\+\-–−]?\d+/\d+$')- 分子分母都是整数
+13/21 -12/3
允许空格版本
1(R('^[\+\-–−]?\d+$') + R('^/$') + R('^\d+$')).add_action(merge)- 分子分母都是整数
- *注意*: 正负号不允许与分子分开
+12 / 13 -12 / 32 12 / 33
value_single 完整的单个数表示
| |
| |
range 范围
joined_range 范围匹配不允许空格
| |
- 横线等分割
+12.3-12.3 -12,3~13.2
spaced_range 范围允许空格
| |
| |
- 不许有 ~ 等范围分隔符
to_range 通过 "to" 间隔表示范围
| |
12.3 to 13.5
plusminus_range 通过误差限 "±" 表示范围
| |
12.3 ± 13.2
bracket_range 通过括号 "()" 表示范围
| |
+12.3 (23) +12.3 (2)
between_range 通过 "between … and …" 表示范围
| |
between 12.3 and 13.6
(数值 + 单位)匹配规则 value_element
| |
number
| |
| |
spaced_range
| |
- 空格分割(分词分隔符)
- 注:
允许单位
# 设单位 kg +12.3 kg - 13.1 12.3 kg ~ 13.2 12.3 kg 13.2 # ~ 忽略
construct_quantity_re
相比于 construct_unit_element 多出的正则匹配规则
^((?P<split>[\+\-–−]?\d+([\.\-\−]?\d+)?)|((?P<split2>.*)(\(|\/|\[)))匹配举例:
- +3: 正数
- -2: 负数
- 3.5: 小数
- 2.2-1.3: 范围, 通过 \-\− 连接的两个数
开括号:
- (
- /
- [
Snowball 代码问题记录
chemdataextractor.relex.Cluster::update_pattern()
149 行:
传入的 phrase.relations 问题
- phrase 并不是选定的,而是 self.phrases[-1]
是否有影响?
- self.pattern.relations 用途 提供 Phrase.Relation.Entity.tag 用于 Entitiy 排序
聚类中 Entity.parse_expression 用于解析新句子
- Entity.parse_expression 来源于 model.field.parse_expression
- 只要 Relation 全即可
- 因此 Entity.parse_expression 对 Relation 无影响, 对 phrase 是谁无影响
结论
- 无影响
Snowball 算法概念
相似度
使用余弦相似度 cosine similarity 公式:
\begin{equation} sim(x, y) = \frac{x \cdot y}{ \lVert x \rVert \cdot \lVert y \rVert} \end{equation}Single Pass Clustering (SPC) 算法
优点:
不需要设定 cluster 数量
- 但是,要指定 similarity 阈值
BaseType 类型特点
ModelType 类型
- 特殊字段
self.model_class
- 特殊字段
ListType 和 SetType
- self.field
Model 字段问题
字段类型
BaseType 和 衍生类规定
字段值
容器: BaseModel._values: Dict[name, value]
BaseModel._values[self.name] = value
- self.name 是字段的名称, 通过 ModelMeta 元类自动设置
多个 model 问题
单个句子 多个 model
self.models = [A, B, …]
嵌套 model + 多个 model 问题
self.models = [MolModel] 和 self.models = [Compound, MolModel] 结果不一致
| |
官方通用 parser
chemdataextractor.parse.auto.AutoSentenceParser
构建逻辑:
| |
句子和 token 切分
ChemWordTokenizer
执行切分:
Sentence::tokens()- 调用:
ChemWordTokenizer::get_word_tokens(self, sentence, additional_regex=None) 调用:
ChemWordTokenizer::span_tokenize(sentence.text, additional_regex)通过空格切分
'\s+'- re.finditer(regex, sentence.text, re.U)
二次切分
self._subspan(s, spans[i], spans[i + 1] if i + 1 < len(spans) else None, additional_regex)
跳过特殊词
- self.SPLIT: —, <—-> 等
- self.SPLIT_END_WORD: 's 're 等
- self.SPLIT_START_WORD: "''" 等
- self.NO_SPLIT: mm-hm, mm-mm 等不可分割词
跳过 url
http://开头ftp://开头www.开头
文章作者
上次更新 2022-03-24 (d2bfef1)