Создайте следующий код и назовите его run.py
from vllm import LLM, SamplingParams
prompts = [
"Have you followed marsggbo in Zhihu?",
«Ты ударил три раза подряд?»
] # входитьprompts
sampling_params = SamplingParams(temperature=0.8, top_k=50) # Стратегия выборки
llm = LLM(model="facebook/opt-125m", tensor_parallel_size=2) # инициализация LLM
outputs = llm.generate(prompts, sampling_params) # полное рассуждение
for output in outputs:
prompt = output.prompt
generated_text = output.outputs[0].text
print(f"Prompt: {prompt!r}, Generated text: {generated_text!r}")
выполнить команду:python run.py
。Скрипт автоматически Модель Параллельно с тензорамииз方式在两个 GPU Выполните вычисления вывода дальше.
Общий ход всего процесса рассуждений показан на рисунке ниже, а именно 1 Учитывая определенное количество подсказок (массив строк) 2. vllm будет использовать модуль Планировщика для автоматического планирования предложений, требующих рассуждения. 3. В соответствии с результатами планирования используйте токенизатор для преобразования строки в идентификатор приглашения, а затем передайте ее в модель для расчета, чтобы получить результаты прогнозирования логитов. 4. Выборка результатов в соответствии с результатами прогнозирования логитов и заранее установленной стратегией выборки для получения нового идентификатора токена. 5. Сохраните результаты отбора проб на вывод.
Структурные связи между основными модулями vLLM. Далее мы начнем с простых модулей (т. е. ввода, выборки и вывода) и закончим подробным введением в модуль LLM.
Как показано на рисунке выше, мы видим, что vLLM разработала множество подмодулей для входных предложений. Эти модули используются по-разному, но они связаны друг с другом. Подробно они представлены ниже.
впервые увидел SequenceStatus
,Его исходный код выглядит следующим образом:
class SequenceStatus(enum.Enum):
"""Status of a sequence."""
WAITING = enum.auto() # Ожидание середина, предложение не начало рассуждения, или рассуждение еще не закончилось
RUNNING = enum.auto() # 运行середина SWAPPED = enum.auto() # обменян
FINISHED_STOPPED = enum.auto() # Остановлено
FINISHED_LENGTH_CAPPED = enum.auto() # Длина ограничена
FINISHED_ABORTED = enum.auto() # ужесередина止 FINISHED_IGNORED = enum.auto() # игнорируется
@staticmethod
def is_finished(status: "SequenceStatus") -> bool:
# суждениесостояние Это Остановлено、Длина ограничена、ужесерединаостановись илиигнорируется
return status in [
SequenceStatus.FINISHED_STOPPED,
SequenceStatus.FINISHED_LENGTH_CAPPED,
SequenceStatus.FINISHED_ABORTED,
SequenceStatus.FINISHED_IGNORED,
]
SequenceData
Используется для хранения связанных с последовательностьюизданные。Этот класс имеет три атрибута:prompt_token_ids
(подсказать словоизотметкаID)、output_token_ids
(генерировать текстизотметкаID)иcumulative_logprob
(совокупная логарифмическая вероятность)。
class SequenceData:
def __init__(
self,
prompt_token_ids: List[int],
) -> None:
self.prompt_token_ids = prompt_token_ids
self.output_token_ids: List[int] = []
self.cumulative_logprob = 0.0
Sequence
Используется для хранения изданных последовательностей.、состояниеиблокировать информация, и каждая последовательность имеет уникальный идентификатор,Прямо сейчасseq_id
。Обратите внимание на следующееизкод:
SequenceData
СохранятьSequenceStatus.WAITING
logical_token_blocks
Включатьиз block Число будет больше.class Sequence:
def __init__(
self,
seq_id: int,
prompt: str,
prompt_token_ids: List[int],
block_size: int,
) -> None:
self.seq_id = seq_id
self.prompt = prompt
self.block_size = block_size
self.data = SequenceData(prompt_token_ids) # данные
self.logical_token_blocks: List[LogicalTokenBlock] = []
# Initialize the logical token blocks with the prompt token ids.
self._append_tokens_to_blocks(prompt_token_ids) # блокировать информацию
self.status = SequenceStatus.WAITING # состояние
...
Sequence
толькоодинокийпоследовательностьиз Выражение,seq_id
этоизуникальный идентификатор。SequenceGroup
则是为Понятно表示много个последовательность,request_id
этоизуникальный идентификатор,Указывает, какой это запрос.
Конкретно,можно увидеть__init__
Функция имеет параметр, который seqs: List[Sequence]
,Он представляет собой один или несколько Sequence Состав исписок,тогда пройдетself.seqs_dict = {seq.seq_id: seq for seq in seqs}
Преобразование в словарь для удобства управления.,Этот словарь из key это каждый Sequence изуникальный идентификаторseq_id
。
class SequenceGroup:
def __init__(
self,
request_id: str,
seqs: List[Sequence],
sampling_params: SamplingParams,
arrival_time: float,
lora_request: Optional[LoRARequest] = None,
prefix: Optional[Prefix] = None,
) -> None:
self.request_id = request_id
self.seqs_dict = {seq.seq_id: seq for seq in seqs}
self.sampling_params = sampling_params
self.arrival_time = arrival_time
...
Ниже приведен пример сценария, в котором LLMEngine использует Sequence и SequenceGroup в vLLm:
class LLMEngine:
def add_request(
self,
request_id: str,
prompt: Optional[str],
sampling_params: SamplingParams,
prompt_token_ids: Optional[List[int]] = None,
arrival_time: Optional[float] = None,
lora_request: Optional[LoRARequest] = None,
prefix_pos: Optional[int] = None,
) -> None:
prompt_token_ids = self.encode_request(
request_id=request_id,
prompt=prompt,
prompt_token_ids=prompt_token_ids,
lora_request=lora_request) # Преобразовать последовательность строк в id
# Create the sequences.
block_size = self.cache_config.block_size
seq_id = next(self.seq_counter)
seq = Sequence(seq_id, prompt, prompt_token_ids, block_size,
lora_request)
# Create the sequence group.
seq_group = SequenceGroup(request_id, [seq], sampling_params,
arrival_time)
# Add the sequence group to the scheduler.
self.scheduler.add_seq_group(seq_group)
можно увидетьSequenceGroup
изseqs
параметр在最初阶段на самом делетолькоодинокийпоследовательность ,Прямо сейчас[seq]
。但是我们知道на самом деле一个 prompt Может быть несколько выходных результатов,такSequenceGroup
изглазиззаключается в управлениивходить promptизмногосгенерированная последовательностьинформация。Если мы установимSamplingParams.n=2
(第 4 Знакомство с фестивалем),Затем в процессе рассуждениясередина,SequenceGroup
Будет добавлен новый Последовательность, это новое Sequence из seq_id Оказывается, из этого Sequence В отличие от этого, конкретные детали кода будут представлены в следующей статье середина.
class SequenceGroupMetadata:
def __init__(
self,
request_id: str,
is_prompt: bool,
seq_data: Dict[int, SequenceData],
sampling_params: SamplingParams,
block_tables: Dict[int, List[int]],
) -> None:
self.request_id = request_id
self.is_prompt = is_prompt
self.seq_data = seq_data
self.sampling_params = sampling_params
self.block_tables = block_tables
...
SequenceGroupMetadata Записывается некоторая метаинформация, как показано в следующем коде. Scheduler модуль是нравиться何生成这些информацияиз:
request_id
то есть SequenceGroupиз request_idseq_data
это словарь, ключ это каждый Sequenceиз seq_id,value Это соответствует из data (Прямо сейчас SequenceData)block_tables
такжеэто словарь, ключ такжеэто каждый Sequenceиз seq_id,value Это переписка Sequence Применять blockclass Scheduler:
def schedule(self) -> Tuple[List[SequenceGroupMetadata], SchedulerOutputs]:
scheduler_outputs = self._schedule()
# Create input data structures.
seq_group_metadata_list: List[SequenceGroupMetadata] = []
for seq_group in scheduler_outputs.scheduled_seq_groups:
seq_data: Dict[int, SequenceData] = {}
block_tables: Dict[int, List[int]] = {}
for seq in seq_group.get_seqs(status=SequenceStatus.RUNNING):
seq_id = seq.seq_id
seq_data[seq_id] = seq.data # одинокий SequenceData
block_tables[seq_id] = self.block_manager.get_block_table(seq) # Соответствует информации о последовательности из блока
seq_group_metadata = SequenceGroupMetadata(
request_id=seq_group.request_id,
is_prompt=scheduler_outputs.prompt_run,
seq_data=seq_data,
sampling_params=seq_group.sampling_params,
block_tables=block_tables,
lora_request=seq_group.lora_request,
prefix=seq_group.prefix,
)
seq_group_metadata_list.append(seq_group_metadata)
return seq_group_metadata_list, scheduler_outputs
SequenceOutput и Отношения SequenceGroupOutputiz аналогичны Sequence и Группа последовательности. SequenceOutput фактически записывает предыдущий входить token id И соответствующий вывод из token id。
class SequenceOutput:
def __init__(
self,
parent_seq_id: int,
output_token: int,
logprobs: Dict[int, float],
) -> None:
self.parent_seq_id = parent_seq_id
self.output_token = output_token
self.logprobs = logprobs
class SequenceGroupOutput:
def __init__(
self,
samples: List[SequenceOutput],
prompt_logprobs: Optional[PromptLogprobs],
) -> None:
self.samples = samples
self.prompt_logprobs = prompt_logprobs
SamplingParams содержит следующие параметры:
n
:Чтобы создатьизпоследовательностьизколичество,По умолчанию — 1.best_of
:从много少个последовательностьсередина选择最佳последовательность,должно быть больше, чем n, значение по умолчанию равно n。temperature
:Используется для контроля результатов сборкиизслучайность,Более низкие температуры дадут более детерминированные результаты.,Более высокие температуры дадут более случайные результаты.top_p
:Используется для фильтрации сгенерированной лексики.середина Вероятность ниже заданного порогаизсловарный запас,Контролируйте случайность.top_k
:До выбора k кандидаты Жетон, контроль разнообразия.presence_penalty
:Используется для контроля результатов сборкисередина特定словарный запасизчастота появления。frequency_penalty
:Используется для контроля результатов сборкисерединасловарный запасизчастотное распределение。repetition_penalty
:Используется для контроля результатов сборкисерединаизсловарный запасстепень повторения。use_beam_search
:лииспользоватьпоиск луча для генерациипоследовательность.length_penalty
:Используется для контроля результатов сборкиизраспределение длины。early_stopping
:ли В процессе генерациисередина Остановитесь раньше。stop
:Чтобы прекратить генерироватьизсловарный запассписок。stop_token_ids
:Чтобы прекратить генерироватьизсловарный запасизIDсписок。include_stop_str_in_output
:ли在输出результатсередина Включать停止字符串。ignore_eos
:В процессе генерациисерединали忽略结束符号。max_tokens
:生成последовательностьизмаксимальная длина。logprobs
:Используется для записи процесса генерациииз概率информация。prompt_logprobs
:Используется для записи процесса генерациииз概率информация,для конкретных подсказок.skip_special_tokens
:ли跳过特殊符号。spaces_between_special_tokens
:ли在特殊符号之间添加空格。Настройки этих параметров обычно зависят от конкретных требований и производительности модели. Вот некоторые общие рекомендации по настройке:
temperature
:нижеизтемпература(нравиться0.2)даст больше уверенностиизрезультат,И вышеизтемпература(нравиться0.8)будет производить больше случайныхизрезультат。ты можешьв соответствии с Отрегулируйте в соответствии с вашими потребностями.presence_penalty、frequency_penalty и repetition_penalty
:这些параметр可以Используется для контроля результатов сборкисерединаизсловарный запас分布истепень повторения。ты можешьв соответствии с Отрегулируйте в соответствии с вашими потребностями.use_beam_search
:Поиск луча часто используется для получения изображений более высокого качества.изрезультат,Но это может снизить скорость генерации。ты можешьв соответствии с Отрегулируйте в соответствии с вашими потребностями.length_penalty
:这个параметр可以Используется для контроля результатов сборкииздлина。вышеиззначения будут производиться дольшеизрезультат,而нижеиззначение приведет к более короткомуизрезультат。ты можешьв соответствии с Отрегулируйте в соответствии с вашими потребностями.early_stopping
:нравиться果您不希望生成过长изрезультат,Для этого параметра можно установить значение True.stop и stop_token_ids
:ты можешьиспользовать这些параметр来指定生成результатизконечное состояние。Вывод в основном используется для представления языковой модели (LLM) из сгенерированных результатов, включая следующие два модуля:
CompletionOutput
RequestOutput
Благодаря приведенному выше введению мы знаем request 可能Включатьмного个последовательность,CompletionOutput
используется для выражения request середина某个последовательностьизполный выводизданные,Чтосерединаподизindex
就表示该последовательность在 request расположение индекса серединаиз
class CompletionOutput:
def __init__(
self,
index: int, # Вывод результатов в запрос серединаиз index
text: str, # Создать из текста
token_ids: List[int], # Создать из текст соответствует из token ID список
cumulative_logprob: float,
logprobs: Optional[SampleLogprobs],
finish_reason: Optional[str] = None, # Причина завершения последовательности (SequenceStatus)
lora_request: Optional[LoRARequest] = None,
) -> None:
self.index = index
self.text = text
self.token_ids = token_ids
self.finish_reason = finish_reason
...
RequestOutput
则表示 request 所有последовательностьиз输出результат,возьми этоизинициализация函数можно увидеть它记录Понятно对应из request_id
。
class RequestOutput:
def __init__(
self,
request_id: str,
prompt: str,
prompt_token_ids: List[int],
prompt_logprobs: Optional[PromptLogprobs],
outputs: List[CompletionOutput],
finished: bool,
lora_request: Optional[LoRARequest] = None,
) -> None:
self.request_id = request_id
self.prompt = prompt
self.prompt_token_ids = prompt_token_ids
self.outputs = outputs
self.finished = finished
...
Давайте посмотримRequestOutputизfrom_seq_groupможно легко понятьCompletionOutput
и RequestOutput
是нравиться何использоватьиз Понятно。для простоты понимания,Код был удален,Но на конечный результат это не влияет:
class RequestOutput:
@classmethod
def from_seq_group(cls, seq_group: SequenceGroup) -> "RequestOutput":
# 1. Get the top-n sequences.
n = seq_group.sampling_params.n # Каждая последовательность возвращает количество сгенерированных последовательностей.
seqs = seq_group.get_seqs()
# в соответствии накопительный logprob значение, которое нужно выбрать перед n сгенерированная последовательность
sorting_key = lambda seq: seq.get_cumulative_logprob()
sorted_seqs = sorted(seqs, key=sorting_key, reverse=True)
top_n_seqs = sorted_seqs[:n]
# 2. Create the outputs.
outputs: List[CompletionOutput] = []
for seq in top_n_seqs:
logprobs = seq.output_logprobs
finshed_reason = SequenceStatus.get_finished_reason(seq.status)
output = CompletionOutput(seqs.index(seq), seq.output_text,
seq.get_output_token_ids(),
seq.get_cumulative_logprob(), logprobs,
finshed_reason)
outputs.append(output)
# Every sequence in the sequence group should have the same prompt.
prompt = seq_group.prompt
prompt_token_ids = seq_group.prompt_token_ids
prompt_logprobs = seq_group.prompt_logprobs
finished = seq_group.is_finished()
return cls(seq_group.request_id,
prompt,
prompt_token_ids,
prompt_logprobs,
outputs,
finished,
lora_request=seq_group.lora_request)
RequestOutput
передается черезизseq_group: SequenceGroup
После анализа получаемиз。В процессе разбора есть два основных этапа.:
CompletionOutput
список,и какRequestOutput
изинициализацияпараметр。