標籤:

深度學習中的Python語言2:Numpy數組、索引、數據類型、運算、廣播的介紹

文章目錄:

  1. 數組(Arrays)
  2. 數組索引(Array indexing)
  3. 數據類型(Datatypes)
  4. 數組運算(Array math)
  5. 廣播(Broadcasting)

注意,該文章不是大而全的python使用說明,如果你想要的話可以去python官方文檔查看。

此文章的重點在於,介紹一些基本概念和方法,理解這些概念在深度學習中的應用。

下面是正文:

1. 數組(Arrays)

先介紹一下Numpy。

Numpy是Python語言用於科學計算的核心庫之一。

想要使用Numpy庫的話,只需要在Python代碼的開頭引用該庫即可:

import numpy as npn

這行代碼的意思是引用numpy庫,別稱是np,也就是說下面你可以使用np代替numpy。

數組是『網格化』的值的集合,也就是說,數組是多維的,如果說列表(list)可以用於表示向量的話(請參考這裡),數組可以用於表示矩陣(二維數組)。這在機器學習中非常常用,一定要記住!!!重要的內容要加粗!!!

import numpy as npnna = np.array([1, 2, 3]) # Create a rank 1 arraynprint(type(a)) # Prints "<class numpy.ndarray>"nprint(a.shape) # Prints "(3,)"nprint(a[0], a[1], a[2]) # Prints "1 2 3"na[0] = 5 # Change an element of the arraynprint(a) # Prints "[5, 2, 3]"nnb = np.array([[1,2,3],[4,5,6]]) # Create a rank 2 arraynprint(b.shape) # Prints "(2, 3)"nprint(b[0, 0], b[0, 1], b[1, 0]) # Prints "1 2 4"n

注意以下的代碼:

b = np.array([[1,2,3],[4,5,6]])n

這裡創建二維數組要用中括弧把兩個列表都包括進來。而不是b = np.array([1,2,3],[4,5,6])

注意,這裡創建數組用的是括弧(),裡面的元素是列表[ ],這在數學上也能對應起來了,向量組成矩陣。

另外,數組裡的維度也叫做rank。

numpy提供可很多創建數組的方法:

import numpy as npnna = np.zeros((2,2)) # Create an array of all zerosnprint(a) # Prints "[[ 0. 0.]n # [ 0. 0.]]"nnb = np.ones((1,2)) # Create an array of all onesnprint(b) # Prints "[[ 1. 1.]]"nnc = np.full((2,2), 7) # Create a constant arraynprint(c) # Prints "[[ 7. 7.]n # [ 7. 7.]]"nnd = np.eye(2) # Create a 2x2 identity matrixnprint(d) # Prints "[[ 1. 0.]n # [ 0. 1.]]"nne = np.random.random((2,2)) # Create an array filled with random valuesnprint(e) # Might print "[[ 0.91940167 0.08143941]n # [ 0.68744134 0.87236687]]"n

其他創建數組的方法見這裡。

2. 數組索引(Array indexing)

另外一個重要的就是數組索引,這裡先介紹第一種方法:

切片(slicing),這裡的切片和列表的切片方式一樣,不同的是,由於數組是多維的,因此需要對不同維度分別切片。

import numpy as npnn# Create the following rank 2 array with shape (3, 4)n# [[ 1 2 3 4]n# [ 5 6 7 8]n# [ 9 10 11 12]]na = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]])nn# Use slicing to pull out the subarray consisting of the first 2 rowsn# and columns 1 and 2; b is the following array of shape (2, 2):n# [[2 3]n# [6 7]]nb = a[:2, 1:3]nn# A slice of an array is a view into the same data, so modifying itn# will modify the original array.nprint(a[0, 1]) # Prints "2"nb[0, 0] = 77 # b[0, 0] is the same piece of data as a[0, 1]nprint(a[0, 1]) # Prints "77"n

上面的代碼解釋一下:

首先,array的索引是從0開始,所以[:2]指的是從0到2,但是不包括2,也就是第0行和第1行。

同理,[1:3]指的是1到3,但是不包括3,也就第1列和第2列(前面還有一個第0列,這裡沒有索引)。

另外需要說明的是,如果b是通過切片的方式得到的a的子數組,其實在內存中,b只是指向a中對應的元素。因此改變b的值,a的值也會跟著改變。這點非常重要!!!

再來看下面一段代碼。

import numpy as npnn# Create the following rank 2 array with shape (3, 4)n# [[ 1 2 3 4]n# [ 5 6 7 8]n# [ 9 10 11 12]]na = np.array([[1,2,3,4], [5,6,7,8], [9,10,11,12]])nn# Two ways of accessing the data in the middle row of the array.n# Mixing integer indexing with slices yields an array of lower rank,n# while using only slices yields an array of the same rank as then# original array:nrow_r1 = a[1, :] # Rank 1 view of the second row of anrow_r2 = a[1:2, :] # Rank 2 view of the second row of anprint(row_r1, row_r1.shape) # Prints "[5 6 7 8] (4,)"nprint(row_r2, row_r2.shape) # Prints "[[5 6 7 8]] (1, 4)"nn# We can make the same distinction when accessing columns of an array:ncol_r1 = a[:, 1]ncol_r2 = a[:, 1:2]nprint(col_r1, col_r1.shape) # Prints "[ 2 6 10] (3,)"nprint(col_r2, col_r2.shape) # Prints "[[ 2]n # [ 6]n # [10]] (3, 1)"n

