ホームページ  >  記事  >  テクノロジー周辺機器  >  ChatGPT はどのようにして単語ごとに出力するのでしょうか?

ChatGPT はどのようにして単語ごとに出力するのでしょうか?

WBOY
WBOY転載
2023-06-02 17:40:222051ブラウズ

ChatGPT はどのようにして単語ごとに出力するのでしょうか?

#ChatGPT が質問に答えると、単語ごとにポップアップ表示され、人々が真剣に考えているという印象を与えます。

この会話の基本的な内容は次のとおりです:

Q: 大規模言語生成モデルはどのように機能しますか
回答: 大規模言語生成モデル (GPT-3 など) は、深層学習テクノロジーに基づくニューラル ネットワーク モデルです。これらのモデルは多くの場合、自然言語処理の分野で大きな成功を収めた「Transformer」と呼ばれるアーキテクチャを使用します。

大規模な言語生成モデルがどのように機能するかについての概要は、

です。 .. 段落を省略します...

テキストの生成: モデルがトレーニングされると、それを使用してテキストを生成できます。最初のテキストまたはプロンプトが与えられると、モデルは学習した確率分布に基づいて次の単語を生成し、生成された単語を入力として使用して次の単語の生成を継続し、必要なテキスト長に達するか生成されるまでこれを繰り返します。シンボル。

...段落を省略...

大規模な言語モデル、大規模言語モデル。LLM と呼ばれます。

モデルの観点から見ると、LLM は、テキストの長さの制限に達するかターミネータが生成されるまで、推論を実行するたびにトークンを生成します。

サーバーの観点から見ると、生成されたトークンは、HTTPS プロトコルを通じてブラウザーに 1 つずつ返す必要があります。

クライアント/サーバー モードでは、従来の対話方法は、クライアントがリクエストを送信し、レスポンスを受信することです。明らかに、これでは ChatGPT が質問に応答するというシナリオには対応できません。

2 番目に、HTTP に依存してハンドシェイクを実装し、WebSocket にアップグレードされる WebSocket について考えます。ただし、WebSocket ではクライアントとサーバーの両方が継続的にソケットを占有する必要があり、サーバー側のコストが比較的高くなります。

ChatGPT は妥協策を使用します: サーバー送信イベント (略して SSE)。これは OpenAI の API ドキュメントから見つけることができます:

ChatGPT はどのようにして単語ごとに出力するのでしょうか?

SSE モードでは、クライアントはサーバーにリクエストを 1 回送信するだけでよく、サーバーは最後まで要求されるまで出力を続けることができます。インタラクション プロセス全体を次の図に示します。

ChatGPT はどのようにして単語ごとに出力するのでしょうか?

SSE は依然としてアプリケーション層の送信プロトコルとして HTTP を使用し、HTTP の長い接続を最大限に活用しています。サーバー側のプッシュ機能を実装する機能。

コードの観点から見ると、SSE モードと単一の HTTP リクエストの違いは次のとおりです。

  1. クライアントはオンにする必要があります。 keep -alive は、接続がタイムアウトしないことを保証します。
  2. HTTP 応答ヘッダーには、Content-Type=text/event-stream、Cache-Cnotallow=no-cache などが含まれます。
  3. HTTP レスポンスの本文は、通常、「data: ...」のような構造になります。
  4. HTTP 応答には、接続タイムアウトを回避するために空のデータが含まれる場合があります。

ChatGPT API を例に挙げると、リクエストを送信するときに、stream パラメーターを true に設定すると SSE 機能が有効になりますが、次のような SDK に注意する必要があります。データ。 。

通常モードでは、http.Response を取得した後、ioutil.ReadAll を使用してデータを読み取ります。コードは次のようになります。 :

func main() {payload := strings.NewReader(`{"model": "gpt-3.5-turbo","messages": [{"role": "user", "content": "大语言生成式模型是如何工作的"}],"max_tokens": 1024,"temperature": 1,"top_p": 1,"n": 1,"stream": false}`)client := &http.Client{}req, _ := http.NewRequest("POST", "https://api.openai.com/v1/chat/completions", payload)req.Header.Add("Content-Type", "application/json")req.Header.Add("Authorization", "Bearer <openai-token>")resp, err := client.Do(req)if err != nil {fmt.Println(err)return}defer resp.Body.Close()body, _ := ioutil.ReadAll(resp.Body)fmt.Println(string(body))}</openai-token>

