
简介这是一套面向零基础学习者与高校教学场景的微软人工智能课程中文版资料按十二周、二十四课的学习路径组织内容覆盖符号人工智能、知识表示与推理、神经网络与深度学习、计算机视觉、自然语言处理以及遗传算法和多智能体系统等主题。包内共三百七十五个文件以八十个 Markdown 课程讲义、五十三个 Notebook 练习代码、一百零一个示意图和三十多个配置文件为主另有少量 Python 脚本、网页文件、视频与容器配置压缩包约五十六点七兆。已有约一百四十人学习适合按大纲自学、课堂演示或实验复现。资料结合 TensorFlow 与 PyTorch 代码讲解关键概念配有测验、实验室活动和伦理人工智能原则便于读者从概念理解走向动手实践并借助 Notebook 逐步复现图像与文本处理模型形成可回查、可运行的课程学习档案。1. 从一堆跑不起来的 notebook 说起这套 AI 入门课程到底值不值得拆上个月帮一个做后端的 A 同学看环境他硬盘里存了七八套“人工智能学习路线”其中有一套是某头部软件厂商开源、已经被社区翻成中文的入门课程讲义、练习代码、课后小测都齐。结果他卡在第三个 notebook报错ModuleNotFoundError: No module named tensorflow折腾两天就放弃了。这个反直觉的点值得先说劝退新人的从来不是反向传播的数学而是内核选错、版本对不上、数据路径找不到这些工程琐事。这份 AI 入门课程资源的价值也在这里——它把“人工智能”拆成了二十多个能在一台普通笔记本上跑完的课时从 AI 历史、神经网络基础一路铺到计算机视觉、自然语言处理、强化学习和遗传算法每一课配一份讲义、一份可执行的 Notebook 练习代码、一次课前小测和一次课后小测。它适合两类人想系统过一遍 AI 基础但不想一上来啃论文的工程开发者以及需要现成教学素材的讲师。下面按我自己拆的过程讲结构怎么排、环境怎么钉、核心代码长什么样、哪里最容易翻车、最后怎么改成自己的东西。2. 课程结构拆解二十多个课时是怎么从感知机排到强化学习的一套课程能不能自学看目录就知道。这套东西的组织方式很“工程”按周次切片每周一个主题域主题域下面再按课时编号展开。中文版在原始结构上做了一层翻译讲义的 Markdown 单元基本全中文代码单元里的变量名和函数名保留英文——这个取舍是对的翻译变量名会让后面查 API 文档的人彻底断线。2.1 仓库目录按「周次 主题」两级切片的排布我把仓库拉到本地后重命名成了ai-for-starters-zh目录大致是这个形状ai-for-starters-zh/ ├── lessons/ │ ├── 1-Introduction/ │ │ ├── README.md # 本课讲义中文 │ │ ├── notebook.ipynb # 练习代码可直接跑 │ │ └── assignment.ipynb # 课后作业带 TODO 空位 │ ├── 2-Symbolic/ │ ├── 3-NeuralNetworks/ │ ├── 4-ComputerVision/ │ ├── 5-NLP/ │ ├── 6-Other/ │ └── 7-Ethics/ ├── translations/ # 各语言版本的对照表与翻译进度 ├── etc/ # 图片、示意图、数据集说明 └── README.md # 总目录 学习路径建议这种切法的好处是每一课都自成闭环打开lessons/4-ComputerVision/就能从讲义读到代码不需要在仓库里来回跳。坏处也明显——课时之间的依赖是隐式的比如第 3 周的神经网络课默认你已经理解了第 2 周讲的感知机和梯度讲义里不会反复提醒。我一般会先把README.md的周次表抄成一张自己的进度表标上“已跑通 / 只读未跑 / 跳过”不然很容易在第 5 周回头补第 3 周的代码。translations/这个目录容易被忽略。它是给多语言版本做对照用的里面有术语表和翻译进度标记。中文版里术语不统一的情况确实存在比如 “regression” 有的地方译“回归”有的地方直接留英文“embedding” 一半译“嵌入”一半写“词向量”。做二次开发或者给团队做内训时建议自己维护一份术语对照别指望仓库里那份是全的。2.2 每一课的四件套讲义、练习、小测、作业同一个课时目录里通常有四类文件阅读顺序错了效率会差很多。我踩过一次先打开assignment.ipynb结果里面全是# TODO和空的assert完全不知道要填什么浪费了半小时。文件作用建议打开顺序备注README.md讲义正文含概念、图示、外部链接1中文版主体先通读一遍不要求全懂notebook.ipynb可执行练习代码边讲边跑2逐 cell 执行别点“全部运行”课前/课后小测选择题检验概念3有的版本放在讲义内部或独立链接assignment.ipynb课后作业留空让你补4带测试 cell跑通才算过讲义和 Notebook 的分工很清晰讲义负责讲“为什么”Notebook 负责演示“怎么做”。以神经网络那一课为例讲义里画了神经元、激活函数、损失函数的示意图Notebook 里则是用几十行代码把同样的东西实现一遍。两边对照着看比单独看任何一边都有效。提示Notebook 里有些 cell 会训练模型跑一次几分钟。别用“全部运行”按 cell 逐个跑中间出错能立刻定位到是哪一步的数据形状不对。2.3 依赖地图哪些库在哪一类课里才出现这套课程的依赖是分阶段的不是一上来全装。搞清楚这张地图能省掉一大半“装了用不上”的麻烦。阶段主要依赖用在哪类课是否必需基础/符号 AInumpy、pandas、matplotlib前几周的导论、搜索算法、知识表示必需神经网络tensorflow/keras、scikit-learn感知机、全连接网络、训练技巧必需计算机视觉opencv-python、Pillow、tensorflow卷积网络、图像分类、目标检测入门必需自然语言处理nltk、scikit-learn、jieba文本预处理、词袋/TF-IDF、情感分类必需其他 AIscikit-learn、numpy遗传算法、强化学习的最小实现按需tensorflow是唯一一个需要重点盯版本的依赖后面第 5 章会专门讲它和 Keras 的版本纠缠。jieba是中文版额外需要的——原始课程的文本课基于英文语料中文版补充了中文分词示例不加这个库那几个 cell 直接报错。opencv-python只在视觉课里用如果你只想跑神经网络那几课可以先不装能省两百多兆。3. 环境搭建把 Jupyter 内核、依赖版本和数据路径一次钉死环境这一步是新手最容易翻车的地方也是最不值得反复花时间的地方。我的做法是一遍做对、把配置写成文件以后换机器直接照着跑。3.1 建一个干净的虚拟环境别用系统 Python系统 Python 是所有玄学问题的源头。我见过太多“明明装了却说找不到”的案例本质是 pip 装到了 A 解释器Jupyter 用的是 B 解释器。# 1. 在仓库根目录建虚拟环境3.10 或 3.11 都行别用太新的 python -m venv .venv # 2. 激活。Windows 用 .venv\Scripts\activate source .venv/bin/activate # 3. 升级 pip老版本 pip 解析依赖会失败 python -m pip install --upgrade pip # 4. 分两步装先装基础再装深度学习出错好定位 pip install jupyterlab numpy pandas matplotlib seaborn scikit-learn pip install tensorflow2.15.1 opencv-python nltk jieba这里刻意把tensorflow钉在2.15.1。原因不在课程本身而在 2.16 之后 Keras 被拆成了独立包keras.layers的导入路径和旧写法对不上课程里大量from tensorflow.keras import ...的老代码会直接崩。钉版本是成本最低的止损。装完立刻验证一次别等到打开 Notebook 才发现问题python -c import tensorflow as tf, numpy, sklearn, cv2; print(tf.__version__, tf.config.list_physical_devices(GPU))这行代码做两件事确认四个核心库都能导入以及打印 TensorFlow 版本和可见的 GPU 列表。返回[]说没检测到 GPU用 CPU 跑这套课程的练习完全够手写数字那个数据集在 CPU 上训练一轮也就十几秒。依赖装完之后把锁文件固化下来pip freeze requirements.txt以后同事克隆你的改造版仓库一句pip install -r requirements.txt就能复现你的环境。这一步看着多余等你在第二台机器上被版本差异坑过一次就会习惯性加上了。3.2 注册内核 批量自检 notebook 能不能跑虚拟环境建好了Jupyter 默认还是不会用它。必须显式注册一个内核python -m ipykernel install --user --name ai-starters --display-name Python (AI Starters)--name是内核的内部标识起个短的不带空格的--display-name是 Notebook 界面里下拉框看到的文字可以写人话。注册完打开 Notebook在 Kernel 菜单里切到Python (AI Starters)右上角显示的内核名要和它一致。仓库里几十个 notebook手动一个个开太慢。写个脚本扫一遍把内核不对的、没有代码单元的挑出来import json import pathlib bad [] for p in pathlib.Path(lessons).rglob(*.ipynb): nb json.loads(p.read_text(encodingutf-8)) # 内核名写在 metadata.kernelspec.name 里缺省说明没设过 kernel nb.get(metadata, {}).get(kernelspec, {}).get(name, ) n_code sum(1 for c in nb[cells] if c[cell_type] code) if kernel ! python3 or n_code 0: bad.append((str(p), kernel, n_code)) print(f待修正 {len(bad)} 个) for row in bad[:10]: print(row)脚本的逻辑很短遍历lessons下所有.ipynb读 JSON从metadata.kernelspec.name取内核名统计代码单元数量。判定条件是“内核不是python3”或者“一个代码单元都没有”。前者说明这个 notebook 绑了别的内核后者多半是纯讲义或者翻译时把代码单元弄丢了。输出待修正 0 个就说明整套练习代码可以直接跑。注意metadata.kernelspec.name是python3不代表一定用你的虚拟环境它只是默认名。真正判断标准是打开 notebook 后跑一句import sys; print(sys.executable)路径里带.venv才算对。3.3 数据、模型、图片的路径约定路径问题是第三大坑。Notebook 的工作目录取决于你从哪里启动 Jupyter——在仓库根目录启动和在lessons/4-ComputerVision/里启动Path.cwd()返回的完全不一样pd.read_csv(data/xxx.csv)这种相对路径就时灵时不灵。我的习惯是在每个 notebook 的第一个 cell 里固定一个根目录常量from pathlib import Path # 从当前工作目录向上找直到看见 lessons 目录认定这是仓库根 ROOT Path.cwd() while not (ROOT / lessons).exists() and ROOT ! ROOT.parent: ROOT ROOT.parent DATA ROOT / etc / data print(仓库根:, ROOT, | 存在:, DATA.exists())向上回溯找lessons目录的做法比硬编码绝对路径可移植比os.chdir安全。后面所有读写都基于ROOT拼notebook 从哪个目录启动都不影响。数据集本身分两类一类是课程自带的小样本放在etc/下面跟着仓库走另一类是框架自动下载的比如keras.datasets.mnist.load_data()会拉到~/.keras/datasets/。第一类要求你把仓库完整克隆下来别只复制lessons目录第二类第一次运行需要能下载下载过一次之后就缓存在本地后面离线也能跑。4. 核心代码拆解从手写感知机到 Keras 卷积网络的完整链路课程最值得逐行读的就是代码部分。它有个很好的设计同一个概念先用最原始的 NumPy 手写一遍再换成框架的几行 API让你知道框架替你做了什么。4.1 先手写一个感知机把「权重更新」这件事看清楚神经网络那几课的开头不是直接上 Keras而是让你用二十行 NumPy 实现一个感知机。这段代码短但把训练的本质讲透了。import numpy as np class Perceptron: def __init__(self, n_features, lr0.1, epochs20): self.w np.zeros(n_features) # 权重向量长度等于特征数 self.b 0.0 # 偏置标量 self.lr lr # 学习率决定每次修正的步子多大 self.epochs epochs # 遍历训练集的轮数 def predict(self, X): # 加权和 0 判为正类 1否则 0阶跃函数不可导所以只做分类不做概率 return (X self.w self.b 0).astype(int) def fit(self, X, y): for _ in range(self.epochs): for xi, yi in zip(X, y): y_hat int(xi self.w self.b 0) err yi - y_hat # 分对了 err0分错了 err±1 self.w self.lr * err * xi # 只有 err 非 0 时才真的更新 self.b self.lr * err return selffit里那个err是整个算法的核心。分类正确时err 0权重一点都不动分类错误时err是1或-1权重沿着输入向量的方向推一把。这就是“感知机学习规则”也是后面梯度下降的雏形——区别只在于这里用阶跃函数直接给对错而神经网络用损失函数的梯度给出方向和幅度。lr的取值直接影响收敛。太大权重会来回震荡甚至发散太小二十轮跑完还没走到边界。课程里给的默认值偏保守实际换数据集时要自己调。epochs建议一开始设小一点比如 5看损失曲线有没有下降趋势再往上加。配套的小练习通常是把数据换成线性不可分的异或问题然后让你观察准确率卡在 50% 上不去。这个设计很到位它逼你承认单层感知机的天花板为后面“加隐藏层”和“换激活函数”铺好了动机。4.2 换成 Keras全连接网络做手写数字分类讲完手写版神经网络课的第二段用 Keras 重做同一件事。代码短了十倍但每一行都有讲究。import tensorflow as tf from tensorflow import keras # 1. 数据60000 张 28x28 的灰度图标签是 0-9 (x_train, y_train), (x_test, y_test) keras.datasets.mnist.load_data() # 2. 预处理展平成 784 维向量并归一化到 [0,1] x_train x_train.reshape(-1, 784).astype(float32) / 255.0 x_test x_test.reshape(-1, 784).astype(float32) / 255.0 # 3. 模型一层隐藏层 Dropout 输出层 model keras.Sequential([ keras.layers.Dense(128, activationrelu, input_shape(784,)), keras.layers.Dropout(0.2), keras.layers.Dense(10, activationsoftmax), ]) # 4. 编译优化器、损失函数、监控指标 model.compile( optimizerkeras.optimizers.Adam(learning_rate1e-3), losssparse_categorical_crossentropy, metrics[accuracy], ) # 5. 训练每批 64 张抽 10% 做验证 history model.fit( x_train, y_train, epochs5, batch_size64, validation_split0.1, )几个参数值得单独说。reshape(-1, 784)里的-1让 NumPy 自己算样本数784 28 × 28因为Dense层只吃一维输入。除以255.0是归一化不除的话像素值在 0-255 之间梯度尺度太大学习率得调到很小才能稳住。损失函数选sparse_categorical_crossentropy而不是categorical_crossentropy区别只在标签格式前者吃整数标签3后者吃 one-hot[0,0,0,1,0,...]。MNIST 的标签本来就是整数用 sparse 版本省一次转换也省内存。Dropout(0.2)放在隐藏层之后训练时随机丢掉 20% 的神经元是抑制过拟合最便宜的手段。验证集准确率和训练集差得远的时候把这个值往上调两者都很低说明是欠拟合该加层或者加训练轮数。validation_split0.1从训练集里切 5400 张出来做验证history里能拿到每轮的val_loss和val_accuracy。跑五轮验证准确率一般能到 97% 上下——这就是“框架替你做了梯度计算、参数更新、批次调度”之后的效果对比 4.1 那二十行能直观感受到抽象层的价值。4.3 卷积网络图像分类那一课的骨架长什么样全连接网络把图像展平丢掉了空间结构。计算机视觉那几课的重点就是把这个结构捡回来。# MNIST 图像是单通道卷积层需要 (28, 28, 1) 的形状 x_train_cnn x_train.reshape(-1, 28, 28, 1) x_test_cnn x_test.reshape(-1, 28, 28, 1) model keras.Sequential([ # 第一组卷积32 个 3x3 卷积核提取边缘、角点这类低级特征 keras.layers.Conv2D(32, (3, 3), activationrelu, input_shape(28, 28, 1)), keras.layers.MaxPooling2D((2, 2)), # 2x2 池化长宽各减半 # 第二组卷积64 个卷积核在低级特征上组合出更复杂的图案 keras.layers.Conv2D(64, (3, 3), activationrelu), keras.layers.MaxPooling2D((2, 2)), keras.layers.Flatten(), # 展平后接分类头 keras.layers.Dense(64, activationrelu), keras.layers.Dense(10, activationsoftmax), ]) model.compile(optimizeradam, losssparse_categorical_crossentropy, metrics[accuracy]) model.summary() # 打印每层输出形状和参数量调结构前先看这个Conv2D的第一个参数是卷积核数量第二个是核尺寸。数字越大提取的特征越丰富但参数量和显存占用同步上涨。MaxPooling2D((2, 2))在保留显著特征的前提下把特征图缩小一半既能降计算量也能扩大后续卷积核的感受野。model.summary()是调试时最该养成的习惯。它会打印每一层的输出形状和参数量形状对不上的问题一眼就能看出来比等到fit报错再往回翻快得多。这套结构在 MNIST 上跑五轮能到 99% 左右比全连接版本高一个多点代价是每次训练慢几倍——这就是用算力换精度最朴素的例子。4.4 文本课的一条链路分词、向量化、分类自然语言处理那几课的流程和图像不一样中间多了“文本转数字”这一环。中文版在这里做了本地化把原本基于英文的分词换成了中文分词。import jieba from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.linear_model import LogisticRegression from sklearn.model_selection import train_test_split # 1. 分词jieba 按词切比按字切保留更多语义 def cut(text): return .join(jieba.cut(text)) texts [cut(t) for t in raw_texts] # raw_texts 是原始中文语料 # 2. 向量化TF-IDF词频高但到处都有的词权重会被压低 vec TfidfVectorizer(max_features5000, ngram_range(1, 2)) X vec.fit_transform(texts) # 稀疏矩阵形状 (样本数, 特征数) y labels # 0/1 情感标签 # 3. 切分 训练 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy) clf LogisticRegression(max_iter1000) clf.fit(X_train, y_train) print(准确率:, clf.score(X_test, y_test))jieba.cut返回的是生成器用 .join()拼成空格分隔的字符串因为TfidfVectorizer默认按空格切词。max_features5000只保留权重最高的 5000 个词控制特征维度ngram_range(1, 2)同时统计单词和相邻词组“不 好看”这种否定结构才不会被拆散。stratifyy保证训练集和测试集的类别比例一致样本不均衡时这一项很关键。LogisticRegression在这里只是当基线用跑通之后课程会引导你换成神经网络分类头对比两者的差距——这也是整套课的一贯节奏先给你一个能跑通的朴素方案再让你补上更复杂的版本。5. 常见问题排查内核、版本、路径这三类坑占了九成仓库里的报错看着千奇百怪按我的经验九成能归到内核、版本、路径三类。下面五条都是我自己或身边人真实遇到的每条按“现象—原因—解决”写。5.1 明明装了 TensorFlowNotebook 里还是 ModuleNotFoundError现象命令行pip list能看到tensorflow 2.15.1Notebook 里import tensorflow依然报ModuleNotFoundError。原因Jupyter 内核指向的是系统解释器你的pip install装进了虚拟环境两个 Python 各活各的。pip list走的是激活后的虚拟环境Notebook 用的是另一个。解决跑 3.2 那行ipykernel install注册内核然后在 Notebook 里执行import sys; print(sys.executable)输出路径必须包含.venv。如果显示的是/usr/bin/python之类说明内核还是没切过来重启 JupyterLab 再选一次。5.2 升级到新版 TensorFlow 后所有from tensorflow.keras全部报错现象AttributeError: module keras.api._v2.keras has no attribute ...或者ImportError: cannot import name layers from keras。原因新版本把 Keras 3 拆成了独立包导入路径和 API 都有变动课程里的老代码是照着 Keras 2 写的。这不是代码写错了是环境跑在了不匹配的版本上。解决退回去。pip install tensorflow2.15.1这个版本内部还是绑的 Keras 2兼容性最好。实在要用新版就装tf-keras兼容包并在每个 notebook 顶部把导入改成import tf_keras as keras。我一般选前者课程练习不值得为新 API 花时间。5.3 数据集下载超时load_data()卡住不动现象跑到keras.datasets.mnist.load_data()就没反应了等几分钟后抛TimeoutError或URLError。原因这个函数第一次调用会去下载mnist.npz网络不通或者下载被限速就卡在这。解决手动下载mnist.npz放到~/.keras/datasets/目录下文件名保持原样。下次load_data()检测到本地有缓存就不走网络了。同理视觉课里其他框架自带的数据集也是这个路径规则。放之前确认一下文件名带不带版本后缀放错了函数依然会重新下载。5.4 相对路径时灵时不灵换个目录启动就 FileNotFoundError现象昨天还能跑今天从另一个目录启动 Jupyterpd.read_csv(etc/data/xxx.csv)直接报找不到文件。原因Notebook 的工作目录等于启动 Jupyter 时所在的目录不是 notebook 文件所在的目录。启动位置变了所有相对路径的基准都变了。解决用 3.3 里的ROOT向上回溯方案所有路径基于ROOT拼接别写裸的相对路径。临时排查可以在第一个 cell 加print(Path.cwd())先看清楚当前在哪。5.5 中文图表全是方块matplotlib 显示不出汉字现象画出来的图标题、坐标轴标签里的中文全是小方块报missing from font(s)。原因matplotlib 的默认字体是英文字体没有中文字形。解决在绘图前显式指定一个系统里装了的字体。Windows 上常用SimHeimacOS 上常用Arial Unicode MSLinux 上装Noto Sans CJK之后用同名。再加两行修负号显示import matplotlib.pyplot as plt plt.rcParams[font.sans-serif] [SimHei, Arial Unicode MS, Noto Sans CJK SC] plt.rcParams[axes.unicode_minus] False # 否则负号也会变成方块字体列表按顺序回退写多个能在不同机器上都兼容。这个配置只影响当前内核重启后要重新跑所以建议放进每个绘图 notebook 的初始化 cell。6. 二次开发把课程 notebook 抽成能复用的训练脚本课程 notebook 适合学习不适合复用。想拿它改造成自己的实验模板第一步是把训练逻辑从 cell 里抽出来变成带参数的函数和脚本。我一般按这张表对照着改改造点课程里的写法我改成什么为什么数据加载直接调load_data()抽成load_dataset(name, root)换成自己的数据时只改一处模型结构在 cell 里定义Sequentialbuild_model(dropout0.2)返回模型方便做超参对比训练参数硬编码epochs5argparse命令行传入跑多组实验不用改代码随机性不设种子固定seed并写进日志结果可复现不然调参全靠玄学输出只 print 准确率存 metrics.json 模型权重事后能对比、能回溯落地成一个脚本大概是这样# train_mnist.py —— 从 notebook 抽出来的可复现训练脚本 import argparse, json, random import numpy as np import tensorflow as tf from tensorflow import keras def set_seed(seed: int) - None: # 三处随机源都要固定少一个都复现不了 random.seed(seed) np.random.seed(seed) tf.random.set_seed(seed) def build_model(dropout: float 0.2) - keras.Model: return keras.Sequential([ keras.layers.Dense(128, activationrelu, input_shape(784,)), keras.layers.Dropout(dropout), keras.layers.Dense(10, activationsoftmax), ]) def main() - None: ap argparse.ArgumentParser() ap.add_argument(--epochs, typeint, default5) ap.add_argument(--batch-size, typeint, default64) ap.add_argument(--lr, typefloat, default1e-3) ap.add_argument(--seed, typeint, default42) args ap.parse_args() set_seed(args.seed) (x_tr, y_tr), (x_te, y_te) keras.datasets.mnist.load_data() x_tr x_tr.reshape(-1, 784).astype(float32) / 255.0 x_te x_te.reshape(-1, 784).astype(float32) / 255.0 model build_model() model.compile(optimizerkeras.optimizers.Adam(args.lr), losssparse_categorical_crossentropy, metrics[accuracy]) hist model.fit(x_tr, y_tr, epochsargs.epochs, batch_sizeargs.batch_size, validation_split0.1, verbose2) loss, acc model.evaluate(x_te, y_te, verbose0) json.dump({test_loss: loss, test_acc: acc, args: vars(args)}, open(frun_seed{args.seed}.json, w), ensure_asciiFalse, indent2) if __name__ __main__: main()改完之后验证方式也变了不再靠眼睛看 notebook 输出而是跑两次同 seed 的训练比对run_seed42.json里的test_acc是否完全一致。一致说明随机源固定住了实验才具备可比性。再补一个快速冒烟测试——把--epochs设成 1、样本量砍到 1000确认整条链路不报错再放全量跑。从那以后我每次改造课程代码都强制先走一遍“抽脚本、固定种子、存指标”这三步再动手调模型。不然改了七八版超参回头想复现最好那一次连当时用的 dropout 是多少都记不清那种感觉比调不出精度还难受。希望帮到你。本文还有配套的精品资源点击获取