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

1,332次阅读
没有评论

文本表示在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、百度等平台型巨头持续布局
文章搜索
热门文章
潞晨尤洋:日常办公没必要上私有模型,这三类企业才需要 | MEET2026

潞晨尤洋:日常办公没必要上私有模型,这三类企业才需要 | MEET2026

潞晨尤洋:日常办公没必要上私有模型,这三类企业才需要 | MEET2026 Jay 2025-12-22 09...
反超Nano Banana!OpenAI旗舰图像生成模型上线

反超Nano Banana!OpenAI旗舰图像生成模型上线

反超Nano Banana!OpenAI旗舰图像生成模型上线 Jay 2025-12-17 10:25:43 ...
“昆山杯”第二十七届清华大学创业大赛决赛举行

“昆山杯”第二十七届清华大学创业大赛决赛举行

“昆山杯”第二十七届清华大学创业大赛决赛举行 一水 2025-12-22 17:04:24 来源:量子位 本届...
企业级智能体落地,谁没踩这四种大坑?无问芯穹的系统性解法来了

企业级智能体落地,谁没踩这四种大坑?无问芯穹的系统性解法来了

企业级智能体落地,谁没踩这四种大坑?无问芯穹的系统性解法来了 衡宇 2025-12-16 20:10:53 来...
最新评论
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.
热评文章
小米语音首席科学家:AI发展的本质就像生物进化,不开源要慢1000倍 | MEET2026

小米语音首席科学家:AI发展的本质就像生物进化,不开源要慢1000倍 | MEET2026

小米语音首席科学家:AI发展的本质就像生物进化,不开源要慢1000倍 | MEET2026 克雷西 2025-...
顶尖技术+标准产品+创新模式+可靠服务,打造大模型商业落地中国范式

顶尖技术+标准产品+创新模式+可靠服务,打造大模型商业落地中国范式

顶尖技术+标准产品+创新模式+可靠服务,打造大模型商业落地中国范式 思邈 2025-12-16 10:24:0...
PPIO姚欣:AI正在进入自主行动与创造时代,智能体需要全新的操作系统|MEET2026

PPIO姚欣:AI正在进入自主行动与创造时代,智能体需要全新的操作系统|MEET2026

PPIO姚欣:AI正在进入自主行动与创造时代,智能体需要全新的操作系统|MEET2026 梦瑶 2025-12...
Dexmal原力灵机提出ManiAgent,用多智能体协作重构机器人操控

Dexmal原力灵机提出ManiAgent,用多智能体协作重构机器人操控

Dexmal原力灵机提出ManiAgent,用多智能体协作重构机器人操控 量子位的朋友们 2025-12-16...
推特吵架吵出篇论文!谢赛宁团队新作iREPA只要3行代码

推特吵架吵出篇论文!谢赛宁团队新作iREPA只要3行代码

推特吵架吵出篇论文!谢赛宁团队新作iREPA只要3行代码 henry 2025-12-16 15:03:31 ...