Numpy簡(jiǎn)介
NumPy 簡(jiǎn)介
Python 很方便脚乡,但也會(huì)很慢。不過俯艰,它允許你訪問執(zhí)行用 C 等語言編寫的代碼的庫(kù)锌订。NumPy 就是這樣一個(gè)庫(kù):它為 Python 中的數(shù)學(xué)運(yùn)算提供了一個(gè)更快速的替代方案辆飘,可以與數(shù)字組高效搭配使用 - 如矩陣蜈项。
NumPy 是一個(gè)很大的庫(kù),我們?cè)谶@里只講一些皮毛侥衬。如果你打算用 Python 進(jìn)行很多的數(shù)學(xué)計(jì)算, 則很有必要花一些時(shí)間閱讀這篇文檔 以了解更多常侦。
導(dǎo)入 NumPy
在導(dǎo)入 NumPy 庫(kù)時(shí)聋亡,大多數(shù)情況下(包括這里)你會(huì)看到的一個(gè)約定际乘,是將其命名為 np,像這樣:
import numpy as np
現(xiàn)在投蝉,你可以給函數(shù)和類型名稱加上前綴 “np” 來使用該庫(kù)征堪,你會(huì)在下面的示例中看到佃蚜。
數(shù)據(jù)類型和形狀
NumPy 中處理數(shù)字的最常見方式是通過 ndarray
對(duì)象。他們與 Python 列表相似熟尉,但是可以有任意數(shù)量的維度斤儿。而且恐锦,ndarray
支持快速的數(shù)學(xué)運(yùn)算一铅,這就是我們想要的。
由于它可以存儲(chǔ)任意數(shù)量的維度飘蚯,你可以使用 ndarray
來表示我們以前涵蓋的任何數(shù)據(jù)類型:標(biāo)量局骤、向量暴凑、矩陣或張量现喳。
標(biāo)量
NumPy 中的標(biāo)量 比 Python 中的標(biāo)量類型更多。不像 Python 只有基本類型 int冰单、float 等诫欠,NumPy 可以讓你指定有符號(hào)和無符號(hào)的類型以及不同的大小。因此轿偎,除了 Python 的 int被廓,你可以使用 uint8嫁乘、int8亦渗、uint16、int16 等類型多律。
這些類型很重要狼荞,因?yàn)槟闼鶆?chuàng)建的每個(gè)對(duì)象(向量帮碰、矩陣殉挽、張量)最終都會(huì)存儲(chǔ)標(biāo)量。而且一死,當(dāng)你創(chuàng)建 NumPy 數(shù)組時(shí)投慈,可以指定類型 - 但是數(shù)組中的每個(gè)項(xiàng)必須具有相同的類型冠骄。在這方面凛辣,NumPy 數(shù)組更像是 C 數(shù)組扁誓,而非 Python 列表阳堕。
如果要?jiǎng)?chuàng)建一個(gè)包含標(biāo)量的 NumPy 數(shù)組,方法是將值傳遞給 NumPy 的 array 函數(shù)肚邢,如下所示:
s = np.array(5)
不過你仍然可以在 ndarray拭卿、NumPy 標(biāo)量和普通的 Python 標(biāo)量之間執(zhí)行數(shù)學(xué)運(yùn)算峻厚,你將在元素級(jí)數(shù)學(xué)運(yùn)算課程中看到惠桃。你可以通過檢查數(shù)組的 shape
屬性來查看數(shù)組的形狀。你可以執(zhí)行代碼:
s.shape
它會(huì)打印出結(jié)果劈狐,即一對(duì)空括號(hào) ()肥缔。 這表示它的維度為零汹来,是標(biāo)量收班。
即使標(biāo)量位于數(shù)組中闺阱,你仍然可以像正常標(biāo)量一樣使用它們。你可以鍵入:
x = s + 3
x 現(xiàn)在會(huì)等于 8瘦穆。如果你檢查 x 的類型扛或,可能會(huì)發(fā)現(xiàn)它是 numpy.int64碘饼,因?yàn)樗谑褂?NumPy 類型,而不是 Python麸锉。
順便說一下花沉,即使標(biāo)量類型也支持大部分的數(shù)組函數(shù)媳握。所以你可以調(diào)用 x.shape蛾找,它會(huì)返回 ()娩脾,因?yàn)樗木S度為零,即使它不是數(shù)組打毛。如果你使用普通的 Python 標(biāo)量來嘗試柿赊,就會(huì)收到一個(gè)錯(cuò)誤。
Vectors
要?jiǎng)?chuàng)建一個(gè)向量隘冲,你可以將 Python 列表傳遞給 array
函數(shù)闹瞧,像這樣:
v = np.array([1,2,3])
如果你檢查向量的shape屬性,它將返回表示向量的一維長(zhǎng)度的單個(gè)數(shù)字展辞。在上面的示例中,v.shape
會(huì)返回(3,)
罗珍。
現(xiàn)在有了數(shù)字洽腺,你可以看到形狀是一個(gè)元組,以及每個(gè) ndarray的維度的大小覆旱。對(duì)于標(biāo)量蘸朋,它只是一個(gè)空的元組,但是向量有一個(gè)維度扣唱,所以元組包含一個(gè)數(shù)字和一個(gè)逗號(hào)藕坯。(Python 不能將 (3)理解為具有一個(gè)項(xiàng)的元組,所以它需要逗號(hào)噪沙。你可以在此 閱讀有關(guān)元組的更多信息)
你也可以使用索引訪問向量中的元素炼彪,如下所示:
x = v[1]
現(xiàn)在 x 等于 2。
NumPy 還支持高級(jí)索引技術(shù)正歼。例如辐马,要從第二個(gè)元素向前訪問項(xiàng)目,你可以這樣說:v[1:]
局义,然后它會(huì)返回?cái)?shù)組[2, 3]
喜爷。NumPy 切片功能非常強(qiáng)大冗疮,允許你訪問 ndarray中的任何項(xiàng)目組合。但它有時(shí)也會(huì)有點(diǎn)復(fù)雜檩帐,你可以在此文檔中閱讀更多詳情术幔。
Matrices
你使用 NumPy 的 array 函數(shù)創(chuàng)建矩陣,跟創(chuàng)建向量一樣轿塔。但是特愿,這次你不只是傳入一個(gè)列表,而是提供列表的列表勾缭,其中每個(gè)列表代表一行。所以要?jiǎng)?chuàng)建一個(gè)包含數(shù)字 1 到 9 的 3x3 矩陣目养,你可以這樣做:
m = np.array([[1,2,3], [4,5,6], [7,8,9]])
檢查它的 shape屬性將返回元組 (3, 3)俩由,表示它有兩個(gè)維度,每個(gè)的長(zhǎng)度為 3癌蚁。你可以像向量一樣訪問矩陣的元素幻梯,但要使用額外的索引值。所以要在上面的矩陣中找到數(shù)字 6努释,你可以訪問 m[1][2]碘梢。
張量
張量與向量和矩陣一樣,但它們可以有更多的維度伐蒂。例如煞躬,要?jiǎng)?chuàng)建一個(gè) 3x3x2x1 的張量,你可以這樣做:
t = np.array([[[[1],[2]],[[3],[4]],[[5],[6]]],[[[7],[8]],\ [[9],[10]],[[11],[12]]],[[[13],[14]],[[15],[16]],[[17],[17]]]])
而t.shape
會(huì)返回 (3, 3, 2, 1)
逸邦。
你可以像在矩陣中一樣訪問項(xiàng)目恩沛,但需要使用更多的索引。所以 t[2][1][1][0]
將返回 16
缕减。
更改形狀
有時(shí)雷客,你需要更改數(shù)據(jù)的形狀,而無需實(shí)際更改其內(nèi)容桥狡。例如搅裙,你可能有一個(gè)一維的向量,但是需要一個(gè)二維的矩陣裹芝。實(shí)現(xiàn)它的方式有兩種部逮。
假設(shè)你有以下向量:
v = np.array([1,2,3,4])
調(diào)用 v.shape
會(huì)返回(4,)
。但要是你想得到一個(gè) 1x4 矩陣呢局雄?你可以使用 reshape 函數(shù)甥啄,就像這樣:
x = v.reshape(1,4)
調(diào)用 x.shape
會(huì)返回 (1,4)
。如果你想獲得一個(gè) 4x1 矩陣炬搭,可以這樣做:
x = v.reshape(4,1)
reshape函數(shù)不只是添加大小為 1 的維度蜈漓。查閱此文檔 了解更多示例穆桂。
關(guān)于更改 NumPy 數(shù)組的形狀還有一點(diǎn):如果你看看有經(jīng)驗(yàn)的 NumPy 用戶的代碼,經(jīng)常會(huì)看到他們使用一種特殊的切片語法融虽,而不是調(diào)用 reshape享完。使用該語法,前面的兩個(gè)示例會(huì)是這樣的:x = v[None, :]
or x = v[:, None]
這些行創(chuàng)建一個(gè)切片有额,查看 v的所有項(xiàng)目般又,但要求 NumPy 為相關(guān)軸添加大小為 1 的新維度。你現(xiàn)在看起來可能覺得很奇怪巍佑,但它是一種常見的技術(shù)茴迁,所以了解它沒什么壞處。
Numpy元素級(jí)運(yùn)算
Python 中的方式
假設(shè)你有一個(gè)數(shù)字列表萤衰,你想向列表中的每一項(xiàng)加上 5 堕义。如果沒有 NumPy,你可以像下面這樣做:
values = [1,2,3,4,5]
for i in range(len(values)):
values[i] += 5
# 現(xiàn)在的值為 [6,7,8,9,10]
這是講得通的脆栋,但是你要編寫很多代碼倦卖,而且因?yàn)樗羌?Python,所以運(yùn)行的很慢椿争。
NumPy 中的方式
在 NumPy 中怕膛,我們可以這么做:
values = [1,2,3,4,5]
values = np.array(values) + 5
# 現(xiàn)在值是包含 [6,7,8,9,10] 的一個(gè) ndarray
創(chuàng)建該數(shù)組可能看起來很奇怪,但通常你總是要將數(shù)據(jù)存儲(chǔ)在 ndarray 中的秦踪。所以如果你已經(jīng)有一個(gè)名為 values
的 ndarray褐捻,你可以這么做:
values += 5
我們應(yīng)該指出,NumPy 實(shí)際上有用于添加洋侨、乘法等的函數(shù)舍扰。但它也支持使用標(biāo)準(zhǔn)的數(shù)學(xué)運(yùn)算符。 所以以下兩行是等價(jià)的:
x = np.multiply(some_array, 5)
x = some_array * 5
我們通常會(huì)使用運(yùn)算符而不是函數(shù)希坚,因?yàn)樗鼈兏奖沔I入边苹,也更容易閱讀,不過這只是個(gè)人偏好裁僧。
再看一個(gè)使用標(biāo)量和 ndarrays 進(jìn)行運(yùn)算的例子个束。假設(shè)你有一個(gè)矩陣 m 并且你想重用它,但首先你需要將其所有值設(shè)為零聊疲。這很簡(jiǎn)單茬底,只需給它乘以零,并將結(jié)果分配回矩陣就行了获洲,如下所示:
m *= 0
# 現(xiàn)在 m 中的每個(gè)元素都是 0阱表,無論它有多少維度
元素級(jí)矩陣運(yùn)算
與標(biāo)量和矩陣一起使用的相同函數(shù)和運(yùn)算符也適用于其他維度。你只需要確保執(zhí)行運(yùn)算的項(xiàng)目具有兼容的形狀。
假設(shè)你想得到矩陣的平方值最爬。方法是 x = m * m
(或者如果你要將值分配回 m涉馁,則是 m *= m
)
這是可以運(yùn)作的,因?yàn)樗莾蓚€(gè)形狀相同的矩陣之間的元素乘法爱致。(在這個(gè)例子中烤送,它們的形狀相同,是因?yàn)樗鼈儗?shí)際上是相同的對(duì)象糠悯。)
看一個(gè)示例:
a = np.array([[1,3],[5,7]])
a
# 顯示以下結(jié)果:
# array([[1, 3],
# [5, 7]])
b = np.array([[2,4],[6,8]])
b
# 顯示以下結(jié)果:
# array([[2, 4],
# [6, 8]])
a + b
# 顯示以下結(jié)果:
# array([[ 3, 7],
# [11, 15]])
如果您嘗試使用不兼容的形狀帮坚,就像視頻中的另一個(gè)示例一樣,你會(huì)收到一個(gè)錯(cuò)誤:
a = np.array([[1,3],[5,7]])
a
# 顯示以下結(jié)果:
# array([[1, 3],
# [5, 7]])
c = np.array([[2,3,6],[4,5,9],[1,8,7]])
c
# 顯示以下結(jié)果:
# array([[2, 3, 6],
# [4, 5, 9],
# [1, 8, 7]])
a.shape
# 顯示以下結(jié)果:
# (2, 2)
c.shape
# 顯示以下結(jié)果:
# (3, 3)
a + c
# 顯示以下結(jié)果:
# ValueError: operands could not be broadcast together with shapes (2,2) (3,3)
你會(huì)在后面的介紹中詳細(xì)了解“不能一起廣播”的意義互艾,現(xiàn)在只需注意這兩種形狀是不同的试和,因此我們無法執(zhí)行元素級(jí)運(yùn)算。
Numpy矩陣乘法
元素級(jí)乘法
你已看過了一些元素級(jí)乘法忘朝。你可以使用multiply
函數(shù)或 *
運(yùn)算符來實(shí)現(xiàn)灰署。回顧一下局嘁,它看起來是這樣的,實(shí)現(xiàn)元素對(duì)應(yīng)位置相乘:
m = np.array([[1,2,3],[4,5,6]])
m
# 顯示以下結(jié)果:
# array([[1, 2, 3],
# [4, 5, 6]])
n = m * 0.25
n
# 顯示以下結(jié)果:
# array([[ 0.25, 0.5 , 0.75],
# [ 1. , 1.25, 1.5 ]])
m * n
# 顯示以下結(jié)果:
# array([[ 0.25, 1. , 2.25],
# [ 4. , 6.25, 9. ]])
np.multiply(m, n)
# 相當(dāng)于 m * n
# 顯示以下結(jié)果:
# array([[ 0.25, 1. , 2.25],
# [ 4. , 6.25, 9. ]])
矩陣乘積
要獲得矩陣乘積晦墙,你可以使用 NumPy 的 matmul 函數(shù)悦昵。
如果你有兼容的形狀,那就像這樣簡(jiǎn)單:
a = np.array([[1,2,3,4],[5,6,7,8]])
a
# 顯示以下結(jié)果:
# array([[1, 2, 3, 4],
# [5, 6, 7, 8]])
a.shape
# 顯示以下結(jié)果:
# (2, 4)
b = np.array([[1,2,3],[4,5,6],[7,8,9],[10,11,12]])
b
# 顯示以下結(jié)果:
# array([[ 1, 2, 3],
# [ 4, 5, 6],
# [ 7, 8, 9],
# [10, 11, 12]])
b.shape
# 顯示以下結(jié)果:
# (4, 3)
c = np.matmul(a, b)
c
# 顯示以下結(jié)果:
# array([[ 70, 80, 90],
# [158, 184, 210]])
c.shape
# 顯示以下結(jié)果:
# (2, 3)
如果您的矩陣具有不兼容的形狀晌畅,則會(huì)出現(xiàn)以下錯(cuò)誤:
np.matmul(b, a)
# 顯示以下錯(cuò)誤:
# ValueError: shapes (4,3) and (2,4) not aligned: 3 (dim 1) != 2 (dim 0)
NumPy 的 dot 函數(shù)
有時(shí)候但指,在你以為用 matmul函數(shù)的地方,可能會(huì)看到 NumPy 的 dot 函數(shù)抗楔。事實(shí)證明棋凳,如果矩陣是二維的,那么 dot和 matmul函數(shù)的結(jié)果是相同的连躏。
所以這兩個(gè)結(jié)果是等價(jià)的:
a = np.array([[1,2],[3,4]])
a
# 顯示以下結(jié)果:
# array([[1, 2],
# [3, 4]])
np.dot(a,a)
# 顯示以下結(jié)果:
# array([[ 7, 10],
# [15, 22]])
a.dot(a)
# 你可以直接對(duì) `ndarray` 調(diào)用 `dot`
# 顯示以下結(jié)果:
# array([[ 7, 10],
# [15, 22]])
np.matmul(a,a)
# array([[ 7, 10],
# [15, 22]])
雖然這些函數(shù)對(duì)于二維數(shù)據(jù)返回相同的結(jié)果剩岳,但在用于其他數(shù)據(jù)形狀時(shí),你應(yīng)該謹(jǐn)慎選擇要使用哪種入热。你可以在 matmul 和 dot文檔中詳細(xì)了解它們的差異拍棕,并找到其他 NumPy 函數(shù)的鏈接。
矩陣轉(zhuǎn)置
轉(zhuǎn)置
在 NumPy 中獲得矩陣的轉(zhuǎn)置非常容易勺良。只需訪問其“T”屬性即可绰播。有一個(gè) transpose() 函數(shù)也可以返回同樣的結(jié)果,但是你很少看到它在任何地方使用尚困,因?yàn)檩斎?T 的方法要簡(jiǎn)單得多蠢箩。 :)
例如:
m = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]])
m
# 顯示以下結(jié)果:
# array([[ 1, 2, 3, 4],
# [ 5, 6, 7, 8],
# [ 9, 10, 11, 12]])
m.T
# 顯示以下結(jié)果:
# array([[ 1, 5, 9],
# [ 2, 6, 10],
# [ 3, 7, 11],
# [ 4, 8, 12]])
NumPy 在進(jìn)行轉(zhuǎn)置時(shí)不實(shí)際移動(dòng)內(nèi)存中的任何數(shù)據(jù)——只是改變對(duì)原始矩陣的索引方式,所以是非常高效的。
修改轉(zhuǎn)置矩陣會(huì)改變?cè)季仃嚨闹?/strong>
但是谬泌,這也意味著你也要特別注意修改對(duì)象的方式滔韵,因?yàn)樗鼈児蚕硐嗤臄?shù)據(jù)。例如呵萨,對(duì)于上面同一個(gè)矩陣 m
奏属,我們來創(chuàng)建一個(gè)新的變量 m_t
來存儲(chǔ) m
的轉(zhuǎn)置。然后看看如果我們修改 m_t
中的值潮峦,會(huì)發(fā)生什么:
m_t = m.T
m_t[3][1] = 200
m_t
# 顯示以下結(jié)果:
# array([[ 1, 5, 9],
# [ 2, 6, 10],
# [ 3, 7, 11],
# [ 4, 200, 12]])
m
# 顯示以下結(jié)果:
# array([[ 1, 2, 3, 4],
# [ 5, 6, 7, 200],
# [ 9, 10, 11, 12]])
注意它是如何修改轉(zhuǎn)置和原始矩陣的囱皿!這是因?yàn)樗鼈?strong>共享相同的數(shù)據(jù)副本。所以記住忱嘹,將轉(zhuǎn)置視為矩陣的不同視圖嘱腥,而不是完全不同的矩陣。
實(shí)際用例
假設(shè)你有以下兩個(gè)矩陣拘悦,稱為“inputs”和“weights”齿兔,
inputs = np.array([[-0.27, 0.45, 0.64, 0.31]])
inputs
# 顯示以下結(jié)果:
# array([[-0.27, 0.45, 0.64, 0.31]])
inputs.shape
# 顯示以下結(jié)果:
# (1, 4)
weights = np.array([[0.02, 0.001, -0.03, 0.036], \
[0.04, -0.003, 0.025, 0.009], [0.012, -0.045, 0.28, -0.067]])
weights
# 顯示以下結(jié)果:
# array([[ 0.02 , 0.001, -0.03 , 0.036],
# [ 0.04 , -0.003, 0.025, 0.009],
# [ 0.012, -0.045, 0.28 , -0.067]])
weights.shape
# displays the following result:
# (3, 4)
我在這里不會(huì)講解它們的用途,因?yàn)槟闵院蠖紩?huì)學(xué)到础米,但是最終你會(huì)想要獲得這兩個(gè)矩陣的矩陣乘積分苇。
如果你像現(xiàn)在這樣去嘗試,會(huì)獲得一個(gè)錯(cuò)誤:
np.matmul(inputs, weights)
# 顯示以下錯(cuò)誤:
# ValueError: shapes (1,4) and (3,4) not aligned: 4 (dim 1) != 3 (dim 0)
如果你上了矩陣乘法課屁桑,那應(yīng)該見過這個(gè)錯(cuò)誤医寿。它報(bào)告說形狀不兼容,因?yàn)樽筮吘仃嚨牧袛?shù) 4 不等于右邊矩陣的行數(shù) 3蘑斧。
所以有問題靖秩,但是注意,如果你獲取 weights 矩陣的轉(zhuǎn)置竖瘾,它會(huì):
np.matmul(inputs, weights.T)
# 顯示以下結(jié)果:
# array([[-0.01299, 0.00664, 0.13494]])
如果你獲取 inputs 的轉(zhuǎn)置沟突,并調(diào)換它們的順序也可以,就像我們?cè)谝曨l中展示的那樣:
np.matmul(weights, inputs.T)
# 顯示以下結(jié)果:
# array([[-0.01299],#
# [ 0.00664],
# [ 0.13494]])
這兩個(gè)答案是彼此的轉(zhuǎn)置捕传,所以你使用的乘法只取決于你想要的輸出的形狀惠拭。