def test_reduce_prod(self, some_seq_batch): result = SequenceBatch.reduce_prod(some_seq_batch) assert_tensor_equal(result, [ [4, 10], [0, 4], [1, 1] ])
def test_embed(self): sequences = [ [], [1, 2, 3], [3, 3], [2] ] vocab = SimpleVocab([0, 1, 2, 3, 4]) indices = SequenceBatch.from_sequences(sequences, vocab) embeds = GPUVariable(torch.FloatTensor([ [0, 0], [2, 2], # 1 [3, 4], # 2 [-10, 1], # 3 [11, -1] # 4 ])) embedded = SequenceBatch.embed(indices, embeds) correct = np.array([ [[0, 0], [0, 0], [0, 0]], [[2, 2], [3, 4], [-10, 1]], [[-10, 1], [-10, 1], [0, 0]], [[3, 4], [0, 0], [0, 0]] ], dtype=np.float32) assert_tensor_equal(embedded.values, correct)
def test_embed_indices(self, embedder): indices = GPUVariable(torch.LongTensor([ [0, 1], [2, 2], [4, 5], ])) embeds = embedder.embed_indices(indices) assert_tensor_equal(embeds, [ [[1, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0]], [[0, 0, 1, 0, 0, 0], [0, 0, 1, 0, 0, 0]], [[0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 1]], ]) indices = GPUVariable( torch.LongTensor([ [[0, 1], [1, 0]], [[2, 2], [3, 2]], ])) embeds = embedder.embed_indices(indices) assert_tensor_equal(embeds, [ [[[1, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0]], [[0, 1, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0]]], [[[0, 0, 1, 0, 0, 0], [0, 0, 1, 0, 0, 0]], [[0, 0, 0, 1, 0, 0], [0, 0, 1, 0, 0, 0]]], ])
def multi_vocab_indices(self, sequences, vocabs, min_seq_length=0): """Convert a batch of sequences into indices, where each token is converted into a **tuple** of indices. Args: sequences (list[list[unicode]]): a batch of sequences vocabs (list[list[Vocab]]): vocabs[v] = a batch of vocabs (one per example in the batch), corresponding to vocab v min_seq_length (int): see SequenceBatch.from_sequences Returns: SequenceBatch: mask (Variable): has shape (batch_size, seq_length) values (Variable): has shape (batch_size, seq_length, num_vocabs) """ seq_batches = [ SequenceBatch.from_sequences(sequences, vocabs_v, min_seq_length) for vocabs_v in vocabs ] values_list, mask_list = zip(*seq_batches) for mask in mask_list: assert_tensor_equal(mask, mask_list[0]) # all masks should be the same values = torch.stack(values_list, 2) return SequenceBatch(values, mask_list[0])
def test_transfer_copy_probs(self): # not actual probs, but that's ok probs = torch.FloatTensor([ # special base copy [1, 2, 3, 10, 11, 12, 14, 16], [4, 5, 6, 20, 21, 22, 24, 26], ]) base_vocab = HardCopyVocab('a b'.split(), num_copy_tokens=3) dynamic_vocabs = [ HardCopyDynamicVocab(base_vocab, 'b a c d e f g'.split( )), # d, e, f, g don't get assigned copy tokens (not enough) HardCopyDynamicVocab( base_vocab, 'e f f f a d'.split()), # e, f, g get assigned copy tokens ] AttentionDecoderCellOutput._transfer_copy_probs( probs, dynamic_vocabs, base_vocab) assert_tensor_equal( probs, [ [ 1, 2, 3, 24, 23, 0, 0, 16 ], # copy prob for 'c' is not transferred, since it's not in base [4, 5, 6, 46, 21, 22, 24, 0 ], # only prob for 'a' gets transferred ])
def test_reduce_mean(self, some_seq_batch): result = SequenceBatch.reduce_mean(some_seq_batch, allow_empty=True) assert_tensor_equal(result, [[2.5, 3.5], [0, 4], [0, 0]]) with pytest.raises(ValueError): SequenceBatch.reduce_mean(some_seq_batch, allow_empty=False)
def test_reduce_max(self, some_seq_batch): with pytest.raises(ValueError): # should complain about empty sequence SequenceBatch.reduce_max(some_seq_batch) values = GPUVariable( torch.FloatTensor([ [ [1, 2], [4, 5], [4, 4] ], # actual max is in later elements, but shd be suppressed by mask [[0, -4], [43, -5], [-1, -20]], # note that all elements in 2nd dim are negative ])) mask = GPUVariable(torch.FloatTensor([ [1, 0, 0], [1, 1, 0], ])) seq_batch = SequenceBatch(values, mask) result = SequenceBatch.reduce_max(seq_batch) assert_tensor_equal(result, [ [1, 2], [43, -4], ])
def test_forward(self, embedder, dom_elements): d0, d1, d2, d3, d1a, d1b, d2a = dom_elements pad = DOMElementPAD() # a batch of DOM element sequences # one unrealistic aspect of this batch is that different sequences share the same elements # (won't happen in the actual task) batch = [ [d0, d1, d2, d3, d1a, d1b, d2a], [d1, d2, d3, d1a, pad, pad, pad], [d1a, d2a, pad, pad, pad, pad, pad], ] # the correct embeddings for each DOM element: # the original embedding, concatenated with the average of your neighbors d0_ = [0.1, 0, 0, 0] # d0 should have no neighbors d1_ = [1, 1, 2.5, 2.5] d2_ = [2, 2, 2, 2] d3_ = [3, 3, 1.5, 1.5] d1a_ = [1, 0, 1, 0.5] d1b_ = [1, 0.5, 1, 0] d2a_ = [2, 0, 0, 0] # d2a should have no neighbors pad_ = [0, 0, 0, 0] correct = np.array([ [d0_, d1_, d2_, d3_, d1a_, d1b_, d2a_], [d1_, d2_, d3_, d1a_, pad_, pad_, pad_], [d1a_, d2a_, pad_, pad_, pad_, pad_, pad_], ], dtype=np.float32) result = embedder(batch) assert_tensor_equal(result, correct)
def base_plus_copy_indices(words, dynamic_vocabs, base_vocab, volatile=False): """Compute base + copy indices. Args: words (list[list[unicode]]) dynamic_vocabs (list[HardCopyDynamicVocab]) base_vocab (HardCopyVocab) volatile (bool) Returns: MultiVocabIndices """ unk = base_vocab.UNK copy_seqs = [] for seq, dyna_vocab in izip(words, dynamic_vocabs): word_to_copy = dyna_vocab.word_to_copy_token normal_copy_seq = [] for w in seq: normal_copy_seq.append(word_to_copy.get(w, unk)) copy_seqs.append(normal_copy_seq) # each SeqBatch.values has shape (batch_size, seq_length) base_indices = SequenceBatch.from_sequences(words, base_vocab, volatile=volatile) copy_indices = SequenceBatch.from_sequences(copy_seqs, base_vocab, volatile=volatile) assert_tensor_equal(base_indices.mask, copy_indices.mask) # has shape (batch_size, seq_length, 2) concat_values = torch.stack([base_indices.values, copy_indices.values], 2) return MultiVocabIndices(concat_values, base_indices.mask)
def test_modify(self, vocab): whitelists = [ ['eat', 'what', 'drive'], # only eat and drive will have lemmas matched ['hello', 'drive'], # only drive will have lemmas matched ['hi'], # no lemmas will match ] always_allowed = [] always_allowed.append(vocab.STOP) always_allowed.extend(vocab.copy_tokens) modifier = LexicalWhitelister(whitelists, vocab, self.word_to_forms, always_allowed=always_allowed) extension_probs = np.reshape(np.arange(0, 45, dtype=np.float32), (3, 15)) modified = modifier.modify(extension_probs, None, None) # STOP token is enabled # all the copy tokens are enabled assert_tensor_equal(modified, [ [0, 0, 2, 3, 4, 5, 0, 0, 0, 9, 10, 11, 12, 13, 14], [0, 0, 17, 0, 0, 0, 0, 0, 0, 24, 25, 26, 27, 28, 29], [0, 0, 32, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 43, 44], ])
def test_reduce_sum(self, some_seq_batch): result = SequenceBatch.reduce_sum(some_seq_batch) assert_tensor_equal(result, [ [5, 7], [0, 4], [0, 0], ])
def test_embed_tokens(self, embedder): tokens = ['b', 'c', 'c'] embeds = embedder.embed_tokens(tokens) assert_tensor_equal(embeds, [ [0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1], ])
def test_embed_seq_batch(self, multi_vocab_indices, token_embedder): embeds = token_embedder.embed_seq_batch(multi_vocab_indices) assert_tensor_equal(embeds.values, [ [[10, 11, 20, 21], [8, 9, 18, 19], [10, 11, 20, 21], [10, 11, 20, 21]], [[0, 1, 22, 23], [8, 9, 0, 1], [10, 11, 16, 17], [0, 1, 0, 1]], ]) assert_tensor_equal(embeds.mask, multi_vocab_indices.mask)
def test_base_plus_copy_indices(self, multi_vocab_indices): assert_tensor_equal(multi_vocab_indices.mask, [ [1, 1, 1, 1], [1, 1, 1, 0], ]) assert_tensor_equal(multi_vocab_indices.values, [ [[5, 10], [4, 9], [5, 10], [5, 10]], [[0, 11], [4, 0], [5, 8], [0, 0]], # z maps to UNK in base idx, b maps to UNK in copy idx ])
def test_embedding_from_array(self): emb = TokenEmbedder._embedding_from_array(np.array([[9, 9], [8, 7]], dtype=np.float32)) assert isinstance(emb, Embedding) values = emb(GPUVariable(torch.LongTensor([[0, 0], [1, 0]]))) assert_tensor_equal(values, [ [[9, 9], [9, 9]], [[8, 7], [9, 9]], ])
def test_expand_dims_for_broadcast(): low_tensor = torch.FloatTensor([[1, 2, 3], [4, 5, 6]]) # (2, 3) high_tensor = torch.zeros(2, 3, 8, 1) new_tensor = expand_dims_for_broadcast(low_tensor, high_tensor) assert new_tensor.size() == (2, 3, 1, 1) assert_tensor_equal(new_tensor.squeeze(), low_tensor) with pytest.raises(AssertionError): bad_tensor = torch.zeros(2, 4, 8, 1) # prefix doesn't match expand_dims_for_broadcast(low_tensor, bad_tensor)
def test_batch_tile(): # (2, 3) v = torch.FloatTensor([[1, 2, 3], [4, 5, 6]]) tiled = batch_tile(v, 3) assert_tensor_equal(tiled, [[[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6]]]) v = torch.LongTensor([1, 2, 3]) assert_tensor_equal(batch_tile(v, 4), [ [1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], ])
def test_split(self, aligns): items = aligns.split() assert len(items) == 4 assert_tensor_equal(items[0].values, [ [1, 3], [0, 0], [0, 0] ]) assert_tensor_equal(items[0].mask, [ [1, 1], [1, 0], [0, 0] ]) assert_tensor_equal(items[2].values, [ [2, 0], [0, 0], [0, 0] ]) assert_tensor_equal(items[2].mask, [ [1, 0], [0, 0], [0, 0] ])
def test(self, aligns): assert_tensor_equal(aligns.indices, [ [[1, 3], [0, 0], [2, 0], [1, 3]], [[0, 0], [0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0], [0, 0]], ]) assert_tensor_equal(aligns.mask, [ [[1, 1], [0, 0], [1, 0], [1, 1]], [[1, 0], [0, 0], [0, 0], [0, 0]], [[0, 0], [0, 0], [0, 0], [0, 0]], ])
def test_gated_update(): h = GPUVariable(torch.FloatTensor([ [1, 2, 3], [4, 5, 6], ])) h_new = GPUVariable(torch.FloatTensor([ [-1, 2, 3], [4, 8, 0], ])) update = GPUVariable(torch.FloatTensor([[0], [1] ])) # only update the second row out = gated_update(h, h_new, update) assert_tensor_equal(out, [[1, 2, 3], [4, 8, 0]])
def test_log_sum_exp(self): values = GPUVariable(torch.FloatTensor([ [0, 1, -2, -3], [-2, -5, 1, 0], ])) mask = GPUVariable(torch.FloatTensor([ [1, 1, 1, 0], [1, 1, 0, 0], ])) seq_batch = SequenceBatch(values, mask, left_justify=False) result = SequenceBatch.log_sum_exp(seq_batch) correct = [1.3490122167681864, -1.9514126484262577] assert_tensor_equal(result, correct)
def test_from_sequences(self, sequences, vocab): seq_batch = SequenceBatch.from_sequences(sequences, vocab) assert_tensor_equal(seq_batch.values, np.array([ [1, 2, 2, 3], [3, 0, 0, 0], [0, 0, 0, 0], ], dtype=np.int32)) assert_tensor_equal(seq_batch.mask, np.array([ [1, 1, 1, 1], [1, 0, 0, 0], [0, 0, 0, 0], ], dtype=np.float32))
def test_weighted_sum(self, some_seq_batch): weights = GPUVariable(torch.FloatTensor([ [0.5, 0.3, 0], [0.8, 0.2, 0], [0, 0, 0], ])) result = SequenceBatch.weighted_sum(some_seq_batch, weights) # [1, 2] * 0.5 + [4, 5] * 0.3 = [0.5 + 1.2, 1 + 1.5] = [1.7, 2.5] # [0, 4] * 0.8 = [0, 3.2] # 0 # Weights on entries where mask[i, j] = 0 get ignored, as desired. assert_tensor_equal(result, [ [1.7, 2.5], [0, 3.2], [0, 0], ])
def test_embed_tokens(self, token_embedder): assert_tensor_equal(token_embedder.embed_tokens(['c', 'z']), [[10, 11, 20, 21], [0, 1, 22, 23]]) assert_tensor_equal(token_embedder.embed_tokens(['b', 'b']), [[8, 9, 18, 19], [8, 9, 0, 1]]) assert_tensor_equal(token_embedder.embed_tokens(['c', 'c']), [[10, 11, 20, 21], [10, 11, 16, 17]]) assert_tensor_equal(token_embedder.embed_tokens(['c', WordVocab.STOP]), [[10, 11, 20, 21], [4, 5, 0, 1]])
def test_bpr_loss(self): scores = GPUVariable(torch.FloatTensor([ 4.5, 3, 0, 1.6, 0, 5, 11, 2, 9, 1, 2, 4, ])) group_size = 4 loss = Seq2SeqScorer._bpr_loss(scores, group_size) correct_margins = np.array([ 1.5, 4.5, 2.9, -5, -11, -2, 8, 7, 5 ]) correct_losses = np.log(1 + np.exp(-correct_margins)) correct_loss = np.mean(correct_losses) assert_tensor_equal(loss, correct_loss)
def test_forward(self, copy_source, alignments): memory_dim, query_dim, attn_dim = 4, 3, 2 attn = SoftCopyAttention(memory_dim, query_dim, attn_dim) attn_ex = AttentionExample() memory_transform, query_transform, v_transform = attn_ex.params memory_cells = attn_ex.memory_cells query = attn_ex.query # manually set parameters of the base attention base_attn = attn._base_attention base_attn.memory_transform.data.set_(float_tensor(memory_transform)) base_attn.query_transform.data.set_(float_tensor(query_transform)) base_attn.v_transform.data.set_(float_tensor(v_transform)) # compute correct logits exp_logits = torch.exp(attn_ex.correct_logits) boost = float_tensor_var([ [.2, 0], [.3, .3], [0, 0], [0, 0], [0, .01], ]) correct_logits = torch.log(exp_logits + boost) # compute with module attn_out = attn(memory_cells, query, alignments, copy_source) assert_tensor_equal(attn_out.logits, correct_logits) assert_tensor_equal(attn_out.orig_logits, attn_ex.correct_logits) assert_tensor_equal(attn_out.boost, boost)
def test_multi_vocab_indices(self): vocabs = [ [SimpleVocab('a b c d e'.split()), SimpleVocab('x y z'.split())], [SimpleVocab('e d c b a'.split()), SimpleVocab('y z x'.split())], ] sequences = [ 'a b a e'.split(), 'y y y x z'.split(), ] indices = SequenceBatch.multi_vocab_indices(sequences, vocabs) assert_tensor_equal(indices.values, [ [[0, 4], [1, 3], [0, 4], [4, 0], [0, 0]], [[1, 0], [1, 0], [1, 0], [0, 2], [2, 1]], ]) assert_tensor_equal(indices.mask, [ [1, 1, 1, 1, 0], [1, 1, 1, 1, 1], ])
def test_cat(self): x1 = SequenceBatchElement( GPUVariable(torch.FloatTensor([ [[1, 2], [3, 4]], [[8, 2], [9, 0]]])), GPUVariable(torch.FloatTensor([ [1], [1] ]))) x2 = SequenceBatchElement( GPUVariable(torch.FloatTensor([ [[-1, 20], [3, 40]], [[-8, 2], [9, 10]]])), GPUVariable(torch.FloatTensor([ [1], [0] ]))) x3 = SequenceBatchElement( GPUVariable(torch.FloatTensor([ [[-1, 20], [3, 40]], [[-8, 2], [9, 10]]])), GPUVariable(torch.FloatTensor([ [0], [0] ]))) result = SequenceBatch.cat([x1, x2, x3]) assert_tensor_equal(result.values, [ [[[1, 2], [3, 4]], [[-1, 20], [3, 40]], [[-1, 20], [3, 40]]], [[[8, 2], [9, 0]], [[-8, 2], [9, 10]], [[-8, 2], [9, 10]]], ]) assert_tensor_equal(result.mask, [ [1, 1, 0], [1, 0, 0] ])
def act(self, state): """If the current state is a "frontier", then expands the "frontier" by taking an action that has not been taken before. Otherwise, takes actions to go to a "frontier." Args: states (np.array) Returns: action (int) """ assert_tensor_equal(state, self._current_node.history[0]) unexplored_actions = self._current_node.unexplored_actions if self._planned_path is not None: expected_state, action = self._planned_path.pop(0) assert expected_state == self._current_node if len(self._planned_path) == 0: self._planned_path = None return action elif len(unexplored_actions) > 0: # On a frontier action = np.random.choice(unexplored_actions) self._planned_path = None # Give up on any earlier plans return action else: print("Planning") self._planned_path = self._exploration_graph.reachable_frontier( self._current_node) if self._planned_path is not None: expected_state, action = self._planned_path.pop(0) assert expected_state == self._current_node if len(self._planned_path) == 0: self._planned_path = None return action # No known way to get to frontier print("No reachable frontiers") return np.random.choice(range(self._num_actions))
def test_states_to_image_var(self): # mock a MiniWoBState object arr_to_state = lambda arr: Bunch(observation=Bunch(image=np.array(arr, dtype=np.float32))) arrs = [ # ex0 [ [[1, 2], # R [3, 4]], [[1, 0], # G [3, 0]], [[0, 2], # B [0, 4]], ], # ex1 [ [[10, 20], # R [30, 40]], [[10, 0], # G [30, 0]], [[0, 20], # B [0, 40]], ], # ex2 [ [[100, 200], # R [300, 400]], [[100, 0], # G [300, 0]], [[0, 200], # B [0, 400]], ] ] correct = np.array(arrs, dtype=np.float32) states = [arr_to_state(arr) for arr in arrs] image_var = MiniWoBPolicy._states_to_image_var(states) assert_tensor_equal(correct, image_var)