I am attempting to write a small dependency parser using a multiclass perceptron as the learner. The parser is a simple machine that has a few actions: START_ARC, MOVE_RIGHT, END_ARC and to move forwards to the next token that should be treated. I have the code for it below: class DependencyParser1: """ self.token_index must be manually increased along with the sentence, as each token has been processed. Do it as soon as self.start_arc() has been called. self.move_right() only moves the move pointer to the next token, while self.token_index keeps track of the token that is to be assigned a head. Each arc is a tuple [x, y] where x is the start, and y is the head. """ def __init__(self): self.move_index = 0 self.token_index = -1 self.arcs = [] self.arc = [0, 0] def move_right(self): self.move_index += 1 def start_arc(self): self.arc[0] = self.token_index + 1 def end_arc(self): self.arc[1] = 0 if self.move_index == 0 else self.move_index + 1 self.arcs.append(self.arc) self.arc = [0, 0] self.move_index = 0 def reset(self): self.arcs = [] self.arc = [0, 0] self.token_index = - 1 self.move_index = 0 The trainer looks like this: class Trainer: def __init__(self, parser): self.parser = parser self.features = [] self.classes = [] def train(self, sentence): arc_start_index = 0 # 0 is the first token. The root # is numbered 0, the rest of the # tokens are numbered as the index # plus 1. sentence_length = len(sentence) while arc_start_index < sentence_length: self.parser.token_index += 1 self.parser.start_arc() self.features.append(FeaturesRecorder.record(locals())) self.classes.append(START_ARC) arc_end_index = 0 head = int(sentence[arc_start_index].head) - 1 while arc_end_index < head: arc_end_index += 1 self.parser.move_right() self.features.append(FeaturesRecorder.record(locals())) self.classes.append(MOVE_RIGHT) self.parser.end_arc() self.features.append(FeaturesRecorder.record(locals())) self.classes.append(END_ARC) arc_start_index += 1 The trainer can accurately reproduce the arcs of existing treebank data when being fed the actions that the trainer learned. class ParserRunner: def __init__(self, parser, perceptron): self.parser = parser self.perceptron = perceptron def cheat(self, sentence, actions): actions_copy = actions.copy() arc_start_index = 0 sentence_length = len(sentence) while actions_copy: action = actions_copy.pop(0) if action == START_ARC: self.parser.token_index += 1 self.parser.start_arc() elif action == MOVE_RIGHT: self.parser.move_right() elif action == END_ARC: self.parser.end_arc() return self.parser.arcs def parse(self, sentence): arc_start_index = 0 sentence_length = len(sentence) arc_is_started = 0 while arc_start_index < sentence_length: recording = FeaturesRecorder.record(locals()) print(recording) action = self.perceptron.predict([recording]) print(action) #print(action) if action == START_ARC: self.parser.token_index += 1 self.parser.start_arc() arc_is_started = 1 elif action == MOVE_RIGHT: self.parser.move_right() elif action == END_ARC: self.parser.end_arc() arc_start_index += 1 return self.parser.arcs I have encountered an error where the predictions that the perceptron makes are just to end the current arc without doing anything else, and it does this for each token. I would be happy to post more information on request. This is my first time writing a dependency parser from scratch. I was expecting the ParserRunner.parse() method to choose a different action than to end the arc. Continue reading...