OpenAI Completions 解码参数详解

Completions 是指GPT模型接收一个输入字符串,然后自动生成一个完成的输出字符串。这种功能通常用于生成文本,例如自动生成文章、电子邮件回复或聊天记录等。用户可以指定输入字符串的前缀,然后让模型生成可能的后缀。这个过程是自动的,不需要实时的交互,因此 completions 功能通常被认为是一种非交互式的应用。

Doc:https://platform.openai.com/docs/api-reference/completions/create

1. 接口使用方法

POST https://api.openai.com/v1/completions

我们需要向该接口发送 POST 请求,并传递该任务相关参数即可完成 Completions 任务的调用。

import os
import openai
import requests
import json
import re


def get_completions(prompt, echo=False, n=1, top_p=0.9, max_len=200):

    request_url = 'https://api.openai.com/v1/completions'

    headers = {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer ' + open('openai_api_key').read(),
    }

    # data 需要序列化为 json 字符串
    data = json.dumps({
        "model": "text-davinci-003",
        # 是否在生成的文本包含输入的 prompt
        'echo': echo,
        'prompt': prompt,
        # 生成的最大token数量
        'max_tokens': max_len,
        # 每一个候选token的概率
        'top_p': top_p,
        # 对于每个 prompt 生成候选的数量
        'n': n,
        'temperature': 0,
    })

    response = requests.post(request_url, headers=headers, data=data)
    response = json.loads(response.text)

    generate_text = []
    for text in response['choices']:
        # 去除控制字符
        text = re.sub(r'[\x01-\x1F]', ' ', text['text'])
        # 去除多余空格
        text = ' '.join(text.split())
        generate_text.append(text)

    return generate_text


if __name__ == '__main__':

    result = get_completions('请随机生成一副对联')
    print(result)

    result = get_completions('帮我随机生成一篇自我介绍')
    print(result)

生成结果:

['上联:满山红叶秋色浓 下联:江水碧波夕阳红']
['大家好,我叫XXX,来自XXX,是一名XXX专业的大学生。我有着丰富的学习经历,曾参加过多次社会实践,积累了丰富的实践经验。我喜欢阅读,喜欢探索新领域,乐于分享自己的知识和经验。我乐观开朗,']

2. 模型参数详解

以下较为简单的模型参数:

  1. prompt 是送入模型的内容,模型根据对该内容的理解生成后续的内容
  2. suffix 在模型生成的补全文本之前增加的前缀
  3. max_tokens 生成的最大 token 数量,如果模型输出时碰到结束词或者最大 token 数量将会停止生成
  4. n 可以指定生成并返回多少条候选结果
  5. stop 它告诉模型在何时停止生成文本,可以是一个单词、一个短语、一个特殊字符,例如:句号
  6. echo 表示是否把 prompt 文本添加到返回的 Completions 前面
  7. logprobs 用于分析模型的输出行为
  8. model 指的是模型的名字,其中在 Completion 任务中可用的模型如下:

其中,直观生成文本效果比较好的是:text-davinci-003 模型,文档里也提到该模型要比 curie、babbage、 ada 模型性能和效果要更好好。

top_p 参数 是影响到每一个 token 产生时,模型要考虑那些 Token。例如:top_p 设置为 0.8,当前时间步候选的词的概率分布为:

A 0.4
B 0.35
C 0.2
D 0.05
E 0.04

此时,前两个 Token A + B 的总概率为 0.75,如果再加上 C 的概率,将会操作设置的 top_p 阈值。所以,这一时间步我们只考虑 A 和 B 作为候选词,模型会从这两个 Token 中随机选择一个作为当前时间步的输出。这种采样方法也叫做 nucleus sampling,top_p 中的 p 指的是 probability。

从这里,可以看到较大的 top_p 值可以增加每一个时间步候选 Token 数量,从而增加生成文本的多样性。该值默认为 1。

temperature 参数 会影响到模型预测 Token 的概率分布,从而使得能够将一些原来不可能作为候选的 Token 纳入到候选序列中,默认值为 1 不使用温度参数。它是如何影响到概率分布的呢?先看下 temperature 参数是如何参与到 Token 概率分布的 SoftMax 公式 中:

公式中 \(x_{i}\) 表示模型对预测当前时间步为 i 的 logits。加上 Temperature 之后,对每个 logits 值产生了影响,此时 softmax 结果肯定会受到影响。该参数在当前场景下的取值范围为 [0, 2],我们可以通过一个实验来看看该值是如何影响到候选 Token 的概率分布,下面给出一个计算代码,重点关注计算结果:

import torch
import torch.nn as nn


# 固定随机数种子
torch.manual_seed(66)
# 当前时间步预测每一个Token的logits
logits = torch.randn(1, 3)
print(logits)
# 前时间步预测每一个Token的概率分布
probas = torch.softmax(logits, dim=-1)
print('temperature=1的概率分布:', probas)
print('#' * 60)
print()