実行して完全な結果を得るには約 20 秒かかります:

{"id": "chatcmpl-7KklTf9mag5tyBXLEqM3PWQn4jlfD","object": "chat.completion","created": 1685180679,"model": "gpt-3.5-turbo-0301","usage": {"prompt_tokens": 21,"completion_tokens": 358,"total_tokens": 379},"choices": [{"message": {"role": "assistant","content": "大语言生成式模型通常采用神经网络来实现,具体工作流程如下:\n\n1. 数据预处理:将语料库中的文本数据进行预处理,包括分词、删除停用词(如“的”、“了”等常用词汇)、去重等操作,以减少冗余信息。\n\n2. 模型训练:采用递归神经网络(RNN)、长短期记忆网络(LSTM)或变种的Transformers等模型进行训练,这些模型都具有一定的记忆能力,可以学习到语言的一定规律,并预测下一个可能出现的词语。\n\n3. 模型应用:当模型完成训练后,可以将其应用于实际的生成任务中。模型接收一个输入文本串,并预测下一个可能出现的词语,直到达到一定长度或遇到结束符号为止。\n\n4. 根据生成结果对模型进行调优:生成的结果需要进行评估,如计算生成文本与语料库文本的相似度、流畅度等指标,以此来调优模型,提高其生成质量。\n\n总体而言,大语言生成式模型通过对语言的规律学习,从而生成高质量的文本。"},"finish_reason": "stop","index": 0}]}

stream を何も変更せずに true に設定した場合、リクエストの合計消費量は 28 秒になります。多くのストリーム メッセージの場合:

ChatGPT はどのようにして単語ごとに出力するのでしょうか?

上の図は、ioutil.ReadAll モードを使用して chatgpt API を呼び出している Postman の図です。ストリーム読み取りを実装するには、http.Response.Body をセグメントで読み取ることができます。これが機能する理由は次のとおりです。

  1. http.Response.Body のタイプは io.ReaderCloser で、最下層です。 HTTP 接続に依存し、ストリーム読み取りをサポートします。
  2. #SSE によって返されたデータは改行文字で分割されています\n

したがって、修正方法は次のようになります。 bufio.NewReader(resp.Body)ラップして for ループで読み取ります。コードは次のとおりです:

// stream event 结构体定义type ChatCompletionRspChoiceItem struct {Deltamap[string]string `json:"delta,omitempty"` // 只有 content 字段Indexint `json:"index,omitempty"`Logprobs *int`json:"logprobs,omitempty"`FinishReason string`json:"finish_reason,omitempty"`}type ChatCompletionRsp struct {IDstring`json:"id"`Objectstring`json:"object"`Created int `json:"created"` // unix secondModel string`json:"model"`Choices []ChatCompletionRspChoiceItem `json:"choices"`}func main() {payload := strings.NewReader(`{"model": "gpt-3.5-turbo","messages": [{"role": "user", "content": "大语言生成式模型是如何工作的"}],"max_tokens": 1024,"temperature": 1,"top_p": 1,"n": 1,"stream": true}`)client := &http.Client{}req, _ := http.NewRequest("POST", "https://api.openai.com/v1/chat/completions", payload)req.Header.Add("Content-Type", "application/json")req.Header.Add("Authorization", "Bearer "+apiKey)req.Header.Set("Accept", "text/event-stream")req.Header.Set("Cache-Control", "no-cache")req.Header.Set("Connection", "keep-alive")resp, err := client.Do(req)if err != nil {fmt.Println(err)return}defer resp.Body.Close()reader := bufio.NewReader(resp.Body)for {line, err := reader.ReadBytes('\n')if err != nil {if err == io.EOF {// 忽略 EOF 错误break} else {if netErr, ok := err.(net.Error); ok && netErr.Timeout() {fmt.Printf("[PostStream] fails to read response body, timeout\n")} else {fmt.Printf("[PostStream] fails to read response body, err=%s\n", err)}}break}line = bytes.TrimSuffix(line, []byte{'\n'})line = bytes.TrimPrefix(line, []byte("data: "))if bytes.Equal(line, []byte("[DONE]")) {break} else if len(line) > 0 {var chatCompletionRsp ChatCompletionRspif err := json.Unmarshal(line, &chatCompletionRsp); err == nil {fmt.Printf(chatCompletionRsp.Choices[0].Delta["content"])} else {fmt.Printf("\ninvalid line=%s\n", line)}}}fmt.Println("the end")}

