LayoutLM:深入解析文件影象理解的強大模型

LayoutLM:深入解析文件影象理解的強大模型

1. 簡要介紹

在數位化時代,我們每天都會接觸到大量的文件,包括掃描件、表格、收據等。如何讓計算機理解這些 包含文字和佈局資訊的文件,一直是人工智慧領域的研究重點。傳統的自然語言處理(NLP)模型主要關注文字內容,而忽略了文件的佈局和視覺資訊,這在處理文件影象時會遇到瓶頸. 為了解決這個問題,微軟在2020年6月推出了 LayoutLM 模型.

  • 背景歷史:
    • 在LayoutLM之前,NLP模型主要關注文字輸入,而計算機視覺模型主要關注影象輸入.
    • LayoutLM的出現,首次將 影象、文字和2D位置 資訊作為輸入,實現了 多模態 資訊處理.
  • 開發團隊: LayoutLM由 Yiheng Xu, Minghao Li, Lei Cui, Shaohan Huang, Furu Wei, 和 Ming Zhou 共同開發.
  • 功能:
    • LayoutLM旨在 理解文件影象,從而實現 資訊提取、表單理解、收據理解和文件分類 等任務.
    • 它透過同時建模文字和佈局資訊之間的互動,從而 顯著提高 了文件影象理解的效能.
    • LayoutLM 可以從 掃描文件或影象 中提取特定的、重點資訊.

2. 架構設計

LayoutLM 的架構基於 BERT (Bidirectional Encoder Representations from Transformers). 它在 BERT 的基礎上增加了 兩種新的輸入嵌入

  • 2D 位置嵌入 (2D Position Embeddings): 用於表示文件中 文字的空間位置. 與傳統的只考慮單詞順序的位置嵌入不同,2D 位置嵌入使用每個單詞的 邊界框座標 (x0, y0, x1, y1) 來定義其在頁面上的位置. 文件的左上角被視為座標系的原點 (0, 0). 這些座標被歸一化到 0-1000 的範圍內,然後嵌入到模型可以理解的數值表示中.
  • 影象嵌入 (Image Embeddings): 用於 整合視覺資訊. LayoutLM 將影象分割成與 OCR 文字對應的區域,並利用這些區域的視覺特徵生成影象嵌入. 影象嵌入有助於模型理解文件的視覺風格,從而增強文件理解能力.

預訓練 (Pre-training):

  • LayoutLM 使用 Masked Visual-Language Model (MVLM) 進行預訓練. MVLM 是受掩碼語言模型啟發的技術,但它同時考慮文字和2D位置嵌入作為輸入. 模型學習預測被掩碼的單詞,透過上下文的文字和空間位置資訊進行預測.
  • LayoutLM 還使用 Multi-label Document Classification (MDC) 進行預訓練. 該任務訓練 LayoutLM 處理帶有多個標籤的掃描文件,使其能夠從多個領域聚合知識並生成更好的文件級別表示,儘管它不是大型模型預訓練的必要條件.
  • LayoutLM的預訓練使用了 IIT-CDIP Test Collection 1.0 資料集,該資料集包含超過600萬份文件和1100萬份掃描文件影象.

3. 能處理的文件型別

LayoutLM 擅長處理那些 佈局和視覺資訊對於理解內容至關重要 的文件. 包括以下型別:

  • 表單 (Forms): LayoutLM 在表單理解任務上取得了非常好的效果,能夠準確地處理具有特定欄位和佈局的結構化文件. FUNSD 資料集 通常用於訓練和評估 LayoutLM 的表單理解能力.
  • 收據 (Receipts): LayoutLM 在收據理解任務中也表現出色. 它可以從收據中提取資料,並利用文字和佈局資訊. SROIE 資料集 用於微調 LayoutLM 的收據資料.
  • 掃描文件 (Scanned documents): LayoutLM 能夠有效地處理掃描文件,同時建模文字和佈局資訊之間的互動.
  • 商務文件 (Business Documents): LayoutLM 可應用於各種商務文件,包括:
    • 採購訂單 (Purchase orders)
    • 財務報告 (Financial reports)
    • 商務郵件 (Business emails)
    • 銷售協議 (Sales agreements)
    • 供應商合同 (Vendor contracts)
    • 信件 (Letters)
    • 發票 (Invoices)
    • 簡歷 (Resumes)
  • 其他視覺豐富的文件 (Other Visually Rich Documents): LayoutLM 適用於任何視覺豐富的文件,在這些文件中,佈局顯著增強了語言表示.

