Comment utiliser NLTK pour générer des phrases à partir d'une grammaire induite?

j'ai une (grande) liste de phrases analysées (qui ont été analysées en utilisant le Stanford parser), par exemple, la phrase "Maintenant, vous pouvez être divertis" a l'arbre suivant:

(ROOT
  (S
    (ADVP (RB Now))
    (, ,)
    (NP (PRP you))
    (VP (MD can)
      (VP (VB be)
        (VP (VBN entertained))))
    (. .)))

j'utilise l'ensemble des arbres de phrases pour induire une grammaire en utilisant nltk:

import nltk

# ... for each sentence tree t, add its production to allProductions
allProductions += t.productions()

# Induce the grammar
S = nltk.Nonterminal('S')
grammar = nltk.induce_pcfg(S, allProductions)

Maintenant je voudrais utiliser grammar pour générer de nouvelles phrases aléatoires. Mon espoir est que, depuis la grammaire a été appris à partir d'un ensemble d'exemples d'entrées, puis les peines seront générés sémantiquement similaires. Je peux faire ça à nltk?

Si je ne peux pas utiliser nltk pour ce faire, d'autres outils existent, qui peuvent prendre la (éventuellement reformaté) grammar et de générer des phrases?

13
demandé sur stepthom 2013-02-21 22:27:37

5 réponses

dans NLTK 2.0 vous pouvez utiliser nltk.parse.generate pour générer possible phrases pour une grammaire.

ce code définit une fonction qui devrait générer une seule phrase basée sur les règles de production dans un (P)CFG.

# This example uses choice to choose from possible expansions
from random import choice
# This function is based on _generate_all() in nltk.parse.generate
# It therefore assumes the same import environment otherwise.
def generate_sample(grammar, items=["S"]):
    frags = []
    if len(items) == 1:
        if isinstance(items[0], Nonterminal):
            for prod in grammar.productions(lhs=items[0]):
                frags.append(generate_sample(grammar, prod.rhs()))
        else:
            frags.append(items[0])
    else:
        # This is where we need to make our changes
        chosen_expansion = choice(items)
        frags.append(generate_sample,chosen_expansion)
    return frags

pour utiliser les poids de votre PCFG, vous voudrez évidemment utiliser une meilleure méthode d'échantillonnage que choice(), qui suppose implicitement que toutes les extensions du noeud courant sont équiprobables.

13
répondu dmh 2016-12-29 02:35:18

tout d'abord, si vous générez des phrases aléatoires, elles peuvent être sémantiquement correctes, mais elles perdront probablement leur sens.

(Il me semble un peu comme ceux d'étudiants du MIT ont fait avec leur programme SCIgen qui est Auto-Génératrice de papier scientifique. Très intéressant btw.)

de toute façon, je ne l'ai jamais fait moi-même, mais cela semble possible avec nltk.bigrams, vous risquez de façon à jeter un coup d'oeil Production Aléatoire Texte avec Bigrams.

Vous pouvez aussi générer tous les sous-arbres d'un arbre, je ne suis pas sûr si c'est ce que vous voulez.

4
répondu ForceMagic 2018-10-06 03:44:39

avec un nltk Texte object vous pouvez appeler ' generate ()' sur celui-ci qui "imprimera du texte aléatoire, généré en utilisant un modèle de langage trigramme."http://nltk.org/_modules/nltk/text.html

2
répondu Ryan O'Neill 2013-03-23 22:39:35

ma solution pour générer une phrase aléatoire à partir d'un nltk existant.Cfg grammar:

def generate_sample(grammar, prod, frags):        
    if prod in grammar._lhs_index: # Derivation
        derivations = grammar._lhs_index[prod]            
        derivation = random.choice(derivations)            
        for d in derivation._rhs:            
            generate_sample(grammar, d, frags)
    elif prod in grammar._rhs_index:
        # terminal
        frags.append(str(prod))

Et maintenant, il peut être utilisé:

frags = []  
generate_sample(grammar, grammar.start(), frags)
print( ' '.join(frags) )
2
répondu acimutal 2017-12-22 08:56:59

inspiré par ce qui précède, en voici un qui utilise l'itération au lieu de la récursion.

import random

def rewrite_at(index, replacements, the_list):
    del the_list[index]
    the_list[index:index] = replacements

def generate_sentence(grammar):
    sentence_list = [grammar.start()]
    all_terminals = False
    while not all_terminals:
        all_terminals = True
        for position, symbol in enumerate(sentence_list):
            if symbol in grammar._lhs_index:
                all_terminals = False
                derivations = grammar._lhs_index[symbol]
                derivation = random.choice(derivations) # or weighted_choice(derivations) if you have a function for that
                rewrite_at(position, derivation.rhs(), sentence_list)
    return sentence_list

Ou si vous voulez l'arbre de la dérivation de celui-ci.

from nltk.tree import Tree

def tree_from_production(production):
    return Tree(production.lhs(), production.rhs())

def leaf_positions(the_tree):
    return [the_tree.leaf_treeposition(i) for i in range(len(the_tree.leaves()))]

def generate_tree(grammar):
    initial_derivations = grammar._lhs_index[grammar.start()]
    initial_derivation = random.choice(initial_derivations) # or weighed_choice if you have that function
    running_tree = tree_from_production(initial_derivation)
    all_terminals = False
    while not all_terminals:
        all_terminals = True
        for position in leaf_positions(running_tree):
            node_label = running_tree[position]
            if node_label in grammar._lhs_index:
                all_terminals = False
                derivations = grammar._lhs_index[node_label]
                derivation = random.choice(derivations) # or weighed_choice if you have that function
                running_tree[position] = tree_from_production(derivation)
    return running_tree

Voici une fonction pighted_choice pour les règles de production NLTK PCFG à utiliser avec ce qui précède, adapté de la réponse de Ned Batchelder ici pour les fonctions de choix pondérées en général:

def weighted_choice(productions):
    prods_with_probs = [(prod, prod.prob()) for prod in productions]
    total = sum(prob for prod, prob in prods_with_probs)
    r = random.uniform(0, total)
    upto = 0
    for prod, prob in prods_with_probs:
        if upto + prob >= r:
            return prod
        upto += prob
    assert False, "Shouldn't get here"
0
répondu David Schueler 2018-06-23 21:55:51