def next_minibatch(self, num_samples, number_of_workers=1, worker_rank=0, device=None): if self._total_num_samples >= self._max_samples: return {} # determine how many samples, starting from self._cursor, will fit into the requested minibatch size of num_samples begin = self._cursor end = self._cursor assert begin < self._num_samples actual_num_samples = { name: 0 for name in self._data.keys() } while end < self._num_samples: new_num_samples = { name: actual_num_samples[name] + (MinibatchSourceFromData._get_len(value[end]) if self._is_sequence[name] else 1) for name, value in self._data.items() } # return up to requested number of samples. but at least one even if longer # also stop if we hit the maximum requested number of samples max_num_samples = max(new_num_samples.values()) if actual_num_samples and (max_num_samples > num_samples or self._total_num_samples + max_num_samples > self._max_samples): break actual_num_samples = new_num_samples end += 1 self._total_num_samples += max(actual_num_samples.values()) # the minibatch data to return result = {} # [stream_info] -> MinibatchData at_end = (end == self._num_samples) for si in self.streams.values(): arg = self._data[si.name] if isinstance(arg, Value): # if entire corpus is one big Value, then slice NDArrayView directly data = arg.data sub_shape = data.shape[1:] extent = (end - begin,) + sub_shape start_offset = (begin,) + tuple(0 for _ in sub_shape) if number_of_workers != 1: # slice_view presently does not support strides raise ValueError('distributed reading from Value objects is not supported') mb_data = data.slice_view(start_offset, extent, data.is_read_only) else: # in case of distributed reading, we sub-slice the minibatch #print('rank/worker', worker_rank, number_of_workers, 'reading', slice(begin+worker_rank, end+worker_rank, number_of_workers)) mb_data = arg[begin+worker_rank:end+worker_rank:number_of_workers] if number_of_workers != 1: mb_data = mb_data.copy() # un-stride it, to avoid performance warning if isinstance(mb_data, list): # create a Value object if si.name not in self._vars: # this case is more complex, we need a CNTK Variable from cntk import input_variable, device self._vars[si.name] = input_variable(**self._types[si.name]) value = Value.create(self._vars[si.name], mb_data) else: value = Value(mb_data) result[si] = MinibatchData(value, num_sequences=end - begin, num_samples=actual_num_samples[si.name], sweep_end=at_end or (self._total_num_samples >= self._max_samples)) # wrap around the cursor self._cursor = 0 if at_end else end return result
def test_op_times_reduce_sequence_axis(device_id, precision): dt_precision = PRECISION_TO_TYPE[precision] from cntk import times, Value, TIMES_REDUCE_SEQUENCE_AXIS_WITHOUT_INFERRED_INPUT_RANK from cntk import sequence dim = 10 seq = [[0,1,2], [3], [4,5,6,7,8,9]] right_data = Value.one_hot(seq, dim, dtype=dt_precision) right_var = sequence.input(shape=(dim), is_sparse=True, dtype=dt_precision) left_data = [AA([1,1,1],dtype=dt_precision), AA([1],dtype=dt_precision), AA([1,1,1,1,1,1],dtype=dt_precision)] left_var = sequence.input(shape=(1), dtype=dt_precision) func = times(left_var, right_var, infer_input_rank_to_map=TIMES_REDUCE_SEQUENCE_AXIS_WITHOUT_INFERRED_INPUT_RANK) func2 = sequence.reduce_sum(times(left_var, right_var)) assert func.dynamic_axes == func2.dynamic_axes _, forward_output = func.forward({left_var:left_data, right_var:right_data}) actual_forward = forward_output[func.output] expected_forward = AA([[[1,1,1,0,0,0,0,0,0,0]], [[0,0,0,1,0,0,0,0,0,0]], [[0,0,0,0,1,1,1,1,1,1]]]) assert np.allclose(actual_forward, expected_forward)
def test_op_times_reduce_sequence_axis(device_id, precision): dt_precision = PRECISION_TO_TYPE[precision] from cntk import times, Value, TIMES_REDUCE_SEQUENCE_AXIS_WITHOUT_INFERRED_INPUT_RANK from cntk import sequence dim = 10 seq = [[0,1,2], [3], [4,5,6,7,8,9]] right_data = Value.one_hot(seq, dim, dtype=dt_precision) right_var = sequence.input_variable(shape=(dim), is_sparse=True, dtype=dt_precision) left_data = [AA([1,1,1],dtype=dt_precision), AA([1],dtype=dt_precision), AA([1,1,1,1,1,1],dtype=dt_precision)] left_var = sequence.input_variable(shape=(1), dtype=dt_precision) func = times(left_var, right_var, infer_input_rank_to_map=TIMES_REDUCE_SEQUENCE_AXIS_WITHOUT_INFERRED_INPUT_RANK) func2 = sequence.reduce_sum(times(left_var, right_var)) assert func.dynamic_axes == func2.dynamic_axes _, forward_output = func.forward({left_var:left_data, right_var:right_data}) actual_forward = forward_output[func.output] expected_forward = AA([[[1,1,1,0,0,0,0,0,0,0]], [[0,0,0,1,0,0,0,0,0,0]], [[0,0,0,0,1,1,1,1,1,1]]]) assert np.allclose(actual_forward, expected_forward)
def train(self, x, q_value_targets, actions=None): assert actions is not None, 'actions cannot be None' # We need to add extra dimensions to shape [N, 1] => [N, 1] if check_rank(q_value_targets.shape, 1): q_value_targets = q_value_targets.reshape((-1, 1)) # Add extra dimensions to match shape [N, 1] required by one_hot if check_rank(actions.shape, 1): actions = actions.reshape((-1, 1)) # We need batch axis if check_rank(x.shape, len(self._environment.shape)): x = prepend_batch_axis(x) self._trainer.train_minibatch({ self._environment: x, self._actions: Value.one_hot(actions, self._nb_actions), self._q_targets: q_value_targets }) # Counter number of train calls self._steps += 1 # Update the model with the target one if (self._steps % self._target_update_interval) == 0: self._target = self._model.clone( CloneMethod.freeze, {self._environment: self._environment})
def train(self, x, q_value_targets, actions=None): assert actions is not None, 'actions cannot be None' # We need to add extra dimensions to shape [N, 1] => [N, 1] if check_rank(q_value_targets.shape, 1): q_value_targets = q_value_targets.reshape((-1, 1)) # Add extra dimensions to match shape [N, 1] required by one_hot if check_rank(actions.shape, 1): actions = actions.reshape((-1, 1)) # We need batch axis if check_rank(x.shape, len(self._environment.shape)): x = prepend_batch_axis(x) self._trainer.train_minibatch({ self._environment: x, self._actions: Value.one_hot(actions, self._nb_actions), self._q_targets: q_value_targets }) # Counter number of train calls self._steps += 1 # Update the model with the target one if (self._steps % self._target_update_interval) == 0: self._target = self._model.clone( CloneMethod.freeze, {self._environment: self._environment} )
def test_eval_sparse_dense(tmpdir, device_id): from cntk import Axis from cntk.io import MinibatchSource, CTFDeserializer, StreamDef, StreamDefs from cntk.ops import input, times input_vocab_dim = label_vocab_dim = 69 ctf_data = '''\ 0 |S0 3:1 |# <s> |S1 3:1 |# <s> 0 |S0 4:1 |# A |S1 32:1 |# ~AH 0 |S0 5:1 |# B |S1 36:1 |# ~B 0 |S0 4:1 |# A |S1 31:1 |# ~AE 0 |S0 7:1 |# D |S1 38:1 |# ~D 0 |S0 12:1 |# I |S1 47:1 |# ~IY 0 |S0 1:1 |# </s> |S1 1:1 |# </s> 2 |S0 60:1 |# <s> |S1 3:1 |# <s> 2 |S0 61:1 |# A |S1 32:1 |# ~AH ''' ctf_file = str(tmpdir / '2seqtest.txt') with open(ctf_file, 'w') as f: f.write(ctf_data) mbs = MinibatchSource(CTFDeserializer( ctf_file, StreamDefs(features=StreamDef(field='S0', shape=input_vocab_dim, is_sparse=True), labels=StreamDef(field='S1', shape=label_vocab_dim, is_sparse=True))), randomize=False, epoch_size=2) raw_input = sequence.input(shape=input_vocab_dim, sequence_axis=Axis('inputAxis'), name='raw_input', is_sparse=True) mb_valid = mbs.next_minibatch(minibatch_size_in_samples=100, input_map={raw_input: mbs.streams.features}, device=cntk_device(device_id)) z = times(raw_input, np.eye(input_vocab_dim)) e_reader = z.eval(mb_valid, device=cntk_device(device_id)) # CSR with the raw_input encoding in ctf_data one_hot_data = [[3, 4, 5, 4, 7, 12, 1], [60, 61]] data = [ csr(np.eye(input_vocab_dim, dtype=np.float32)[d]) for d in one_hot_data ] e_csr = z.eval({raw_input: data}, device=cntk_device(device_id)) assert np.all([np.allclose(a, b) for a, b in zip(e_reader, e_csr)]) # One-hot with the raw_input encoding in ctf_data data = Value.one_hot(one_hot_data, num_classes=input_vocab_dim, device=cntk_device(device_id)) e_hot = z.eval({raw_input: data}, device=cntk_device(device_id)) assert np.all([np.allclose(a, b) for a, b in zip(e_reader, e_hot)])
def test_one_hot_skip(): a = Value.one_hot([[0,1,Value.ONE_HOT_SKIP]], 3) i = sequence.input_variable(shape=(3,)) b = i * 1 expected = [[[ 1., 0., 0.], [ 0., 1., 0.], [ 0., 0., 0.]]] assert np.allclose(b.eval({i:a}), expected)
def test_eval_sparse_dense(tmpdir, device_id): from cntk import Axis from cntk.io import MinibatchSource, CTFDeserializer, StreamDef, StreamDefs from cntk.ops import input_variable, times input_vocab_dim = label_vocab_dim = 69 ctf_data = '''\ 0 |S0 3:1 |# <s> |S1 3:1 |# <s> 0 |S0 4:1 |# A |S1 32:1 |# ~AH 0 |S0 5:1 |# B |S1 36:1 |# ~B 0 |S0 4:1 |# A |S1 31:1 |# ~AE 0 |S0 7:1 |# D |S1 38:1 |# ~D 0 |S0 12:1 |# I |S1 47:1 |# ~IY 0 |S0 1:1 |# </s> |S1 1:1 |# </s> 2 |S0 60:1 |# <s> |S1 3:1 |# <s> 2 |S0 61:1 |# A |S1 32:1 |# ~AH ''' ctf_file = str(tmpdir/'2seqtest.txt') with open(ctf_file, 'w') as f: f.write(ctf_data) mbs = MinibatchSource(CTFDeserializer(ctf_file, StreamDefs( features = StreamDef(field='S0', shape=input_vocab_dim, is_sparse=True), labels = StreamDef(field='S1', shape=label_vocab_dim, is_sparse=True) )), randomize=False, epoch_size = 2) batch_axis = Axis.default_batch_axis() input_seq_axis = Axis('inputAxis') label_seq_axis = Axis('labelAxis') input_dynamic_axes = [batch_axis, input_seq_axis] raw_input = input_variable( shape=input_vocab_dim, dynamic_axes=input_dynamic_axes, name='raw_input', is_sparse=True) mb_valid = mbs.next_minibatch(minibatch_size_in_samples=100, input_map={raw_input : mbs.streams.features}, device=cntk_device(device_id)) z = times(raw_input, np.eye(input_vocab_dim)) e_reader = z.eval(mb_valid, device=cntk_device(device_id)) # CSR with the raw_input encoding in ctf_data one_hot_data = [ [3, 4, 5, 4, 7, 12, 1], [60, 61] ] data = [csr(np.eye(input_vocab_dim, dtype=np.float32)[d]) for d in one_hot_data] e_csr = z.eval({raw_input: data}, device=cntk_device(device_id)) assert np.all([np.allclose(a, b) for a,b in zip(e_reader, e_csr)]) # One-hot with the raw_input encoding in ctf_data data = Value.one_hot(one_hot_data, num_classes=input_vocab_dim, device=cntk_device(device_id)) e_hot = z.eval({raw_input: data}, device=cntk_device(device_id)) assert np.all([np.allclose(a, b) for a,b in zip(e_reader, e_hot)])
def test_one_hot_int_types(dtype): data = [[0, 2, 1], [1]] if dtype is not None: data = [np.asarray(d, dtype=dtype) for d in data] a = Value.one_hot(data, 3) i = sequence.input_variable(shape=(3, )) b = i * 1 expected = [[[1., 0., 0.], [0., 0., 1.], [0., 1., 0.]], [[0., 1., 0.]]] for a, b in zip(b.eval({i: a}), expected): assert np.allclose(a, b)
def test_op_scatter_sparse(device_id): input_sparse_indices = [[1, 3, 5, 5], [2, 4], [0, 2]] vocab_size = 6 input_data = Value.one_hot(input_sparse_indices, vocab_size) a = C.sequence.input_variable(shape=(vocab_size,), is_sparse=True, name='a') a_last_scatter = C.sequence.scatter(C.sequence.last(a), C.sequence.is_first(a)) a_last_scatter_dense = C.times(a_last_scatter, np.eye(vocab_size)) res = a_last_scatter_dense.eval({a : input_data}) assert np.array_equal(res[0], np.asarray([[0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0]])) assert np.array_equal(res[1], np.asarray([[0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 0]])) assert np.array_equal(res[2], np.asarray([[0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 0, 0]]))
def test_eval_one_hot_seq(one_hot_batch, device_id): dim = 10 multiplier = 2 for var_is_sparse in [True, False]: in1 = sequence.input_variable(shape=(dim,), is_sparse=var_is_sparse) # Convert CNTK node value to dense so that we can compare it later z = times(in1, np.eye(dim)*multiplier) # Convert expectation to dense expected = [np.eye(dim)[seq]*multiplier for seq in one_hot_batch] batch = Value.one_hot(one_hot_batch, num_classes=dim, device=cntk_device(device_id)) result = z.eval({in1: batch}, device=cntk_device(device_id)) assert np.all([np.allclose(a,b) for a,b in zip(result, expected)])
def test_eval_one_hot_seq(one_hot_batch, device_id): dim = 10 multiplier = 2 for var_is_sparse in [True, False]: in1 = input_variable(shape=(dim,), is_sparse=var_is_sparse) # Convert CNTK node value to dense so that we can compare it later z = times(in1, np.eye(dim)*multiplier) # Convert expectation to dense expected = [np.eye(dim)[seq]*multiplier for seq in one_hot_batch] batch = Value.one_hot(one_hot_batch, num_classes=dim, device=cntk_device(device_id)) result = z.eval({in1: batch}, device=cntk_device(device_id)) assert np.all([np.allclose(a,b) for a,b in zip(result, expected)])
def test_disallow_seq_starts_with_Value_objects(): one_hot_batch = [[2,5], [0,1,6]] dim = 10 in1 = input_variable(shape=(dim,), is_sparse=True) z = times(in1, np.eye(dim)) batch = Value.one_hot(one_hot_batch, num_classes=dim) with pytest.raises(ValueError): result = z.eval(({in1: batch}, len(batch)*[True])) with pytest.raises(ValueError): result = z.eval({in1: (batch, len(batch)*[True])})
def test_one_hot_int_types(dtype): data = [[0,2,1],[1]] if dtype is not None: data = [np.asarray(d, dtype=dtype) for d in data] a = Value.one_hot(data, 3) i = sequence.input_variable(shape=(3,)) b = i * 1 expected = [[[ 1., 0., 0.], [ 0., 0., 1.], [ 0., 1., 0.]], [[ 0., 1., 0.]]] for a, b in zip (b.eval({i:a}), expected): assert np.allclose(a, b)
def test_disallow_seq_starts_with_Value_objects(): one_hot_batch = [[2, 5], [0, 1, 6]] dim = 10 in1 = input(shape=(dim, ), is_sparse=True) z = times(in1, np.eye(dim)) batch = Value.one_hot(one_hot_batch, num_classes=dim) with pytest.raises(ValueError): result = z.eval(({in1: batch}, len(batch) * [True])) with pytest.raises(ValueError): result = z.eval({in1: (batch, len(batch) * [True])})
def test_times_transpose_sequence_param(device_id, precision): dt_precision = PRECISION_TO_TYPE[precision] from cntk import times_transpose, parameter, sequence, Value dim = 5 num_sequences = 2 seq = [i for i in range(dim)] identity = np.identity(dim, dtype=dt_precision) input_data = Value.one_hot([seq] * num_sequences, dim, dtype=dt_precision) input_var = sequence.input_variable(shape=(dim), needs_gradient=True, dtype=dt_precision) e = parameter(shape=(dim, ), init=1, dtype=dt_precision) z = times_transpose(e, input_var) e_grad = z.grad({input_var: input_data}, [e, input_var])
def test_op_times_sparse_grad(device_id, precision): dt_precision = PRECISION_TO_TYPE[precision] from cntk import times, times_transpose, parameter, reshape, Value, sequence dim = 5 num_sequences = 2 seq = [i for i in range(dim)] identity = np.identity(dim, dtype=dt_precision) input_data = Value.one_hot([seq]*num_sequences, dim, dtype=dt_precision) input_var = sequence.input(shape=(dim), is_sparse=True, needs_gradient=False, dtype=dt_precision) e = parameter(shape = (dim, dim), init = identity, dtype=dt_precision) z = reshape(times_transpose(e, times(input_var, e)), dim) e_grad = z.grad({input_var : input_data}, [e]) assert np.allclose(e_grad, np.ones((dim,dim))*4)
def test_op_times_sparse_grad(device_id, precision): dt_precision = PRECISION_TO_TYPE[precision] from cntk import times, times_transpose, parameter, reshape, Value, sequence dim = 5 num_sequences = 2 seq = [i for i in range(dim)] identity = np.identity(dim, dtype=dt_precision) input_data = Value.one_hot([seq]*num_sequences, dim, dtype=dt_precision) input_var = sequence.input_variable(shape=(dim), is_sparse=True, needs_gradient=False, dtype=dt_precision) e = parameter(shape = (dim, dim), init = identity, dtype=dt_precision) z = reshape(times_transpose(e, times(input_var, e)), dim) e_grad = z.grad({input_var : input_data}, [e]) assert np.allclose(e_grad, np.ones((dim,dim))*4)
def test_op_gather_sparse(device_id): input_sparse_indices = [[1, 3, 5, 5], [2, 4], [0, 2]] vocab_size = 6 input_data = Value.one_hot(input_sparse_indices, vocab_size) a = C.sequence.input_variable(shape=(vocab_size,), is_sparse=True, name='a') a_last = C.sequence.last(a) a_last_dense = C.times(a_last, np.eye(vocab_size)) res = a_last_dense.eval({a : input_data}) assert np.array_equal(res, [[0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 1, 0], [0, 0, 1, 0, 0, 0]]) a_last_2 = C.sequence.slice(a, -2, 0) a_last_2_dense = C.times(a_last_2, np.eye(vocab_size)) res = a_last_2_dense.eval({a : input_data}) assert np.array_equal(res, [[[0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 1]], [[0, 0, 1, 0, 0, 0], [0, 0, 0, 0, 1, 0]], [[1, 0, 0, 0, 0, 0], [0, 0, 1, 0, 0, 0]]])
def peek(model, epoch): # load dictionaries global query_wl, slots_wl, query_dict, slots_dict if query_wl is None: query_wl = [line.rstrip('\n') for line in open(data_dir + "/../BrainScript/query.wl")] slots_wl = [line.rstrip('\n') for line in open(data_dir + "/../BrainScript/slots.wl")] query_dict = {query_wl[i]:i for i in range(len(query_wl))} slots_dict = {slots_wl[i]:i for i in range(len(slots_wl))} # run a sequence through seq = 'BOS flights from new york to seattle EOS' # example string w = [query_dict[w] for w in seq.split()] # convert to word indices z = model(Value.one_hot([w], vocab_size)) # run it through the model best = np.argmax(z,axis=2) # classify # show result print("Example Sentence After Epoch [{}]".format(epoch)) for query, slot_label in zip(seq.split(),[slots_wl[s] for s in best[0]]): print("\t{}\t{}".format(query, slot_label))
def train(self, s, a, r, s_, t, w): """ Updates the network weights using the given minibatch data :param s: Tensor[state_dim] Current state :param a: Tensor[int] Action taken at state s :param r: Tensor[float] State resulting from taking action a at state s :param s_: Tensor[state_dim] Reward received for taking action a at state s :param t: Tensor[boolean] True if s_ was a terminal state and false otherwise :param w: Tensor[float] Importance sampling weights """ a = Value.one_hot(a.tolist(), self.action_dim) td_error = self.trainer.train_minibatch( { self.pre_states: s, self.actions: a, self.rewards: r, self.post_states: s_, self.terminals: t, self.is_weights: w }, outputs=[self.td_error]) return td_error[0]
def peek(model, epoch): # load dictionaries global query_wl, slots_wl, query_dict, slots_dict if query_wl is None: query_wl = [ line.rstrip('\n') for line in open(data_dir + "/../BrainScript/query.wl") ] slots_wl = [ line.rstrip('\n') for line in open(data_dir + "/../BrainScript/slots.wl") ] query_dict = {query_wl[i]: i for i in range(len(query_wl))} slots_dict = {slots_wl[i]: i for i in range(len(slots_wl))} # run a sequence through seq = 'BOS flights from new york to seattle EOS' # example string w = [query_dict[w] for w in seq.split()] # convert to word indices z = model(Value.one_hot([w], vocab_size)) # run it through the model best = np.argmax(z, axis=2) # classify # show result print("Example Sentence After Epoch [{}]".format(epoch)) for query, slot_label in zip(seq.split(), [slots_wl[s] for s in best[0]]): print("\t{}\t{}".format(query, slot_label))
def test_one_hot_skip(): a = Value.one_hot([[0, 1, Value.ONE_HOT_SKIP]], 3) i = sequence.input_variable(shape=(3, )) b = i * 1 expected = [[[1., 0., 0.], [0., 1., 0.], [0., 0., 0.]]] assert np.allclose(b.eval({i: a}), expected)
def test_one_hot_raises(): with pytest.raises(ValueError): s = Value.one_hot([[1.0, 2.0], [3.]], 4)
assert b.shape == (2,2,3) @pytest.mark.parametrize("batch, seq_starts, expected", [ ([AA([5, 6, 7]), AA([8])], [True, False], [[2, 1, 1], [1, 0, 0]]), ([AA([5]), AA([8])], [True, False], [[2], [1]]), ([[5, 6, 7], [8]], [True, False], [[2, 1, 1], [1, 0, 0]]), (Value.one_hot([[3, 4, 5, 1], [60, 61]], num_classes=62), [True, False], ValueError), ]) def test_mask(batch, seq_starts, expected): shape = () var = sequence.input_variable(shape) if type(expected) == type(ValueError): with pytest.raises(expected): s = sanitize_batch(var, batch, seq_starts) else: s = sanitize_batch(var, batch, seq_starts) assert np.allclose(s.mask, expected) def test_one_hot_raises(): with pytest.raises(ValueError):
def test_Value_raises(): from cntk import NDArrayView, Value with pytest.raises(ValueError): nd = NDArrayView.from_dense(np.asarray([[[4, 5]]], dtype=np.float32)) val = Value(nd)
def test_eval_one_hot_bad(one_hot_batch, dim, device_id): with pytest.raises(ValueError): batch = Value.one_hot(one_hot_batch, num_classes=dim, device=cntk_device(device_id))
assert b.shape == (2,2,3) @pytest.mark.parametrize("batch, seq_starts, expected", [ ([AA([5, 6, 7]), AA([8])], [True, False], [[2, 1, 1], [1, 0, 0]]), ([AA([5]), AA([8])], [True, False], [[2], [1]]), ([[5, 6, 7], [8]], [True, False], [[2, 1, 1], [1, 0, 0]]), (Value.one_hot([[3, 4, 5, 1], [60, 61]], num_classes=62), [True, False], ValueError), ]) def test_mask(batch, seq_starts, expected): shape = () var = input_variable(shape) if type(expected) == type(ValueError): with pytest.raises(expected): s = sanitize_batch(var, batch, seq_starts) else: s = sanitize_batch(var, batch, seq_starts) assert np.allclose(s.mask, expected) def test_one_hot(): with pytest.raises(ValueError):
def test_one_hot(): with pytest.raises(ValueError): s = Value.one_hot([[1.0, 2.0], [3.]], 4) with pytest.raises(ValueError): s = Value.one_hot([1, 2], 4)