2015-12-29 12 views
5

में चर लंबाई मैट्रिक्स मैं मैट्रिक्स L की एक सूची है, जहां प्रत्येक आइटम M एक x*n मैट्रिक्स है (x एक चर रहा है, n एक निरंतर है)।लूप से अधिक (या vectorize) थेनो

मैं L में सभी आइटम के लिए M'*M का योग (M'M की पक्षांतरित है) की गणना करने के लिए निम्न पायथन कोड के रूप में चाहते हैं करता है:

for M in L: 
    res += np.dot(M.T, M) 

असल में मैं थेनो में यह लागू करना चाहते हैं (जो नहीं करता है वेरिएबल लम्बाई बहुआयामी सरणी का समर्थन नहीं करते हैं), और मैं सभी मैट्रिक्स को उसी आकार में पैड नहीं करना चाहता क्योंकि यह बहुत अधिक जगह बर्बाद कर देगा (कुछ मैट्रिक्स बहुत बड़े हो सकते हैं)।

क्या ऐसा करने का कोई बेहतर तरीका है?

संपादित:

L थेनो संकलन से पहले जाना जाता है।

संपादित:

@DanielRenshaw और @Divakar से दो उत्कृष्ट उत्तर प्राप्त हुए, भावनात्मक रूप से स्वीकार करने के लिए एक का चयन करने के लिए मुश्किल।

+0

थानो संकलन से पहले 'एल' की लंबाई ज्ञात है? –

+0

@DanielRenshaw हाँ, और एल में प्रत्येक मैट्रिक्स का आकार भी जाना जाता है – dontloo

उत्तर

3

आप केवल पहले अक्ष के साथ इनपुट सरणी पैड कर सकते हैं जो सभी x के एड अप हैं। इस प्रकार, हम एक लंबे (X,n) सरणी के साथ समाप्त होंगे, जहां X =x1+x2+x3+....। इसे स्थानांतरित किया जा सकता है और इसके डॉट उत्पाद को स्वयं के आकार (n,n) का वांछित आउटपुट होगा। यह सब शक्तिशाली डॉट उत्पाद का लाभ उठाने वाले शुद्ध वेक्टरकृत समाधान के साथ हासिल किया जाता है।इस प्रकार, कार्यान्वयन होगा -

# Concatenate along axis=0 
Lcat = np.concatenate(L,axis=0) 

# Perform dot product of the transposed version with self 
out = Lcat.T.dot(Lcat) 

रनटाइम परीक्षण और सत्यापित करें उत्पादन -

In [116]: def vectoized_approach(L): 
    ...: Lcat = np.concatenate(L,axis=0) 
    ...: return Lcat.T.dot(Lcat) 
    ...: 
    ...: def original_app(L): 
    ...: n = L[0].shape[1] 
    ...: res = np.zeros((n,n)) 
    ...: for M in L: 
    ...:  res += np.dot(M.T, M) 
    ...: return res 
    ...: 

In [117]: # Input 
    ...: L = [np.random.rand(np.random.randint(1,9),5)for iter in range(1000)] 

In [118]: np.allclose(vectoized_approach(L),original_app(L)) 
Out[118]: True 

In [119]: %timeit original_app(L) 
100 loops, best of 3: 3.84 ms per loop 

In [120]: %timeit vectoized_approach(L) 
1000 loops, best of 3: 632 µs per loop 
+0

यह वास्तव में पसंदीदा दृष्टिकोण होगा यदि 'x' के आकार में भिन्नता छोटा है (यानी कोई मैट्रिक्स बहुत गद्देदार होने की आवश्यकता नहीं है)। मैंने इस दृष्टिकोण सहित एक पूर्ण तुलना देने के लिए अपना जवाब अपडेट कर दिया है। –

+0

@DanielRenshaw ठीक है, यह दृष्टिकोण सिर्फ संगत है, यहां कोई पैडिंग नहीं है। इस प्रकार, मुझे लगता है कि इनपुट सरणी के आकार इनपुट सूची में पर्याप्त संख्या में सरणी दिए जाने पर प्रदर्शन भिन्नता के लिए कोई फर्क नहीं पड़ता। – Divakar

