Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

test: [RLOS2023] add test for slate #4629

Merged
merged 3 commits into from
Aug 7, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions python/tests/test_framework/slate/action_space.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
def threeSlot_clothes_sunny_raining(**kwargs):
iteration = kwargs.get("iteration", 0)
# before iteration 500, it is sunny and after it is raining
if iteration > 500:
return [
["buttonupshirt", "highvis", "rainshirt"],
["formalpants", "rainpants", "shorts"],
["rainshoe", "formalshoes", "flipflops"],
]

return [
["tshirt", "longshirt", "turtleneck"],
["workpants", "shorts", "formalpants"],
["formalshoes", "runners", "flipflops"],
]
78 changes: 78 additions & 0 deletions python/tests/test_framework/slate/data_generation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import random
import os
from test_helper import get_function_object

script_directory = os.path.dirname(os.path.realpath(__file__))
random.seed(10)


def random_number_items(items):
num_items_to_select = random.randint(1, len(items))
return random.sample(items, num_items_to_select)


def generate_slate_data(
num_examples,
num_actions,
reward_function,
logging_policy,
action_space,
num_slots=1,
num_context=1,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

num_context is conflicting with context_name. Can we allow context_name to be a list and infer no_context from it?

context_name=None,
slot_name=None,
ataymano marked this conversation as resolved.
Show resolved Hide resolved
):

dataFile = f"slate_test_{num_examples}_{len(num_actions)}_{num_slots}.txt"

reward_function_obj = get_function_object(
"slate.reward_functions", reward_function["name"]
)
logging_policy_obj = get_function_object(
"slate.logging_policies", logging_policy["name"]
)

action_space_obj = get_function_object("slate.action_space", action_space["name"])

def return_cost_probability(chosen_action, chosen_slot, context=1):
cost = reward_function_obj(
chosen_action, context, chosen_slot, **reward_function["params"]
)
logging_policy["params"]["num_action"] = num_actions[chosen_slot - 1]
logging_policy["params"]["chosen_action"] = chosen_action
probability = logging_policy_obj(**logging_policy["params"])
return cost, probability

if not slot_name:
slot_name = [f"slot_{index}" for index in range(1, num_slots + 1)]
with open(os.path.join(script_directory, dataFile), "w") as f:
for i in range(num_examples):
chosen_actions = []
if num_context > 1:
context = random.randint(1, num_context)
if not context_name:
context_name = [f"{index}" for index in range(1, num_context + 1)]
for s in range(num_slots):
chosen_actions.append(random.randint(1, num_actions[s]))
chosen_actions_cost_prob = [
return_cost_probability(action, slot + 1, context)
for slot, action in enumerate(chosen_actions)
]
total_cost = sum([cost for cost, _ in chosen_actions_cost_prob])

f.write(f"slates shared {total_cost} |User {context_name[context-1]}\n")
# write actions
action_space["params"]["iteration"] = i
action_spaces = action_space_obj(**action_space["params"])
for ind, slot in enumerate(action_spaces):
for a in slot:
f.write(
f"slates action {ind} |Action {a}\n",
)

for s in range(num_slots):
f.write(
f"slates slot {chosen_actions[s]}:{chosen_actions_cost_prob[s][1]} |Slot {slot_name[s]}\n"
)
f.write("\n")
return os.path.join(script_directory, dataFile)
3 changes: 3 additions & 0 deletions python/tests/test_framework/slate/logging_policies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
def even_probability(chosen_action, **kwargs):
num_actions = kwargs["num_action"]
return round(1 / num_actions, 2)
11 changes: 11 additions & 0 deletions python/tests/test_framework/slate/reward_functions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
def fixed_reward(chosen_action, context, slot, **kwargs):
reward = kwargs["reward"]
return reward[slot - 1][chosen_action - 1]


def changing_reward(chosen_action, context, slot, **kwargs):
reward = kwargs["reward"]
iteration = kwargs.get("iteration", 0)
if iteration > 500:
reward = [i[::-1] for i in reward]
return reward[slot - 1][chosen_action - 1]
130 changes: 130 additions & 0 deletions python/tests/test_framework/test_configs/slate.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
[
{
"test_name": "slates",
"data_func": {
"name": "generate_slate_data",
"params": {
"num_examples": 1000,
"num_action": [
ataymano marked this conversation as resolved.
Show resolved Hide resolved
3,
3,
ataymano marked this conversation as resolved.
Show resolved Hide resolved
3
],
"reward_function": {
"name": "changing_reward",
ataymano marked this conversation as resolved.
Show resolved Hide resolved
"params": {
"reward": [
[
0,
1,
1
],
[
1,
0,
1
],
[
1,
1,
0
]
]
}
},
"logging_policy": {
"name": "even_probability",
"params": {}
},
"action_space": {
"name": "threeSlot_clothes_sunny_raining",
"params": {}
},
"num_slot": 3,
"num_context": 2
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are not using context here, so maybe we can start with 2 separate test scenarios:

  • without context change (with reward function definition similar to what we have now)
  • with context change (and with more explicit context dependency in reward function definition)

}
},
"assert_functions": [
{
"name": "assert_loss",
"params": {
"expected_loss": 3.1,
"decimal": 0.1
}
},
{
"name": "assert_prediction",
"params": {
"expected_value": [
0.866667,
0.066667,
0.066667
],
"threshold": 0.8,
"atol": 0.01,
"rtol": 0.01
}
}
],
"grids": {
"g0": {
"#base": [
"--slates"
]
},
"g1": {
"--epsilon": [
0.1,
0.2,
0.3
]
},
"g2": {
"--first": [
1,
2
]
},
"g3": {
"--bag": [
5,
6,
7
]
},
"g4": {
"--cover": [
1,
2,
3
]
},
"g5": {
"--squarecb": [
"--gamma_scale 1000",
"--gamma_scale 10000"
]
},
"g6": {
"--synthcover": [
""
]
},
"g7": {
"--regcb": [
""
]
},
"g8": {
"--softmax": [
""
]
}
},
"grids_expression": "g0 * (g1 + g2 + g3 + g4 + g5 +g6 + g7 + g8)",
ataymano marked this conversation as resolved.
Show resolved Hide resolved
"output": [
"--readable_model",
"-p"
]
}
]
3 changes: 1 addition & 2 deletions python/tests/test_framework/test_core.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
dynamic_function_call,
get_function_object,
evaluate_expression,
variable_mapping,
copy_file,
)
from conftest import STORE_OUTPUT
Expand Down Expand Up @@ -43,7 +42,7 @@ def cleanup_data_file():
@pytest.fixture
def test_descriptions(request):
resource = request.param
yield resource #
yield resource
cleanup_data_file()


Expand Down
Loading