PostgreSQL码农集散地

9个大问题,微调与提示词格式

9个问题 说清 微调数据集格式 和 微调后的提示词格式 关系

我想探讨一下“微调数据集的格式”和“微调后的提示词”的关系说清楚. 在微调了几次后, 发现这些问题可能是需要特别注意的.  否则可能带来的结果是:

  • 为什么微调后的模型不按我预想的来输出?

例如我下载了某中医相关数据集

  • https://huggingface.co/datasets/ticoAg/Chinese-medical-dialogue/tree/main/data

    下面用9个问题 说清 微调数据集格式 和 微调后的提示词格式 的关系

    • 1、微调数据集格式的选择 : chat, tools, completions 和 text
    • 2、为什么 微调数据集格式 和 微调后的提示词格式 要匹配?
    • 3、细说 text 格式
    • 4、text 格式里为什么要嵌入一些固定格式?
    • 5、text格式只能用一个eos_token吗?
    • 6、大模型管理平台 如何 嵌入 提示词模版
    • 7、很多大模型管理平台都支持模板, 例如mistral, openai, ollama , ollama如何进行嵌入
    • 8、解读ollama某个模型的 template
    • 9、如何使用 用户输入 和 template 构造出发给大模型的prompt?

1、微调格式的选择 : chat, tools, completions 和 text

以下是从huggingface下载的数据集, 我需要将其转换为`mlx-examples/lora.py`支持的微调jsonl数据格式.    
我知道lora.py微调时支持4种格式: chat, tools, completions 和 text.    
如果我要使用微调后的模型来进行completions行为的交互(用户输入提示input, 模型回复output), 那么微调的数据格式是不是也应该采用completions格式? 如果是这样的话, 应该如何转换?    
另一个问题: 在训练数据中的prompt和completion的末尾是不是都需要加eos_token? 解释一下为什么, 并且请在转换代码中考虑这个问题.    
数据如下:   
```  
[  
    {  
"instruction": "孩子出现扁桃体炎高烧不能吃什么",  
"input": "我家的孩子是男宝宝,9岁,一开始,说自己嗓子疼,发现,说话的时候声音有点沙哑,同时,一直都有点发烧,请问:孩子出现扁桃体炎高烧不能吃什么。",  
"output": "孩子扁桃体发炎建议宝妈及时带孩子去治疗,在饮食上首先蔬菜和水果比较清淡,是孩子治疗期间不错的选择,因为很多水果都有清热解毒的作用,而且富含维生素,可帮助提高免疫力,抵御疾病,比如点梨  
,梨性凉,具有退烧、润喉、化痰、止痛的作用,可减轻急性扁桃体炎咽喉不适的症状,日常饮食的话应该以清淡、易消化,好吞咽为主,比如像粥、面等都是非常不错的,同时还要注意尽量不要让孩子吃发物,比如像牛羊肉、狗肉以及海鲜等,都是可能加剧症状的,饮食对扁桃体炎的治疗也是很关键的,务必注意。"
,  
"history": null  
    },  
    {  
"instruction": "孩子出现扁桃体炎总发烧可以吃什么食物",  
"input": "我家女宝,今年1岁,一开始,说话时觉得嗓子疼,发现,说话的时候声音有点沙哑,另外,有点发烧也没精神,请问:孩子出现扁桃体炎总发烧可以吃什么食物。",  
"output": "在治疗儿童扁桃体炎的同时,饮食上也是需要引起足够重视的,一般是建议给孩子多吃一些水果和蔬菜的,水果和蔬菜都富含维生素及微量元素,而且相对清淡,所以对疾病的治疗是非常有帮助的,水果我们  
一般建议选择一些性平的,同时富含维生素的,比如像金橘、猕猴桃之类的,应选择一些比较好吞咽和消化的食物,像稀粥等都是不错的,不过还要注意像刺激辛辣的食物,以及一些发物在这一阶段都是不建议给孩子吃的,饮食对扁桃体炎的治疗也是很关键的,务必注意。"
,  
"history": null  
    },  
    {  
    ...  
    },  
    ...  
]  
```  

好的,针对你的问题,我将提供详细的解答和代码示例,帮助你将数据转换为 mlx-examples/lora.py 支持的 completions 格式,并解释 eos_token 的重要性。

1. 数据格式选择:completions

是的,如果你的目标是使用微调后的模型进行 completions 行为的交互(用户输入提示 input,模型回复 output),那么微调的数据格式应该采用 completions 格式。 completions 格式非常适合这种 prompt-completion 的场景。

2. 数据转换:Python 代码示例

以下 Python 代码可以将你提供的数据转换为 completions 格式的 JSONL 文件,并添加 eos_token。

import json  
import os  

