mirror of
https://github.com/wassname/Castor.git
synced 2026-09-09 11:13:20 +08:00
Check in MP-CNN Lite Model (#108)
* Add MP-CNN Lite model * MP-CNN Lite bug fixes
This commit is contained in:
+5
-1
@@ -13,6 +13,7 @@ from common.evaluation import EvaluatorFactory
|
||||
from common.train import TrainerFactory
|
||||
from utils.serialization import load_checkpoint
|
||||
from .model import MPCNN
|
||||
from .lite_model import MPCNNLite
|
||||
|
||||
|
||||
def get_logger():
|
||||
@@ -39,6 +40,7 @@ def evaluate_dataset(split_name, dataset_cls, model, embedding, loader, batch_si
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description='PyTorch implementation of Multi-Perspective CNN')
|
||||
parser.add_argument('model_outfile', help='file to save final model')
|
||||
parser.add_argument('--arch', help='model architecture to use', choices=['mpcnn', 'mpcnn_lite'], default='mpcnn')
|
||||
parser.add_argument('--dataset', help='dataset to use, one of [sick, msrvid, trecqa, wikiqa]', default='sick')
|
||||
parser.add_argument('--word-vectors-dir', help='word vectors directory',
|
||||
default=os.path.join(os.pardir, 'Castor-data', 'embeddings', 'GloVe'))
|
||||
@@ -95,7 +97,9 @@ if __name__ == '__main__':
|
||||
|
||||
filter_widths = list(range(1, args.max_window_size + 1)) + [np.inf]
|
||||
ext_feats = dataset_cls.EXT_FEATS if args.sparse_features else 0
|
||||
model = MPCNN(args.word_vectors_dim, args.holistic_filters, args.per_dim_filters, filter_widths,
|
||||
|
||||
model_cls = MPCNN if args.arch == 'mpcnn' else MPCNNLite
|
||||
model = model_cls(args.word_vectors_dim, args.holistic_filters, args.per_dim_filters, filter_widths,
|
||||
args.hidden_units, dataset_cls.NUM_CLASSES, args.dropout, ext_feats,
|
||||
args.attention, args.wide_conv)
|
||||
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
import numpy as np
|
||||
import torch
|
||||
import torch.nn as nn
|
||||
import torch.nn.functional as F
|
||||
|
||||
from mp_cnn.model import MPCNN
|
||||
|
||||
|
||||
class MPCNNLite(MPCNN):
|
||||
|
||||
def __init__(self, n_word_dim, n_holistic_filters, n_per_dim_filters, filter_widths, hidden_layer_units, num_classes, dropout, ext_feats, attention, wide_conv):
|
||||
super(MPCNNLite, self).__init__(n_word_dim, n_holistic_filters, n_per_dim_filters, filter_widths, hidden_layer_units, num_classes, dropout, ext_feats, attention, wide_conv)
|
||||
self.arch = 'mpcnn_lite'
|
||||
|
||||
def _add_layers(self):
|
||||
holistic_conv_layers_max = []
|
||||
|
||||
for ws in self.filter_widths:
|
||||
if np.isinf(ws):
|
||||
continue
|
||||
|
||||
padding = ws - 1 if self.wide_conv else 0
|
||||
|
||||
holistic_conv_layers_max.append(nn.Sequential(
|
||||
nn.Conv1d(self.in_channels, self.n_holistic_filters, ws, padding=padding),
|
||||
nn.Tanh()
|
||||
))
|
||||
|
||||
self.holistic_conv_layers_max = nn.ModuleList(holistic_conv_layers_max)
|
||||
|
||||
def _get_n_feats(self):
|
||||
COMP_1_COMPONENTS_HOLISTIC, COMP_2_COMPONENTS = 2 + self.n_holistic_filters, 2
|
||||
n_feats_h = self.n_holistic_filters * COMP_2_COMPONENTS
|
||||
n_feats_v = (
|
||||
# comparison units from holistic conv for max pooling for non-infinite widths
|
||||
((len(self.filter_widths) - 1) ** 2) * COMP_1_COMPONENTS_HOLISTIC +
|
||||
# comparison units from holistic conv for max pooling for infinite widths
|
||||
3
|
||||
)
|
||||
n_feats = n_feats_h + n_feats_v + self.ext_feats
|
||||
return n_feats
|
||||
|
||||
def _get_blocks_for_sentence(self, sent):
|
||||
block_a = {}
|
||||
for ws in self.filter_widths:
|
||||
if np.isinf(ws):
|
||||
sent_flattened, sent_flattened_size = sent.contiguous().view(sent.size(0), 1, -1), sent.size(1) * sent.size(2)
|
||||
block_a[ws] = {
|
||||
'max': F.max_pool1d(sent_flattened, sent_flattened_size).view(sent.size(0), -1)
|
||||
}
|
||||
continue
|
||||
|
||||
holistic_conv_out_max = self.holistic_conv_layers_max[ws - 1](sent)
|
||||
block_a[ws] = {
|
||||
'max': F.max_pool1d(holistic_conv_out_max, holistic_conv_out_max.size(2)).contiguous().view(-1, self.n_holistic_filters)
|
||||
}
|
||||
|
||||
return block_a
|
||||
|
||||
def _algo_1_horiz_comp(self, sent1_block_a, sent2_block_a):
|
||||
comparison_feats = []
|
||||
regM1, regM2 = [], []
|
||||
for ws in self.filter_widths:
|
||||
x1 = sent1_block_a[ws]['max'].unsqueeze(2)
|
||||
x2 = sent2_block_a[ws]['max'].unsqueeze(2)
|
||||
if np.isinf(ws):
|
||||
x1 = x1.expand(-1, self.n_holistic_filters, -1)
|
||||
x2 = x2.expand(-1, self.n_holistic_filters, -1)
|
||||
regM1.append(x1)
|
||||
regM2.append(x2)
|
||||
|
||||
regM1 = torch.cat(regM1, dim=2)
|
||||
regM2 = torch.cat(regM2, dim=2)
|
||||
|
||||
# Cosine similarity
|
||||
comparison_feats.append(F.cosine_similarity(regM1, regM2, dim=2))
|
||||
# Euclidean distance
|
||||
pairwise_distances = []
|
||||
for x1, x2 in zip(regM1, regM2):
|
||||
dist = F.pairwise_distance(x1, x2).view(1, -1)
|
||||
pairwise_distances.append(dist)
|
||||
comparison_feats.append(torch.cat(pairwise_distances))
|
||||
|
||||
return torch.cat(comparison_feats, dim=1)
|
||||
|
||||
def _algo_2_vert_comp(self, sent1_block_a, sent2_block_a):
|
||||
comparison_feats = []
|
||||
ws_no_inf = [w for w in self.filter_widths if not np.isinf(w)]
|
||||
for ws1 in self.filter_widths:
|
||||
x1 = sent1_block_a[ws1]['max']
|
||||
for ws2 in self.filter_widths:
|
||||
x2 = sent2_block_a[ws2]['max']
|
||||
if (not np.isinf(ws1) and not np.isinf(ws2)) or (np.isinf(ws1) and np.isinf(ws2)):
|
||||
comparison_feats.append(F.cosine_similarity(x1, x2).unsqueeze(1))
|
||||
comparison_feats.append(F.pairwise_distance(x1, x2).unsqueeze(1))
|
||||
comparison_feats.append(torch.abs(x1 - x2))
|
||||
|
||||
return torch.cat(comparison_feats, dim=1)
|
||||
|
||||
def forward(self, sent1, sent2, ext_feats=None, word_to_doc_count=None, raw_sent1=None, raw_sent2=None):
|
||||
# Attention
|
||||
if self.attention != 'none':
|
||||
sent1, sent2 = self.concat_attention(sent1, sent2, word_to_doc_count, raw_sent1, raw_sent2)
|
||||
|
||||
# Sentence modeling module
|
||||
sent1_block_a = self._get_blocks_for_sentence(sent1)
|
||||
sent2_block_a = self._get_blocks_for_sentence(sent2)
|
||||
|
||||
# Similarity measurement layer
|
||||
feat_h = self._algo_1_horiz_comp(sent1_block_a, sent2_block_a)
|
||||
feat_v = self._algo_2_vert_comp(sent1_block_a, sent2_block_a)
|
||||
combined_feats = [feat_h, feat_v, ext_feats] if self.ext_feats else [feat_h, feat_v]
|
||||
feat_all = torch.cat(combined_feats, dim=1)
|
||||
|
||||
preds = self.final_layers(feat_all)
|
||||
return preds
|
||||
+1
-1
@@ -216,7 +216,7 @@ class MPCNN(nn.Module):
|
||||
attention_emb2 = torch.cat((attention_weighted_sent2, sent2), dim=1)
|
||||
return attention_emb1, attention_emb2
|
||||
|
||||
def forward(self, sent1, sent2, ext_feats=None, word_to_doc_count=None, raw_sent1=None, raw_sent2=None, sent1_nonstatic=None, sent2_nonstatic=None):
|
||||
def forward(self, sent1, sent2, ext_feats=None, word_to_doc_count=None, raw_sent1=None, raw_sent2=None):
|
||||
# Attention
|
||||
if self.attention != 'none':
|
||||
sent1, sent2 = self.concat_attention(sent1, sent2, word_to_doc_count, raw_sent1, raw_sent2)
|
||||
|
||||
Reference in New Issue
Block a user