Closed MinionAttack closed 2 years ago
@MinionAttack
Please kindly note that features in feats
strictly follow the order you initialize the fields,
CoNLL(FORM=(WORD, CHAR, ELMO, BERT), LEMMA=LEMMA, CPOS=TAG, HEAD=HEAD, DEPREL=DEPREL, PHEAD=LABEL)
so the order of obtaining feature vectors should correspond strictly:
# these look a bit misplaced
if 'head' in self.args.feat:
feat_embeds.append(self.head_embed(feats.pop(0)))
if 'deprel' in self.args.feat:
feat_embeds.append(self.deprel_embed(feats.pop(0)))
Sorry that these aspects are missing in the documentation.
@yzhangcs thanks for the answer but what do you mean with "# these look a bit misplaced".
Also I'm passing the features in this order:
--feat tag char head deprel
@MinionAttack
but what do you mean with "# these look a bit misplaced"
the order of tag char head deprel
being fetched out should be char -> tag -> head -> deprel
the order of
tag char head deprel
being fetched out should bechar -> tag -> head -> deprel
Oh, I think I've understood it. You mean that the order to follow is this one:
CoNLL(FORM=(WORD, CHAR, ELMO, BERT), LEMMA=LEMMA, CPOS=TAG, HEAD=HEAD, DEPREL=DEPREL, PHEAD=LABEL)
So I've changed the order and set this:
if 'tag' in self.args.feat:
feat_embeds.append(self.tag_embed(feats.pop()))
if 'char' in self.args.feat:
feat_embeds.append(self.char_embed(feats.pop(0)))
if 'elmo' in self.args.feat:
feat_embeds.append(self.elmo_embed(feats.pop(0)))
if 'bert' in self.args.feat:
feat_embeds.append(self.bert_embed(feats.pop(0)))
if 'lemma' in self.args.feat:
feat_embeds.append(self.lemma_embed(feats.pop(0)))
to this:
if 'char' in self.args.feat:
feat_embeds.append(self.char_embed(feats.pop(0)))
if 'elmo' in self.args.feat:
feat_embeds.append(self.elmo_embed(feats.pop(0)))
if 'bert' in self.args.feat:
feat_embeds.append(self.bert_embed(feats.pop(0)))
if 'lemma' in self.args.feat:
feat_embeds.append(self.lemma_embed(feats.pop(0)))
if 'tag' in self.args.feat:
feat_embeds.append(self.tag_embed(feats.pop(0)))
if 'head' in self.args.feat:
feat_embeds.append(self.head_embed(feats.pop(0)))
if 'deprel' in self.args.feat:
feat_embeds.append(self.deprel_embed(feats.pop(0)))
So now they will always be popped in the right order. Am I right?
@MinionAttack Yeah, exactly.
Hello, I am reopening this topic so as not to duplicate it. After being able to train a LSTM by adding these features, I am not able to make it predict files. When it tries to get head
and deprel
:
if 'tag' in self.args.feat:
feat_embeds.append(self.tag_embed(feats.pop(0)))
if 'head' in self.args.feat:
feat_embeds.append(self.head_embed(feats.pop(0)))
if 'deprel' in self.args.feat:
feat_embeds.append(self.deprel_embed(feats.pop(0)))
word_embed, feat_embed = self.embed_dropout(word_embed, torch.cat(feat_embeds, -1))
I'm getting an error here:
....
feat_embeds.append(self.head_embed(feats.pop(0)))
IndexError: pop from empty list
I have tried to debug the code but I can't find the place where the feats
variable is initialised or loaded with the data. I have tried to follow the code flow, so:
At line 137 in supar/parsers/parser.py
the _predict
method receives a loader
which is used to extract the batches to iterate over at line 183 in supar/parsers/sdp.py
. Then in the next line the feats
variable is unpacked from batch
:
words, *feats = batch
Where the batch
is:
batch = Batch(words, chars, tags)
and its content is:
fields = ['words', 'chars', 'tags']
sentences = [ ... ]
transformed = { 'words': Tensor, 'chars': Tensor, 'tags': Tensor}
That means that feats
is a list of 2 Tensors, which I think corresponds to char
and tag
, instead of 4 Tensors for char
, tag
, head
and deprel
.
Finally, on line 134 of supar/models/model.py
:
feat_embeds = []
if 'char' in self.args.feat:
feat_embeds.append(self.char_embed(feats.pop(0)))
if 'elmo' in self.args.feat:
feat_embeds.append(self.elmo_embed(feats.pop(0)))
if 'bert' in self.args.feat:
feat_embeds.append(self.bert_embed(feats.pop(0)))
if 'lemma' in self.args.feat:
feat_embeds.append(self.lemma_embed(feats.pop(0)))
if 'tag' in self.args.feat:
feat_embeds.append(self.tag_embed(feats.pop(0)))
if 'head' in self.args.feat:
feat_embeds.append(self.head_embed(feats.pop(0)))
if 'deprel' in self.args.feat:
feat_embeds.append(self.deprel_embed(feats.pop(0)))
word_embed, feat_embed = self.embed_dropout(word_embed, torch.cat(feat_embeds, -1))
# concatenate the word and feat representations
embed = torch.cat((word_embed, feat_embed), -1)
return embed
I can see that the previously saved model (self
) contains the features:
...
char_embed = CharLSTM(87, 50, n_out=100, pad_index=0)
...
deprel_embed = Embedding(31, 100)
...
head_embed = Embedding(64, 100)
...
tag_embed = Embedding(17, 100)
word_embed = Embedding(225, 300)
Can you give me any hints or guidance on where feats
are loaded and initialised? To see why it has a length of 2 instead of 4.
Regards.
@MinionAttack Oh that's weird. Could you print the saved transform
, which I suppose should contain 9 fields as you saved.
Doing debug, if I copy the value of the variable trasform
inside dataset
I get:
CoNLL(
(words): Field(pad=<pad>, unk=<unk>, bos=<bos>, lower=True)
(chars): SubwordField(pad=<pad>, unk=<unk>, bos=<bos>)
(tags): Field(bos=<bos>)
)
I attach an image for you to see it better.
I noticed that flattened_fields
shows words
, chars
and tags
, but not the ones I added (head
and deprel
). Could this be related?
@MinionAttack Does the two fields exist while training?
Yes, I have trained a new model and I have set a breakpoint at line 70 in supar/parsers/parser.py
right before the model starts to train. Inspecting the self variable which is an instance of BiaffineSemanticDependencyParser
I can see that the value of the transform
attribute is:
CoNLL(
(words): Field(pad=<pad>, unk=<unk>, bos=<bos>, lower=True)
(chars): SubwordField(pad=<pad>, unk=<unk>, bos=<bos>)
(tags): Field(bos=<bos>)
(heads): Field(bos=<bos>)
(relations): Field(bos=<bos>)
(labels): ChartField()
)
(relations is the name given to DEPREL
in the build method of supar/parsers/sdp.py
)
And the value of flattened_fields
is:
flattened_fields = [(words): Field(pad=<pad>, unk=<unk>, bos=<bos>, lower=True), (chars): SubwordField(pad=<pad>, unk=<unk>, bos=<bos>), (tags): Field(bos=<bos>), (heads): Field(bos=<bos>), (relations): Field(bos=<bos>), (labels): ChartField()]
I attach again an image for you to see it better:
So I think the fields exist in the training phase, am I right?
EDIT 1:
I have also debugged to see what is saved after the training phase and in the save method of supar/parsers/parser.py
, in:
state = {'name': self.NAME,
'args': args,
'state_dict': state_dict,
'pretrained': pretrained,
'transform': self.transform}
torch.save(state, path, pickle_module=dill)
The value of self.transform
is:
CoNLL(
(words): Field(pad=<pad>, unk=<unk>, bos=<bos>, lower=True)
(chars): SubwordField(pad=<pad>, unk=<unk>, bos=<bos>)
(tags): Field(bos=<bos>)
(heads): Field(bos=<bos>)
(relations): Field(bos=<bos>)
(labels): ChartField()
)
And the flattened_fields
attribute has both heads
(HEAD) and relations
(DEPREL) fields.
flattened_fields = [(words): Field(pad=<pad>, unk=<unk>, bos=<bos>, lower=True), (chars): SubwordField(pad=<pad>, unk=<unk>, bos=<bos>), (tags): Field(bos=<bos>), (heads): Field(bos=<bos>), (relations): Field(bos=<bos>), (labels): ChartField()]
EDIT 2:
After seeing in EDIT 1 that the values are stored in the model I have debugged the code when loading the model to predict. In the load
method of supar/parsers/parser.py
:
model.to(args.device)
transform = state['transform']
parser = cls(args, model, transform)
parser.checkpoint_state_dict = state['checkpoint_state_dict'] if args.checkpoint else None
return parser
The value of transform
contains the head and relations fields and both are present in _flattenedfields too. But what I have found is that when parser.predict(**args)
is called in supar/cmds/cmd.py
:
If I set a breakpoint in the line self.transform.eval()
, head and relations fields are present but after this line is executed, the values are gone.
Why changing the status to self.train(False)
removes the values? Reading the documentation it says:
Attributes: training (bool): Sets the object in training mode. If
False
, some data fields not required for predictions won't be returned. Default:True
.
So this makes that the flattened_fields
function in supar/utils/transform.py
ignores them?
@property
def flattened_fields(self):
flattened = []
for field in self:
if field not in self.src and field not in self.tgt:
continue
if not self.training and field in self.tgt:
continue
if not isinstance(field, Iterable):
field = [field]
for f in field:
if f is not None:
flattened.append(f)
return flattened
Because they are stored in self.tgt
. I see that in the train and evaluate command is set to self.transform.train()
.
So I guess this is why they are missing. Can this behaviour be changed or would it affect the operation of the parser?
@MinionAttack I see, only fields in transform.src
will be loaded during prediction.
https://github.com/yzhangcs/parser/blob/09f37241ba5ad2b4bf971ae1e59185d7c172aa58/supar/utils/transform.py#L134-L140
So you may need to change the above lines to:
@property
def src(self):
return self.FORM, self.LEMMA, self.CPOS, self.POS, self.FEATS, self.DEPREL, self.PHEAD
@property
def tgt(self):
return self.HEAD, self.PDEPREL
Ah ok, I didn't realise that. Thanks again for the help.
Hi,
I'm trying to add new features to use with LSTM, after reading the documentation and looking at the code, I've tried to replicate it looking at how it's done with
tag
,char
orlemma
. I want to use during training the 7th and 8th column in the CoNLL-U format to see if I get better models.For now, I've added the features I want to use (supar/cmds/biaffine_sdp.py):
subparser.add_argument('--feat', '-f', choices=['tag', 'char', 'lemma', 'head', 'deprel', 'elmo', 'bert'], nargs='+', help='features to use')
Then, following the way it's done with
tag
,char
orlemma
I've added the code to use the new features (supar/parsers/sdp.py):I also add them in
supar/models/model.py
:When I try to train a model to see if these changes are an improvement, I get this output:
But I get an error when the training is about to start:
The error is located at:
I have tried to find out what is happening, but I am not able to find the cause of the problem. What am I doing wrong or what have I forgotten to modify to make it work?
Regards.