再看大模型RAG问答中的文本解析组件:Markdown格式转换工具Marker的实现流程及评估方式

1,383次阅读
没有评论

文本表示在RAG流程中扮演着十分重要的角色。

我们在《大模型落地的一些前沿观点:兼看知识图谱增强大模型问答的几个方案及CEVAL榜单评测启发》(地址:https://mp.weixin.qq.com/s/bgR8cjeACLN0TCLjRN8jNQ)中有讲过在文档解析缓解,有比如专门可以用来识别数学公式的开源项目:Nougat: https://facebookresearch.github.io/nougat/ 

因为数学公式和表格在 markdown 里都可以用纯文本表示,其输入是单页 pdf 转成的图片,输出是这页pdf对应的 markdown(MMD,Mathpix MD)格式的纯文本序列。

其在训练数据收集阶段,根据PDF文件中的分页符拆分Markdown格式,收集来自arxiv、PubMed Central等平台的科学论文PDF数据集,以及LaTeX源代码,共超过800万页,具体来说,研究人员页面栅格化为图像以创建最终的配对数据集。

再看大模型RAG问答中的文本解析组件:Markdown格式转换工具Marker的实现流程及评估方式

今天,我们再来看看一个pdf转markdown的项目(先说结论,不支持中文),对其基本实现流程(其中的几个模块和参考项目)以及如何进行评估进行介绍,供大家一起参考。

709篇原创内容

公众号

一 、Markdown格式转换工具Marker实现流程

Marker:将PDF、EPUB和MOBI文档转换成Markdown格式的工具。地址:github.com/VikParuchuri/marker,

其特性在于:针对书籍和科学论文等多种PDF文档进行优化支持,移除页眉、页脚和其他冗余元素。转换大多数公式为Latex格式,格式化代码块和表格。

其基本实现流程如下:

首先,提取文本,必要时进行OCR(启发式、Tesseract)

其次,检测页面布局(布局分割器、列检测器)

相关的工具有:https://huggingface.co/vikp/layout_segmenter、https://huggingface.co/vikp/column_detector

例如:

https://github.com/VikParuchuri/marker/blob/master/marker/ocr/page.py

再看大模型RAG问答中的文本解析组件:Markdown格式转换工具Marker的实现流程及评估方式

然后,清理并格式化每个区块(启发式方法、nougat)

相关的工具有:https://huggingface.co/facebook/nougat-base

例如:

https://github.com/VikParuchuri/marker/blob/master/marker/cleaners

再看大模型RAG问答中的文本解析组件:Markdown格式转换工具Marker的实现流程及评估方式

最后合并区块并对完整文本进行后处理(启发式方法、pdf_postprocessor)

相关的工具有:https://huggingface.co/vikp/pdf_postprocessor_t5

例如:

https://github.com/VikParuchuri/marker/blob/master/marker/postprocessors/editor.py

再看大模型RAG问答中的文本解析组件:Markdown格式转换工具Marker的实现流程及评估方式

具体转换流程代码:

def convert_single_pdf(
        fname: str,
        model_lst: List,
        max_pages=None,
        metadata: Optional[Dict]=None,
        parallel_factor: int = 1
) -> Tuple[str, Dict]:
    lang = settings.DEFAULT_LANG
    if metadata:
        lang = metadata.get("language", settings.DEFAULT_LANG)

    # Use tesseract language if available,使用tesseract进行ocr识别
    tess_lang = settings.TESSERACT_LANGUAGES.get(lang, "eng")
    spell_lang = settings.SPELLCHECK_LANGUAGES.get(lang, None)
    if "eng" not in tess_lang:
        tess_lang = f"eng+{tess_lang}"

    # Output metadata,使用pymupdf进行pdf内容读取
    out_meta = {"language": lang}

    filetype = find_filetype(fname)
    if filetype == "other":
        return "", out_meta

    out_meta["filetype"] = filetype

    doc = pymupdf.open(fname, filetype=filetype)
    if filetype != "pdf":
        conv = doc.convert_to_pdf()
        doc = pymupdf.open("pdf", conv)

    blocks, toc, ocr_stats = get_text_blocks(
        doc,
        tess_lang,
        spell_lang,
        max_pages=max_pages,
        parallel=parallel_factor * settings.OCR_PARALLEL_WORKERS
    )

    out_meta["toc"] = toc
    out_meta["pages"] = len(blocks)
    out_meta["ocr_stats"] = ocr_stats
    if len([b for p in blocks for b in p.blocks]) == 0:
        print(f"Could not extract any text blocks for {fname}")
        return "", out_meta

    # Unpack models from list,对文本块进行识别
    nougat_model, layoutlm_model, order_model, edit_model = model_lst

    block_types = detect_document_block_types(
        doc,
        blocks,
        layoutlm_model,
        batch_size=settings.LAYOUT_BATCH_SIZE * parallel_factor
    )

    # Find headers and footers,找到页眉页脚
    bad_span_ids = filter_header_footer(blocks)
    out_meta["block_stats"] = {"header_footer": len(bad_span_ids)}

    annotate_spans(blocks, block_types)

    # Dump debug data if flags are set
    dump_bbox_debug_data(doc, blocks)

    blocks = order_blocks(
        doc,
        blocks,
        order_model,
        batch_size=settings.ORDERER_BATCH_SIZE * parallel_factor
    )

    # Fix code blocks,处理code模块
    code_block_count = identify_code_blocks(blocks)
    out_meta["block_stats"]["code"] = code_block_count
    indent_blocks(blocks)

    # Fix table blocks,处理表格模块
    merge_table_blocks(blocks)
    table_count = create_new_tables(blocks)
    out_meta["block_stats"]["table"] = table_count

    for page in blocks:
        for block in page.blocks:
            block.filter_spans(bad_span_ids)
            block.filter_bad_span_types()

    filtered, eq_stats = replace_equations(
        doc,
        blocks,
        block_types,
        nougat_model,
        batch_size=settings.NOUGAT_BATCH_SIZE * parallel_factor
    )
    out_meta["block_stats"]["equations"] = eq_stats

    # Copy to avoid changing original data
    merged_lines = merge_spans(filtered)
    text_blocks = merge_lines(merged_lines, filtered)
    text_blocks = filter_common_titles(text_blocks)
    full_text = get_full_text(text_blocks)

    # Handle empty blocks being joined
    full_text = re.sub(r'\n{3,}', '\n\n', full_text)
    full_text = re.sub(r'(\n\s){3,}', '\n\n', full_text)

    # Replace bullet characters with a -
    full_text = replace_bullets(full_text)

    # Postprocess text with editor model,对文本进行编辑优化
    full_text, edit_stats = edit_full_text(
        full_text,
        edit_model,
        batch_size=settings.EDITOR_BATCH_SIZE * parallel_factor
    )
    out_meta["postprocess_stats"] = {"edit": edit_stats}

    return full_text, out_meta

二、Markdown格式转换工具Marker如何进行评估

同样的,我们来看看,如何对其进行评估,地址https://github.com/VikParuchuri/marker/blob/master/marker/benchmark/scoring.py中对该过程进行了描述:

import math
from rapidfuzz import fuzz, distance
import re
CHUNK_MIN_CHARS = 25

"""先对文本进行tokenizer"""
def tokenize(text):
    # Combined pattern
    pattern = r'([^\w\s\d\'])|([\w\']+)|(\d+)|(\n+)|( +)'
    result = re.findall(pattern, text)
    # Flatten the result and filter out empty strings
    flattened_result = [item for sublist in result for item in sublist if item]
    return flattened_result
    
""对文本进行切片"""
def chunk_text(text):
    chunks = text.split("\n")
    chunks = [c for c in chunks if c.strip() and len(c) > CHUNK_MIN_CHARS]
    return chunks

"""计算chunk之间的重合度"""
def overlap_score(hypothesis_chunks, reference_chunks):
    length_modifier = len(hypothesis_chunks) / len(reference_chunks)
    search_distance = max(len(reference_chunks) // 5, 10)
    chunk_scores = []
    chunk_weights = []
    for i, hyp_chunk in enumerate(hypothesis_chunks):
        max_score = 0
        chunk_weight = 1
        i_offset = int(i * length_modifier)
        chunk_range = range(max(0, i_offset-search_distance), min(len(reference_chunks), i_offset+search_distance))
        for j in chunk_range:
            ref_chunk = reference_chunks[j]
            score = fuzz.ratio(hyp_chunk, ref_chunk, score_cutoff=30) / 100
            if score > max_score:
                max_score = score
                chunk_weight = math.sqrt(len(ref_chunk))
        chunk_scores.append(max_score)
        chunk_weights.append(chunk_weight)
    chunk_scores = [chunk_scores[i] * chunk_weights[i] for i in range(len(chunk_scores))]
    return chunk_scores, chunk_weights

"""对得分进行归一化"""
def score_text(hypothesis, reference):
    # Returns a 0-1 alignment score
    hypothesis_chunks = chunk_text(hypothesis)
    reference_chunks = chunk_text(reference)
    chunk_scores, chunk_weights = overlap_score(hypothesis_chunks, reference_chunks)
    return sum(chunk_scores) / sum(chunk_weights)

总结

不过,该工具也存在的问题,只支持与英语类似的语言(西班牙语、法语、德语、俄语等)。不支持中文、日文、韩文等。这块需要自行进行针对性的中文修改。

参考文献

1、github.com/VikParuchuri/marker

正文完
可以使用微信扫码关注公众号(ID:xzluomor)
post-qrcode
 0
评论(没有评论)

文心AIGC

2024 年 12 月
 1
2345678
9101112131415
16171819202122
23242526272829
3031  
文心AIGC
文心AIGC
人工智能ChatGPT,AIGC指利用人工智能技术来生成内容,其中包括文字、语音、代码、图像、视频、机器人动作等等。被认为是继PGC、UGC之后的新型内容创作方式。AIGC作为元宇宙的新方向,近几年迭代速度呈现指数级爆发,谷歌、Meta、百度等平台型巨头持续布局
文章搜索
热门文章
清库存!DeepSeek突然补全R1技术报告,训练路径首次详细公开

清库存!DeepSeek突然补全R1技术报告,训练路径首次详细公开

清库存!DeepSeek突然补全R1技术报告,训练路径首次详细公开 Jay 2026-01-08 20:18:...
训具身模型遇到的很多问题,在数据采集时就已经注定了丨鹿明联席CTO丁琰分享

训具身模型遇到的很多问题,在数据采集时就已经注定了丨鹿明联席CTO丁琰分享

训具身模型遇到的很多问题,在数据采集时就已经注定了丨鹿明联席CTO丁琰分享 衡宇 2026-01-08 20:...
「北京版幻方」冷不丁开源SOTA代码大模型!一张3090就能跑,40B参数掀翻Opus-4.5和GPT-5.2

「北京版幻方」冷不丁开源SOTA代码大模型!一张3090就能跑,40B参数掀翻Opus-4.5和GPT-5.2

「北京版幻方」冷不丁开源SOTA代码大模型!一张3090就能跑,40B参数掀翻Opus-4.5和GPT-5.2...
AI金矿上打盹的小红书,刚刚醒了一「点点」

AI金矿上打盹的小红书,刚刚醒了一「点点」

AI金矿上打盹的小红书,刚刚醒了一「点点」 鱼羊 2025-12-26 17:04:08 来源:量子位 一个积...
最新评论
ufabet ufabet มีเกมให้เลือกเล่นมากมาย: เกมเดิมพันหลากหลาย ครบทุกค่ายดัง
tornado crypto mixer tornado crypto mixer Discover the power of privacy with TornadoCash! Learn how this decentralized mixer ensures your transactions remain confidential.
ดูบอลสด ดูบอลสด Very well presented. Every quote was awesome and thanks for sharing the content. Keep sharing and keep motivating others.
ดูบอลสด ดูบอลสด Pretty! This has been a really wonderful post. Many thanks for providing these details.
ดูบอลสด ดูบอลสด Pretty! This has been a really wonderful post. Many thanks for providing these details.
ดูบอลสด ดูบอลสด Hi there to all, for the reason that I am genuinely keen of reading this website’s post to be updated on a regular basis. It carries pleasant stuff.
Obrazy Sztuka Nowoczesna Obrazy Sztuka Nowoczesna Thank you for this wonderful contribution to the topic. Your ability to explain complex ideas simply is admirable.
ufabet ufabet Hi there to all, for the reason that I am genuinely keen of reading this website’s post to be updated on a regular basis. It carries pleasant stuff.
ufabet ufabet You’re so awesome! I don’t believe I have read a single thing like that before. So great to find someone with some original thoughts on this topic. Really.. thank you for starting this up. This website is something that is needed on the internet, someone with a little originality!
ufabet ufabet Very well presented. Every quote was awesome and thanks for sharing the content. Keep sharing and keep motivating others.
热评文章
陈天桥代季峰打响2026大模型第一枪:30B参数跑出1T性能

陈天桥代季峰打响2026大模型第一枪:30B参数跑出1T性能

陈天桥代季峰打响2026大模型第一枪:30B参数跑出1T性能 鹭羽 2026-01-06 14:28:58 来...
OpenAI推理第一人离职,7年打造了o3/o1/GPT-4/Codex

OpenAI推理第一人离职,7年打造了o3/o1/GPT-4/Codex

OpenAI推理第一人离职,7年打造了o3/o1/GPT-4/Codex 衡宇 2026-01-06 13:0...
OpenAI推理第一人离职,7年打造了o3/o1/GPT-4/Codex

OpenAI推理第一人离职,7年打造了o3/o1/GPT-4/Codex

OpenAI推理第一人离职,7年打造了o3/o1/GPT-4/Codex 衡宇 2026-01-06 13:0...
老黄All in物理AI!最新GPU性能5倍提升,还砸掉了智驾门槛

老黄All in物理AI!最新GPU性能5倍提升,还砸掉了智驾门槛

老黄All in物理AI!最新GPU性能5倍提升,还砸掉了智驾门槛 闻乐 2026-01-06 09:09:0...