Topic: Applicare funzione ad array numpy  (Letto 1444 volte)

0 Utenti e 1 Visitatore stanno visualizzando questo topic.

Offline Claudio_F

  • python sapiens sapiens
  • ******
  • Post: 1.157
  • Punti reputazione: 2
    • Mostra profilo
Applicare funzione ad array numpy
« il: Luglio 15, 2015, 13:08 »
Sto provando ad usare numpy. Quali metodi efficienti in termini di velocità esistono per applicare una funzione agli elementi di un np array? Per ora sono arrivato a questo, ma da quanto leggo la velocità di esecuzione è comunque legata alla funzione scritta in Python... cioè ci si risparmia solo il tempo dell'iterazione esplicita con for, map ecc:
[codice]import numpy as np
modifier = np.vectorize(lambda x: 1. / (1. + np.e**(-x)))
data = np.array([.5, .6, .7, .8, .9, 1], float)
new_data = modifier(data)[/codice]

Offline GlennHK

  • python sapiens sapiens
  • ******
  • Post: 1.643
  • Punti reputazione: 1
    • Mostra profilo
    • La Tana di GlennHK
Re: Applicare funzione ad array numpy
« Risposta #1 il: Luglio 15, 2015, 14:12 »
In questo caso la cosa più veloce da fare è:

[codice]
import numpy as np 
from scipy.special import expit

data = np.array([.5, .6, .7, .8, .9, 1], float) 
new_data = expit(data)
[/codice]

Offline Claudio_F

  • python sapiens sapiens
  • ******
  • Post: 1.157
  • Punti reputazione: 2
    • Mostra profilo
Re: Applicare funzione ad array numpy
« Risposta #2 il: Luglio 16, 2015, 08:54 »
Funziona molto bene grazie. Naturalmente non avrei mai scoperto che quella funzione esegue quel calcolo (neppure dalla descrizione  :|).

Per il resto, se da una parte il tempo di esecuzione della funzione in cui uso np array e expit (invece di liste/cicli Python) si è ridotto di quasi otto volte, da qualche altra è aumentato peggiorando le performance complessive... ipotizzo conversioni ed eventuali "np allocazioni", ma ora non ho il tempo per estrarre un codice minimo o valutare le diverse condizioni (come pochi array molto grandi vs moltissimi array piccoli).

Offline GlennHK

  • python sapiens sapiens
  • ******
  • Post: 1.643
  • Punti reputazione: 1
    • Mostra profilo
    • La Tana di GlennHK
Re: Applicare funzione ad array numpy
« Risposta #3 il: Luglio 16, 2015, 09:16 »
Vai di profiling magari e controlla ;)

Per rispondere alla domanda iniziale, in questo caso specifico ho trovato una funzione perfetta per lo scopo (ps: bastava googlare numpy sigmoid function... che è esattamente il nome di quella funzione ;) ), negli altri casi se le performance sono un problema penso che la soluzione che dà il miglior apporto qualità/prezzo sia riscrivere in cython, è quasi-python, interagisce alla grande con numpy e dà C-speed.

Offline GlennHK

  • python sapiens sapiens
  • ******
  • Post: 1.643
  • Punti reputazione: 1
    • Mostra profilo
    • La Tana di GlennHK
Re: Applicare funzione ad array numpy
« Risposta #4 il: Luglio 16, 2015, 14:54 »
Ah, e mi sono scordato di puntualizzare una cosa: già roba come np.exp(-x) è sensibilmente più veloce di np.e**(-x).

Offline Claudio_F

  • python sapiens sapiens
  • ******
  • Post: 1.157
  • Punti reputazione: 2
    • Mostra profilo
Re: Applicare funzione ad array numpy
« Risposta #5 il: Luglio 17, 2015, 16:59 »
Ci sarà anche qualcosa per Y = x/(1-x)  ?  :embarrassed:

Un'altra operazione che volevo effettuare è ottenere una matrice moltiplicando un array 'b' per ciascun elemento di un altro array 'a':
[codice]>>> a
array([ 1.,  2.,  3.])
>>> b
array([ 4.,  5.,  6.,  7.,  8.,  9.])
>>> z = np.array([x*b for x in a])
>>> z
array([[  4.,   5.,   6.,   7.,   8.,   9.],
       [  8.,  10.,  12.,  14.,  16.,  18.],
       [ 12.,  15.,  18.,  21.,  24.,  27.]])
