diff --git a/extension/llm/sampler/sampler.cpp b/extension/llm/sampler/sampler.cpp index 2153a144a22..f0e4c02415b 100644 --- a/extension/llm/sampler/sampler.cpp +++ b/extension/llm/sampler/sampler.cpp @@ -251,6 +251,20 @@ int32_t Sampler::sample(T* logits) { return next; } +template <> +int32_t Sampler::sample( + executorch::aten::Half* logits) { + if (inv_temperature_ == 0.0f) { + return sample_argmax(logits); + } + float_logits_buffer_.resize(vocab_size_); + for (int i = 0; i < vocab_size_; i++) { + float_logits_buffer_[i] = static_cast(logits[i]); + } + + return sample(float_logits_buffer_.data()); +} + template <> int32_t Sampler::sample( executorch::aten::BFloat16* logits) { @@ -264,8 +278,6 @@ int32_t Sampler::sample( template int32_t Sampler::sample(float* logits); template int32_t Sampler::sample(uint16_t* logits); -template int32_t Sampler::sample( - executorch::aten::Half* logits); } // namespace llm } // namespace extension diff --git a/extension/llm/sampler/sampler.h b/extension/llm/sampler/sampler.h index 13e964dcb27..6d8300f8cde 100644 --- a/extension/llm/sampler/sampler.h +++ b/extension/llm/sampler/sampler.h @@ -84,6 +84,9 @@ class ET_EXPERIMENTAL Sampler { std::vector float_logits_buffer_; }; +template <> +int32_t Sampler::sample(executorch::aten::Half* logits); + template <> int32_t Sampler::sample( executorch::aten::BFloat16* logits); diff --git a/extension/llm/sampler/test/test_sampler.cpp b/extension/llm/sampler/test/test_sampler.cpp index 4c6718b1e16..ccd8cd0f5e5 100644 --- a/extension/llm/sampler/test/test_sampler.cpp +++ b/extension/llm/sampler/test/test_sampler.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -69,6 +70,39 @@ TEST(SamplerTest, TestBFloat16SamplingUsesFloatArithmetic) { float_sampler.sample(float_logits.data())); } +class HalfSamplingTest : public TestWithParam> {}; + +TEST_P(HalfSamplingTest, MatchesFloatWithLargeVocabulary) { + constexpr int kVocabSize = 32768; + const auto [topp, topk] = GetParam(); + Sampler half_sampler{kVocabSize, 0.7f, topp, 42}; + Sampler float_sampler{kVocabSize, 0.7f, topp, 42}; + half_sampler.set_topk(topk); + float_sampler.set_topk(topk); + + std::vector half_logits(kVocabSize); + std::vector float_logits(kVocabSize); + for (int trial = 0; trial < 16; ++trial) { + for (int i = 0; i < kVocabSize; ++i) { + half_logits[i] = executorch::aten::Half( + trial % 2 == 0 ? 0.0f : (i % 1024) * 0.001f - 0.512f); + float_logits[i] = static_cast(half_logits[i]); + } + EXPECT_EQ( + half_sampler.sample(half_logits.data()), + float_sampler.sample(float_logits.data())) + << "trial " << trial; + } +} + +INSTANTIATE_TEST_SUITE_P( + Sampler, + HalfSamplingTest, + Values( + std::make_pair(1.0f, 0), + std::make_pair(0.9f, 0), + std::make_pair(1.0f, 4096))); + TEST(SamplerTest, TestTopKRestrictsToCandidates) { // With topk=3, sampling must always return one of the top-3 indices, // regardless of the random draw.