クライアント側を読み取った後、サーバー側を見てみましょう。ここで、chatgpt サーバーを模擬して、テキストをそのまま返してみます。ここには 2 つの点が関係します。

  1. 応答ヘッダーでは、Connection を keep-alive に設定し、Content-Type を text/event-stream に設定する必要があります。
  2. respnose を書き込んだ後、クライアントにフラッシュする必要があります。

コードは次のとおりです:

func streamHandler(w http.ResponseWriter, req *http.Request) {w.Header().Set("Connection", "keep-alive")w.Header().Set("Content-Type", "text/event-stream")w.Header().Set("Cache-Control", "no-cache")var chatCompletionRsp ChatCompletionRsprunes := []rune(`大语言生成式模型通常使用深度学习技术,例如循环神经网络(RNN)或变压器(Transformer)来建模语言的概率分布。这些模型接收前面的词汇序列,并利用其内部神经网络结构预测下一个词汇的概率分布。然后,模型将概率最高的词汇作为生成的下一个词汇,并递归地生成一个词汇序列,直到到达最大长度或遇到一个终止符号。在训练过程中,模型通过最大化生成的文本样本的概率分布来学习有效的参数。为了避免模型产生过于平凡的、重复的、无意义的语言,我们通常会引入一些技巧,如dropout、序列扰动等。大语言生成模型的重要应用包括文本生成、问答系统、机器翻译、对话建模、摘要生成、文本分类等。`)for _, r := range runes {chatCompletionRsp.Choices = []ChatCompletionRspChoiceItem{{Delta: map[string]string{"content": string(r)}},}bs, _ := json.Marshal(chatCompletionRsp)line := fmt.Sprintf("data: %s\n", bs)fmt.Fprintf(w, line)if f, ok := w.(http.Flusher); ok {f.Flush()}time.Sleep(time.Millisecond * 100)}fmt.Fprintf(w, "data: [DONE]\n")}func main() {http.HandleFunc("/stream", streamHandler)http.ListenAndServe(":8088", nil)}

実際のシナリオでは、返されるデータは別のサービスまたは関数から取得されます。このサービスまたは関数呼び出しの戻り時間が不安定なため、クライアントが長時間メッセージを受信できない可能性があるため、一般的な処理方法は次のとおりです。 #サードパーティへの呼び出しはゴルーチンに配置されます。

    時間を経過するタイマーを作成します。チェックマークを付けて、空のメッセージをクライアントに送信します。
  1. 応答時間が長すぎるのを避けるために、タイムアウト チャネルを作成します。
  2. さまざまなチャネルからデータを読み取るには、次のデモ コードのような適切なキーワードを選択します。
    // 声明一个 event channel// 声明一个 time.Tick channel// 声明一个 timeout channelselect {case ev := 
  3. 概要大規模な言語モデルの結果全体の生成と応答には比較的時間がかかりますが、トークンごとに生成される応答は比較的高速です。ChatGPT は、この機能と SSE テクノロジーを完全に組み合わせて、単語ごとにポップアップします。返信はユーザーの質的向上を達成しました。経験。

LLAMA/Little Alpaca (市販されていません) または Stable Diffusion/Midjourney のいずれであっても、生成モデルを調べます。オンライン サービスを提供する場合、SSE テクノロジーを使用してユーザー エクスペリエンスを向上させ、サーバー リソースを節約できます。

以上がChatGPT はどのようにして単語ごとに出力するのでしょうか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事は51cto.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。