Skip links
Main content

Surface realisation: plain and simple

zaterdag 16 november 2013 12:38

Or: a method for generating a proper sentence from a set of relations

Up until now I did not really create new sentences. I used the phrase structure of the question to generate the response [link]. There were two problems with this.

I had started out creating a phrase structure representation of a sentence. This structure I later used to generate the answer. Later I found that it was easier to use a semantic representation, based on logical relations, to find the answer to the question. The phrase structure was now used only to generate the answer and it had proven rather difficult to build. I made heavy (too heavy) use of unification techniques to build it, and the problems I ran into can best be compared to a fly that gets entangled in a spider's web.

Also, the uneasy feeling had always been there that I could not really create new sentences. I had only been able to create responses to questions. I would not be able to create new sentences from scratch. Or, if I could, I would need to specify the complete syntactic tree structure of the sentence.

These motives lead me to a search for a more direct creation of a sentence from the semantic relations with which I worked. I wanted to find a simple way to turn a set of relations directly into a sentence. A process called surface realisation.

I believe I found such a method.

Input

In their magnificent book "Building natural language generation systems", Ehud Reiter and Robert Dale discern the possible input representations of a surface realiser. The representation I work with falls in the category of "meaning specification" (paragraph 6.3.2). It is a representation of meaning that is close or identical to the semantic form, and does not specify any lexical entries.

As a logical form I have learned to love neo-Davidsonian semantics, as popularized by Terence Parsons in his seminal work "Events in the semantics of english". It turns events into "first class citizens" and creates separate relations for subject and object: subject(?event, ?subject), object(?event, ?object)

The idea of using properties to pass data from one rule to the next was inspired by the use of "registers" (or roles) as described in chapter 5.2 of Terry Winograd's work "Language as a cognitive process: syntax", even though in that context they were used in Augmented Transition Networks.

