AI工程师的数学速成:线代·概率·信息论必备知识精讲
AI工程师的数学速成:线代·概率·信息论必备知识精讲
适读人群:数学底子一般、想搞懂AI原理但怕数学的Java工程师 阅读时长:约25分钟
那次让我当场自闭的组会
去年底,我参加了一次公司的AI技术分享会。演讲者是隔壁算法组的博士,PPT上写满了公式:Softmax、KL散度、梯度下降的矩阵形式……
我旁边坐着一个前端同事,他低声问我:"这些公式是啥意思?"
我支吾了半天,最后只能说:"大概是……让模型变聪明的公式吧。"
他点了点头,显然没被说服。我自己也没被说服。
那个场景让我当场自闭了。我写了10年Java,天天用LLM的API,但对背后的数学几乎一无所知。我不知道Embedding为什么是高维向量,不知道为什么相似度要用余弦不用欧氏距离,也不知道为什么Transformer要用Softmax而不是其他函数。
这种感觉就像——我天天开车,却完全不知道引擎是怎么工作的。大多数时候没问题,但一旦出了故障,我只能等人来修。
所以我花了三个月,专门补了AI工程师需要的数学基础。不是考研级别的全面学习,而是只学"看懂AI代码和论文"所需要的最小知识集合。
今天把精华浓缩给你,目标是:读完这篇文章,你能看懂80%的AI技术博客。
第一部分:线性代数——AI世界的语言
向量:一切的基础
AI里所有的"理解"都被表示成向量。一个词、一张图、一句话,最终都变成一串数字。
"猫" → [0.2, -0.5, 0.8, 0.1, ...] (比如384维)
"狗" → [0.3, -0.4, 0.7, 0.2, ...]
"汽车" → [-0.5, 0.9, -0.2, 0.6, ...]为什么用向量? 因为向量之间可以计算相似度,而相似的词,向量也应该相近。
/**
* 向量操作工具类
* 理解这些操作,就理解了Embedding的核心
*/
public class VectorMath {
/**
* 余弦相似度:计算两个向量方向的相似程度
* 范围:[-1, 1],1表示完全相同方向,0表示垂直,-1表示相反
*
* 用余弦而不用欧氏距离的原因:
* 余弦只关心方向,不关心长度(模长)
* 这样"猫"和"一只小猫"的向量长度可能不同,但方向相近,相似度高
*/
public static double cosineSimilarity(float[] v1, float[] v2) {
if (v1.length != v2.length) throw new IllegalArgumentException("维度不匹配");
double dotProduct = 0; // 点积
double norm1 = 0; // v1的模长
double norm2 = 0; // v2的模长
for (int i = 0; i < v1.length; i++) {
dotProduct += v1[i] * v2[i];
norm1 += v1[i] * v1[i];
norm2 += v2[i] * v2[i];
}
// 防止除零
if (norm1 == 0 || norm2 == 0) return 0;
return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2));
}
/**
* 点积(内积)
* Attention机制中 Q·K 用的就是点积
* 点积越大,表示两个向量越"对齐"
*/
public static double dotProduct(float[] v1, float[] v2) {
double sum = 0;
for (int i = 0; i < v1.length; i++) {
sum += v1[i] * v2[i];
}
return sum;
}
/**
* 向量加法:可以做"语义运算"
* 经典例子:国王 - 男人 + 女人 ≈ 女王
* 这不是魔法,而是训练好的Embedding空间的几何性质
*/
public static float[] add(float[] v1, float[] v2) {
float[] result = new float[v1.length];
for (int i = 0; i < v1.length; i++) {
result[i] = v1[i] + v2[i];
}
return result;
}
}矩阵乘法:神经网络的核心运算
神经网络的每一层,本质上都是一次矩阵乘法:输出 = 权重矩阵 × 输入向量。
输入:[0.5, 0.3, 0.2](3维向量)
权重矩阵(4×3):
[0.1, 0.2, 0.3]
[0.4, 0.5, 0.6]
[0.7, 0.8, 0.9]
[0.1, 0.3, 0.5]
输出:4维向量(矩阵行数×输入列数)为什么矩阵乘法这么重要? 因为它可以做特征变换——把低维特征变成高维,或者把高维压缩成低维。Transformer里的Q、K、V矩阵,都是这样的线性变换。
第二部分:概率论——AI的不确定性表达
概率分布:模型"思考"的方式
LLM生成文字的过程,不是查字典,而是在所有词汇上维护一个概率分布,然后采样。
比如,给定"今天天气",模型可能给出:
"很好" → 35%
"不错" → 25%
"晴朗" → 20%
"糟糕" → 10%
"..." → 10%这就是softmax函数的作用:把任意数值(logits)转换成概率分布(总和为1)。
/**
* Softmax:将任意实数转换为概率分布
*
* 为什么要用e的指数?
* 1. 保证所有值为正
* 2. 放大最大值(让模型"更自信")
* 3. 数学上可微,便于梯度计算
*/
public class SoftmaxDemo {
public static double[] softmax(double[] logits) {
// 数值稳定性技巧:减去最大值,避免e^x溢出
double maxLogit = Arrays.stream(logits).max().orElse(0);
double[] exps = new double[logits.length];
double sum = 0;
for (int i = 0; i < logits.length; i++) {
exps[i] = Math.exp(logits[i] - maxLogit);
sum += exps[i];
}
double[] probs = new double[logits.length];
for (int i = 0; i < logits.length; i++) {
probs[i] = exps[i] / sum;
}
return probs;
}
public static void main(String[] args) {
// 模拟LLM输出的logits(原始分数)
double[] logits = {2.0, 1.0, 0.5, 0.1};
double[] probs = softmax(logits);
String[] tokens = {"很好", "不错", "晴朗", "糟糕"};
for (int i = 0; i < tokens.length; i++) {
System.out.printf("%s: %.1f%%\n", tokens[i], probs[i] * 100);
}
// 输出:
// 很好: 49.4%
// 不错: 26.9%
// 晴朗: 19.8% (注意:不是简单按比例,指数放大了差距)
// 糟糕: 3.9%
}
}Temperature参数的数学本质
你一定用过temperature参数。现在可以从数学上理解它了:
/**
* Temperature对概率分布的影响
*
* temperature > 1:分布更"平",随机性更高,创意更强
* temperature < 1:分布更"尖",模型更确定,但可能更保守
* temperature = 0:贪婪解码,每次选概率最高的词(确定性输出)
*/
public static double[] softmaxWithTemperature(double[] logits, double temperature) {
// temperature通过除以logits来实现
// temperature越大,logits之间的差距越小,分布越平
double[] scaledLogits = new double[logits.length];
for (int i = 0; i < logits.length; i++) {
scaledLogits[i] = logits[i] / temperature;
}
return softmax(scaledLogits);
}第三部分:信息论——理解模型的"损失"
熵:不确定性的度量
熵(Entropy)衡量一个分布有多"随机"。
抛硬币(正面50%,反面50%):熵 = 1 bit → 最不确定
硬币只有正面(100%):熵 = 0 bit → 完全确定
天气预报(4种等概率结果):熵 = 2 bit → 2 bits的信息量在AI里的意义:模型的输出分布熵越低,说明模型越"自信";熵越高,说明模型越"迷茫"。
交叉熵:训练模型的核心目标
交叉熵损失(Cross-Entropy Loss)是几乎所有分类模型的训练目标,理解它就理解了模型怎么"学习"。
/**
* 交叉熵损失
*
* 直觉解释:
* 如果真实答案是"很好"(概率=1),但模型预测"很好"只有10%的概率
* → 损失很大(-log(0.1) ≈ 2.3)
*
* 如果模型预测"很好"有90%的概率
* → 损失很小(-log(0.9) ≈ 0.1)
*
* 训练过程就是最小化这个损失
*/
public class CrossEntropyDemo {
/**
* 计算单个样本的交叉熵损失
* @param trueLabel 真实标签的索引
* @param predictedProbs 模型预测的概率分布
*/
public static double crossEntropyLoss(int trueLabel, double[] predictedProbs) {
// 只计算真实标签对应位置的概率对数
// CE = -log(p_true)
double trueProbability = predictedProbs[trueLabel];
// 数值稳定:避免log(0)
trueProbability = Math.max(trueProbability, 1e-15);
return -Math.log(trueProbability);
}
/**
* KL散度:衡量两个分布的差异
* 在强化学习微调(RLHF/PPO)中大量使用,
* 用来控制微调后的模型不要偏离原始模型太远
*/
public static double klDivergence(double[] p, double[] q) {
double kl = 0;
for (int i = 0; i < p.length; i++) {
if (p[i] > 0) {
// KL(P||Q) = Σ p(x) * log(p(x)/q(x))
kl += p[i] * Math.log(p[i] / Math.max(q[i], 1e-15));
}
}
return kl;
}
}第四部分:Attention机制的数学直觉
这是Transformer的核心,很多人觉得难,其实理解了前面的基础,Attention很直白。
/**
* Self-Attention的简化实现
* 帮助理解Transformer的工作原理
*
* 类比:在图书馆找书
* Q = 你的搜索关键词
* K = 每本书的标题/摘要
* V = 每本书的实际内容
*
* 过程:用Q和每个K计算相关性 → softmax得到权重 → 用权重加权V
*/
public class AttentionDemo {
public static double[] selfAttention(
double[][] queries, // Q矩阵
double[][] keys, // K矩阵
double[][] values, // V矩阵
int queryIdx) { // 要计算哪个位置的attention
int d = keys[0].length; // key的维度
double sqrtD = Math.sqrt(d); // 缩放因子,防止点积过大导致梯度消失
// 1. 计算注意力分数 = Q·K / sqrt(d_k)
double[] scores = new double[keys.length];
for (int i = 0; i < keys.length; i++) {
scores[i] = dotProduct(queries[queryIdx], keys[i]) / sqrtD;
}
// 2. Softmax归一化为注意力权重
double[] attentionWeights = softmax(scores);
// 3. 用权重加权Value,得到输出
double[] output = new double[values[0].length];
for (int i = 0; i < values.length; i++) {
for (int j = 0; j < output.length; j++) {
output[j] += attentionWeights[i] * values[i][j];
}
}
return output;
}
// 工具方法
private static double dotProduct(double[] a, double[] b) {
double sum = 0;
for (int i = 0; i < a.length; i++) sum += a[i] * b[i];
return sum;
}
private static double[] softmax(double[] x) {
double max = Arrays.stream(x).max().orElse(0);
double[] exps = Arrays.stream(x).map(v -> Math.exp(v - max)).toArray();
double sum = Arrays.stream(exps).sum();
return Arrays.stream(exps).map(e -> e / sum).toArray();
}
}数学知识与工程实践的对应表
| 数学概念 | 在AI工程中的体现 | 你会在哪里遇到它 |
|---|---|---|
| 余弦相似度 | 向量检索的相似度计算 | Milvus/Qdrant配置similarity_metric |
| Softmax | 分类输出、注意力权重 | 模型输出层、Temperature参数 |
| 交叉熵 | 模型训练的损失函数 | 微调时的loss曲线 |
| KL散度 | RLHF中的约束项 | 读PPO论文、DPO论文 |
| 矩阵乘法 | 神经网络每一层的计算 | 理解为何GPU重要(GPU擅长矩阵乘法) |
| 梯度/导数 | 反向传播,模型学习的方式 | 调学习率lr、理解过拟合 |
| 概率分布 | LLM的输出本质 | top_p/top_k采样策略 |
| 信息熵 | 模型不确定性 | RAG里的置信度过滤 |
第五部分:梯度下降——模型是怎么"学习"的
很多人说"了解梯度下降对工程没用",我不同意。理解梯度下降,才能看懂微调时的loss曲线,才能知道为什么学习率要衰减,才能理解过拟合和欠拟合的本质。
/**
* 梯度下降的最简单演示
* 用最简单的例子理解:模型是怎么通过错误来调整参数的
*
* 场景:假设我们有一个最简单的"模型"
* y = w * x(预测房价 = 权重 * 面积)
* 通过梯度下降找到最优的w
*/
public class GradientDescentDemo {
public static void main(String[] args) {
// 训练数据:面积 -> 价格(万元/平米)
double[] areas = {60, 80, 100, 120, 150};
double[] prices = {120, 160, 200, 240, 300}; // 理想情况:2万/平米
double w = 0.5; // 初始权重(随机猜一个)
double learningRate = 0.0001; // 学习率:每次调整的步长
int epochs = 1000; // 训练轮数
System.out.println("开始训练...");
for (int epoch = 0; epoch < epochs; epoch++) {
// 1. 前向传播:用当前参数预测
double totalLoss = 0;
double gradient = 0;
for (int i = 0; i < areas.length; i++) {
double predicted = w * areas[i];
double error = predicted - prices[i];
// 2. 计算损失(MSE:均方误差)
totalLoss += error * error;
// 3. 计算梯度(损失对w的导数)
// dLoss/dw = 2 * error * x
gradient += 2 * error * areas[i];
}
// 4. 反向传播:用梯度更新参数
// 梯度为正 → w太大了 → 减小w
// 梯度为负 → w太小了 → 增大w
w = w - learningRate * (gradient / areas.length);
if (epoch % 100 == 0) {
System.out.printf("Epoch %d: w=%.4f, Loss=%.2f%n",
epoch, w, totalLoss / areas.length);
}
}
System.out.printf("训练完成: w=%.4f (理想值应接近2.0)%n", w);
// 输出大约是 w=2.0,因为价格约等于面积*2
}
}工程意义:
- 学习率太大:参数更新幅度大,loss震荡,模型不收敛
- 学习率太小:收敛极慢,需要几倍的训练时间
- Loss下降曲线平稳:模型在正常学习
- Loss突然飙高:可能遇到了异常数据,或学习率太大
这就是为什么微调模型时,你需要关注训练日志里的loss曲线变化。
第六部分:常见面试题解析
学完数学基础,来看看AI工程岗位面试中经常考的数学题:
余弦vs欧氏距离的直觉例子:
"我喜欢吃苹果"的向量 → [0.8, 0.3, ...](假设3维简化) "非常非常喜欢吃苹果苹果苹果"的向量 → [2.4, 0.9, ...](重复出现,模长更大)
- 欧氏距离:这两句距离很大(因为模长差很多)
- 余弦相似度:这两句非常相似(因为方向几乎一样)
在语义搜索里,我们关心的是"语义方向",不关心"表达强度",所以用余弦相似度。
学习路径建议
这套学习路径,不需要系统学完整本教材。每个阶段只要理解核心概念和工程意义就够了,总共2个月可以完成。
最重要的一条经验:别死磕公式推导,要把数学和代码对照着看。每个公式,找到对应的代码实现,就能理解它在做什么。
