Optimiser une somme avec numpy

Le problème exposé dans ce sujet a été résolu.

Bonjour,

Je travaille sur la classification de documents avec la méthode de Bernoulli. J’utilise Python 3.5 et numpy pour faire mes calculs :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
"""
Un document est de la forme :
{
    'class': 0,
    'words': {
        0:5, 3:6, # word:count si count > 0
    }
}
"""

def classes_distrib(data):
    distrib = np.zeros(N_CLASSES)

    for doc in data:
        distrib[doc['class']] += 1

    return distrib


def has_word(doc, word):
    return word in doc['words']


def bernoulli_training(data, voc_size):
    N = classes_distrib(data)
    Pi = N / len(data)
    df = np.zeros((N_CLASSES, voc_size))

    for doc in data:
        for word in doc['words']:
            df[doc['class'], word] += 1

    # Laplace smoothing
    PC = df + 1
    for k in range(N_CLASSES):
        PC[k] /= N[k] + 2

    return Pi, PC


def bernoulli_test(doc, Pi, PC, voc_size):
    PiF = np.log(Pi)

    for k in range(N_CLASSES):
        for word in range(voc_size):
            if has_word(doc, word):
                PiF[k] += np.log(PC[k][word])
            else:
                PiF[k] += np.log(1 - PC[k][word])

    return np.argmax(PiF)

Seulement, mes boucles dans ma fonction de test ne me semblent pas optimales et je me demandais si numpy me permettait de faire ça plus efficacement. Je suis tombé là-dessus mais passer par une liste en compéhension prendrait de la place inutilement. Et je ne vois pas comment vectoriser ma fonction has_word.

Merci !

+0 -0

Bonjour,

mes boucles dans ma fonction de test ne me semblent pas optimales

« Sembler », quand on fait de l’optimisation, est souvent insuffisant (même si boucle, python et performance ne font pas bon ménage, on est d’accord).

On peut facilement connaitre le temps passé dans chaque fonction à l’aide d’un petit décorateur (je crois que j’ai piqué le code à Sam&Max),

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
__dev_time = []

def compute_time(fn):
    def wrap(*args, **kwargs):
        t = time.time()
        res = fn(*args, **kwargs)
        __dev_time.append((fn.__name__, time.time() - t))
        return res
    return wrap

@compute_time
def bernoulli_test(doc, Pi, PC, voc_size):
    ...

puis lire le contenu de __dev_time.


Pour virer des boucles explicites, tu peux utiliser le fait que numpy sait faire des opérations sur les matrices. Ainsi,

1
2
3
4
5
6
7
PC = df + 1
for k in range(N_CLASSES):
    PC[k] /= N[k] + 2

# équivalent :

PC = (df + 1) / (N + 2)

N’hésite à utiliser np.sum (ça supprime facilement la boucle en k de bernoulli_test).

Après, quels temps de calcul as-tu, et que vises-tu ?

+1 -0

Oui ou on utilise un vrai profiler, python est bien fournit de ce coté

Ayant toujours fait mes codes mes profilages à l’arrache, je veux bien savoir ce que tu recommandes (dans le genre clair et simple) ?

Parce que personnellement, à part time() en python et gprof en C/C++/Fortran, j’y connais que dalle (mais ça m’a toujours suffit1).


  1. Enfin bon, je n’utilise pas de débogueur, donc je ne suis pas une référence. :-°  

+0 -0

« Sembler », quand on fait de l’optimisation, est souvent insuffisant (même si boucle, python et performance ne font pas bon ménage, on est d’accord).

En l’occurrence, c’est de l’optimisation de confort, pour ne pas attendre trop longtemps et pour apprendre à utiliser numpy de façon optimale.

Pour virer des boucles explicites, tu peux utiliser le fait que numpy sait faire des opérations sur les matrices. Ainsi,

1
2
3
4
5
6
7
PC = df + 1
for k in range(N_CLASSES):
    PC[k] /= N[k] + 2

# équivalent :

PC = (df + 1) / (N + 2)

Merci, j’ignorais le concept de broadcasting.

N’hésite à utiliser np.sum (ça supprime facilement la boucle en k de bernoulli_test).

J’ignore si ce que j’ai fait correspond à ce à quoi tu pensais :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
def bernoulli_test(words, Pi, PC, voc_size):
    PC = np.copy(PC)

    # Words in the document
    wid = np.array([word in words for word in range(voc_size)])
    PC[wid] = np.log(PC[wid])

    # Words not in the document
    wid = np.logical_not(wid)
    PC[wid] = np.log(1 - PC[wid])

    PiF = np.sum(PC, axis=0)  # Sum over the rows, i.e. the words
    PiF += np.log(Pi)

    return np.argmax(PiF)

Merci pour ta réponse. ^^

+1 -0
Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte