1.2 线性代数(上)向量、矩阵与线性变换
1.先说清楚:为什么深度学习非得用线性代数
上一篇我们聊到模型本质上就是一个函数 ,输入 经过一堆计算吐出一个预测 。当时我故意藏了一件事没细说,那就是 和参数 到底长什么样。你如果跟着某位讲师打开过一节PyTorch教程,大概率满屏都飘着tensor这个词。tensor翻译成张量,听着很玄乎,说穿了它就是一个多维的数表。零维的张量是一个孤零零的数,一维的张量是向量,二维的张量是矩阵,三维往上的张量没有专门的中文名字,大家就统一叫它张量。深度学习里几乎所有数据都得先变成这种形状,才能喂进网络里。
那么问题来了,为什么非得这样?因为神经网络一层接一层往下传,每一层干的活儿说穿了就三步:拿这一层的参数(一个矩阵)去乘以上一层送过来的数据(一个向量),再加一个偏置(一个向量),最后过一道激活函数。ReLU这种激活函数你后面会天天见到。所以整个网络的骨架就是反复地做矩阵乘向量,搞懂线性代数,等于搞懂了网络里数据流动的那条主路。这一篇我们把向量、矩阵和线性变换这三个最基础的概念先打牢,下一篇再聊范数、特征值、SVD这些更硬核的工具。
2.向量:把一组数排成一队
向量(vector)这个词你中学就听过。在线性代数里,向量就是有顺序的一组数,写成括号里一长条。比如:
这里 是一个向量,3放在第一个位置,2放在第二个位置,这两个数的位置绝不能乱。向量里有几个数,就叫几维向量,这个几就是它的维度(dimension)。上面这个 是二维向量。再来一个三维的:
就是三维向量。
干巴巴的定义说完,我们换一个生活化的说法。还记得上一篇那个学生的例子吧,复习时长5小时、作业完成度0.8,那这个学生就可以写成一个向量:
向量里第一个位置永远放复习时长,第二个位置永远放作业完成度。光看复习时长一个数,信息太少,把好几个数拼到一块儿组成一个向量,这个学生就被描述得立体多了。再往里加特征也容易,比如把课堂发言次数、每周自习时长也塞进去,维度就涨上去了。深度学习里管向量里的每个数叫一个特征(feature),所以一个向量就是一个样本的全部特征打包成一份。
其实各行各业都能这样把对象写成一个向量。我之前翻过一本讲量化金融的入门书,书里把一支股票写成一个向量,第一个位置放开盘价、第二个放成交量、第三个放换手率,一支股票就被压成了一串数,送进模型里去分析。运动员的体能数据也是同一个道理,跑得多快、跳得多高、反应多灵敏,几个数排成一队,就够给这个人画像了。向量这件工具之所以通用,就在于只要你能把对象拆成几个数,它就接得住。
这里得顺便提一个特别容易踩的坑。日常说话的时候大家爱说特征向量这个词,但在线性代数里它有另一个专门的意思(叫eigenvector,特征值那一套,下一篇会聊),跟样本的特征拼出来的向量完全是两码事。我们这一篇里说特征向量,默认指的是后者,也就是把特征拼起来那种。等以后聊到特征值再正经区分这两个。
再说一层几何直觉。把向量的每个数当成一个坐标,向量就能在空间里画成一根从原点出发的箭头(原点就是坐标轴相交的那个位置,记作 )。二维向量 就是平面上一根从 指向 的箭头。这个画面特别重要,后面讲线性变换的时候你得在脑子里看到这根箭头是怎么被搬来搬去的。
向量还能算加法和数乘,这两个运算就是线性代数的根基。加法就是把两个同维向量对应位置相加:
数乘就是把一个数乘到向量的每个位置上:
这两个运算看着朴素,所谓线性这两个字,骨子里就是它们撑起来的。线性变换必须靠加法和数乘搭出来,所以线性代数这门课整个名字,研究的就是加法和数乘能玩出什么花样。
3.点积:一眼看两个向量像不像
加法和数乘有点无聊,真正好玩的运算来了,叫点积(dot product),有时候也叫内积。点积干的事是把两个同维向量对应位置相乘,再全加起来,得到一个数。设两个二维向量:
其中 、 是 第一个和第二个位置上的数,叫作 的分量,、 同理是 的分量。 和 的点积写成 ,中间那个点读作dot,计算方式是:
举个带数字的例子。还是用学生那个向量,张三 (复习5小时、作业完成度0.8),李四 (复习4小时、作业完成度0.9)。两个人的点积是:
这个20.72告诉你什么呢?你可以粗略理解为两个人在这些特征上整体重叠得挺高,那他们俩就挺像。再算一个小明同学 (复习0小时、作业完成度0.1)跟张三的点积:
0.08几乎为零,说明小明跟张三根本不在一个画风,完全是两类学习状态。你看,相似度这件事就被点积给量化了。各大App里那种找跟你兴趣相近的用户、猜你喜欢的功能,底层基本就是算向量点积,规模一大就升级成矩阵乘矩阵,本质都一样。
点积还有一个等价的几何写法:
这里 表示向量 的长度(这回说的是箭头画出来有多长,跟维度不是一回事), 是两个向量箭头之间的夹角, 是这个夹角的余弦值。这个式子告诉你,点积其实就等于一个向量在另一个向量上投影的长度,再乘以另一个向量本身的长度。两个箭头方向越一致( 接近1),点积越大,方向越反着来( 接近-1),点积越小。所以相似度这个直觉背后是有几何支撑的,方向越一致就越像。
不过这里有个坑你得知道。点积的大小会被向量长度影响,一个巨长的向量跟谁算点积都偏大。所以工程上更常见的做法是先把向量归一化(除以自己的长度)再算点积,那东西叫余弦相似度(cosine similarity)。等到后面聊检索和大模型embedding的时候,这个词会反复冒头,到时候你可别觉得陌生。
4.矩阵:一摞向量,外加一份配方表
向量搞懂了,矩阵(matrix)就是水到渠成的事。矩阵说白了就是把好几个向量按行摞起来的一张数表。比如:
这是一个2行2列的矩阵,记作 矩阵(读作2乘2)。 的第一行是 ,第二行是 。
上一篇就提过,矩阵特别适合装一整层网络的参数。原因在于,矩阵乘向量这个操作,恰好就是把输入向量按好几份不同配方重新调配,得到好几个新特征。我们把这个操作写成式子:
这里 是矩阵, 是输入向量, 是输出向量, 写在一起表示矩阵 乘以向量 。
来算个具体例子。还是用学生那个向量 ,配一个矩阵:
矩阵 的第一行 是第一份配方,意思是取0.7份复习时长、再取0.3份作业完成度,调配成第一个新特征。第二行 是第二份配方,取0.2份复习时长、0.9份作业完成度,调配成第二个新特征。算出来的新向量 就是:
原始的二维特征 经过矩阵 这一乘,又得到一个二维新特征 。每一个新特征都是原始特征按某份配方重新调配出来的。神经网络每一层干的就是这件事,只不过矩阵更大(一层动不动几百几千维),配方更复杂,本质一回事。其实这跟厨房里调酱汁很像,同一批原料(原始特征),换一份配比(矩阵的一行),就出来一种新味道(新特征),大厨手里那张写满比例的方子,说白了就是一张矩阵。
这里我得敲下黑板。矩阵 的行数决定了输出有几个新特征,矩阵 的列数必须和输入向量 的维度对上号。你要是看到一个 矩阵,那它就把二维输入变成三维输出,凭空多出来一个特征,这就叫升维。反过来一个 矩阵就是把三维压成二维。深度学习里隐藏层经常比输入层还宽,靠的就是矩阵把维度撑大。刘慈欣在《三体》里写过一个词叫降维打击,从数学上看,用矩阵把维度缩回去,恰好就是这么个意思。
5.线性变换:矩阵把空间捏来捏去
矩阵乘向量这件事,在数学里有个更高级的说法叫线性变换(linear transformation)。变换(transformation)说白了就是函数的另一种叫法,输入一个向量、吐出一个新向量的函数。线性则是说这个变换满足两条规矩:
第一条意思是先把两个向量 和 加起来再变换,结果和分别变换后再相加完全一样。第二条意思是先把向量 数乘 倍再变换,结果和先变换再数乘 倍也完全一样,这里的 就是任意一个数。这两条规矩合起来你可以粗略理解为:变换是直的和平的,不弯曲也不扭曲,相当于把整个空间像拉橡皮筋一样均匀地扯一扯。
光看公式还是抽象,我们直接看画面。下面这几种二维线性变换,你最好都能在脑子里画出来。
第一种是缩放。取一个矩阵:
矩阵 乘以任何向量,都会把这个向量的两个分量都放大到2倍,整个平面横竖各放大2倍,面积变成原来的4倍。深度学习里特征动不动就被某个数乘放大缩小,缩放矩阵天天出场。
第二种是旋转。把任何向量逆时针转90度,对应的矩阵是:
拿 去乘 (指向正东方向的箭头),得到 (指向正北方向的箭头),果然逆时针转了90度。旋转矩阵在图像处理里特别常见,深度学习做数据增强(data augmentation)的时候经常会随机旋转图片来扩充训练集。
第三种叫剪切,画面更有意思:
拿 去乘一个正方形四个角的所有向量,正方形会被推成一个平行四边形,就好像有人抓住正方形的顶边往右拉了一下、底边却纹丝不动。剪切听着冷门,但在一些数据预处理里其实有用。
把这几种变换稍微排列组合一下,你就能拼出绝大多数能想到的线性变换。神经网络每一层,本质上就是先用一个矩阵做一次这种扯橡皮筋的活儿,再加一个偏置(bias),最后套一道非线性激活函数(ReLU那种)。所以你把矩阵这一乘到底干了什么在脑子里想明白,整层网络在干什么你心里就有底了。
这里把偏置这个小东西也解释一下。它其实就是给每一个新特征额外加一个数,写成式子是 ,其中 是一个跟 同样大小的向量,叫偏置向量。这个加偏置的操作让线性变换多了一点平移的味道,要是没它,整层网络就只能在原点附近打转转,能学的东西就少了一大截。
6.线性组合和张成空间:这堆向量能撑出多大的场子
最后一个概念叫线性组合和张成空间(span)。听着抽象,其实就一句话:拿几个向量,通过随便搭配加法和数乘(还记得前面那两个基本运算吧),能凑出来的所有向量构成一片区域,这片区域就叫这几个向量的张成空间。每一组搭配方式叫一个线性组合。
举个例子。两个向量 和 (这两个特殊向量叫标准基),它们的任意线性组合 都能凑出整个二维平面,所以它们的张成空间就是整个 ( 表示所有二维实数向量的集合),其中 、 是任意两个数。但如果换成 和 ,这两个向量共线了,再怎么线性组合也只能凑出x轴这一条直线,张成空间从二维被挤瘪到了一维,这就亏了。
这个概念现在你可能觉得用不上,但等你后面学到特征选择、PCA、SVD这些大招的时候,会发现它们本质上都在琢磨同一件事:手里那堆特征向量到底张成了多大一片空间,能不能装得下我想表达的全部信息。手里要是一堆高度共线的特征(互相之间能近似凑出来),张成空间就被挤瘪了,看起来维度很高,实际信息量却很有限。这种维度看着唬人但信息其实没多少的窘境,线性代数有专门的工具去度量,比如范数、秩、特征值这些,下一篇我们接着聊。
7.收个尾
这一篇我们花了不少篇幅把向量、点积、矩阵和线性变换这几个底子打牢。你回头再去看神经网络那一层套一层的结构,会发现它就是反复在做矩阵乘向量、加偏置、套激活这件事。把这个骨架吃透了,后面不管碰上多复杂的网络结构,你都能稳住心态一层一层拆。
下一篇我们接着往深里走,聊范数、特征值、SVD这些更硬核的工具,到时候降维打击这个词会从一句口头玩笑变成实打实的数学操作。
练习
Q1. 矩阵乘向量 这个操作,从"配方"的角度解释它在干啥?为什么说搞懂它就搞懂了神经网络一层数据流动的主路?
矩阵的每一行就是一份配方,规定了原始特征各取多少、怎么调配,对应相乘相加后得到新向量的一个分量。所以 就是把输入向量按好几份不同配方重新调配,榨出一摞新特征。神经网络每一层干的就是"矩阵乘向量、加偏置、套激活"这三步,矩阵这一乘是骨架,搞懂它整层数据怎么流动你心里就有底了。顺带一提,矩阵 的行数决定输出几个新特征,列数得和输入维度对上号。
Q2. 张三 ,小明 ,算它俩的点积。这个数告诉你什么?
点积是 ,几乎为零。点积粗略量化了两个向量有多像,0.08 说明小明跟张三根本不在一个画风,是两类完全不同的学习状态。各大App里那种"猜你喜欢"的功能,底层基本就是算向量点积。
Q3. 课文特意敲黑板提醒过"特征向量"这个词有两种意思,分别指啥?另外点积的大小容易被什么带偏,工程上怎么补救?
一种是样本的"特征"拼出来的向量(日常说法),另一种是线性代数里的 eigenvector,特征值那一套(下一篇才正经讲),俩完全不是一回事。点积会被向量长度带偏——一个巨长的向量跟谁算点积都偏大,所以工程上常先把向量归一化(除以自己的长度)再算点积,那东西叫余弦相似度,后面讲检索和大模型 embedding 的时候会反复冒头。