這段代碼解釋了「用切片和整數混合的方式進行索引」和「用索引的方式進行索引」的區別:

也就是a[1, :]和a[1:2, :]的區別,很明顯,這裡1是整數方式,「:」是索引方式。

這個區別可以用一句話概括:用整數方式有可能得到降維的數組,而用切片方式得到的數組永遠是原數組的子數組。

下面介紹使用整數數組對另一個數組進行索引。

import numpy as npnna = np.array([[1,2], [3, 4], [5, 6]])nn# An example of integer array indexing.n# The returned array will have shape (3,) andnprint(a[[0, 1, 2], [0, 1, 0]]) # Prints "[1 4 5]"nn# The above example of integer array indexing is equivalent to this:nprint(np.array([a[0, 0], a[1, 1], a[2, 0]])) # Prints "[1 4 5]"nn# When using integer array indexing, you can reuse the samen# element from the source array:nprint(a[[0, 0], [1, 1]]) # Prints "[2 2]"nn# Equivalent to the previous integer array indexing examplenprint(np.array([a[0, 1], a[0, 1]])) # Prints "[2 2]"n

也就是說,[0, 1, 2]是一個數組,用它來索引數組a。

這樣得到的是一個新的數組,也就是說改變這個新的數組的值是不會影響原來的數組的。

再來看一個例子:

import numpy as npnn# Create a new array from which we will select elementsna = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])nnprint(a) # prints "array([[ 1, 2, 3],n # [ 4, 5, 6],n # [ 7, 8, 9],n # [10, 11, 12]])"nn# Create an array of indicesnb = np.array([0, 2, 0, 1])nn# Select one element from each row of a using the indices in bnprint(a[np.arange(4), b]) # Prints "[ 1 6 7 11]"nn# Mutate one element from each row of a using the indices in bna[np.arange(4), b] += 10nnprint(a) # prints "array([[11, 2, 3],n # [ 4, 5, 16],n # [17, 8, 9],n # [10, 21, 12]])n

這是用整數數組進行索引的另一個例子,還可以用整數數組給原數組的元素加上一個數值。

最後介紹一下布爾型數組索引的例子:

import numpy as npnna = np.array([[1,2], [3, 4], [5, 6]])nnbool_idx = (a > 2) # Find the elements of a that are bigger than 2;n # this returns a numpy array of Booleans of the samen # shape as a, where each slot of bool_idx tellsn # whether that element of a is > 2.nnprint(bool_idx) # Prints "[[False False]n # [ True True]n # [ True True]]"nn# We use boolean array indexing to construct a rank 1 arrayn# consisting of the elements of a corresponding to the True valuesn# of bool_idxnprint(a[bool_idx]) # Prints "[3 4 5 6]"nn# We can do all of the above in a single concise statement:nprint(a[a > 2]) # Prints "[3 4 5 6]"n

這是一個非常常用的方法,用於選出數組中符合某種特定條件的元素。

更多的資料請點擊這裡。

3. 數據類型(Datatypes)

首先,數組是具有相同數據類型的數據的集合。

數組的數據類型(Datatypes)在創建數組時已經自動指定了,但是我們也可以強迫數組使用我們指定的數據類型。見下面的代碼例子。

import numpy as npnnx = np.array([1, 2]) # Let numpy choose the datatypenprint(x.dtype) # Prints "int64"nnx = np.array([1.0, 2.0]) # Let numpy choose the datatypenprint(x.dtype) # Prints "float64"nnx = np.array([1, 2], dtype=np.int64) # Force a particular datatypenprint(x.dtype) # Prints "int64"n

更多的資料看這裡。

4. 數組運算(Array math)

基礎的數學運算都是元素運算(elementwise),無論是使用重載的運算符還是函數。

import numpy as npnnx = np.array([[1,2],[3,4]], dtype=np.float64)ny = np.array([[5,6],[7,8]], dtype=np.float64)nn# Elementwise sum; both produce the arrayn# [[ 6.0 8.0]n# [10.0 12.0]]nprint(x + y)nprint(np.add(x, y))nn# Elementwise difference; both produce the arrayn# [[-4.0 -4.0]n# [-4.0 -4.0]]nprint(x - y)nprint(np.subtract(x, y))nn# Elementwise product; both produce the arrayn# [[ 5.0 12.0]n# [21.0 32.0]]nprint(x * y)nprint(np.multiply(x, y))nn# Elementwise division; both produce the arrayn# [[ 0.2 0.33333333]n# [ 0.42857143 0.5 ]]nprint(x / y)nprint(np.divide(x, y))nn# Elementwise square root; produces the arrayn# [[ 1. 1.41421356]n# [ 1.73205081 2. ]]nprint(np.sqrt(x))n

