如何使用 NLTK 正则表达式模式用 UP/DOWN 指标注释财经新闻?

Posted

技术标签:

【中文标题】如何使用 NLTK 正则表达式模式用 UP/DOWN 指标注释财经新闻?【英文标题】:How to use NLTK Regex patterns to annotate financial news with UP/DOWN indicator? 【发布时间】:2020-08-28 13:22:52 【问题描述】:

我正在复制本文中描述的算法:https://arxiv.org/pdf/1811.11008.pdf

在最后一页上,它描述了使用以下示例提取在标记为“NP JJ”的语法中定义的叶子:营业利润率为 8.3%,而一年前为 11.8%。

我期待看到标有“NP JJ”的叶子,但我没有。我正在为为什么(相对较新的正则表达式)撕扯我的头发。

def split_sentence(sentence_as_string):
    ''' function to split sentence into list of words
    '''
    words = word_tokenize(sentence_as_string)

    return words

def pos_tagging(sentence_as_list):

    words = nltk.pos_tag(sentence_as_list)

    return words

def get_regex(sentence, grammar):

    sentence = pos_tagging(split_sentence(sentence));

    cp = nltk.RegexpParser(grammar) 

    result = cp.parse(sentence) 

    return result


example_sentence = "Operating profit margin was 8.3%, compared to 11.8% a year earlier."

grammar = """JJ : < JJ.∗ > ∗
            V B : < V B.∗ >
            NP : (< NNS|NN >)∗
            NP P : < NNP|NNP S >
            RB : < RB.∗ >
            CD : < CD >
            NP JJ : : < NP|NP P > +(< (>< .∗ > ∗ <) >) ∗ (< IN >< DT > ∗ < RB > ∗ < JJ > ∗ < NP|NP P >) ∗ < RB > ∗(< V B >< JJ >< NP >)∗ < V B > (< DT >< CD >< NP >) ∗ < NP|NP P > ∗ < CD > ∗ < .∗ > ∗ < CD > ∗| < NP|NP P >< IN >< NP|NP P >< CD >< .∗ > ∗ <, >< V B > < IN >< NP|NP P >< CD >"""

grammar = grammar.replace('∗','*')

tree = get_regex(example_sentence, grammar)

print(tree)

【问题讨论】:

奇怪的纸张,但让我们尝试浏览一下 =) 感谢阿尔瓦斯的反馈,真的很有帮助。不知道谁对你的答案投了反对票,我认为这真的很直观。 【参考方案1】:

首先看How to use nltk regex pattern to extract a specific phrase chunk?

让我们看看句子的 POS 标签是什么:

from nltk import word_tokenize, pos_tag

text = "Operating profit margin was 8.3%, compared to 11.8% a year earlier."
pos_tag(word_tokenize(text))

[出]:

[('Operating', 'NN'),
 ('profit', 'NN'),
 ('margin', 'NN'),
 ('was', 'VBD'),
 ('8.3', 'CD'),
 ('%', 'NN'),
 (',', ','),
 ('compared', 'VBN'),
 ('to', 'TO'),
 ('11.8', 'CD'),
 ('%', 'NN'),
 ('a', 'DT'),
 ('year', 'NN'),
 ('earlier', 'RBR'),
 ('.', '.')]

第一个问题!任何标签中都没有JJ

该句子的任何 POS 中都没有 JJ 标签。

让我们回到论文https://arxiv.org/pdf/1811.11008.pdf

尽管如此,NP JJ 并不是最终目标;最终目标是根据一些启发式方法生成UPDOWN 标签。

