专栏导语 :本专栏系统讲解数学建模的经典方法与 AI 智能方法,并打通两者之间的联系。上一篇我们拿到了两张地图(常用模型地图与算法地图:数模知识全局导航),知道了"遇到哪类问题该用哪类模型"。但地图只解决"用什么",不解决"怎么算出来"------AHP 的权重要算特征向量,微分方程要求数值解,数据要先清洗再建模。这一篇把兵器配齐:六件 Python 兵器各管一段,再加上 LaTeX 把结果排版成论文,让"从题目到论文"真正落地。
文章目录
- 一、为什么需要"工具链"
-
- [1.1 环境安装:装好 Python,一条命令配齐](#1.1 环境安装:装好 Python,一条命令配齐)
- 二、NumPy:数值计算的基座
-
- [2.1 定位:所有工具的地基](#2.1 定位:所有工具的地基)
- [2.2 核心操作速查](#2.2 核心操作速查)
- [2.3 建模实例:AHP 判断矩阵求权重](#2.3 建模实例:AHP 判断矩阵求权重)
- [2.4 广播机制:不同形状也能算](#2.4 广播机制:不同形状也能算)
- [2.5 建模高频场景与常见坑](#2.5 建模高频场景与常见坑)
- 三、Pandas:数据处理的第一站
-
- [3.1 定位:赛题附件的"第一站"](#3.1 定位:赛题附件的"第一站")
- [3.2 核心操作速查](#3.2 核心操作速查)
- [3.3 建模实例:赛题数据预处理](#3.3 建模实例:赛题数据预处理)
- [3.4 常见坑](#3.4 常见坑)
- 四、SciPy:现成的算法库
-
- [4.1 定位:算法地图的"实体版"](#4.1 定位:算法地图的"实体版")
- [4.2 实例一:curve\_fit 拟合 Logistic](#4.2 实例一:curve_fit 拟合 Logistic)
- [4.3 实例二:solve\_ivp 解 SIR------微分方程类落地](#4.3 实例二:solve_ivp 解 SIR——微分方程类落地)
- [4.4 实例三:linprog 解线性规划------优化类落地](#4.4 实例三:linprog 解线性规划——优化类落地)
- 五、Matplotlib:论文级可视化
-
- [5.1 定位:评委第一眼看图](#5.1 定位:评委第一眼看图)
- [5.2 图类型选型表](#5.2 图类型选型表)
- [5.3 论文级图表的四个要素 + 中文显示模板](#5.3 论文级图表的四个要素 + 中文显示模板)
- [5.4 常见坑](#5.4 常见坑)
- 六、scikit-learn:机器学习的统一入口
-
- [6.1 定位:分类聚类的"总仓库"](#6.1 定位:分类聚类的"总仓库")
- [6.2 建模实例一:聚类](#6.2 建模实例一:聚类)
- [6.3 建模实例二:分类](#6.3 建模实例二:分类)
- [6.4 建模实例三:回归(预测类的最小入口)](#6.4 建模实例三:回归(预测类的最小入口))
- [6.5 与传统数模怎么选](#6.5 与传统数模怎么选)
- 七、PyTorch:什么时候才轮到它
-
- [7.1 定位:先简后繁,别为了用而用](#7.1 定位:先简后繁,别为了用而用)
- [7.2 核心概念:每个都对应你已经会的东西](#7.2 核心概念:每个都对应你已经会的东西)
- [7.3 最小示例:亲眼看梯度下降跑起来](#7.3 最小示例:亲眼看梯度下降跑起来)
- [7.4 该不该上 PyTorch:一张判断表](#7.4 该不该上 PyTorch:一张判断表)
- 八、LaTeX:论文排版的最后一公里
-
- [8.1 为什么不用 Word](#8.1 为什么不用 Word)
- [8.2 论文骨架模板](#8.2 论文骨架模板)
- [8.3 数学公式速查](#8.3 数学公式速查)
- [8.4 三线表:论文表格的标准格式](#8.4 三线表:论文表格的标准格式)
- [8.5 插图、交叉引用与参考文献](#8.5 插图、交叉引用与参考文献)
- [8.6 避坑清单与协作方式](#8.6 避坑清单与协作方式)
- 九、全链路速查表:从题目到论文
- 十、学习路径建议
- 十一、小结
- 个人主页 :蒲公英eric
- 专栏传送门 :《数学建模 × AI:从经典模型到智能方法》
- 学习方向:大数据 / 数学建模 / AI交叉领域
- 人生格言:建模没有终点,只有不断迭代的更优解
📌本文是专栏「数学建模 × AI:从经典模型到智能方法」的第 4 篇文章。
一、为什么需要"工具链"
上一篇的模型-算法-工具对照表里,最后一列写的就是 Python 工具------NumPy、Pandas、SciPy、scikit-learn......但只给了名字,没讲怎么用。这一篇就干一件事:把这张表的最后一列展开。
先把分工说清楚。六件兵器各管一段,谁也不抢谁的活:
| 兵器 | 一句话定位 | 管什么 | 不管什么 |
|---|---|---|---|
| NumPy | 数值计算的基座 | 矩阵、向量、各种数学运算 | 读表格、画图 |
| Pandas | 数据处理的第一站 | 读数据、清洗、统计、变形 | 高性能数值计算 |
| SciPy | 现成的算法库 | 拟合、规划、积分、统计检验 | 神经网络 |
| Matplotlib | 可视化 | 论文里的每一张图 | 数据处理 |
| scikit-learn | 机器学习统一入口 | 分类、聚类、回归、降维 | 深度学习 |
| PyTorch | 深度学习框架 | 神经网络、GPU 加速、自动求导 | 传统模型(没必要用它) |
最后还有一件"排版兵器"------LaTeX 。它不属于 Python 工具链,但没有它,前面六件兵器的成果就交不出去:竞赛的最终交付物是一篇 PDF 论文,公式、三线表、插图、参考文献,LaTeX 都是业界标准。
为什么反复强调"落地":上一篇说过,地图的价值是帮你定位,但"路还得自己走"。评委不看你脑子里装了多少模型,只看论文里跑出了什么结果。工具链就是从"我懂这个模型"到"我跑出了结果"的那座桥。
1.1 环境安装:装好 Python,一条命令配齐
先到 Python 官网 下载安装 Python(3.9 及以上版本均可;别装刚发布的最新版 ,用发布一年以上的成熟版本,避免部分科学计算库还没适配)。Windows 安装时务必勾选 "Add Python to PATH" ------不勾的话命令行认不出 python 和 pip,这是新手第一大坑。
装好后打开命令行(Win+R 输入 cmd 回车),一条命令装齐五件兵器:
bash
pip install numpy pandas scipy matplotlib scikit-learn
下载慢的话,在命令末尾加上清华镜像参数 -i https://pypi.tuna.tsinghua.edu.cn/simple 即可。最后补上 PyTorch:
bash
pip install torch # CPU 版,建模够用
# 显卡好的去 PyTorch 官网选好自己的环境,复制官方给出的安装命令
写代码用什么?推荐 VS Code + 官方 Python 插件:打开任意文件夹写 .py 文件,补全、调试、运行一键到位;而且 8.6 节写 LaTeX 还要再用它(装 LaTeX Workshop 插件),一个编辑器管到底 。喜欢网页交互风格的,pip install jupyter 后运行 jupyter notebook 也可以------逐格运行、图表内嵌,探索数据阶段很好用。
进阶方法 :想给不同比赛配不同的库版本(一个装新版、一个装旧版保证复现),用 Python 自带的
venv:python -m venv modeling创建独立环境,Windows 下执行modeling\Scripts\activate激活(Mac/Linux 用source modeling/bin/activate),之后pip装的包只影响这个环境。比赛时团队统一环境,避免"我电脑上能跑"的悲剧。
二、NumPy:数值计算的基座
2.1 定位:所有工具的地基
NumPy 的核心是 ndarray(n 维数组)------一个装数字的多维容器,底层用 C 实现,整块内存连续存储,所以快 。Pandas 建在它上面,scikit-learn 的输入也是它。可以这么记:NumPy 管"数",Pandas 管"表",表也是数构成的。
它的快来自向量化------把"对每个元素做运算"的循环压成一条数学式子,交给底层 C 一次算完:
python
import numpy as np
# 不推荐:Python 循环算一万个点的平方,慢
result = [x ** 2 for x in range(10000)]
# 推荐:向量化,一行搞定,快几十倍
x = np.arange(10000)
result = x ** 2
2.2 核心操作速查
| 操作 | 代码 | 建模用途 |
|---|---|---|
| 创建数组 | np.array([1, 2, 3])、np.zeros(5) |
存放数据、初始化 |
| 索引切片 | a[0]、a[1:3]、a[:, 1] |
取某一列、某一段数据 |
| 矩阵乘法 | A @ B |
AHP 判断矩阵、状态转移 |
| 转置/逆 | A.T、np.linalg.inv(A) |
解线性方程组 |
| 特征值/特征向量 | np.linalg.eig(A) |
AHP 求权重(判断矩阵是正互反矩阵,用通用求解器) |
| 解方程组 | np.linalg.solve(A, b) |
线性模型参数求解 |
| 聚合 | a.sum()、a.mean(axis=0) |
按列求均值做标准化 |
| 随机数 | np.random.rand(1000) |
蒙特卡洛仿真 |
2.3 建模实例:AHP 判断矩阵求权重
上一篇评价类讲过 AHP 的三步:构造判断矩阵 → 一致性检验 → 算权重。其中"算权重"的特征值法,用 NumPy 就是几行:
python
import numpy as np
# 3个准则两两比较的判断矩阵(1-9标度法)
A = np.array([
[1, 3, 5],
[1/3, 1, 3],
[1/5, 1/3, 1]
])
eigval, eigvec = np.linalg.eig(A) # 判断矩阵是正互反矩阵(一般不对称),用通用求解器
k = np.argmax(eigval.real) # 最大实部对应主特征值λmax(Perron-Frobenius定理保证其为唯一正实主特征值)
lam_max = eigval.real[k] # 最大特征值
weights = eigvec[:, k].real
weights = weights / weights.sum() # 归一化后就是权重
print(weights) # 例如 [0.633, 0.260, 0.106]
# 一致性检验
n = A.shape[0]
CI = (lam_max - n) / (n - 1) # 一致性指标
RI = {1: 0, 2: 0, 3: 0.58, 4: 0.90, # 随机一致性指标RI(1-9阶常用值)
5: 1.12, 6: 1.24, 7: 1.32, 8: 1.41, 9: 1.45} # 更高阶需另查表
CR = CI / RI[n]
print(f"CR = {CR:.4f}") # CR < 0.1 才通过
其中,np.linalg.eig 为通用特征值求解器------判断矩阵 A A A 满足 a i j ⋅ a j i = 1 a_{ij} \cdot a_{ji} = 1 aij⋅aji=1(正互反矩阵),一般不对称 ,不能用实对称专用的 eigh(它只读矩阵的一个三角部分,对非对称输入不报错但结果全错);由 Perron-Frobenius 定理,正互反矩阵的最大特征值 λ max \lambda_{\max} λmax 必为正实数且唯一,对应特征向量为正向量,故取 .real 即可, w i = v i ∑ j v j w_i = \frac{v_i}{\sum_j v_j} wi=∑jvjvi 为归一化后的权重向量( v v v 为 λ max \lambda_{\max} λmax 对应的特征向量), C I = λ max − n n − 1 CI = \frac{\lambda_{\max} - n}{n - 1} CI=n−1λmax−n 衡量判断矩阵偏离一致性的程度, C R < 0.1 CR < 0.1 CR<0.1 时认为一致性可以接受。
对照上一篇 :这段代码就是评价类"三件套"里 AHP 的最后两步。判断矩阵哪里来?靠两两比较打分(主观);想要客观权重,就用熵权法------同样只用 NumPy 循环外的一点点代码。后续模型专文会把熵权法、TOPSIS 的代码逐一给全。
2.4 广播机制:不同形状也能算
NumPy 最"魔法"也最容易懵的一点是广播(broadcasting):形状不同的数组运算时,自动把小的"补"成大的再算。
python
# 3个班级、4门课的分数矩阵,想给每门课减去一个基准分
scores = np.array([[80, 85, 90, 78],
[75, 82, 88, 91],
[92, 78, 84, 80]])
base = np.array([5, 3, 2, 4]) # 形状(4,)
adjusted = scores - base # 形状(3,4)减(4,),自动广播
规则一句话:从最后一个维度往前对齐,维度相等或其中一个是 1 就能广播。记住它,后面写标准化、距离计算(每行减去均值再除以标准差)时会反复用到。
2.5 建模高频场景与常见坑
高频场景:
- 2024 国赛 A 题「板凳龙闹元宵」:两百多块板凳沿螺旋线运动,每个时刻都要算所有板凳的位置------向量化一次算完,比逐点循环快几个数量级;
- 2025 国赛 A 题「烟幕干扰弹投放策略」:三维运动学 + 大量蒙特卡洛仿真,全是 NumPy 数组运算;
- 任何优化模型的约束矩阵、评价模型的指标矩阵 ,底层数据都是
ndarray。
常见坑:
| 坑 | 现象 | 正确做法 |
|---|---|---|
| 视图不是拷贝 | b = a[1:3] 改 b 连 a 也变了 |
需要独立副本时用 a[1:3].copy() |
| 整数除法精度 | np.array([1, 2]) / 2 结果是 float64 没问题,但 np.int32 溢出会静默环绕 |
大数运算前转 dtype=np.float64 |
| 行向量/列向量 | (3,) 既不是行也不是列 |
明确需要行列时用 reshape(-1, 1) 或 reshape(1, -1) |
eigh 误用于非对称矩阵 |
AHP 判断矩阵喂给 eigh 不报错,但权重结果全错(它只读一个三角部分) |
判断矩阵是正互反 而非对称,特征值问题必须用 eig |
三、Pandas:数据处理的第一站
3.1 定位:赛题附件的"第一站"
比赛发的附件十有八九是 Excel/CSV 表格------销售记录、监测数据、指标统计。Pandas 就是为表格而生的 :它的 DataFrame 是一个带行标签、列标签的二维表,读进来、洗干净、统计出规律,全靠它。
近年趋势 :数据分析型题目越来越多。2023 国赛 C 题「蔬菜类商品的自动定价与补货决策」给了三年多的批发/零售流水,第一步必须把原始流水按品类、按时间聚合出销量和定价------这道"开胃菜"完全靠 Pandas。2018 国赛 C 题的零售数据同理。数据洗不干净,后面模型再漂亮都是空中楼阁。
3.2 核心操作速查
| 操作 | 代码 | 说明 |
|---|---|---|
| 读取 | pd.read_csv('a.csv') / pd.read_excel('a.xlsx') |
中文乱码就加 encoding='gbk' |
| 概览 | df.head()、df.info()、df.describe() |
拿到数据先跑这三句 |
| 选列/筛行 | df['销量']、df[df['销量'] > 100] |
布尔筛选最高频 |
| 缺失值 | df.isna().sum()、df.dropna()、df.fillna(0) |
先统计再决定删还是补 |
| 分组聚合 | df.groupby('品类')['销量'].sum() |
流水变汇总的核心操作 |
| 透视表 | df.pivot_table(index=..., columns=..., values=...) |
行列两个维度的交叉统计 |
| 合并 | pd.merge(df1, df2, on='ID') |
多表按主键拼接 |
| 时间处理 | pd.to_datetime(df['日期']) |
按月/季重采样必用 |
3.3 建模实例:赛题数据预处理
模拟一下 2023 国赛 C 题那类流水数据的标准预处理流程:
python
import pandas as pd
# 1. 读入流水数据(中文乱码常见,两种编码都试)
df = pd.read_csv('sales.csv', encoding='utf-8') # 不行就换 'gbk'
# 2. 概览:多少行、哪些列有缺失、类型对不对
print(df.info())
# 3. 清洗:日期转标准格式,关键列缺失的行丢弃,退货记录剔除
df['销售日期'] = pd.to_datetime(df['销售日期'])
df = df.dropna(subset=['销量', '销售单价'])
df = df[df['销量'] > 0]
# 4. 聚合:按品类按月汇总销量,得到定价模型要用的输入
df['月份'] = df['销售日期'].dt.to_period('M')
monthly = df.groupby(['品类', '月份']).agg(
总销量=('销量', 'sum'),
均价=('销售单价', 'mean')
).reset_index()
# 5. 透视:换个视角看品类×月份的销量矩阵
pivot = df.pivot_table(index='品类', columns='月份',
values='销量', aggfunc='sum')
注意 :
groupby+agg这套组合拳是数据分析题的"标准起手式"。写论文时,这一节对应论文的"数据处理"部分------把缺失值多少、剔除了什么、按什么口径聚合,写清楚,评委才认为你的结果可信。
3.4 常见坑
| 坑 | 现象 | 正确做法 |
|---|---|---|
| 链式赋值警告 | df[df['a']>1]['b'] = 0 不生效 |
用 df.loc[df['a']>1, 'b'] = 0 |
| 编码 | read_csv 报 UnicodeDecodeError |
依次试 utf-8、gbk、gb18030 |
| 日期被读成字符串 | 按月聚合时直接报错 | 读入后立刻 pd.to_datetime |
| 隐式索引混乱 | 删过行后 df.loc[0] 报错 |
loc 按标签、iloc 按位置,删行后想按位置取用 iloc |
四、SciPy:现成的算法库
4.1 定位:算法地图的"实体版"
上一篇的算法地图里,梯度类、精确算法、采样类那么多算法,大部分不用自己写 ------SciPy 已经实现了。它的定位一句话:NumPy 管"存数算数",SciPy 管"算法"------拟合、规划、积分、统计检验,全是现成的。
| 子模块 | 管什么 | 建模高频函数 |
|---|---|---|
scipy.optimize |
拟合与优化 | curve_fit(曲线拟合)、linprog(线性规划)、minimize(非线性) |
scipy.integrate |
积分与微分方程 | solve_ivp(常微分方程组数值解) |
scipy.stats |
统计分布与检验 | 各种分布的概率计算、假设检验、相关系数 |
scipy.interpolate |
插值 | interp1d(缺失数据补全、曲线光滑) |
4.2 实例一:curve_fit 拟合 Logistic
本专栏的下一篇将用人口增长问题演示了完整建模闭环,其中"模型求解"用的正是 curve_fit:给出模型形式,让 SciPy 自动找出最贴合真实数据的参数。
python
import numpy as np
from scipy.optimize import curve_fit
# Logistic模型:K承载量,r增长率,t0拐点时间
def logistic(t, K, r, t0):
return K / (1 + np.exp(-r * (t - t0)))
# years、population 是真实人口数据(第0篇用美国1790-2020年普查数据)
popt, pcov = curve_fit(logistic, years, population, p0=[400, 0.03, 1900])
K, r, t0 = popt # 拟合出的三个参数
其中,logistic 为 Logistic 模型的解析形式,p0 为参数初值(给个量级正确的初值能显著提升收敛速度),popt 为拟合出的最优参数数组,pcov 为参数的协方差矩阵(对角线开方即参数的标准误差,论文里写参数估计的置信区间要用它)。
原理一句话 :
curve_fit的本质就是最小二乘 ------找一组参数让"模型预测值与真实值之差的平方和"最小,底层调用的是 MINPACK 的梯度类算法。第 0 篇说过"最小二乘=损失函数",机器学习训练神经网络玩的也是同一个游戏------这条线后续 AI 文章会正式接上。
4.3 实例二:solve_ivp 解 SIR------微分方程类落地
上一篇仿真/微分方程类讲过 SIR 传染病模型。微分方程 d S d t = − β S I , d I d t = β S I − γ I , d R d t = γ I \frac{dS}{dt} = -\beta S I,\quad \frac{dI}{dt} = \beta S I - \gamma I,\quad \frac{dR}{dt} = \gamma I dtdS=−βSI,dtdI=βSI−γI,dtdR=γI 一般没有解析解,数值解一行搞定:
python
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import solve_ivp
def sir(t, y, beta, gamma):
S, I, R = y
return [-beta*S*I, beta*S*I - gamma*I, gamma*I]
sol = solve_ivp(sir, [0, 100], [0.99, 0.01, 0.0], # 时间范围与初值
args=(0.3, 0.1), # beta=0.3, gamma=0.1
t_eval=np.linspace(0, 100, 1000))
plt.plot(sol.t, sol.y[0], label='S 易感者')
plt.plot(sol.t, sol.y[1], label='I 感染者')
plt.plot(sol.t, sol.y[2], label='R 康复者')
plt.legend()
其中, S S S、 I I I、 R R R 分别为易感者、感染者、康复者占总人口的比例, β \beta β 为感染率系数, γ \gamma γ 为康复率系数,solve_ivp 默认采用自适应步长的 RK45 方法(Runge-Kutta 家族,上一篇算法地图里提到的 RK4 的自适应升级版),t_eval 指定输出解的时间点。
对照上一篇 :微分方程类选型口诀说"单变量、连续时间 → ODE(用 odeint 求解)",
solve_ivp是它的新一代替代品,接口更统一。注意 SIR 的假设边界(均匀混合、无潜伏期)------上一篇限制条件速查表里写明了,实际疫情要用 SEIR 放宽。
4.4 实例三:linprog 解线性规划------优化类落地
优化类是数模第一大类。标准形式 min c T x , s.t. A u b x ≤ b u b , x ≥ 0 \min c^T x,\quad \text{s.t. } A_{ub}x \le b_{ub},\quad x \ge 0 mincTx,s.t. Aubx≤bub,x≥0 用 linprog 直接解:
python
from scipy.optimize import linprog
# 例:max 10x + 8y,s.t. x+y≤40, x+2y≤60, x,y≥0
c = [-10, -8] # linprog只做min,目标取负号
A = [[1, 1], [1, 2]]
b = [40, 60]
res = linprog(c, A_ub=A, b_ub=b, bounds=[(0, None), (0, None)])
if not res.success: # 先判成功再取解,否则拿到的是无效结果
raise ValueError(res.message) # 无可行解/目标无界时,message会说明原因
print(res.x, -res.fun) # 最优解 [20, 20],最优值 360
其中, c c c 为目标函数系数向量(求最大值时取负、结果再取负), A u b , b u b A_{ub}, b_{ub} Aub,bub 为不等式约束的系数矩阵与右端项,bounds 为变量的取值范围,res.success 为求解状态标志(务必先判成功再取解 ),res.x 为最优解向量,res.fun 为最小化的目标值。
什么时候升级 :变量是整数(0-1 决策、排班、选址)时,
linprog不行,用PuLP(上一篇优化类表里给过的工具,语法更接近建模语言)。变量上千且非线性,转启发式------还是上一篇地图的路标。工具是按地图层级升级的,不是瞎选的。
五、Matplotlib:论文级可视化
5.1 定位:评委第一眼看图
论文评阅时间极短,图表是评委最先看的部分------一张信息清晰、坐标规范、有图例的图,胜过三段文字。Matplotlib 是 Python 画图的底座,其他可视化库(seaborn 等)都是它的封装。
5.2 图类型选型表
| 想表达什么 | 用什么图 | 建模场景 |
|---|---|---|
| 趋势随时间变化 | 折线图 plt.plot |
人口预测曲线、销量走势 |
| 两组量的相关关系 | 散点图 plt.scatter |
价格与销量的弹性关系 |
| 多类别数量对比 | 柱状图 plt.bar |
各品类销量对比 |
| 矩阵/相关系数 | 热力图 plt.imshow |
指标相关性矩阵 |
| 三维曲面 | Axes3D.plot_surface |
优化目标函数地形、二维概率密度 |
| 分布形态 | 直方图 plt.hist |
蒙特卡洛仿真结果的分布 |
5.3 论文级图表的四个要素 + 中文显示模板
一张合格的论文图要有:坐标轴含义与单位、图例、足够的分辨率、可复现的绘图代码。中文显示和负号显示的坑,用下面这个模板一次解决:
python
import matplotlib.pyplot as plt
import numpy as np
plt.rcParams['font.sans-serif'] = ['SimHei'] # 中文显示(Windows)
plt.rcParams['axes.unicode_minus'] = False # 负号正常显示
t = np.linspace(0, 100, 500)
plt.figure(figsize=(8, 5), dpi=120) # 尺寸与分辨率
plt.plot(t, 400/(1+np.exp(-0.05*(t-40))), label='r=0.05')
plt.plot(t, 400/(1+np.exp(-0.10*(t-40))), label='r=0.10')
plt.xlabel('时间 t / 年') # 轴含义+单位
plt.ylabel('人口 P(t)')
plt.title('不同增长率下的 Logistic 曲线')
plt.legend() # 图例
plt.grid(alpha=0.3)
plt.savefig('logistic.png', dpi=300, bbox_inches='tight') # 论文用300dpi
其中,figsize 与 dpi 共同决定图片分辨率(论文插图建议 dpi=300 以上),rcParams 两行设置解决中文与负号的乱码问题,bbox_inches='tight' 让保存的图片裁掉周围空白。
进阶方法 :
plt.subplot(2, 2, i)可在一张图里排多幅子图(敏感性分析时一图四变特别好用);seaborn库一行sns.set_theme()就能把默认样式美化到论文水准。图的风格全文要统一------所有图的字体、线宽、配色保持一致,这是很多队伍忽略的细节分。
5.4 常见坑
| 坑 | 现象 | 正确做法 |
|---|---|---|
| 中文变方框 | 图里中文全显示成 □ | 设置 font.sans-serif(Mac 用 Arial Unicode MS) |
| 负号变乱码 | 坐标轴负号显示异常 | axes.unicode_minus = False |
| 图太糊 | 插到 PDF 里模糊 | dpi=300 且用 savefig 而不是截图 |
| 线堆在一起认不出 | 多条曲线难区分 | 加 label + legend(),或用不同 linestyle |
六、scikit-learn:机器学习的统一入口
6.1 定位:分类聚类的"总仓库"
上一篇分类聚类类的模型------逻辑回归、KMeans、判别分析、DBSCAN------全部在 scikit-learn 里,而且 API 完全统一 :所有模型都是三步,fit(学习)→ predict(预测)→ score(评估)。学会一个,就学会了全部,这是它作为"统一入口"的含义。
| 模块 | 管什么 | 建模场景 |
|---|---|---|
cluster |
聚类 | KMeans、层次聚类、DBSCAN |
linear_model |
线性/逻辑回归 | 回归预测、二分类 |
tree / ensemble |
树与集成 | 决策树、随机森林(要特征重要性时用) |
preprocessing |
预处理 | 标准化、归一化、编码 |
model_selection |
模型评估 | 训练测试集划分、交叉验证 |
decomposition |
降维 | PCA 主成分分析 |
6.2 建模实例一:聚类
python
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
# df 为第三章预处理好的数据表;X 为特征矩阵(每行一个样本)
X = df[['指标1', '指标2', '指标3']].values
X_std = StandardScaler().fit_transform(X) # 标准化,消除量纲
km = KMeans(n_clusters=3, n_init=50, random_state=42)
labels = km.fit_predict(X_std) # 一行得到聚类结果
df['类别'] = labels
其中,StandardScaler 把每列变为均值 0、标准差 1(不做的话量纲大的指标会主导距离计算,聚类结果失真),n_clusters 为簇数(结合肘部法/轮廓系数确定,不是拍脑袋),n_init 为换不同初始中心重复运行的次数(KMeans 对初始点敏感,上一篇限制条件表里写过),random_state 固定随机种子保证结果可复现。
6.3 建模实例二:分类
python
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score
# X_std 承接 6.2;y 为标签列------每个样本的真实类别(如玻璃的"高钾/铅钡"类型)
y = df['类型'].values
X_train, X_test, y_train, y_test = train_test_split(
X_std, y, test_size=0.3, random_state=42, stratify=y)
clf = LogisticRegression(max_iter=1000).fit(X_train, y_train)
y_pred = clf.predict(X_test)
print(accuracy_score(y_test, y_pred)) # 测试集准确率
其中,test_size=0.3 表示 30% 数据留作测试、70% 用于训练,stratify=y 保证划分后两集合中各类别比例一致(类别不平衡时必须加),fit 在训练集上学习参数,accuracy_score 在测试集上评估------在测试集上评估而不是训练集,是机器学习的第一纪律。
**2022 国赛 C 题「古代玻璃制品的成分分析与鉴别」**就是典型的分类题:根据成分数据判断玻璃类型,用逻辑回归/决策树 + 交叉验证是标准打法。分类聚类在传统数模里偏配角,在 AI 路线上是主角------本专栏 AI 子专栏的 SVM、随机森林、神经网络全部基于这套 API 展开,现在学的就是以后的地基。
6.4 建模实例三:回归(预测类的最小入口)
预测类题目最常用的线性回归,同样是三步:
python
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
# 回归的目标是连续量(销量、价格等),与分类的类别标签是两回事,故记作 y_reg
y_reg = df['销量'].values
X_train, X_test, y_train, y_test = train_test_split(
X_std, y_reg, test_size=0.3, random_state=42)
reg = LinearRegression().fit(X_train, y_train) # 在训练集上拟合
print(reg.coef_, reg.intercept_) # 回归系数与截距,可直接写进论文
print(reg.score(X_test, y_test)) # 测试集 R²
其中,y_reg 为从数据表中取出的目标列------回归要预测的连续量(销量、价格等),注意它与分类任务里的类别标签 y y y 不是一回事 ;fit 用最小二乘法估计系数(与 4.2 节 curve_fit 的底层思想一致),reg.coef_ 为各自变量的回归系数(含义是"其他变量不变时,该指标每增加一个单位,因变量的平均变化量",可解释性好),reg.score 返回决定系数 R 2 R^2 R2。当特征间多重共线、系数估计不稳时,换成 Ridge / Lasso,接口一字不改------这正是统一 API 的好处。
6.5 与传统数模怎么选
一句话判断:有标签走分类,没标签走聚类;数据少、要解释,传统模型优先;数据多、要精度,机器学习上。 这正是上一篇"模型选择三原则"(先简后繁、数据说话、可解释优先)在工具层的体现。
七、PyTorch:什么时候才轮到它
7.1 定位:先简后繁,别为了用而用
上一篇模型选择原则一就是"先简后繁"------大部分数模题用不到 PyTorch。数据量小(几百行)、要求可解释、传统模型效果够用时,上神经网络属于"为了炫技而复杂",评委不买账。
真正需要 PyTorch 的三种情况:
- 数据量大的深度预测:长时序、高维特征,传统模型精度明显不够(LSTM、Transformer 预测);
- 非结构化数据:图像、文本类题目,卷积/预训练模型是标配;
- 研究 AI 本身:本专栏"用数模研究 AI"那条线,必须能操作模型的内部梯度。
7.2 核心概念:每个都对应你已经会的东西
PyTorch 没有想象中神秘,四个核心概念都能在前面的内容里找到影子:
| PyTorch 概念 | 相当于 | 说明 |
|---|---|---|
| 张量 Tensor | NumPy 的 ndarray |
多维数组,还能搬上 GPU |
| 自动求导 autograd | 梯度类算法的"发动机" | 自动算出损失函数对每个参数的梯度 |
nn.Module |
模型地图里的"模型" | 把网络结构封装成类 |
| 优化器 Optimizer | 梯度下降的"步长控制" | SGD、Adam 等更新参数的策略 |
其中,张量(Tensor)与 ndarray 的区别在于两点:可放入 GPU 并行计算、可自动求导;自动求导机制依据链式法则逐层反向计算梯度(即"反向传播"),这正是上一篇算法地图里梯度类算法的底层实现。
7.3 最小示例:亲眼看梯度下降跑起来
不用任何神经网络模块,只用自动求导拟合一元线性回归 y = w x + b y = wx + b y=wx+b,把上一篇 3.2 节"沿负梯度方向迭代"的文字变成看得见的代码:
python
import torch
x = torch.tensor([1., 2., 3., 4.])
y = 2 * x + 1 # 真实规律
w = torch.tensor([0.], requires_grad=True)
b = torch.tensor([0.], requires_grad=True)
for step in range(200):
pred = w * x + b
loss = ((pred - y) ** 2).mean() # 均方误差=损失函数
loss.backward() # 自动求 dL/dw, dL/db
with torch.no_grad():
w -= 0.01 * w.grad # 沿负梯度走一步
b -= 0.01 * b.grad
w.grad.zero_() # 梯度清零,准备下一步
b.grad.zero_()
print(w.item(), b.item()) # 输出约 2.0 和 1.0
其中, L = 1 n ∑ i = 1 n ( w x i + b − y i ) 2 L = \frac{1}{n}\sum_{i=1}^{n}(wx_i + b - y_i)^2 L=n1∑i=1n(wxi+b−yi)2 为均方误差损失函数, w w w、 b b b 为待学习参数(requires_grad=True 表示要对其求梯度),backward() 调用自动求导机制计算 ∂ L ∂ w \frac{\partial L}{\partial w} ∂w∂L 与 ∂ L ∂ b \frac{\partial L}{\partial b} ∂b∂L;with torch.no_grad() 让参数更新这一步脱离自动求导追踪(否则更新操作本身会被记入计算图,下一轮 backward() 会报错);学习率 0.01 控制每步沿负梯度方向移动的距离。
AI 联系 :这十几行代码就是所有深度学习训练的骨架 ------把
w*x+b换成亿级参数的网络、把数据换大、把优化器换成 Adam,本质流程一字不改。第 0 篇说"最小二乘=损失函数",上一篇说"梯度下降是神经网络训练的核心",到这里两条伏笔正式会师。AI 子专栏讲神经网络时,就从这个例子出发往上盖楼。
7.4 该不该上 PyTorch:一张判断表
| 情况 | 决策 |
|---|---|
| 数据几百行、关系简单 | 线性回归/逻辑回归(sklearn),不上深度学习 |
| 要向评委解释每个参数的含义 | 树模型 + 特征重要性,不上深度学习 |
| 时序长、维度高、传统方法精度瓶颈 | LSTM/Transformer(PyTorch) |
| 图像/文本/语音数据 | 卷积网络/预训练模型(PyTorch) |
| 题目研究 AI 模型自身(对抗攻击、鲁棒性) | 必须掌握自动求导(PyTorch) |
量化参考:经验上,样本量在千行以内、特征只有几十个、且存在明确机理模型时,一般用不上深度学习;数据量到万级以上、或传统方法精度明显见顶时,再考虑 PyTorch。这个参考不是硬边界,但足以帮你挡掉大多数"为了用而用"。
规范提醒 :近年大赛对 AI 工具的使用已有官方规范(上一篇趋势七结合 2026 国赛 B 题详细讲过)。把工具链练熟,最大的底气是"自己能实现"------无论规范怎么变,能自己写代码复现模型的人永远不慌。
八、LaTeX:论文排版的最后一公里
8.1 为什么不用 Word
建模论文公式密集、图表众多、三人协作、频频改版。Word 在交叉引用、公式编号、参考文献、多人协作上全都吃力,LaTeX 全都省心。一句话:公式和交叉引用一多,LaTeX 的效率就是碾压性的。国内外竞赛官方都提供 LaTeX 模板,评委看到的漂亮论文大多是它排的。
8.2 论文骨架模板
下面是一个通用的论文骨架(国赛/美赛结构大同小异),保存为 main.tex 直接可编译:
latex
\documentclass[12pt]{article}
\usepackage{ctex} % 中文支持(需用 XeLaTeX 编译)
\usepackage{amsmath,amssymb} % 公式
\usepackage{booktabs} % 三线表
\usepackage{graphicx} % 插图
\usepackage{float} % 提供 [H] 图片强制定位
\usepackage{geometry}
\geometry{a4paper, margin=2.5cm}
\title{基于Logistic增长模型的XX问题研究}
\author{}
\date{}
\begin{document}
\maketitle
\begin{abstract}
针对问题一,本文建立了......模型,求解得到......。针对问题二......\\
\textbf{关键词}:Logistic模型;最小二乘;蒙特卡洛仿真
\end{abstract}
\section{问题重述}
\section{模型假设}
\begin{itemize}
\item 假设人口增长满足Logistic方程刻画的光滑性要求;
\item 假设研究期内环境承载量 $K$ 保持不变。
\end{itemize}
\section{符号说明}
\section{模型的建立与求解}
\section{模型的评价与推广}
\end{document}
其中,documentclass 声明文档类型与正文字号,ctex 宏包提供中文支持(必须用 XeLaTeX 编译器),abstract 环境是摘要------数模论文的摘要决定第一印象,评委初选基本只看摘要 ,section 自动生成章节编号,不用手打。
现成模板 :Overleaf 上搜索国赛模板
cumcmthesis或美赛模板mcmthesis,格式(页眉、承诺书、编号规则)都已按官方要求调好,比赛时直接在模板里填内容,不要比赛当天才第一次打开模板。
8.3 数学公式速查
| 想写什么 | LaTeX 代码 | 效果 |
|---|---|---|
| 行内公式 | $r > 0$ |
r > 0 r > 0 r>0 |
| 行间公式(带编号) | \begin{equation} ... \end{equation} |
居中单独成行 |
| 分数 | \frac{dP}{dt} |
d P d t \frac{dP}{dt} dtdP |
| 求和 | \sum_{i=1}^{n} x_i |
∑ i = 1 n x i \sum_{i=1}^{n} x_i ∑i=1nxi |
| 积分 | \int_0^T f(t)\,dt |
∫ 0 T f ( t ) d t \int_0^T f(t)\,dt ∫0Tf(t)dt |
| 矩阵 | bmatrix 环境 |
方括号矩阵 |
| 多行推导对齐 | align 环境 + & 对齐 |
逐步推导不跳步 |
| 条件定义 | cases 环境 |
分段函数 |
建模里最常用的多行推导长这样("推导不跳步"的排版载体):
latex
\begin{align}
\frac{dP}{dt} &= rP\left(1 - \frac{P}{K}\right) \\
\frac{d^2P}{dt^2} &= r\left(1 - \frac{2P}{K}\right)\frac{dP}{dt}
\end{align}
其中,$P(t)$ 为第 $t$ 年的人口数量,$r$ 为固有增长率,$K$ 为环境最大承载量;
第二个式子对增长速度 $\frac{dP}{dt}$ 再求导,用于判断拐点位置。
写作约定 :本专栏的每条公式后面都会跟一句"其中,......"解释全部新符号------写论文时请保持这个习惯,评委没有义务猜你符号的含义。
8.4 三线表:论文表格的标准格式
论文里的表格用三线表 (只有顶线、栏目线、底线三条横线,无竖线),booktabs 宏包三行搞定:
latex
\begin{table}[H]
\centering
\caption{符号说明}
\begin{tabular}{cl}
\toprule
符号 & 含义 \\
\midrule
$P(t)$ & 第 $t$ 年的人口数量 \\
$r$ & 人口固有增长率 \\
$K$ & 环境最大承载量 \\
\bottomrule
\end{tabular}
\end{table}
其中,table 环境是浮动表格容器,[H](需 float 宏包)强制表格出现在代码书写位置而不是"漂"到别处,\caption 生成表题,\toprule / \midrule / \bottomrule 分别画顶线、栏目线、底线,{cl} 表示第一列居中、第二列左对齐。
写作约定 :本专栏所有表格都用三线表格式、表下配说明文字,论文同理------每个表格下面必须有一段"表 X 展示了......"的文字解读,绝不让表格裸奔。
8.5 插图、交叉引用与参考文献
插图 (图片文件放同目录,或用 \graphicspath{``{images/}} 统一指定路径):
latex
\begin{figure}[H]
\centering
\includegraphics[width=0.8\textwidth]{logistic.png}
\caption{不同增长率下的 Logistic 曲线}
\end{figure}
其中,[H] 强制图在原位,width=0.8\textwidth 控制图宽为版心的 80%(用相对宽度,避免图片溢出页面),\caption 放在 \includegraphics 之后即生成图下方的图注(表注在表上方、图注在图下方,这是通用排版规范)。
交叉引用 :给公式、图、表打 \label{eq:logistic}、\label{fig:curve},正文用 \eqref{eq:logistic}、\ref{fig:curve} 引用。改版重排时编号自动更新------这是 LaTeX 对 Word 的最大优势,也是多人协作时"图 3 到底是哪张"的终结者。
参考文献:
latex
\begin{thebibliography}{9}
\bibitem{jiang2021} 司守奎, 孙兆亮. 数学建模算法与应用[M]. 第3版. 国防工业出版社, 2021.
\end{thebibliography}
正文里 \cite{jiang2021} 即生成上标引用,编号自动管理。
8.6 避坑清单与协作方式
| 坑 | 现象/正确做法 |
|---|---|
| 中文编译报错 | 必须用 XeLaTeX 编译器(配合 ctex 宏包),不是 pdfLaTeX |
| 图片不浮动 H 报错 | 忘了 \usepackage{float} |
% $ & # _ { } 消失或报错 |
这些是特殊字符,需加反斜杠转义,尤其是百分号 |
| 图片找不到 | 图片与 .tex 同目录,或用 \graphicspath 指定 |
| 编译总不过 | 先注释掉一半内容二分定位错误;看报错行号往上翻几行找真凶 |
编辑器怎么选 :本地两件套------TeXStudio(开箱即用,适合第一次接触)或 VS Code + LaTeX Workshop 插件(适合已在用 VS Code 的人);在线就是 Overleaf。取舍很简单:本地编译快、断网能写,但环境配置费劲;在线零配置、协作强,但依赖网络。比赛三人协作建议直接用 Overleaf,平时练习本地和在线各跑一遍找手感。
协作神器 :Overleaf 在线 LaTeX,三人同时编辑、实时预览、自动云同步,还内置了各大竞赛模板。比赛期间把项目链接分享给队友即可,彻底告别"论文最新版到底在谁电脑上"。
九、全链路速查表:从题目到论文
六件兵器 + LaTeX 各就各位后,整条流水线是这样的:
Excel/CSV 附件数据
│
▼
┌─────────────┐ 清洗、筛选、聚合、透视
│ Pandas │ ────────────────────────────► 规整的数据表
└─────────────┘ │
▼
┌─────────────────────────┐
│ NumPy │
│ 矩阵运算 / 向量化计算 │
└─────────────────────────┘
│
┌─────────────────────────┬───────────────┼───────────────┐
▼ ▼ ▼ ▼
┌────────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐
│ SciPy │ │ sklearn │ │ PyTorch │ │ PuLP │
│ 拟合/规划/ │ │ 分类/聚类/ │ │ 深度学习 │ │ 整数规划 │
│ 微分方程 │ │ 回归/降维 │ │ (必要时) │ │ (必要时) │
└────────────┘ └────────────┘ └────────────┘ └────────────┘
│ │ │ │
└─────────────────────────┴───────────────┼───────────────┘
▼
┌─────────────────────────┐
│ Matplotlib │
│ 结果可视化(300dpi) │
└─────────────────────────┘
│
▼
┌─────────────────────────┐
│ LaTeX │
│ 公式/三线表/引用成文 │
└─────────────────────────┘
把上一篇的"问题类型"和这一篇的"兵器入口"合到一张表,就是你要贴在工位上的那张纸:
| 问题类型(上一篇) | 兵器 | 核心入口 | 记忆锚点 |
|---|---|---|---|
| 评价 | NumPy + Pandas | np.linalg.eig(AHP)、归一化(熵权/TOPSIS) |
特征向量=权重 |
| 预测 | SciPy(+ statsmodels) | curve_fit、statsmodels.tsa(ARIMA) |
最小二乘=损失函数 |
| 优化 | SciPy / PuLP | linprog、minimize、整数规划用 PuLP |
max 先取负号 |
| 分类聚类 | scikit-learn | fit / predict / score |
统一三步走 |
| 仿真 | NumPy + SciPy | np.random 蒙特卡洛、solve_ivp |
大数定律兜底 |
| 图论 | networkx(补充兵器) | nx.shortest_path 等 |
一行装,按需查文档 |
| 微分方程 | SciPy | solve_ivp |
RK 自适应步长 |
| 任何题 | Matplotlib + LaTeX | savefig(dpi=300)、\begin{figure}[H] |
图表=论文的脸面 |
networkx 补充一句 :图论题(最短路、网络流、PageRank)用它,也是一行
pip install networkx。上一篇对照表里出现过,这里归队------它算"第七件小兵器",按需取用即可。
环境固化(收个尾) :1.1 节说过"团队统一环境",这里补上落地动作------比赛第一天就导出环境清单:pip freeze > requirements.txt。队友拿到清单后pip install -r requirements.txt一键复现。统一靠清单,不靠口头。
十、学习路径建议
兵器配齐不等于都要练到精通。按优先级分三阶段:
第一阶段(数据基本功,赛前必须形成肌肉记忆):
- Pandas:读入 →
info/describe→ 清洗 →groupby聚合,整条流程盲打; - NumPy:数组运算、广播、
linalg常用函数; - Matplotlib:折线/散点/柱状 + 中文显示模板 + 300dpi 保存。
练法:随便找一篇历年赛题的附件数据,做一次完整的探索性分析(EDA),产出 3 张论文级图表。
第二阶段(模型落地) :
-
SciPy 三大件:
curve_fit、solve_ivp、linprog; -
scikit-learn:标准化 + KMeans + 逻辑回归 + 训练测试集划分。
练法 :把上一篇学习路径第一阶段的模型(AHP 三件套、线性规划、回归、蒙特卡洛)每个都跑通一个最小例子------地图上的每个格子都要用代码踩一遍。
第三阶段(论文与进阶) :
-
LaTeX:在 Overleaf 上用竞赛模板把一篇做过的题写成完整论文(摘要 → 三线表 → 公式推导 → 插图 → 参考文献);
-
PyTorch:跑通 7.3 节的梯度下降例子,理解自动求导;等 AI 子专栏讲神经网络时再深入。
记住 :工具是给模型打工的。先用第一阶段把数据处理练熟,再按需取用第二阶段;LaTeX 别拖到比赛前才学,第一次用模板排完一篇论文,你就知道流程图里"问题重述到参考文献"每一步长什么样了。
十一、小结
到这里,专栏的导读地图集齐了四块拼图:
- 第 1 篇 给了流程:七步建模闭环,问题重述到论文成稿;
- 第 2 篇给了方向:七个出题趋势,知道该往哪使劲;
- 第 3 篇给了地图:七类问题的模型工具箱、六类求解算法,以及模型-算法-工具对照表;
- 本篇配齐了兵器:NumPy 管数、Pandas 管表、SciPy 管算法、Matplotlib 管图、scikit-learn 管学习、PyTorch 管深度学习,LaTeX 把这一切排版成论文。
其实下一篇的人口增长问题,就是这套工具链的一次预演------curve_fit 做最小二乘拟合、Matplotlib 画"解析解、数值解与真实数据三线重合"的验证图,只不过当时你看到的是故事,现在你看懂的是每一行代码背后"哪个兵器在出力"。
下一篇是专栏前面内容的收束篇 :用一个完整的小例子,把"流程 → 趋势 → 地图 → 兵器"四篇串成一条线------从拿到题目那一刻的定位,到选模型、写代码、画图,最后落成论文。导读篇到此收口,实战篇正式开始。
📌 本文是专栏「数学建模 × AI:从经典模型到智能方法」的第 4 篇文章。
- 上一篇回顾 :常用模型与算法:数学建模知识全局导航
- 下一篇预告 :下一篇用一个完整例子串起"流程、趋势、地图、兵器",导读地图收口,实战开始。
👉 点击订阅专栏,第一时间收到更新通知!
如果你在阅读过程中有任何疑问,欢迎在评论区留言交流。😊