Python機(jī)器學(xué)習(xí)中一個非常重要的工具包,也就是大名鼎鼎的numpy。
俗話說得好,機(jī)器學(xué)習(xí)要想玩的溜,你可以不會寫Python,但一定不能不會調(diào)庫(大霧)。Numpy可以說是Python中最基礎(chǔ)也是最重要的工具庫了,要用Python做機(jī)器學(xué)習(xí),玩轉(zhuǎn)各種框架,Numpy是必須要會的。像是TensorFlow、pytorch這些知名框架都是基于Numpy進(jìn)行計算的,可想而知它的重要性。
網(wǎng)上關(guān)于Numpy的介紹非常多,但說來說去無非是一個Python中數(shù)值計算的非常重要的基礎(chǔ)包,可以用來很方便地做一些矩陣和大數(shù)據(jù)的運算。
Numpy是做什么的我們很好理解,但是我們可能更加好奇它更深層次的意義究竟是什么?關(guān)于這個問題我們從淺到深不停地追問,可以得到許多不同的答案。
最淺層的回答很簡單,Numpy很方便,計算速度快,可以很方便地進(jìn)行矩陣運算。在Andrew的課程當(dāng)中,他曾經(jīng)演示過,同樣的矩陣運算,如果我們通過Python中的循環(huán)實現(xiàn)速度會比調(diào)用Numpy慢上多達(dá)上百倍。這個差異顯然是非??膳碌摹?/p>
但為什么Numpy會更快呢?
我們追問下去,又會得到一個新的答案。因為Numpy包底層是通過C++實現(xiàn)的,顯然C++運算比Python快得多,所以Numpy自然就更快了。
難道Numpy就只是因為C++更快這么簡單嗎?
這個問題已經(jīng)超越了Numpy本身,我們需要從Python的特性來回答了。Python是一門解釋型語言,也就是說當(dāng)我們執(zhí)行Python的時候,其實是執(zhí)行了一個Python的解釋器。由Python的解釋器來解釋執(zhí)行Python的每一行代碼。
如果我們把解釋器理解成虛擬機(jī),把Python執(zhí)行的代碼理解成虛擬機(jī)當(dāng)中的程序。如果我們虛擬機(jī)多開的話,是很難保證線程安全的。為了解決這個問題,Python設(shè)計了GIL機(jī)制,也就是全局解釋器鎖,它保證了同一時刻最多只有一個解釋器線程在執(zhí)行。
這個機(jī)制保證了線程安全,但是也限制了Python多線程的使用。Python的多線程本質(zhì)上是偽多線程,因為解釋器只有一個線程在跑。所以如果我們想要通過多線程并發(fā)來加速計算的話,這是不可能的。
而矩陣和向量的一些操作是可以通過多線程并發(fā)來加速計算的,而Python本身的特性導(dǎo)致了Python不能執(zhí)行這樣的操作。那么通過Python調(diào)用C++實現(xiàn)的計算庫也就是唯一的選擇了。實際上不僅是Numpy,幾乎所有Python的計算庫,都是通過Python調(diào)用其他語言實現(xiàn)的。Python本身只是最上層的調(diào)用方。
理解了這點除了對于Python可以有更加清晰的認(rèn)識之外,也有助于之后學(xué)習(xí)TensorFlow等其他框架。
Numpy之所以好用,是因為我們可以通過Numpy很方便地創(chuàng)建高維的數(shù)組和矩陣,以及進(jìn)行對應(yīng)的矩陣運算。我們今天先來看看創(chuàng)建的部分。
舉個例子,比如在原生Python當(dāng)中,當(dāng)我們需要創(chuàng)建一個二維數(shù)組的時候,往往需要些很長的定義。比如我們想要一個10 * 10的數(shù)組:
arr = [[0 for _ in range(10)] for _ in range(10)]
但是在Numpy當(dāng)中就會很方便,只需要一行。
import numpy as np
arr = np.zeros((10, 10))
第一行當(dāng)中我們引入了numpy,為了編碼方便,我們將它重新命名成了np。這個是業(yè)內(nèi)慣用做法,幾乎所有使用numpy的程序員都會這么重命名。
在numpy當(dāng)中,存儲高維數(shù)組的對象叫做ndarray,與之對應(yīng)的是存儲矩陣的mat。其實這兩者區(qū)別不大,mat支持矩陣的運算,ndarray基本上也都支持。我們有這么一個印象即可,關(guān)于mat內(nèi)容我們會在之后介紹。
我們創(chuàng)建除了ndarray之后,關(guān)于獲取ndarray基本信息的api大概有下面四個。
第一個是通過.ndim查看ndarray的維度,也就是查看這是一個幾維的數(shù)組:
第二個是通過.shape獲取這個ndarray在各個維度的大?。?/p>
第三個是通過.dtype獲取這個ndarray中元素的類型:
最后一個是tolist()方法,可以將一個ndarray轉(zhuǎn)化成Python原生的list進(jìn)行返回。
那么我們怎么創(chuàng)建numpy中的ndarray呢?
大概也有幾種辦法,首先,既然numpy中的ndarray可以轉(zhuǎn)換成Python原生的list,同樣Python中原生的list也可以轉(zhuǎn)換成numpy中的ndarray。
和轉(zhuǎn)換變量類型的語法很像,我們通過np.array()轉(zhuǎn)換即可。
nums = [1, 3, 4, 6]
arr = np.array(nums)
除了通過Python中原生的list轉(zhuǎn)換,我們還可以根據(jù)自己的需要創(chuàng)建新的ndarray。numpy創(chuàng)建array的方法有很多,我們先來介紹一下其中比較基礎(chǔ)的幾種。
np.arange可以生成一個等差序列,有些類似于Python中原生的range。不過它更加靈活,我們可以只傳入一個整數(shù),它會返回一個從0開始的序列:
np.arange(10)
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
我們也可以指定首尾元素和間隔,numpy會自動幫我們生成一個等差序列:
np.arange(1, 5, 0.5)
array([1. , 1.5, 2. , 2.5, 3. , 3.5, 4. , 4.5])
除此之外,numpy中還提供了ones和zeros兩個api,可以生成全為0和全為1的元素。
np.zeros((3, 4))
array([[0., 0., 0., 0.],
[0., 0., 0., 0.],
[0., 0., 0., 0.]])
np.ones((2, 3))
array([[1., 1., 1.],
[1., 1., 1.]])
我們還可以使用eye或者是identity生成一個N * N的單位矩陣:
np.eye(3)
array([[1., 0., 0.],
[0., 1., 0.],
[0., 0., 1.]])
除此之外,還有一個full的api可以指定shape和數(shù)值,用我們指定的數(shù)值填充出一個指定大小的數(shù)組來:
np.full((3, 4), 3)
array([[3, 3, 3, 3],
[3, 3, 3, 3],
[3, 3, 3, 3]])
但是這個api我們用的不多,因為我們可以用ones生成一個全為1的數(shù)組,然后乘上一個我們想要的值,就等價于full。
另外,ones, zeros, full這幾個api還有一個對應(yīng)的like方法。所謂的like方法就是我們傳入另外一個ndarray代替shape,numpy會根據(jù)這個ndarray的形狀生成一個對應(yīng)形狀的新array。
我們來看個例子吧,首先我們生成一個順序的序列:
ex1 = np.arange(10)
然后我們通過zeros_like方法生成一個同樣大小的全為0的矩陣:
ex2 = np.zeros_like(ex1)
它其實等價于:
np.zeros(ex1.shape)
其他幾個like方法也大同小異,因為可替代性很強(qiáng),所以我也用的不多。
numpy支持的數(shù)據(jù)類型很多,除了常用的int和float之外,還支持復(fù)數(shù)類型的complex,某種程度上來說和golang支持的類型比較接近。
其中int類型一共分為int8,int32,int64和int128,其中每一種又分為帶符號的和不帶符號的。例如int8就是帶符號的8位二進(jìn)制表示的int,而uint8則是不帶符號位的。浮點數(shù)沒有無符號浮點數(shù),一共分為float16,float32,float64和flaot128。
復(fù)數(shù)也有三種,分別是complex64,complex128和complex256。除此之外還有string_和object以及unicode_這三種類型。
我們可以通過調(diào)用astype方法更改ndarray中所有變量的類型:
ex1 = np.arange(10)
ex1.astype(np.float64)
array([0., 1., 2., 3., 4., 5., 6., 7., 8., 9.])
除了人為轉(zhuǎn)換之外,我們還可以在創(chuàng)建的時候通過dtype這個參數(shù)來表示我們想要創(chuàng)建的數(shù)據(jù)的類型,這樣可以避免之后轉(zhuǎn)換的麻煩。
ex1 = np.arange(10, dtype=np.float32)
這篇文章當(dāng)中我們不僅介紹了Numpy的創(chuàng)建的方法,還聊了Python這門語言的一些特性。正是因為Python本身多線程的限制,導(dǎo)致它在需要高并發(fā)計算的場景下性能很差。才會需要通過Python去調(diào)用C++或者是其他語言的底層實現(xiàn)。這也是為什么Python經(jīng)常被稱為膠水語言的原因。
Numpy可以認(rèn)為是Python進(jìn)行機(jī)器學(xué)習(xí)的基礎(chǔ),當(dāng)然除了Numpy之外,像是pandas、matplotlib 以及scikit-learn等庫也是必不可少的。我們會從Numpy開始,一點一點把這些常用的庫都給大家分享一遍。
聯(lián)系客服