赞
踩
- {
- "top_k": 5,
- "temperature": 0.8,
- "num_beams": 1,
- "top_p": 0.75,
- "repetition_penalty": 1.5,
- "max_tokens": 30000,
- "message": [
- {
- "content": "你好",
- "role": "user"
- }
- ]
- }
1.Temperature
用于调整随机从生成模型中抽样的程度,因此每次点击“生成”时,相同的提示可能会产生不同的输出。温度为 0 将始终产生相同的输出。温度越高随机性越大!
2.Top p
动态设置tokens候选列表的大小。 将可能性之和不超过特定值的top tokens列入候选名单。
Top p 通常设置为较高的值(如 0.75),目的是限制可能被采样的低概率 token 的长度。
3.Top k
允许其他高分tokens有机会被选中。 这种采样引入的随机性有助于在很多情况下生成的质量。 top-k 参数设置为 3意味着选择前三个tokens。
将如果 k 和 p 都启用,则 p 在 k 之后起作用。
在大模型训练好之后,如何对训练好的模型进行解码(decode)是一个火热的研究话题。
在自然语言任务中,我们通常使用一个预训练的大模型(比如GPT)来根据给定的输入文本(比如一个开头或一个问题)生成输出文本(比如一个答案或一个结尾)。为了生成输出文本,我们需要让模型逐个预测每个 token ,直到达到一个终止条件(如一个标点符号或一个最大长度)。在每一步,模型会给出一个概率分布,表示它对下一个单词的预测。例如,如果输入的文本是“我最喜欢的”,那么模型可能会给出下面的概率分布:
那么,我们应该如何从这个概率分布中选择下一个单词呢?以下是几种常用的方法:
以上方法都有各自的问题,而 top-k 采样和 top-p 采样是介于贪心解码和随机采样之间的方法,也是目前大模型解码策略中常用的方法。
在上面的例子中,如果使用贪心策略,那么选择的 token 必然就是“女孩”。
贪心解码是一种合理的策略,但也有一些缺点。例如,输出可能会陷入重复循环。想想智能手机自动建议中的建议。当你不断地选择建议最高的单词时,它可能会变成重复的句子。
Top-k 采样是对前面“贪心策略”的优化,它从排名前 k 的 token 中进行抽样,允许其他分数或概率较高的token 也有机会被选中。在很多情况下,这种抽样带来的随机性有助于提高生成质量。
top-k 采样的思路是,在每一步,只从概率最高的 k 个单词中进行随机采样,而不考虑其他低概率的单词。例如,如果 k=2,那么我们只从女孩、鞋子中选择一个单词,而不考虑大象、西瓜等其他单词。这样可以避免采样到一些不合适或不相关的单词,同时也可以保留一些有趣或有创意的单词。
下面是 top-k 采样的例子:
通过调整 k 的大小,即可控制采样列表的大小。“贪心策略”其实就是 k = 1的 top-k 采样。
下面是top-k 的代码实现:
- import torch
- from labml_nn.sampling import Sampler
-
- # Top-k Sampler
- class TopKSampler(Sampler):
- # k is the number of tokens to pick
- # sampler is the sampler to use for the top-k tokens
- # sampler can be any sampler that takes a logits tensor as input and returns a token tensor; e.g. `TemperatureSampler`.
- def __init__(self, k: int, sampler: Sampler):
- self.k = k
- self.sampler = sampler
-
- # Sample from logits
- def __call__(self, logits: torch.Tensor):
- # New logits filled with −∞; i.e. zero probability
- zeros = logits.new_ones(logits.shape) * float('-inf')
- # Pick the largest k logits and their indices
- values, indices = torch.topk(logits, self.k, dim=-1)
- # Set the values of the top-k selected indices to actual logits.
- # Logits of other tokens remain −∞
- zeros.scatter_(-1, indices, values)
- # Sample from the top-k logits with the specified sampler.
- return self.sampler(zeros)
假设我们要补全 “The sun rises in the…(太阳升起在…)” 这个句子。那么,在没有 top-k sampling 的情况下,模型会将词汇表中的每个 token 都视为可以放置在序列之后的可能结果。然后,就会有一定的概率写出一些荒谬的内容,比如“The sun rises in the refrigerator.(太阳升起在冰箱里)”。通过进行 top-k sampling ,模型会筛选这些真正糟糕的选择,只考虑前 k 个最佳 token。通过截断大部分糟糕的 token,我们会失去一些内容多样性,但是内容的质量会大幅提高。
总结一下,top-k 有以下有点:
但是 top-k 也有一些缺点,比如:
因此,我们通常会考虑 top-k 和其它策略结合,比如 top-p。
top-k 有一个缺陷,那就是“k 值取多少是最优的?”非常难确定。于是出现了动态设置 token 候选列表大小策略——即核采样(Nucleus Sampling)。
top-p 采样的思路是,在每一步,只从累积概率超过某个阈值 p 的最小单词集合中进行随机采样,而不考虑其他低概率的单词。这种方法也被称为核采样(nucleus sampling),因为它只关注概率分布的核心部分,而忽略了尾部部分。例如,如果 p=0.9,那么我们只从累积概率达到 0.9 的最小单词集合中选择一个单词,而不考虑其他累积概率小于 0.9 的单词。这样可以避免采样到一些不合适或不相关的单词,同时也可以保留一些有趣或有创意的单词。
下图展示了 top-p 值为 0.9 的 Top-p 采样效果:
top-p 值通常设置为比较高的值(如0.75),目的是限制低概率 token 的长尾。我们可以同时使用 top-k 和 top-p。如果 k 和 p 同时启用,则 p 在 k 之后起作用。
下面是 top-p 代码实现的例子:
- import torch
- from torch import nn
-
- from labml_nn.sampling import Sampler
-
-
- class NucleusSampler(Sampler):
- """
- ## Nucleus Sampler
- """
- def __init__(self, p: float, sampler: Sampler):
- """
- :param p: is the sum of probabilities of tokens to pick $p$
- :param sampler: is the sampler to use for the selected tokens
- """
- self.p = p
- self.sampler = sampler
- # Softmax to compute $P(x_i | x_{1:i-1})$ from the logits
- self.softmax = nn.Softmax(dim=-1)
-
- def __call__(self, logits: torch.Tensor):
- """
- Sample from logits with Nucleus Sampling
- """
-
- # Get probabilities $P(x_i | x_{1:i-1})$
- probs = self.softmax(logits)
-
- # Sort probabilities in descending order
- sorted_probs, indices = torch.sort(probs, dim=-1, descending=True)
-
- # Get the cumulative sum of probabilities in the sorted order
- cum_sum_probs = torch.cumsum(sorted_probs, dim=-1)
-
- # Find the cumulative sums less than $p$.
- nucleus = cum_sum_probs < self.p
-
- # Prepend ones so that we add one token after the minimum number
- # of tokens with cumulative probability less that $p$.
- nucleus = torch.cat([nucleus.new_ones(nucleus.shape[:-1] + (1,)), nucleus[..., :-1]], dim=-1)
-
- # Get log probabilities and mask out the non-nucleus
- sorted_log_probs = torch.log(sorted_probs)
- sorted_log_probs[~nucleus] = float('-inf')
-
- # Sample from the sampler
- sampled_sorted_indexes = self.sampler(sorted_log_probs)
-
- # Get the actual indexes
- res = indices.gather(-1, sampled_sorted_indexes.unsqueeze(-1))
-
- #
- return res.squeeze(-1)
top-p sampling 与 top-k sampling 非常相似,只是它使用可能性分数(likelihood scores)而不是 token 排名(token ranks)来决定应保留哪些 token。更具体地说,它只考虑那些可能性分数超过阈值 p 的排名靠前的 token,而将其余的 token 丢弃。
与 top-k sampling 相比,top-p sampling 的优势在有许多较差或平庸的序列后续词时就会显现出来。 例如,假设下一个 token 只有几个比较好的选择,却有几十个只是隐约挨边的选择。如果我们使用 k=25 的 top-k sampling(译者注:k 代表的是保留的 token 数量) ,我们将考虑许多较差的 token 选择。相比之下,如果我们使用 top-p sampling 来过滤掉概率分布中最底层的 10%(译者注:将 token 的可能性概率从大到小排序,只保留从概率最大开始、累积概率之和达到 90%为止的 tokens),那么我们可能只需要考虑那些分数较高的 token,同时过滤掉其他的 token。
在实际应用中,与 top-k sampling 相比,top-p sampling 往往能够获得更好的效果。 它能够更加适应输入的上下文,并提供更灵活的筛选。因此,总的来说,top-p 和 top-k sampling 都可以在非零的 temperature 下使用,以较低的质量成本获取输出内容的多样性,但 top-p sampling 通常效果更好。
Temperature 采样受统计热力学的启发,高温意味着更可能遇到低能态。在概率模型中,logits 扮演着能量的角色,我们可以通过将 logits 除以温度来实现温度采样,然后将其输入 Softmax 并获得采样概率。
越低的温度使模型对其首选越有信心,而高于1的温度会降低信心。0温度相当于 argmax 似然,而无限温度相当于均匀采样。
Temperature 采样中的温度与玻尔兹曼分布有关,其公式如下所示:
有机器学习背景的朋友第一眼看到上面的公式会觉得似曾相识。没错上面的公式跟 Softmax 函数 :
很相似,本质上就是在 Softmax 函数上添加了温度(T)这个参数。Logits 根据我们的温度值进行缩放,然后传递到 Softmax 函数以计算新的概率分布。
上面“我喜欢漂亮的___”这个例子中,初始温度T = 1,我们直观看一下 T取不同值的情况下,概率会发生什么变化:
通过上图我们可以清晰地看到,随着温度的降低,模型愈来愈越倾向选择”女孩“;另一方面,随着温度的升高,分布变得越来越均匀。当 T =50
时,选择”西瓜“的概率已经与选择”女孩“的概率相差无几了。
通常来说,温度与模型的“创造力”有关。但事实并非如此。温度只是调整单词的概率分布。其最终的宏观效果是,在较低的温度下,我们的模型更具确定性,而在较高的温度下,则不那么确定。
下面是 Temperature 采样的代码实现:
- import torch
- from torch.distributions import Categorical
-
- from labml_nn.sampling import Sampler
-
-
- class TemperatureSampler(Sampler):
- """
- ## Sampler with Temperature
- """
- def __init__(self, temperature: float = 1.0):
- """
- :param temperature: is the temperature to sample with
- """
- self.temperature = temperature
-
- def __call__(self, logits: torch.Tensor):
- """
- Sample from logits
- """
-
- # Create a categorical distribution with temperature adjusted logits
- dist = Categorical(logits=logits / self.temperature)
-
- # Sample
- return dist.sample()
通常我们是将 top-k、top-p、Temperature 联合起来使用。使用的先后顺序是 top-k->top-p->Temperature。
我们还是以前面的例子为例。
首先我们设置 top-k = 3,表示保留概率最高的3个 token。这样就会保留女孩、鞋子、大象这3个 token。
接下来,我们可以使用 top-p 的方法,保留概率的累计和达到 0.8 的单词,也就是选取女孩和鞋子这两个 token。接着我们使用 Temperature = 0.7 进行归一化,变成:
接着,我们可以从上述分布中进行随机采样,选取一个单词作为最终的生成结果。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。