「N文粗通線性代數(shù)」開(kāi)篇辭:
本來(lái)想模仿網(wǎng)紅爆款文,寫篇《一文讀懂線性代數(shù)》。但實(shí)際一寫,發(fā)現(xiàn)很難。別說(shuō)讓讀者讀懂,光是把一些概念粗淺地寫明白,也起碼要寫兩篇,于是把標(biāo)題改成《兩文粗通線性代數(shù)》。寫著寫著,發(fā)現(xiàn)兩篇也不夠,最后只好改成《N文粗通線性代數(shù)》。
學(xué)習(xí)數(shù)學(xué),從來(lái)都是一分耕耘,一分收獲,來(lái)不得半點(diǎn)自欺與浮躁,任何投機(jī)取巧都注定枉然。但刻苦用功不等于死記硬背、機(jī)械刷題。事實(shí)上,書本上抽象的數(shù)學(xué)原理,很多都是從自然現(xiàn)象與實(shí)際生活中歸納總結(jié)出來(lái)的。只要善于聯(lián)系數(shù)學(xué)原理與實(shí)際生活,你會(huì)發(fā)現(xiàn)掌握線性代數(shù)并不難。
——吳進(jìn)遠(yuǎn)
撰文 | 吳進(jìn)遠(yuǎn)
在全世界的大學(xué)里,“線性代數(shù)”這個(gè)名字給人的印象是學(xué)起來(lái)很難,很容易掛科,而且對(duì)找工作沒(méi)什么用。其實(shí)這門課的應(yīng)用并不窄。有個(gè)笑話說(shuō),有位教授開(kāi)了線性代數(shù)課,選修者寥寥。于是下個(gè)學(xué)期他把課名改為“人工智能工程基礎(chǔ)”,結(jié)果選修者趨之若鶩。至于學(xué)起來(lái)難不難,要看能不能找到合適的學(xué)習(xí)方法,方法對(duì)了就不難了。
書歸正傳,話說(shuō)某近視宅男,某日下樓到早點(diǎn)鋪買早餐。眼鏡忘在家里,看不清黑板上寫的價(jià)目。宅男心算能力超強(qiáng),碰巧服務(wù)員小妹口算能力也超強(qiáng),而且唱收唱付聲音清亮。
于是,宅男就一邊排隊(duì),一邊聽(tīng)著前邊顧客買早點(diǎn)的品種數(shù)量和服務(wù)員小妹報(bào)的總價(jià),據(jù)此計(jì)算各種早點(diǎn)的單價(jià)。
(1)先乘再累加
為了簡(jiǎn)化問(wèn)題,我們假定早點(diǎn)鋪只賣三種早點(diǎn):
1)油餅,單價(jià)3元;
2)茶葉蛋,單價(jià)4元;
3)豆腐腦,單價(jià)7元。
當(dāng)然這幾個(gè)單價(jià)對(duì)宅男來(lái)說(shuō)是未知的。
第一個(gè)顧客買了一個(gè)油餅、一個(gè)茶葉蛋、一碗豆腐腦,服務(wù)員小妹報(bào)價(jià)14元;
第二個(gè)顧客買了兩個(gè)油餅、四個(gè)茶葉蛋、四碗豆腐腦,服務(wù)員小妹報(bào)價(jià)50元;
第三個(gè)顧客買了三個(gè)油餅、五個(gè)茶葉蛋、八碗豆腐腦,服務(wù)員小妹報(bào)價(jià)85元;
第四個(gè)顧客買了三個(gè)油餅、一個(gè)茶葉蛋、一碗豆腐腦,服務(wù)員小妹報(bào)價(jià)20元。
這幾筆交易可以列成下面一個(gè)表:
服務(wù)員小妹是怎樣算出總價(jià)的呢?很簡(jiǎn)單,把顧客購(gòu)買每種食品的數(shù)量乘以它們各自的單價(jià),再把各個(gè)乘積加和起來(lái),就得到了總價(jià)。這個(gè)計(jì)算方法,可以用下圖表示:
(2)矩陣與向量相乘
上面這個(gè)算法,可以用一個(gè)更簡(jiǎn)潔的矩陣乘法公式寫出來(lái):
有的時(shí)候,只有一個(gè)下標(biāo)的矩陣可以稱為向量?;蛘哒f(shuō),向量有時(shí)可以看成是一種特殊的矩陣。因此,上面這個(gè)公式顯示了矩陣乘法的一種特殊情況,即一個(gè)矩陣與一個(gè)向量相乘,得到另一個(gè)向量。不過(guò)注意,這兩個(gè)向量的意義不一定一樣,它們的維度也不一定一樣。
向量與矩陣之間的乘法是按照下面的公式進(jìn)行的
在上面的計(jì)算中,我們把左邊矩陣中一行里j=1到3的元素,與右邊矩陣(或向量)一列中j=1到3的元素一對(duì)對(duì)相乘然后累加,就得到新向量的一個(gè)元素。大家是不是覺(jué)得這話聽(tīng)著像繞口令?現(xiàn)在我告訴你我是怎么記住這個(gè)算法的。
我們可以把矩陣乘法中左邊的矩陣想象成一串串橫掛的羊肉串,把右邊的矩陣(或向量)看成是豎著插的糖葫蘆。二者相乘的時(shí)候,把糖葫蘆舉高,再橫過(guò)來(lái)。
然后把糖葫蘆與第一串羊肉串懟到一起,把一個(gè)個(gè)糖葫蘆球與對(duì)應(yīng)的一片片羊肉一對(duì)一對(duì)地?cái)]下來(lái),打爛攪勻(即一對(duì)對(duì)地做乘法),再把一對(duì)對(duì)混合物捏在一起(即累加)。這樣就形成了新串燒(新向量)的第一個(gè)球。
用同樣的糖葫蘆與第二羊肉串做相同操作,就得到新串燒的第二球。對(duì)左邊矩陣的所有行重復(fù)這個(gè)運(yùn)算,最終就得到了一個(gè)新的向量??傊?,只要始終把左邊的矩陣看成橫掛的串燒,右邊的看成豎插的,就不會(huì)記混。
(3)矩陣與矩陣相乘
我們可以把上面買早餐的問(wèn)題再擴(kuò)展一下,比如這個(gè)餐廳的老板鼓勵(lì)同學(xué)早起,規(guī)定早上七點(diǎn)以前有早起優(yōu)惠,這樣就有了兩組不同的單價(jià)。而同樣的顧客購(gòu)買同樣品種數(shù)量的食品,早起或睡懶覺(jué)就會(huì)有不同的總價(jià)。這種情況下,我們就有了一個(gè)公式:一個(gè)4行3列矩陣,乘以一個(gè)3行2列矩陣,得到一個(gè)4行2列矩陣。
在這個(gè)公式中,我們特意把食品的品種下標(biāo)寫成油、蛋、豆,而價(jià)格的下標(biāo)寫成平(常)、早(起)。這樣寫的目的,是強(qiáng)調(diào)不同的下標(biāo)表示的意義可能是不同的。盡管我們平時(shí)都用1,2,3,4等下標(biāo),但不同下標(biāo)即便使用相同的數(shù)字,它們代表的意義也可能相差十萬(wàn)八千里,千萬(wàn)不可以傻傻分不清。
上面公式中保留的1,2,3,4代表了不同的顧客,如果不嫌煩瑣,我們也完全可以把這些顧客用他們的自然姓名標(biāo)注,比如張三、李四、陳老大、王二麻子等等。不過(guò),代數(shù)代數(shù),妙就妙在這個(gè)“代”字上。數(shù)學(xué)中一個(gè)非常重要的技巧,就是把一些繁雜的事物,以及對(duì)這些繁雜事物的處理方法,打包起來(lái),用一個(gè)比較簡(jiǎn)單的符號(hào)代替。通過(guò)這樣一種層層迭代,人們就可以認(rèn)識(shí)與處理遠(yuǎn)遠(yuǎn)超過(guò)人腦處理能力的復(fù)雜事物。比如前面談到的矩陣乘法,就可以用一個(gè)很簡(jiǎn)單的公式寫出來(lái):
更進(jìn)一步,我們甚至可以把這個(gè)運(yùn)算直接寫成 y = Ax 。
(4)愛(ài)因斯坦求和約定
大家都知道愛(ài)因斯坦,他腦子比咱們大家都好使,上面這個(gè)公式對(duì)他應(yīng)該是小菜一碟??墒羌懿蛔∷焯熳鲞@樣的推導(dǎo)運(yùn)算,整天“西格瑪”“西格瑪”地寫,也夠煩的,于是他老人家就提出了一個(gè)著名的愛(ài)因斯坦求和約定。這個(gè)約定最主要的一件事,就是遇到類似的矩陣乘法可以不寫“西格瑪”。兩個(gè)數(shù)懟到一起,如果有相同的下標(biāo)或上標(biāo),就表示對(duì)這個(gè)下標(biāo)(或上標(biāo))的所有成員一對(duì)對(duì)相乘然后求和。于是上面那個(gè)公式就簡(jiǎn)化為:
這個(gè)約定看似只做了很少的簡(jiǎn)化,但可以想象一下,如果討論的問(wèn)題涉及很多個(gè)矩陣連續(xù)相乘,我們就需要寫上很多層連加號(hào)。這種情況下,使用愛(ài)因斯坦求和約定帶來(lái)的好處就非常明顯了。
愛(ài)因斯坦求和約定還涉及一些細(xì)節(jié),比如下標(biāo)與上標(biāo)的使用規(guī)范。又比如用拉丁字母做上下標(biāo)代表三維空間中1,2,3這三個(gè)維度,而用希臘字母做上下標(biāo)時(shí)表示時(shí)間加空間,即0,1,2,3這四個(gè)維度。這些大家讀到具體的文獻(xiàn)時(shí)就會(huì)接觸到。
(5)硬件相乘累加運(yùn)算單元
這種兩組數(shù)一對(duì)一對(duì)地相乘再累加的運(yùn)算,在數(shù)學(xué)的很多領(lǐng)域都有非常廣泛的應(yīng)用。除了矩陣乘法,我們學(xué)過(guò)加權(quán)平均,兩個(gè)矢量的內(nèi)積(點(diǎn)積)都是這樣的形式。由于這種運(yùn)算使用得太頻繁了,在現(xiàn)代的微處理器、數(shù)字信號(hào)處理器以及現(xiàn)場(chǎng)可編程門陣列(FPGA)等邏輯芯片中,人們經(jīng)常會(huì)把相乘累加運(yùn)算單元直接設(shè)計(jì)進(jìn)去。
上圖就是一個(gè)典型的相乘累加運(yùn)算單元。輸入的數(shù)據(jù)從ai和xi這兩個(gè)端口送進(jìn)去,每個(gè)時(shí)鐘周期放一對(duì)數(shù)。這一對(duì)數(shù)相乘之后,其乘積送到累加器。每次得到的新的乘積都與留存在寄存器中的部分和y相加。等到ai與xi這兩個(gè)向量中所有的成員都刷過(guò)一遍之后,寄存器y就累加出了這兩個(gè)向量的內(nèi)積。
(6)矩陣乘法與圖片旋轉(zhuǎn)
大家可能會(huì)問(wèn),這種矩陣或者向量的乘法運(yùn)算,除了買早餐算賬,還有什么用呢?我們現(xiàn)在討論一個(gè)和吃沒(méi)有關(guān)系的例子。
外出旅游,拍了一張很好看的風(fēng)景照,可惜手機(jī)沒(méi)有端平,地平線拍歪了。這樣一張照片,怎么才能把地平線調(diào)平呢?
照片是由縱橫排列的像素構(gòu)成的,要想把地平線調(diào)平,只要把所有像素圍繞一個(gè)原點(diǎn)旋轉(zhuǎn)一定角度就可以了。具體講,是把橫坐標(biāo)為x1,縱坐標(biāo)為x2的像素,按照下面這個(gè)公式,移動(dòng)到橫坐標(biāo)y1,縱坐標(biāo)y2的位置。或者說(shuō),我們要對(duì)照片每個(gè)像素的坐標(biāo)做線性變換。(注意我們這里分別用x和y代表轉(zhuǎn)動(dòng)前后的坐標(biāo),而用下標(biāo)1、2代表橫坐標(biāo)與縱坐標(biāo)。這是為了和我們前面的用法統(tǒng)一形式。)
把所有像素的坐標(biāo)都按照這個(gè)公式算出來(lái),再把各個(gè)像素移動(dòng)到新的位置,我們就可以看到整個(gè)照片旋轉(zhuǎn)了一個(gè)角度。當(dāng)然實(shí)際操作時(shí),新圖片中可能會(huì)有一些像素被漏掉,這就需要根據(jù)周圍像素顯示的色彩亮度數(shù)據(jù)來(lái)做插值,把這些漏掉的像素填上。
(7)美顏磨皮
如今照相,美顏已成為標(biāo)配功能。所謂美顏,其實(shí)就是對(duì)照片中的像素做一系列的修改。這些像素修改,很多時(shí)候要用到矩陣乘法。磨皮是美顏中的一個(gè)重要功能。比如下面這位笑臉帥哥,不滿意自己皮膚,尤其是眼睛下方皮膚的松弛感,就給自己的照片做了磨皮。
原始照片有很多瑕疵,皮膚上各種斑點(diǎn)非常多。經(jīng)過(guò)磨皮,這些缺陷被抹掉了。
磨皮有非常多的算法,上網(wǎng)搜一搜甚至能找到不少學(xué)術(shù)會(huì)議論文。磨皮算法中最簡(jiǎn)單的一種,是把一個(gè)小范圍內(nèi)的像素做加權(quán)平均,然后生成一個(gè)新的像素。用公式寫出來(lái)是這樣的:
這個(gè)算法可以寫成矩陣乘法的形式:
這里請(qǐng)讀者注意,公式中所有權(quán)重因子的和等于1。新生成圖片的每個(gè)像素都是原始圖片對(duì)應(yīng)點(diǎn)附近九個(gè)像素的加權(quán)平均。這九個(gè)像素對(duì)新像素的貢獻(xiàn)是不同的,其中中心像素的貢獻(xiàn)為0.2,比周邊像素的貢獻(xiàn)(0.1)要高。經(jīng)過(guò)這個(gè)運(yùn)算,原始圖片中相鄰像素亮度(或顏色)突然跳變的情形就被平均掉了,圖片看起來(lái)就顯得更加平滑。
對(duì)于大多數(shù)彩色數(shù)字圖片,每個(gè)像素包括紅綠藍(lán)三種顏色,q1到q9是這九個(gè)像素中某一種顏色的亮度值,上面的運(yùn)算要對(duì)所有三種顏色重復(fù)進(jìn)行。這些亮度值通常是整數(shù),范圍是0到255,用8個(gè)比特來(lái)表述。
在處理器硬件中,整數(shù)運(yùn)算通常要比浮點(diǎn)運(yùn)算快,功耗也更低。此外,乘法的功耗要比普通的加減法高。只是我們平時(shí)編程的時(shí)候,運(yùn)算操作會(huì)交給編譯器調(diào)用的事先寫好的二進(jìn)制代碼,我們完全感覺(jué)不到底層處理過(guò)程,但所有這些運(yùn)算消耗的電能是實(shí)打?qū)嵉?。要想?jié)約能耗,我們就應(yīng)該盡量減少浮點(diǎn)運(yùn)算,減少乘法,于是上面這個(gè)矩陣乘法可以寫成:
這個(gè)公式中,由于大部分權(quán)重為1,所以不需要做乘法,直接把整數(shù)亮度值累加起來(lái)就可以了。中間權(quán)重為2,相當(dāng)于把對(duì)應(yīng)的整數(shù)q5加兩次,也相當(dāng)于把q5所有的二進(jìn)制比特向左移動(dòng)一位。
最后累加出來(lái)的結(jié)果,我們把它乘以0.1,而不是除以10。這是因?yàn)槌ㄊ撬膭t運(yùn)算中最麻煩的——不僅僅是筆算時(shí)麻煩,在計(jì)算機(jī)中也同樣如此。
有時(shí)候,我們會(huì)把節(jié)約功耗的想法在算法設(shè)計(jì)的過(guò)程中就體現(xiàn)出來(lái),比如像下面這個(gè)公式里的情形。
這個(gè)公式中,整數(shù)亮度值與權(quán)重值的乘法,完全可以通過(guò)移位來(lái)實(shí)現(xiàn),乘以2或4分別相當(dāng)于向左移動(dòng)1位或2位。最后累加結(jié)果需要除以16,可以用向右移動(dòng)4位實(shí)現(xiàn)。
對(duì)大多數(shù)碼農(nóng)來(lái)說(shuō),這類節(jié)約資源與功耗的技巧可有可無(wú),因?yàn)楝F(xiàn)代處理器都比較快,像磨皮這種偶爾用一次的子程序,上述技巧帶來(lái)的好處不很明顯。不過(guò),如果是FPGA等資源有限的硬件環(huán)境,計(jì)算量又非常大的情況下,這種設(shè)計(jì)與實(shí)現(xiàn)算法的技巧就會(huì)有用武之地了。
各位讀者可能會(huì)問(wèn),宅男算出食品單價(jià)了沒(méi)有?別急,我和您說(shuō)過(guò)一篇文章寫不完,欲知后事如何,且聽(tīng)下回分解。
特 別 提 示
1. 進(jìn)入『返樸』微信公眾號(hào)底部菜單“精品專欄“,可查閱不同主題系列科普文章。
2. 『返樸』提供按月檢索文章功能。關(guān)注公眾號(hào),回復(fù)四位數(shù)組成的年份+月份,如“1903”,可獲取2019年3月的文章索引,以此類推。
版權(quán)說(shuō)明:歡迎個(gè)人轉(zhuǎn)發(fā),任何形式的媒體或機(jī)構(gòu)未經(jīng)授權(quán),不得轉(zhuǎn)載和摘編。轉(zhuǎn)載授權(quán)請(qǐng)?jiān)凇阜禈恪刮⑿殴娞?hào)內(nèi)聯(lián)系后臺(tái)。