+0

इस दृष्टिकोण के थेनो संस्करण के लिए पैडिंग की आवश्यकता होगी। –

5

यह देखते हुए कि थिएनो संकलन होने से पहले मैट्रिस की संख्या ज्ञात है, कोई भी थैनो मैट्रिस की नियमित पायथन सूची का उपयोग कर सकता है।

यहां एक पूरा उदाहरण है जो numpy और Theano संस्करणों के बीच अंतर दिखा रहा है।

यह कोड @ Divakar के वेक्टरीकृत दृष्टिकोण के साथ तुलना करने के लिए अद्यतन किया गया है जो बेहतर प्रदर्शन करता है। थेनो के लिए दो वेक्टरकृत दृष्टिकोण संभव हैं, जहां एक थैनो संगतता करता है, और एक जहां numpy concatenation करता है जिसके परिणामस्वरूप थानो को पास किया जाता है।

import timeit 
import numpy as np 
import theano 
import theano.tensor as tt 


def compile_theano_version1(number_of_matrices, n, dtype): 
    assert number_of_matrices > 0 
    assert n > 0 
    L = [tt.matrix() for _ in xrange(number_of_matrices)] 
    res = tt.zeros(n, dtype=dtype) 
    for M in L: 
     res += tt.dot(M.T, M) 
    return theano.function(L, res) 


def compile_theano_version2(number_of_matrices): 
    assert number_of_matrices > 0 
    L = [tt.matrix() for _ in xrange(number_of_matrices)] 
    concatenated_L = tt.concatenate(L, axis=0) 
    res = tt.dot(concatenated_L.T, concatenated_L) 
    return theano.function(L, res) 


def compile_theano_version3(): 
    concatenated_L = tt.matrix() 
    res = tt.dot(concatenated_L.T, concatenated_L) 
    return theano.function([concatenated_L], res) 


def numpy_version1(*L): 
    assert len(L) > 0 
    n = L[0].shape[1] 
    res = np.zeros((n, n), dtype=L[0].dtype) 
    for M in L: 
     res += np.dot(M.T, M) 
    return res 


def numpy_version2(*L): 
    concatenated_L = np.concatenate(L, axis=0) 
    return np.dot(concatenated_L.T, concatenated_L) 


def main(): 
    iteration_count = 100 
    number_of_matrices = 20 
    n = 300 
    min_x = 400 
    dtype = 'float64' 
    theano_version1 = compile_theano_version1(number_of_matrices, n, dtype) 
    theano_version2 = compile_theano_version2(number_of_matrices) 
    theano_version3 = compile_theano_version3() 
    L = [np.random.standard_normal(size=(x, n)).astype(dtype) 
     for x in range(min_x, number_of_matrices + min_x)] 

    start = timeit.default_timer() 
    numpy_res1 = np.sum(numpy_version1(*L) 
         for _ in xrange(iteration_count)) 
    print 'numpy_version1', timeit.default_timer() - start 

    start = timeit.default_timer() 
    numpy_res2 = np.sum(numpy_version2(*L) 
         for _ in xrange(iteration_count)) 
    print 'numpy_version2', timeit.default_timer() - start 

    start = timeit.default_timer() 
    theano_res1 = np.sum(theano_version1(*L) 
         for _ in xrange(iteration_count)) 
    print 'theano_version1', timeit.default_timer() - start 

    start = timeit.default_timer() 
    theano_res2 = np.sum(theano_version2(*L) 
         for _ in xrange(iteration_count)) 
    print 'theano_version2', timeit.default_timer() - start 

    start = timeit.default_timer() 
    theano_res3 = np.sum(theano_version3(np.concatenate(L, axis=0)) 
         for _ in xrange(iteration_count)) 
    print 'theano_version3', timeit.default_timer() - start 

    assert np.allclose(numpy_res1, numpy_res2) 
    assert np.allclose(numpy_res2, theano_res1) 
    assert np.allclose(theano_res1, theano_res2) 
    assert np.allclose(theano_res2, theano_res3) 


