LayoutLM: 문서 이미지 이해를 위한 강력한 모델 심층 분석
title: LayoutLM: 문서 이미지 이해를 위한 강력한 모델 심층 분석 date: 2024-12-21 slug: layoutlm authors: [kevinpeng] categories: - AI助手 tags: - layoutlm - 文档处理 description: LayoutLM 문서 이미지 이해 모델 심층 분석. 마이크로소프트에서 공개한 다중 모달 AI 모델로, 텍스트와 레이아웃 정보를 결합하여 양식 처리, 영수증 인식 등 다양한 시나리오에 적용 가능합니다.
1. 간략한 소개
디지털 시대에 우리는 매일 스캔 문서, 표, 영수증 등 다양한 종류의 문서를 접합니다. 컴퓨터가 이러한 텍스트와 레이아웃 정보를 모두 포함하는 문서를 이해하도록 만드는 방법은 인공지능 분야에서 오랫동안 연구되어 온 핵심 주제입니다. 기존의 자연어 처리(NLP) 모델은 주로 텍스트 내용에 초점을 맞추고, 문서의 레이아웃 및 시각 정보는 무시해 왔기 때문에 문서 이미지를 처리할 때 한계에 부딪히곤 했습니다. 이 문제를 해결하기 위해 마이크로소프트는 2020년 6월 LayoutLM 모델을 발표했습니다.
- 배경 및 역사:
- LayoutLM 이전에는 NLP 모델이 주로 텍스트 입력에 집중했고, 컴퓨터 비전 모델은 주로 이미지 입력에 집중했습니다.
- LayoutLM은 이미지, 텍스트, 2차원 위치 정보를 동시에 입력으로 받아 다중 모달(multi-modal) 정보 처리를 실현한 최초의 모델입니다.
- 개발 팀: LayoutLM은 Yiheng Xu, Minghao Li, Lei Cui, Shaohan Huang, Furu Wei, 그리고 Ming Zhou가 공동 개발했습니다.
- 기능:
- LayoutLM은 문서 이미지의 의미를 이해함으로써 정보 추출, 양식 해석, 영수증 해석, 문서 분류 등의 작업을 수행할 수 있도록 설계되었습니다.
- 텍스트 정보와 레이아웃 정보 간 상호작용을 동시에 모델링함으로써 문서 이미지 이해 성능을 현저히 향상시킵니다.
- LayoutLM은 스캔 문서 또는 이미지에서 특정하고 중요한 정보를 추출할 수 있습니다.
2. 아키텍처 설계
LayoutLM의 아키텍처는 BERT(Bidirectional Encoder Representations from Transformers)를 기반으로 합니다. BERT에 기반하여 LayoutLM은 두 가지 새로운 입력 임베딩을 추가합니다:
- 2D 위치 임베딩(2D Position Embeddings): 문서 내 텍스트의 공간적 위치를 표현하기 위한 임베딩입니다. 기존의 단어 순서만 고려하는 위치 임베딩과 달리, 2D 위치 임베딩은 각 단어의 바운딩 박스 좌표(x₀, y₀, x₁, y₁)를 사용해 페이지 상에서의 위치를 정의합니다. 문서의 왼쪽 위 모서리를 좌표계 원점(0, 0)으로 간주합니다. 이러한 좌표는 0–1000 범위로 정규화된 후, 모델이 이해할 수 있는 수치 형태의 임베딩으로 변환됩니다.
- 이미지 임베딩(Image Embeddings): 시각 정보 통합을 위한 임베딩입니다. LayoutLM은 이미지를 OCR 텍스트와 대응하는 영역으로 분할하고, 해당 영역의 시각적 특징을 활용해 이미지 임베딩을 생성합니다. 이미지 임베딩은 모델이 문서의 시각적 스타일을 이해하도록 도와, 문서 해석 능력을 강화합니다.
사전 학습(Pre-training):
- LayoutLM은 마스킹 시각-언어 모델(Masked Visual-Language Model, MVLM)을 사용해 사전 학습됩니다. MVLM은 마스킹 언어 모델(Masked Language Model)에서 영감을 얻은 기술로, 텍스트와 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]: 어휘 집합에 없는 단어를 나타내기 위한 ‘알 수 없음’ 토큰입니다.
- 적절한 토크나이저 선택: 토크나이징에는 LayoutLMTokenizer 또는 LayoutLMTokenizerFast를 사용합니다. LayoutLMTokenizerFast는 Hugging Face의
tokenizers라이브러리 기반의 더 빠른 버전입니다.
5. 실행 환경 요구 사항
LayoutLM의 실행 환경 요구 사항은 다음과 같습니다:
- 프로그래밍 언어 및 프레임워크: LayoutLM은 PyTorch 또는 TensorFlow 프레임워크를 사용해 구현 및 훈련할 수 있습니다.
- PyTorch는 신경망 및 딥러닝 모델 구현에 널리 쓰이는 오픈소스 머신러닝 라이브러리입니다.
- TensorFlow는 신경망 및 딥러닝 모델 구현에 사용되는 또 다른 인기 있는 오픈소스 머신러닝 라이브러리입니다.
- Hugging Face Transformers 라이브러리: LayoutLM 사용을 위한 핵심 라이브러리로, 사전 훈련된 모델, 토크나이저 및 기타 도구를 제공합니다.
- 이 라이브러리는 LayoutLMModel, LayoutLMForMaskedLM, LayoutLMForSequenceClassification, LayoutLMForTokenClassification, LayoutLMForQuestionAnswering 등 다양한 작업에 맞춘 LayoutLM 모델 변형을 제공합니다.
- OCR 엔진: 문서 이미지에서 텍스트와 그에 대응하는 바운딩 박스를 추출하기 위해 OCR(광학 문자 인식) 엔진이 필요합니다.
- 일반적으로 사용되는 OCR 엔진은 Tesseract입니다.
- OCR 엔진은 이미지 내 텍스트를 기계가 읽을 수 있는 텍스트로 변환하고, 위치 임베딩에 필요한 좌표를 제공합니다.
- 이미지 처리 라이브러리: 문서 이미지 처리를 위해 Pillow(PIL)와 같은 이미지 처리 라이브러리가 필요합니다.
- 데이터 처리 라이브러리: 데이터 전처리를 위해 NumPy, Pandas와 같은 라이브러리가 필요합니다.
- 하드웨어 요구 사항: 모델 훈련을 수행하려면 GPU를 사용하면 훈련 속도를 현저히 높일 수 있습니다.
- Python 환경: 필요한 라이브러리를 설치한 Python 프로그래밍 환경이 필요합니다.
- 토크나이저: 토크나이징을 위해 LayoutLMTokenizer 또는 LayoutLMTokenizerFast를 사용해야 합니다. LayoutLMTokenizerFast는 Hugging Face의
tokenizers라이브러리 기반의 더 빠른 버전입니다.- 토크나이저는 텍스트를 모델이 이해할 수 있는 토큰 단위로 분할하는 역할을 합니다.
- 데이터셋: 작업 유형에 따라 서로 다른 데이터셋이 필요합니다. 예를 들어, 양식 해석에는 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을 사용해 문서 분류 작업을 수행하는