[/codice]
... ma senza usare quella list comprehension...
« Ultima modifica: Luglio 17, 2015, 17:02 da Claudio_F »

Offline GlennHK

  • python sapiens sapiens
  • ******
  • Post: 1.643
  • Punti reputazione: 1
    • Mostra profilo
    • La Tana di GlennHK
Re: Applicare funzione ad array numpy
« Risposta #6 il: Luglio 17, 2015, 17:07 »
Il secondo è un outer product, usa np.outer o aggiungi un nuovo asse ad uno dei due array e sfrutta il broadcasting...

Per quanto riguarda il primo quesito, non saprei, non mi sembra una funzione nota.

Offline GlennHK

  • python sapiens sapiens
  • ******
  • Post: 1.643
  • Punti reputazione: 1
    • Mostra profilo
    • La Tana di GlennHK
Re: Applicare funzione ad array numpy
« Risposta #7 il: Luglio 17, 2015, 17:14 »
[codice]
>>> a = np.arange(1, 4)
>>> b = np.arange(4, 10)
>>> np.outer(a,b)
array([[  4.,   5.,   6.,   7.,   8.,   9.],
       [  8.,  10.,  12.,  14.,  16.,  18.],
       [ 12.,  15.,  18.,  21.,  24.,  27.]])

>>> a[:,np.newaxis] * b
array([[  4.,   5.,   6.,   7.,   8.,   9.],
       [  8.,  10.,  12.,  14.,  16.,  18.],
       [ 12.,  15.,  18.,  21.,  24.,  27.]])
[/codice]

Offline GlennHK

  • python sapiens sapiens
  • ******
  • Post: 1.643
  • Punti reputazione: 1
    • Mostra profilo
    • La Tana di GlennHK
Re: Applicare funzione ad array numpy
« Risposta #8 il: Luglio 17, 2015, 17:22 »
Trovata  :D

Puoi riscrivere x/(1-x) come np.exp(scipy.special.logit(x))

Offline Claudio_F

  • python sapiens sapiens
  • ******
  • Post: 1.157
  • Punti reputazione: 2
    • Mostra profilo
Re: Applicare funzione ad array numpy
« Risposta #9 il: Luglio 17, 2015, 19:44 »
Che risultati strani (nel senso che la logit non sempre è più veloce della forma estesa con exp e log, e solo una volta è più veloce della forma base x/(1-x)):

[codice]
   eseguite 1M volte        x=singolo float  x_len=1   x_len=100   x_len=10000
-------------------------------------------------------------------------------
       x/(1.-x)             0.140s           2.729s    3.636s      67.982s

np.exp(np.log(1./(1.-x)))   2.953s           4.718s    12.090s     689.993s

    np.exp(logit(x))        2.888s           2.006s    9.896s      777.403s
[/codice]
« Ultima modifica: Luglio 17, 2015, 19:54 da Claudio_F »

Offline Claudio_F

  • python sapiens sapiens
  • ******
  • Post: 1.157
  • Punti reputazione: 2
    • Mostra profilo
Re: Applicare funzione ad array numpy
« Risposta #10 il: Luglio 17, 2015, 19:52 »
Riciclo il post doppio.

Intanto velo pietoso sul discorso su x/(1-x)... la funzione era x*(1-x)  :sick:

Ma al di la di questo continuo a trovare numpy molto più lento di Python liscio.

Contestualizzo: prendendo il via da qualche post fa sulle reti neurali volevo riscrivere per svago e ripasso l'algoritmo di una rete feed forward, dimensionabile a piacere come numero di strati e unità per strato, con apprendimento back propagation. L'algoritmo funziona regolarmente sia con Python liscio che con numpy, ma Python liscio va 7 (sette!) volte più veloce.

Riporto come esempio la sola procedura di esecuzione, premettendo che si basa su un array di strati, dove ogni strato è una struttura contenente un array di uscita 'y', una matrice pesi 'w', un array 'delta' per l'apprendimento, e nel caso di numpy anche un array 'bias' che nella versione "normale" è incorporato nella matrice pesi (che ha quindi una colonna in più).

Versione numpy:
[codice]def esegui(self, x):    # x = array ingressi
    for strato in self.strati[1:]:
        strato.y = expit((x * strato.w).sum(axis=1) + strato.bias)
        x = strato.y    # uscita diventa ingresso strato seguente
    return x
[/codice]

