۲۹ شهریور ۱۴۰۳

Techboy

اخبار و اطلاعات روز تکنولوژی

NumPy چیست؟ ریاضیات آرایه و ماتریس سریعتر در پایتون

بیاموزید که چگونه این کتابخانه محبوب پایتون، ریاضیات را در مقیاس سرعت می بخشد، به خصوص وقتی با ابزارهایی مانند Cython و Numba جفت شود.

بیاموزید که چگونه این کتابخانه محبوب پایتون، ریاضیات را در مقیاس سرعت می بخشد، به خصوص وقتی با ابزارهایی مانند Cython و Numba جفت شود.

Python راحت و منعطف است، اما از نظر سرعت محاسباتی خام بسیار کندتر از زبان های دیگر است. اکوسیستم پایتون با ابزارهایی جبران کرده است که اعداد را در مقیاس در پایتون سریع و راحت می کند.

NumPy یکی از رایج‌ترین ابزارهای پایتون است که توسعه‌دهندگان و دانشمندان داده برای کمک به محاسبات در مقیاس از آن استفاده می‌کنند. این کتابخانه ها و تکنیک هایی را برای کار با آرایه ها و ماتریس ها ارائه می دهد که همگی توسط کد نوشته شده به زبان های پرسرعت مانند C، C++ و Fortran پشتیبانی می شوند. و، تمام عملیات NumPy خارج زمان اجرای پایتون انجام می‌شود، بنابراین محدودیت‌های پایتون محدود نمی‌شوند.

استفاده از NumPy برای ریاضی آرایه و ماتریس در پایتون

بسیاری از عملیات ریاضی، به ویژه در یادگیری ماشینی یا علوم داده، شامل کار با ماتریس‌ها یا فهرست‌هایی از اعداد است. روش ساده برای انجام این کار در پایتون این است که اعداد را در یک ساختار ذخیره کنید، معمولاً یک list پایتون، سپس روی ساختار حلقه بزنید و عملیاتی را روی هر عنصر آن انجام دهید. این هم کند و هم ناکارآمد است، زیرا هر عنصر باید جلو و عقب از یک شی پایتون به یک عدد بومی ماشین ترجمه شود.

NumPy یک نوع آرایه تخصصی ارائه می دهد که برای کار با انواع عددی بومی ماشین مانند اعداد صحیح یا شناور بهینه شده است. آرایه ها می توانند ابعاد مختلفی داشته باشند، اما هر آرایه از یک نوع داده یکنواخت یا dtype برای نمایش داده های زیرین خود استفاده می کند.

یک مثال ساده در اینجا آمده است:


import numpy as np
np.array([0, 1, 2, 3, 4, 5, 6])

این یک آرایه NumPy یک بعدی از لیست ارائه شده ایجاد می کند. ما یک dtype برای این آرایه تعیین نکردیم، بنابراین به طور خودکار از داده های ارائه شده استنباط می شود که یک عدد صحیح امضا شده ۳۲ یا ۶۴ بیتی خواهد بود (بسته به پلتفرم). اگر بخواهیم در مورد dtype صریح باشیم، می‌توانیم این کار را انجام دهیم:


np.array([0, 1, 2, 3, 4, 5, 6], dtype=np.uint32)

np.uint32 همانطور که از نام آن پیداست، dtype برای یک عدد صحیح ۳۲ بیتی بدون علامت است.

ممکن است از اشیاء عمومی پایتون به عنوان dtype برای آرایه NumPy استفاده کنید، اما اگر این کار را انجام دهید، با NumPy عملکرد بهتری نسبت به خودتان نخواهید داشت. به طور کلی با پایتون. NumPy برای انواع عددی بومی ماشین (intfloats) به جای انواع Python-native بهترین کار را دارد. (اعداد مختلط، نوع اعشاری).

چگونه NumPy ریاضیات آرایه را در پایتون سرعت می دهد

بخش بزرگی از سرعت NumPy از استفاده از انواع داده‌های بومی ماشین به‌جای انواع شیء پایتون ناشی می‌شود. اما دلیل مهم دیگر سریع بودن NumPy این است که راه هایی برای کار با آرایه ها بدون نیاز به آدرس دادن جداگانه هر عنصر را ارائه می دهد.

آرایه های NumPy بسیاری از رفتارهای اشیاء پایتون معمولی را دارند، بنابراین استفاده از استعاره های رایج پایتون برای کار با آنها وسوسه انگیز است. اگر بخواهیم یک آرایه NumPy با اعداد ۰-۱۰۰۰ ایجاد کنیم، در تئوری می‌توانیم این کار را انجام دهیم:


x = np.array([_ for _ in range(1000)])