Finally, I represent verb predicates as atoms, as seen in "Speech and Language processing", By Jurafsky and Martin (Int'l edition, paragraph 14.4), a process called "reification". For example: I write isa(?event, Walk) to describe a walking event. More generally, I use only a fixed set of predicates to represent every possible thing. The rules I define are based on these predicates and therefore there number must be fixed and limited.

This is the example I will work out here:

mood(?event, Declarative)
tense(?event, Past)
isa(?event, Embarrass)
subject(?event, ?subject)
object(?event, ?object)
name(?subject, "Reginald")
name(?object, "Oswald")

The sentence we want to realize is:

Reginald embarrassed Oswald.

The example is deliberately kept very simple to explain the basic technique.

Structures

Here is the syntactic structure that should emerge in the realization process. This structure is not provided as input. I am just showing it to make it easier to follow the process.

S
|
+--NP
|   |
|   +--propernoun: "Reginald"
|
+--VP
    |
    +--verb: "embarasses"
    |
    +--NP
        |
        +--propernoun: "Oswald"

For the rest of the explanation it is also necessary to know that each of the nodes in this tree has a fixed set of properties:

S (event, subject, object)
|
+--NP (entity)
|   |
|   +--propernoun (entity)
|
+--VP (event, subject, object)
    |
    +--verb (event)
    |
    +--NP (entity)
        |
        +--propernoun (entity)

The entity of a propernoun is the same as the entity of above NP. The event of a verb is the same as the event of the above VP. This will be enforced by the generation rules.

Generation: S

Let's start. The generator is provided with a grammar that contains a set of generation rules. All rules are checked from top to bottom. The first one that matches, fires. This is the first matching rule:

[
    rule:
        S => NP VP,
    condition:
        mood(S.event, Declarative) and subject(S.event, S.subject),
    bind: {
        NP.entity = S.subject;
        VP.event = S.event
    }
]

It matches because the relations provided as input match the condition of the rule:

mood(S.event, Declarative) and subject(S.event, S.subject)

While matching, the properties of S are bound to the relations that match the condition:

Condition Input Binding
 mood(S.event, Declarative)  mood(?event, Declarative)  S.event = ?event
 subject(S.event, S.subject)  subject(?event, ?subject)  S.event = ?event, S.subject = ?subject

The rule says: generate the surface representation "NP VP". It means: in the sentence to be generated, the NP phrase will precede the VP phrase. This does not tell us which words to expect yet, but it will tell us already something about the emerging sentence.

Next, the properties of the parent node (S) are passed on to the children (NP and VP):

NP.entity = S.subject;
VP.event = S.event

The property value of "subject" of node S is assigned to property "entity" of node NP. The property value of "event" of node S is assigned to property "event" of node VP.

This means:

NP.entity = ?subject
VP.event = ?event

Generation: NP

The rule we just handled left us with two new goals: NP and VP.

For NP, the generator looks for a rule with NP as its antecedent. There are many of these rules, and the first one that matches the condition will be used. This is it:

[
    rule:
        NP => propernoun,
    condition:
        name(NP.entity, ?name),
    bind: {
        propernoun.entity = NP.entity
    }
]

The condition says:

name(NP.entity, ?name)

From the previous rule we inherit

NP.entity = ?subject

so the bound condition is

name(?subject, ?name)

This matches the following relation in the input

name(?subject, "Reginald")

and the variable ?name is bound to the constant "Reginald"

?name = "Reginald"

Note that property bindings are passed from parent node to child node. Variables stay within the scope of a single rule. For example: the variable ?name is active in this rule, but will not be passed on elsewhere.

The "bind" is applied and this gives us:

propernoun.entity = ?subject

So the original subject was passed from the S, to the NP and will now be passed on to the propernoun node.

Note also, that all rules have direct access to all relations in the input. This provides great flexibility on the part of the conditions.

Still no word has been realized, but this will now change.

Generation: propernoun

The following rule now matches:

[
    rule:
        propernoun => word,
    condition:
        name(propernoun.entity, ?name),
    word:
        name(propernoun.entity, ?name)
]

The condition matches with this relation in the input:

name(?subject, "Reginald")

You will find that this rule has the form "propernoun => word". To the generator this "word" means that an actual word is realized here. And this word will be sought in the lexicon. The generator will look through the lexicon for a word that satisfies this condition:

name(?subject, "Reginald")

In this case, the word is not really available in the lexicon, because it is the name of a person, but the generator has a special routine that knows how to create the surface representation of this relation. It yields simply:

"Reginald"

The first word has been found! Let's continue with the remaining open goal: we must find a rule for our VP!

Generation: VP

Here is the rule that matches:

[
    rule:
        VP => simpleVerb NP,
    condition:
        isa(VP.event, ?type) and object(VP.event, VP.object),
    bind: {
        simpleVerb.event = VP.event;
        NP.entity = VP.object
    }
]

Note especially that the VP.object was _not_ passed on from the parent node S. It is introduced here as it is bound to the condition:

VP.object = ?object

Due to the "bind" of this rule, ?object is now passed on to the NP:

NP.entity = ?object

On to the simpleVerb realization!

Generation: simpleVerb

This rule matches:

[
    rule:
        simpleVerb => word,
    condition:
        isa(verb.event, ?type) and tense(verb.event, ?tense),
    word:
        isa(verb.event, ?type) and tense(verb.event, ?tense)
]

because this condition matches:

isa(?event, Embarrass)

and

tense(?event, Past)

and ?tense is bound to Past

?tense = Past

in the input. Since this is a "word" rule, the generator will now look through the lexicon for a word that matches

isa(?event, Embarrass) and tense(?event, Past)

This is the lexical entry that it finds:

[
    form: 'embarrassed',
    partOfSpeech: 'simpleVerb',
    semantics: isa(this.event, Embarrass) and tense(this.event, Past)
]

and so the word "embarrassed" is realized.

Generate the second NP

There is now still an open goal for the second NP that was encountered during generation. It proceeds in the same way as the first and it finally realizes the propernoun "Oswald".

Concluding words

I realise that all this may not really look "plain and simple" to you :) But the thing is that I found only methods that were still much more complicated, while reading about this stuff.

It has been a great pleasure for me to be able to give you a worked out example of an actual process of surface realisation. Even in "Building natural language generation systems" you will not find out such an example. It is considered a solved problem, left to existing software packages. The problem with this is that these packages expect a special form of input that may not be the one I want. And I expect the same holds for you too. I think the above process could at least give you an idea about the inner workings of realisation. Use it to your advantage.

Labels
nlp

« Terug