让我们重新表述一下步骤:

    使用解析器解析句子(在本例中,正则表达式解析器使用某种语法

    确定句子具有模式的信号,该模式可以告诉使用最终标签。

    2a。遍历解析树以提取另一个模式,它告诉我们有关性能指标和数值的信息。

    2b。使用提取的提取数值来确定方向性UP / DOWN 使用一些启发式方法

    2c。用 (2b) 中标识的UP / Down 标记句子

让我们看看我们可以先构建哪个组件。

2b。提取另一种模式,它告诉我们性能指标和数值。

我们知道某个百分比的输出总是来自

CD NN
('8.3', 'CD'), ('%', 'NN')
('11.8', 'CD'), ('%', 'NN')

所以让我们尝试在语法中捕捉到它。

patterns = """
PERCENT: <CD><NN>
"""

PChunker = RegexpParser(patterns)
PChunker.parse(pos_tag(word_tokenize(text)))

[出]:

Tree('S', [('Operating', 'NN'), ('profit', 'NN'), ('margin', 'NN'), ('was', 'VBD'), 
  Tree('PERCENT', [('8.3', 'CD'), ('%', 'NN')]), 
(',', ','), ('compared', 'VBN'), ('to', 'TO'), 
  Tree('PERCENT', [('11.8', 'CD'), ('%', 'NN')]), 
('a', 'DT'), ('year', 'NN'), ('earlier', 'RBR'), ('.', '.')])

现在,我们如何得到这个:

    识别信号表明句子具有可以告诉使用最终标签的模式。

我们知道&lt;PERCENT&gt; compared to &lt;PERCENT&gt; 是一个很好的模式,所以让我们尝试对其进行编码。

我们知道compared to 有标签VBN TO 来自

 ('8.3', 'CD'),
 ('%', 'NN'),
 (',', ','),
 ('compared', 'VBN'),
 ('to', 'TO'),
 ('11.8', 'CD'),
 ('%', 'NN'),

这个怎么样:

patterns = """
PERCENT: <CD><NN>
P2P: <PERCENT><.*>?<VB.*><TO><PERCENT>
"""

PChunker = RegexpParser(patterns)
PChunker.parse(pos_tag(word_tokenize(text)))

[出]:

Tree('S', [('Operating', 'NN'), ('profit', 'NN'), ('margin', 'NN'), ('was', 'VBD'), 
           Tree('P2P', [
               Tree('PERCENT', [('8.3', 'CD'), ('%', 'NN')]), 
               (',', ','), ('compared', 'VBN'), ('to', 'TO'), 
               Tree('PERCENT', [('11.8', 'CD'), ('%', 'NN')])]
               ), 
           ('a', 'DT'), ('year', 'NN'), ('earlier', 'RBR'), ('.', '.')]
    )

但该模式可以是任意数字。我们需要performance indicator 的信号

由于我不是金融领域的领域专家,简单地使用operating profit margin 的存在可能是一个很好的信号,即

from nltk import word_tokenize, pos_tag, RegexpParser

patterns = """
PERCENT: <CD><NN>
P2P: <PERCENT><.*>?<VB.*><TO><PERCENT>
"""

PChunker = RegexpParser(patterns)


text = "Operating profit margin was 8.3%, compared to 11.8% a year earlier."

indicators = ['operating profit margin']
for i in indicators:
    if i in text.lower():
        print(PChunker.parse(pos_tag(word_tokenize(text))))

[出]:

(S
  Operating/NN
  profit/NN
  margin/NN
  was/VBD
  (P2P
    (PERCENT 8.3/CD %/NN)
    ,/,
    compared/VBN
    to/TO
    (PERCENT 11.8/CD %/NN))
  a/DT
  year/NN
  earlier/RBR
  ./.)

现在我们如何获得UP / DOWN

2b。使用提取的数值来确定方向性 UP / DOWN 使用一些启发式方法

仅从例句来看,除了“earlier”之外,没有其他任何东西可以告诉我们关于数字的先行性。

所以让我们假设一下,如果我们有PERCENT VBN TO PERCENT earlier 模式,我们说第二个百分比是一个较旧的数字。

import nltk
from nltk import word_tokenize, pos_tag, RegexpParser

patterns = """
PERCENT: <CD><NN>
P2P: <PERCENT><.*>?<VB.*><TO><PERCENT><.*>*<RBR>
"""

def traverse_tree(tree, label=None):
    # print("tree:", tree)
    for subtree in tree:
        if type(subtree) == nltk.tree.Tree and subtree.label() == label:
            yield subtree

PChunker = RegexpParser(patterns)

parsed_text = PChunker.parse(pos_tag(word_tokenize(text)))
for p2p in traverse_tree(parsed_text, 'P2P'):
    print(p2p)

[出]:

(P2P
  (PERCENT 8.3/CD %/NN)
  ,/,
  compared/VBN
  to/TO
  (PERCENT 11.8/CD %/NN)
  a/DT
  year/NN
  earlier/RBR)

还有UP / DOWN 标签?

import nltk
from nltk import word_tokenize, pos_tag, RegexpParser

patterns = """
PERCENT: <CD><NN>
P2P: <PERCENT><.*>?<VB.*><TO><PERCENT><.*>*<RBR>
"""

PChunker = RegexpParser(patterns)


def traverse_tree(tree, label=None):
    # print("tree:", tree)
    for subtree in tree:
        if type(subtree) == nltk.tree.Tree and subtree.label() == label:
            yield subtree

def labelme(text):
    parsed_text = PChunker.parse(pos_tag(word_tokenize(text)))
    for p2p in traverse_tree(parsed_text, 'P2P'):
        # Check if the subtree ends with "earlier".
        if p2p.leaves()[-1] ==  ('earlier', 'RBR'):
            # Check if which percentage is larger. 
            percentages = [float(num[0]) for num in  p2p.leaves() if num[1] == 'CD']
            # Sanity check that there's only 2 numbers from our pattern.
            assert len(percentages) == 2
            if percentages[0] > percentages[1]:
                return 'DOWN'
            else:
                return 'UP'

text = "Operating profit margin was 8.3%, compared to 11.8% a year earlier."

labelme(text)

现在问题来了……

**你想写这么多规则并使用上面的labelme() 捕获它们吗? **

你写的模式是万无一失的吗?

例如是否存在使用指标比较百分比和“较早”的模式不会像预期的“上升”或“下降”的情况

我们为什么要在 AI 时代编写规则?

你是否已经有人工注释的数据,其中有句子及其对应的 UP/DOWN 标签?如果有,让我建议像 https://allennlp.org/tutorials 或 https://github.com/huggingface/transformers/blob/master/notebooks/03-pipelines.ipynb

【讨论】:

以上是关于如何使用 NLTK 正则表达式模式用 UP/DOWN 指标注释财经新闻?的主要内容,如果未能解决你的问题,请参考以下文章

NLTK 正则表达式和 CFG

NLTK 正则表达式标记器在正则表达式中不能很好地处理小数点

如何使用正则表达式或熊猫过滤 NLTK 的 FreqDist 计数器

NLTK实现文本切分

NLTK - nltk.tokenize.RegexpTokenizer - 正则表达式未按预期工作

NLTK RegEx Chunker 未使用通配符捕获已定义的语法模式