একটি NumPy অ্যারের প্রতিটি কক্ষে একটি ক্রিয়াকলাপের দক্ষ মূল্যায়ন


124

একটি প্রদত্ত NumPy অ্যারের একটি , দ্রুততম / সবচেয়ে বেশি কার্যকরী প্রয়োগ করতে উপায় কি একই ফাংশন, চাই, যে সেল?

  1. মনে করুন যে আমরা A (i, j) কে f (A (i, j)) প্রদান করব

  2. ফাংশন, এফ , এর একটি বাইনারি আউটপুট নেই, সুতরাং মুখোশ (আইএন) ক্রিয়াকলাপগুলি সাহায্য করবে না।

"সুস্পষ্ট" ডাবল লুপ পুনরাবৃত্তি (প্রতিটি ঘর মাধ্যমে) অনুকূল সমাধান?


উত্তর:


165

আপনি কেবলমাত্র ফাংশনটিকে ভেক্টরাইজ করতে পারেন এবং তারপরে প্রতিটি বার যখন আপনার প্রয়োজন হবে এটি সরাসরি কোনও নম্পি অ্যারেতে প্রয়োগ করতে পারেন:

import numpy as np

def f(x):
    return x * x + 3 * x - 2 if x > 0 else x * 5 + 8

f = np.vectorize(f)  # or use a different name if you want to keep the original f

result_array = f(A)  # if A is your Numpy array

ভেক্টরাইজিংয়ের সময় সরাসরি একটি স্পষ্ট আউটপুট টাইপ নির্দিষ্ট করা সম্ভবত আরও ভাল:

f = np.vectorize(f, otypes=[np.float])

19
আমি ভীত যে ভেক্টরাইজড ফাংশনটি "ম্যানুয়াল" ডাবল লুপ পুনরাবৃত্তির চেয়ে দ্রুত আর অ্যারির সমস্ত উপাদানগুলির থেকে অ্যাসাইনমেন্টের চেয়ে দ্রুততর হতে পারে না। বিশেষত, কারণ এটি ফলাফলটি নতুনভাবে তৈরি ভেরিয়েবলের কাছে সঞ্চয় করে (এবং সরাসরি প্রাথমিক ইনপুটটিতে নয়)। আপনার জবাবের জন্য অনেক ধন্যবাদ যদিও :)
পিটার

1
@ পিটার: আহ, এখন আমি দেখতে পাচ্ছি যে আপনি ফলাফলটি আপনার মূল প্রশ্নে প্রাক্তন অ্যারেগুলিকে ফিরিয়ে দেওয়ার কথা উল্লেখ করেছেন। আমি দুঃখিত যখন আমি এটি প্রথম পড়ার সময় মিস করেছি। হ্যাঁ, সেক্ষেত্রে ডাবল লুপটি দ্রুত হওয়া উচিত। তবে আপনি কি অ্যারের চ্যাপ্টা ভিউতে একটি লুপ ব্যবহার করে দেখেছেন? এটি কিছুটা দ্রুত হতে পারে , যেহেতু আপনি একটি সামান্য লুপ ওভারহেড সংরক্ষণ করেন এবং প্রতিটি পুনরাবৃত্তিতে নম্পিকে একটি কম গুন এবং সংযোজন (ডেটা অফসেট গণনা করার জন্য) করতে হবে। এছাড়াও এটি নির্বিচারে মাত্রিক অ্যারেগুলির জন্য কাজ করে। খুব ছোট অ্যারেগুলিতে ধীর হতে পারে।
blubberdiblub

45
vectorizeফাংশন বর্ণনায় প্রদত্ত সতর্কতাটি লক্ষ্য করুন : ভেক্টরাইজ ফাংশনটি মূলত সুবিধার জন্য সরবরাহ করা হয়, পারফরম্যান্সের জন্য নয়। বাস্তবায়ন লুপের জন্য মূলত একটি। সুতরাং এটি সম্ভবত প্রক্রিয়াটি একেবারে গতি বাড়িয়ে তুলবে না।
গ্যাব্রিয়েল

vectorizeরিটার্নের ধরণ কীভাবে নির্ধারণ করে সেদিকে মনোযোগ দিন । এটি বাগ তৈরি করেছে। frompyfuncকিছুটা দ্রুত, তবে একটি টাইপ অবজেক্ট অ্যারে প্রদান করে। উভয় ফিড স্কেলার, সারি বা কলামগুলি নয়।
hpulj

1
@ গ্যাব্রিয়েল np.vectorizeআমার ফাংশনটি (যা আরকে 45 ব্যবহার করে) আমাকে ফেলে দেওয়া আমাকে 20
ডলার



0

আমি বিশ্বাস করি আমি এর থেকে ভাল সমাধান খুঁজে পেয়েছি। অজগর সার্বজনীন ফাংশন ( ডকুমেন্টেশন দেখুন ) এ ফাংশনটি পরিবর্তন করার ধারণা , যা হুডের নীচে সমান্তরাল গণনা অনুশীলন করতে পারে।

ufuncসি-তে নিজের কাস্টমাইজড কেউ লিখতে পারেন , যা অবশ্যই আরও দক্ষ, বা অনুরোধ করে np.frompyfunc, যা বিল্ট-ইন ফ্যাক্টরি পদ্ধতিতে তৈরি। পরীক্ষার পরে, এটি এর চেয়ে আরও কার্যকর np.vectorize:

f = lambda x, y: x * y
f_arr = np.frompyfunc(f, 2, 1)
vf = np.vectorize(f)
arr = np.linspace(0, 1, 10000)

%timeit f_arr(arr, arr) # 307ms
%timeit f_arr(arr, arr) # 450ms

আমি আরও বড় নমুনাগুলি পরীক্ষা করেছি এবং উন্নতি আনুপাতিক। অন্যান্য পদ্ধতির পারফরম্যান্সের তুলনা করার জন্য, এই পোস্টটি দেখুন


0

যখন 2 ডি-অ্যারে (বা এনডি-অ্যারে) সি- বা এফ-সংলগ্ন হয়, তখন 2d-অ্যারেতে কোনও ফাংশন ম্যাপিংয়ের এই কার্যটি কার্যত 1 ডি-অ্যারেতে কোনও ফাংশন ম্যাপিংয়ের মতোই হয় - আমরা কেবল এটি সেভাবে দেখতে হবে, যেমন মাধ্যমে np.ravel(A,'K')

1 ডি-অ্যারের সম্ভাব্য সমাধানটি এখানে উদাহরণস্বরূপ আলোচনা করা হয়েছে

যাইহোক, যখন 2 ডি-অ্যারের স্মৃতিটি সংক্ষিপ্ত নয়, তখন পরিস্থিতিটি খানিকটা জটিল হয়, কারণ যদি অক্ষকে ভুল ক্রমে পরিচালনা করা হয় তবে কেউ সম্ভাব্য ক্যাশে মিস করা এড়াতে চান।

সেরা সম্ভাব্য ক্রমে অক্ষগুলি প্রক্রিয়া করার জন্য নম্পির কাছে ইতিমধ্যে একটি যন্ত্রপাতি রয়েছে। এই যন্ত্রপাতি ব্যবহার করার একটি সম্ভাবনা হ'ল np.vectorize। তবে নম্পির ডকুমেন্টেশনে np.vectorizeবলা হয়েছে যে এটি "প্রাথমিকভাবে সুবিধার্থে সরবরাহ করা হয়, কার্য সম্পাদনের জন্য নয়" - একটি ধীর পাইথন ফাংশন পুরো সম্পর্কিত ওভারহেডের সাথে ধীর পাইথন ফাংশন স্থির রাখে! আর একটি সমস্যা হ'ল এর বিশাল স্মৃতি-খরচ consumption উদাহরণস্বরূপ দেখুন এই এসও-পোস্ট

যখন কেউ সি-ফাংশনটির পারফরম্যান্স পেতে চান তবে নম্পির যন্ত্রপাতি ব্যবহার করতে চান, তবে একটি ভাল সমাধান হ'ল ইউফঙ্কস তৈরির জন্য নাম্বার ব্যবহার করা, উদাহরণস্বরূপ:

