ترفندهای Numpy

9184
ترفندهای Numpy

Numpy Array بسیاری از عملیات‌ها را بازنویسی می‌کند، بنابراین رمزگشایی از آن‌ها می‌تواند دشوار باشد. در ادامه مجموعه‌ای از لحظات چالش‌برانگیز Numpy و ترفندهای آن‌ها آورده شده است.

ترفند 1: Collection1 == Collection2

در Numpy هنگامی که == به دو مجموعه اعمال می‌شود به معنی مقایسه عنصری است و حاصل آن یک آرایه است. این ترفند را می‌توان با سایر عملیات‌هایی که ورودی آرایه دارند، ترکیب کرد.

>> X_train, y_train, X_test, y_test = load_CIFAR10(cifar10_dir)
# Training labels (y_train) shape:  (50000,)
# to pick out correct y examples for one class (assuming that class is indexed at 5)
>> print(y_train == 5)
# array([False, False, False, ..., False, False, False], dtype=bool)
>> idxs = np.flatnonzero(y_train == 5)

()np.flatnonzero یک آرایه را به‌عنوان ورودی می‌گیرد. آرایه Boolean در پایتون دارای ویژگی نگاشت True به 1 و False به 0 است. بنابراین ()flatnonzero به‌راحتی می‌تواند ایندکس مواردی که از کلاس 5 هستند را انتخاب کند.

ترفند 2: بردارسازی/حذف حلقه با ()np.tile

گاهی اوقات وقتی می‌خواهید دو ماتریس را به هم اضافه کنید و مجبورید آن‌ها را به‌صورت دستی مرتب و broadcast کنید، می‌توانید از ()np.tile استفاده کنید. ()np.tile ماتریس فعلی را کپی کرده و به شکلی که می‌خواهید broadcast می‌کند. کم‌ترین بعد را 1 نگه دارید تا ماتریس/بردار شما ثابت بماند.

هر حلقه for را می‌توان در () نوشت و از مزیت عملیات برداری استفاده کرد:

# for a matrix shape of (10,5), assume this is our training example
>> train = np.random.randn(10,5)
# for another matrix shape of (5,5), assume this is our training example
# 5 examples, each example has 5 data points
>> test = np.random.randn(5,5)
# We want to form a matrix of (5, 10), each data point on every row is the
# difference between sum of test-data[j] and train-data[i].
>> train = np.sum(train, axis=1)
>> test = np.sum(test, axis=1)

# duplicate train data of shape (10,) 5 times
>> expanded_train = np.tile(train, [test.shape[0],1])
# (5,10)
>> expanded_test = np.tile(test, [train.shape[0],1])
# (10, 5)
>> expanded_test = expanded_test.T
# (5,10) -> transpose it so we can do subtraction
>> result = expanded_train - expanded_test
# (5,10)

()np.tile همچنین می‌تواند برای گسترش آرایه‌ها و امکان ضرب، تقسیم و جمع عناصر مورد استفاده قرار گیرد. با این حال بهتر است برای چنین عملکردی از ()np.expand_dims استفاده شود، زیرا ()expand_dims بطور خودکار تشخیص می‌دهد که گسترش روی کدام بعد باید انجام شود و مانند np.tile نیازی به ترانهاده نهایی ندارد.

ترفند 3: استفاده از آرایه برای اسلایسینگ جفتی

اسلایسینگ آرایه در Numpy اغلب شگفت‌انگیز است. این شگفتی از بردارسازی max() hinge-loss در SVM ناشی می‌شود. تابع زیان چند‌کلاسی SVM برای تفریق امتیاز کلاس صحیح، به امتیاز کلاس اشتباه نیاز دارد. هنگام ضرب داخلی ماتریس وزن و ماتریس آموزش، ماتریسی به شکل (num_train, num_classes) دریافت می‌کنید. با این حال چگونه می‌توانید امتیاز کلاس‌های صحیح را بدون افتادن در حلقه، به دست آورید؟ با فرض اینکه برچسب‌های y به شکل (,num_train) باشند.

در این شرایط انتخاب pair-wise می‌تواند مفید باشد:

>> X = np.random.randn(500, 3073)
>> num_train = X.shape[0]
>> y_label = np.random.randint(10, (500,))
>> W = np.random.randn(3073, 500)
>> scores = X.dot(W)
>> correct_scores = scores[range(num_train),y_label]

اگر عملیات آرایه [] در numpy دارای دو آرگومان (که هر دو آرایه هستند) باشد، Numpy این عملیات pair-wise را اجرا می‌کند، مانند مثال زیر:

>> a = np.asarray([1,2,3])
>> b = np.asarray([1,2,3])
>> correct_scores = scores[a, b]

این برنامه مقدار [1,1]، [2,2] و [3,3] را از ماتریس امتیازات انتخاب می‌کند. راه دیگر برای استفاده معکوس از آن، انتساب مقدار است. بنابراین اگر بخواهیم امتیاز label صحیح را برای تابع زیان (loss function) حذف کنیم، می‌توانیم به صورت زیر عمل کنیم:

>> scores[range(X.shape[0]), y_label] = 0

با این کار تمام امتیازات label صحیح روی 0 تنظیم می‌شود. بنابراین وقتی آن‌ها را جمع می‌کنیم، label صحیح به‌هیچ‌وجه بر هزینه کلی تأثیر نمی‌گذارد.

مطلب مشابه: تغییرات PHP 8.0

ترفند 4: استفاده هوشمندانه از : برای استخراج شکل مناسب

گاهی اوقات با یک آرایه سه‌بعدی با شکل (N, T, D) مواجه می‌شوید، در حالی که تابع شما باید به شکل (N, D) باشد. در چنین شرایطی ()reshape بیشتر از آنکه مفید باشد ضرر خواهد داشت. بنابراین یک راه‌حل ساده برای شما باقی می‌ماند:

for t in xrange(T):
  x[:, t, :] = # ...

می‌توانید از آن برای استخراج یا تخصیص مقادیر استفاده کنید.

ترفند 5: استفاده از Array به‌عنوان اندیس Slicing

Numpy همچنین می‌تواند یک آرایه دیگر را به‌عنوان اسلایسینگ در نظر بگیرد. فرض کنید x یک آرایه اندیسی به شکل (N, T) است. هر اندیس از عناصر x در بازه 0 =< idx < V قرار دارد و ما می‌خواهیم چنین آرایه اندیسی را به آرایه‌ای با وزن واقعی تبدیل کنیم. با استفاده از یک ماتریس وزنی w با شکل (V, D)، به‌سادگی می‌توانیم:

N, T, V, D = 2, 4, 5, 3

x = np.asarray([[0, 3, 1, 2], [2, 1, 0, 3]])  # (N, T)
W = np.linspace(0, 1, num=V*D).reshape(V, D)  # (V, D)

# this is the only required line
out = W[x]  # (N, T, D)

Numpy از مقدار اصلی x به‌عنوان اندیس برای استخراج مقادیر از W استفاده می‌کند.

ترفند 6: Unfunc at

Numpy گروه خاصی از توابع به نام unfunc دارد. numpy.add ،numpy.subtract و… متعلق به این گروه خاص از توابع هستند. Numpy روش‌های از‌پیش‌تعریف‌شده‌ای برای این توابع مانند at دارد. این تابع، عملیات (فرض کنید این عملیات add یا subtract باشد) را در مکان(های) مشخصی انجام می‌دهد.

()ununc.at سه آرگومان را می‌گیرد. آرگومان اول ماتریسی است که قصد تغییر آن را دارید، آرگومان دوم اندیس‌های ماتریسی است که می‌خواهید تغییر دهید و آرگومان سوم مقداری است که می‌خواهید تغییر دهید (بسته به اینکه تابع unfunc چیست).

یک مثال ساده:

>>> a = np.array([1, 2, 3, 4])
>>> np.add.at(a, [0, 1, 2, 2], 1)
>>> print(a)
array([2, 3, 5, 4])

بیایید به یک مثال پیشرفته‌تر نگاه کنیم: از این ترفند برای به‌روز‌رسانی ماتریس جاسازی کلمه (word embedding) استفاده کنید!

# dout: Upstream gradients of shape (N, T, D)
# x: from example in Trick 5 (N, T), indices
# V: the length of total vocabulary
# D: weight matrix dimension
# task here is to build a dW to modify
# the original weight matrix

dW = np.zeros((V, D), dtype=dout.dtype)
np.add.at(dW, x, dout)

توجه کنید که Numpy ماتریس x را به اندیس‌های مجزا تبدیل می‌کند و از آن برای انتساب مقادیر dout به dW استفاده می‌کند. dout ،np.add.at را مسطح می‌کند تا ابعاد آن (N*T, D) شود. این با ابعاد x یعنی (N, T) مقایسه می‌شود تا ببینیم که آیا حاصل‌ضرب ابعاد x و N*T با هم مطابقت دارند یا خیر. فقط وقتی این اتفاق بیفتد، شما همان مقدار از آرایه‌های D را که در x دستور داده شده است به dW نسبت می‌دهید، که برای گرفتن آرایه‌های D نیز اتفاق می‌افتد.

آموزش برنامه نویسی با کوئرا کالج
کوئرا بلاگ

اشتراک در
اطلاع از
guest

8 دیدگاه‌
قدیمی‌ترین
تازه‌ترین بیشترین واکنش
بازخورد (Feedback) های اینلاین
View all comments
yousef
2 سال قبل

بسیار عالی

علیرضا
علیرضا
1 سال قبل

کد 3و 5 اجرا نشد

نیما حیدری‌نسب
ادمین
پاسخ به  علیرضا

سلام.
مشکل کد ۳ برطرف شد. ممنون که اطلاع دادید.
کد ۵ مشکلی نداره. حواستون باشه که import numpy as np رو در ابتدای کدتون بنویسید.

محمد مهدی
محمد مهدی
1 سال قبل

کلییییی ممنون از کوئرا،خیلی دنبال همچین مطلبی بودم

محمد زمانی خادمانلو
محمد زمانی خادمانلو
1 سال قبل

سلام علیکم
بنده هر وقت از numpy در حل سوالات استفاده کرده ام خطا داده. ولی کتابخانه math مشکلی ندارد.

محمد
محمد
1 سال قبل

احسنت!