unilm 在进行信息提取时,如何将单词聚类成语义实体?

bvpmtnay  于 2个月前  发布在  其他
关注(0)|答案(2)|浏览(33)

你好,大家!

关于从表单中提取信息的问题,我有一个疑问。不确定是否应该在正确的位置提出这个问题。如果你认为这个帖子应该放在其他地方,请告诉我。

我一直在网络上寻找这个问题的答案已经有一段时间了,但至今仍未找到令人满意的答案。

我的目标是自动提取表单的内容,将其作为(问题;答案)对的列表。
已经有一些教程和笔记本专门介绍如何使用“transformers” python库中的LayoutLMForTokenClassification(例如:here ;或 here )来执行信息提取。然而,通过这种方式,我们只能实现对单词的标记。
为了实现我期望的结果,我们需要做两件更多的事情:
A] 首先,我们需要将单词组合成语义实体;即将组成字段名称/构成问题的单词组合在一起;或者将构成答案的单词组合在一起(例如:首先是值为3.5 kg的单词,然后是单位的单词)。
B] 然后我们需要确定/提取这些实体之间的关系。例如,我们需要找到将一个语义实体(问题)与一个关联的答案(可能也不存在)之间的关系。
已经有一些资源专门用于执行关系提取部分。例如,微软的unilm团队开发了LayoutLMv2ForRelationExtraction模型(这里有一个教程笔记本),实际上,他们在“transformers”仓库中打开了一个PR来添加这个模型类。PR的最新版本是here
然而,为了起作用,这个模型假设我们已经知道语义实体,因此需要A]步骤。但是,那么我们如何实现这一步呢?这就是我在努力解决的问题。
使用LayoutLMTokenClassification的输出可能会看起来像这样:

然而,要使用RelationExtraction模型,我们需要这样的东西:

当然,我们有标签数据的这个信息,但在推理时,对于整个新文档没有这个信息。
那么,对于新文档,我们如何做到这一点呢?OCR输出的单词顺序可能与我们需要考虑单词的实际顺序不一致,如果我们仅仅依赖于标签值来进行解码/构建语义实体的话。
我们可以用各种方式来建模这个问题:尤其是,可以将它视为在一个有向图的节点之间找到边的问题,其中每个顶点的入度和出度值都不超过1,且没有循环。
我已经根据计算单词边界框之间的距离来制作了一个简单的算法,以便考虑到单词相对于彼此的空间位置中包含的信息,但它离完美还很远,有时会产生错误的结果。
在我看来,LayoutLM-based模型本来就应该考虑单词的语义信息和空间信息,以便在LayoutLMForTokenClassification模型中进行适当的标记(特别是,如果我们参考FUNSD数据集),所以遗憾的是,这个信息("每个标记/每个单词属于哪个语义实体")没有被它输出真是太可惜了。
有人知道如何在A]步骤中进行操作吗?特别是,我们需要创建一个专用模型,如LayoutLMForTokenClassification和LayoutLMv2ForRelationExtraction,才能实现这一点吗?还是有可能以某种方式重用/升级LayoutLMForTokenClassification模型,以便产生允许进行语义实体构建任务的输出?

nukf8bse

nukf8bse1#

我已经在我的项目marie-ai中实现了这个功能,目前还在进行中。代码是模块化的,所以你可以提取代码并独立使用每个部分。

以下是你可以查看如何实现的代码参考:https://github.com/gregbugaj/marie-ai/blob/main/marie/executor/ner/ner_extraction_executor.py
在文档/docs/models/named-entity-recognition中,你可以看到配置和数据是如何组织的。
简而言之,我使用CVAT创建了一个带注解的数据集,然后将其从CVAT提供的COCO格式转换为FUNSD格式,并使用unilm进行了微调。

在模型中,我们有特定的标签,而不是通用的问题/答案,这是FUNSD所没有的。有一个工具可以完成这种转换。

示例用法

from marie.executor import NerExtractionExecutor
  from marie.utils.image_utils import hash_file

  # setup executor
  models_dir = ("/mnt/data/models/")
  executor = NerExtractionExecutor(models_dir)

  img_path = "/tmp/sample.png"
  checksum = hash_file(img_path)

  # invoke executor
  docs = None
  kwa = {"checksum": checksum, "img_path": img_path}
  results = executor.extract(docs, **kwa)

  print(results)

配置片段:

