This repository provides the pretrained models for the paper "BWArea Model: Learning World Model, Inverse Dynamics, and Policy for Controllable Language Generation". A BWArea model is a complex system that consists of three main components:
The BWArea model can simulate a language model by using the inverse dynamics model to understand (i.e., derive actions from) any given prompt, and then generate language by inputting actions from either the inverse dynamics model or the policy model.
However, the BWArea model is capable of more than just language generation. It is possible to train a custom policy to maximize any reward function, resulting in a task-specific policy model. The reward function can be manually designed, allowing for the creation of policies that accomplish various tasks such as negotiation, persuasion, playing text-based games, and more.
BWArea Model contains three parts: Lanugage World Model (1.1B), Inverse Dynamics Model (0.5B) and Policy Model (1.1B), 2.7B in total. Each module can be utilized seperately or combined for distinguished objective.
1import torch
2from bwareaModel.model_utils import create_intention_model
3from bwareaModel.tokenizer import load_hf_tokenizer
4# load tokenizer
5tokenizer = load_hf_tokenizer(
6 "../intention_pretrained_2.7B_30G", # your model path
7 fast_tokenizer=True,
8 add_special_tokens=None,
9)
10# load model
11model = create_intention_model(
12 "../intention_pretrained_2.7B_30G", # your model path
13 tokenizer=tokenizer,
14 dtype=torch.bfloat16
15)
1# The language world model take actions as input and generate the next token.
2# In this example, you can try different actions and see how the language world model generates
3examples = "I like eating" # this is the prompt that is to be understood by the inverse dynamics model
4fixed_action_idx = 2 # choose your action between 0 to 63
5encodes = tokenizer.encode(examples)
6input_ids = torch.LongTensor(encodes).unsqueeze(0)
7attention_mask = torch.ones_like(input_ids)
8outputs = model(input_ids=input_ids, attention_mask=attention_mask, action_idx=fixed_action_idx)
9logits_next = outputs.logits[:, -1]
10idx = logits_next.argmax(dim=1, keepdim=True)
11output_ids = torch.cat([input_ids, idx], dim=-1).long().squeeze(0)
12examples_out = tokenizer.decode(output_ids)
13print(examples_out, "(fixed action idx = {})".format(fixed_action_idx))
14
15# generation by some different actions. Not that these outputs are not random tokens, but each has a certain semantic meaning.
16
17# <s> I like eating! (fixed action idx = 0)
18# <s> I like eating well (fixed action idx = 1)
19# <s> I like eating n (fixed action idx = 2)
20# <s> I like eating raw (fixed action idx = 3)
21# <s> I like eating a (fixed action idx = 4)
22# <s> I like eating correctly (fixed action idx = 5)
23# <s> I like eating at (fixed action idx = 6)
24# <s> I like eating them (fixed action idx = 7)
25# <s> I like eating lots (fixed action idx = 8)
26# <s> I like eating or (fixed action idx = 9)
27# <s> I like eating out (fixed action idx = 10)
28# <s> I like eating it (fixed action idx = 11)
29# <s> I like eating in (fixed action idx = 12)
30# <s> I like eating this (fixed action idx = 13)
31# <s> I like eating pot (fixed action idx = 14)
32# <s> I like eating bread (fixed action idx = 15)
33# <s> I like eating my (fixed action idx = 16)
34# <s> I like eating meat (fixed action idx = 17)
35# <s> I like eating the (fixed action idx = 18)
36# <s> I like eating car (fixed action idx = 19)
1fixed_action_idx = torch.randint(0, 64, size=input_ids.shape).long()
2outputs = model(input_ids=input_ids, attention_mask=attention_mask, action_idx=fixed_action_idx)
3logits_next = outputs.logits[:, -1]
4idx = logits_next.argmax(dim=1, keepdim=True)
5output_ids = torch.cat([input_ids, idx], dim=-1).long().squeeze(0)
6examples_out = tokenizer.decode(output_ids)
7print(examples_out)
8
9# <s> I like eating fresh
1examples = "I like eating"
2encodes = tokenizer.encode(examples)
3input_ids = torch.LongTensor(encodes).unsqueeze(0)
4attention_mask = torch.ones_like(input_ids)
5outputs = model.forward_inverse(input_ids=input_ids, attention_mask=attention_mask)
6action_idx = outputs.action_index[:, :-1]
7print(action_idx.shape, action_idx)
8
9# print outputs
10# torch.Size([1, 4]) tensor([[45, 45, 45, 45]])
11# means that the sentence "I like eating" mainly using action no.45
12
1# The policy model was pretrained according to the training data.
2# This example shows the actions of the pre-trained policy
3model.set_action_sampling(greedy=False, temp=2.0) # greedy=True for deterministic action, temp for temperature of action sampling
4examples = "I like eating"
5encodes = tokenizer.encode(examples)
6input_ids = torch.LongTensor(encodes).unsqueeze(0)
7attention_mask = torch.ones_like(input_ids)
8outputs = model.forward_policy(input_ids=input_ids, attention_mask=attention_mask)
9
10# get action logits
11action_logits = outputs.logits[:, -1]
12
13# get action index
14action_index = outputs.action_index[:, -1]
15print(action_index)
1# This example shows the intermediate variable of using the pretrained policy model in the language world model.
2model.set_action_sampling(greedy=False, temp=2.0) # greedy=True for determinitic action, temp for temperature of action sampling
3examples = "I like eating"
4encodes = tokenizer.encode(examples)
5input_ids = torch.LongTensor(encodes).unsqueeze(0)
6attention_mask = torch.ones_like(input_ids)
7outputs = model(input_ids=input_ids, attention_mask=attention_mask)
8
9# get action index
10action_index = outputs.action_index
11
12# get logits
13logits = outputs.logits
14
15# get embeddings
16embeddings = outputs.last_hidden_state
17print(logits.shape, embeddings.shape)
1# This example uses the BWArea as a common LLM for language generation.
2examples = "I like eating"
3encodes = tokenizer.encode(examples)
4input_ids = torch.LongTensor(encodes).unsqueeze(0)
5attention_mask = torch.ones_like(input_ids)
6batch_inputs = {
7 "input_ids": input_ids,
8 "attention_mask": attention_mask,
9}
10
11with torch.no_grad():
12 outputs = model.generate(
13 **batch_inputs,
14 max_new_tokens=10,
15 pad_token_id=tokenizer.pad_token_id,
16 do_sample=True,
17 top_p=1.0,
18 temperature=0.8,
19 top_k=1,
20 )
21 outputs = outputs.squeeze(0)
22 examples_output = tokenizer.decode(outputs, skip_special_tokens=True)
23 print(examples_output)
24
25# I like eating something soothing and helping to tone my body and
Since BWArea model treats the language generation as a decision tasks on the language world model and a certain reward (human intention or specific tasks), one of the advantage of BWArea Model is that we can only optimize the policy model to align a certain human intention or tasks.
1# This example trains the policy using your own reward function.
2tokenizer = load_hf_tokenizer(
3 "../intention_pretrained_2.7B_30B/", # model path
4 fast_tokenizer=True,
5 add_special_tokens=None,
6)
7# load model
8model = create_intention_model(
9 "../intention_pretrained_2.7B_30B/", # model path
10 tokenizer=tokenizer,
11 dtype=torch.bfloat16
12)
13
14import torch.nn as nn
15import torch.nn.functional as F
16def mark_only_param_as_trainable(model: nn.Module, bias: str = "none") -> None:
17 for n, p in model.named_parameters():
18 if bias not in n:
19 p.requires_grad = False
20mark_only_param_as_trainable(model, bias="policy")
21trainable_params = [p for n, p in model.named_parameters() if p.requires_grad]
22optimizer = torch.optim.AdamW(trainable_params, lr=1e-4)
23
24# define your reward function
25def reward_function(seq):
26 return torch.randn(seq.shape[0], 1)
27
28batch_inputs = {
29 "input_ids": torch.randint(10, 30000, size=(4, 16)).long(),
30 "attention_mask": torch.ones((4, 16)).long(),
31}
32prompt_length = batch_inputs['input_ids'].shape[1]
33
34# sampling
35model.reset_action_info()
36outputs = model.generate(
37 **batch_inputs,
38 max_new_tokens=10,
39 pad_token_id=tokenizer.pad_token_id,
40 do_sample=True,
41 top_p=1.0,
42 temperature=0.8,
43 top_k=1,
44)
45# reward labeling
46reward = reward_function(outputs)
47
48# get action index
49acction_info = model.get_action_info()
50action_idx = torch.cat(acction_info["action_idx"], dim=1)
51model.reset_action_info()
52
53# get action logits
54outputs_mask = torch.ones_like(outputs).long()
55outputs_mask[outputs_mask == tokenizer.pad_token_id] = 0
56outputs = model.forward_policy(input_ids=outputs, attention_mask=outputs_mask)
57action_logits = outputs.logits[:, prompt_length:]
58
59# compute loss with reward, action_index and action_logits
60action_mask = outputs_mask[:, prompt_length:]
61action_log_probs = torch.log(F.softmax(action_logits, dim=-1))
62action_log_probs = action_log_probs.gather(index=action_idx.unsqueeze(-1), dim=-1).squeeze(-1)
63
64print(reward.shape, action_log_probs.shape, action_mask.shape)
65loss = - (reward * action_log_probs * action_mask).sum() / action_mask.sum()
66print(loss.item())
67
68# optimize
69optimizer.zero_grad()
70loss.backward()
71optimizer.step()