Skip to main content

Choosing the next token

When generating natural language, there are multiple possible next words that make sense in a sentence. Code is more rigid and the list of possible Tokens is reduced.

So, you need a different Transformer decoding methods.

Using the greedy approach (always picking the most likely token) leads to repetitive text: This is also true with a sampling approach on low temperatures (lower than 0.3).

describe('parseBooleanVariables', () => {
it('should parse boolean variables correctly', () => {
const config = {
ENV_VARIABLE_1: 'true',
ENV_VARIABLE_2: 'false',
ENV_VARIABLE_3: 'true',
ENV_VARIABLE_4: 'false',
ENV_VARIABLE_5: 'true',
ENV_VARIABLE_6: 'false',
ENV_VARIABLE_7: 'true',
// ... Goes on infinitly ...

But using a sampling approach leads to code that is "too creative" (bad):

import {NativeConfig} from 'react-native-config';
import {GenericEnvironment} from '..';

export const parseBooleanVariables = (
config: NativeConfig,
): GenericEnvironment => {
const configWithActualBooleans = {...config} as GenericEnvironment;

Object.keys(config).map(key => {
if (config[key] === 'true') {
configWithActualBooleans[key] = true;
} else if (config[key] === 'false') {
configWithActualBooleans[key] = false;
}
});

return configWithActualBooleans;
};

------
import {parseBooleanVariables} from './parseBooleanVariables';

describe('parseBooleanVariables', () => {
it('should return true for all KadLind upgrading Rome Alzheimer ... garbage output

What we want is the code of the greedy approach, but without the repetition. To do this, we can penalize in the probability distribution tokens that were recently generated multiple times. So, by the time "ENV" is produced for the forth time in a few lines, it's probability is lowered enough that it is no longer the top token and something else is produced.

This is described more in depth here: https://arxiv.org/pdf/1909.05858.pdf

My implementation looks like this, we add a special temperature like variable that is only applied on repeated tokens.

past_tokens = []
device = "mps"

def generate_next_token(temperature=0.2, repetition_penality=1.2):
global past_tokens
outputs = model(**self.prompt_as_tokens)
logits = outputs.logits[0][-1] / temperature

scores = torch.ones(logits.shape[0]).to(device)
for i in range(len(self.past_tokens)):
scores[past_tokens[i]] += 1 * repetition_penality

logits = logits / scores

probability_distribution = torch.nn.functional.softmax(logits, dim=-1)

predicted_token = torch.multinomial(probability_distribution, num_samples=1, replacement=False)

# Add the new token to the list of past tokens.
past_tokens.append(predicted_token)
new_tok = tokenizer.decode(predicted_token, skip_special_tokens=True)