Decoder-only Scoring API (#6460)
Co-authored-by: Chanh Nguyen <cnguyen@linkedin.com>
This commit is contained in:
@@ -18,6 +18,7 @@ import copy
|
||||
import dataclasses
|
||||
import json
|
||||
import logging
|
||||
import math
|
||||
import os
|
||||
import pickle
|
||||
import signal
|
||||
@@ -42,6 +43,7 @@ from typing import (
|
||||
)
|
||||
|
||||
import fastapi
|
||||
import torch
|
||||
import uvloop
|
||||
import zmq
|
||||
import zmq.asyncio
|
||||
@@ -1433,6 +1435,100 @@ class TokenizerManager:
|
||||
if len(self.model_update_tmp) == self.server_args.dp_size:
|
||||
self.model_update_result.set_result(self.model_update_tmp)
|
||||
|
||||
async def score_request(
|
||||
self,
|
||||
query: Optional[Union[str, List[int]]] = None,
|
||||
items: Optional[Union[str, List[str], List[List[int]]]] = None,
|
||||
label_token_ids: Optional[List[int]] = None,
|
||||
apply_softmax: bool = False,
|
||||
item_first: bool = False,
|
||||
request: Optional[Any] = None,
|
||||
) -> List[List[float]]:
|
||||
"""
|
||||
See Engine.score() for more details.
|
||||
"""
|
||||
if label_token_ids is None:
|
||||
raise ValueError("label_token_ids must be provided")
|
||||
|
||||
if self.tokenizer is not None:
|
||||
vocab_size = self.tokenizer.vocab_size
|
||||
for token_id in label_token_ids:
|
||||
if token_id >= vocab_size:
|
||||
raise ValueError(
|
||||
f"Token ID {token_id} is out of vocabulary (vocab size: {vocab_size})"
|
||||
)
|
||||
|
||||
# Handle string or tokenized query/items
|
||||
if isinstance(query, str) and (
|
||||
isinstance(items, str)
|
||||
or (isinstance(items, list) and (not items or isinstance(items[0], str)))
|
||||
):
|
||||
# Both query and items are text
|
||||
items_list = [items] if isinstance(items, str) else items
|
||||
if item_first:
|
||||
prompts = [f"{item}{query}" for item in items_list]
|
||||
else:
|
||||
prompts = [f"{query}{item}" for item in items_list]
|
||||
batch_request = GenerateReqInput(
|
||||
text=prompts,
|
||||
return_logprob=True,
|
||||
token_ids_logprob=label_token_ids,
|
||||
stream=False,
|
||||
sampling_params={"max_new_tokens": 1},
|
||||
)
|
||||
elif (
|
||||
isinstance(query, list)
|
||||
and isinstance(items, list)
|
||||
and items
|
||||
and isinstance(items[0], list)
|
||||
):
|
||||
# Both query and items are token IDs
|
||||
if item_first:
|
||||
input_ids_list = [item + query for item in items]
|
||||
else:
|
||||
input_ids_list = [query + item for item in items]
|
||||
batch_request = GenerateReqInput(
|
||||
input_ids=input_ids_list,
|
||||
return_logprob=True,
|
||||
token_ids_logprob=label_token_ids,
|
||||
stream=False,
|
||||
sampling_params={"max_new_tokens": 1},
|
||||
)
|
||||
else:
|
||||
raise ValueError(
|
||||
"Invalid combination of query/items types for score_request."
|
||||
)
|
||||
|
||||
results = await self.generate_request(batch_request, request).__anext__()
|
||||
scores = []
|
||||
|
||||
for result in results:
|
||||
# Get logprobs for each token
|
||||
logprobs = {}
|
||||
for logprob, token_id, _ in result["meta_info"].get(
|
||||
"output_token_ids_logprobs", []
|
||||
)[0]:
|
||||
if token_id in label_token_ids:
|
||||
logprobs[token_id] = logprob
|
||||
|
||||
# Get scores in order of label_token_ids
|
||||
score_list = [
|
||||
logprobs.get(token_id, float("-inf")) for token_id in label_token_ids
|
||||
]
|
||||
|
||||
# Apply softmax to logprobs if needed
|
||||
if apply_softmax:
|
||||
score_list = torch.softmax(torch.tensor(score_list), dim=0).tolist()
|
||||
else:
|
||||
# Convert logprobs to probabilities if not using softmax
|
||||
score_list = [
|
||||
math.exp(x) if x != float("-inf") else 0.0 for x in score_list
|
||||
]
|
||||
|
||||
scores.append(score_list)
|
||||
|
||||
return scores
|
||||
|
||||
|
||||
async def print_exception_wrapper(func):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user