main() 

जब (जैसे कुछ) चलाने इस प्रिंट

numpy_version1 1.47830819649 
numpy_version2 1.77405482179 
theano_version1 1.3603150303 
theano_version2 1.81665318145 
theano_version3 1.86912039489 

पास का दावा है, दिखा रहा है कि थेनो और numpy संस्करणों दोनों की गणना सटीकता के उच्च स्तर के लिए एक ही परिणाम। float64 के बजाय float32 का उपयोग करते समय स्पष्ट रूप से यह सटीकता कम हो जाएगी।

समय के नतीजे बताते हैं कि वेक्टरिज्ड दृष्टिकोण बेहतर नहीं हो सकता है, यह मैट्रिक्स आकारों पर निर्भर करता है। ऊपर दिए गए उदाहरण में मैट्रिस बड़े हैं और गैर-कॉन्सटेनेशन दृष्टिकोण तेज है, लेकिन n और min_x पैरामीटर main फ़ंक्शन में बहुत छोटे होने के बाद बदल दिए जाते हैं तो कॉन्सटेनेशन दृष्टिकोण तेज होता है। GPU (केवल Theano संस्करण) पर चलते समय अन्य परिणाम हो सकते हैं।

+0

धन्यवाद डैनियल, यह मेरे लिए बहुत उपयोगी है। – dontloo

+0

क्या आप 'number_of_matrices' के लिए बड़ी संख्या का उपयोग कर सकते हैं? चूंकि मूल कोड उस के माध्यम से looped, यह समझ में आता है कि इसके लिए पर्याप्त संख्या है। – Divakar

+0

20 से 200 तक 'number_of_matrices' बढ़ाना सापेक्ष समय नहीं बदलता है। Concatenate + vectorized dot matrices पर बड़े होने पर मैट्रिक्स पर एक बार फिर से धीमा होने से काफी धीमा है। –

1

इसके अलावा @ DanielRenshaw के जवाब देने के लिए, अगर हम 1000 के मैट्रिक की संख्या में वृद्धि, compile_theano_version1 समारोह RuntimeError: maximum recursion depth exceeded निकलेगा , और compile_theano_version2 संकलित करने के लिए हमेशा के लिए लगता है।

वहाँ typed_list का उपयोग करके इस के लिए एक ठीक है:

def compile_theano_version4(number_of_matrices, n): 
    import theano.typed_list 
    L = theano.typed_list.TypedListType(tt.TensorType(theano.config.floatX, broadcastable=(None, None)))() 
    res, _ = theano.scan(fn=lambda i: tt.dot(L[i].T, L[i]), 
         sequences=[theano.tensor.arange(number_of_matrices, dtype='int64')]) 
    return theano.function([L], res.sum(axis=0)) 

इसके अलावा, मैं float32 के लिए सभी प्रासंगिक चर के डेटा प्रकार सेट और @ GPU पर DanielRenshaw की पटकथा भाग गया, यह पता चला कि @ दिवाकर के सुझाव (theano_version3) इस मामले में सबसे कुशल है। हालांकि @DanielRenshaw के रूप में, एक विशाल मैट्रिक्स का उपयोग करना हमेशा एक अच्छा अभ्यास नहीं हो सकता है।

अनुवर्ती मेरी मशीन पर सेटिंग्स और आउटपुट हैं।

iteration_count = 100 
number_of_matrices = 200 
n = 300 
min_x = 20 
dtype = 'float32' 
theano.config.floatX = dtype 


numpy_version1 5.30542397499 
numpy_version2 3.96656394005 
theano_version1 5.26742005348 
theano_version2 1.76983904839 
theano_version3 1.03577589989 
theano_version4 5.58366179466 
संबंधित मुद्दे