简介:《人工智能课后习题答案(清华大学出版社)》是一份面向高校人工智能课程学习者与研究人员的习题解析文档,聚焦教材各章节核心知识点,帮助读者通过例题演练掌握人工智能形式化方法。内容覆盖搜索算法、命题逻辑与谓词逻辑、知识表示、机器学习基础、描述逻辑与框架、贝叶斯网络及概率推理等模块,既有基础概念梳理,也有典型题目推导。文件包为单个PDF文档,大小仅212KB,便于下载和离线查看;答案中包含了八数码问题的启发函数设计、子句集归结证明、最一般合一求解、语义网络与框架表示以及贝叶斯条件概率计算等完整解题步骤,适合在复习或作业遇到困难时对照查阅。已有1924人学习下载,对于希望系统巩固人工智能理论、提升逻辑推理与建模能力的读者来说,是一份实用且轻量的配套学习资料。
1. 人工智能课后习题答案这份 PDF,值得你花两小时处理而不是五分钟略过
很多人的电脑里都躺着一份《人工智能》教材配套的课后习题答案PDF,下载后最多翻一翻,然后继续去刷视频。我却习惯把每一份这种答案PDF当成一个待解析的结构化数据集来处理。原因很简单:人工智能课程里大量概念——搜索、逻辑推理、机器学习、神经网络——课后题答案往往就是一个可验证的结论,抽出来配上题目,就是一套高质量自测题。这篇笔记我会用一个最常见的文件名“人工智能课后习题答案(某大学出版社).pdf”作为例子,讲清楚怎么把一份被动浏览的答案文档,变成能检索、能复核、能安排复习节奏的资料库。适合期末备考、自学入门,以及想验证自己代码实现是否正确的读者。
2. 先把 PDF 拆出结构:查页码、找章节、定排版规则
拿到一份答案PDF,第一反应往往是掏出阅读器开始翻,或者直接右键复制文字。这是效率最低的路线。答案类PDF通常混合了多种来源:同一本书,可能前半部分是从电子版直接导出的,后半部分是从纸质版拍照扫描的;也可能章节顺序是乱的,一个书签都没有,只有连续页码。不先做结构扫描,后面提取文本时会反复翻车。
所以我拿到任何课后习题答案PDF,都会先把它当作数据源做三件事:确认页数和是否加密,定位章节标题,判断题目和答案是以什么粒度混排的。这三件事做完,后续提取方案才能定下来。
2.1 用 pdfinfo 和 pdftotext 快速看页数与章节标题
常见做法是装 poppler-utils,它提供 pdfinfo、pdftotext、pdftoppm 一组命令行工具。Linux 上一条命令装上,macOS 用 brew 装同样名字的包,Windows 也能找到编译好的二进制。我一般先用 pdfinfo 看元信息:
pdfinfo 人工智能课后习题答案.pdf输出里关注三类字段:
| 字段 | 含义 | 决定什么 |
|---|---|---|
| Pages | 总页数 | 与教材章节数量初步对应 |
| Encrypted | 是否加密 | 加密要先解除 |
| Page size | 页面尺寸 | 扫描版通常是不规则的 72 dpi 尺寸 |
如果 Encrypted 不是 no,先解密再处理。解密命令是:
qpdf --decrypt --password=xxx 人工智能课后习题答案.pdf cleared.pdf参数说明:--password 后面接打开密码,没有密码也要传空字符串;--decrypt 会生成一份去除权限限制的新文件。我见过不少课后答案PDF是禁止打印或禁止裁剪的权限加密,但文字层本身可以复制,如果不解除,后续重排或者重新加书签时很多工具会直接拒绝写文件。
拿到未加密文件后,用 pdftotext 转换一版纯文本,然后搜索章节标题:
pdftotext -layout cleared.pdf answers_full.txt grep -nE "第[0-9一二三]+章|Chapter [0-9]+|复习题|参考答案" answers_full.txt | head -n 80命令说明:-layout 参数让 pdftotext 按原始排版顺序输出文本,而不是简单的从左到右逐行读,这对双栏页面尤其重要。grep 的 -E 表示扩展正则,一次匹配中英文章节关键词;head -n 80 防止章节过多刷屏。输出的行号直接对应 answers_full.txt 的行号,比翻阅读器定位章节快得多。
这里有一个容易忽略的点:有些答案PDF根本没有“第X章”这样的标题,而是用“习题3”或“3 答案”代替。所以第一轮 grep 结果很少时不要急着下结论,用“答案”“习题”“思考题”次一级关键词再搜一轮。我一般会写一个循环把这些候选词全扫一遍,宁可多匹配也不要漏。章节定位的准确度直接影响后面答案台账的章节字段,错一个对应关系,统计结果就全歪了。
2.2 判断答案是随题附答案还是集中答案
这两类结构决定了提取时按什么粒度切分。
随题附答案的PDF,每一道题后面紧跟着“答:”“解:”,题目和答案互相交错,字数波动大。集中答案的PDF,前面几十页全是题目,后面几十页全是答案,两者可以完全分离。判断方法有两种:第一种是肉眼扫一页;第二种是统计文本行长度分布。集中答案的题目页通常一个问题占多行、空行多;答案页则短句多、行密度高。用 awk 就能粗算:
awk 'length($0)>0 {count++; sum+=length($0); if(length($0)<30) short++} END {printf "总行数:%d 平均长度:%.1f 短行占比:%.2f\n", count, sum/count, short/count}' answers_full.txt参数说明:length($0) 是当前行字符数,sum/count 是平均行长,short 记录长度小于 30 字符的行数。短行占比超过 0.6,基本可判断是以答案为主的页面。这个判断不精确,但能在五分钟内给出倾向。
如果是随题附答案,提取时要用“题目编号”作为切分点,比如“3-1”“习题3.2”这类前缀。如果是集中答案,直接按页码区间切,比如答案部分在第80页之后。后面章节会分别给出处理路径。还有一种常见陷阱:同一份PDF混排了两种结构,前几章随题附答案,后面章节集中答案,遇到这种情况只能半自动处理,不能写死规则。
2.3 用 pypdf 建立章节页码映射
没有书签的答案PDF,我会顺手生成一个“章节—页码”映射表,方便后面按章节抽取内容。pypdf 是零系统依赖的纯 Python 库,用来逐页扫描最顺手:
from pypdf import PdfReader reader = PdfReader("cleared.pdf") for page_no, page in enumerate(reader.pages, start=1): text = page.extract_text() or "" if "第3章" in text or "Chapter 3" in text: print(f"第3章起始页码: {page_no}")逻辑说明:extract_text 在部分扫描页上会返回 None,所以用 or "" 兜底;找到章节关键词就直接打印页码。这里并不修改 PDF,只是把“章节页码”记下来,后续按这个页码切分或用 pdftotext -f -l 精确抽取就都有了依据。pypdf 的纯文本提取速度比 pdftotext 慢不少,只适合章节量不大时用,文件超过两百页建议回到命令行方案。
3. 把习题答案从 PDF 里提取出来:pdfplumber 与 OCR 兜底
结构摸清之后,进入内容提取阶段。这一章给三条路线:文字版用 pdfplumber、扫描版用 Tesseract OCR、输出格式按用途选择。三条路线不是互斥的,一份答案PDF可能前几十页是文字版,后几十页是扫描版,需要组合使用。
3.1 用 pdfplumber 提取文字版答案的最小脚本
pdfplumber 是目前处理文字型PDF最稳的 Python 库之一,它把每一页解析成对象,可以按坐标切分。先安装:
pip install pdfplumber然后写一个提取全部文本并保留页码标记的脚本:
import pdfplumber source_pdf = "人工智能课后习题答案.pdf" output_txt = "answers_raw.txt" with pdfplumber.open(source_pdf) as pdf: with open(output_txt, "w", encoding="utf-8") as f: for i, page in enumerate(pdf.pages, start=1): text = page.extract_text() if text: f.write(f"\n===== PAGE {i} =====\n") f.write(text)参数说明:pdfplumber.open 打开PDF;enumerate 生成的 i 是页码;page.extract_text() 会按坐标拼出文本,如果返回 None 说明这页没有文字层,要转 OCR。分页标记“===== PAGE i ====="是给自己看的,后续按页切块或者定位错位题目都靠它。
实际使用中最大的坑是多栏排版会打乱阅读顺序。比如答案分两栏,extract_text 可能先把左栏整个读完再读右栏,这是对的;但有些双栏页面遇到表格穿插,顺序就乱了。遇到这种情况,改用 extract_words 拿单词坐标,再按坐标重新排序:
words = page.extract_words() left = [w for w in words if w["x0"] < page.width / 2] right = [w for w in words if w["x0"] >= page.width / 2] def sort_words(word_list): return " ".join(w["text"] for w in sorted(word_list, key=lambda w: (w["top"], w["x0"]))) line = sort_words(left) + "\n" + sort_words(right)参数说明:extract_words 返回每个单词的字典,x0 是左侧坐标,top 是纵向坐标,width 是页面宽度。判断 x0 与 width/2 的关系来分栏,再按 top 从上到下、x0 从左到右排序。这个方案能解决九成双栏问题,但遇到栏内小标题、公式、图片混排,还是会有个别行顺序不对劲,需要自己加白名单微调。
3.2 扫描版答案用 OCR 兜底:Tesseract 加中文语言包
如果 pdftotext 输出的文件是空的,说明没有文字层,只能用 OCR。Tesseract 是开源里最常用的,配合 chi_sim 语言包可以识别中文。先用 pdftoppm 把 PDF 转成图片,再逐张识别:
pdftoppm -png -r 300 人工智能课后习题答案.pdf page for img in page-*.png; do tesseract "$img" "${img%.png}" -l chi_sim --psm 6; done cat page-*.txt > answers_ocr.txt参数说明:pdftoppm 的 -r 300 是分辨率,300 DPI 是文字识别的最低保证,低于 200 会显著掉点;tesseract 的 -l chi_sim 指定中文简体,--psm 6 表示把整页当作一个统一文本块。如果页面有明显分栏,把 6 改成 4 会按列识别,速度慢但准确率高。
扫描版转出来的图片质量经常不理想,尤其老教材的影印答案。我一般会在识别前把图片做一次灰度加二值化处理,用 ImageMagick 一行搞定:
for img in page-*.png; do convert "$img" -colorspace gray -threshold 80% "prep_${img}"; done处理后再跑 tesseract。注意 threshold 80% 会丢弃浅灰文字,如果答案页背景发黄或者纸张有网格线,这会明显提升识别率;但反黑底白字时会全军覆没,需要先 negate 翻转。多页 OCR 结果合并时顺序取决于文件名,务必用 sort -V 排列再 cat,避免第10页跑到第2页前面。
数学公式是 OCR 的硬伤,识别出来的“x1”“x2”经常变成“x1”“x2”,上标下标全平了。所以扫描版答案中,公式部分不要指望自动提取,后面避坑章会讲怎么处理。
3.3 输出格式怎么选:Markdown、JSON 还是 CSV
提取出来的原始文本下一步按用途选格式。我一般用三种:
| 格式 | 适用场景 | 主要缺点 |
|---|---|---|
| Markdown | 自己复习、导入笔记软件 | 公式和代码块需要二次整理 |
| JSON | 后续写脚本按题号检索 | 键结构需要先定义 |
| CSV | 题目与答案一一对照、做错题本 | 字段内不能有逗号或换行 |
如果只做一件事,我会选 CSV。因为大多数习题答案可以抽象成“章节、题号、题目摘要、答案结论”四列,CSV 正交,能用 Excel 或 sqlite 处理。转换时注意编码:
python3 - <<'EOF' import csv with open("answers_structured.csv", "w", newline="", encoding="utf-8-sig") as f: writer = csv.writer(f) writer.writerow(["章节", "题号", "题目摘要", "答案"]) # 这里假设前面已经得到按页拆好的文本 writer.writerow(["ch3", "3-1", "简述A*算法的可采纳性", "见原文"]) EOF脚本说明:utf-8-sig 编码是为了让 Excel 打开 CSV 不出现中文乱码;实际落地时,题目摘要字段不能直接塞整段题面,而是截取前 60 字作为摘要,否则表格炸开。CSV 的字段里如果有换行,要用双引号包住,写入时 csv 库会自动处理,但如果答案文本是从 OCR 来的,里面可能带着 \r\n,需要提前 replace 成空格。
如果后续要写脚本做随机抽卡,JSON 比 CSV 顺手。把 CSV 转成 JSON 也很简单:读入行,按题号做 key。
4. 答案不一定正确:把它当程序输出一样核对
课后习题答案PDF本质是人工整理的产物,也会错,而且教材改版后答卷页码错位、印刷公式丢符号、答案数值抄错的情况屡见不鲜。所以处理答案最重要的一步不是提取,而是核对。这一章讲怎么用低成本方式验证。
4.1 建立答案台账:章节、题号、结论三列汇总
我不建议直接从头到尾读答案,而是先建一个答案台账,把每一道题的结论浓缩成一行,形成一张表:
import sqlite3 conn = sqlite3.connect("answer_ledger.db") conn.execute(""" CREATE TABLE IF NOT EXISTS answers ( chapter TEXT, problem_no TEXT, question_summary TEXT, answer_conclusion TEXT, status TEXT DEFAULT 'unverified' ) """) conn.commit() conn.close()逻辑说明:chapter 是章节名,problem_no 是题号,question_summary 是题目摘要,answer_conclusion 是答案里最核心的判断或数值,status 是验证状态,unverified 表示尚未核对,pass 表示已验证通过,failed 表示对不上。字段不存完整推导过程,只存结论。因为推导过程往往占用大量字符,而且复制到表格里会把行结构撑烂。
建表之后,从之前的 CSV 导入到 SQLite。pandas 的 read_csv 然后 to_sql 是最省事的路子:
import pandas as pd import sqlite3 df = pd.read_csv("answers_structured.csv", encoding="utf-8-sig") conn = sqlite3.connect("answer_ledger.db") df.to_sql("answers", conn, if_exists="replace", index=False) conn.close()参数说明:if_exists="replace" 表示每次重新生成表,避免重复导入造成脏数据。导入后可以用 SQL 查还没验证的题:
SELECT chapter, problem_no, answer_conclusion FROM answers WHERE status = 'unverified';这样每天复习前先把 unverified 的题处理掉,而不是闷头从头看。
4.2 用可执行代码验证答案里的算法结果
人工智能课后题里大量题目属于“算一算”类型,比如计算状态空间大小、验证某个搜索算法的扩展顺序、推导贝叶斯后验。这类题目答案是可以在脚本里重算的。以朴素贝叶斯分类器后验概率计算为例,答案会给一个具体数值,我们可以用 Python 重算一次:
def naive_bayes_positive(prior_pos, prior_neg, likelihood_pos, likelihood_neg): raw_pos = prior_pos * likelihood_pos raw_neg = prior_neg * likelihood_neg return raw_pos / (raw_pos + raw_neg) # 假设答案给出后验概率为 0.857 calculated = naive_bayes_positive(0.5, 0.5, 0.9, 0.15) print(f"{calculated:.3f}")参数说明:前两个参数是先验概率,后两个是条件似然;返回归一化后的后验。打印保留三位小数,用 abs 比较答案与计算结果:
answer = 0.857 if abs(calculated - answer) < 0.001: print("验证通过") else: print("验证失败,检查输入或答案")我遇到过最邪门的情况是答案写 0.875,重算结果是 0.857,初次以为是公式写错,反复检查后发现是印刷丢了一个数字。这类问题如果你只看答案,根本发现不了,只有写代码重算才能揪出来。别期待所有题都能脚本验证,论述题、画图题只能人工看,但只要三成题目能跑出数值,这份答案的可信度就能上升一大截。
关于论述题核对,我一般不打分,只做关键词提醒。把题目摘要和答案结论放在一起,用 grep 匹配是否有“不收敛”“NP完全”“复杂度上界”这类关键表达。虽然不能判断正误,但能快速识别答案是否答非所问。比如题目问“为什么 A* 算法是可采纳的”,答案结论里如果完全没有“启发函数”“低估”“代价”这些词,那基本可以断定这份答案不匹配题目。
5. 避坑指南:处理习题答案 PDF 时踩过的 4 个雷
这一章专门写排障,每一条都是处理答案PDF时真实发生的坑。
5.1 中文乱码:提取出来全是方块或问号
现象:pdfplumber 和 pdftotext 提取后,中文变成一堆 __ 或者 ?,英文和数字正常。
原因:PDF 内部使用了自定义字体编码或子集化字体,没有标准 Unicode 映射。文字在页面里是“画”出来的形状,复制时无法对应到正确字符。
解决:先用 pdffonts 检查字体列表:
pdffonts cleared.pdf输出会列出字体名、类型、编码方式。如果看到很多 Type 3 字体,或者字体名前缀像一串乱码,就不要在编码上死磕了,直接 OCR 反而更快。如果是标准字体但系统缺中文字体,装上对应语言包后重新 pdftotext 就能解决。建议先花五分钟跑 pdffonts,能省两小时调编码的时间。
5.2 公式变成错位符号:上下标混成一团
现象:提取后,数学公式变成一堆希腊字母堆在行尾,上标下标全平了,“x_i”变成“xi”,极限符号“lim”和取值的边界完全无法分辨。
原因:pdfplumber 按文本坐标输出,公式是自动排版对象,没有语义,上下标和主体符号被当作普通字符按位置拼出来。
解决:对公式部分不做文本提取,改用页面对应的图片区域。先定位公式坐标:
import pdfplumber with pdfplumber.open("人工智能课后习题答案.pdf") as pdf: page = pdf.pages[10] words = page.extract_words() for w in words: if "∫" in w["text"] or "Σ" in w["text"]: print(w["text"], w["top"], w["bottom"])参数说明:extract_words 返回每个词的坐标,top/bottom 是纵向位置,x0/x1 是横向位置。定位到公式词后,用 page.crop(bbox) 裁出公式区域保存为图片。我一般会裁得比识别到的坐标宽一些,上下各扩 5 个点,避免公式边框被切掉。这个方案保留公式原貌,代价是笔记里会混入图片块,无法搜索,但对复习来说完全够用。
5.3 答案与题目错位:页码对不上,白看一章
现象:第 3 章的答案实际上对应第 4 章的题,连着看几页才发现。
原因:教材改版后题目重排,但 PDF 答案沿用旧版目录;或者答案来自不同作者编写的习题选解,章节顺序不完全一致。
解决:不要轻信页码。拿一章内有明确数字的题目用脚本重算,比如后验概率、搜索步数、状态空间大小,算完与答案比对。账目对不上就直接在台账里标 unverified。前一章的 4.1 台账在这里派上用场,status 字段可以提醒自己先别细读。我还习惯在章节页码映射表里备注“疑似错位”,等整本验证完再回头梳理偏移量。很多情况下,错位是整体偏了一章,找到规律后批量修正页码关系就行。
5.4 OCR 后序号漂移:第 10 题被识别成第 1o 题
现象:tesseract 识别后,题目编号里的数字和字母混淆,比如“10”变成“1o”,“0”变成“O”,导致排序错乱。
原因:OCR 对数字和字母的区分受字体影响,小字号下 1 和 l、0 和 o 分不清。
解决:OCR 后用正则把“第[0-9OoIl]+题”统一成“第{n}题”,再做一次排序校验:
import re def normalize_problem_no(text): # 把字母 O/o 当作 0,字母 l 当作 1 fixed = text.replace("O", "0").replace("o", "0").replace("l", "1") return re.sub(r"第([0-9]+)题", lambda m: f"第{int(m.group(1))}题", fixed) print(normalize_problem_no("第1o题"))逻辑说明:replace 会把英文大写 O、小写 o 换成数字 0,把小写 l 换成 1。注意不能直接把大写 I 也替换成 1,因为题目里可能出现“第Ⅰ题”这种罗马数字,替换后反而出错。统一后再做一次排序,看题号序列是否连续:3-1、3-2、3-3……如果中间跳号或重号,十有八九就是 OCR 把序号认错了。这个坑在扫描版答案里几乎必现,尤其是中文题号配阿拉伯数字的排版。
6. 进阶玩法:把答案倒过来用,做成自测卡和错题本
前面几章做的所有工作,最终目的不是为了存一堆文件,而是让复习效率高一个量级。
6.1 把答案拆成题目卡和答案卡,用间隔重复刷题
常见做法是使用间隔重复抽卡工具,但我不想引入太重依赖。只需要把之前的 CSV 转换成两个 Markdown 文件:一个只含题目摘要,一个只含答案结论。复习时先看题目卡,自己讲一遍思路,再翻答案卡对照。这个“必要困难”比直接看答案 PDF 多一步,记忆效果差异非常大。
import csv with open("answers_structured.csv", encoding="utf-8-sig") as f: rows = list(csv.DictReader(f)) with open("question_cards.md", "w", encoding="utf-8") as fq: with open("answer_cards.md", "w", encoding="utf-8") as fa: for r in rows: fq.write(f"### {r['章节']} {r['题号']}\n\n{r['题目摘要']}\n\n") fa.write(f"### {r['章节']} {r['题号']}\n\n{r['答案']}\n\n")参数说明:DictReader 以 CSV 第一行作为字段名;写入时把题号和章节拼成标题,保证两个文件的标题完全一致,方便对照。如果题目摘要太长,可以截断成 60 字。随机抽卡时,先把题目卡文件的行顺序打乱,每天只刷 10 张,刷完当天不重复,隔两天再回来。我自己的经验是,这种复习方式比反复通读整本答案 PDF 有效的多,因为每次都要先回忆推导步骤,而不是看着答案被动点头。
6.2 答案倒推考点:把解题步骤换成考点标签
答案不仅给结论,还给出推导过程。我们可以从推导反推考点,给每道题打标签,比如“搜索树的扩展顺序”“贝叶斯公式归一化”“SVM 对偶问题”。这一步用人工做最好,因为题量不大,一晚上能标完一百道。做法是在 CSV 里新增一列 tag,然后按 tag 分组统计:
python3 - <<'EOF' import csv, collections with open("answers_structured.csv", encoding="utf-8-sig") as f: rows = list(csv.DictReader(f)) cnt = collections.Counter(r["tag"] for r in rows if r.get("tag")) for k, v in cnt.most_common(10): print(k, v) EOF这段命令统计最高频的考点标签,得到的结果就是复习优先级清单。比如发现“贝叶斯公式归一化”出现 20 次,而“决策树剪枝”只出现 3 次,说明前者是重点,需要优先掌握。按考点分布复习,比按章节顺序复习更快暴露薄弱点。我后来养成的习惯是,拿到任何答案资料,先做结构扫描,再做答案台账,最后才进入阅读和抽卡。这样花掉两小时,但后面复习省的时间是十几倍。希望帮到你。
本文还有配套的精品资源,点击获取