این کار می کند، اما عملکرد آن تا زمانی که پایتون برای ایجاد یک لیست و NumPy آن لیست را به آرایه تبدیل می کند، مخفی می شود.

در مقابل، ما می‌توانیم همین کار را به مراتب کارآمدتر در خود NumPy انجام دهیم:


x = np.arange(1000)

شما می توانید از بسیاری از انواع دیگر عملیات داخلی NumPy برای ایجاد آرایه های جدید استفاده کنید. بدون حلقه: ایجاد آرایه هایی از صفر (یا هر مقدار اولیه دیگر)، یا استفاده از مجموعه داده، بافر یا منبع دیگری موجود.

یکی دیگر از راه‌های کلیدی که NumPy کارها را سرعت می‌بخشد، ارائه راه‌هایی برای عدم پرداختن به عناصر آرایه به صورت جداگانه برای انجام کار روی آنها در مقیاس است.

همانطور که در بالا ذکر شد، آرایه‌های NumPy برای راحتی کار بسیار شبیه سایر اشیاء پایتون عمل می‌کنند. برای مثال، آنها را می‌توان مانند فهرست‌های نمایه‌گذاری کرد. arr[0] به اولین عنصر آرایه NumPy دسترسی دارد. این به شما امکان می دهد عناصر جداگانه را در یک آرایه تنظیم یا بخوانید.

با این حال، اگر می‌خواهید تمام عناصر یک آرایه را تغییر دهید، بهتر است از توابع «پخش کردن» NumPy استفاده کنید – روش‌هایی برای اجرای عملیات در کل یک آرایه یا یک قطعه، بدون حلقه زدن در پایتون. باز هم، این به این دلیل است که تمام کارهای حساس به عملکرد را می توان در خود NumPy انجام داد.

یک مثال در اینجا آمده است:


x1 = np.array(
    [np.arange(0, 10),
    np.arange(10,20)]
)

این یک آرایه دوبعدی NumPy ایجاد می کند که هر بعد آن از طیفی از اعداد تشکیل شده است. (ما می توانیم آرایه هایی با هر تعداد ابعاد را با استفاده از لیست های تو در تو در سازنده ایجاد کنیم.)


[[ ۰  ۱  ۲  ۳  ۴  ۵  ۶  ۷  ۸  ۹]
 [۱۰ ۱۱ ۱۲ ۱۳ ۱۴ ۱۵ ۱۶ ۱۷ ۱۸ ۱۹]]

اگر می‌خواهیم محورهای این آرایه را در پایتون جابه‌جا کنیم، باید نوعی حلقه بنویسیم. NumPy به ما اجازه می دهد تا این نوع عملیات را با یک دستور انجام دهیم:


x2 = np.transpose(x1)

خروجی:


[[ ۰ ۱۰]
 [ ۱ ۱۱]
 [ ۲ ۱۲]
 [ ۳ ۱۳]
 [ ۴ ۱۴]
 [ ۵ ۱۵]
 [ ۶ ۱۶]
 [ ۷ ۱۷]
 [ ۸ ۱۸]
 [ ۹ ۱۹]]

عملیاتی مانند اینها کلید استفاده از NumPy هستند. NumPy فهرست گسترده‌ای از روال‌های داخلی را برای دستکاری داده‌های آرایه ارائه می‌کند. داخلی برای جبر خطی، تبدیل های فوریه گسسته و تولید کننده های اعداد شبه تصادفی شما را از این مشکل نجات می دهد که مجبور باشید خودتان آن چیزها را هم بچرخانید. در بیشتر موارد، می‌توانید بدون استفاده از عملیات پایتون، آنچه را که نیاز دارید، با یک یا چند داخلی انجام دهید.

توابع جهانی NumPy (ufuncs)

مجموعه دیگری از ویژگی هایی که NumPy ارائه می دهد که به شما امکان می دهد تکنیک های محاسباتی پیشرفته را بدون حلقه های پایتون انجام دهید عملکردهای جهانی یا به اختصار ufuncs. ufuncها یک آرایه را می گیرند، برخی عملیات را روی هر عنصر آرایه انجام می دهند و نتایج را به آرایه دیگری ارسال می کنند یا عملیات را در محل انجام می دهند.

یک مثال:


x1 = np.arange(1, 9, 3)
x2 = np.arange(2, 18, 6)
x3 = np.add(x1, x2)

در اینجا، np.add هر عنصر x1 را می گیرد و به x2 اضافه می کند و نتایج در آرایه ای جدید ذخیره می شود. x3. این [ ۳ ۱۲ ۲۱] را به دست می‌دهد. تمام محاسبات واقعی در خود NumPy انجام می شود.