# runtime generated C-function as ufunc
import numba as nb
@nb.vectorize(target="cpu")
def nb_vf(x):
    return x+2*x*x+4*x*x*x

এটি সহজেই মারধর করে np.vectorizeতবে একই ফাংশনটি যখন নাম্পি-অ্যারে গুণ / সংযোজন হিসাবে সম্পাদিত হবে, অর্থাত্‍

# numpy-functionality
def f(x):
    return x+2*x*x+4*x*x*x

# python-function as ufunc
import numpy as np
vf=np.vectorize(f)
vf.__name__="vf"

সময়-পরিমাপ-কোডের জন্য এই উত্তরের পরিশিষ্ট দেখুন:

এখানে চিত্র বর্ণনা লিখুন

নুম্বার সংস্করণ (সবুজ) পাইথন ফাংশন (অর্থাত্ np.vectorize) এর চেয়ে প্রায় 100 গুণ বেশি গতিযুক্ত , এটি অবাক হওয়ার মতো নয়। তবে এটি নম্পি-কার্যকারিতা থেকে প্রায় 10 গুণ বেশি গতিযুক্ত, কারণ নম্বাস সংস্করণটির মধ্যবর্তী অ্যারেগুলির প্রয়োজন নেই এবং এইভাবে আরও দক্ষতার সাথে ক্যাশে ব্যবহার করা হয়।


যদিও নাম্বার ইউফুঙ্ক পদ্ধতিটি ব্যবহারযোগ্যতা এবং পারফরম্যান্সের মধ্যে একটি ভাল বাণিজ্য-বন্ধ, এটি এখনও আমরা করতে পারি না সেরা। তবুও কোনও কাজের জন্য সিলভার বুলেট বা কোনও পদ্ধতির সর্বোত্তম উপায় নেই - একটি সীমাবদ্ধতা কী এবং কীভাবে সেগুলি প্রশমিত করা যায় তা বুঝতে হবে।

উদাহরণস্বরূপ, তুরীয় কাজকর্মের জন্য (যেমন exp, sin, cos) numba কোনো সুফল উপর উপলব্ধ করা হয় না numpy এর np.exp(কোন অস্থায়ী নির্মিত অ্যারে রয়েছে - গতি-আপ এর প্রধান উৎস)। যাইহোক, আমার অ্যানাকোন্ডা ইনস্টলেশন 8192 এর চেয়ে বড় ভেক্টরগুলির জন্য ইন্টেলের ভিএমএল ব্যবহার করে - মেমরিটি সংক্ষিপ্ত না হলে এটি এটি করতে পারে না। সুতরাং ইন্টেলের ভিএমএল ব্যবহার করতে সক্ষম হওয়ার জন্য উপাদানগুলিকে একটি স্বতন্ত্র স্মৃতিতে অনুলিপি করা ভাল be

import numba as nb
@nb.vectorize(target="cpu")
def nb_vexp(x):
    return np.exp(x)

def np_copy_exp(x):
    copy = np.ravel(x, 'K')
    return np.exp(copy).reshape(x.shape) 

তুলনার ন্যায্যতার জন্য, আমি ভিএমএলের সমান্তরালতা বন্ধ করেছি (পরিশিষ্টের কোডটি দেখুন):

এখানে চিত্র বর্ণনা লিখুন

যেমনটি দেখতে পাচ্ছে, ভিএমএল একবার লাথি মারল, অনুলিপি করার ওভারহেড ক্ষতিপূরণের চেয়ে বেশি। তবুও একবার এল 3 ক্যাশের জন্য ডেটা খুব বড় হয়ে গেলে সুবিধাটি ন্যূনতম হওয়ায় কাজটি আবার মেমরি-ব্যান্ডউইথ-সীমাবদ্ধ হয়ে যায়।

অন্যদিকে, নাম্বা ইন্টেলের এসভিএমএলও ব্যবহার করতে পারে, যেমনটি এই পোস্টে ব্যাখ্যা করা হয়েছে :