4. 使用技巧

  • OCR 引擎: 使用 OCR (Optical Character Recognition) 引擎 (例如 Tesseract)從文件影象中提取文字及其對應的邊界框.
  • 邊界框歸一化: 在將邊界框座標輸入 LayoutLM 之前,將它們歸一化到 0-1000 範圍. 透過將邊界框座標除以文件影象的原始寬度和高度,然後乘以 1000 進行歸一化.
  • 特殊標記: LayoutLM 使用特殊標記來處理文字,包括:
    • [CLS]: 分類標記,用於序列分類,並且是序列的第一個標記.
    • [SEP]: 分隔符標記,用於分隔多個序列.
    • [PAD]: 填充標記,用於填充不同長度的序列.
    • [MASK]: 掩碼標記,用於掩碼語言建模.
    • [UNK]: 未知標記,用於表示詞彙表中未知的單詞.
  • 選擇合適的 Tokenizer: 使用 LayoutLMTokenizer 或 LayoutLMTokenizerFast 進行分詞. LayoutLMTokenizerFast 是一個更快的版本,基於 Hugging Face 的 tokenizers 庫.

5. 執行環境要求

LayoutLM的執行環境要求主要包括以下幾個方面:

  • 程式語言和框架:LayoutLM可以使用 PyTorchTensorFlow 框架進行實現和訓練。
    • PyTorch 是一個開源的機器學習庫,常用於實現神經網路和深度學習模型。
    • TensorFlow 是另一個流行的開源機器學習庫,也用於實現神經網路和深度學習模型。
  • Hugging Face Transformers 庫:這是使用LayoutLM的核心庫,提供了預訓練模型、tokenizer 以及其他工具。
    • 這個庫提供了LayoutLM模型的各種實現,包括用於不同任務的變體,例如LayoutLMModel, LayoutLMForMaskedLM, LayoutLMForSequenceClassification, LayoutLMForTokenClassification 和 LayoutLMForQuestionAnswering。
  • OCR引擎:需要一個 OCR (光學字元識別) 引擎 從文件影象中提取文字及其對應的邊界框。
    • 常用的OCR引擎是 Tesseract
    • OCR引擎將影象中的文字轉換為機器可讀的文字,並提供位置嵌入所需的座標。
  • 影象處理庫:需要影象處理庫來處理文件影象,例如 Pillow (PIL)
  • 資料處理庫:需要使用資料處理庫,例如 NumPyPandas 進行資料處理。
  • 硬體要求: 如果要進行模型的訓練,GPU 可以顯著加快訓練速度。
  • Python 環境: 需要 Python 程式設計環境,並安裝所需的庫。
  • Tokenizer:需要使用 LayoutLMTokenizerLayoutLMTokenizerFast 進行分詞。 LayoutLMTokenizerFast 是一個更快的版本,基於Hugging Face的tokenizers庫。
    • Tokenizer負責將文字分割成模型可以理解的token。
  • 資料集: 不同的任務需要不同的資料集。例如,FUNSD資料集用於表單理解,SROIE資料集用於收據理解,RVL-CDIP資料集用於文件影象分類。

總而言之,使用LayoutLM需要一個配置了適當庫(如 Transformers, PyTorch 或 Tensorflow,以及OCR引擎)的Python環境,以及一個能夠進行資料預處理和模型訓練的平臺。

6. 程式碼範例

以下是一個使用 LayoutLM 進行序列分類的 PyTorch 程式碼範例:

import os
import numpy as np
import pandas as pd
from tqdm.auto import tqdm
from sklearn.model_selection import train_test_split
import pytesseract
from PIL import Image, ImageDraw, ImageFont
import torch
from datasets import Dataset, Features, Sequence, ClassLabel, Value, Array2D
from transformers import LayoutLMTokenizer, LayoutLMForSequenceClassification, AdamW

# Load the dataset
# Assuming you have a dataframe named 'df' with columns 'image_path', 'words', 'bbox', 'label'
# The bounding box coordinates should be normalized

# Create a dictionary for label to index mapping
labels = df['label'].unique().tolist()
label2idx = {label: idx for idx, label in enumerate(labels)}

# Load the tokenizer and model
tokenizer = LayoutLMTokenizer.from_pretrained("microsoft/layoutlm-base-uncased")

# Define a function to encode training examples
def encode_training_example(example, max_seq_length=512, pad_token_box=):
    words = example['words']
    normalized_word_boxes = example['bbox']
    assert len(words) == len(normalized_word_boxes)
    token_boxes = []
    for word, box in zip(words, normalized_word_boxes):
        word_tokens = tokenizer.tokenize(word)
        token_boxes.extend([box] * len(word_tokens))
    special_tokens_count = 2
    if len(token_boxes) > max_seq_length - special_tokens_count:
       token_boxes = token_boxes[: (max_seq_length - special_tokens_count)]
    token_boxes = [] + token_boxes + []
    encoding = tokenizer(' '.join(words), padding='max_length', truncation=True)
    input_ids = tokenizer(' '.join(words), truncation=True)["input_ids"]
    padding_length = max_seq_length - len(input_ids)
    token_boxes += [pad_token_box] * padding_length
    encoding['bbox'] = token_boxes
    encoding['label'] = label2idx[example['label']]
    assert len(encoding['input_ids']) == max_seq_length
    assert len(encoding['attention_mask']) == max_seq_length
    assert len(encoding['token_type_ids']) == max_seq_length
    assert len(encoding['bbox']) == max_seq_length
    return encoding

