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