{
    "question_answer_map" : {
        "member_name": "member_name_answer",
        "member_number": "member_number_answer",
        "pan": "pan_answer",
        "dos": "dos_answer",
        "patient_name": "patient_name_answer"
    }
}

结果

{
      "page": 0,
      "category": "DOS",
      "value": {
        "question": {
          "line": 13,
          "key": "DOS",
          "bbox": [
            97.774,
            1975.2,
            355.074,
            55.964
          ],
          "score": 0.999998,
          "text": {
            "text": "DATE OF SERVICE:",
            "confidence": 0.9999
          }
        },
        "answer": {
          "line": 13,
          "key": "DOS_ANSWER",
          "bbox": [
            532.611,
            1975.2,
            432.264,
            52.672
          ],
          "score": 0.999642,
          "text": {
            "text": "7/25/2022 - 7/25/2022",
            "confidence": 0.9997
          }
        }
      }

"那么,对于新文档,我们应该如何做呢?OCR输出的单词顺序可能与我们需要考虑单词的实际顺序不一致,如果我们仅仅依赖标签值来执行解码/构建语义实体的话。"
为此,我有一个自定义的TextExtractionExecutor,它可以执行边界框检测、线聚合和ICR。你可以再次查看源代码。

示例:

executor = TextExtractionExecutor()
        results = executor.extract(docs, **kwa)

        print(results)
        store_json_object(results, os.path.join("/tmp/fragments", "results.json"))

结果

{
        "id": 188,
        "text": "2699.00",
        "confidence": 0.9999,
        "box": [
          1897,
          1998,
          145,
          29
        ],
        "line": 60,
        "word_index": 240
      },


 {
        "line": 3,
        "wordids": [
          223,
          225,
          229
        ],
        "text": "PO BOX 39034",
        "bbox": [
          2402,
          164,
          107,
          30
        ],
        "confidence": 0.9989
      },

这将给你正确的文本行文本和它们的wordid

uhry853o

uhry853o2#

你好,Grebugaj,

感谢你的回答。
我已经查看并调查了你提到的marie-ai的代码。如果我理解正确的话:

  • 为了将单词聚合成语义实体,你假设给定语义实体的所有单词都只包含在单行中,并且单词按照阅读顺序给出。似乎没有考虑到'B-'或'I-'标记的单词的顺序,将它们放入同一个实体的关键是它们属于同一个实体类型,以及遵循整个文档中单词的顺序。为了补偿TokenClassification模型输出的标记可能存在的错误,允许用户合并位于另一个类型语义实体“中间”的单词。
  • 一行被定义为一个包围可能包含多个单词的边界框,由使用的OCR工具输出。因此,在给定文档/文档的“行”(整宽度)上可能存在多行这样的线?此外,目前不确定这样的“行”是否可以包含位于文档的不同垂直位置上的多个真实行(不同的垂直位置)上的单词。
  • 为了找到(QUESTION; ANSWER)对,你使用的算法依赖于上述过程将单词聚合成语义实体的输出,并假定遵守阅读顺序:在找到一个问题后,将其与根据阅读顺序跟随的所有实体关联起来,这些实体与问题兼容,直到遇到另一个问题实体。

你关于TextExtractionExecutor的说法很有趣:基本上,你通过训练一个专门用于输出数据的模型来解决这个问题,使得上述过程所做出的假设得到满足。
我尝试在自己的文档上执行文本提取器,看看输出会是什么样子,并确保我正确理解了上面过程所使用的概念“行”。但当然,我缺少适当的模型权重("Loading from ./model_zoo/unilm/dit/text_detection/td-syn_dit-l_mrcnn.pth"),所以最后失败了,无法做到这一点。
无论如何,关键是看起来我们确实需要一个机器学习模型来处理执行令牌分类后的数据分析,以便为管道的下一步做好准备,尤其是(QUESTION; ANSWER)提取。
在这种情况下,我想我会调查使用LayoutLMForTokenClassification模型的工作,以构建另一个模型,该模型的角色将是直接输出语义实体,或者至少将每个令牌与其标识其语义实体及其在其中的位置的ID关联起来。的确,假设LayoutLMForTokenClassification模型能够正确完成工作,它本身已经需要隐式地知道这些信息,以便区分标签应以'B-'开头的令牌和那些应以'I-'开头的令牌;所以从理论上讲,这只是使这些信息在模型输出中可用的问题。
无论如何,再次感谢您分享您如何解决这个问题的工作!

相关问题