PDF文件轉換成其他格式常常是個大難題,大量的信息被鎖在PDF里,AI應用無法直接訪問。如果能把PDF文件或其對應的圖像轉換成結構化或半結構化的機器可讀格式,那就能大大緩解這個問題,同時也能顯著增強人工智能應用的知識庫。
嘿,各位AI探險家們!今天我們將踏上了一段奇妙的PDF解析之旅,探索了那些不用OCR(光學字符識別)也能搞定PDF的神奇小模型。就像哈利·波特不用魔杖也能施法一樣,這些小模型用神經網絡直接“讀懂”PDF,省去了繁瑣的OCR步驟,簡直是AI界的“無杖魔法”!
RAG(Retrieval-Augmented Generation)基建之PDF解析的“魔法”與“陷阱”
文章目錄
- 概述
- 詳細介紹
- Donut
- 編碼器
- 訓練
- 微調
- Nougat
- 模型架構
- 訓練數據集的構建
- Pix2Struct
- 模型架構
- 預訓練任務
- 預訓練數據集
- 微調
- 一絲感悟
- 關于預訓練任務
- 關于預訓練數據
- 關于性能
- 基于流水線 vs. OCR-Free
- OCR-Free小型模型方法的局限性
- 結論
概述
之前介紹的基于流水線的PDF解析方法主要使用OCR引擎進行文本識別。然而,這種方法計算成本高,對語言和文檔類型的靈活性較差,且OCR錯誤可能影響后續任務。
因此,應該開發OCR-Free方法,如圖1所示。這些方法不顯式使用OCR來識別文本,而是使用神經網絡隱式完成任務。本質上,這些方法采用端到端的方式,直接輸出PDF解析結果。
OCR-Free vs. 流水線:誰更香?
從結構上看,OCR-Free方法比基于流水線的方法更簡單。OCR-Free方法主要需要注意的方面是模型結構的設計和訓練數據的構建。OCR-Free方法雖然一步到位,避免了中間步驟的“損耗”,但它的訓練和推理速度有點慢,像是一輛豪華跑車,雖然性能強大,但油耗高。而基于流水線的方法則像是一輛經濟型小車,雖然步驟多,但每個模塊都很輕量,適合大規模部署。
接下來,我們將介紹幾種具有代表性的OCR-Free小型模型PDF解析框架:
-
- Donut:PDF解析界的“甜甜圈”
Donut這個小家伙,別看它名字甜,干起活來可是一點都不含糊。它用Swin Transformer當“眼睛”,BART當“嘴巴”,直接把PDF圖像“吃”進去,吐出一串JSON格式的“甜點”。不用OCR,全靠神經網絡,簡直是PDF解析界的“甜品大師”!
- Donut:PDF解析界的“甜甜圈”
-
- Nougat:PDF解析界的“牛軋糖”
Nougat,名字聽起來就很有嚼勁,它的絕活是把PDF圖像變成Markdown。它特別擅長處理復雜的公式和表格,簡直是PDF解析界的“糖果工匠”。不過,它的生成速度有點慢,像牛軋糖一樣,嚼起來需要點耐心。
- Nougat:PDF解析界的“牛軋糖”
-
- ** Pix2Struct:PDF解析界的“像素魔法師”**
Pix2Struct是個視覺語言理解的高手,它的任務是從屏蔽的網頁截圖中預測HTML解析。它不僅能處理PDF,還能搞定網頁截圖,簡直是多才多藝的“像素魔法師”。不過,它的訓練數據來自網頁,可能會帶來一些“有害內容”,使用時得小心點。
- ** Pix2Struct:PDF解析界的“像素魔法師”**
詳細介紹
Donut
如圖2所示,Donut是一個端到端模型,旨在全面理解文檔圖像。其架構簡單,由基于Transformer的視覺編碼器和文本解碼器模塊組成。
Donut不依賴任何與OCR相關的模塊,而是使用視覺編碼器從文檔圖像中提取特征,并直接使用文本解碼器生成token序列。輸出序列可以轉換為JSON等結構化格式。
代碼如下:
class DonutModel(PreTrainedModel):r"""Donut: 一個端到端的OCR-Free文檔理解Transformer。編碼器將輸入的文檔圖像映射為一組嵌入,解碼器預測所需的token序列,可以將其轉換為結構化格式,給定提示和編碼器輸出的嵌入"""config_class = DonutConfigbase_model_prefix = "donut"def __init__(self, config: DonutConfig):super().__init__(config)self.config = configself.encoder = SwinEncoder(input_size=self.config.input_size,align_long_axis=self.config.align_long_axis,window_size=self.config.window_size,encoder_layer=self.config.encoder_layer,name_or_path=self.config.name_or_path,)self.decoder = BARTDecoder(max_position_embeddings=self.config.max_position_embeddings,decoder_layer=self.config.decoder_layer,name_or_path=self.config.name_or_path,)def forward(self, image_tensors: torch.Tensor, decoder_input_ids: torch.Tensor, decoder_labels: torch.Tensor):"""給定輸入圖像和所需的token序列計算損失,模型將以教師強制的方式進行訓練參數:image_tensors: (batch_size, num_channels, height, width)decoder_input_ids: (batch_size, sequence_length, embedding_dim)decode_labels: (batch_size, sequence_length)"""encoder_outputs = self.encoder(image_tensors)decoder_outputs = self.decoder(input_ids=decoder_input_ids,encoder_hidden_states=encoder_outputs,labels=decoder_labels,)return decoder_outputs......
編碼器
Donut使用Swin-Transformer作為圖像編碼器,因為它在初步的文檔解析研究中表現出色。該圖像編碼器將輸入的文檔圖像轉換為一組高維嵌入。這些嵌入將作為文本解碼器的輸入。
對應代碼如下:
class SwinEncoder(nn.Module):r"""基于SwinTransformer的Donut編碼器使用預訓練的SwinTransformer設置初始權重和配置,然后修改詳細配置作為Donut編碼器參數:input_size: 輸入圖像大小(寬度,高度)align_long_axis: 如果高度大于寬度,是否旋轉圖像window_size: SwinTransformer的窗口大小(=patch大小)encoder_layer: SwinTransformer編碼器的層數name_or_path: 預訓練模型名稱,要么在huggingface.co.注冊,要么保存在本地。否則,將設置為`swin_base_patch4_window12_384`(使用`timm`)。"""def __init__(self,input_size: List[int],align_long_axis: bool,window_size: int,encoder_layer: List[int],name_or_path: Union[str, bytes, os.PathLike] = None,):super().__init__()self.input_size = input_sizeself.align_long_axis = align_long_axisself.window_size = window_sizeself.encoder_layer = encoder_layerself.to_tensor = transforms.Compose([transforms.ToTensor(),transforms.Normalize(IMAGENET_DEFAULT_MEAN, IMAGENET_DEFAULT_STD),])self.model = SwinTransformer(img_size=self.input_size,depths=self.encoder_layer,window_size=self.window_size,patch_size=4,embed_dim=128,num_heads=[4, 8, 16, 32],num_classes=0,)self.model.norm = None# 使用swin初始化權重if not name_or_path:swin_state_dict = timm.create_model("swin_base_patch4_window12_384", pretrained=True).state_dict()new_swin_state_dict = self.model.state_dict()for x in new_swin_state_dict:if x.endswith("relative_position_index") or x.endswith