
简介《我看见的世界》是李飞飞Fei-Fei Li亲笔撰写的AI主题著作这份PDF电子书面向人工智能爱好者、科技从业者及关注技术社会影响的读者以作者个人视角串联起机器学习、深度学习等核心概念。资源包共1个PDF文件大小约4.39MB含完整目录、版权页及正文适合在电脑或移动设备上直接阅读。已有8662人学习下载。书中以2018年李飞飞赴美国国会作证的真实经历开篇展现科学家面对AI伦理议题的紧张与担当并由此延伸出AI定义、发展历史、伦理政策、斯坦福教育实践及医疗交通等应用案例。读者既能从中感受一位移民科学家从求学到引领AI研究的成长历程也能获得关于AI现状、社会影响与未来趋势的深度认知是一份兼具专业洞察与人文温度的阅读资源。1. 从一份编号 PDF 读起我看见的世界首先是一道数据题资料目录里出现【R213】The Worlds I See 我看见的世界.pdf 这样的文件时大多数人习惯双击打开读两页我习惯先问一句它能不能被程序稳定解析。这是一位华人计算机视觉学者写的个人叙事书名中英成对文件里还夹着归档编号但 PDF 内部是什么状态——有没有文本层、字体嵌没嵌、元数据和文件名一不一致——不打开看永远不知道。把一份 PDF 从“能打开”推进到“能检索、能抽取、能归档”是整个知识库建设里最磨人也最容易被低估的一步。下面把这类编号书单 PDF 的检查、抽文、归档和排错流程完整拆开。2. 为什么 PDF 是“伪结构化”档案先搞懂它到底存了什么PDF 是文档领域里最特殊的常见格式。你以为是“一页书”程序看到的是一串对象和流你以为有“标题、段落、列表” PDF 里多数时候只有坐标、字体和绘制指令。它不是一份可编辑文档而是一份把排版结果定格下来的快照。处理《我看见的世界》这类中英双语的电子书 PDF 时尤其明显英文原版和中文译本可能是同一套排版字体也可能中文部分直接用了未嵌入的 CID 字体所有解析问题都从这层“伪结构化”开始。2.1 PDF 的三层物理结构对象、内容流、字符映射从物理层面看一份标准 PDF 至少由三层组成。最底层是语法层文件被切成一个个 object有数字编号有类型内容存放在 stream 里中间层是页面内容流里面是BT/ET、Tj/TJ、Tm这类绘图操作符它们负责描述“在哪个坐标、用哪个字体、画什么字”最上层才是语义层由字体对象、字符编码表和 ToUnicode CMap 组成负责把内容流里的字符编码映射成真实 Unicode 文字。我拿到文件会先把这三层分开看。文本提取看起来是读取文字实际是顺着“页面 → 内容流 → 字体 → CMap → Unicode”这条链路逐层打通。很多 PDF 打开时显示正常却抽不出文本就是因为显示只依赖前两层而提取依赖第三层。中英混排的书更容易踩中这个问题中文部分如果用了Type0字体却没有完整的 ToUnicode 表阅读器靠字形轮廓照样渲染抽取程序却拿不到任何有效字符。这不是工具不行是文件本身缺了语义层。2.2 五条命令给 PDF 做“体检”先看再抽别一上来就解析在写任何提取脚本前我习惯先用一组工具把文件底细摸清楚。五条命令就能覆盖大多数判断。# 1) 基本信息页数、PDF 版本、页面尺寸、是否带标签 pdfinfo R213_The_Worlds_I_See.pdf # 2) 字体清单看中文字体是否嵌入、是否有 ToUnicode 映射 pdffonts R213_The_Worlds_I_See.pdf # 3) 结构健康度检查交叉引用表和对象流是否损坏 qpdf --check R213_The_Worlds_I_See.pdf # 4) 图像资源统计扫描图数量判断是否混入图片层 pdfimages -list R213_The_Worlds_I_See.pdf # 5) 元数据看 Title、Author 字段与文件名是否一致 exiftool R213_The_Worlds_I_See.pdf这五条的输出各看一个侧面。pdfinfo最该看的是页数和页面尺寸如果一本书英文版和中文版页数差异很大很可能中文版是重新排版的pdffonts看的是字体列里有没有(emb)标记没有emb就说明字体未嵌入换一台机器显示和提取都可能变样qpdf --check最直接输出里有CHECK OK就说明结构健康pdfimages -list用来分辨它是不是扫描版统计出的图像数量如果接近页数说明这本书大概率没有文本层后面得走 OCRexiftool看元数据但要注意文件名的作者字段和内部Author字段经常不是一回事。我用表把这五条命令对应到一个快速判断逻辑上实际处理时不用每条都跑按怀疑方向挑着看就行。命令核心输出判断结论pdfinfoPages、Page size页数异常则可能中英版本混排pdffontsemb 标记、CMap 信息字体未嵌入则提取部署环境会翻车qpdf --check对象结构错误列表有错误先修复再抽文本pdfimages -list图像数量、尺寸图片数约等于页数则该走 OCRexiftoolTitle、Author、Producer元数据与文件名不一致则要建映射2.3 归档编号【R213】应该放在哪一层而不是文件名里我注意到这类文件命名喜欢把编号、中英文名、作者字段全拼进文件名例如【R213】The Worlds I See 我看见的世界.pdf。这种命名有个隐患文件名是给人看的不是给程序看的。方括号在 shell 里有特殊含义作者字段会随文件重命名丢失编号和内容没有建立稳定关联。常见做法是把【R213】作为外部索引主键放进一个独立的元数据表里而不是依赖文件名本身。我更推荐在文档库目录下维护一份R213_meta.csv字段至少包括doc_id、file_name、title_en、title_zh、author、pages、source_path。这样即使文件在团队协同时被重命名、移动、压缩编号和内容的关系仍然可追踪。至于 PDF 内部的元数据能不动就不动盲目用工具改写 Title 字段可能破坏数字签名这是一条容易被忽略的边界。3. 从双语 PDF 里稳定抽正文像拆电路一样做逐块提取体检通过之后才进入提取阶段。对【R213】这种书目标不是把整本书倒成一大段文字而是稳定、可回溯地抽到每一页的有效内容。PDF 文本提取有两条主流路线选哪条取决于你要的是“快”还是“可控”。3.1 两条提取路线poppler 与 PyMuPDF 的取舍poppler 是命令行工具集pdftotext一条命令就能出全文参数简单适合批量预览PyMuPDF 是 Python 库能拿到页面块级结构、坐标、字体信息适合需要精细控制的场景。我自己的习惯是先用pdftotext -layout跑一遍看清这本书的文本层质量再决定要不要写 PyMuPDF 脚本。两者的核心差异在排序和粒度。维度pdftotextPyMuPDF上手成本一条命令约 20 行脚本输出粒度按页输出纯文本按 block / line / word 输出页码坐标不保留每个块都带 bbox 坐标双栏排版依赖-layout参数可由sortTrue控制适合场景快速探查文本层结构化归档、切章、溯源3.2 最小提取脚本保留页码锚点的按块抽取我用 PyMuPDF 跑过很多次这类双语电子书下面这个脚本是我会保留在工具库里的最小版本。它的核心不是“把文字拿出来”而是“把文字按块拿准”。import fitz # PyMuPDF doc fitz.open(R213_The_Worlds_I_See.pdf) out_lines [] for page_no in range(len(doc)): page doc[page_no] # sortTrue 让块按自上而下、自左而右排序双栏书也能基本还原阅读顺序 blocks page.get_text(blocks, sortTrue) for block in blocks: x0, y0, x1, y1, text, block_no, block_type block if block_type ! 0: # 0 文本块1 图片块2 其他 continue if y0 60 or y1 page.rect.height - 60: continue # 过滤页眉页脚 text .join(text.split()) # 压缩块内多余换行 out_lines.append(f[p{page_no 1}] {text}) with open(extracted_text.txt, w, encodingutf-8) as fp: fp.write(\n.join(out_lines))这里有两个参数值得说清楚。sortTrue控制块的排序顺序PDF 页面里的文字块本来没有天然顺序这个参数会按坐标做一次排序对双语双栏的书效果最明显坐标过滤条件y0 60和y1 page.rect.height - 60是经验值专门对付页眉页码。如果这本书的页眉文字位置偏中间不要死守 60可以先跑一遍看坐标分布再调。3.3 双语混排的三个处理细节顺序、空白、断词中英混排的书在提取时比单一语言麻烦在三个点。第一是顺序中文段落和英文段落可能左右分栏也可能上下分栏sortTrue只能处理相对规整的排版遇到嵌套混排还是要按坐标手动分桶第二是空白英文单词被 PDF 内容流拆成字符后简单压缩空格会把The Worlds I See拼成TheWorldsISee所以 .join(text.split())里的空格处理其实是分语言做的中文行可以紧凑拼接英文行必须保留单词间隔第三是断词中文标点全角、英文标点半角在行尾换行时容易出现半截标点后续做章节切分时要注意按语言分别清洗。4. 把三百页的书拆成可检索文档库目录树、章节化 Markdown 与索引表文本抽出来只是第一步真正有落地价值的是把这份抽文结果组织成一个团队可复用的文档库。对【R213】来说最终产物不是一份extracted_text.txt而是一个目录结构清晰、每章可独立检索、每段能回溯页码的资料库。4.1 目录树先按编号建不要按书名建我在资料库里习惯用编号做第一层目录。book_library/R213/下面放五个东西raw/存原始 PDFtext/存全量提取文本chapters/存章节化后的 Markdownindex/存索引表meta/存元数据信息。为什么不用书名因为同一本书可能有英文原版、中译本、影印版按书名建目录会分裂成好几处按编号建目录则天然聚合。book_library/ └── R213/ ├── raw/R213_The_Worlds_I_See.pdf ├── text/extracted_text.txt ├── chapters/R213_ch01_扉页.md ├── chapters/R213_ch02_序言.md ├── chapters/R213_ch03_正文前段.md ├── index/R213_chapter_index.csv └── meta/R213_meta.csv这套结构的优点是任何人接手时只看目录就知道哪个文件是原始素材、哪个是加工产物。正文里写得再花哨的 PDF落到这个结构里都只剩一个编号加一堆可追溯的文本文件。4.2 按 PDF 自带目录切章节落成带页码锚点的 Markdown很多电子书 PDF 内部本来就带书签目录PyMuPDF 可以直接读取。下面的脚本用get_toc()拿到章节层级再按章节切分文本块输出带页码锚点的 Markdown 文件。import fitz from pathlib import Path PDF_PATH Path(book_library/R213/raw/R213_The_Worlds_I_See.pdf) OUT_DIR Path(book_library/R213/chapters) OUT_DIR.mkdir(parentsTrue, exist_okTrue) doc fitz.open(PDF_PATH) toc doc.get_toc() # 条目结构(层级, 标题, 页码) # 把“页码”转成“页面索引”PDF 书签页码从 1 开始 items [(level, title, page - 1) for level, title, page in toc] for i, (level, title, start_page) in enumerate(items): end_page items[i 1][2] if i 1 len(items) else len(doc) lines [f# {title}, f 来源页p{start_page 1} - p{end_page}, ] for pno in range(start_page, end_page): page_text doc[pno].get_text(text) lines.append(page_text) fname fR213_ch{i 1:02d}_{title[:12]}.md (OUT_DIR / fname).write_text(\n.join(lines), encodingutf-8)这段脚本的关键在get_toc()拿到的页码是 PDF 内部定义的书签页码不是文件物理页码两者常有偏移一定要先做page - 1的转换。切章边界来自目录如果 PDF 本身没有书签就需要退回到按页文本密度来估计章节位置代码逻辑就不一样了。4.3 索引表兜住所有“对不上”的情况切出来的章节一定会和原书目录有偏差这不奇怪很多电子书的书签页码是错的。我通常在生成章节的同时落一张R213_chapter_index.csv把每个章节文件的文件名、起始页码、结束页码、章节标题记录下来。这张表以后有两个用处一是快速回答“这句话在第几页”二是排查书签偏移时不用重新跑全量提取。doc_id,chapter_file,title,start_page,end_page R213,R213_ch01_扉页.md,扉页,1,3 R213,R213_ch02_序言.md,序言,4,9 R213,R213_ch03_正文前段.md,正文前段,10,86索引表一旦建立后续所有检索、引用、溯源都以它为准。别再相信自己眼睛看到的页码要以脚本打印出来的物理页为准这是避坑的基础。5. 四个高频翻车点PDF 解析的踩坑现场与对策处理【R213】这类双语 PDF 的过程里我踩过不少坑也帮人排过不少错现象各异根因大多集中在四类。5.1 扫描版 PDF 直接抽文本抽出来全是空白现象pdftotext输出文件只有零星几个字甚至完全空白但 PDF 用阅读器打开展示正常。原因这份 PDF 本身就是整页扫描图文字全在图像里没有文本层。判定方法是用前面的pdfimages -list如果图片数量接近页数几乎可以确认是扫描版。解决先转图像再 OCR我一般用 Tesseract 加中文语言包命令大致是tesseract page_001.png out -l chi_simeng。注意识别结果还是纯文本没有坐标信息所以 OCR 版的归档无法像原生文本版那样精确按块切章。5.2 中文字体未嵌入抽取出的汉字变成方框或乱码现象pdffonts里中文相关字体没有(emb)标记抽取出来的内容不是“看见”而是????或方框。原因PDF 字体未嵌入文本提取依赖字体内部的编码映射映射缺失时程序只能拿到空码位。解决先回到pdffonts看字体名如果字体本身能识别可以尝试qpdf --decrypt解开权限限制后再提取如果内容流里的字符映射本身就是空的那只能靠 OCR 兜底这条没有后悔药。5.3 页眉页脚混进正文切章锚点整体错位现象切出来的章节 Markdown 顶部总是混着书名、页码甚至把章标题都冲掉。原因我在 3.2 脚本里用坐标过滤了页眉页脚但不同书页页眉位置不同一旦某页页眉正好压在正文块中间过滤条件就失效了。解决不要只靠单一y坐标阈值先对整本书跑一遍块坐标分布统计页眉页脚的 y 值范围再按每一节的模式过滤。判断标准很简单页眉在全书几百页里通常是同一位置找出它们共同的 y 区间即可。5.4 文件名里的【R213】在批处理脚本里惹出怪问题现象一段遍历*.pdf的 shell 脚本跑到这本带方括号的 PDF 时要么找不到文件要么传参被拆断。原因方括号在 shell glob 里有字符类的含义文件名里的[会让部分脚本把它当成模式而不是普通字符。解决给文件路径加引号只是第一步还得用find ... -print0配合while IFS read -r -d 处理文件名。我自己在这种场景下会把索引文件单独命名成R213_The_Worlds_I_See.pdf去掉方括号同时在元数据表里保留原始文件名两边互不干扰。6. 让这份 PDF 真正进入团队知识库切块、溯源与最后一公里文本抽完、章节切完资料库已经可以用了。但如果要给检索或问答系统用还差最后一步把章节文本切成语义完整的块并给每个块打上来源标记。6.1 按章节边界切块而不是按固定 token 数切很多检索系统默认按定长切块比如每 500 字一刀。这对技术手册勉强可用但对《我看见的世界》这类带有叙事性的书是场灾难——一个完整的故事被硬生生切断检索时前后文拼不起来。我习惯按章节边界切再用段落做二次分块。from pathlib import Path chapter_dir Path(book_library/R213/chapters) for md_file in sorted(chapter_dir.glob(R213_ch*.md)): text md_file.read_text(encodingutf-8) blocks text.split(\n\n) # 按空行切段 chunk, chunk_size [], 0 for block in blocks: block_len len(block.strip()) if chunk_size block_len 800 and chunk: # 一段完整语料落袋 print(f{md_file.name}: {.join(chunk)[:30]}...) chunk, chunk_size [], 0 chunk.append(block) chunk_size block_len这里的 800 只是一个建议值叙事类文本我一般控制在 600 到 1000 字之间太长检索不精准太短上下文不全。关键是切块逻辑必须尊重原文的空行标记空行往往是段落边界也是语义边界。6.2 每个块都带上一张“来源身份证”切出来的块如果只有正文检索到了也不知道出处。我的做法是在每块开头加三行元数据章节文件名、页码区间、原书标题。这样下游无论接数据库还是接大模型检索回答问题时都能溯源到具体位置。习惯上我更愿意在归档阶段多写几行字段而不是等翻车后再回查。以前我图省事做过一次无标记切块结果一条错误引用追着改了三轮从那以后所有块都带来源标记就成了固定动作。一位熟悉 PDF 处理的朋友说过一句话PDF 解析不是技术题是耐心题。大多数问题都能靠“先体检、再提取、后归档”的流程挡在门外。这套流程跑顺之后收益是长期的。希望帮到你。本文还有配套的精品资源点击获取