def forward(self, x): """Executes this layer as part of a forward pass through the model. Args: x: Tensor of same shape and dtype as the input signature used to initialize this layer. Returns: Tensor of same shape and dtype as the input. """ m1, m2, mb, w1, w2, b2 = self.weights if self._mode != 'predict': w1 = np.reshape(w1.T, (-1, self._d_ff)) w2 = np.reshape(w2, (self._d_ff, -1)) x_shape = x.shape x = np.reshape(x, [-1, x_shape[-1]]) # Easier to operate on flattened x. # Q: should we add bias and/or put relu after the low-rank m1 dot? mask_logits = np.dot(np.dot(x, m1), m2) + mb mask_logits = np.reshape(mask_logits, [-1, self._d1, self._d2]) # Softmax. mask_logsumexp = fastmath.logsumexp(mask_logits, axis=-1, keepdims=True) log_mask = mask_logits - mask_logsumexp mask = np.exp(log_mask) # Gumbel-softmax with straight-through discretization. rng1, rng2 = fastmath.random.split(self.rng, 2) u = fastmath.random.uniform(rng1, mask.shape, np.float32, 1e-6, 1.0 - 1e-6) g = -np.log(-np.log(u)) quant_mask = np.argmax(log_mask + g * self._temperature, axis=-1) if self._mode == 'train': # Tricks from Section 2.1 in https://arxiv.org/abs/1801.09797 quant_mask = metrics.one_hot(quant_mask, self._n_elements_in_block) quant_mask = fastmath.stop_gradient(quant_mask) quant_mask += mask - fastmath.stop_gradient( mask) # straight-through # We will sometimes (50% of the batches) use the soft-mask instead of # the quantized mask to improve training stability (see the paper above). # Q: is selecting 50% of batches the best? Other %? Mixed in-batch? select = fastmath.random.uniform(rng2, (), np.float32, -1.0, 1.0) quant_mask = np.where(select > 0.0, quant_mask, mask) quant_mask = np.reshape(quant_mask, [-1, self._d_ff]) if self._mode == 'train': # In training, run full matmul to get benefits from the above tricks. mid = np.dot(x, w1) * quant_mask # [joint_batch, d_ff] relu = np.where(mid <= 0, np.zeros_like(mid), mid) res = np.dot(relu, w2) + b2 elif self._mode == 'predict': # w1 = np.reshape(w1.T, (self._d1, self._d2, -1)) # w2 = np.reshape(w2, (self._d1, self._d2, -1)) # This implementation mimicks inference. It's not efficient for large # size of joint_batch, but at inference that will be 1 most of the time. # Shapes: # quant_mask is [joint_batch, self._d1] # w1 is [d_model, self._d1, self._d2] # we'll index w1 with advanced numpy indexing, first range over # self._d1 times the batch size, second range being quant_mask batch_size = quant_mask.shape[0] idx1 = np.array([np.arange(self._d1)] * batch_size) # flatten indices and select from w1 idx1 = np.reshape(idx1, [-1]) idx2 = np.reshape(quant_mask, [-1]) w = w1[idx1, idx2, :] # now we have per-element weights with batch dim w = np.reshape(w, [batch_size, self._d1, -1]) mid = np.einsum('ai,aji->aj', x, w) relu = np.where(mid <= 0, np.zeros_like(mid), mid) # w2 is [self._d1, self._d2, d_model] v = w2[idx1, idx2, :] v = np.reshape(v, [batch_size, self._d1, -1]) res = np.einsum('ai,aij->aj', relu, v) + b2 else: quant_mask = metrics.one_hot(quant_mask, self._n_elements_in_block) quant_mask = np.reshape(quant_mask, [-1, self._d_ff]) mid = np.dot(x, w1) * quant_mask # [joint_batch, d_ff] relu = np.where(mid <= 0, np.zeros_like(mid), mid) res = np.dot(relu, w2) + b2 return np.reshape(res, x_shape) # un-flatten if needed
def forward(self, x): """Executes this layer as part of a forward pass through the model. Args: x: Tensor of same shape and dtype as the input signature used to initialize this layer. Returns: Tensor of same shape and dtype as the input. """ m1, w1, w2, b2 = self.weights x_shape = x.shape x = np.reshape(x, [-1, x_shape[-1]]) # Easier to operate on flattened x. # Q: check if we need bias and/or put relu after the m1 dot? mask_logits = np.dot(x, m1) # Softmax. mask_logsumexp = fastmath.logsumexp(mask_logits, axis=-1, keepdims=True) log_mask = mask_logits - mask_logsumexp mask = np.exp(log_mask) # Gumbel-softmax with straight-through discretization. # TODO(lukaszkaiser, chowdhery): Extract this block and share rng1, rng2 = fastmath.random.split(self.rng, 2) u = fastmath.random.uniform(rng1, mask.shape, np.float32, 1e-6, 1.0 - 1e-6) g = -np.log(-np.log(u)) selected_experts = np.argmax(log_mask + g * self._temperature, axis=-1) if self._mode == 'train': # Tricks from Section 2.1 in https://arxiv.org/abs/1801.09797 quant_mask = metrics.one_hot(selected_experts, self._num_experts) quant_mask = fastmath.stop_gradient(quant_mask) quant_mask += mask - fastmath.stop_gradient( mask) # straight-through # We will sometimes (50% of the batches) use the soft-mask instead of # the quantized mask to improve training stability (see the paper above). # Q: is selecting 50% of batches the best? Other %? Mixed in-batch? select = fastmath.random.uniform(rng2, (), np.float32, -1.0, 1.0) quant_mask = np.where(select > 0.0, quant_mask, mask) else: quant_mask = metrics.one_hot(selected_experts, self._num_experts) quant_mask = np.reshape(quant_mask, [-1, self._num_experts, 1]) quant_mask_shape = quant_mask.shape batch_size = quant_mask.shape[0] if self._mode == 'predict' and batch_size == 1: # This implementation mimicks inference for batch_size 1. start_idx = selected_experts[0] * self._n_elements_in_block # w1 is [d_model, d_ff], w is [d_model, n_elements_in_block] w = jax.lax.dynamic_slice(w1, [0, start_idx], [w1.shape[0], self._n_elements_in_block]) mid = np.dot(x, w) relu = np.where(mid <= 0, np.zeros_like(mid), mid) # w2 is [d_ff, d_model], v is [n_elements_in_block, d_model] v = jax.lax.dynamic_slice( w2, [start_idx, 0], [self._n_elements_in_block, w2.shape[-1]]) v = np.reshape(v, [self._n_elements_in_block, -1]) res = np.dot(relu, v) + b2 else: expanded_mask = np.broadcast_to( quant_mask, (quant_mask_shape[0], quant_mask.shape[1], self._n_elements_in_block)) expanded_mask = np.reshape(expanded_mask, (-1, self._d_ff)) mid = np.dot(x, w1) * expanded_mask # [joint_batch, d_ff] relu = np.where(mid <= 0, np.zeros_like(mid), mid) res = np.dot(relu, w2) + b2 return np.reshape(res, x_shape) # un-flatten if needed
def entropy(self, inputs): (_, std) = self._params(inputs) return jnp.sum(jnp.exp(std) + .5 * jnp.log(2.0 * jnp.pi * jnp.e), axis=-1)
def f(model_output, targets): # pylint: disable=invalid-name probabilities = fastmath.expit(model_output) binary_entropies = -(targets * jnp.log(probabilities) + (1 - targets) * (jnp.log(1 - probabilities))) return jnp.average(binary_entropies)
def Log(): """Returns a layer that computes the element-wise logarithm of a tensor.""" return Fn('Log', lambda x: jnp.log(x)) # pylint: disable=unnecessary-lambda
def entropy(self, log_probs): del log_probs # would be helpful if self._std was learnable return jnp.exp(self._std) + .5 * jnp.log(2.0 * jnp.pi * jnp.e)
def gumbel_sample(log_probs, temperature=1.0): """Gumbel sampling from a categorical distribution.""" u = numpy.random.uniform(low=1e-6, high=1.0 - 1e-6, size=log_probs.shape) g = -np.log(-np.log(u)) return np.argmax(log_probs + g * temperature, axis=-1)