defconvert_to_completions(data, eos_token="<|endoftext|>"):
"""  
    将数据转换为 completions 格式,并添加 eos_token。  

    Args:  
        data: 原始数据列表,每个元素是一个包含 "instruction", "input", "output" 的字典。  
        eos_token: end of sequence token.  

    Returns:  
        转换后的 completions 格式数据列表。  
    """

    completions_data = []  
for item in data:  
        prompt = item["instruction"] + "\n" + item["input"] + eos_token  #instruction和input拼接作为prompt  
        completion = item["output"] + eos_token  
        completions_data.append({"prompt": prompt, "completion": completion})  
return completions_data  


defsave_to_jsonl(data, filename):
"""  
    将数据保存到 JSONL 文件。  

    Args:  
        data: 要保存的数据列表。  
        filename: JSONL 文件名。  
    """

with open(filename, "w", encoding="utf-8") as f:  
for item in data:  
            json.dump(item, f, ensure_ascii=False)  
            f.write("\n")  


# 示例数据 (替换成你的实际数据)  
data = [  
    {  
"instruction": "孩子出现扁桃体炎高烧不能吃什么",  
"input": "我家的孩子是男宝宝,9岁,一开始,说自己嗓子疼,发现,说话的时候声音有点沙哑,同时,一直都有点发烧,请问:孩子出现扁桃体炎高烧不能吃什么。",  
"output": "孩子扁桃体发炎建议宝妈及时带孩子去治疗,在饮食上首先蔬菜和水果比较清淡,是孩子治疗期间不错的选择,因为很多水果都有清热解毒的作用,而且富含维生素,可帮助提高免疫力,抵御疾病,比如点梨\n,梨性凉,具有退烧、润喉、化痰、止痛的作用,可减轻急性扁桃体炎咽喉不适的症状,日常饮食的话应该以清淡、易消化,好吞咽为主,比如像粥、面等都是非常不错的,同时还要注意尽量不要让孩子吃发物,比如像牛羊肉、狗肉以及海鲜等,都是可能加剧症状的,饮食对扁桃体炎的治疗也是很关键的,务必注意。",  
"history": None
    },  
    {  
"instruction": "孩子出现扁桃体炎总发烧可以吃什么食物",  
"input": "我家女宝,今年1岁,一开始,说话时觉得嗓子疼,发现,说话的时候声音有点沙哑,另外,有点发烧也没精神,请问:孩子出现扁桃体炎总发烧可以吃什么食物。",  
"output": "在治疗儿童扁桃体炎的同时,饮食上也是需要引起足够重视的,一般是建议给孩子多吃一些水果和蔬菜的,水果和蔬菜都富含维生素及微量元素,而且相对清淡,所以对疾病的治疗是非常有帮助的,水果我们\n一般建议选择一些性平的,同时富含维生素的,比如像金橘、猕猴桃之类的,应选择一些比较好吞咽和消化的食物,像稀粥等都是不错的,不过还要注意像刺激辛辣的食物,以及一些发物在这一阶段都是不建议给孩子吃的,饮食对扁桃体炎的治疗也是很关键的,务必注意。",  
"history": None
    }  
]  

# 转换数据  
completions_data = convert_to_completions(data)  

# 保存到 JSONL 文件  
output_filename = "completions_data.jsonl"
save_to_jsonl(completions_data, output_filename)  

print(f"数据已转换为 completions 格式并保存到 {output_filename}")  

