pring AI > Spring AI API > Prompts
大家可以直接看官網:https://docs.spring.io/spring-ai/reference/api/prompt.html
提示是指導AI模型生成特定輸出的輸入。這些提示的設計和措辭顯著影響模型的響應。
與Spring AI中的AI模型進行最低級別交互時,處理Spring AI中的提示與在Spring MVC中管理“視圖”有些相似。這涉及創建帶有占位符的廣泛文本,用于動態內容。然后,根據用戶請求或應用程序中的其他代碼替換這些占位符。另一個類比是包含占位符的SQL語句,用于某些表達式。
隨著Spring AI的發展,它將引入與AI模型交互的更高級別的抽象。本節描述的基礎類可以類比為JDBC在其角色和功能上。例如,ChatClient類類似于JDK中的核心JDBC庫。在此基礎上,Spring AI可以提供類似于JdbcTemplate、Spring Data Repositories的輔助類,最終還有考慮與模型過去交互的更高級的結構,如ChatEngines和Agents。
提示的結構在AI領域內隨著時間的推移而發展。最初,提示是簡單的字符串。隨著時間的推移,它們發展到包括特定輸入的占位符,如“AI:”,AI模型可以據此做出相應的響應。OpenAI通過將多個消息字符串分類為不同的角色,然后再由AI模型處理,引入了更多結構化的提示方法。
通常使用ChatClient的generate方法,該方法接受一個Prompt實例并返回一個ChatResponse。
Prompt類作為一個容器,包含一系列有組織的消息對象,每個對象形成提示的一個部分。每個Message在提示中扮演一個獨特的角色,其內容和意圖各不相同。這些角色可以包括各種元素,從用戶詢問到AI生成的響應或相關的背景信息。這種安排使得與AI模型的復雜和詳細交互成為可能,因為提示是由多個消息構建的,每個消息在對話中扮演一個特定的角色。
以下是一個截斷的Prompt類版本,為了簡潔起見,省略了構造函數和實用方法:
public class Prompt {
private final List<Message> messages;
// constructors and utility methods omitted
}
Message接口封裝了一個文本消息、一個屬性集合作為Map、一個稱為MessageType的分類,以及對于那些多模態模型的媒體對象列表。接口定義如下:
public interface Message {
String getContent();
List<Media> getMedia();
Map<String, Object> getProperties();
MessageType getMessageType();
}
AI模型可以處理的各種消息類型的不同實現。一些模型,如來自OpenAI的模型,根據會話角色區分消息類別。這些角色通過MessageType有效映射,如下所述。
AI中提示的發展已經從基本的、直接的文本轉變為具有特定角色和結構的更有組織和復雜的格式。
最初,提示是簡單的字符串——只是文本行。隨著時間的推移,這發展到包括這些字符串中的特定占位符,如“用戶:”,AI模型可以據此做出相應的響應。這是朝著更結構化提示邁出的一步。
OpenAI隨后引入了一種更有組織的方法。在他們的模型中,提示不僅僅是單個字符串,而是一系列消息。盡管每個消息仍然是文本形式,但被分配了一個特定的角色。這些角色對消息進行分類,為AI模型澄清了每個提示段的上下文和目的。這種結構化的方法增強了與AI溝通的細微差別和有效性,因為提示的每個部分在交互中都扮演了一個獨特而明確的角色。
主要角色包括:
系統角色:指導AI的行為和響應風格,為AI解釋和回復輸入設置參數或規則。這類似于在開始對話之前向AI提供指令。
用戶角色:代表用戶的輸入——他們的問題、命令或對AI的陳述。這個角色是基礎,因為它構成了AI響應的基礎。
助手角色:AI對用戶輸入的響應。不僅僅是一個答案或反應,對于維持對話的流暢性至關重要。通過跟蹤AI之前的響應(其“助手角色”消息),系統確保交互是連貫和上下文相關的。
功能角色:這個角色處理對話中的特定任務或操作。雖然系統角色設置了AI的總體行為,但功能角色專注于執行用戶要求的某些動作或命令。這就像AI中的一個特殊功能,需要時用于執行特定功能,如計算、獲取數據或其他超出僅僅對話的任務。這個角色允許AI除了對話響應之外,還提供實際幫助。
在Spring AI中,角色以枚舉的形式表示,如下所示:
public enum MessageType {
USER("user"),
ASSISTANT("assistant"),
SYSTEM("system"),
FUNCTION("function");
private final String value;
MessageType(String value) {
this.value=value;
}
public String getValue() {
return value;
}
public static MessageType fromValue(String value) {
for (MessageType messageType : MessageType.values()) {
if (messageType.getValue().equals(value)) {
return messageType;
}
}
throw new IllegalArgumentException("Invalid MessageType value: " + value);
}
}
Spring AI中提示模板的關鍵組件是PromptTemplate類。這個類使用由Terence Parr開發的StringTemplate引擎來構建和管理提示。
PromptTemplate類旨在便于創建結構化的提示,然后將這些提示發送給AI模型進行處理。
public class PromptTemplate implements PromptTemplateActions, PromptTemplateMessageActions {
// Other methods to be discussed later
}
這個類實現的接口支持提示創建的不同方面:
PromptTemplateStringActions專注于創建和渲染提示字符串,代表了最基本的提示生成形式。
PromptTemplateMessageActions專門用于通過生成和操作Message對象來創建提示。
PromptTemplateActions旨在返回Prompt對象,該對象可以傳遞給ChatClient以生成響應。
雖然這些接口在許多項目中可能不會廣泛使用,但它們展示了提示創建的不同方法。
實現的接口是:
public interface PromptTemplateStringActions {
String render();
String render(Map<String, Object> model);
}
方法String render():將提示模板渲染成最終的字符串格式,沒有外部輸入,適用于沒有占位符或動態內容的模板。
方法String render(Map model):增強渲染功能以包括動態內容。它使用一個Map,其中map鍵是提示模板中的占位符名稱,值是要插入的動態內容。
public interface PromptTemplateMessageActions {
Message createMessage();
Message createMessage(Map<String, Object> model);
}
方法Message createMessage():創建一個沒有額外數據的Message對象,用于靜態或預定義的消息內容。
方法Message createMessage(Map model):擴展消息創建功能以整合動態內容,接受一個Map,其中每個條目代表消息模板中的占位符及其相應的動態值。
public interface PromptTemplateActions extends PromptTemplateStringActions {
Prompt create();
Prompt create(Map<String, Object> model);
}
方法Prompt create():生成一個沒有外部數據輸入的Prompt對象,適用于靜態或預定義的提示。
方法Prompt create(Map model):擴展提示創建功能以包括動態內容,接受一個Map,其中每個map條目是提示模板中的占位符及其相關的動態值。
以下是一個簡單的示例,取自AI Workshop on PromptTemplates。
PromptTemplate promptTemplate=new PromptTemplate("Tell me a {adjective} joke about {topic}");
Prompt prompt=promptTemplate.create(Map.of("adjective", adjective, "topic", topic));
return chatClient.call(prompt).getResult();
另一個示例取自AI Workshop on Roles。
String userText="Tell me about three famous pirates from the Golden Age of Piracy and why they did. Write at least a sentence for each pirate.";
Message userMessage=new UserMessage(userText);
String systemText="You are a helpful AI assistant that helps people find information. Your name is {name}. You should reply to the user's request with your name and also in the style of a {voice}.";
SystemPromptTemplate systemPromptTemplate=new SystemPromptTemplate(systemText);
Message systemMessage=systemPromptTemplate.createMessage(Map.of("name", name, "voice", voice));
Prompt prompt=new Prompt(List.of(userMessage, systemMessage));
List response=chatClient.call(prompt).getResults();
這展示了如何通過使用SystemPromptTemplate創建具有系統角色的消息并傳遞占位符值來構建Prompt實例。然后,將具有用戶角色的消息與具有系統角色的消息結合起來形成提示。然后將提示傳遞給ChatClient以獲得生成性響應。
Spring AI支持org.springframework.core.io.Resource抽象,因此您可以將提示數據放在文件中,這些文件可以直接在PromptTemplates中使用。例如,您可以在Spring管理的組件中定義一個字段來檢索Resource。
@Value("classpath:/prompts/system-message.st")
private Resource systemResource;
然后將該資源直接傳遞給SystemPromptTemplate。
SystemPromptTemplate systemPromptTemplate=new SystemPromptTemplate(systemResource);
在生成性AI中,創建提示是開發者的關鍵任務。這些提示的質量和結構顯著影響AI輸出的有效性。投入時間和精力設計周到的提示可以極大地改善AI的結果。
在AI社區中分享和討論提示是一種常見做法。這種協作方法不僅創建了一個共享的學習環境,而且還導致了高效提示的識別和使用。
這個領域的研究通常涉及分析和比較不同的提示,以評估它們在各種情況下的有效性。例如,一個重要研究表明,以“深呼吸,一步一步解決這個問題”開始提示顯著提高了解決問題的效率。這突顯了精心選擇的語言對生成性AI系統性能的影響。
掌握提示的有效使用,特別是隨著AI技術的快速發展,是一個持續的挑戰。您應該認識到提示工程的重要性,并考慮使用社區和研究的見解來改進提示創建策略。
在開發提示時,重要的是整合幾個關鍵組件,以確保清晰和有效性:
指令:向AI提供清晰直接的指令,類似于您如何與人交流。這種清晰度 對于幫助AI“理解”預期非常重要。
外部上下文:在必要時包括相關的背景信息或對AI響應的具體指導。這種“外部上下文”為提示提供了框架,并幫助AI把握整體場景。
用戶輸入:這是直接的部分——用戶直接的請求或問題構成了提示的核心。
輸出指示:這個方面可能有些棘手。它涉及指定AI響應的期望格式,例如JSON。但是,請注意,AI可能不總是嚴格遵循這種格式。例如,它可能在實際JSON數據之前加上一個短語,如“這是你的JSON”,或者有時生成一個類似JSON的結構,但不是準確的。在制作提示時,提供AI預期問題和答案格式的示例可能非常有益。這種做法幫助AI“理解”您的查詢的結構和意圖,從而產生更精確和相關的響應。雖然這份文檔沒有深入探討這些技術,但它們為進一步探索AI提示工程提供了起點。
以下是一些資源列表,供進一步調查。
文本摘要:+ 將大量文本縮減為簡潔的摘要,捕捉關鍵點和主要思想,同時省略較不重要的細節。
問題回答:+ 專注于從提供的文本中得出特定答案,基于用戶提出的問題。它是關于根據查詢精確定位和提取相關信息。
文本分類:+ 系統地將文本分類到預定義的類別或組中,分析文本并根據其內容將其分配給最合適的類別。
對話:+ 創建交互式對話,AI可以與用戶進行來回溝通,模擬自然的對話流程。
代碼生成:+ 根據特定用戶需求或描述生成功能性代碼片段,將自然語言指令翻譯成可執行代碼。
Few-shot Learning:+ 使模型能夠在對特定問題類型的示例最少或沒有的情況下進行準確的預測或響應,利用學習的概括來理解和處理新任務。
Chain-of-Thought:+ 將多個AI響應鏈接起來,創建一個連貫且具有上下文意識的對話。它幫助AI保持討論的線索,確保相關性和連續性。
ReAct (Reason + Act):+ 在這種方法中,AI首先分析(思考)輸入,然后確定最合適的行動或響應。它結合了理解與決策。
提示創建和優化框架:
微軟提供了一個結構化的方法來開發和完善提示。這個框架指導用戶創建有效的提示,從AI模型中引出期望的響應,優化交互以實現清晰和高效。
標記在AI模型處理文本的方式中至關重要,它們作為一座橋梁,將單詞(按我們的理解)轉換為AI模型可以處理的格式。這種轉換分為兩個階段:單詞在輸入時被轉換為標記,然后這些標記在輸出時再轉換回單詞。
將文本分解為標記的過程是AI模型理解和處理語言的基礎。AI模型使用這種標記化格式來理解和響應提示。
為了更好地理解標記,可以將它們視為單詞的部分。通常,一個標記代表大約四分之三的單詞。例如,莎士比亞的全部作品,總計約90萬字,將轉換為大約120萬個標記。
嘗試使用OpenAI Tokenizer UI,看看單詞是如何轉換為標記的。
標記除了在AI處理中的技術角色之外,還具有實際影響,特別是關于計費和模型能力:
計費:AI模型服務通常根據標記使用情況進行計費。輸入(提示)和輸出(響應)都貢獻了總標記計數,使較短的提示更具成本效益。
模型限制:不同的AI模型有不同的標記限制,定義了它們的“上下文窗口”——它們一次能夠處理的最大信息量。例如,GPT-3的限制是4K標記,而其他模型如Claude 2和Meta Llama 2的限制是100K標記,一些研究模型可以處理高達1百萬標記。
上下文窗口:模型的標記限制決定了它的上下文窗口。超過此限制的輸入不會被模型處理。重要的是只發送最小有效集的信息進行處理。例如,當詢問“哈姆雷特”時,無需包括莎士比亞其他作品的所有標記。
響應元數據:AI模型響應的元數據包括使用的標記數量,這是管理使用情況和成本的重要信息。
之前說過結構化Prompt,這是一個具體案例的使用,本例是把公眾號上中文技術文章翻譯成選擇的語言。
基本思路是用戶輸入文章的url,系統用Playwright讀取html內容,然后利用SemanticKernel的OpenAIChatCompletionService功能,按照提示詞翻譯,最后用Playwright把結果發送到Qiit(一個日本技術博客網站)上。
結構化的提示詞如下:
# Role: 軟件技術翻譯專家
## Profile:
### Author: gsw
### Version: 2.0
### Language: {{language}}
### Description: 我是一個專門把中文技術文章翻譯成Language指定的技術文章的AI 角色。
## Goals: 能準確地把中文技術文章翻譯成Language指定技術文章。
## Constrains:
1. 把html轉成markdown輸出,在輸出時,請注意要翻譯成Language指定的語言
2. 把代碼放在專有的代碼塊標識中,如果分析不出是什么類型的代碼,就以 C# 代碼塊進行標識
4. 你不會在翻譯時添加自己的看法,只是原文翻譯
5. 翻譯完后, 不會詢問是否有其它問題
6. 注意html中的圖片(img)標簽,要轉成markdown的形式同時給出
7. 保持原文輸出,請全部翻譯輸出,請全部翻譯輸出,請全部翻譯輸出
8. 在翻譯完成后,最后一行添加“(Translated by GPT)”字樣
## Skills:
1. 具有強大的軟件技術知識獲取和整合能力
2. 擁有廣泛的編程語言知識庫, 掌握提問和回答的技巧
3. 擁有排版審美, 會利用序號, 縮進, 分隔線和換行符等等來美化信息排版
4. 擅長使用比喻的方式來讓用戶理解知識
5. 充分利用markdown語法來排版
## Workflows:
1. 讓用戶以 "標題:[]" 的方式指定需要翻譯的標題。
2. 讓用戶以 "內容:[]" 的方式指定需要翻譯的內容。
3. 針對用戶給定的標題和內容進行翻譯,不要帶“標題:”和“內容:”字樣,第一行是標題,第二行以后是內容。
## Initialization
作為角色 <Role>,你擁有 <Skills>,嚴格遵守 <Constrains>,使用默認 <Language> ,按照 <Workflows>輸出結果。
原來提示詞是把網頁文字取出來,進行翻譯,圖片進行了特殊處理,像表格之類的格式就會丟掉,后來作了一下優化,讓GPT直接把html轉成markdown,這樣即使有特列的html表示,也能轉成相對友好的markdown格式。
具體C#代碼實現如下:
using Microsoft.Playwright;
using Microsoft.SemanticKernel.ChatCompletion;
using Microsoft.SemanticKernel.Connectors.OpenAI;
using System.Text.RegularExpressions;
using System.Text;
namespace TranslateAgent
{
public partial class MainForm : Form
{
public MainForm()
{
InitializeComponent();
}
private void MainForm_Load(object sender, EventArgs e)
{
var lans=new List<string>
{
"日語","英語","法語","德語","韓語"
};
LanComBox.DataSource=lans;
}
(string title, string content) SplitArticle(string article)
{
if (!string.IsOrWhiteSpace(article))
{
var reader=new StringReader(article);
var title=reader.ReadLine();
var content=reader.ReadToEnd();
return (title, content);
}
throw new Exception("文章為空!");
}
async Task<bool> PublishArticleAsync(string translatorContent)
{
var (title, content)=SplitArticle(translatorContent);
var url="";
this.Invoke(()=>
{
url=UrlTextBox.Text;
});
if (!string.IsOrWhiteSpace(url))
{
content +=$"\r\n元のリンク:{url}";
}
using var playwright=await Playwright.CreateAsync();
await using var browser=await playwright.Chromium.LaunchAsync(new BrowserTypeLaunchOptions
{
Headless=false,
Args=["--start-maximized"]
});
var page=await browser.NewPageAsync();
await page.GotoAsync("https://qiita.com/");
await page.ClickAsync("a[href='/login?callback_action=login_or_signup&redirect_to=%2F&realm=qiita']");
var userArr=File.ReadAllLines("C:/gpt/qiita_user.txt");
await page.FillAsync("#identity", userArr[0]);
await page.FillAsync("#password", userArr[1]);
await page.ClickAsync("input[name='commit']");
await page.GotoAsync("https://qiita.com/drafts/new");
await page.FillAsync("input[placeholder='記事タイトル']", title);
await page.FillAsync("input[placeholder='タグを入力してください。スペース區切りで5つまで入力できます。']", "C# .NET");
await page.FillAsync("div[role='textbox']", content);
MessageBox.Show("請確認發表內容,并且手動在彈出的內核瀏覽器中發布!請注意,點擊確定后會自動關閉內核瀏覽器!!!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Information);
return true;
}
async Task<(string Title, string Content)> GetArticleAsync(string url)
{
using var playwright=await Playwright.CreateAsync();
await using var browser=await playwright.Chromium.LaunchAsync(/*new BrowserTypeLaunchOptions { Headless=false }*/);
var page=await browser.NewPageAsync();
await page.GotoAsync(url);
var title=await page.Locator("#activity-name").InnerTextAsync();
var locator=page.Locator("#js_content");
var images=await locator.GetByRole(AriaRole.Img).ElementHandlesAsync();
var imageList=new List<string>();
foreach (var image in images)
{
var imgUrl=await image.GetAttributeAsync("data-src");
imageList.Add(imgUrl);
}
var html=await locator.InnerHTMLAsync();
var imgTagPattern=@"<img[^>]*>";
html=Regex.Replace(html, imgTagPattern, "[圖片]");
await page.SetContentAsync("<div id='js_content'>" + html + "</div>");
var content=await page.Locator("#js_content").InnerTextAsync();
var reader=new StringReader(content);
var index=0;
var contentBuilder=new StringBuilder();
while (true)
{
var line=await reader.ReadLineAsync();
if (!string.IsOrWhiteSpace(line.Trim()))
{
if (line.Trim()=="[圖片]")
{
contentBuilder.AppendLine($"");
index++;
}
else
{
contentBuilder.AppendLine(line);
}
}
if (reader.Peek() <=0)
{
break;
}
}
return (title, contentBuilder.ToString());
}
async Task<string> OpenAIChatSampleAsync(string title, string content)
{
var key=File.ReadAllText(@"C:\GPT\key.txt");
var chatModelId="gpt-4-0125-preview";
OpenAIChatCompletionService chatCompletionService=new(chatModelId, key);
return await StartChatAsync(chatCompletionService, title, content);
}
async Task<string> StartChatAsync(IChatCompletionService chatGPT, string title, string content)
{
var lan="日文";
this.Invoke(()=>
{
lan=LanComBox.Text;
});
var prompt=File.ReadAllText(Environment.CurrentDirectory + "/Prompt.md");
prompt=prompt.Replace("{{language}}", lan);
var chatHistory=new ChatHistory(prompt);
var userContent=$"標題:{title}\r\n內容:{content}";
chatHistory.AddUserMessage(userContent);
return await MessageStreamOutputAsync(chatGPT, chatHistory);
}
async Task<string> MessageStreamOutputAsync(IChatCompletionService chatGPT, ChatHistory chatHistory)
{
var list=chatGPT.GetStreamingChatMessageContentsAsync(chatHistory);
var fullMessage=string.Empty;
await foreach (var item in list)
{
if (item==)
{
continue;
}
fullMessage +=item.Content;
this.Invoke(()=>
{
TranslationTextBox.AppendText(item.Content);
});
}
return fullMessage;
}
private void TranButton_Click(object sender, EventArgs e)
{
try
{
var url=UrlTextBox.Text;
if (string.IsOrWhiteSpace(url))
{
MessageBox.Show("輸入的url有誤,請重新輸入!", "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
var pattern=@"^(https?:\/\/)?(?s).*$";
var regex=new Regex(pattern, RegexOptions.IgnoreCase);
bool isValid=regex.IsMatch(url);
if (!isValid)
{
MessageBox.Show("輸入的url有誤,請重新輸入!", "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
else
{
Task.Run(()=>
{
try
{
var (title, content)=GetArticleAsync(url).Result;
this.Invoke(()=>
{
OriginalTextBox.Lines=new string[] { title, content };
});
var translatorContent=OpenAIChatSampleAsync(title, content).Result;
var result=PublishArticleAsync(translatorContent).Result;
if (!result)
{
MessageBox.Show("翻譯失敗!", "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
catch (Exception exc)
{
MessageBox.Show(exc.Message, "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
});
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
private void LanComBox_SelectedIndexChanged(object sender, EventArgs e)
{
this.Text="中文->" + LanComBox.Text;
}
private void ClearButton_Click(object sender, EventArgs e)
{
UrlTextBox.Clear();
OriginalTextBox.Clear();
TranslationTextBox.Clear();
}
}
}
運行結果一:
運行結果二:
器之心專欄
機器之心編輯部
本文提出了一個名為 SpeechGen 的統一框架,該框架可用于任意的 speech LM 及各類語音生成任務,具有很好的潛力。
論文鏈接:https://arxiv.org/pdf/2306.02207.pdf
Demo 頁面:https://ga642381.github.io/SpeechPrompt/speechgen.html
Code: https://github.com/ga642381/SpeechGen
引言與動機
大型語言模型(LLMs)在人工智能生成內容(AIGC)方面引起了相當大的關注,特別是隨著 ChatGPT 的出現。
然而,如何用大型語言模型處理連續語音仍然是一個未解決的挑戰,這一挑戰阻礙了大型語言模型在語音生成方面的應用。因為語音信號包含豐富的信息,如說話者和情感,超越了純文本數據,基于語音的語言模型 (speech language model (speech LM)) 不斷涌現。
雖然與基于文本的語言模型相比,語音語言模型仍處于早期階段,但由于語音數據中蘊含著比文本更豐富的信息,它們具備巨大的潛力,令人充滿期待。
研究人員正積極探索提示 (prompt) 范式的潛力,以發揮預訓練語言模型的能力。這種提示通過微調少量參數,引導預訓練語言模型做特定的下游任務。這種技術因其高效和有效而在 NLP 領域備受青睞。在語音處理領域,SpeechPrompt 展示出了在參數效率方面的顯著改進,并在各種語音分類任務中取得了競爭性的表現。
然而,提示能否幫助語音語言模型完成生成任務仍是未解之謎。在本文中,我們提出一個創新的統一框架:SpeechGen,旨在激發語音語言模型進行生成任務的潛力。如下圖所示,將一段語音、一個特定的提示 (prompt) 喂給 speech LM 作為輸入,speech LM 就能做特定的任務。比如將紅色的 prompt 當作輸入,speech LM 就能做 speech translation 的任務。
我們提出的框架具有以下優點:
1. 無文本 (Textless):我們的框架以及其所依賴的語音語言模型獨立于文字數據,擁有無可估量的價值。畢竟,獲取標記文本與語音配對的過程耗時繁瑣,而且在某些語言中甚至無法找到合適的文本。無需文字的特性使得我們的強大語音生成能力得以覆蓋各種語言需求,讓全人類受益匪淺。
2. 多功能性 (Versatility):我們開發的框架通用性極高,能應用于各種各樣的語音生成任務。論文中實驗使用語音翻譯、語音修復、語音連續當作例子。
3. 簡易性 (Easy to follow):我們提出的框架為各類語音生成任務提供了通用解決方案,讓設計下游模型和損失函數變得輕而易舉。
4. 可遷移性 (Transferability):我們的框架不僅容易適應未來更先進的語音語言模型,還蘊藏著巨大的潛力,讓效率和效果得到進一步提升。尤其令人振奮的是,隨著先進語音語言模型即將問世,我們的框架將迎來更為強大的發展。
5. 經濟性 (Affordability):我們的框架經過精心的設計,只需訓練少量參數,而不是整個龐大的語言模型。這極大地減輕了計算負擔,并允許在 GTX 2080 GPU 上執行訓練過程。大學的實驗室也能負擔得起這樣的運算開銷。
SpeechGen介紹
我們的研究方法在于構建一個全新的框架 SpeechGen,該框架主要利用語音語言模型(Spoken Language Models, SLMs)進行各種下游語音生成任務的微調。在訓練過程中,SLMs 的參數保持不變,我們的方法側重于學習任務特定的提示(Prompt)向量。SLMs 通過同時對提示向量和輸入單元進行條件設置,有效地生成特定語音生成任務所需的輸出。然后,這些離散單元輸出被輸入到基于單元的語音合成器中,生成對應的波形。
我們的 SpeechGen 框架由三個元素組成:語音編碼器、SLM 和語音解碼器(Speech Decoder)。
首先,語音編碼器將波形作為輸入,并將其轉換為由有限詞匯表導出的單位序列。為了縮短序列長度,會移除重復的連續單位以生成壓縮的單位序列。然后,SLM 作為單位序列的語言模型,通過預測前一單位和單位序列的后續單位來優化可能性。我們對 SLM 進行提示調整,以引導其根據任務生成適當的單位。最后,SLM 生成的標記由語音解碼器處理,將其轉換回波形。在我們的提示調整策略中,提示向量會在輸入序列的開始處插入,這將引導 SLMs 在生成過程中的方向。具體插入的提示數量,則取決于 SLMs 的架構。在序列到序列的模型中,編碼器輸入和解碼器輸入都會加入提示,但在只有編碼器或只有解碼器的架構中,只會在輸入序列前面添加一個提示。
在序列到序列的 SLMs(如 mBART)中,我們采用了自我監督學習模型(如 HuBERT)來處理輸入和目標語音。這樣做可以為輸入生成離散單元,并為目標生成對應的離散單元。我們在編碼器和解碼器輸入的前面都添加了提示向量,以構造輸入序列。此外,我們還通過替換注意力機制中的關鍵值對,以進一步增強提示的指導能力。
在模型訓練中,我們以交叉熵損失作為所有生成任務的目標函數,通過比較模型的預測結果和目標離散單元標簽來計算損失。在這個過程中,提示向量是模型中唯一需要訓練的參數,而 SLMs 的參數在訓練過程中保持不變,這確保了模型行為的一致性。我們通過插入提示向量,引導 SLMs 從輸入中提取任務特定信息,并提高產生符合特定語音生成任務的輸出的可能性。這種方法允許我們微調并調整 SLMs 的行為,而無需修改其基礎參數。
總的來說,我們的研究方法基于一種全新的框架 SpeechGen,通過訓練提示向量,引導模型的生成過程,并使其能有效地產生符合特定語音生成任務的輸出。
實驗
我們的框架可以用于任意的 speech LM 及各類生成任務,具有很好的潛力。在我們的實驗中,由于 VALL-E 和 AudioLM 不是開源的,我們選擇使用 Unit mBART 作為 speech LM 進行案例研究。我們用語音翻譯 (speech translation)、語音修復 (speech inpainting)、語音連續 (speech continuation) 當作例子,來展示我們框架的能力。這三個任務的示意圖如下圖所示。所有的任務都是語音輸入,語音輸出,無需文本幫助。
語音翻譯
我們在訓練語音翻譯 (speech translation) 時,用的是西班牙文轉英文的任務。我們給模型輸入西班牙語的語音,希望模型產生英文的語音,整個過程無需文本幫助。以下是幾個語音翻譯的例子,我們會展示正確答案 (ground truth) 與模型的預測 (model prediction)。這些演示示例表明模型的預測捕捉到了正確答案的核心含義。
語音修補
在我們進行語音修補 (speech inpainting) 的實驗中,我們特別選取超過 2.5 秒的音頻片段作為后續處理的目標語音,并通過隨機選擇過程挑選出一段時長介于 0.8 至 1.2 秒的語音片段。然后我們對選出的片段進行掩碼,模擬語音修補任務中缺失或受損的部分。我們使用詞錯誤率 (WER) 和字符錯誤率 (CER) 作為評估受損片段修復程度的指標。
對 SpeechGen 生成的輸出與受損語音進行比較分析,我們的模型可以顯著重建口語詞匯,將 WER 從 41.68% 降低到 28.61%,將 CER 從 25.10% 降低到 10.75%,如下表所示。這意味著我們提出的方法能夠顯著提高語音重建的能力,最終促進語音輸出的準確性和可理解性。
下圖是一個展示樣例,上面的子圖是受損的語音,下面的子圖是 SpeechGen 產生的語音,可以看到,SpeechGen 很好地修復了受損的語音。
語音連續
我們將通過 LJSpeech 展示語音連續任務的實際應用。在訓練提示(prompt)期間,我們的策略是讓模型只看到片段的 seed segment,這個 seed segment 占據了語音總長度的部分比例,我們將其稱為條件比率(condition ratio, r),并讓模型繼續生成后續的語音。
以下是一些實例,黑色的文字代表種子片段(seed segment),紅色的文字則是 SpeechGen 生成的句子(這里的文字首先經過語音識別得到結果。在訓練和推理過程中,模型完全進行的是語音到語音的任務,且完全不接收任何文字信息)。不同的條件比率使 SpeechGen 能夠生成不同長度的語句以實現連貫性,并完成一句完整的話。從質量角度看,生成的句子與種子片段在語法上基本一致,并且語義相關。雖然,生成的語音仍然無法完美地傳達一個完整的意思。我們預期這個問題將在未來更強大的語音模型中得到解決。
不足與未來方向
語音語言模型和語音生成正處于蓬勃發展的階段,而我們的框架則提供了一種巧妙地利用強大語言模型進行語音生成的可能性。然而,這個框架仍有一些尚待完善之處,也有許多值得我們深入研究的問題。
1. 與基于文本的語言模型相比,語音語言模型目前還處于發展的初級階段。雖然我們提出的提示框架能激發語音語言模型做語音生成任務,但并不能達到卓越的性能。不過,隨著語音語言模型的不斷進步,比如從 GSLM 到 Unit mBART 的大轉身,提示的表現有了明顯的提升。特別是以前對 GSLM 具有挑戰性的任務,現在在 Unit mBART 下表現出更好的性能。我們預計未來會出現更多先進的語音語言模型嶄露頭角。
2. 超越內容信息:當前的語音語言模型并不能完全捕捉到說話者和情感信息,這給當前的語音提示框架在有效處理這些信息方面帶來了挑戰。為了克服這個限制,我們引入即插即用模塊,專門為框架注入說話者和情感信息。展望未來,我們預計未來的語音語言模型將整合和利用這些內容之外的信息,以提高性能并更好地處理語音生成任務中的說話者和情感相關方面。
3. 提示生成的可能性:對于提示生成,我們有著靈活多變的選擇,可以集成各種類型的指示,包括文本和圖像指示。想象一下,我們可以訓練一個神經網絡,讓它用圖像或文本作為輸入,而不是像本文中那樣使用訓練好的 embedding 當作提示。這個訓練好的網絡將成為提示生成器,為框架增添了多樣性。這樣的方式會讓提示生成變得更加有趣、更加豐富多彩。
結論
本文我們探索了使用提示來解鎖語音語言模型在各種生成任務中的性能。我們提出了一個名為 SpeechGen 的統一框架,該框架僅有約 10M 的可訓練參數。我們所提出的框架具有幾大特性,包括無需文本、多功能性、高效性、可轉移性和可負擔性。為了展示 SpeechGen 框架的能力,我們以 Unit mBART 為案例進行研究,并在三個不同的語音生成任務上進行實驗:語音翻譯、語音修復和語音延續。
當這篇論文提交到 arXiv 時,Google 提出了一種更先進的語音語言模型 ——SPECTRON,它為我們展示了語音語言模型在建模說話人和情感等信息的可能性。這無疑是一個令人興奮的消息,隨著先進語音語言模型的不斷提出,我們的統一框架具有巨大的潛力。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。