
向量跟矩阵这两个词我至少听了十年。做数据处理也好搞机器学习也罢几乎所有事情最后都会落到它们身上一条样本是一个向量一个模型权重是一张矩阵一整个数据集就是一堆向量拼起来的矩阵。你说它抽象也好说它基础也罢绕不开的东西始终绕不开。这篇文章打算用比较接地气的方式把从“向量是什么”到“矩阵能干嘛”再到工程里那些听着高级的词汇全串成一条清晰的主线。不想看公式的可以直接跳结论想上手复现的我也把常见代码和踩坑记录写在后面。1. 先搞懂向量它不只是一个“数组”1.1 向量的两种面孔几何箭头与数据列表学向量最容易被卡住的地方就是它有两种完全不同的视角。在物理课上向量是一个带箭头的线有长度、有方向用来表示力、速度、位移。但在计算机里向量就是一组数字比如[0.2, 0.8, 0.5]a是三个特征构成的一条样本。这两种说法其实是一回事。你可以把[0.2, 0.8, 0.5]想象成三维空间中的一个点从原点指向那个点的箭头就是向量。别小看这个“箭头”和“点”的切换——理解这一点之后很多后续概念都会变得顺理成章向量的加法就是箭头首尾相接向量的数乘就是箭头拉长或缩短而两个向量之间的“距离”就是箭头的长度差。在做机器学习的时候我更习惯把向量看作“物体的数字化描述”。比如说一个人身高 170cm、体重 65kg、年龄 28 岁这就是一个三维向量[170, 65, 28]。把一万个人的这些数字堆在一起你就得到一张矩阵。明明是同样的数据换个视角就从“数表”变成了“空间里的点云”。这是我觉得学习线性代数最值得投入的一件事建立几何直觉。1.2 向量范数给向量一把刻度尺有了向量你第一件想做的事大概就是测量它有多长或者两个向量离多远。这时候就要用到向量范数。最常见的是 L2 范数也就是向量各分量平方和的平方根几何上就是箭头长度。第二个常见的是 L1 范数各分量绝对值之和它对应的是“曼哈顿距离”——想象你在棋盘格街道上走不能斜穿只能横着走、竖着走。工程上选哪一范数很有讲究L2 范数光滑、可导适合做梯度优化L1 范数有稀疏性很多特征选择模型会拿它当惩罚项因为它会把不重要的系数干脆压成零。Python 里计算也很方便import numpy as np v np.array([3.0, 4.0]) l2 np.linalg.norm(v) # 5.0 l1 np.sum(np.abs(v)) # 7.0实际开发中L2 范数还有一个高频用途向量归一化。做文本检索、人脸比对之前我几乎都会把向量归一化成长度为 1这样后续算内积的时候得到的就是余弦相似度而不是受向量长度影响的原始内积。很多新人在第一次做向量数据库召回时会发现“明明语义很像结果相似度很低”大概率就是没做归一化或者用了错误的指标。1.3 点积与叉乘两个高频操作向量的点积和叉乘是刷题和面试都爱考的点也是深入理解后面内容的基础。点积内积的公式是分量相乘再求和几何意义是|a||b|cosθ也就是向量 a 在 b 方向上的投影长度再乘以 b 的模长。当两个向量都归一化之后点积就直接等于夹角余弦范围在 -1 到 1 之间这成为了推荐系统、文本搜索里相似度计算的基石。我一直觉得“点积”这个名字起得特别贴切——两个向量“点”在一起结果是一个标量。叉乘在国内教材里经常被一笔带过但它其实很实用。叉乘的结果是一个垂直于原来两个向量的新向量方向由右手定则决定。算三角形法向量、判断多边形朝向、做三维空间旋转全都要用到它。还有一个很多学生踩坑的细节在求法向量时交叉相乘之后要交叉填负号比如(a2b3 - a3b2, a3b1 - a1b3, a1b2 - a2b1)中间分量的符号特别容易搞反我第一次手算就吃了这个亏。2. 矩阵的多种身份从数表到变换2.1 矩阵作为“数表”数据天然就是矩阵矩阵最直白的身份就是一张数字表格。传感器采集的多通道信号、多个样本多条特征、用户对物品的评分随手一摆就是一张矩阵。工程中常见的混淆矩阵、文献共现矩阵、邻接矩阵本质上全部是“有结构的二维表”。不要觉得这个身份太简单实际上很多问题就是因为没意识到“二维表就是矩阵”才走了弯路。比如在业务数据分析里我们要对“用户-商品”评分矩阵做补全听起来高深其实就是在填一张缺了很多数字的表格对“文档-词”矩阵做降维就是在把一张高维稀疏表压缩成低维稠密表。一旦你意识到这些对象都是矩阵线性代数里的一整套工具就全都可以搬过来用了。2.2 矩阵作为“变换”左乘一个矩阵就是在操作空间矩阵的第二个身份更加本质它描述了一个线性变换。所谓线性变换就是一条直线经过变换之后仍然是一条直线坐标原点保持不变空间中的点只是被拉伸、压缩、旋转或者切变。举个例子二维平面里想把一个点(x, y)逆时针旋转 90 度不需要联想什么几何画图直接左乘旋转矩阵[[0, -1], [1, 0]]就行。把一个图形横向拉长就乘[[2, 0], [0, 1]]。多个变换叠加比如先旋转再拉伸就变成多个矩阵依次左乘。正是因为“矩阵乘法对应变换的复合”矩阵乘法的定义才长得那么奇怪——它不是为了为难学生而是为了让“先做这个变换、再做那个变换”恰好等于“一次性做完两个变换”。在这个视角下矩阵的每一列也有了新含义它表示原来的基向量被“射”到了哪里。列向量放一起组成了变换后的坐标系。理解了这个后面看特征值分解、奇异值分解都会眼前一亮。2.3 矩阵作为“方程组”增广矩阵与消元法矩阵的第三种常见身份是线性方程组的紧凑写法。方程组2x 3y 8x - y -1写成矩阵形式就是Ax b。把系数矩阵 A 和向量 b 拼在一起得到增广矩阵[A | b]然后利用行变换化简这就是高斯消元法。我读书时总觉得消元法很套路后来才体会到它的意义行变换对应着“在等式两边做同样的操作”目标是把矩阵变成阶梯形甚至行最简形一眼看出解的情况。最妙的是如果未知数个数少于有效方程个数你会发现某一行变成全零说明方程组可能无解或无穷多解——这个过程根本不需要死记硬背动手算一次比什么都管用。实际写代码时解线性方程组几乎不会手算直接调用numpy.linalg.solve或scipy.linalg.solve即可但概念上理解增广矩阵依然很重要因为在最小二乘问题、线性回归的闭式解里你经常需要构造形如[A | b]的矩阵然后求伪逆。2.4 矩阵乘法为什么偏要这样定义矩阵乘法的定义让很多初学者疑惑为什么第 i 行第 j 列的元素等于前一个矩阵第 i 行与后一个矩阵第 j 列的点积答案还是回到“复合变换”。假设你要先把向量 x 用矩阵 B 变换再用矩阵 A 变换得到的最终向量是A(Bx)。如果用一个矩阵 C 来代表“先 B 后 A”这个整体操作那么 C 的第 i 行第 j 列就必须等于 A 的第 i 行和 B 的第 j 列做内积。这不是人为规定出来的而是从“两种变换结果必须一致”严丝合缝推导出来的。工程上我经常用矩阵乘法来批量处理数据。比如有 10000 个样本每个样本 20 个特征权重矩阵是 20 维到 3 维的映射那么一次X W就能同时完成 10000 个样本的特征变换。很多人写循环慢慢算速度慢且代码丑换成矩阵乘法之后不仅快语义还更清晰。需要提醒的是矩阵乘法不满足交换律AB和BA绝大多数情况下不相等这对应到工程上就是“先旋转再移动”和“先移动再旋转”效果不一样必须格外小心。3. 矩阵计算中的经典操作分块、求逆与特征分解3.1 分块矩阵把大矩阵拆成小积木处理大型矩阵时直接对整个矩阵运算往往又慢又难理解。分块矩阵的思路是把一个大矩阵按行列切成若干子块把每个子块当成一个“元素”来看原本的乘法、求逆公式依然在块层面成立。在热搜索里“分块矩阵求逆”和“分块矩阵的 n 次方公式”是高频词说明很多人在这个点上卡过。分块求逆最常见的场景是形如M [[A, B], [C, D]]的矩阵其中 A 和 D 的舒尔补条件满足时可逆。最基本的块对角矩阵最简单diag(A, D)的逆就是diag(A^{-1}, D^{-1})。如果 B 和 C 不为零就要用分块求逆公式核心思想是先把右下角块替换成它的舒尔补S D - C A^{-1} B然后逐块写出。这个公式我不建议死记实际编码时可以直接用scipy.linalg.block_diag构造块对角矩阵或者用 numpy 的分片索引来验证结果。分块矩阵的 n 次方公式则有一个非常实用的特例如果矩阵是分块对角矩阵那么M^n diag(A^n, D^n)也就是每个子块分别求 n 次方。这个结论在递推算法、图论路径计数里都能派上用场。遇到非对角分块建议先做相似变换把它化为若尔当形或对角形再算幂比硬推广公式可靠得多。3.2 特征值与特征向量抓住矩阵的“主轴”特征值和特征向量是矩阵理论里最迷人的地方。简单说如果存在非零向量 v 和标量 λ使得Av λv那么 v 就是 A 的特征向量λ 是特征值。用几何语言说特征向量是那些被矩阵 A 作用后“方向不变、只被伸缩”的特殊向量伸缩的倍率就是特征值。为什么工程这么爱提特征值分解因为很多动态系统、数据演化的长期行为都由最大的几个特征值决定。做数据降维的 PCA说白了就是计算协方差矩阵的特征向量把数据投影到特征值最大的几个方向上从而保留下最重要的“主轴”。推荐系统里的矩阵分解、图算法里的谱聚类也全部建立在这套理论上。我在实际调参时经常做一件事先用np.linalg.eig或np.linalg.eigh算一下协方差矩阵的特征值看看特征值衰减曲线。如果前几个特征值占了总能量的 90% 以上那么保留对应维度就够了如果特征值衰减非常缓慢说明数据本身在高维空间里分布很均匀强行降维效果会有限。3.3 矩阵求逆能少求就少求矩阵求逆是数学上漂亮、数值上危险的操作。公式A^{-1} adj(A) / det(A)只适合手算小矩阵和理解原理工程上是不能直接用的因为行列式的计算复杂度极高而且数值不稳定。真正的做法是用 LU 分解或 Cholesky 分解去解线性方程组。很多新人在代码里特别喜欢写inv(A) b而不是solve(A, b)。前者先求逆矩阵再乘向量速度慢、误差大后者直接做分解并求解又快又稳。我第一次处理数百维的矩阵时踩过这个坑后来就养成了习惯解方程永远用np.linalg.solve需要最小二乘解时用np.linalg.lstsq或伪逆np.linalg.pinv。提到“矩阵反演公式”很多人会想到分子动力学中的矩阵反演引理也叫 Woodbury 恒等式(A U C V)^{-1} A^{-1} - A^{-1} U (C^{-1} V A^{-1} U)^{-1} V A^{-1}这个公式的神奇之处在于如果你已经知道 A 的逆再对 A 做一个小规模扰动U C V时不需要重新求整个大矩阵的逆只需要对低维矩阵求逆。在卡尔曼滤波、增量学习、贝叶斯线性回归里这个技巧能大幅减少计算量。我建议把它当成“高级优化技巧”收藏真正用到时能省下不少训练时间。3.4 矩阵连乘问题动态规划的经典练手题“计算矩阵连乘问题最优解的动态规划算法”也是热搜词里的常客。它本身是一个经典算法题若干矩阵相乘因为矩阵乘法满足结合律不同的括号顺序会导致完全不同的乘法次数问题是找到代价最小的加括号方式。状态转移方程我至今印象深刻dp[i][j] min(dp[i][k] dp[k1][j] p[i-1] * p[k] * p[j])其中p是矩阵的维度序列dp[i][j]表示第 i 个到第 j 个矩阵连乘的最小乘法次数。这个问题的核心是“区间 DP”从小到大枚举区间长度在每个区间内枚举分割点 k。经典样例A1(30×35), A2(35×15), A3(15×5), A4(5×10), A5(10×20), A6(20×25)最优括号化之后一共只需要 15125 次乘法而盲目从左到右算需要上十万次。做这类题的意义不在于题目本身而在于训练一种拆解问题的能力——把复杂运算的“顺序”当成一个可以被优化的决策变量这在工程调度和计算图优化里都能找到影子。import sys def matrix_chain_order(p): n len(p) - 1 dp [[0] * (n 1) for _ in range(n 1)] s [[0] * (n 1) for _ in range(n 1)] for length in range(2, n 1): for i in range(1, n - length 2): j i length - 1 dp[i][j] sys.maxsize for k in range(i, j): cost dp[i][k] dp[k 1][j] p[i - 1] * p[k] * p[j] if cost dp[i][j]: dp[i][j] cost s[i][j] k return dp, s4. 向量与矩阵在现代应用里的落地场景4.1 支持向量机用点积找“最大间隔”支持向量机这个名字本身就带着向量味。它的核心思想简单粗暴在特征空间里找一个超平面把不同类别的样本分开并且让这个超平面距离最近的样本点支持向量尽可能远。这个“找到最大间隔”的优化问题最后会变成一个只和样本两两点积有关的对偶问题——这也是为什么核技巧能成立的底层原因你不需要直接在高维空间里算特征只要知道高维空间里的内积就能完成分类。“怎么用支持向量机 全流程”这个问题我按自己的套路拆解为五步第一步整理数据特征用数值型类别用 0/1 编码第二步标准化SVM 对特征尺度很敏感标准化能避免大数值特征主导间隔第三步选核函数线性核适合高维稀疏数据RBF 核适合非线性边界第四步训练并调 C 参数C 越大越强调分类正确容易过拟合C 越小间隔越宽第五步用混淆矩阵评估而不要只看准确率。代码上用 sklearn 很简单from sklearn.svm import SVC from sklearn.preprocessing import StandardScaler from sklearn.metrics import confusion_matrix, classification_report scaler StandardScaler() X_train scaler.fit_transform(X_train) X_test scaler.transform(X_test) model SVC(kernelrbf, C1.0) model.fit(X_train, y_train) y_pred model.predict(X_test) print(confusion_matrix(y_test, y_pred))有一点想提醒大家RBF 核里的gamma参数决定了单个样本的影响半径。gamma 太大决策边界会非常崎岖几乎贴着训练数据gamma 太小边界过于平滑模型可能死活分不开。我一般用网格搜索同时调 C 和 gamma比默认参数效果好得多。4.2 混淆矩阵分类模型最诚实的体检报告混淆矩阵就是一张二维表行代表真实类别列代表预测类别。一个二分类的混淆矩阵长这样真正例TP、假正例FP、假负例FN、真负例TN各占一个格子。从这四个数字能推出准确率、精确率、召回率、F1比单一准确率信息量大太多。热词里出现“yolo混淆矩阵总合不唯一”我猜很多人在目标检测里遇到过类似困惑自己画出来的混淆矩阵各行相加、各列相加对不上总数。真实原因一般是目标检测的模型输出里包含大量背景框、置信度阈值过滤或者类别重映射不同实现方式对“忽略区域”和“低置信度预测”的处理不一致。如果你发现矩阵总和不对第一步先检查两个东西一是预测结果是否已经按置信度阈值过滤过二是是否把背景类单独拉出来统计了。不要急着怀疑代码 bug。另一个高频需求是“python多分类混淆矩阵代码”。多分类时建议设置labels参数确保矩阵的行列顺序一致否则很容易出现类别移位。我用得最多的代码是import numpy as np from sklearn.metrics import confusion_matrix cm confusion_matrix(y_true, y_pred, labels[0, 1, 2]) print(cm) row_sum cm.sum(axis1) col_sum cm.sum(axis0) normalized cm.astype(float) / row_sum[:, None]归一化后的混淆矩阵在展示不平衡分类结果时特别好用你可以直观看到每一类的召回率而不是被大类别样本数带偏。4.3 向量数据库把“向量”变成检索的引擎过去几年“向量数据库集成与优化”成了一个很热的话题。它做的事本质上就是把任意内容文本、图片、音频通过模型变成向量然后把海量向量存入数据库查询时用同样的模型把问题也变成向量然后查找最相似的一批向量。问题的关键变成了“向量之间的距离怎么算、怎么索引更快”。关于“ollama 安装了向量模型后如何使用”如果你本地已经用 ollama 跑了一个嵌入模型一般流程是先通过接口把文本变成向量比如调用 embedding 接口得到一串浮点数然后把它写入向量数据库。很多人卡在“模型装好了但不知道向量怎么提取”核心问题是没搞清楚需要调用的接口路径和返回结果格式。我习惯先在 Python 里直接请求本地接口验证输出维度是否符合预期再开始批量入库。优化方面我总结过三个点。第一向量必须归一化否则余弦相似度和欧式距离的结果可能不一致。第二索引参数比模型参数更容易被忽略比如 HNSW 里的M和efConstructionM 控制每个节点的连接数调大能提高召回率但会消耗更多内存和构建时间。第三批量写入永远比单条写入快得多生产中尽量攒一批再入库并且给向量字段建好索引再查否则全表扫描会让你怀疑人生。下面是一个简单的嵌入和入库伪流程我用过不少类似的模式import requests import json # 1. 提取向量 resp requests.post(http://localhost:11434/api/embeddings, json{model: embedding-model, prompt: 你好}) vector resp.json().get(embedding) # 2. 写入数据库示意 # db.insert(items, vectorvector, metadata{text: 你好}) # 3. 查询 # results db.search(vector, top_k10)4.4 张量、向量与矩阵从名字到应用的层级关系先把概念说破标量是 0 维张量向量是 1 维张量矩阵是 2 维张量更高维的数组统称张量。在做深度学习时“张量”几乎就是多维数组的普通话称呼并没有比矩阵多出更多神秘含义。你完全可以理解成向量是“一个有方向的数据列表”矩阵是“一张有结构的二维数表”张量就是“任意更多维的数据容器”。热词里有“张量和向量的区别”我遇到的新手困惑大多是搞不清楚什么时候用“向量”什么时候用“张量”。我的经验是向量强调“方向和长度”的几何语义张量强调“数据的形状和多维索引方式”。在机器学习代码里你通常不会看到vector这个类型只会看到shape是(N,)或(N, 1)的数组。这个(N,)与(N, 1)的区别也曾经坑过我前者是一维向量后者是 N 行 1 列的矩阵虽然形状相似但在矩阵乘法里的广播行为完全不同。建议在写代码时明确形状必要时用reshape(-1, 1)把它补成列向量。5. 常见问题与排查技巧实录5.1 矩阵运算遇到维数不匹配这是最常见的报错。出现ValueError: shapes (3,2) and (4,2) not aligned时很多人下意识认为是“两个矩阵形状不一致”其实真正的含义是第一个矩阵的列数不等于第二个矩阵的行数。建议按照矩阵乘法定义逐项核对做A B时A 的 shape 必须是(m, k)B 必须是(k, n)结果才是(m, n)。写代码时我习惯先把每个矩阵的shape打印出来标好名字再组合基本上一分钟就能定位问题。5.2 逆矩阵不存在或计算不稳定求逆报错或者结果巨大通常是两种情况矩阵奇异秩亏缺或者条件数过大。奇异矩阵在数学上就没有逆工程上应该用伪逆np.linalg.pinv最小二乘场景它比普通逆稳定得多。条件数大说明矩阵接近奇异即便求出了逆结果也极不可靠。这时候不要硬算回到问题本身想一想特征是否高度相关是否有多重共线性加一点正则化比如岭回归里加λI往往能让矩阵变得可逆且结果合理。5.3 混淆矩阵的“总数对不上”我遇到过一个特别有代表性的 bug在二分类任务里因为测试集某一类的样本太少预测时模型把所有样本都判成了多数类结果混淆矩阵出现整行或者整列为 0。这不是 bug而是数据不平衡的表现看“准确率”会觉得还不错但看混淆矩阵才会发现模型其实啥都没学会。另一个常见问题是测试集里部分样本的预测结果没被收集导致y_pred长度小于y_true直接用confusion_matrix会报错或对不齐。排查时先对比两个数组的长度再确认类别编号是否连续。我自己的习惯是先跑一个最原始的 2 乘 2 矩阵确认逻辑通了再扩展到多分类。5.4 向量数据库召回效果差如果你发现向量数据库检索出来的结果“好像不太相关”不要急着调索引参数。我先后碰到过三种情况各自对应不同解法一是文本没有预处理模型对太长的文本生成“平均化”的向量导致语义被稀释解决方法是分句或分段后再嵌入二是没有过滤噪声词导致向量偏向高频词解决方法是清洗文本并考虑停用词三是查询向量的嵌入模型与库内向量用了不同版本或不同模型这种“模型不一致”会导致向量空间完全错位召回自然一塌糊涂。我在生产里碰到过最隐蔽的问题就是两个模型口径不一致当时排查了很久最后把所有向量的来源模型信息记到元数据里才彻底解决。6. 一些操作心得踩过的坑多了之后我最大的体会是不要试图“记住”所有公式而要抓住“向量是描述矩阵是操作”这条主线。任何矩阵运算你先问自己它到底在做什么乘法是复合变换求逆是逆向搜索特征分解是找主轴分块是把复杂问题拆小。带着这个问题去写代码基本不会迷路。另外一个很实用的习惯是在工程里每一步都留好“形状检查点”。每次执行完矩阵操作立刻输出x.shape并且写上注释说明这一维表示什么意思。这个习惯虽然简单却能省下无数排查时间。向量数据库、SVM、混淆矩阵这些听起来高级的东西落到地面上无非就是“向量的相似度”“矩阵的运算”“指标的统计”三件事。把三件事想透了遇到新概念就不会慌。最后再说一个小建议如果时间允许在笔记本上手工算一遍 3 乘 3 矩阵的乘法、转置、行列式、特征值不用多一遍就够。这个动作会让你真正理解那些代码背后的代价和意义。很多工程师写得一手漂亮代码却在矩阵结果的形状上栽跟头就是吃亏在没动手建立过这种手感。