web-dev-qa-db-fra.com

Application de Spacy Parser à Pandas DataFrame w / Multiprocessing

Disons que j'ai un jeu de données, comme

iris = pd.DataFrame(sns.load_dataset('iris'))

Je peux utiliser Spacy et .apply pour analyser une colonne de chaînes en jetons (mon vrai jeu de données a> 1 mot/jeton par entrée bien sûr)

import spacy # (I have version 1.8.2)
nlp = spacy.load('en')
iris['species_parsed'] = iris['species'].apply(nlp)

résultat:

   sepal_length   ... species    species_parsed
0           1.4   ... setosa          (setosa)
1           1.4   ... setosa          (setosa)
2           1.3   ... setosa          (setosa)

Je peux également utiliser cette fonction de multitraitement pratique ( grâce à ce blog ) pour effectuer la plupart des fonctions d'application arbitraires sur une trame de données en parallèle:

from multiprocessing import Pool, cpu_count
def parallelize_dataframe(df, func, num_partitions):

    df_split = np.array_split(df, num_partitions)
    pool = Pool(num_partitions)
    df = pd.concat(pool.map(func, df_split))

    pool.close()
    pool.join()
    return df

par exemple:

def my_func(df):
    df['length_of_Word'] = df['species'].apply(lambda x: len(x))
    return df

num_cores = cpu_count()
iris = parallelize_dataframe(iris, my_func, num_cores)

résultat:

   sepal_length species  length_of_Word
0           5.1  setosa               6
1           4.9  setosa               6
2           4.7  setosa               6

... Mais pour une raison quelconque, je ne peux pas appliquer l'analyseur Spacy à une trame de données utilisant le multitraitement de cette façon.

def add_parsed(df):
    df['species_parsed'] = df['species'].apply(nlp)
    return df

iris = parallelize_dataframe(iris, add_parsed, num_cores)

résultat:

   sepal_length species  length_of_Word species_parsed
0           5.1  setosa               6             ()
1           4.9  setosa               6             ()
2           4.7  setosa               6             ()

Y a-t-il une autre façon de procéder? J'adore Spacy pour la PNL, mais j'ai beaucoup de données texte et je voudrais donc paralléliser certaines fonctions de traitement, mais j'ai rencontré ce problème.

17
Max Power

Spacy est hautement optimisé et effectue le multitraitement pour vous. Par conséquent, je pense que votre meilleur pari est de retirer les données du Dataframe et de les transmettre au pipeline Spacy sous forme de liste plutôt que d'essayer d'utiliser .apply directement.

Vous devez ensuite rassembler les résultats de l'analyse et les remettre dans le Dataframe.

Donc, dans votre exemple, vous pouvez utiliser quelque chose comme:

tokens = []
lemma = []
pos = []

for doc in nlp.pipe(df['species'].astype('unicode').values, batch_size=50,
                        n_threads=3):
    if doc.is_parsed:
        tokens.append([n.text for n in doc])
        lemma.append([n.lemma_ for n in doc])
        pos.append([n.pos_ for n in doc])
    else:
        # We want to make sure that the lists of parsed results have the
        # same number of entries of the original Dataframe, so add some blanks in case the parse fails
        tokens.append(None)
        lemma.append(None)
        pos.append(None)

df['species_tokens'] = tokens
df['species_lemma'] = lemma
df['species_pos'] = pos

Cette approche fonctionnera bien sur de petits ensembles de données, mais elle gruge votre mémoire, donc pas génial si vous voulez traiter d'énormes quantités de texte.

23
Ed Rushton