from llvmlite import binding
# set before import
binding.set_option('SVML', '-vector-library=SVML')

import numba as nb

@nb.vectorize(target="cpu")
def nb_vexp_svml(x):
    return np.exp(x)

এবং ভিএমএল ব্যবহার করে সমান্তরাল ফলন:

এখানে চিত্র বর্ণনা লিখুন

নাম্বার সংস্করণটির ওভারহেড কম রয়েছে, তবে কিছু মাপের জন্য ভিএমএল অতিরিক্ত অনুলিপি ওভারহেড থাকা সত্ত্বেও এসভিএমএলকে বীট করে num যা নাম্বার ইউফুনসগুলি সমান্তরাল না হওয়ায় কিছুটা অবাক হয় না।


তালিকা:

উ: বহুপদী ফাংশনের তুলনা:

import perfplot
perfplot.show(
    setup=lambda n: np.random.rand(n,n)[::2,::2],
    n_range=[2**k for k in range(0,12)],
    kernels=[
        f,
        vf, 
        nb_vf
        ],
    logx=True,
    logy=True,
    xlabel='len(x)'
    ) 

বি এর তুলনা exp:

import perfplot
import numexpr as ne # using ne is the easiest way to set vml_num_threads
ne.set_vml_num_threads(1)
perfplot.show(
    setup=lambda n: np.random.rand(n,n)[::2,::2],
    n_range=[2**k for k in range(0,12)],
    kernels=[
        nb_vexp, 
        np.exp,
        np_copy_exp,
        ],
    logx=True,
    logy=True,
    xlabel='len(x)',
    )

0

উপরের সমস্ত উত্তরগুলির সাথে তুলনা করা ভাল, তবে যদি আপনাকে ম্যাপিংয়ের জন্য কাস্টম ফাংশন ব্যবহার করতে হবে এবং আপনার থাকতে হবে numpy.ndarrayএবং আপনাকে অ্যারের আকার ধরে রাখতে হবে।

আমি মাত্র দুটি তুলনা করেছি, তবে এটি আকারটি ধরে রাখবে ndarray। তুলনা করার জন্য আমি 1 মিলিয়ন এন্ট্রি সহ অ্যারে ব্যবহার করেছি। এখানে আমি স্কোয়ার ফাংশন ব্যবহার করি। আমি n মাত্রিক অ্যারের জন্য সাধারণ কেস উপস্থাপন করছি। দ্বিমাত্রিক মাত্র দাও iter2D জন্য।

import numpy, time

def A(e):
    return e * e

def timeit():
    y = numpy.arange(1000000)
    now = time.time()
    numpy.array([A(x) for x in y.reshape(-1)]).reshape(y.shape)        
    print(time.time() - now)
    now = time.time()
    numpy.fromiter((A(x) for x in y.reshape(-1)), y.dtype).reshape(y.shape)
    print(time.time() - now)
    now = time.time()
    numpy.square(y)  
    print(time.time() - now)

আউটপুট

>>> timeit()
1.162431240081787    # list comprehension and then building numpy array
1.0775556564331055   # from numpy.fromiter
0.002948284149169922 # using inbuilt function

এখানে আপনি numpy.fromiterব্যবহারকারীর স্কোয়ার ফাংশনটি স্পষ্ট দেখতে পাচ্ছেন , আপনার পছন্দের যে কোনওটি ব্যবহার করুন। আপনি যদি ফাংশনটি i, j অ্যারের সূচকগুলির উপর নির্ভরশীল হন তবে অ্যারের আকারের উপর পুনরাবৃত্তি করুন for ind in range(arr.size), আপনার 1 ডি সূচক এবং অ্যারের ন্যাম্পি.উনরভেল_ইন্ডেক্সের আকারের উপর ভিত্তি করে numpy.unravel_indexপেতে ব্যবহার i, j, ..করুন

এই উত্তরগুলি এখানে অন্য প্রশ্নের আমার উত্তর দ্বারা অনুপ্রাণিত হয়

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.