# Function to prepare data loaders from dataframe
def training_dataloader_from_df(data_df):
    dataset = Dataset.from_pandas(data_df)
    features = Features({
        'words': Sequence(Value('string')),
        'bbox': Sequence(Sequence(Value('int64'))),
        'label': Value('string'),
        })

    encoded_dataset = dataset.map(encode_training_example, features=features, remove_columns=dataset.column_names)
    encoded_dataset.set_format(type='torch', columns=['input_ids','bbox', 'attention_mask', 'token_type_ids', 'label'])
    dataloader = torch.utils.data.DataLoader(encoded_dataset, batch_size=4, shuffle=True)
    return dataloader

# Split train and validation datasets
train_data, valid_data = train_test_split(df, test_size=0.2, random_state=42)

# Create dataloaders
train_dataloader = training_dataloader_from_df(train_data)
valid_dataloader = training_dataloader_from_df(valid_data)

# Define the device to train on
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

# Load the model
model = LayoutLMForSequenceClassification.from_pretrained(
    "microsoft/layoutlm-base-uncased", num_labels=len(label2idx)
)
model.to(device);

# Define optimizer
optimizer = AdamW(model.parameters(), lr=4e-5)

# Training loop
num_epochs = 3
for epoch in range(num_epochs):
    print("Epoch:", epoch)
    training_loss = 0.0
    training_correct = 0
    model.train()
    for batch in tqdm(train_dataloader):
        labels = batch["label"].to(device)
        outputs = model(
           input_ids=batch["input_ids"].to(device), bbox=batch["bbox"].to(device),
            attention_mask=batch["attention_mask"].to(device),
            token_type_ids=batch["token_type_ids"].to(device), labels=labels
        )
        loss = outputs.loss
        training_loss += loss.item()
        predictions = outputs.logits.argmax(-1)
        training_correct += (predictions == labels).float().sum()
        loss.backward()
        optimizer.step()
        optimizer.zero_grad()
    print("Training Loss:", training_loss / batch["input_ids"].shape)
    training_accuracy = 100 * training_correct / len(train_data)
    print("Training accuracy:", training_accuracy.item())
    validation_loss = 0.0
    validation_correct = 0
    model.eval()
    with torch.no_grad():
        for batch in tqdm(valid_dataloader):
            labels = batch["label"].to(device)
            outputs = model(
                input_ids=batch["input_ids"].to(device), bbox=batch["bbox"].to(device),
                attention_mask=batch["attention_mask"].to(device),
                token_type_ids=batch["token_type_ids"].to(device), labels=labels
            )
            loss = outputs.loss
            validation_loss += loss.item()
            predictions = outputs.logits.argmax(-1)
            validation_correct += (predictions == labels).float().sum()
    print("Validation Loss:", validation_loss / batch["input_ids"].shape)
    validation_accuracy = 100 * validation_correct / len(valid_data)
    print("Validation accuracy:", validation_accuracy.item())

這個範例程式碼展示了如何使用 LayoutLM 進行文件分類. 其中,需要注意的是,輸入資料需要包含文字內容(words),對應的邊界框座標(bbox),以及類別標籤(label),且邊界框座標需要歸一化到 0-1000 的範圍內.

7. 常見問題

  • LayoutLM 和 BERT 的區別是什麼?
    • BERT 主要處理文字資訊,而 LayoutLM 同時處理文字、佈局和視覺資訊.
    • LayoutLM 透過 2D位置嵌入影象嵌入 來整合佈局和視覺資訊,使其能夠更好地理解文件影象.
  • 如何處理不同大小的文件影象?
    • 透過 歸一化邊界框座標,使 LayoutLM 能夠處理各種大小的文件影象.
  • LayoutLM 可以處理中文文件嗎?
    • LayoutLM 可以處理多語言文件,包括中文,前提是使用合適的 tokenizer 和預訓練模型.
  • 如何選擇合適的預訓練模型?
    • Hugging Face Transformers 庫提供了各種預訓練的 LayoutLM 模型。您可以根據自己的任務和資料選擇合適的模型.
  • 如何提高 LayoutLM 的效能?
    • 使用高質量的 OCR 結果.
    • 使用與任務相關的 微調資料.
    • 調整模型 引數,例如學習率和訓練輪數.