NumPy - 使用陣列進行效能最佳化



使用陣列進行效能最佳化

使用陣列進行效能最佳化包括提高陣列操作的效率,例如減少計算時間和記憶體使用。

我們應該出於以下原因最佳化效能:

  • 速度: 更快的計算速度帶來更快的結果和更靈敏的應用程式。
  • 可擴充套件性: 最佳化的程式碼可以有效地處理更大的資料集和更復雜的操作。
  • 資源效率: 減少記憶體使用和計算開銷。

使用向量化運算

向量化運算指的是能夠一步執行整個陣列或矩陣上的運算,而無需使用顯式迴圈。

這是透過廣播和內部最佳化實現的,使這些運算更快、更高效。

示例

在以下示例中,我們使用 NumPy 的陣列運算對兩個大型陣列“a”和“b”執行向量化加法。此運算計算陣列的元素級和並將結果儲存在新陣列“c”中:

import numpy as np

# Create two large arrays
a = np.random.rand(1000000)
b = np.random.rand(1000000)

# Vectorized addition
c = a + b
print (c)

以下是獲得的輸出:

[0.91662816 0.65486861 1.60409272 ... 0.95122935 1.12795861 0.15812103]

利用高效的資料型別

為陣列選擇合適的資料型別對於最佳化 NumPy 中的效能和記憶體使用非常重要。

例如,使用np.float32而不是np.float64可以顯著影響記憶體使用和效能,尤其是在處理大型資料集時。

在 NumPy 中,資料型別(或 dtype)定義了陣列儲存的元素型別以及儲存每個元素所需的儲存空間。

示例

在此示例中,我們演示了透過建立具有雙精度(64 位)浮點數的陣列,然後使用 astype() 方法將其轉換為單精度(32 位)來使用精度更改:

import numpy as np

# Create an array with double precision (64-bit)
arr_double = np.array([1.0, 2.0, 3.0], dtype=np.float64)

# Print the original double precision array
print("Original double precision array:")
print(arr_double)
print("Data type:", arr_double.dtype)

# Convert to single precision (32-bit)
arr_single = arr_double.astype(np.float32)

# Print the converted single precision array
print("\nConverted single precision array:")
print(arr_single)
print("Data type:", arr_single.dtype)

這將產生以下結果:

Original double precision array:
[1. 2. 3.]
Data type: float64

Converted single precision array:
[1. 2. 3.]
Data type: float32

避免使用 NumPy 函式的迴圈

在 NumPy 中,主要優勢之一是可以避免使用內建函式和陣列運算顯式迴圈。這種方法通常稱為向量化。

透過使用 NumPy 函式,您可以一次對整個陣列執行運算,這與使用迴圈相比更加簡潔。

示例

在下面的示例中,我們使用 np.mean() 函式計算陣列元素的平均值,而無需使用任何顯式迴圈:

import numpy as np

# Create an array
arr = np.array([1, 2, 3, 4, 5])

# Calculate the mean of array elements
mean = np.mean(arr)
print("mean:",mean)  

以下是上述程式碼的輸出:

mean: 3.0

使用廣播進行向量化

廣播指的是能夠對形狀不同的陣列執行元素級運算。它遵循一組規則來確定如何對形狀不同的陣列進行對齊以進行運算:

  • 相同維度: 如果陣列具有不同的維度,則較小陣列的形狀在左側填充 1,直到兩個形狀具有相同的長度。
  • 維度相容性: 當兩個維度相等或其中一個為 1 時,它們是相容的。對於每個維度,如果大小不同,並且如果它們都不為 1,則廣播失敗。
  • 拉伸: 尺寸為 1 的陣列沿該尺寸拉伸以匹配另一個數組尺寸的大小。

示例

在以下示例中,我們廣播“array_1d”以匹配“array_2d”的形狀,從而允許元素級加法:

import numpy as np

# Create a 2D array and a 1D array
array_2d = np.array([[1, 2, 3], [4, 5, 6]])
array_1d = np.array([10, 20, 30])

# Add the 1D array to each row of the 2D array
result = array_2d + array_1d
print(result)

獲得的輸出如下所示:

[[11 22 33]
 [14 25 36]]

就地運算用於向量化

NumPy 中的就地運算指的是直接修改陣列的資料,而無需建立新陣列來儲存結果,從而節省記憶體並提高效能。

這是透過使用修改原始陣列內容的運算子和函式來實現的。這些運算通常使用帶就地字尾的運算子(例如,+=、-=、*=、/=)或支援就地修改的函式。

示例:使用就地運算子

在此示例中,我們直接對陣列應用算術運算“+=”而無需建立新的陣列:

import numpy as np

# Create an array
arr = np.array([1, 2, 3, 4, 5])

# Add 10 to each element in-place
arr += 10
print(arr)  

執行上述程式碼後,我們得到以下輸出:

[11 12 13 14 15]

示例:使用就地函式

在這裡,我們使用 NumPy exp() 函式就地計算陣列中每個元素的指數值:

import numpy as np

# Create an array with a floating-point data type
arr = np.array([1, 2, 3, 4, 5], dtype=np.float64)

# Compute the exponential of each element in-place
np.exp(arr, out=arr)
print(arr)

執行上述程式碼後,我們得到以下輸出:

[  2.71828183   7.3890561   20.08553692  54.59815003 148.4131591 ]

使用記憶體檢視進行向量化

記憶體檢視指的是訪問或檢視陣列中相同底層資料的不同方式,而無需複製它。此概念允許您建立陣列的不同“檢視”或“切片”,這些檢視可以以各種方式操作相同的資料:

  • 切片: 當您切片陣列時,NumPy 會建立原始陣列的檢視,而不是副本。此檢視共享相同的記憶體緩衝區,因此對檢視的更改會影響原始陣列,反之亦然。
  • 重塑: 重塑陣列會建立具有不同形狀的相同資料的新的檢視。這不會更改底層資料,但會更改其解釋方式。

示例:切片

在下面的示例中,我們建立了一個二維 NumPy 陣列和原始陣列的檢視(切片)。修改檢視也會影響原始陣列:

import numpy as np

# Create a 2D array
arr = np.array([[1, 2, 3], [4, 5, 6]])

# Create a view (slice) of the original array
view = arr[:, 1:]

# Modify the view
view[0, 0] = 99

print(arr)

我們得到如下所示的輸出:

[[ 1 99  3]
 [ 4  5  6]]

示例:重塑

在這裡,我們使用 arange() 函式建立一個一維 NumPy 陣列,然後將其重塑為一個具有 3 行 4 列的二維陣列,更改其結構同時保留原始資料:

import numpy as np

# Create a 1D array
arr = np.arange(12)

# Reshape to a 2D array
reshaped = arr.reshape((3, 4))

print(reshaped)

我們得到如下所示的輸出:

[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

使用步長進行向量化

步長是一個元組,指示遍歷陣列時每個維度要步進的位元組數。它們確定如何在記憶體中訪問陣列元素,從而深入瞭解資料的佈局和訪問方式。

步長為您提供每個維度的記憶體偏移量。例如,在二維陣列中,第二個維度的步長告訴您在記憶體中移動多少位元組才能訪問該行中的下一個元素。

示例

在以下示例中,我們建立了一個二維 NumPy 陣列,並使用strides屬性檢索遍歷陣列時每個維度要步進的位元組數:

import numpy as np

# Create a 2D array
arr = np.array([[1, 2, 3], [4, 5, 6]])

# Print the strides of the array
print(arr.strides)

我們得到如下所示的輸出:

(24, 8)
廣告