1
0

utils.py 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. import warnings
  2. from typing import Dict, List, Optional, Sequence, Tuple, Union
  3. from aphrodite.common.sequence import SampleLogprobs
  4. TokensText = Tuple[List[int], str]
  5. def check_outputs_equal(
  6. *,
  7. outputs_0_lst: Sequence[TokensText],
  8. outputs_1_lst: Sequence[TokensText],
  9. name_0: str,
  10. name_1: str,
  11. ):
  12. """
  13. Compare the two sequences generated by different models,
  14. which should be equal.
  15. """
  16. assert len(outputs_0_lst) == len(outputs_1_lst)
  17. for prompt_idx, (outputs_0,
  18. outputs_1) in enumerate(zip(outputs_0_lst,
  19. outputs_1_lst)):
  20. output_ids_0, output_str_0 = outputs_0
  21. output_ids_1, output_str_1 = outputs_1
  22. # The text and token outputs should exactly match
  23. fail_msg = (f"Test{prompt_idx}:"
  24. f"\n{name_0}:\t{output_str_0!r}"
  25. f"\n{name_1}:\t{output_str_1!r}")
  26. assert output_str_0 == output_str_1, fail_msg
  27. assert output_ids_0 == output_ids_1, fail_msg
  28. TokensTextLogprobs = Tuple[List[int], str, Optional[Union[List[Dict[int,
  29. float]],
  30. SampleLogprobs]]]
  31. def check_logprobs_close(
  32. *,
  33. outputs_0_lst: Sequence[TokensTextLogprobs],
  34. outputs_1_lst: Sequence[TokensTextLogprobs],
  35. name_0: str,
  36. name_1: str,
  37. num_outputs_0_skip_tokens: int = 0,
  38. warn_on_mismatch: bool = True,
  39. ):
  40. """
  41. Compare the logprobs of two sequences generated by different models,
  42. which should be similar but not necessarily equal.
  43. Arguments:
  44. * outputs_0_lst: First sequence to compare
  45. * outputs_0_lst: Second sequence to compare
  46. * name_0: sequence #0 name
  47. * name_1: sequence #1 name
  48. * num_outputs_0_skip_tokens: If > 0, specifies the number of initial
  49. sequence #0 tokens & logprobs to discard
  50. before comparison, i.e. all
  51. of sequence #1 will be compared to
  52. sequence #0 beginning at index
  53. num_outputs_0_skip_tokens
  54. * warn_on_mismatch: Issue a warning if there is token-wise or text-wise
  55. mismatch between the two sequences
  56. """
  57. assert len(outputs_0_lst) == len(outputs_1_lst)
  58. # Loop through responses to each prompt.
  59. for prompt_idx, (outputs_0,
  60. outputs_1) in enumerate(zip(outputs_0_lst,
  61. outputs_1_lst)):
  62. output_ids_0, output_str_0, logprobs_0 = outputs_0
  63. output_ids_1, output_str_1, logprobs_1 = outputs_1
  64. if logprobs_0 is None:
  65. logprobs_0 = [None] * len(output_ids_0)
  66. if logprobs_1 is None:
  67. logprobs_1 = [None] * len(output_ids_1)
  68. # Skip specified number of initial sequence #0 tokens
  69. # & logprobs, leaving output text as-is for simplicity
  70. # (text mismatches may generate warnings but do not
  71. # cause the test to fail.)
  72. if num_outputs_0_skip_tokens < 0:
  73. raise ValueError("num_outputs_0_skip_tokens must be non-negative")
  74. output_ids_0 = output_ids_0[num_outputs_0_skip_tokens:]
  75. logprobs_0 = logprobs_0[num_outputs_0_skip_tokens:]
  76. # Loop through generated tokens.
  77. for idx, (output_id_0,
  78. output_id_1) in enumerate(zip(output_ids_0, output_ids_1)):
  79. # If generated tokens don't match, then
  80. if output_id_0 != output_id_1:
  81. logprobs_elem_0 = logprobs_0[idx]
  82. logprobs_elem_1 = logprobs_1[idx]
  83. # Each predicted token must be in top N logprobs of the other
  84. fail_msg = (
  85. f"Test{prompt_idx}:"
  86. f"\nMatched tokens:\t{output_ids_0[:idx]}"
  87. f"\n{name_0}:\t{output_str_0!r}\t{logprobs_elem_0}"
  88. f"\n{name_1}:\t{output_str_1!r}\t{logprobs_elem_1}")
  89. assert logprobs_elem_0 is not None, fail_msg
  90. assert logprobs_elem_1 is not None, fail_msg
  91. assert output_id_0 in logprobs_elem_1, fail_msg
  92. assert output_id_1 in logprobs_elem_0, fail_msg
  93. if warn_on_mismatch:
  94. with warnings.catch_warnings():
  95. # This ensures that repeated warnings are shown
  96. # in the output, not just the first occurrence
  97. warnings.simplefilter("always")
  98. warnings.warn(fail_msg, stacklevel=2)
  99. # Break out since sequences will now diverge.
  100. break
  101. else:
  102. if output_str_0 != output_str_1 and warn_on_mismatch:
  103. # The token outputs exactly match,
  104. # so the text outputs should exactly match as well
  105. fail_msg = (f"Test{prompt_idx}:"
  106. f"\n{name_0}:\t{output_str_0!r}"
  107. f"\n{name_1}:\t{output_str_1!r}")
  108. with warnings.catch_warnings():
  109. # This ensures that repeated warnings are shown
  110. # in the output, not just the first occurrence
  111. warnings.simplefilter("always")
  112. warnings.warn(fail_msg, stacklevel=2)