YunchaoYang / Blogs

blogs and notes, https://yunchaoyang.github.io/blogs/
0 stars 0 forks source link

Serve large language models #58

Open YunchaoYang opened 4 months ago

YunchaoYang commented 4 months ago

1. Ollama

1. use Ollama CLI:

ollama serve
ollama run llama2:7b, llama3, llama3:70b, mistral, dophin-phi, phi, neural-chat, codellama, llama2:13b, llama2:70b
ollama list
ollama show 
ollama pull
ollama cp
ollama rm 
ollama push

A list of available base models: https://github.com/ollama/ollama#model-library

2. use Modelfile to customize your model.

The format is similar to DockerFile. For example:

FROM llama2
# sets the temperature to 1 [higher is more creative, lower is more coherent]
PARAMETER temperature 1
# sets the context window size to 4096, this controls how many tokens the LLM can use as context to generate the next token
PARAMETER num_ctx 4096

# sets a custom system message to specify the behavior of the chat assistant
SYSTEM You are Mario from super mario bros, acting as an assistant.
  1. Save it as a file ./Modelfile
  2. ollama crate choose-a-model-name -f ./Modelfile
  3. ollama run choose-a-model-name
    1. start using this model.

More examples are available in the examples directory.

Ollama + Langchain for local serving RAG model:

Following this tutorial to build a local serving LLM with RAG for document retrieval:

pip install langchain pip install pypdf pip install chromadb pip install gradio pip install fastembed pip install

build a chatbox with gradio and ollama: https://bibek-poudel.medium.com/create-your-own-chatbot-with-llama2-ollama-and-gradio-5c60ecb1aad0

YunchaoYang commented 1 month ago

vLLM

The following is from

image

2. 整体核心模块

image

3. Sequence

image 如上图我们可以看到 vLLM 为输入的句子设计了很多子模块,这些模块的用处各不相同,但是有彼此之间有关系,下面分别详细介绍一下。

3.1 SequenceStatus

首先看到 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,
        ]

3.2 SequenceData

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

3.3 Sequence

Sequence 用于存储序列的数据、状态和块信息,且每个序列有唯一标识,即seq_id。注意看下面的代码:

数据其实是通过上面的 SequenceData 保存的 默认初始化状态,所有句子序列的状态都是 SequenceStatus.WAITING 所谓块信息,其实就是 vLLM 会在初始化阶段预留出一定数量的CPU 和 GPU 内存,一般是以 token 为单位的,例如在初始化的时候会使用值全为 0,大小为 (256, 128)的 prompt_ids做 warm up。每个序列会按照实际大小申请 block 来记录内存使用情况,即序列 token 数越多,属性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 # 状态
        ...

3.4 SequenceGroup

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
        ...

3.5 SequenceGroupMetadata

3.6 SequenceOutput 和 SequenceGroupOutput

4. SamplingParams

image

SamplingParams 包含以下参数:

n:要生成的序列的数量,默认为 1。 best_of:从多少个序列中选择最佳序列,需要大于 n,默认等于 n。 temperature:用于控制生成结果的随机性,较低的温度会使生成结果更确定性,较高的温度会使生成结果更随机。 top_p:用于过滤掉生成词汇表中概率低于给定阈值的词汇,控制随机性。 top_k:选择前 k 个候选 token,控制多样性。 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:您可以使用这些参数来指定生成结果的结束条件。

5. Output 模块

YunchaoYang commented 1 month ago

HuggingFace Transformers

YunchaoYang commented 1 month ago

Llama.cpp

YunchaoYang commented 1 month ago

Llamafile

YunchaoYang commented 1 month ago

Deepspeed

YunchaoYang commented 1 month ago

Huggingface TGI