# temperature=[0, 1)
def test01():

    # 加入温度参数
    T_logits = logits / 0.2
    print(T_logits)

    # 前时间步预测每一个Token的概率分布
    probas = torch.softmax(T_logits, dim=-1)
    print('temperature=0.2的概率分布:', probas)

    print('-' * 60)

    T_logits = logits / 0.8
    print(T_logits)

    # 前时间步预测每一个Token的概率分布
    probas = torch.softmax(T_logits, dim=-1)
    print('temperature=0.8的概率分布:', probas)


# temperature=(1, 2]
def test02():

    # 加入温度参数
    T_logits = logits / 1.2
    print(T_logits)

    # 前时间步预测每一个Token的概率分布
    probas = torch.softmax(T_logits, dim=-1)
    print('temperature=1.2的概率分布:', probas)

    print('-' * 60)

    T_logits = logits / 1.8
    print(T_logits)

    # 前时间步预测每一个Token的概率分布
    probas = torch.softmax(T_logits, dim=-1)
    print('temperature=1.8的概率分布:', probas)


if __name__ == '__main__':
    test01()
    print('\n')
    test02()

程序输出结果:

tensor([[ 1.8289, -0.2198,  0.3424]])
temperature=1的概率分布: tensor([[0.7380, 0.0951, 0.1669]])
############################################################

tensor([[ 9.1447, -1.0989,  1.7122]])
temperature=0.2的概率分布: tensor([[9.9937e-01, 3.5563e-05, 5.9133e-04]])
------------------------------------------------------------
tensor([[ 2.2862, -0.2747,  0.4281]])
temperature=0.8的概率分布: tensor([[0.8109, 0.0626, 0.1265]])


tensor([[ 1.5241, -0.1831,  0.2854]])
temperature=1.2的概率分布: tensor([[0.6798, 0.1233, 0.1970]])
------------------------------------------------------------
tensor([[ 1.0161, -0.1221,  0.1902]])
temperature=1.8的概率分布: tensor([[0.5687, 0.1822, 0.2490]])

我们可以将 Temperature 的值分为三部分来考虑:

  1. Temperature=1,表示模型只考虑正常的模型输出得到的 Token 的概率分布
  2. Temperature=[0, 1) 我们会发现温度参数值越低,则原来概率较大的 Token 概率会变得更大,概率较小的 Token 的概率会变得越小,这就使得生成文本时更多的考虑原来概率较大的词作为输出。使得模生成文本的多样性收到了限制,生成的文本更单一。简单来说,原来较大的概率变得更大,原来较小的概率变得更小。
  3. Temperature=(1, 2] 我们会发现,原来概率较大的 Token 的概率值会变得更小一些,原来概率较小的 Token 的概率会变得越大,此时你会发现原来较小概率的 Token 就有更多的机会被作为候选,使得模型生成的文本更具有多样性。简单来说,原来较大的概率变得较小,原来较小的概率变得较大,更加平均。

如果结合 top_p 参数,这个效果更加明显。大于 1 的温度值会使得概率分布更加平均,更多的 Token 会被纳入到 top_p 中,从而增加了生成文本的多样性。

注意一点的是,我前面实现并未考虑温度值等于0时的计算,当设置温度值为0时,我们可以使用 1e-9 等等非常小的常数来代替,避免出现除0异常。此时,模型输出变得单一,并且更多的考虑概率最高的 Token.

presence_penalty 和 frequency_penalty 两个参数也是和文本多样性有关的参数。这两个参数都在考虑某个 Token 是否出现在之前生成的序列中,如果出现了则进行惩罚。文档中给出的惩罚计算公式如下:

从计算公式可以看到:

  1. presence_penalty 只要当前 Token 在之前的序列中出现,则对该 Token 的 logits 进行惩罚
  2. frequency_penalty 则也考虑了当前 Token 在之前出现的次数,出现的次数越多则惩罚越重

从这里,我们也可以看到,这两个参数设置的目的是为了让后续的序列生成时尽可能避免重复,增加生成内容的多样性。

logit_bias 参数默认值为 null 表示不使用 logit 偏置,它也能够像前面的 temperature、presence_penalty、frequency_penalty 等一样改变 Token 的概率分布。在模型生成概率分布之前添加到模型的输出中。具体效果会因模型而异,但介于-1到1之间的值应该会减少或增加选中单词的概率;而像-100或100这样的值则会禁止或排他性地选择相关单词。举个例子,可以传递{“50256”: -100}来防止模型生成特定的单词。由于某个 Token 值加上 -100 之后将会变得非常小,计算得到的其概率值将会非常非常小,即:被选中作为候选的可能性非常小。

best_of 参数会控制生成候选自动补全的数量,n 参数则指定从 best_of 中选择多少个结果返回。需要注意的是,best_of 必须大于 n。

未经允许不得转载:一亩三分地 » OpenAI Completions 解码参数详解
评论 (0)

4 + 4 =