简介:这份微软 AI-For-Beginners 中文版课程包面向零基础到入门阶段的学习者,围绕为期12周、24课的课程大纲组织内容,覆盖符号人工智能、神经网络与深度学习、计算机视觉、自然语言处理、遗传算法和多智能体系统等主题,帮助读者建立较完整的 AI 入门知识框架。压缩包共375个文件、约56.7MB,以101张png示意图、80份md课程文档、53个ipynb笔记本为主,辅以json配置、py脚本及少量html、vue页面和mp4演示,兼顾阅读、动手实验与项目展示。目前已有140人学习下载,适合按周推进或按主题查阅。内容强调动手实践,配套测验与实验室活动,并以TensorFlow、PyTorch代码阐释关键概念;读者可沿课程目录完成代码练习、理解模型原理,同时接触道德AI原则与知识表示、推理等传统方法。
1. 先动手再补理论:这份 AI 课程资源卡住的人比想象中多
很多人拿到 AI 课程资源,第一步是把压缩包解开、把 notebook 一口气全点开,然后就没有然后了。这份 AI-For-Beginners 中文版的特别之处,不在于目录有多长,而在于每个模块后面都挂着能跑的练习代码:讲规则推理的给一个专家系统小 demo,讲感知机的给一个能画出决策边界的 notebook,讲文本的给一条能换语料的分类流程。它解决的是「理论看懂、代码跑不起来」这个断裂问题,适合三类人:想系统补一遍经典 AI 骨架的开发者、要拿现成材料带课的讲师、以及准备面试却说不清卷积到底学了什么的人。下面按我实际跑通的顺序写,先拆骨架,再配环境,最后把踩过的坑摊开。
2. 课程骨架拆解:先符号 AI 再神经网络,这个顺序不是随便排的
2.1 模块排布与选型理由:规则系统为什么必须放在学习算法前面
这门课的模块顺序是:AI 导论与历史 → 知识表示 → 专家系统与推理 → 神经网络与感知机 → 计算机视觉 → 文本与自然语言处理。看起来像时间线,实际上是拿「没有学习能力的智能系统」当对照组。专家系统靠人手写规则加一个推理机往前推,好处是可解释、可断点调试,你能指着某一条规则说「就是它把结论带偏的」;神经网络靠损失函数和梯度下降,中间层是一团你没法逐条读的权重。先学前者,你才知道后面那个黑匣子到底替代掉了什么,也才看得懂为什么「可解释性」在工程落地里会被反复拿出来讨论。
这个顺序对动手也有直接影响。符号 AI 那几节的练习代码长这样:一个规则字典、一个事实集合、一个前向推理循环,跑起来 print 一下推理链就完事,调试手段就是打断点和看日志。到了神经网络,练习代码变成矩阵乘法、激活函数、反向传播,出错时你看到的只有 loss 不降。两套调试习惯完全不同,课程把它们前后摆开,等于逼你换一次思维方式。我见过不少人直接跳到 CNN 那几节,结果卡在「为什么这个 3×3 卷积核能提取边缘」上,回不去也上不来,血泪经验就是先把前面的对照组走完。
再往下拆,每个模块的练习代码形态是固定的:知识表示是图搜索和状态转移,神经网络是矩阵运算加梯度,计算机视觉是卷积核加池化加全连接,文本处理是词袋和 TF-IDF 再过渡到嵌入向量。认出这个「一个模块一种代码形态」的规律,你后面看任何一个 notebook 都能在三分钟内判断出它要你改哪一行。
2.2 对照目录做一张自己的进度表
课程材料给的是知识点清单,但清单不等于计划。我的做法是先扫一遍每个模块的 notebook,看它最后输出什么(一张图、一个准确率、一段推理结果),再倒推需要几个小时。下面这张表是我自己排的节奏,时间单位按晚自习算,不是精确工时:
| 模块 | 核心概念 | 练习代码形态 | 建议节奏 | 是否必做 |
|---|---|---|---|---|
| AI 导论与历史 | 智能的定义、流派划分 | 几乎无代码,读文档 | 半个晚上快读 | 读,不敲 |
| 知识表示 | 状态空间、图搜索、约束 | 搜索算法 + 状态转移 | 一到两个晚上 | 必做 |
| 专家系统 | 规则库、推理机、冲突消解 | 规则字典 + 前向推理循环 | 一到两个晚上 | 必做 |
| 神经网络与感知机 | 权重、激活、损失、梯度 | 手写感知机 + 训练循环 | 两到三个晚上 | 必做,且要手敲 |
| 计算机视觉 | 卷积、池化、检测、分割 | CNN 训练 + 可视化特征图 | 三个晚上起 | 分类必做,检测分割可看 |
| 文本与自然语言 | 分词、向量化、分类、嵌入 | 文本管线 + 小分类器 | 两个晚上 | 至少跑通一条端到端 |
表格里「是否必做」这一列是关键。带课的人容易犯的错是全部按顺序讲完,结果时间全花在历史背景上,真正需要动手的感知机和 CNN 反而赶进度。我的建议是历史那节当睡前读物,把省下来的时间砸在梯度推导和卷积核尺寸这两处,因为它们是你后面所有调试的根基。
2.3 哪些模块可以快进,哪些必须自己敲一遍
可以快进的是偏叙述性的部分:流派之争、发展脉络、以及一些应用场景的铺陈。这些内容读一遍有印象就行,不值得为它开环境。
必须自己敲一遍的是三类。第一类是感知机的完整训练循环,从初始化权重、前向算输出、算损失、反向更新,一行都不能抄,抄完你不知道梯度消失长什么样。第二类是卷积核的可视化,把训练前后的卷积核权重画出来对比,这一步做完你对「学到的特征」才有实感。第三类是文本处理里的向量化,从分词到词袋到分类器,亲手跑一遍你会立刻明白为什么中文分词和英文分词是两码事。剩下的目标检测、语义分割这类偏工程整合的模块,看懂数据流和接口形状就够了,不必强求从头训一个模型,笔记本的算力也不允许。
3. 把练习代码跑起来:环境、内核、数据三件事
3.1 虚拟环境与依赖:把「能跑」限制在一个 Python 版本上
这门课的练习代码分散在几十个 notebook 里,依赖跨度不小,有的要深度学习框架,有的只要 numpy 和 matplotlib。最省事的做法反而是最不该做的做法——直接往系统 Python 里 pip install。一旦某个框架升级带崩了旧接口,你连回滚到哪个版本都不知道。
我的习惯是给这门课单开一个虚拟环境,名字写死,别用默认的venv目录名,免得和别的项目混。建好之后先升级打包工具再装依赖,老版本 pip 在解析依赖冲突时给出的报错经常指向错误的包,会让你往错误方向排查半小时。
# 建一个只服务这门课的环境,目录名固定,方便以后一眼认出来 python3 -m venv .venv-ai4b source .venv-ai4b/bin/activate # Windows 换成 .venv-ai4b\Scripts\activate # 先升级打包工具:老版本 pip 解析依赖冲突时的报错经常指错包 python -m pip install --upgrade pip setuptools wheel # 按课程根目录的依赖清单安装,别一个个手敲 pip install -r requirements.txt # notebook 运行器和内核装进同一个环境,避免内核指向系统 Python pip install jupyter ipykernel这几行里最容易被跳过的是第二行。依赖清单里通常会写tensorflow>=x这种宽松约束,pip 会尽量挑最新版,而练习代码是按当时那版写的,接口签名一变,notebook 第一格就崩。第三行的依赖清单如果有锁定版本(带==的那种),优先用锁定的;如果全是>=,装完之后立刻pip freeze > requirements.lock.txt存一份快照,后面出问题能原样重建。
3.2 内核注册与执行顺序:notebook 不按顺序跑就是玄学
Jupyter 最坑的地方在于它允许你跳着执行单元格,而且不给你任何提示。你以为自己在跑第 5 格,实际上第 3 格定义的变量还是上一次会话的残留,结果当然对不上课程文档。解决办法不是靠自己记,而是把环境注册成独立内核,让 notebook 明确知道自己该找哪个解释器。
# 把当前环境注册成独立内核,名字固定,Jupyter 里挑内核时不会选错 python -m ipykernel install --user --name ai4b --display-name "Python (AI4B)" # 确认内核已注册,输出里应该能看到 ai4b jupyter kernelspec list内核名字一旦固定,后面换机器、换环境的时候只要重新注册同名内核,notebook 里保存的内核引用就不用改。这里有个细节:notebook 文件头里记录的是内核的显示名,不是路径,所以你把旧环境删了但没删内核,Jupyter 里还能选到它,点进去就是一堆 ImportError。遇到这种「明明装了包却导入失败」的情况,先跑jupyter kernelspec list看一眼,八成是内核指向了一个已经不存在的解释器。
执行顺序上我一般开两个窗口:一个用来读课程讲解,一个用来跑代码,每跑完一节就 Restart Kernel and Run All 一次。慢是慢了点,但能保证你看到的输出确实是这一节的代码产出的,不是上一节残留的。
3.3 数据落地与路径:给每个 notebook 一个稳定的工作目录
练习代码里读数据的路径基本都是相对路径,比如./data/mnist.npz这种。相对路径的基准不是 notebook 文件所在目录,而是你启动 Jupyter 时所在的目录。于是同一个 notebook,从课程根目录启动能跑,从 notebooks 子目录启动就 FileNotFoundError,很多人第一次踩这个坑会怀疑自己下载的数据损坏了。
我的处理方式是把所有数据集统一收到用户目录下一个固定的位置,notebook 里改一次绝对路径,后面不管从哪启动都不受影响。
import os import pathlib # 先打印当前工作目录:相对路径出错时,第一件事就是看这个 print("cwd:", os.getcwd()) # 所有数据集统一放一个目录,跨项目复用,也不用每次重新下载 DATA_ROOT = pathlib.Path.home() / "datasets" DATA_ROOT.mkdir(parents=True, exist_ok=True) print("data root:", DATA_ROOT) # 顺手做个体检:目录里已有的数据文件有多大,避免重复联网拉取 for p in sorted(DATA_ROOT.iterdir()): size_mb = p.stat().st_size // 1024 // 1024 print(f"{p.name:>24} {size_mb:>6} MB")这段代码的价值在两处。一是os.getcwd()那行 print,排错时能立刻区分「路径写错」和「工作目录错」。二是目录体检,深度学习数据集动辄几十上百兆,训练前先确认文件在、大小合理,比训练到一半报读取失败要省时间。有些 notebook 会用框架自带的数据集接口自动下载,那种情况下我一般先让它下到默认缓存目录,再把缓存目录软链到DATA_ROOT,避免同一份数据在磁盘上存好几份。
3.4 批量执行与结果留档:把「跑通」变成「可复现」
手动点 notebook 有个隐患:你只验证了自己点过的那几个格子。课程里几十个 notebook,靠手点既慢又容易漏。更稳的做法是用 nbconvert 批量跑一遍,把执行结果另存成副本,原始文件保持干净,这样你既能确认每个 notebook 都能从头跑到尾,又不会把输出污染进要分享的文件里。
mkdir -p executed # 逐个执行 notebook,结果写到 executed/ 下,原文件不动 for nb in notebooks/*.ipynb; do jupyter nbconvert --to notebook --execute \ --ExecutePreprocessor.timeout=1800 \ --output-dir executed \ "$nb" done--ExecutePreprocessor.timeout这个参数得看情况调。纯 numpy 的小 notebook 几分钟就跑完,但涉及模型训练的,默认超时很容易被触发,我一般给到 1800 秒。批量执行还有个副作用:某个 notebook 报错会中断整个循环,所以更稳妥的写法是在循环里接一句失败日志,把报错的 notebook 名记下来单独处理,而不是让整个批次停在那里。跑完之后对着executed/目录数一遍文件数,和notebooks/对得上,才算这批练习真的通了。
4. 踩坑排查:仓库能克隆,notebook 跑不通的五种情形
4.1 依赖与内核:ImportError 和内核错位
现象:notebook 第一格就抛ImportError: cannot import name 'xxx' from 'keras',但你刚确认过框架装好了。
原因:环境里混进了多个版本的框架,或者内核指向的是系统 Python 而不是你建的那个虚拟环境。课程依赖清单里如果写的是宽松版本约束,pip 很可能给你装了一个比练习代码新的版本,接口被挪了位置。
解决:先jupyter kernelspec list确认内核路径,不对就重装内核;路径对的话用pip show看实际版本,和练习代码里的 import 对不上就按锁定清单重装。我的原则是一个环境只装一套深度学习框架,不要图省事把两套塞一起,它们对底层库的版本要求经常打架。
4.2 数据与路径:FileNotFoundError 与下载卡住
现象:读取数据那格报找不到文件,或者下载进度条卡在某个百分比再也不动。
原因:前者是 3.3 说的工作目录问题,相对路径的基准和你以为的不一样。后者多半是默认下载源在当前网络下不通,框架的重试逻辑又不给你明确提示。
解决:把数据目录改成绝对路径,先把os.getcwd()打出来确认。下载卡住的话,换成手动下载再放到缓存目录,路径规则通常在框架文档里写得很清楚,放对位置它就不会再联网。我一般习惯在训练脚本开头加一段「数据存在性检查」,缺文件就直接退出并打印期望路径,比跑到第三轮才炸要友好得多。
4.3 图表与中文:方框、空图和负号消失
现象:画出来的图里中文标题全是方框,或者坐标轴的负号变成了一个奇怪的字符。
原因:matplotlib 默认字体不含中文字形,遇到中文就画成缺字方块。负号的问题同理,默认字体里没有对应的 unicode 字符。
解决:显式指定一个系统里真实存在的中文字体,同时关掉 unicode 负号渲染。注意别硬写一个你机器上没有的字体名,那样不报错但也不生效,排查起来很费劲。
import matplotlib import matplotlib.pyplot as plt from matplotlib import font_manager # 从候选字体里挑一个系统真实装了的,不要硬写不存在的名字 candidates = ["Noto Sans CJK SC", "Source Han Sans SC", "SimHei", "Microsoft YaHei"] installed = {f.name for f in font_manager.fontManager.ttflist} for name in candidates: if name in installed: matplotlib.rcParams["font.sans-serif"] = [name] print("using font:", name) break else: print("没有找到候选中文字体,需要先装一个") # 负号走 unicode 时默认字体常缺字形,关掉更稳 matplotlib.rcParams["axes.unicode_minus"] = Falsefont_manager.fontManager.ttflist那一步是重点,它列的是当前进程实际能用的字体,比fc-list之类的系统级命令更贴近 matplotlib 的真实视角。
4.4 结果对不上:随机种子与残留状态
现象:同一个 notebook 跑两遍,准确率差好几个百分点;或者课程文档里写着某个输出,你跑出来完全不是那个数。
原因:一是没固定随机性,权重初始化、数据打乱、dropout 都带随机;二是单元格式的残留状态,跳着执行导致某个变量还是上一轮的值。
解决:进 notebook 第一件事就是设种子,并且把设种子的代码放在最顶上,保证在任何模型构建和训练之前执行。
import random import numpy as np def set_seed(seed: int = 42) -> None: """固定随机性:让两次运行的指标可以对比,也方便报错时复现。""" random.seed(seed) np.random.seed(seed) # 框架是可选的,课程里不同 notebook 用到的不一样,按需补设 try: import tensorflow as tf tf.random.set_seed(seed) except ImportError: pass try: import torch torch.manual_seed(seed) except ImportError: pass set_seed(42)设完种子不代表绝对可复现,GPU 上的部分算子仍有非确定性,但对课程练习来说,误差已经能压到可以接受的程度。残留状态那条没有捷径,纪律就是每节开头 Restart & Run All。
4.5 训练太慢或者内存爆掉
现象:某个 CNN 的 notebook 跑了半小时还没到第一个 epoch,或者直接 MemoryError 退出。
原因:练习代码默认按完整数据集和较大 batch 写,而你的机器是普通笔记本;另外有些 notebook 会一次性把所有图片读进内存做预处理。
解决:这不是 bug,是资源边界。我的做法是先把样本量砍到几千条、epoch 砍到 1,确认整条链路(读数据 → 前向 → 算损失 → 反向 → 打印指标)能走通,再逐步放大。batch size 从 32 往下调到 8 往往能救回内存,代价是慢一点,但至少能看到训练在动。真要在本机跑完整训练,先把数据预处理结果缓存成文件,别每次启动都重算一遍。
5. 进阶:把课程 notebook 改成自己的实验模板
5.1 从「能跑」到「能改」:抽出配置层
课程 notebook 有个通病,超参数散落在各个单元格里:学习率写在第 8 格,epoch 写在第 12 格,数据路径写在第 3 格。你想做一组对比实验,得来回改好几处,改漏一次结果就不可比。我的做法是每个模块跑通之后立刻做一次「抽出常量」的重构,把可调的参数收进一个结构体。
from dataclasses import dataclass @dataclass class ExpConfig: dataset: str = "mnist" model: str = "cnn" epochs: int = 3 batch_size: int = 32 lr: float = 1e-3 seed: int = 42 limit: int = 2000 # 先用小样本验证链路,再放开到全量 # 这些字段就是课程 notebook 里散落各处的硬编码常量, # 收拢之后改实验只动一处,两组结果才有可比性 cfg = ExpConfig()做完这一步,对比实验就变成了实例化两个ExpConfig的事,跑完把结果记进一张表里,哪个参数动了、指标怎么变,一目了然。这一步不难,难的是忍住不跳过——跳过的人做第三组实验就开始记不清基线是什么了。
5.2 验证是否真的吃透:三个动作
判断自己有没有读懂,我一般用三个动作自测,任何一个做不出来就说明还在抄代码阶段。
| 动作 | 具体做法 | 说明什么 |
|---|---|---|
| 改数据不改代码 | 换一份自己的小数据集,只改路径和类别数 | 你理解了数据管线的接口边界 |
| 改结构不改流程 | 把 CNN 换成两层全连接,训练流程原样保留 | 你分得清模型和训练循环的职责 |
| 反向调参 | 故意把学习率调大十倍,预测并解释崩掉的现象 | 你掌握了损失曲线的读法 |
第三个动作最有用。学习率过大时 loss 会震荡甚至发散,这个现象课程里通常会提一句,但只有你自己把它跑出来,下次在真实项目里看到类似的曲线才反应得过来。我一般会在 notebook 末尾加一格专门做这个「破坏性实验」,跑完再改回去,成本很低。
最后说个我自己的教训。早些年我看这类课程,习惯把 notebook 全部跑通就打勾,觉得任务完成了,结果三个月后被人问起卷积核尺寸对输出特征图的影响,脑子里一片空白。从那以后我每次跑完一个模块,都强制自己在空白文件里重写一遍核心循环,不许看原代码,写不出来就当没学会。这个习惯救过我很多次,也希望帮到你。
本文还有配套的精品资源,点击获取