第6章 工程實踐——真實場景大不同
第6章 工程實踐——真實場景大不同
??通過前面章節的學習,相信讀者應該已經具備了一定NLP算法應用開發能力,雖然需要借助大語言模型,但這也是一種能力,畢竟用戶并不關心產品后面用了什么技術。一款產品或應用開發完成后,接下來就是要面向市場和客戶了,這中間有非常多的工作要做,即便我們只是為整套產品或服務提供一個接口,那也有許多要考慮的因素。
??在之前的“相似匹配”和“句詞分類”章節,我們在應用部分提及到一些開發應用要注意的事項,不過它們大多和具體的應用相關。本章我們將從整體角度介紹利用大語言模型接口開發一款要上線面向市場的應用或服務時應該考慮的內容。我們重點關注四個方面,首先是評測,它是能否上線的標準;然后是安全,它是上線不得不考慮的話題;最后是網絡請求,涉及一些網絡請求方面的設計和技巧。
6.1 評測:決定是否上線的標準
6.1.1 為什么評測
??在我們之前的章節中,除了“句詞分類”中的微調部分外,幾乎沒有涉及這個話題,但其實這非常重要。首先,我們來看為什么需要評測。在工程開發中,我們都知道有一個測試工程師的職位,主要的職責就是對產品的各個功能進行各種各樣的測試,以保證其功能正常、符合預期。我們在服務上線前往往也會對自己的接口進行壓測,看能否達到上線標準。
??對一個算法模型來說,這一步是類似的,只不過它要評測的是模型輸出的內容是否符合我們的預期目標。理論上來說,我們永遠都能找到模型預測錯誤的負例,所以實際上線后的結果也不可能百分之百正確。這是算法模型與其他功能開發不太一樣的地方,因為它本質上是一個根據已有數據學習到一種”策略“然后去預測新數據的過程。
??測試往往需要一批數據集,這批數據集應該盡量和真實場景接近,但一定不能包含在訓練數據中。評測一個模型其實就是評測它在”未見過“樣例上的效果。實際場景中,我們往往會在整個訓練數據集中先切出去一塊作為測試集,它的分布與訓練集是一致的。測試集的數量一般為整個數據集的20%,但這個不是必須的,可以視具體情況增加或減少測試集。對一個語言模型來說,比較重要的一點是,構造模型輸入和長度截斷的方法應和訓練集一致。通過模型得到輸出后,我們需要將輸出和標準答案進行對比,然后統計相關數據,最終得到評價指標。
6.1.2 NLU常用評測指標
??不同的任務往往會采用不同的評測指標,對于NLU任務來說,一般我們會使用精準率(precision,P)、召回率(recall,R)、\(F_1\)等指標衡量,它們一般可以通過表6-1所示的混淆矩陣計算得到。
表6-1 評測用混淆矩陣
| 真實情況 | 預測結果正例 | 預測結果負例 |
|---|---|---|
| 正例 | 真正例(true positive,TP) | 假負例(false negative,FN) |
| 負例 | 假正例(false positive,FP) | 真負例(true negative,TN) |
相關指標計算如式(6.1)、式(6.2)和式(6.3)所示。
??精準率和召回率在通常情況下是一種權衡關系,提高精準率會降低召回率,反過來也一樣,\(F_1\)值則綜合考慮了兩者。舉個例子,假設我們有一個垃圾郵件分類器,測試集數量為100,其中垃圾郵件(此處為正例)為20封,其余80封為正常郵件。模型預測結果為30封正例,但其實只有15封是真正的正例。也就是說,30封垃圾郵件中有15封被正確識別,另外15封其實是正常郵件,被誤識別為垃圾郵件;同時,模型預測結果為70封的負例中,有5封其實是垃圾郵件,未被模型正確識別。此時,混淆矩陣如表6-2所示。
表6-2 示例混淆矩陣
| 真實情況 | 預測結果正例=30 | 預測結果負例=70 |
|---|---|---|
| 正例=20 | TP=15 | FN=5 |
| 負例=80 | FP=15 | TN=65 |
對應的指標如式(6.4)、式(6.5)和式(6.6)所示。
??在這個例子中,應該20封垃圾郵件,我們找到了15封,召回率就是15/20,但是我們一共預測出了30封,精度就只有15/30。這個場景下,沒有識別出來垃圾郵件是可接受的,但如果把用戶的正常郵件識別為垃圾郵件,那用戶肯定有意見。所以,這就要保證精度很高,換句話說,只要是被標記為垃圾郵件的就一定是垃圾郵件(雖然可能會漏掉一些垃圾郵件)。這時候要求的概率閾值就比較高,比如95%,只有大于該閾值我們才判斷為垃圾郵件。這也就意味著,本來模型判斷為垃圾郵件的,有可能因為概率沒有達到95%而被標記為正常郵件。此時,精度非常高,但同時我們會漏掉很多垃圾郵件,導致召回率下降。反過來,如果召回率上升,意味著更多正常郵件可能被識別為垃圾郵件,精準率下降。我們給出調整后的混淆矩陣示例,如表6-3所示,讀者不妨自己重新計算指標。
表6-3 調整后的混淆矩陣
| 真實情況 | 預測結果正例=10 | 預測結果負例=70 |
|---|---|---|
| 正例=20 | TP=10 | FN=10 |
| 負例=80 | FP=0 | TN=80 |
??當然,如果我們的模型能夠讓這些被識別為負例的正例的概率提高到閾值之上,換句話說這時候的模型效果更好了(更加肯定垃圾郵件是垃圾郵件),那么精準率和召回率就會同時提升,\(F_1\)也會跟著提升。如果能達到這種理想情況那肯定是最好的,但現實往往需要權衡,畢竟有些樣本實在是太難辨認了。
??如果是多分類,則需要分別計算每一個類別的指標,然后綜合,有兩種綜合方法。
- macro方法:先計算每個類別的精準率和召回率,取平均后,再計算\(F_1\)值。
- micro方法:先計算混淆矩陣元素的平均,再計算精準率、召回率和\(F_1\)值。
??各個類別的重要性相對平衡時,可以使用macro方法;更關心總體性能而不是每個類別的性能時,可以使用micro方法。以類別樣本不均衡來舉例說明,如果你想要平等地看待每個樣本,無論類別均衡不均衡,所有樣本都一視同仁,可以選擇micro方法;但是如果你覺得類別是平等的,樣本多的和少的類別應該平等地看待,那可以選擇macro方法。
6.1.3 NLG常用評測指標
??在NLU中,無論是句子分類、Token分類還是其他匹配問題,一般都有一個標準答案。但NLG任務不太一樣,因為是生成式的,所以很難保證輸出的內容和標準答案一模一樣,更不用說,很多任務根本也沒有標準答案。生成式摘要、翻譯等任務往往都有參考答案,我們至少還有個比對的標準;但像生成式寫作、自由問答、對話這種,很多時候則需要針對性的設計評測指標。下面我們針對有參考答案和沒有參考答案的任務分別舉個和大語言模型相關的例子來進行說明。
??有參考答案的任務,我們以生成式文本摘要為例,它要求模型在給定一段文本后,能用幾句話概述這段文本的主要內容和思想,具體可以參考“文本生成”一章。因為這種任務有參考答案,所以我們經常采用相似度來衡量,即計算生成的摘要與參考答案之間的相似度。 這個做法和我們之前在”相似匹配“一章的做法一樣。或者,我們也可以單獨訓練一個二分類模型,判斷生成的內容和參考答案是否相似。更進一步,我們可以細化到Token粒度進行評估,從語義角度評估,常用的方法是BERTScore;從字面量角度評估,常用的方法是BLEU和ROUGE。
??BERTScore借助BERT這樣的預訓練模型計算Token的Embedding,然后計算生成的內容和參考答案Token之間的相似度,并進一步根據式(6.7)和式(6.8)計算精準率和召回率。
其中,\(x\)是參考答案的Token,\(\hat{x}\)是生成內容的Token。得到精準率和召回率,就可以進一步根據式(6.3)計算得到\(F_1\)值。我們舉個具體的例子,如下所示。
ref = "我愛偉大祖國"
hyp = "我愛祖國"
# emd_r shape => (1, 6, 768)
# emd_h shape => (1, 4, 768)
sim = emd_r @ emd_h.transpose(1, 2)
# sim shape => (1, 4, 6)
sim = array([
[[0.9830, 0.5148, 0.5112, 0.5310, 0.4482, 0.4427],
[0.4854, 0.9666, 0.9402, 0.5899, 0.8704, 0.3159],
[0.4613, 0.8755, 0.9184, 0.5819, 0.9397, 0.3576],
[0.4456, 0.3135, 0.3572, 0.5036, 0.3696, 0.9722]
]
])
??上面我們給定參考答案為ref,生成的內容為hyp,通過BERT等預訓練模型首先得到每個Token的向量表示(其中,1為批大小、768為向量維度、6和4為Token數),然后通過矩陣乘法得到相似數組為sim。接下來我們根據式(6.7)和式(6.8)分別計算精準率和召回率,如下所示。
p = 1/4 * sim.max(axis=2).sum()
r = 1/6 * sim.max(axis=1).sum()
??根據得到的精準率和召回率可以進一步計算\(F_1\)值。可以看出,精準率是\(\hat x\)中的每個Token匹配到\(x\)中的一個Token,即匹配到0.9830、0.9666、0.9387和0.9722四個值,對應的位置在\(x\)中的Token是“我”、“愛”、“祖”、“國”。而召回率是\(x\)中的每個Token匹配到\(\hat x\)中的一個Token,“偉大”兩個字沒有匹配到,但“偉”字分數還可以(0.9402),“大”的分數就只有0.5899了。總的來說,這種方法是一種從Token的語義角度來對比生成內容和參考答案的評測方法。
??和BERTScore不同的是,BLEU和ROUGE是按照字面量是否完全相同這樣去比較的,這里的字面量一般會選擇N-Gram,N一般都會選擇多個同時使用(比如1、2、3、4)。它們的算法有不少細節,但是如果從簡單直觀的角度理解,前者衡量有多少個生成內容的Gram出現在參考答案中,后者衡量多少個參考答案的Gram出現在生成內容中。前者類似精準率,后者則類似召回率,他們也可以使用式(6.3)計算得到一個\(F_1\)值。不過單獨使用也沒問題,這一般取決于具體的任務,比如文本摘要就常用ROUGE分數,因為相對而言,我們更加關注生成的摘要有沒有概括到給定文本,即有沒有覆蓋參考答案。而翻譯任務則常用BLEU分數,因為我們更加關注翻譯出來的內容是不是正確。
??以上這些評測方法都有很多現成的工具包,只要安裝后即可方便使用,讀者實際使用時可自行通過搜索引擎搜索。
??沒有參考答案的任務,我們以文案生成為例,這是非常適合大語言模型的一個任務。具體來說,就是給定大量商品和用戶屬性,讓模型針對該用戶就給定商品生成一段有說服力的銷售文案或推薦話術。這一類任務一般都需要人工進行評估,當然,我們也可以讓更好的大語言模型充當人工角色,我們要做的是設計評價指標和標準。這個標準一般視需求而定,但是一般會重點考慮以下因素。
- 準確性。生成的內容是否包含關鍵必要信息(比如商品名稱、價格等),這些信息是否有誤。
- 流暢性。生成的內容讀起來是否通順、合理、有邏輯。這里的流暢不僅指字面上的流暢,還包括語義上的流暢。
- 生動性。生成的內容是否有吸引力,能夠讓用戶產生購買欲。這個要求有點高,不過也并非不可能達到。
??我們可以針對這每一項進行打分,可以采用多人打分取平均;也可以用來對多個模型或服務商進行評測,選擇最合適的。需要再次強調的是,指標的設計務必要結合實際場景,綜合權衡成本和效果,不必追求非常全面。
6.2 安全:必須認真對待的話題
??安全是指模型生成的內容不應該包含任何偏見、敏感、風險等內容。這是產品的生命線,安全問題不過關,產品必然會被下線。所以,作為服務提供方,如果我們要使用生成式語言模型,當它在效果上被驗證可以達到要求后,接下來要重點關注的就是內容安全。
6.2.1 前后處理
??前處理和后處理是我們能想到的最直觀的解決方案。前處理是指將用戶的輸入傳遞給模型前先過一遍風險檢查,這里可以是一個模塊或外部接口(國內很多廠商都提供了類似接口)。如果用戶的輸入是有風險的,就直接返回預設好的回復,不再傳給模型接口生成回復。后處理是指對模型生成的內容進行風險檢查,如果檢測到風險內容,則將該內容屏蔽,或直接返回預設好的回復。
??前后處理我們可以只做一側,也可以兩側同時做,這中間的區別就是多一次接口調用。多一次接口調用意味著響應時間變得更長,同時這方面的費用也會增加。另外需要注意的是,如果是流式輸出,由于Token是一個一個吐出來的,可能需要在一句話結束時就對其進行風險檢查。
??關于風險檢查模塊或接口,可能只是簡單的一個布爾值,表示是否有風險,也可能會收到更加細致的信息,提示調用方是哪個方面的風險,以及置信度多高。我們可以根據實際需要選擇合適的接口,或自主研發相應的模塊。
6.2.2 提示詞
??由于大語言模型本身具備極強的理解能力,所以我們可以在每次輸入的提示詞中描述我們對輸出內容的要求。對于包含上下文的場景(比如之前介紹過的文檔問答),可以限制其必須基于給定上下文內容進行回復。另外,也可以在提示詞中限制輸出長度(注意,是提示詞中而不是接口參數),雖然有時候不太管用,但對于一個理解能力很好的語言模型來說還是有效果的。而且,限制長度還能節省費用。
??通過提示詞控制生成內容操作比較簡單,一般情況下都能達到要求,但依然有幾種情況需要特別注意。
- 給定的上下文本身就是風險內容。此時,給予上下文進行的回復自然也有可能是風險內容。
- 模型本身的知識是不完備的,它并不一定能理解所有的風險,尤其是每個不同用戶期望和認識的“風險”。此時,它認為自己的輸出沒問題,但在我們的場景下可能不合適。
- 不排除有人惡意引導模型輸出風險內容。這就如同網絡駭客一樣,大模型駭客也會利用模型缺陷對其進行攻擊。
??最后要強調的是,即使我們不考慮上面這幾種情況,也在提示詞中做了各種控制,模型依然有可能會輸出不期望的回復,尤其是類似temperature這種控制生成內容多樣性的參數設置的不太保守時。因此,根據實際需要有可能需要和其他方法結合起來一起使用。
6.2.3 可控生成
??在NLP的細分領域,有一個領域叫可控文本生成(controllable text generation,CTG),主要研究如何控制模型的輸出讓其更加可控,即輸出我們想要的,不輸出我們不想要的。可控文本生成的方法很多,由于基本都涉及到模型訓練或微調,因此我們不過多深入介紹,感興趣的讀者可以查找并閱讀相關文獻。簡單來看,可以將可控文本生成分成三類。
- 使用控制Token。一般會在文本開頭增加一個控制生成屬性的Token,這里的屬性可以是情感傾向、主題、風格等等。基于提示詞的控制生成也可以算作這一類型,尤其是當模型經過帶控制的提示詞微調訓練后。
- 使用控制模型。主要體現在生成過程中,使用一個或多個屬性分類器對生成過程進行引導。一般做法是保持語言模型的參數不變,更新分類器的參數使其能夠判別不同屬性,或者區分想要的內容和不想要的內容。生成時使用分類器作為條件,影響語言模型輸出時Token的概率分布。
- 使用反饋控制。典型的代表就是我們第一章“基礎科普”中介紹過的RLHF,比如InstructGPT將有幫助、真實性和無害性作為反饋標準影響模型。此外,類似自訓練、自學習等方法也可以看作從反饋中學習,這里的反饋可能來自人類,也可能來自模型。這類方法從根本上改變了模型,是徹底的控制。
??對于安全問題,雖然本節內容已經給出了不少防范措施,但我們仍然不建議將大模型直接面向用戶。我們實在難以保證其中不出差錯,而一旦出差錯帶來的風險是巨大的。同時,我們也建議在設計上考慮以下一些輔助方案。
- 建議增加消息撤回機制。即使有發送一些風險內容,但只要能及時撤回,在一定程度上也能將風險降到最低。
- 建議對用戶賬號進行嚴格管控,如果有用戶觸發風險內容,應及時予以關注。對刻意發送或誘導模型輸出風險內容的賬號,應馬上對其進行限制。
- 留存所有的對話和消息記錄,以備事后查驗。
??考慮到服務商可能會提供一些關于安全檢查的最佳實踐,也建議讀者在開發前仔細閱讀,盡量遵循文檔進行處理。
6.3 網絡:接口調用并不總是成功
??由于本書涉及到的大模型都是通過接口使用的,這就避免不了網絡請求。本節我們主要介紹和網絡請求相關的實踐。
6.3.1 失敗
??網絡請求失敗是常見的情況,只要我們的服務代碼中有需要通過網絡請求第三方接口的,都應該關注這個問題。對于網絡請求失敗的情況,常用的解決方法是重試。是的,當本次接口調用失敗或超時時,服務端應再次發起請求。不過重試并不是簡單地只要失敗就重新發起請求,這里有非常多的細節需要考慮。
- 哪些情況需要重試?當我們在調用接口時,并不是對所有的失敗均進行重試,比如訪問的令牌到期,服務端返回未鑒權錯誤,此時應該去調用鑒權接口重新獲取令牌,而不是一直重試。一般來說,我們可以對因網絡超時、服務端錯誤等導致的失敗請求進行重試。
- 多久重試一次?連續的重試肯定是不合理的,這可能會導致服務端響應更慢。一般我們會隨時間增加而相應地增加重試間隔時間,比較常用的是指數級增加重試間隔時間,比如第一次間隔2秒,第二次4秒,第三次8秒,以此類推。
- 重試多少次停止?可以肯定的是,我們不可能一直重試下去,當一個接口重試幾次后依然失敗,那可能是服務或網絡已經發生了故障。這種情況下,試多少次都是沒用的。這個可以根據實際場景選擇相應配置,一般最多重試3次即可。
??如果經過以上策略重試后依然失敗,此時我們一般會拋出異常,有可能同時向客戶端返回預先指定的結果,或者回調某個專門處理接口調用失敗的函數。這些一般都需要根據實際需要進行設計。
??重試策略看起來還不錯,不過讓我們考慮一種特殊情況。假設第三方接口服務真的掛了或網絡真的發生了故障,此時,客戶端一直在發請求,而服務端則在重試策略下不停重試,結果自然是每個請求都達到重試上限并最終拋出異常。
??對于這種情況,我們一般會加入熔斷機制,當失敗次數達到某個閾值時,將此服務進行熔斷,直接返回預設好的響應或者干脆拒絕請求。這樣,后面的請求就不會再調用第三方接口了。實際中,我們往往會返回一個簡化版本的處理,比如模型服務掛了,則返回規則結果。這也被稱為服務降級。
??熔斷也經常主動用在高峰限流場景下,當某個時刻請求突然暴增導致資源不夠用時,可以有規劃地對一些不重要的服務進行熔斷。比如做秒殺活動時,為了保證短時間涌入的大量請求能夠得到響應,可以對商品查詢、篩選等功能返回緩存或上一次查詢的結果。
??熔斷機制尤其適合一個接口包含多個請求源,最終返回整合結果的情況。此時,熔斷有問題的請求源,可以保證整個接口依然是可用的。熔斷一段時間后,可以嘗試自動恢復,先放行一定數量請求,如果響應成功則關閉熔斷器,否則繼續對有問題的請求源保持熔斷狀態。
??最后,我們強烈建議對服務增加可視化的配置和監控,同時啟用告警功能,當任一服務出現問題時,能夠及時進行干預 ,保證服務穩定可用。
6.3.2 延遲
??延遲是指接口沒有在給定時間內給出響應,但又不會超時失敗的情況。延遲比失敗更加常見,它不光和當時的網絡狀況有關,也和網絡配置(比如帶寬、是否有專用網絡等)有關,而且還和接口功能的復雜度、請求和返回的數據量等非網絡因素有關。對于大語言模型接口的延遲,我們給出以下實踐建議。
??首先,建議針對不同的需求選擇不同規模的模型。以OpenAI為例,它提供了多個不同版本的模型,讀者可以根據任務難度選擇適當的模型。越復雜的模型,一般響應速度也越慢,耗時較長,而且價格還貴。總之,還是我們一直強調的觀點,工具只是手段,不是目的,能用簡單、低成本方案解決的就不要用復雜的。
??其次,一般大語言模型接口都會提供“停止序列”參數,讀者可以關注并配置該參數,以便及時結束模型的輸出。同時,我們在本章“安全”一節也提到過,可以在提示詞中限制輸出的Token數,讓答案盡量簡短。模型輸出的內容越少,響應時間就越快,延遲越低。當然,接口的請求體(request body)也不應該過大,太長的上下文不僅會增加傳輸時間,而且可能也會增加費用(以OpenAI為例,提示詞也是要計費的)。這可能需要一些語義匹配或信息壓縮提煉技術,每次只傳輸最相關的上下文,“相似匹配”和“句詞分類”這兩章內容均對此有所涉及,此處不再展開討論。
??第三,針對部分場景應用,可以使用流式輸出。流式輸出體驗比較好,用戶很少會感到延遲,因為模型接口一旦接收到請求就開始響應。目前比較常用的服務端方案包括SSE(server-sent events)和WebSocket。SSE是一種基于HTTP的單向通信技術,允許服務器向客戶端發送持續的事件流。WebSocket是雙工通信技術,允許客戶端和服務器建立雙向通信通道。SSE更加適合服務端向客戶端持續發送數據的情況,而WebSocket則更加適合客戶端和服務器實時交互(如對話)的情況。
??第四,考慮使用緩存。這在某些場景下是可以的,比如問答類應用。事實上,只要每次交互的上下文不是動態變化的,都可以考慮使用緩存。
??以上是關于降低大語言模型接口延遲的通用建議,在實際場景中如果遇到延遲問題,建議讀者仔細分析服務代碼,找到關鍵瓶頸進行解決。尤其是當接口中包含比較多的網絡請求時,任何一個請求都可能成為瓶頸,比如當歷史文檔非常龐大時,使用向量庫或數據庫查詢相關上下文也會比較耗時。
6.3.3 擴展
??本書迄今為止一直未提到高并發的場景,也默認用戶請求不太多,單個賬號就可以提供服務,這在現實中是存在的,尤其是新產品或服務還沒有很多用戶的時候。但一款成功的商業應用用戶一定不會少,并發也比較高。此時,對接口服務進行擴展就是我們重點要考慮的。需要說明的是,我們這里主要指橫向擴展。
??當我們決定要對服務進行擴展時,首先要做的是了解基本情況和需求,包括日均調用次數、日調用峰值、平均并發數、最大并發數、期望平均響應時長、是否可以使用緩存等。除此之外,還需要了解大模型服務商的相關政策,比如OpenAI對接口調用就有限制,不同模型、不同類型賬號限制都不一樣。值得說明的是,服務提供商一般都會提供關于擴展的最佳實踐,這也是重點要掌握的信息。這些對于我們接下來的方案至關重要,我們期望能夠以最低的成本滿足業務或用戶需要。
??了解完基本情況,我們就可以對需要的資源和成本進行大致估算。資源主要就是賬號,建議最好有20%以上的冗余。成本估算可以通過提示詞和響應的平均長度,以及日均調用次數進行簡單測算。
??對于賬號資源,我們一般需要構建一個資源池進行統一管理,當需要調用時,先從資源池中選擇一個可用的賬號完成本次調用服務。如果需要的賬號比較多,或者預估未來調用會增長時,建議一開始就把資源池模塊寫好,方便日后自由擴展。
??資源池模塊應該具備基本的添加、刪除、修改功能,建議構建適合企業內部的自定義賬號體系,將真實的賬號綁定在自定義賬號上。這樣做的好處是便于管理,而且還方便支持多個不同的大模型服務商。資源池模塊還有一項基本功能是報表統計功能,包括不同維度統計的調用方、調用次數、失敗率、費用等內容。
??當然,對資源池來說,最重要的功能還是資源調度,簡單來說就是如何為每一次調用分配賬號。我們應該能夠支持多種不同的策略進行調度,比如簡單的隨機選擇賬號、按失敗率從低到高、按不同功能使用不同服務商等。需要特別注意的是,不要把任何業務相關的東西參雜進來,資源池只負責管理資源。
??大語言模型(或其他模型服務)接口,往往還支持批量(batch)模式,也就是一次發送多條請求,同時獲取這些請求的響應。在并發比較高、響應時間要求又不那么高的情況下非常適合。如果是流式輸出,響應時間這一項可以忽略,此時建議使用批量模式。
??使用批量模式,需要在用戶請求和請求大模型服務商接口之間做一層處理,合并用戶請求,批量一次向大模型服務商發起請求,收到反饋后分發到對應的用戶請求響應上。具體地,我們可以維護一個隊列和最小請求間隔時間,在最小請求間隔時間內,固定窗口大小的請求同時出隊進行批量請求。理論上批量大小是不限制的(也意味著我們可以不固定窗口大小),但建議最好選擇一個合適的最大窗口值,既能達到最大的吞吐量,又不至于有太高的延遲。注意,這里是“最大值”,因為當并發很低時,隊列內不一定有最大窗口值數量的請求。
??如果可以的話,每個窗口的大小最好是2的指數(即1、2、4、8、16、32等),因為服務器一般都是GPU,這樣的處理能提升一些效率。當然,大部分服務商應該已經在服務內部做了類似的優化,批大小即使不是2的指數,性能差別應該也幾乎可以忽略。
??剛剛我們提到了使用隊列,事實上,我們強烈建議將服務端寫成隊列模式,這樣做的好處是,當資源不足時,服務依然是可用的,除非資源和請求數量嚴重不均衡,這就意味著我們不需要按照并發的峰值去申請資源。如果對服務的響應時長要求沒那么高,用戶能接受偶爾出現的一定時間的等待,這種方式可以極大地節約資源。
6.4 本章小結
??開發一個演示用的小應用和真正可商用的服務落地之間有非常多的事情要處理。本章我們從評測開始介紹,了解到一個模型服務先得“能用”然后才能討論上線,我們介紹了針對不同任務的評測方法。接著我們強調了安全對一個大模型相關應用或服務的重要性,并給出了一些處理方案。最后,針對大模型服務接口調用,我們給出了如何更好地構建一個穩定、可靠服務的建議。本章內容都針對真實場景下的應用和服務構建,我們給出了一些工程實踐經驗,但讀者應該清楚,實際中遇到的問題可能遠比我們提到的更多。而且,我們也未涉及超大規模分布式處理,這是有意為之的。如果你或你所在的企業已經達到了這種程度,那時一定會有精通算法工程和架構的同事專門負責。總之,真實場景下的工程實踐需要考慮更多因素,需要更加仔細地設計,處處充滿權衡的藝術。

【轉載】https://github.com/datawhalechina/hugging-llm
浙公網安備 33010602011771號