Versione liscia con ottimizzazioni per la velocità, quindi niente for enumerate, range, calcoli o risoluzione di nomi ripetuti:
[codice]def esegui(self, x):                    # x = array ingressi
    lenx = len(x)
    exp = math.exp
    for strato in self.strati[1:]:
        w = strato.w                    # matrice pesi dello strato
        y = strato.y                    # y = array uscite dello strato
        leny = len(y)
        j = 0
        while j < leny:                 # j = indice neurone nello strato
            wj = w[j]                   # array pesi del neurone j
            a = wj[-1]                  # attivazione iniziale = bias
            k = 0
            while k < lenx:             # k = indice peso del neurone
                a += wj[k] * x[k]       # somma contributi pesati ingressi
                k += 1
            y[j] = 1. / (1. + exp(-a))  # calcola attivita` y
            j += 1
        x = y
        lenx = leny
    return x
[/codice]

Gli strati sono così definiti:
[codice]class Strato(object):

    def __init__(self, ny, nx):
        self.y = np.array([0.] * ny, float)
        self.w = np.array( [[0.] * nx for _ in range(ny)], float)
        self.bias = np.array([0.] * ny, float)
        self.delta = np.array([0.] * ny, float)  # serve solo in apprendimento
[/codice]
[codice]class Strato(object):

    def __init__(self, num_uscite, num_ingressi):
        self.y = [0.] * num_uscite
        self.w = [[0.] * (num_ingressi + 1) for _ in range(num_uscite)]
        self.delta = [0.] * num_uscite  # serve solo in apprendimento[/codice]

Il tempo per eseguire un milione di volte di una rete 1-4-1 (un ingresso, un'uscita e uno strato intermedio di 4 unità) risulta di 3.25 secondi con Python liscio e 21.97 secondi con numpy.
« Ultima modifica: Luglio 18, 2015, 21:11 da Claudio_F »

Offline GlennHK

  • python sapiens sapiens
  • ******
  • Post: 1.643
  • Punti reputazione: 1
    • Mostra profilo
    • La Tana di GlennHK
Re: Applicare funzione ad array numpy
« Risposta #11 il: Luglio 17, 2015, 20:11 »
Uhm, sarà crappy l'implementazione della logit...

Edit: a me sembra buona... https://github.com/scipy/scipy/blob/master/scipy/special/_logit.c.src

Edit2: paradossalmente expit va veloce mentre logit è lenta... non so il motivo, ma a quanto pare ti conviene andare di x/(1-x). Anche perchè non conosco il dominio applicativo ma il dominio delle funzioni 2 e 3 è più ristretto della prima.
« Ultima modifica: Luglio 17, 2015, 22:47 da GlennHK »

Offline GlennHK

  • python sapiens sapiens
  • ******
  • Post: 1.643
  • Punti reputazione: 1
    • Mostra profilo
    • La Tana di GlennHK
Re: Applicare funzione ad array numpy
« Risposta #12 il: Agosto 13, 2015, 17:42 »
Mi ero perso il messaggio, sorry :D

Ma "(x * strato.w).sum(axis=1)" non è un dot product? Secondo me un np.dot sarebbe dieci volte più veloce.

Su che OS sviluppi? Hai MKL/Atlas/Blas+Lapack installati?

Offline riko

  • python deus
  • *
  • moderatore
  • Post: 7.453
  • Punti reputazione: 12
    • Mostra profilo
    • RiK0 Tech Temple
Re: Applicare funzione ad array numpy
« Risposta #13 il: Agosto 14, 2015, 15:27 »
Ma al di la di questo continuo a trovare numpy molto più lento di Python liscio.

Certo, perche' non hai capito come va usato numpy (non so se nella documentazione siano chiari o lo diano per scontato... ma qui se ne e' parlato spesso).
Numpy non e' veloce perche' ci hanno spolverato sopra la fatina della formula 1. Numpy e' veloce perche' ti permette di bypassare Python. Se tu pero' continui a scrivere Python, beh... grazie che va piano.

Numpy (come Matlab e tutte le librerie di calcolo matriciale in linguaggi di alto livello) funzionano sul principio di esprimere le operazioni in termini di operazioni matriciali. Non devi pensare in termini di for e while, ma devi pensare in termini di prodotto di matrici, inversione di matrici, versori, tensori e qualunque strumento dell'algebra lineare sia utile per esprimere il problema.

Se il tuo codice numpy usa pesantemente for e while e if, stai *sbagliando* approccio.

Il tuo codice fa proprio questo: usi numpy come modo di tenere cose in memoria, ma poi non usi *nessuna* delle cose che rendono grande numpy.