Vowpal Wabbit
ccb_label.cc
Go to the documentation of this file.
2 #include "reductions.h"
3 #include "example.h"
4 #include "global_data.h"
5 #include "cache.h"
6 #include "vw.h"
7 #include "interactions.h"
8 #include "label_dictionary.h"
9 #include "cb_adf.h"
10 #include "cb_algs.h"
11 #include "constant.h"
12 
13 #include <numeric>
14 #include <algorithm>
15 #include <unordered_set>
16 #include <cmath>
17 
18 using namespace LEARNER;
19 using namespace VW;
20 using namespace VW::config;
21 
22 namespace CCB
23 {
24 void default_label(void* v);
25 
26 size_t read_cached_label(shared_data*, void* v, io_buf& cache)
27 {
28  // Since read_cached_features doesn't default the label we must do it here.
29  default_label(v);
30  CCB::label* ld = static_cast<CCB::label*>(v);
31 
32  if (ld->outcome)
33  {
35  }
37 
38  size_t read_count = 0;
39  char* read_ptr;
40 
41  size_t next_read_size = sizeof(ld->type);
42  if (cache.buf_read(read_ptr, next_read_size) < next_read_size)
43  return 0;
44  ld->type = *(CCB::example_type*)read_ptr;
45  read_count += sizeof(ld->type);
46 
47  bool is_outcome_present;
48  next_read_size = sizeof(bool);
49  if (cache.buf_read(read_ptr, next_read_size) < next_read_size)
50  return 0;
51  is_outcome_present = *(bool*)read_ptr;
52  read_count += sizeof(is_outcome_present);
53 
54  if (is_outcome_present)
55  {
57  ld->outcome->probabilities = v_init<ACTION_SCORE::action_score>();
58 
59  next_read_size = sizeof(ld->outcome->cost);
60  if (cache.buf_read(read_ptr, next_read_size) < next_read_size)
61  return 0;
62  ld->outcome->cost = *(float*)read_ptr;
63  read_count += sizeof(ld->outcome->cost);
64 
65  uint32_t size_probs;
66  next_read_size = sizeof(size_probs);
67  if (cache.buf_read(read_ptr, next_read_size) < next_read_size)
68  return 0;
69  size_probs = *(uint32_t*)read_ptr;
70  read_count += sizeof(size_probs);
71 
72  for (uint32_t i = 0; i < size_probs; i++)
73  {
75  next_read_size = sizeof(a_s);
76  if (cache.buf_read(read_ptr, next_read_size) < next_read_size)
77  return 0;
78  a_s = *(ACTION_SCORE::action_score*)read_ptr;
79  read_count += sizeof(a_s);
80 
82  }
83  }
84 
85  uint32_t size_includes;
86  next_read_size = sizeof(size_includes);
87  if (cache.buf_read(read_ptr, next_read_size) < next_read_size)
88  return 0;
89  size_includes = *(uint32_t*)read_ptr;
90  read_count += sizeof(size_includes);
91 
92  for (uint32_t i = 0; i < size_includes; i++)
93  {
94  uint32_t include;
95  next_read_size = sizeof(include);
96  if (cache.buf_read(read_ptr, next_read_size) < next_read_size)
97  return 0;
98  include = *(uint32_t*)read_ptr;
99  read_count += sizeof(include);
101  }
102 
103  next_read_size = sizeof(ld->weight);
104  if (cache.buf_read(read_ptr, next_read_size) < next_read_size)
105  return 0;
106  ld->weight = *(float*)read_ptr;
107  return read_count;
108 }
109 
110 float ccb_weight(void* v)
111 {
112  CCB::label* ld = (CCB::label*)v;
113  return ld->weight;
114 }
115 
116 void cache_label(void* v, io_buf& cache)
117 {
118  char* c;
119  CCB::label* ld = static_cast<CCB::label*>(v);
120  size_t size = sizeof(uint8_t) // type
121  + sizeof(bool) // outcome exists?
122  + (ld->outcome == nullptr ? 0
123  : sizeof(ld->outcome->cost) // cost
124  + sizeof(uint32_t) // probabilities size
125  + sizeof(ACTION_SCORE::action_score) * ld->outcome->probabilities.size()) // probabilities
126  + sizeof(uint32_t) // explicit_included_actions size
127  + sizeof(uint32_t) * ld->explicit_included_actions.size() + sizeof(ld->weight);
128 
129  cache.buf_write(c, size);
130 
131  *(uint8_t*)c = static_cast<uint8_t>(ld->type);
132  c += sizeof(ld->type);
133 
134  *(bool*)c = ld->outcome != nullptr;
135  c += sizeof(bool);
136 
137  if (ld->outcome != nullptr)
138  {
139  *(float*)c = ld->outcome->cost;
140  c += sizeof(ld->outcome->cost);
141 
142  *(uint32_t*)c = convert(ld->outcome->probabilities.size());
143  c += sizeof(uint32_t);
144 
145  for (const auto& score : ld->outcome->probabilities)
146  {
147  *(ACTION_SCORE::action_score*)c = score;
148  c += sizeof(ACTION_SCORE::action_score);
149  }
150  }
151 
152  *(uint32_t*)c = convert(ld->explicit_included_actions.size());
153  c += sizeof(uint32_t);
154 
155  for (const auto& included_action : ld->explicit_included_actions)
156  {
157  *(uint32_t*)c = included_action;
158  c += sizeof(included_action);
159  }
160 
161  *(float*)c = ld->weight;
162  c += sizeof(ld->weight);
163 }
164 
165 void default_label(void* v)
166 {
167  CCB::label* ld = static_cast<CCB::label*>(v);
168 
169  // This is tested against nullptr, so unfortunately as things are this must be deleted when not used.
170  if (ld->outcome)
171  {
173  delete ld->outcome;
174  ld->outcome = nullptr;
175  }
176 
179  ld->weight = 1.0;
180 }
181 
182 bool test_label(void* v)
183 {
184  CCB::label* ld = static_cast<CCB::label*>(v);
185  return ld->outcome == nullptr;
186 }
187 
188 void delete_label(void* v)
189 {
190  CCB::label* ld = static_cast<CCB::label*>(v);
191  if (ld->outcome)
192  {
194  delete ld->outcome;
195  ld->outcome = nullptr;
196  }
198 }
199 
200 void copy_label(void* dst, void* src)
201 {
202  CCB::label* ldDst = static_cast<CCB::label*>(dst);
203  CCB::label* ldSrc = static_cast<CCB::label*>(src);
204 
205  if (ldSrc->outcome)
206  {
208  ldDst->outcome->probabilities = v_init<ACTION_SCORE::action_score>();
209 
210  ldDst->outcome->cost = ldSrc->outcome->cost;
212  }
213 
215  ldDst->type = ldSrc->type;
216  ldDst->weight = ldSrc->weight;
217 }
218 
219 ACTION_SCORE::action_score convert_to_score(const substring& action_id_str, const substring& probability_str)
220 {
221  auto action_id = static_cast<uint32_t>(int_of_substring(action_id_str));
222  auto probability = float_of_substring(probability_str);
223  if (std::isnan(probability))
224  THROW("error NaN probability: " << probability_str);
225 
226  if (probability > 1.0)
227  {
228  std::cerr << "invalid probability > 1 specified for an outcome, resetting to 1.\n";
229  probability = 1.0;
230  }
231  if (probability < 0.0)
232  {
233  std::cerr << "invalid probability < 0 specified for an outcome, resetting to 0.\n";
234  probability = .0;
235  }
236 
237  return {action_id, probability};
238 }
239 
240 //<action>:<cost>:<probability>,<action>:<probability>,<action>:<probability>,…
242 {
243  auto& ccb_outcome = *(new CCB::conditional_contextual_bandit_outcome());
244 
245  auto split_commas = v_init<substring>();
246  tokenize(',', outcome, split_commas);
247 
248  auto split_colons = v_init<substring>();
249  tokenize(':', split_commas[0], split_colons);
250 
251  if (split_colons.size() != 3)
252  THROW("Malformed ccb label");
253 
254  ccb_outcome.probabilities = v_init<ACTION_SCORE::action_score>();
255  ccb_outcome.probabilities.push_back(convert_to_score(split_colons[0], split_colons[2]));
256 
257  ccb_outcome.cost = float_of_substring(split_colons[1]);
258  if (std::isnan(ccb_outcome.cost))
259  THROW("error NaN cost: " << split_colons[1]);
260 
261  split_colons.clear();
262 
263  for (size_t i = 1; i < split_commas.size(); i++)
264  {
265  tokenize(':', split_commas[i], split_colons);
266  if (split_colons.size() != 2)
267  THROW("Must be action probability pairs");
268  ccb_outcome.probabilities.push_back(convert_to_score(split_colons[0], split_colons[1]));
269  }
270 
271  split_colons.delete_v();
272  split_commas.delete_v();
273 
274  return &ccb_outcome;
275 }
276 
278 {
279  for (const auto& inclusion : split_inclusions)
280  {
282  }
283 }
284 
286 {
287  CCB::label* ld = static_cast<CCB::label*>(v);
288  ld->weight = 1.0;
289 
290  if (words.size() < 2)
291  THROW("ccb labels may not be empty");
292  if (!substring_equal(words[0], "ccb"))
293  {
294  THROW("ccb labels require the first word to be ccb");
295  }
296 
297  auto type = words[1];
298  if (substring_equal(type, "shared"))
299  {
300  if (words.size() > 2)
301  THROW("shared labels may not have a cost");
303  }
304  else if (substring_equal(type, "action"))
305  {
306  if (words.size() > 2)
307  THROW("action labels may not have a cost");
309  }
310  else if (substring_equal(type, "slot"))
311  {
312  if (words.size() > 4)
313  THROW("ccb slot label can only have a type cost and exclude list");
315 
316  // Skip the first two words "ccb <type>"
317  for (size_t i = 2; i < words.size(); i++)
318  {
319  auto is_outcome = std::find(words[i].begin, words[i].end, ':');
320  if (is_outcome != words[i].end)
321  {
322  if (ld->outcome != nullptr)
323  {
324  THROW("There may be only 1 outcome associated with a slot.")
325  }
326 
327  ld->outcome = parse_outcome(words[i]);
328  }
329  else
330  {
331  tokenize(',', words[i], p->parse_name);
333  }
334  }
335 
336  // If a full distribution has been given, check if it sums to 1, otherwise throw.
337  if (ld->outcome && ld->outcome->probabilities.size() > 1)
338  {
339  float total_pred = std::accumulate(ld->outcome->probabilities.begin(), ld->outcome->probabilities.end(), 0.f,
340  [](float result_so_far, ACTION_SCORE::action_score action_pred) {
341  return result_so_far + action_pred.score;
342  });
343 
344  // TODO do a proper comparison here.
345  if (total_pred > 1.1f || total_pred < 0.9f)
346  {
347  THROW("When providing all predicition probabilties they must add up to 1.f");
348  }
349  }
350  }
351  else
352  {
353  THROW("unknown label type: " << type);
354  }
355 }
356 
357 // Export the definition of this label parser.
359  copy_label, test_label, sizeof(CCB::label)};
360 } // namespace CCB
void delete_label(void *v)
Definition: ccb_label.cc:188
int int_of_substring(substring s)
void parse_label(parser *p, shared_data *, void *v, v_array< substring > &words)
Definition: ccb_label.cc:285
void accumulate(vw &all, parameters &weights, size_t offset)
Definition: accumulate.cc:20
example_type
Definition: ccb_label.h:22
void copy_array(v_array< T > &dst, const v_array< T > &src)
Definition: v_array.h:185
CCB::conditional_contextual_bandit_outcome * parse_outcome(substring &outcome)
Definition: ccb_label.cc:241
uint32_t action
Definition: search.h:19
float weight
Definition: ccb_label.h:36
label_parser ccb_label_parser
Definition: ccb_label.cc:358
v_array< substring > parse_name
Definition: parser.h:100
T *& begin()
Definition: v_array.h:42
ACTION_SCORE::action_scores probabilities
Definition: ccb_label.h:19
size_t size() const
Definition: v_array.h:68
void parse_explicit_inclusions(CCB::label *ld, v_array< substring > &split_inclusions)
Definition: ccb_label.cc:277
size_t read_cached_label(shared_data *, void *v, io_buf &cache)
Definition: ccb_label.cc:26
void copy_label(void *dst, void *src)
Definition: ccb_label.cc:200
float float_of_substring(substring s)
void push_back(const T &new_ele)
Definition: v_array.h:107
void clear()
Definition: v_array.h:88
void tokenize(char delim, substring s, ContainerT &ret, bool allow_empty=false)
Definition: io_buf.h:54
void default_label(void *v)
Definition: ccb_label.cc:165
T *& end()
Definition: v_array.h:43
Definition: ccb_label.cc:22
example_type type
Definition: ccb_label.h:32
void buf_write(char *&pointer, size_t n)
Definition: io_buf.cc:94
void cache_label(void *v, io_buf &cache)
Definition: ccb_label.cc:116
ACTION_SCORE::action_score convert_to_score(const substring &action_id_str, const substring &probability_str)
Definition: ccb_label.cc:219
node_pred * find(recall_tree &b, uint32_t cn, example &ec)
Definition: recall_tree.cc:126
v_array< uint32_t > explicit_included_actions
Definition: ccb_label.h:35
Definition: autolink.cc:11
bool substring_equal(const substring &a, const substring &b)
bool test_label(void *v)
Definition: simple_label.cc:70
uint32_t convert(size_t number)
Definition: cache.cc:211
Definition: parser.h:38
void delete_v()
Definition: v_array.h:98
#define THROW(args)
Definition: vw_exception.h:181
constexpr uint64_t c
Definition: rand48.cc:12
float ccb_weight(void *v)
Definition: ccb_label.cc:110
float f
Definition: cache.cc:40
conditional_contextual_bandit_outcome * outcome
Definition: ccb_label.h:34
size_t buf_read(char *&pointer, size_t n)
Definition: io_buf.cc:12