代码解释:

  1. convert_to_completions(data, eos_token) 函数:
  • 遍历原始数据列表。
  • 将 instruction 和 input 拼接起来作为 prompt。
  • 将 output 作为 completion。
  • 在 prompt 和 completion 的末尾都添加 eos_token。
  • 将 prompt 和 completion 组成字典,添加到 completions_data 列表中。
  • save_to_jsonl(data, filename) 函数:
    • 打开文件,以写入模式 ("w") 和 UTF-8 编码 (encoding="utf-8")。
    • 遍历 completions_data 列表。
    • 使用 json.dump() 将每个字典转换为 JSON 字符串,并写入文件。  ensure_ascii=False 确保可以正确处理中文。
    • 写入换行符 (\n),因为 JSONL 文件的每一行都必须是一个独立的 JSON 对象。
  • 主程序:
    • 定义示例数据(替换成你的实际数据)。
    • 调用 convert_to_completions() 函数转换数据。
    • 定义输出文件名。
    • 调用 save_to_jsonl() 函数保存数据到 JSONL 文件。

    3. eos_token 的重要性

    在训练数据中的 prompt 和 completion 的末尾都需要添加 eos_token,原因如下:

    • 告诉模型序列的结束位置:eos_token (End-of-Sequence token) 明确地告诉模型,prompt 和 completion 的内容到此结束。  这对于模型学习序列的边界非常重要。
    • 防止模型生成无限循环: 如果没有 eos_token,模型可能不知道何时停止生成文本,从而导致生成无限循环或不相关的文本。
    • 控制生成过程: 在推理时,模型会一直生成文本,直到遇到 eos_token 或达到最大长度限制。  通过在训练数据中包含 eos_token,可以使模型在适当的时候停止生成。
    • 区分 prompt 和 completion: 虽然 completions 格式已经区分了 prompt 和 completion 字段,但是 eos_token 进一步强化了这种区分,帮助模型更好地理解训练数据的结构。

    常用的 eos_token:

    • <|endoftext|>:GPT 系列模型常用的 eos_token。
    • </s>:一些其他的模型也使用这个 token。
    • 你可以根据你的模型和 tokenizer 的设置选择合适的 eos_token。  确保你使用的 eos_token 存在于你的模型的词汇表中。

    这篇文章的eos_token.py提供了从模型中获取eos_token的方法

    • 《手把手教你炼丹 | 使用医疗诊断公开数据集微调专业医生大模型 , 中医崛起》

    总结

    通过将你的数据转换为 completions 格式,并在 prompt 和 completion 的末尾添加 eos_token,你可以有效地微调 mlx-examples/lora.py,并获得用于 completions 行为的交互模型。 记得根据你的实际数据和模型选择合适的 eos_token。

    2、微调数据集格式 和 微调后的提示词格式 要匹配

    根据你提供的代码微调后的模型, 我在使用时, 输入是不是也应该是这样的:   
    ```  
    孩子出现扁桃体炎高烧不能吃什么.  
    我家的孩子是男宝宝,9岁,一开始,说自己嗓子疼,发现,说话的时候声音有点沙哑,同时,一直都有点发烧,请问:孩子出现扁桃体炎高烧不能吃什么。  
    ```  
    而不应该是这样的:  
    ```  
    孩子出现扁桃体炎高烧不能吃什么.  
    ```  
    或  
    ```  
    我家的孩子是男宝宝,9岁,一开始,说自己嗓子疼,发现,说话的时候声音有点沙哑,同时,一直都有点发烧,请问:孩子出现扁桃体炎高烧不能吃什么。  
    ```  

    是的,你的理解完全正确。

    在使用微调后的模型进行推理时,你的输入应该与训练时 prompt 的格式保持一致。 也就是说,你的输入应该包含 instruction 和 input 两部分,并且按照你在 convert_to_completions 函数中拼接的方式组合。

    所以,正确的输入格式应该是:

    孩子出现扁桃体炎高烧不能吃什么  
    我家的孩子是男宝宝,9岁,一开始,说自己嗓子疼,发现,说话的时候声音有点沙哑,同时,一直都有点发烧,请问:孩子出现扁桃体炎高烧不能吃什么。  

    原因解释:

    • 模型学习的是特定格式: 模型在微调过程中学习的是 instruction + input 到 output 的映射关系。 如果你在推理时只提供 instruction 或 input,模型可能无法正确理解你的意图,因为它没有见过这种格式的训练数据。
    • 保持一致性: 为了获得最佳的生成效果,推理时输入的格式应该与训练数据中的 prompt 格式尽可能地一致。 这有助于模型更好地理解你的输入,并生成更准确、更相关的回复。
    • convert_to_completions 函数的逻辑:  你提供的 convert_to_completions 函数将 instruction 和 input 拼接在一起作为 prompt。 因此,模型期望的输入也是这种拼接后的形式。

    总结:

    为了获得最佳的推理效果,请确保你的输入包含 instruction 和 input 两部分,并按照你在训练数据中使用的格式进行拼接。 这样,模型才能更好地理解你的意图,并生成高质量的回复。

    3、text 格式

    好的, 请你修改一下代码, 把我之前提供的数据转换为text格式的jsonl.  

    好的,这是将你提供的数据转换为 text 格式 JSONL 的代码。

    import json  

    defconvert_to_text(data, eos_token="<|endoftext|>"):
    """  
        将数据转换为 text 格式,并添加 eos_token。  

        Args:  
            data: 原始数据列表,每个元素是一个包含 "instruction", "input", "output" 的字典。  
            eos_token: end of sequence token.  

        Returns:  
            转换后的 text 格式数据列表。  
        """

        text_data = []  
    for item in data:  
            text = item["instruction"] + "\n" + item["input"] + "\n" + item["output"] + eos_token  
            text_data.append({"text": text})  
    return text_data  


    defsave_to_jsonl(data, filename):
    """  
        将数据保存到 JSONL 文件。  

        Args:  
            data: 要保存的数据列表。  
            filename: JSONL 文件名。  
        """

    with open(filename, "w", encoding="utf-8") as f:  
    for item in data:  
                json.dump(item, f, ensure_ascii=False)  
                f.write("\n")  


    # 示例数据 (替换成你的实际数据)  
    data = [  
        {  
    "instruction": "孩子出现扁桃体炎高烧不能吃什么",  
    "input": "我家的孩子是男宝宝,9岁,一开始,说自己嗓子疼,发现,说话的时候声音有点沙哑,同时,一直都有点发烧,请问:孩子出现扁桃体炎高烧不能吃什么。",  
    "output": "孩子扁桃体发炎建议宝妈及时带孩子去治疗,在饮食上首先蔬菜和水果比较清淡,是孩子治疗期间不错的选择,因为很多水果都有清热解毒的作用,而且富含维生素,可帮助提高免疫力,抵御疾病,比如点梨\n,梨性凉,具有退烧、润喉、化痰、止痛的作用,可减轻急性扁桃体炎咽喉不适的症状,日常饮食的话应该以清淡、易消化,好吞咽为主,比如像粥、面等都是非常不错的,同时还要注意尽量不要让孩子吃发物,比如像牛羊肉、狗肉以及海鲜等,都是可能加剧症状的,饮食对扁桃体炎的治疗也是很关键的,务必注意。",  
    "history": None
        },  
        {  
    "instruction": "孩子出现扁桃体炎总发烧可以吃什么食物",  
    "input": "我家女宝,今年1岁,一开始,说话时觉得嗓子疼,发现,说话的时候声音有点沙哑,另外,有点发烧也没精神,请问:孩子出现扁桃体炎总发烧可以吃什么食物。",  
    "output": "在治疗儿童扁桃体炎的同时,饮食上也是需要引起足够重视的,一般是建议给孩子多吃一些水果和蔬菜的,水果和蔬菜都富含维生素及微量元素,而且相对清淡,所以对疾病的治疗是非常有帮助的,水果我们\n一般建议选择一些性平的,同时富含维生素的,比如像金橘、猕猴桃之类的,应选择一些比较好吞咽和消化的食物,像稀粥等都是不错的,不过还要注意像刺激辛辣的食物,以及一些发物在这一阶段都是不建议给孩子吃的,饮食对扁桃体炎的治疗也是很关键的,务必注意。",  
    "history": None
        }  
    ]  

    # 转换数据  
    text_data = convert_to_text(data)  

    # 保存到 JSONL 文件  
    output_filename = "text_data.jsonl"
    save_to_jsonl(text_data, output_filename)  

    print(f"数据已转换为 text 格式并保存到 {output_filename}")  

    代码解释:

    1. convert_to_text(data, eos_token) 函数:
    • 遍历原始数据列表。
    • 将 instruction、input 和 output 拼接在一起,形成一个完整的文本字符串。
    • 在文本字符串的末尾添加 eos_token。
    • 将文本字符串存储在字典中,键为 "text",然后将字典添加到 text_data 列表中。
  • save_to_jsonl(data, filename) 函数:
    • 与之前的代码相同,用于将数据保存到 JSONL 文件。
  • 主程序:
    • 定义示例数据(替换成你的实际数据)。
    • 调用 convert_to_text() 函数转换数据。
    • 定义输出文件名。
    • 调用 save_to_jsonl() 函数保存数据到 JSONL 文件。

    关键区别:

    • 在 convert_to_text 函数中,我们将 instruction、input 和 output 拼接成一个完整的文本,并将其存储在 "text" 键下。 这符合 text 格式的要求,即每个数据项都包含一个 "text" 字段,其中包含要训练的完整文本。
    • text 格式通常用于训练语言模型,使其能够生成连贯的文本。

    使用 text 格式的注意事项:

    • 推理方式: 使用 text 格式微调的模型,通常用于生成完整的文本序列。 你可以提供一个起始文本,然后让模型生成后续的文本。
    • 没有明确的 prompt 和 completion: 与 completions 格式不同,text 格式没有明确的 prompt 和 completion 字段。 模型学习的是整个文本序列的模式。
    • 适用场景:text 格式适用于需要生成完整文本的场景,例如文章生成、故事创作等。

    希望这个代码能够帮助你将数据转换为 text 格式。 请根据你的实际需求选择合适的数据格式。

    4、text 格式里为什么要嵌入一些固定格式

    我看很多text格式的jsonl里面会包含一些固定格式, 例如:   
    ```  
    "text" : "[instruction]内容1[/instruction][input]内容2[/input]\n内容3\n<eos_token>"
    ```  
    如果是这样的微调数据, 我在使用时, 输入是不是也应该拼写为"[instruction]内容1[/instruction][input]内容2[/input]\n", 才能让模型正确输出内容3.  

    是的,你的理解完全正确!

    如果你的 text 格式的 JSONL 数据包含特定的标签或格式,例如 [instruction]...[/instruction][input]...[/input],那么在推理时,你也必须按照相同的格式构建你的输入,才能让模型正确理解并生成期望的输出。

    原因解释:

    1. 模型学习的是数据分布: 模型在训练过程中学习的是训练数据的分布和模式。 如果训练数据包含特定的标签和格式,模型就会将这些标签和格式视为文本的一部分,并学习它们与输出之间的关系。
    2. 保持输入格式一致性: 为了让模型能够正确理解你的输入,并生成与训练数据相似的输出,你必须确保推理时的输入格式与训练数据中的格式尽可能地一致。 这包括标签、分隔符、换行符等。
    3. 模型依赖于标签: 在你提供的例子中,模型可能已经学习到 [instruction] 和 [input] 标签用于分隔不同的信息,并且 \n 用于分隔 input 和 output。 如果你在推理时省略这些标签或分隔符,模型可能无法正确识别输入的不同部分,从而导致生成不准确或不相关的输出。

    推理时的正确输入格式:

    根据你提供的例子,正确的推理输入(提示词/prompt)格式应该是:

    [instruction]内容1[/instruction][input]内容2[/input]\n  

    其中:

    • 内容1 是你的 instruction。
    • 内容2 是你的 input。
    • \n 是换行符,用于分隔 input 和模型将要生成的 output。

    总结:

    如果你的 text 格式的 JSONL 数据包含特定的标签或格式,请务必在推理时保持输入格式的一致性。 否则,模型可能无法正确理解你的输入,并生成不符合预期的输出。 确保你的输入包含所有必要的标签、分隔符和换行符,并且按照与训练数据相同的顺序排列。

    5、text格式只能用一个eos_token

    在text格式中, 是不是只能包含1个eos_token, 那就是文本的结尾.  

    是的,你的理解是正确的。 在 text 格式中,通常只包含一个 eos_token,并且它位于文本的结尾,表示文本序列的结束。

    原因解释:

    • text 格式的定义:text 格式旨在训练模型生成完整的、连贯的文本序列。 因此,整个文本被视为一个单一的序列,而不是像 completions 格式那样分为 prompt 和 completion 两部分。
    • eos_token 的作用:eos_token 的作用是告诉模型文本序列的结束位置。 由于 text 格式只有一个文本序列,因此只需要一个 eos_token 来标记序列的结尾。
    • 多个 eos_token 的含义: 如果在 text 格式中包含多个 eos_token,模型可能会将每个 eos_token 视为一个序列的结束,从而导致生成不完整或不连贯的文本。

    总结:

    在 text 格式中,应该只包含一个 eos_token,并且它位于文本的结尾,表示整个文本序列的结束。 这样可以确保模型正确地学习文本的模式,并生成完整的、连贯的文本。

    6、大模型管理平台 嵌入 提示词模版

    在ollama的modelfile中可以配置template, 这里是不是和提示词的结构有关? 例如前面提到的`[instruction]内容1[/instruction][input]内容2[/input]\n` 是不是可以在template中进行配置, 让用户的输入更加简化?  

    是的,你的理解非常正确! Ollama 的 Modelfile 中的 TEMPLATE 指令正是用于定义提示词的结构,它可以让你在一定程度上简化用户的输入,并让模型更好地理解你的意图。

    TEMPLATE 的作用:

    TEMPLATE 指令允许你定义一个模板,用于将用户的输入包装成模型期望的格式。 这个模板可以包含固定的文本、占位符和特殊字符,用于构建完整的提示词。

    与提示词结构的关系:

    TEMPLATE 指令与提示词的结构密切相关。 如果你的模型是在具有特定结构的提示词上进行训练的(例如 [instruction]...[/instruction][input]...[/input]\n),那么你可以在 TEMPLATE 中定义这个结构,并将用户的输入插入到相应的占位符中。

    简化用户输入:

    通过使用 TEMPLATE,你可以简化用户的输入,让他们只需要提供必要的信息,而不需要手动构建完整的提示词。 例如,如果你的提示词结构是 [instruction]...[/instruction][input]...[/input]\n,你可以在 TEMPLATE 中定义这个结构,并使用占位符来表示 instruction 和 input:

    TEMPLATE """[instruction]{{instruction}}[/instruction][input]{{input}}[/input]\n"""

    然后,用户只需要提供 instruction 和 input 的值,Ollama 会自动将它们插入到模板中,构建完整的提示词。

    示例:

    假设你的模型是在以下格式的提示词上进行训练的:

    [instruction]请根据以下信息回答问题:[/instruction][input]{问题内容}[/input]\n  

    你可以在 Modelfile 中配置 TEMPLATE 如下:

    FROM ...  
    ...  
    TEMPLATE """[instruction]请根据以下信息回答问题:[/instruction][input]{{问题}}[/input]\n"""
    ...  

    然后,用户在使用模型时,只需要提供问题内容:

    ollama run mymodel "什么是深度学习?"

    Ollama 会自动将问题内容插入到 TEMPLATE 中,构建完整的提示词:

    [instruction]请根据以下信息回答问题:[/instruction][input]什么是深度学习?[/input]\n  

    并将这个提示词发送给模型。

    总结:

    Ollama 的 TEMPLATE 指令是一个强大的工具,可以让你定义提示词的结构,简化用户的输入,并提高模型的性能。 通过合理地配置 TEMPLATE,你可以让模型更好地理解你的意图,并生成更准确、更相关的回复。

    注意事项:

    • 确保 TEMPLATE 中定义的结构与模型训练时使用的提示词结构一致。
    • 使用占位符(例如 {{instruction}}、{{input}})来表示用户需要提供的值。
    • 在运行 Ollama 时,使用 ollama run <model_name> "<user_input>" 的格式来提供输入。
    • 如果你的 TEMPLATE 中包含多个占位符,你需要以某种方式将用户的输入映射到这些占位符。 这可以通过 Ollama 的 API 或其他工具来实现。

    7、很多大模型管理平台都支持模板, 例如mistral, openai, ollama

    理论上来说支持提示词模版应该是大模型的管理平台的责任对吧?   

    是的,你的理解是正确的。 理论上来说,支持提示词模板(Prompt Templates)应该是大模型管理平台的责任。

    原因解释:

    1. 解耦模型和应用: 提示词模板是一种将模型与具体应用解耦的方式。 通过将提示词的结构和逻辑从模型本身分离出来,可以更容易地在不同的应用场景中使用同一个模型,而无需修改模型本身。
    2. 简化应用开发: 提示词模板可以简化应用开发过程。 开发人员只需要关注业务逻辑和用户输入,而无需手动构建复杂的提示词。 这可以提高开发效率,并降低出错的风险。
    3. 提高可维护性: 提示词模板可以提高应用的可维护性。 如果需要修改提示词的结构或逻辑,只需要修改模板,而无需修改应用代码。 这可以减少维护成本,并提高应用的灵活性。
    4. 统一管理和版本控制: 大模型管理平台可以提供统一的界面来管理和版本控制提示词模板。 这可以确保所有应用都使用最新的提示词,并方便进行回滚和审计。
    5. 安全性: 集中管理提示词模板可以提高安全性。 平台可以对提示词进行审查和过滤,防止恶意用户利用提示词进行攻击。

    大模型管理平台的角色:

    大模型管理平台应该提供以下功能来支持提示词模板:

    • 模板定义: 允许用户定义提示词模板,包括固定文本、占位符和特殊字符。
    • 模板存储: 提供安全的存储机制来存储提示词模板。
    • 版本控制: 支持对提示词模板进行版本控制,方便进行回滚和审计。
    • 模板管理: 提供统一的界面来管理提示词模板,包括创建、修改、删除和搜索。
    • 模板应用: 允许应用使用提示词模板,并将用户输入插入到相应的占位符中。
    • 安全性: 对提示词进行审查和过滤,防止恶意用户利用提示词进行攻击。

    Ollama 的 TEMPLATE 指令:

    Ollama 的 TEMPLATE 指令可以看作是一种简化版的提示词模板管理机制。 它允许你在 Modelfile 中定义一个简单的模板,并将用户的输入插入到模板中。 虽然 Ollama 的 TEMPLATE 指令功能有限,但它仍然可以帮助你简化用户的输入,并提高模型的性能。

    总结:

    虽然 Ollama 提供了 TEMPLATE 指令,但更完善的提示词模板管理应该由大模型管理平台来负责。 大模型管理平台可以提供更强大的功能和更高的灵活性,从而更好地支持各种应用场景。

    例如这几篇文章都提到mistral支持的text格式如下, 其实在提示:

    • 《[转载] Part3: 准备数据集》
    • 《AI大模型+全文检索+tf-idf+向量数据库+我的文章 系列之5 - 在 Apple Silicon Mac 上微调(fine-tuning)大型语言模型(LLM) 并发布GGUF》
    Mistral 7B 的训练数据应采用[JSONL 格式](https://jsonlines.org/),每行格式如下:JSONL 格式的单行训练数据。    
    ```    
    {"text":"<s>[INST] Instruction[/INST] Model answer</s>[INST] Follow-up instruction[/INST]"}    
    ```    

    ollama管理的mistral模型的template:

    {{- if .Messages }}  
    {{- range $index, $_ := .Messages }}  
    {{- if eq .Role "user" }}  
    {{- if and (eq (len (slice $.Messages $index)) 1) $.Tools }}[AVAILABLE_TOOLS] {{ $.Tools }}[/AVAILABLE_TOOLS]  
    {{- end }}[INST] {{ if and $.System (eq (len (slice $.Messages $index)) 1) }}{{ $.System }}  

    {{ end }}{{ .Content }}[/INST]  
    {{- elseif eq .Role "assistant" }}  
    {{- if .Content }} {{ .Content }}  
    {{- elseif .ToolCalls }}[TOOL_CALLS] [  
    {{- range .ToolCalls }}{"name": "{{ .Function.Name }}", "arguments": {{ .Function.Arguments }}}  
    {{- end }}]  
    {{- end }}</s>  
    {{- elseif eq .Role "tool" }}[TOOL_RESULTS] {"content": {{ .Content }}} [/TOOL_RESULTS]  
    {{- end }}  
    {{- end }}  
    {{- else }}[INST] {{ if .System }}{{ .System }}  

    {{ end }}{{ .Prompt }}[/INST]  
    {{- end }} {{ .Response }}  
    {{- if .Response }}</s>  
    {{- end }}  

    8、解读ollama某个模型的 template

    解读一下这个ollama的template:     
    ```  
    {{- if .Messages }}    
    {{- range $index, $_ := .Messages }}    
    {{- if eq .Role "user" }}    
    {{- if and (eq (len (slice $.Messages $index)) 1) $.Tools }}[AVAILABLE_TOOLS] {{ $.Tools }}[/AVAILABLE_TOOLS]    
    {{- end }}[INST] {{ if and $.System (eq (len (slice $.Messages $index)) 1) }}{{ $.System }}    

    {{ end }}{{ .Content }}[/INST]    
    {{- elseif eq .Role "assistant" }}    
    {{- if .Content }} {{ .Content }}    
    {{- elseif .ToolCalls }}[TOOL_CALLS] [    
    {{- range .ToolCalls }}{"name": "{{ .Function.Name }}", "arguments": {{ .Function.Arguments }}}    
    {{- end }}]    
    {{- end }}</s>    
    {{- elseif eq .Role "tool" }}[TOOL_RESULTS] {"content": {{ .Content }}} [/TOOL_RESULTS]    
    {{- end }}    
    {{- end }}    
    {{- else }}[INST] {{ if .System }}{{ .System }}    

    {{ end }}{{ .Prompt }}[/INST]    
    {{- end }} {{ .Response }}    
    {{- if .Response }}</s>    
    {{- end }}    
    ```  

    这个 Ollama 模板是一个比较复杂的模板,主要用于处理对话场景,特别是涉及到工具调用(Tool Calls)的场景。 让我们逐步解读这个模板:

    整体结构:

    这个模板使用了 Go 模板语法,其中:

    • {{ ... }} 表示模板指令或变量。
    • {{- ... }} 表示去除指令或变量周围的空白字符。
    • . 表示当前上下文。
    • .Messages 表示消息列表(对话历史)。
    • .System 表示系统提示词。
    • .Prompt 表示用户输入的提示词。
    • .Response 表示模型的回复。
    • .Tools 表示可用的工具列表。
    • .ToolCalls 表示模型发起的工具调用。
    • .Content 表示消息的内容。
    • .Role 表示消息的角色(user, assistant, tool)。

    主要逻辑分支:

    模板主要分为两个大的逻辑分支:

    1. {{- if .Messages }}:处理对话历史
    • {{- if eq .Role "user" }}:处理用户消息
    • {{- else if eq .Role "assistant" }}:处理助手消息
    • {{- else if eq .Role "tool" }}:处理工具消息
    • {{- end }}:结束角色判断
    • {{- if and (eq (len (slice $.Messages $index)) 1) $.Tools }}[AVAILABLE_TOOLS] {{ $.Tools }}[/AVAILABLE_TOOLS]{{- end }}:如果这是第一条消息,并且存在可用的工具(.Tools),则输出可用的工具列表。
    • [INST] {{ if and $.System (eq (len (slice $.Messages $index)) 1) }}{{ $.System }} {{ end }}{{ .Content }}[/INST]:输出用户消息的内容,并用 [INST] 和 [/INST] 标签包裹。 如果这是第一条消息,并且存在系统提示词(.System),则输出系统提示词。
    • {{- if .Content }} {{ .Content }}{{- else if .ToolCalls }}[TOOL_CALLS] [{{- range .ToolCalls }}{"name": "{{ .Function.Name }}", "arguments": {{ .Function.Arguments }}}}{{- end }}]{{- end }}</s>:如果助手消息包含内容(.Content),则输出内容。 否则,如果助手消息包含工具调用(.ToolCalls),则输出工具调用信息,并用 [TOOL_CALLS] 标签包裹。 最后,输出 </s> 标签表示消息结束。
    • [TOOL_RESULTS] {"content": {{ .Content }}} [/TOOL_RESULTS]:输出工具的执行结果,并用 [TOOL_RESULTS] 标签包裹。
    • 如果存在对话历史(.Messages 不为空),则进入这个分支。
    • 使用 {{- range $index, $_ := .Messages }} 遍历对话历史中的每一条消息。
    • 根据消息的角色(.Role)进行不同的处理:
    • {{- end }}:结束消息遍历
  • {{- else }}:处理单轮对话
    • 如果不存在对话历史(.Messages 为空),则进入这个分支。
    • [INST] {{ if .System }}{{ .System }} {{ end }}{{ .Prompt }}[/INST]:输出用户输入的提示词(.Prompt),并用 [INST] 和 [/INST] 标签包裹。 如果存在系统提示词(.System),则输出系统提示词。
    • {{- end }}:结束对话历史判断

    通用部分:

    • {{ .Response }}:输出模型的回复(无论是否存在对话历史)。
    • {{- if .Response }}</s>{{- end }}:如果存在模型的回复,则输出 </s> 标签表示消息结束。

    标签解释:

    • [INST] 和 [/INST]:用于包裹用户输入的指令或提示词。
    • [AVAILABLE_TOOLS] 和 [/AVAILABLE_TOOLS]:用于包裹可用的工具列表。
    • [TOOL_CALLS] 和 [/TOOL_CALLS]:用于包裹模型发起的工具调用信息。
    • [TOOL_RESULTS] 和 [/TOOL_RESULTS]:用于包裹工具的执行结果。
    • </s>:表示消息结束。

    总结:

    这个模板主要用于处理对话场景,特别是涉及到工具调用的场景。 它根据消息的角色和内容,输出不同的文本,并使用特定的标签来包裹不同的信息。 通过使用这个模板,可以使模型更好地理解对话历史和工具调用的信息,从而生成更准确、更相关的回复。

    使用场景:

    这个模板适用于以下场景:

    • 对话系统: 用于构建对话机器人,可以处理多轮对话和上下文信息。
    • 工具调用: 用于支持模型调用外部工具,例如搜索引擎、计算器、API 等。
    • 智能助手: 用于构建智能助手,可以根据用户的指令执行各种任务。

    简化用户输入:

    这个模板本身并没有直接简化用户的输入,而是更多地关注如何将用户的输入和对话历史格式化成模型期望的格式。 要简化用户的输入,你需要在应用层面上进行处理,例如提供友好的用户界面,或者使用自然语言理解技术来解析用户的输入。 然后,将解析后的结果传递给 Ollama,Ollama 会根据 TEMPLATE 将其格式化成模型期望的格式。

    希望这个解读能够帮助你理解这个 Ollama 模板!

    9、使用用户输入和template构造出发给大模型的prompt

    如果用户输入“你好”, 请从这个template中构造出发给模型的消息.   

    好的,我们来根据你提供的 Ollama 模板,构造用户输入 "你好" 时发给模型的消息。 假设我们是第一次对话,没有对话历史,也没有系统提示词和工具。

    1. 确定上下文:

    • .Messages 为空 (没有对话历史)
    • .System 为空 (没有系统提示词)
    • .Prompt 为 "你好"
    • .Response 为空 (模型尚未回复)
    • .Tools 为空 (没有可用工具)
    • .ToolCalls 为空 (没有工具调用)

    2. 应用模板:

    由于 .Messages 为空,模板会进入 {{- else }} 分支:

    [INST] {{ if .System }}{{ .System }}    

    {{ end }}{{ .Prompt }}[/INST] {{ .Response }}  
    {{- if .Response }}</s>  
    {{- end }}  

    因为 .System 为空,所以 {{ if .System }}{{ .System }} {{ end }} 不会输出任何内容。

    .Prompt 的值为 "你好",所以 {{ .Prompt }} 会输出 "你好"。

    .Response 为空,所以 {{ .Response }} 不会输出任何内容。

    {{- if .Response }}</s>{{- end }} 也不会输出任何内容,因为 .Response 为空。

    3. 构造的消息:

    将上述步骤的结果拼接起来,最终发给模型的消息是:

    [INST] 你好[/INST]  

    解释:

    • [INST] 和 [/INST] 标签包裹了用户的输入 "你好"。
    • 由于没有对话历史、系统提示词、工具或工具调用,所以消息非常简洁。

    总结:

    当用户输入 "你好" 时,在没有对话历史、系统提示词和工具的情况下,根据你提供的 Ollama 模板,发给模型的消息是 [INST] 你好[/INST]。 这个消息告诉模型用户说 "你好",模型可以根据这个信息生成回复。

    之前不理解为什么mistral微调的jsonl要那种格式, 实际上没有下一条指令的话到结束符位置就可以了. 如下

    • https://github.com/apeatling/simple-guide-to-mlx-finetuning/blob/trunk/train-example.jsonl
    # 训练格式
    {"text":"<s>[INST] What is the purpose of ... 用户输入n in WordPress?[/INST] Custom post types in WordPress extend the ... 模型返回 ontent creators.</s>"}

    # 使用微调后的模型: Ollama使用template拼出的提示格式:
    [INST] 你好[/INST]

    # 其他
    <s> </s> 是bos_token和eos_token
    begin of token
    end of token

     以上内容基于DeepSeek及诸多AI生成, 轻微人工调整, 感谢杭州深度求索人工智能等公司 

     AI 生成的内容请自行辨别正确性, 当然也多了些许踩坑的乐趣, 毕竟冒险是每个男人的天性. 

    参考

    https://github.com/ml-explore/mlx-examples/blob/main/llms/mlx_lm/LORA.md#data