ufuncها همچنین دارای روشهای ویژگی هستند که به شما اجازه می دهد آنها را با انعطاف بیشتری اعمال کنید و نیاز به حلقه های دستی یا منطق سمت پایتون را کاهش دهید. برای مثال، اگر بخواهیم x1 را بگیریم و از np.add برای جمع آرایه استفاده کنیم، می‌توانیم از روش .add استفاده کنیم. np.add.accumulate(x1) به جای حلقه زدن روی هر عنصر در آرایه برای ایجاد مجموع.

به همین ترتیب، فرض کنید می‌خواستیم یک تابع کاهش انجام دهیم – یعنی .add را در امتداد یک محور از یک آرایه چند بعدی اعمال کنیم، با نتایج یک آرایه جدید با یک بعد کمتر. ما می توانستیم حلقه بزنیم و یک آرایه جدید ایجاد کنیم، اما این کار کند خواهد بود. یا می‌توانیم از np.add.reduce برای رسیدن به همان چیزی بدون حلقه استفاده کنیم:


x1 = np.array([[0,1,2],[3,4,5]])
# [[۰ ۱ ۲] [۳ ۴ ۵]]
x2 = np.add.reduce(x1)
# [۳ ۵ ۷]

ما همچنین می‌توانیم کاهش‌های شرطی را با استفاده از آرگومان where انجام دهیم:


x2 = np.add.reduce(x1, where=np.greater(x1, 1))

این x1+x2 را برمی‌گرداند، اما فقط در مواردی که عناصر در محور اول x1 بزرگ‌تر از ۱ باشند. در غیر این صورت، فقط مقدار عناصر در محور دوم را برمی گرداند. باز هم، این ما را از تکرار دستی روی آرایه در پایتون باز می‌دارد. NumPy مکانیسم‌هایی مانند این را برای فیلتر کردن و مرتب‌سازی داده‌ها بر اساس برخی معیارها فراهم می‌کند، بنابراین ما مجبور نیستیم حلقه‌هایی بنویسیم — یا حداقل، حلقه‌هایی که انجام می‌دهیم به حداقل می‌رسد. p>

NumPy و Cython: استفاده از NumPy با C

کتابخانه Cython در پایتون به شما امکان می‌دهد کد پایتون را بنویسید و آن را برای سرعت به C تبدیل کنید، از انواع C برای متغیرها استفاده کنید. این متغیرها می توانند شامل آرایه های NumPy باشند، بنابراین هر کد Cython که بنویسید می تواند مستقیماً با آن کار کند. آرایه های NumPy.

استفاده از Cython با NumPy برخی از ویژگی‌های قدرتمند را به شما می‌دهد:

  • تسریع حلقه‌های دستی: گاهی اوقات چاره‌ای جز حلقه زدن روی یک آرایه NumPy ندارید. نوشتن عملیات حلقه در یک ماژول Cython راهی برای انجام حلقه در C به جای پایتون فراهم می کند و بنابراین افزایش سرعت چشمگیر را امکان پذیر می کند. توجه داشته باشید که این تنها در صورتی امکان پذیر است که انواع همه متغیرهای مورد نظر آرایه NumPy یا نوع C بومی ماشین باشند.
  • استفاده از آرایه‌های NumPy با کتابخانه‌های C: یکی از موارد استفاده رایج برای Cython نوشتن پوشش‌های مناسب Python برای کتابخانه‌های C است. کد Cython می تواند به عنوان پلی بین کتابخانه C موجود و آرایه های NumPy عمل کند.

Cython دو روش را برای کار با آرایه های NumPy امکان پذیر می کند. یکی از طریق نمایش حافظه تایپ شده، یک ساختار Cython برای سریع و دسترسی ایمن به یک آرایه NumPy. یکی دیگر از این موارد این است که یک اشاره گر خام به داده های زیربنایی به دست آورید و مستقیماً با آن کار کنید، اما این به قیمت ناامن بودن بالقوه و نیاز به دانستن طرح‌بندی حافظه شیء از قبل است.

NumPy و Numba: کد پایتون شتاب دهنده JIT برای NumPy

یک راه دیگر برای استفاده از پایتون به شیوه ای کارآمد با آرایه های NumPy استفاده از Numba، یک کامپایلر JIT برای پایتون است. Numba کدهای تفسیر شده توسط Python را به کدهای بومی ماشین ترجمه می کند، با تخصص هایی مانند NumPy. حلقه‌های موجود در پایتون روی آرایه‌های NumPy را می‌توان به‌طور خودکار از این طریق بهینه کرد. اما بهینه‌سازی‌های Numba فقط تا حدی خودکار هستند و ممکن است بهبود عملکرد قابل توجهی را برای همه برنامه‌ها نشان ندهند.