目录如下:
1. 问题引入:一个蒙着眼下山的人
2. 梯度下降的相关概念Review
2.1 步长(Learning rate)
2.2 特征(Feature)
2.3 假设函数(Hypothesis Function)
2.4 损失函数(Loss Function)
3. 梯度下降与代价函数结合(一元线性回归)
4. (※很重要)求解损失函数的导数
5. 梯度下降法的推导流程
5.1先决条件:确认优化模型的假设函数和损失函数
5.2算法相关参数初始化
5.3算法过程
6. 计算损失函数
7. 梯度下降法三兄弟:BGD/SGD/MGD
7.1 全梯度下降法(Full Gradient Descent,FG/批量梯度下降 BGD)
7.2 随机梯度下降法(Stochastic Gradient Descent,SGD)
7.3 小批量梯度下降法(Mini-batch Gradient Descent,MGD)
7.4 怎么选batch_size?
7.5 三兄弟对比表
7.6 别忘了学习率!
8. 案例讲解:学生成绩预测(附录有上机实战,从安装Acaconda开始手把手教学)
8.1 案例背景
8.2 数据
8.3 步骤分析
8.4 数据导入
8.5 模型训练和预测
9. 总结
附录:Jupyter的安装与第一个项目实战(Anaconda版)
附1. 选择Anaconda的理由
附2. Anaconda与jupyter安装步骤
附3. 第一个项目:跑通"学生成绩预测"
附4. 跑通验收标准
附5. 常见坑速查表(Anaconda版)
1. 问题引入:一个蒙着眼下山的人
设想一个场景:大雾天,你被蒙住眼睛,站在一座山的半山腰。你的目标是走到山谷最低点——当然,你看不见整个山势,也看不清谷底在哪里。
你能怎么办?
你只能靠脚下的感觉:感受一下脚下哪个方向是下坡,然后朝那个方向迈一步;到了新位置,再感受一下,再迈一步……就这么一步、一步地往下走。虽然你全程都"看不见全局",但只要每一步都在往下走,你最终就能到达谷底。
这个朴素的想法,就是梯度下降(Gradient Descent)的核心思想。
把场景映射到机器学习里:
| 下山场景 | 机器学习 |
|---|---|
| 你的位置 | 模型参数 |
| 山的海拔 | 损失函数 |
| "下坡的方向" | 梯度的负方向(最陡下降方向) |
| 每步迈的步子大小 | 学习率 |
| 山谷最低点 | 损失函数最小值处(最优参数) |
机器学习里的"训练模型",本质上就是在不断调整参数,让损失函数
越来越小——和蒙眼下山是同一件事。
2. 梯度下降的相关概念Review
在正式推导之前,先复习四个基础概念。
2.1 步长(Learning rate)
步长决定了在梯度下降迭代的过程中,每一步沿梯度负方向前进的长度。用下山的例子,步长就是你当前这一步沿着最陡峭、最易下山的位置走的那一步的长度。
步长太小→走得很慢,半天到不了谷底;
步长太大→一步跨过头,可能在山谷两边来回"弹跳",甚至越走越高(发散)。
2.2 特征(Feature)
特征指的是样本的输入部分。比如有两个"单特征"样本,则第一个样本特征记为,第一个样本输出记为
,即数据集为:
推广到一般情况:,其中
,
是样本个数。
2.3 假设函数(Hypothesis Function)
在监督学习中,为了拟合输入样本而使用的函数,记为。比如对于单特征的样本,可以采用如下拟合函数:
其中、
就是我们要学习的模型参数。
2.4 损失函数(Loss Function)
为了评估模型拟合的好坏,我们用损失函数来度量拟合的程度。损失函数极小化,意味着拟合程度最好,对应的模型参数即为最优参数。
在线性回归中,损失函数通常为"样本输出和假设函数的差,取平方后的平均数"(均方误差):
为什么是"差值的平方"?因为差可正可负,平方后:
① 全部变成非负,方便比较大小;
② 放大了大误差的惩罚(误差 2 的损失是 4,误差 1 的损失是 1,翻倍不是线性增长)。
至于前面为什么乘 1/(2m)——m是为了取平均,2 是为了后面求导时好约分,属于"数学上的小体贴"。
3. 梯度下降与代价函数结合(一元线性回归)
现在我们手里有两样东西:
梯度下降算法(怎么下山):不断重复直到收敛;
线性回归模型(山长什么样);
把两者结合:梯度下降负责"沿最陡方向下山",而这座"山"就是线性回归的损失函数。每迭代一步,和
就沿着各自负梯度的方向迈一小步,直到损失函数不再下降,就得到了最优的拟合直线。
这里有一个非常重要的细节:参数要"同时更新"(simultaneously update)。
"同时更新"的意思是说:计算新和计算新
时,用的都是更新前的旧
、
,而不是先更新
、再用新
去算
。这两者结果完全不同,一定要用临时变量把新值都算好,再一起赋回去。
4. (※很重要)求解损失函数的导数
有了损失函数,梯度下降需要知道每个参数方向上的"坡度",也就是对损失函数求偏导。
我们要分别求和
。这里用到的数学工具是链式法则(对复合函数求导)。
回忆,所以:
(
的系数是 1)
(
的系数是 x)
而对
求导是
,再由链式法则:
当
时(
,2与1/(2m)约分→1/m):
当
时(
,多乘一个
):
把这两个偏导代回梯度下降更新式,算法就可以改写成:
直观理解:
是"预测值与真实值的误差"。误差为正(预测大了)→减去一个正数→θ变小;误差为负(预测小了)→减去一个负数→θ变大。所有样本的误差一起平均,决定这一大步往哪走——这就是"用数据修正模型"。
5. 梯度下降法的推导流程
把整套算法串起来:
5.1先决条件:确认优化模型的假设函数和损失函数
比如对于线性回归,假设函数为(多元情形,用于凑出常数项):
写成求和与向量形式更简洁:
对应的损失函数:
5.2算法相关参数初始化
初始化参数(比如全0或随机小值)、算法终止距离δ和步长(学习率)
。
5.3算法过程
①确定当前位置损失函数的梯度:(就是第4节求的东西)
②当前位置下降一步的距离:
③判断是否对于所有的参数,梯度下降的距离都小于δ——如果小于则算法终止,否则进入步骤 ④
④更新所有的,对于
,其更新表达式如下,更新完毕后再转入步骤①:
不同参数拆开写就是():
......
每个参数都用"自己的那列特征"去加权误差,方向互不干扰、同时更新。
6. 计算损失函数
实际上,损失函数用NumPy几行就能实现:
import numpy as np def computeCost(x, y, theta): inner = np.power(((x * theta.T) - y), 2) # 每个样本的误差平方 return np.sum(inner) / (2 * len(x)) # 全部加起来取平均,再乘 1/2其中x是特征矩阵(形状m×(n+1),第一列全为1),y是真实标签向量,是参数行向量。
x * theta.T就是m个样本的预测值,减去y求平方再平均,就得到了
。
7. 梯度下降法三兄弟:BGD/SGD/MGD
上面的公式(5.3里的④)里有一个变量——"每次更新用多少个样本?" 根据这个答案的不同,梯度下降分为三兄弟:
7.1 全梯度下降法(Full Gradient Descent,FG/批量梯度下降 BGD)
具体做法:更新参数时使用所有的样本。计算训练集所有样本的误差,对其求和再取平均值作为目标函数。
优点:每步方向是全局最准确的,loss 平滑单调下降,凸函数下能稳定收敛到全局最优。
缺点:每次更新都要在整个数据集上计算所有梯度,速度很慢;无法处理超出内存容量限制的数据集;不能在线更新模型,即运行过程中不能增加新的样本。
打个比方:BGD像"全员大会决策制"——每走一步,全公司所有员工都要汇报一遍意见,方向绝对靠谱,但开一次会就要等所有人,走得又慢又累。
7.2 随机梯度下降法(Stochastic Gradient Descent,SGD)
具体做法:每次只代入计算一个样本目标函数的梯度来更新权重,再取下一个样本重复此过程,直到损失函数值停止下降或损失函数值小于某个可以容忍的阈值。
优点:每步只需算1个样本,速度飞快;可以边训练边来新样本(在线学习);由于噪声大,反而通常能较好地避免陷入局部最优解。
缺点:单样本带来的噪声让路线歪歪扭扭,若遇上噪声则容易陷入局部最优解;最终会在最小值附近来回波动,难以精确收敛到最小值。
打个比方:SGD像"个人拍板制"——一个人说往东就往东,走一步算一步,快是真快,但方向全凭个人感觉,路线歪歪扭扭,到了谷底附近还会来回打转。
7.3 小批量梯度下降法(Mini-batch Gradient Descent,MGD)
具体做法:小批量梯度下降是FG和SG的折中方案,一定程度上兼顾了两者的优点。每次从训练样本集中随机抽取一个小样本集,在这个小样本集上用FG的方式迭代更新权重。
被抽出的小样本集所含样本点的个数称为batch_size,通常设置为2的幂次方(如 64、128、256、512),更有利于GPU加速处理。
迭代形式为(从m个样本中选x个样本进行迭代,1<x<m):
两个极端:若 batch_size = 1 →退化为 SGD(每个样本就是一个子集,共m个子集);若 batch_size = m →退化为BGD(只有一个子集 (X, Y))。
打个比方:MGD 像"小组讨论制"——几个代表商量一下方向就走,既不像全员大会那么慢,又不像一个人拍板那么飘,是实际工程中最常用的方案。
三种算法的"下山路线"差异,一张图看懂:
图中蓝色BGD路线平滑稳定直达谷底;紫色SGD路线弯弯绕绕但步伐快;绿色MGD介于两者之间。
关于loss曲线的振荡:
使用BGD,随着迭代次数增加,loss是不断减小的;而使用MGD,随着在不同mini-batch上迭代训练,loss不是单调下降,而是受类似noise的影响出现振荡——比如可能第一个子集是"好子集",而第二个子集
恰好包含了一些噪声。但整体趋势是下降的,最终也能得到较低的loss值,出现细微振荡是正常现象:
7.4 怎么选batch_size?
mini-batch size不能设置得太大或太小;
总体样本不大时(比如m≤2000),直接使用BGD;
m很大时,推荐的mini-batch size为64、128、256、512(2的幂,提高运算速度);
BGD使用所有m个样本,会比较平稳地接近全局最小值,但每次前进的速度有些慢;
SGD每次前进速度很快,但路线曲折、振荡较大,最终会在最小值附近来回波动,难以真正达到最小值处。
7.5 三兄弟对比表
| 对比项 | BGD(全梯度) | SGD(随机) | MGD(小批量) |
|---|---|---|---|
| 每次更新用样本数 | 全部m个 | 1个 | 一小批(batch_size) |
| 更新速度 | 慢 | 快 | 较快 |
| 路径稳定性 | 平滑稳定 | 曲折颠簸 | 折中(有细微振荡) |
| loss 曲线 | 单调下降 | 波动大 | 整体下降带振荡 |
| 能否在线更新 | 不能 | 能 | 能 |
| 是否易陷入局部最优 | 凸函数收敛全局最优 | 噪声大,通常可跳出局部最优 | 介于两者之间 |
| 内存友好度 | 差(需全量数据) | 好 | 好 |
| 实际应用 | 小数据集(m≤2000) | 在线学习/超大数据集 | 最常用 |
7.6 别忘了学习率
!
不管哪一版梯度下降,都躲不开学习率这个"步子大小"的问题:
太小:稳稳当当,但收敛极慢,训练时间感人;
合适:几步就到谷底附近,效率最高;
偏大:在谷底两边来回震荡,loss反复横跳;
过大:一步跨过山谷,越跳越远,直接发散(损失函数爆炸)。
8. 案例讲解:学生成绩预测(附录有上机实战,从安装Acaconda开始手把手教学)
理论知识到此为止,接下来来看一个完整案例。
8.1 案例背景
我们想用"平时成绩+期末成绩"预测"最终成绩"。用sklearn的线性回归API:
from sklearn.linear_model import LinearRegression # LinearRegression() # LinearRegression.coef_:回归系数8.2 数据
| 学生 | 平时成绩 | 期末成绩 | 最终成绩 |
|---|---|---|---|
| 1 | 80 | 86 | 84.2 |
| 2 | 82 | 80 | 80.6 |
| 3 | 85 | 78 | 80.1 |
| 4 | 90 | 90 | ? |
| 5 | 86 | 82 | 83.2 |
| 6 | 82 | 90 | 87.6 |
| 7 | 78 | 80 | 79.4 |
| 8 | 92 | 94 | 93.4 |
那么,第4位同学的最终成绩是"?"——这就是要预测的目标。
8.3 步骤分析
机器学习建模的标准五步:1. 获取数据集 → 2. 数据基本处理 → 3. 特征工程 → 4. 机器学习 → 5. 模型评估。
8.4 数据导入
x = [[80, 86], [82, 80], [85, 78], [90, 90], [86, 82], [82, 90], [78, 80], [92, 94]] y = [84.2, 80.6, 80.1, 90, 83.2, 87.6, 79.4, 93.4]8.5 模型训练和预测
# 实例化 API estimator = LinearRegression() # 使用 fit 方法进行训练 estimator.fit(x, y) # 查看回归系数 estimator.coef_ # array([0.3, 0.7]) # 预测:平时 100、期末 80 的同学最终成绩 pred = estimator.predict([[100, 80]]) print(pred) # [86.]模型的系数是[0.3, 0.7],意味着最终成绩≈0.3×平时成绩+0.7×期末成绩。回看第4位同学:0.3×90 + 0.7×90 = 90,与真实值90完全吻合,说明拟合得很不错。预测"平时100、期末80"的同学→0.3×100 + 0.7×80 = 86,输出正是86.0。
9. 总结
- 梯度下降的本质:沿着损失函数下降最快的方向(负梯度)迭代更新参数,直到损失不再下降——蒙着眼下山。
- 三个关键角色:损失函数
(要爬的山)、学习率
(步子大小)、终止条件δ(走多近算到)。
- 三兄弟怎么选:数据小用BGD,数据大用MGD(batch_size取2的幂),要在线学习/逃离局部最优可以考虑SGD。
- 两个必须小心的坑:学习率太大会发散、太小会龟速;参数必须同时更新。
最后,谢谢恩师陈老师。
如果这篇笔记能帮你把梯度下降讲懂哪怕一点点,欢迎点赞收藏,也欢迎评论区斧正~
接下来,上实战!
附录:Jupyter的安装与第一个项目实战(Anaconda版)
前面把梯度下降的理论和案例都讲完了,纸上谈兵不如动手跑一遍。本文附上用Anaconda安装Jupyter并跑通"学生成绩预测"案例的完整流程(Windows)。
附1. 选择Anaconda的理由
Anaconda是一个"数据科学全家桶"发行版:装好它,Jupyter、numpy等常用库全送,不需要一个一个pip安装,对新手最友好。缺点是安装包比较大(好几个GB哦),但换来的是省心和稳定。
附2. Anaconda与jupyter安装步骤
第1步:下载Anaconda安装包
官网:
https://www.anaconda.com/download(国内网络慢的话,用清华镜像:https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/,下载最新的Windows x86_64 版exe)。
此处以清华源的Anaconda3-2025.12-2-windows-x86_64.exe为例,进行案例讲解。
第2步:安装
双击exe,跟着我的截图一路Next,注意以下两点以及我图片里标注的项:
安装路径建议不放C盘,不要装在带中文和空格的目录;
到 "Advanced Options" 这一步:不要勾选 "Add Anaconda3 to my PATH"。勾了容易和电脑里已有的Python环境抢环境变量。不勾也没关系,因为目前我们不用普通CMD启动,用专门的Anaconda Prompt。
勾选项为:
Create shortcuts:创建开始菜单快捷方式——勾上
Register Anaconda3 as the system Python 3.13:把 Anaconda 的 Python 注册为系统默认 Python——勾上(VSCode/PyCharm能自动识别)
Clear the package cache upon completion:装完后清理安装缓存,省磁盘空间——勾上
配置环境变量:
在系统变量里找到Path并双击:
点击新建:
我们要新建四个相关acaconda的环境变量,请替换成自己的实际路径:
Anaconda
Anaconda\Scripts
Anaconda\Library\bin
Anaconda\Library\mingw-w64\bin
配置完成点击确定退出即可。在应用列表里就可以看到Acaconda的身影了!恭喜我们安装好啦!(但是还要验证有没有真的安装好了)
第3步:验证安装
打开开始菜单→找到并打开Anaconda Prompt(这是Anaconda自带终端,会默认进入它的base环境),输入:
conda --version能显示版本号(如conda 25.x.x)就说明安装成功。
新建虚拟环境:
conda create -n new_env //new_env更换成你的虚拟环境的名字在创建环境、安装各种库时会有一个确认请求,输入y确认即可:
查看环境列表:
conda env list切换虚拟环境:
conda activate new_env退出虚拟环境使用口令conda deactivate。
安装jupyter:
pip install jupyter notebook安装内核:
pip install ipykernel注册内核:
python -m ipykernel install --user --name=myenv --display-name="Python (myenv)"其中:
myenv替换成自己的kernel内核标识符(建议与环境名一致);
引号内的"Python (myenv)"是Jupyter界面里显示的名字,可以随意改成自己喜欢的名字。
输入以下口令可以查看当前所有内核:
jupyter kernelspec list第4步:启动Jupyter
在Anaconda Prompt里输入:
jupyter notebook稍等片刻,浏览器会自动打开http://localhost:8888,看到文件列表页面(你的用户名目录)就成功了。
注意:这个Anaconda Prompt窗口不能关,关了网页就打不开了(Jupyter服务是跟着终端走的)。
但是我们发现,目前的文件夹有很多乱七八糟的内容,我们怎样才能给自己弄一个专门用来放code的空文件夹呢?(为了方便管理)
法一:切换启动目录,专门用一个本地文件夹作为Jupyter根目录
目前不建议在C:\Windows\System32运行jupyter,这个是系统目录,容易有权限问题,文件杂乱。
操作步骤:
1.先把当前jupyter服务关掉:在 Anaconda Prompt窗口,按
Ctrl+C,输入y关闭jupyter;2.在电脑D盘/桌面新建一个文件夹,例如D:\jupyter_code(专门存放所有代码);
3.在Anaconda Prompt(现在是study环境)输入命令,切换到这个目录,再重新启动。
D: cd D:\jupyter_codejupyter notebook打开网页后,根目录就直接是D:\jupyter_code,干干净净,以后所有代码文件都存在这里。
以后每次打开Anaconda Prompt,只要先执行cd D:\jupyter_code,再启动 jupyter,就直接进入这个专属工作区啦。
法二:但如果不想每次都手动cd切换目录,可以修改jupyter配置,默认打开就进D盘工作文件夹:
关闭 jupyter,在 Anaconda Prompt 输入:
jupyter notebook --generate-config找到生成的
jupyter_notebook_config.py,搜索notebook_dir修改为:
c.NotebookApp.notebook_dir = 'D:\\jupyter_code'注意是双反斜杠,去掉前面
#注释符号,保存。再重启jupyter就默认打开这个文件夹。
路径看截图高亮引导:(你的路径不一定跟我的一样,但位置是差不多的)
找到这个.py文件并打开呀,搜索notebook_dir:
添加指定内容:
重新启动jupyter notebook,可以发现已经是干干净净的了!
附3. 第一个项目:跑通"学生成绩预测"
所有准备工作做好,我们开始实战了!
在Jupyter文件列表页右上角点New→Python 3(ipykernel)新建一个笔记本,然后把下面代码逐个复制进格子(Cell),每个格子输完按Shift+Enter运行。
Cell 1:验证环境
print("Hello, Jupyter!") import numpy as np print("numpy 版本:", np.__version__)Cell 2:导入线性回归API
from sklearn.linear_model import LinearRegression啊呀,报错了:报错是因为当前Jupyter选中的study内核对应的Python环境里没有安装scikit-learn库。
我们保存当前代码退出,重新打开窗口,输入(使用清华源要快一点哦):
pip install scikit-learn -i https://pypi.tuna.tsinghua.edu.cn/simple --default-timeout=1000安装完成之后,再重新启动jupyter notebook呀,重新回到code编译之前,记得重启内核(Restart Kernel):
restart之后,所有cell都需要重新运行哦,Jupyter的[数字]代表执行顺序,不是单元格从上到下的顺序!
通过啦!(迈出了具有里程碑的一步!)
Cell 3:准备数据(平时成绩、期末成绩→最终成绩)
x = [[80, 86], [82, 80], [85, 78], [90, 90], [86, 82], [82, 90], [78, 80], [92, 94]] y = [84.2, 80.6, 80.1, 90, 83.2, 87.6, 79.4, 93.4] print("样本数:", len(x))Cell 4:训练模型
estimator = LinearRegression() estimator.fit(x, y) print("回归系数 coef_:", estimator.coef_)看到[0.3 0.7]就说明模型学出来——最终成绩 ≈ 0.3×平时 + 0.7×期末,和我们的结论完全一致。
Cell 5:预测
print("第4位同学预测:", estimator.predict([[90, 90]])) # 应接近 90.0 print("新同学预测:", estimator.predict([[100, 80]])) # 应为 86.0Cell 6(加餐):用numpy手写正文里的损失函数验证参数
import numpy as np def computeCost(x, y, theta): pred = x @ theta inner = np.power((pred - y), 2) return np.sum(inner) / (2 * len(x)) X = np.column_stack([np.ones(len(x)), np.array(x)]) Y = np.array(y) theta = np.array([0.0, 0.3, 0.7]) print("当前损失 J(θ) =", computeCost(X, Y, theta))Cell 7(加餐):画预测效果图(注意!!要提前下载matplotlib库哦)
pip install matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple --default-timeout=1000由于matplotlib默认字体DejaVu Sans不支持中文,编译后会产生很多警告,我们提前在绘图代码最开头设置支持中文的字体,再加一行解决负号乱码。
import matplotlib.pyplot as plt # 设置中文字体 plt.rcParams['font.sans-serif'] = ['SimHei'] #黑体 plt.rcParams['axes.unicode_minus'] = False #负号显示异常问题 pred_all = estimator.predict(x) plt.figure(figsize=(6, 4)) plt.scatter(y, pred_all, color='steelblue', s=40) #s是散点大小 plt.plot([min(y), max(y)], [min(y), max(y)], 'r--', linewidth=2) #linewidth=2红色虚线加粗 plt.xlabel("真实最终成绩", fontsize=11) plt.ylabel("预测最终成绩", fontsize=11) plt.title("学生成绩预测效果", fontsize=12) plt.grid(alpha=0.3) #alpha=0.3浅灰色网格 plt.show()点越集中在红虚线附近,说明预测越准。
附4. 跑通验收标准
其实同时满足这三点,第一个项目就正式跑通了,我们的任务圆满完成!
1、Cell 4输出
coef_为[0.3 0.7];2、Cell 5预测输出
[86.];3、目录里多出一个
.ipynb文件(Ctrl+S保存,名字随意,如test1_student_score.ipynb)。
以后每次使用:打开Anaconda Prompt→jupyter notebook→打开保存的.ipynb或者新建一个继续写。
附5. 常见坑速查表(Anaconda版)
| 现象 | 原因 | 解决办法 |
|---|---|---|
conda不是内部或外部命令 | 普通CMD里没进conda 环境 | 改用开始菜单里的Anaconda Prompt |
| 网页打开但新建时没有Python 3 | 缺ipykernel | Anaconda Prompt里pip install ipykernel后重启 Jupyter |
| 8888端口被占用 | 别处已开了一个 | jupyter notebook --port=8889 |
| 关掉终端网页就打不开 | 正常现象,服务随终端退出 | 重新运行jupyter notebook |
运行代码报ModuleNotFoundError | 缺某个库 | Anaconda Prompt里pip install 库名(必要时使用清华源)后记得在菜单栏Kernel→Restart |
| 想用已有Python跑Jupyter | 与Anaconda 混用 | 不建议混用;Anaconda自带Python,直接用它的环境即可 |
结语:理论配实战,才是最快的掌握方式。
希望这篇总结能帮助到你,如有错误或遗漏,欢迎指正交流!