也就是說,無論是使用運算符:+、-、*、/,還是使用函數 np.add, np.subtract, np.multiply, np.divide都是對矩陣對應位置的元素進行運算。

計算向量內積、矩陣與向量相乘、矩陣相乘均可以使用函數np.dot。

import numpy as npnnx = np.array([[1,2],[3,4]])ny = np.array([[5,6],[7,8]])nnv = np.array([9,10])nw = np.array([11, 12])nn# Inner product of vectors; both produce 219nprint(v.dot(w))nprint(np.dot(v, w))nn# Matrix / vector product; both produce the rank 1 array [29 67]nprint(x.dot(v))nprint(np.dot(x, v))nn# Matrix / matrix product; both produce the rank 2 arrayn# [[19 22]n# [43 50]]nprint(x.dot(y))nprint(np.dot(x, y))n

np.sum是非常常用的函數之一,可以方便的對行或列進行求和:

import numpy as npnnx = np.array([[1,2],[3,4]])nnprint(np.sum(x)) # Compute sum of all elements; prints "10"nprint(np.sum(x, axis=0)) # Compute sum of each column; prints "[4 6]"nprint(np.sum(x, axis=1)) # Compute sum of each row; prints "[3 7]"n

常用數學計算函數列表見官網。

轉置的話在python中可以非常方便:

import numpy as npnnx = np.array([[1,2], [3,4]])nprint(x) # Prints "[[1 2]n # [3 4]]"nprint(x.T) # Prints "[[1 3]n # [2 4]]"nn# Note that taking the transpose of a rank 1 array does nothing:nv = np.array([1,2,3])nprint(v) # Prints "[1 2 3]"nprint(v.T) # Prints "[1 2 3]"n

python提供了大量用於操作矩陣的函數,非常方便,見官網。

5. 廣播(Broadcasting)

python中的『廣播』是一個稍微複雜一點的概念,需要多加練習,加強理解。

但是一旦學會使用『廣播』,這將是一個強大的工具,它能夠幫助我們完成大量重複工作,化簡為繁,也是深度學習演算法中向量化的重要步驟!

『廣播』可以用來計算不同維度的矩陣之間的計算問題,一般用來解決這樣的問題:我們有一個大矩陣和一個小矩陣,我們想用小矩陣對大矩陣進行多次重複的操作。

舉例說明:我們想對矩陣的每一列加上一個向量,常規方法是這樣操作:

import numpy as npnn# We will add the vector v to each row of the matrix x,n# storing the result in the matrix ynx = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])nv = np.array([1, 0, 1])ny = np.empty_like(x) # Create an empty matrix with the same shape as xnn# Add the vector v to each row of the matrix x with an explicit loopnfor i in range(4):n y[i, :] = x[i, :] + vnn# Now y is the followingn# [[ 2 2 4]n# [ 5 5 7]n# [ 8 8 10]n# [11 11 13]]nprint(y)n

當然,上述方法是可行的,但是,當矩陣x非常大時,利用循環進行運算將耗時耗力。

一個巧妙的方法是,我們將向量v複製並擴展成vv,vv的行數與x相同,上述操作等同於x與vv的對應元素相加。用代碼表示就是:

import numpy as npnn# We will add the vector v to each row of the matrix x,n# storing the result in the matrix ynx = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])nv = np.array([1, 0, 1])nvv = np.tile(v, (4, 1)) # Stack 4 copies of v on top of each othernprint(vv) # Prints "[[1 0 1]n # [1 0 1]n # [1 0 1]n # [1 0 1]]"ny = x + vv # Add x and vv elementwisenprint(y) # Prints "[[ 2 2 4n # [ 5 5 7]n # [ 8 8 10]n # [11 11 13]]"n

利用numpy中的『廣播』可以幫我們節省創建vv的過程:

import numpy as npnn# We will add the vector v to each row of the matrix x,n# storing the result in the matrix ynx = np.array([[1,2,3], [4,5,6], [7,8,9], [10, 11, 12]])nv = np.array([1, 0, 1])ny = x + v # Add v to each row of x using broadcastingnprint(y) # Prints "[[ 2 2 4]n # [ 5 5 7]n # [ 8 8 10]n # [11 11 13]]"n

也就是說,numpy自動將v複製並擴展為(4,3)的矩陣並與x相加,卻不需要我們做任何事,僅僅使用『+』即可!

『廣播』更加通用的規則見官網。


推薦閱讀:

Python實現爬蟲代理池?
如果只推介一本python3的書籍,你會推介哪一本?
抓取1400篇Python文章後的故事(內附高質量Python文章推薦)

TAG:Python |