예제 #1
0
 public static void RunEndToEndTest(IList <Tree> binarizedTrees, Oracle oracle)
 {
     for (int index = 0; index < binarizedTrees.Count; ++index)
     {
         State state = ShiftReduceParser.InitialStateFromGoldTagTree(binarizedTrees[index]);
         while (!state.IsFinished())
         {
             OracleTransition gold = oracle.GoldTransition(index, state);
             NUnit.Framework.Assert.IsTrue(gold.transition != null);
             state = gold.transition.Apply(state);
         }
         NUnit.Framework.Assert.AreEqual(binarizedTrees[index], state.stack.Peek());
     }
 }
예제 #2
0
        /// <summary>
        /// Returns an attempt at a "gold" transition given the current state
        /// while parsing a known gold tree.
        /// </summary>
        /// <remarks>
        /// Returns an attempt at a "gold" transition given the current state
        /// while parsing a known gold tree.
        /// Tree is passed in by index so the oracle can precompute various
        /// statistics about the tree.
        /// If we already finalized, then the correct transition is to idle.
        /// If the stack is empty, shift is the only possible answer.
        /// If the first item on the stack is a correct span, correctly
        /// labeled, and it has unaries transitions above it, then if we are
        /// not doing compound unaries, the next unary up is the correct
        /// answer.  If we are doing compound unaries, and the state does not
        /// already have a transition, then the correct answer is a compound
        /// unary transition to the top of the unary chain.
        /// If the first item is the entire tree, with no remaining unary
        /// transitions, then we need to finalize.
        /// If the first item is a correct span, with or without a correct
        /// label, and there are no unary transitions to be added, then we
        /// must look at the next parent.  If it has the same left side, then
        /// we return a shift transition.  If it has the same right side,
        /// then we look at the next subtree on the stack (which must exist).
        /// If it is also correct, then the transition is to combine the two
        /// subtrees with the correct label and side.
        /// TODO: suppose the correct label is not either child label and the
        /// children are binarized states?  We should see what the
        /// debinarizer does in that case.  Perhaps a post-processing step
        /// If the previous stack item is too small, then any binary reduce
        /// action is legal, with no gold transition.  TODO: can this be improved?
        /// If the previous stack item is too large, perhaps because of
        /// incorrectly attached PP/SBAR, for example, we still need to
        /// binary reduce.  TODO: is that correct?  TODO: we could look back
        /// further in the stack to find hints at a label that would work
        /// better, for example
        /// If the current item is an incorrect span, then look at the
        /// containing item.  If it has the same left side, shift.  If it has
        /// the same right side, binary reduce (producing an exact span if
        /// possible).  If neither edge is correct, then any of shift or
        /// binary reduce are acceptable, with no gold transition.  TODO: can
        /// this be improved?
        /// </remarks>
        internal virtual OracleTransition GoldTransition(int index, State state)
        {
            if (state.finished)
            {
                return(new OracleTransition(new IdleTransition(), false, false, false));
            }
            if (state.stack.Size() == 0)
            {
                return(new OracleTransition(new ShiftTransition(), false, false, false));
            }
            IDictionary <Tree, Tree> parents = parentMaps[index];
            Tree             gold            = binarizedTrees[index];
            IList <Tree>     leaves          = leafLists[index];
            Tree             S0          = state.stack.Peek();
            Tree             enclosingS0 = GetEnclosingTree(S0, parents, leaves);
            OracleTransition result      = GetUnaryTransition(S0, enclosingS0, parents, compoundUnaries);

            if (result != null)
            {
                return(result);
            }
            // TODO: we could interject that all trees must end with ROOT, for example
            if (state.tokenPosition >= state.sentence.Count && state.stack.Size() == 1)
            {
                return(new OracleTransition(new FinalizeTransition(rootStates), false, false, false));
            }
            if (state.stack.Size() == 1)
            {
                return(new OracleTransition(new ShiftTransition(), false, false, false));
            }
            if (SpansEqual(S0, enclosingS0))
            {
                Tree parent = parents[enclosingS0];
                // cannot be root
                while (SpansEqual(parent, enclosingS0))
                {
                    // in case we had missed unary transitions
                    enclosingS0 = parent;
                    parent      = parents[parent];
                }
                if (parent.Children()[0] == enclosingS0)
                {
                    // S0 is the left child of the correct tree
                    return(new OracleTransition(new ShiftTransition(), false, false, false));
                }
                // was the second (right) child.  there must be something else on the stack...
                Tree S1          = state.stack.Pop().Peek();
                Tree enclosingS1 = GetEnclosingTree(S1, parents, leaves);
                if (SpansEqual(S1, enclosingS1))
                {
                    // the two subtrees should be combined
                    return(new OracleTransition(new BinaryTransition(parent.Value(), ShiftReduceUtils.GetBinarySide(parent)), false, false, false));
                }
                return(new OracleTransition(null, false, true, false));
            }
            if (ShiftReduceUtils.LeftIndex(S0) == ShiftReduceUtils.LeftIndex(enclosingS0))
            {
                return(new OracleTransition(new ShiftTransition(), false, false, false));
            }
            if (ShiftReduceUtils.RightIndex(S0) == ShiftReduceUtils.RightIndex(enclosingS0))
            {
                Tree S1          = state.stack.Pop().Peek();
                Tree enclosingS1 = GetEnclosingTree(S1, parents, leaves);
                if (enclosingS0 == enclosingS1)
                {
                    // BinaryTransition with enclosingS0's label, either side, but preferring LEFT
                    return(new OracleTransition(new BinaryTransition(enclosingS0.Value(), BinaryTransition.Side.Left), false, false, true));
                }
                // S1 is smaller than the next tree S0 is supposed to be part of,
                // so we must have a BinaryTransition
                if (ShiftReduceUtils.LeftIndex(S1) > ShiftReduceUtils.LeftIndex(enclosingS0))
                {
                    return(new OracleTransition(null, false, true, true));
                }
                // S1 is larger than the next tree.  This is the worst case
                return(new OracleTransition(null, true, true, true));
            }
            // S0 doesn't match either endpoint of the enclosing tree
            return(new OracleTransition(null, true, true, true));
        }
예제 #3
0
        private Pair <int, int> TrainTree(int index, IList <Tree> binarizedTrees, IList <IList <ITransition> > transitionLists, IList <PerceptronModel.Update> updates, Oracle oracle)
        {
            int              numCorrect = 0;
            int              numWrong   = 0;
            Tree             tree       = binarizedTrees[index];
            ReorderingOracle reorderer  = null;

            if (op.TrainOptions().trainingMethod == ShiftReduceTrainOptions.TrainingMethod.ReorderOracle || op.TrainOptions().trainingMethod == ShiftReduceTrainOptions.TrainingMethod.ReorderBeam)
            {
                reorderer = new ReorderingOracle(op);
            }
            // TODO.  This training method seems to be working in that it
            // trains models just like the gold and early termination methods do.
            // However, it causes the feature space to go crazy.  Presumably
            // leaving out features with low weights or low frequencies would
            // significantly help with that.  Otherwise, not sure how to keep
            // it under control.
            if (op.TrainOptions().trainingMethod == ShiftReduceTrainOptions.TrainingMethod.Oracle)
            {
                State state = ShiftReduceParser.InitialStateFromGoldTagTree(tree);
                while (!state.IsFinished())
                {
                    IList <string>     features   = featureFactory.Featurize(state);
                    ScoredObject <int> prediction = FindHighestScoringTransition(state, features, true);
                    if (prediction == null)
                    {
                        throw new AssertionError("Did not find a legal transition");
                    }
                    int              predictedNum = prediction.Object();
                    ITransition      predicted    = transitionIndex.Get(predictedNum);
                    OracleTransition gold         = oracle.GoldTransition(index, state);
                    if (gold.IsCorrect(predicted))
                    {
                        numCorrect++;
                        if (gold.transition != null && !gold.transition.Equals(predicted))
                        {
                            int transitionNum = transitionIndex.IndexOf(gold.transition);
                            if (transitionNum < 0)
                            {
                                // TODO: do we want to add unary transitions which are
                                // only possible when the parser has gone off the rails?
                                continue;
                            }
                            updates.Add(new PerceptronModel.Update(features, transitionNum, -1, learningRate));
                        }
                    }
                    else
                    {
                        numWrong++;
                        int transitionNum = -1;
                        if (gold.transition != null)
                        {
                            transitionNum = transitionIndex.IndexOf(gold.transition);
                        }
                        // TODO: this can theoretically result in a -1 gold
                        // transition if the transition exists, but is a
                        // CompoundUnaryTransition which only exists because the
                        // parser is wrong.  Do we want to add those transitions?
                        updates.Add(new PerceptronModel.Update(features, transitionNum, predictedNum, learningRate));
                    }
                    state = predicted.Apply(state);
                }
            }
            else
            {
                if (op.TrainOptions().trainingMethod == ShiftReduceTrainOptions.TrainingMethod.Beam || op.TrainOptions().trainingMethod == ShiftReduceTrainOptions.TrainingMethod.ReorderBeam)
                {
                    if (op.TrainOptions().beamSize <= 0)
                    {
                        throw new ArgumentException("Illegal beam size " + op.TrainOptions().beamSize);
                    }
                    IList <ITransition>   transitions = Generics.NewLinkedList(transitionLists[index]);
                    PriorityQueue <State> agenda      = new PriorityQueue <State>(op.TrainOptions().beamSize + 1, ScoredComparator.AscendingComparator);
                    State goldState = ShiftReduceParser.InitialStateFromGoldTagTree(tree);
                    agenda.Add(goldState);
                    // int transitionCount = 0;
                    while (transitions.Count > 0)
                    {
                        ITransition           goldTransition = transitions[0];
                        ITransition           highestScoringTransitionFromGoldState = null;
                        double                highestScoreFromGoldState             = 0.0;
                        PriorityQueue <State> newAgenda = new PriorityQueue <State>(op.TrainOptions().beamSize + 1, ScoredComparator.AscendingComparator);
                        State highestScoringState       = null;
                        State highestCurrentState       = null;
                        foreach (State currentState in agenda)
                        {
                            bool           isGoldState = (op.TrainOptions().trainingMethod == ShiftReduceTrainOptions.TrainingMethod.ReorderBeam && goldState.AreTransitionsEqual(currentState));
                            IList <string> features    = featureFactory.Featurize(currentState);
                            ICollection <ScoredObject <int> > stateTransitions = FindHighestScoringTransitions(currentState, features, true, op.TrainOptions().beamSize, null);
                            foreach (ScoredObject <int> transition in stateTransitions)
                            {
                                State newState = transitionIndex.Get(transition.Object()).Apply(currentState, transition.Score());
                                newAgenda.Add(newState);
                                if (newAgenda.Count > op.TrainOptions().beamSize)
                                {
                                    newAgenda.Poll();
                                }
                                if (highestScoringState == null || highestScoringState.Score() < newState.Score())
                                {
                                    highestScoringState = newState;
                                    highestCurrentState = currentState;
                                }
                                if (isGoldState && (highestScoringTransitionFromGoldState == null || transition.Score() > highestScoreFromGoldState))
                                {
                                    highestScoringTransitionFromGoldState = transitionIndex.Get(transition.Object());
                                    highestScoreFromGoldState             = transition.Score();
                                }
                            }
                        }
                        // This can happen if the REORDER_BEAM method backs itself
                        // into a corner, such as transitioning to something that
                        // can't have a FinalizeTransition applied.  This doesn't
                        // happen for the BEAM method because in that case the correct
                        // state (eg one with ROOT) isn't on the agenda so it stops.
                        if (op.TrainOptions().trainingMethod == ShiftReduceTrainOptions.TrainingMethod.ReorderBeam && highestScoringTransitionFromGoldState == null)
                        {
                            break;
                        }
                        State newGoldState = goldTransition.Apply(goldState, 0.0);
                        // if highest scoring state used the correct transition, no training
                        // otherwise, down the last transition, up the correct
                        if (!newGoldState.AreTransitionsEqual(highestScoringState))
                        {
                            ++numWrong;
                            IList <string> goldFeatures   = featureFactory.Featurize(goldState);
                            int            lastTransition = transitionIndex.IndexOf(highestScoringState.transitions.Peek());
                            updates.Add(new PerceptronModel.Update(featureFactory.Featurize(highestCurrentState), -1, lastTransition, learningRate));
                            updates.Add(new PerceptronModel.Update(goldFeatures, transitionIndex.IndexOf(goldTransition), -1, learningRate));
                            if (op.TrainOptions().trainingMethod == ShiftReduceTrainOptions.TrainingMethod.Beam)
                            {
                                // If the correct state has fallen off the agenda, break
                                if (!ShiftReduceUtils.FindStateOnAgenda(newAgenda, newGoldState))
                                {
                                    break;
                                }
                                else
                                {
                                    transitions.Remove(0);
                                }
                            }
                            else
                            {
                                if (op.TrainOptions().trainingMethod == ShiftReduceTrainOptions.TrainingMethod.ReorderBeam)
                                {
                                    if (!ShiftReduceUtils.FindStateOnAgenda(newAgenda, newGoldState))
                                    {
                                        if (!reorderer.Reorder(goldState, highestScoringTransitionFromGoldState, transitions))
                                        {
                                            break;
                                        }
                                        newGoldState = highestScoringTransitionFromGoldState.Apply(goldState);
                                        if (!ShiftReduceUtils.FindStateOnAgenda(newAgenda, newGoldState))
                                        {
                                            break;
                                        }
                                    }
                                    else
                                    {
                                        transitions.Remove(0);
                                    }
                                }
                            }
                        }
                        else
                        {
                            ++numCorrect;
                            transitions.Remove(0);
                        }
                        goldState = newGoldState;
                        agenda    = newAgenda;
                    }
                }
                else
                {
                    if (op.TrainOptions().trainingMethod == ShiftReduceTrainOptions.TrainingMethod.ReorderOracle || op.TrainOptions().trainingMethod == ShiftReduceTrainOptions.TrainingMethod.EarlyTermination || op.TrainOptions().trainingMethod == ShiftReduceTrainOptions.TrainingMethod
                        .Gold)
                    {
                        State state = ShiftReduceParser.InitialStateFromGoldTagTree(tree);
                        IList <ITransition> transitions = transitionLists[index];
                        transitions = Generics.NewLinkedList(transitions);
                        bool keepGoing = true;
                        while (transitions.Count > 0 && keepGoing)
                        {
                            ITransition    transition    = transitions[0];
                            int            transitionNum = transitionIndex.IndexOf(transition);
                            IList <string> features      = featureFactory.Featurize(state);
                            int            predictedNum  = FindHighestScoringTransition(state, features, false).Object();
                            ITransition    predicted     = transitionIndex.Get(predictedNum);
                            if (transitionNum == predictedNum)
                            {
                                transitions.Remove(0);
                                state = transition.Apply(state);
                                numCorrect++;
                            }
                            else
                            {
                                numWrong++;
                                // TODO: allow weighted features, weighted training, etc
                                updates.Add(new PerceptronModel.Update(features, transitionNum, predictedNum, learningRate));
                                switch (op.TrainOptions().trainingMethod)
                                {
                                case ShiftReduceTrainOptions.TrainingMethod.EarlyTermination:
                                {
                                    keepGoing = false;
                                    break;
                                }

                                case ShiftReduceTrainOptions.TrainingMethod.Gold:
                                {
                                    transitions.Remove(0);
                                    state = transition.Apply(state);
                                    break;
                                }

                                case ShiftReduceTrainOptions.TrainingMethod.ReorderOracle:
                                {
                                    keepGoing = reorderer.Reorder(state, predicted, transitions);
                                    if (keepGoing)
                                    {
                                        state = predicted.Apply(state);
                                    }
                                    break;
                                }

                                default:
                                {
                                    throw new ArgumentException("Unexpected method " + op.TrainOptions().trainingMethod);
                                }
                                }
                            }
                        }
                    }
                }
            }
            return(Pair.MakePair(numCorrect, numWrong));
        }