語言模型在軟件開發(fā)的應(yīng)用與挑戰(zhàn)。
語言模型正在變革軟件開發(fā)流程的各個環(huán)節(jié),包括代碼的生成、編輯、測試、調(diào)試等。在開發(fā)和訓(xùn)練代碼語言模型時,人們需要統(tǒng)一的收集清理數(shù)據(jù)、訓(xùn)練模型、更新調(diào)整等。因此,我們預(yù)期,針對模型訓(xùn)練的分析技術(shù)將成為新的一層架構(gòu)來回答“模型是如何產(chǎn)生某個預(yù)測的”、“模型預(yù)測是如何逐漸訓(xùn)練得到的”、以及“我們應(yīng)該怎么做去修改和增強某個預(yù)測”等問題。
在今年 8 月份舉辦的 AICon 全球人工智能開發(fā)與應(yīng)用大會上,上海交通大學(xué)計算機系副教授林云做了專題演講分享“語言模型驅(qū)動的軟件工具思考:可解釋與可溯源”,深入探討了如何分析模型、追溯訓(xùn)練樣本,并構(gòu)建數(shù)字孿生環(huán)境來測試代碼編輯模型,最后展望了未來大模型對軟件開發(fā)范式的影響。
以下是演講實錄(經(jīng) InfoQ 進行不改變原意的編輯整理)。
非常榮幸能夠在這里與大家分享我們團隊的最新研究成果。我們一直在探索如何利用語言模型來生成代碼,并深入理解這些模型背后的原理。目前,語言模型在軟件工程領(lǐng)域的應(yīng)用日益廣泛,已經(jīng)逐步介入到設(shè)計、編程、測試和調(diào)試等多個環(huán)節(jié)。我們的研究團隊致力于將語言模型融入這些環(huán)節(jié)中。
在語言模型出現(xiàn)之前,我們已經(jīng)有了傳統(tǒng)的代碼編輯的技術(shù),但語言模型的介入使得編輯過程變得更加智能化,我們稱之為“生成式編輯”。它能夠輔助我們完成整個代碼棧的工作。接下來,我會介紹我們與字節(jié)跳動合作的一個項目,該項目旨在自動定位代碼編輯的位置,并在特定行生成所需的編輯內(nèi)容。
在語言模型生成代碼之前,我們也在解決測試用例生成的問題。按照傳統(tǒng)方式,我們會將測試用例的生成視為一個約束求解問題,關(guān)注如何實現(xiàn)分支覆蓋和路徑覆蓋。但語言模型的出現(xiàn)讓我們開始思考,我們是否可以實現(xiàn)需求覆蓋,即不僅僅覆蓋 特定的分支,而是結(jié)合需求和分支,生成更符合項目特點的測試用例。
此外,我們也在探索如何讓語言模型自動調(diào)試代碼。過去,開發(fā)者常常自嘲說,自己寫的 bug 含淚也要修復(fù)完。但現(xiàn)在,也許我們要含著淚修復(fù) AI 幫我們寫的 bug.AI 時代的代碼調(diào)試問題也許是一個新的挑戰(zhàn)。因此,我們也希望有新的智能化技術(shù)能夠幫助開發(fā)者發(fā)現(xiàn)并修復(fù) bug.在這項工作中,我們的目標(biāo)是將調(diào)試問題轉(zhuǎn)化為在代碼執(zhí)行軌跡上找到第一個出錯的步驟,然后讓語言模型在這個軌跡上通過交互不斷定位錯誤,并指導(dǎo)開發(fā)者了解錯誤是如何發(fā)生的。
訓(xùn)練軟件工程語言模型的“套路”
當(dāng)我們深入研究語言模型在軟件工程中的應(yīng)用時,我們逐漸發(fā)現(xiàn)了一個反復(fù)出現(xiàn)的模式,或者稱之為“套路”。在這個套路中,我們是這么做的。首先,我們需要收集和清洗來自 Git、JIRA、Jenkins 等軟件工具的數(shù)據(jù),將它們轉(zhuǎn)換成訓(xùn)練數(shù)據(jù)集。這些數(shù)據(jù)集隨后被用來訓(xùn)練代碼模型,最終這些模型被集成到集成開發(fā)環(huán)境(IDE)中。
無論是進行測試生成、調(diào)試、代碼生成還是測試用例生成,我們通常會遵循這個方式。但隨著時間的推移,我們意識到,盡管這個套路在業(yè)界得到了廣泛應(yīng)用,但在實際應(yīng)用中卻并不簡單。例如,當(dāng)我們訓(xùn)練出一個模型后,我們首先想知道的是,模型為什么會做出這樣的預(yù)測。畢竟,模型本質(zhì)上是將大量的數(shù)據(jù)集壓縮編碼到代碼中,然后利用其泛化能力進行各種生成任務(wù)。
那模型的預(yù)測是如何產(chǎn)生的?我們知道,模型并非一蹴而就,而是經(jīng)過數(shù)小時甚至數(shù)天的訓(xùn)練,經(jīng)過多次迭代才得到的。因此,我們想要了解模型預(yù)測的具體生成過程。最終,我們希望能夠提出一些方案,自動矯正模型中不符合我們期望的行為。
上述套路解決的是"AI for SE",即我們提出了 AI 解決方案來幫助程序員完成任務(wù)。但隨著 AI 解決方案的增多,我們發(fā)現(xiàn)需要一個"SE for AI for SE"的基礎(chǔ)框架,以支持和管理這些 AI 解決方案。
案例研究: 交互式代碼編輯 (CoEdPilot)
在具體介紹上述框架解決思路前,我想先跟大家介紹下我們與字節(jié)跳動合作的一個研究案例,這個案例恰恰符合我們之前討論的“套路”。我們稱這個過程為“編代碼、編輯定位”。在現(xiàn)代代碼倉庫中,編寫代碼并不總像 Copilot 那樣,給出一個注釋后自動生成十幾行代碼。更多的時候,我們面臨的是編輯任務(wù):根據(jù)需求修改某一行代碼,刪除一行,或者更改一行中的幾個字符串。這種編輯往往是跨文件的,一次編輯可能會影響到多個文件。
在我們的案例中,我們首先關(guān)注的是編輯定位問題。當(dāng)出現(xiàn)一個需求或者一個編輯請求時,我們希望能夠迅速定位這個編輯在整個項目中如何傳播。接下來,我們想要解決的是編輯生成問題。一旦我們知道某一行需要修改,我們就想進一步推薦出這一行具體應(yīng)該改成什么樣子。我們希望通過人機交互來實現(xiàn)這一點,利用人的反饋來進一步推薦下一輪的編輯定位和編輯生成。
我們的工作目前集中在開發(fā)一個 Visual Studio Code 插件上,這個插件旨在幫助用戶根據(jù)輸入的需求自動定位代碼修改的位置。用戶一開始會輸入需求,插件會生成一個定位提示,顯示整個文件中可能需要修改的地方。在這個提示中,紅色標(biāo)記代表可能需要修改的地方,而綠色標(biāo)記則表示可能需要添加內(nèi)容的位置。
當(dāng)用戶選擇某個特定的位置后,插件會通過一個差異比較(DIFF)視圖來展示這一行代碼可能的修改方式。用戶可以從多個選項中選擇。一旦用戶接受了某些建議或者拒絕了某些建議,這些反饋就會被收集起來,作為新一輪輸入和迭代的數(shù)據(jù)。
這個插件的核心思想在于,我們通過收集代碼提交的信息來訓(xùn)練模型。每個提交通常包含多個代碼修改,這些修改也被一并收集。通過訓(xùn)練,模型能夠在整個項目中滑動窗口,識別出需要修改的地方,并推薦出具體的修改內(nèi)容。
代碼編輯的基本設(shè)計思路
我們的基本設(shè)計思路是將代碼編輯任務(wù)分解為幾個小模型來實現(xiàn),避免直接將整個代碼庫喂給一個大模型,這樣做的原因主要是為了減輕模型的計算負(fù)擔(dān),包含兩個核心部分:任務(wù)分解和矯正反饋。
首先,任務(wù)分解的目標(biāo)是將一個大模型拆分成幾個小模型,這樣可以減少模型的輸入量。例如,輸入 1 萬行代碼與輸入 30 行代碼的效果是有很大差異的。我們使用三到四個小模型來完成這個任務(wù)。
其次,我們希望通過與用戶的交互來實現(xiàn)矯正反饋。具體來說,我們首先使用一個小模型,通過滑動窗口來預(yù)測文件中可能需要修改的位置。核心思想是比較兩段代碼的語義相似度和依賴關(guān)系,以判斷它們是否會產(chǎn)生協(xié)同變化。在得到這些信息后,我們使用另一個小模型,將問題轉(zhuǎn)化為一個分類問題。給定一個滑動窗口,窗口中有多行代碼,我們根據(jù)之前的編輯來預(yù)測每一行可能發(fā)生的編輯類型。這樣,我們不需要處理一個很大的窗口,只需要對每一行進行分類即可。訓(xùn)練模式采用的是指令微調(diào),即給定一個指令(如替換或保留),然后讓模型預(yù)測每一行的編輯類型。得到編輯類型后,我們使用另一個基于 Transformer 的編碼器 - 解碼器模型來生成具體的內(nèi)容。當(dāng)我們確定某一行需要添加或替換時,就讓這個 Transformer 生成相應(yīng)的內(nèi)容。這樣,我們就大大減少了活動窗口的大小。
最后,我們使用另一個模型來學(xué)習(xí)之前的編輯,將之前的編輯作為 Transformer 輸入和反饋設(shè)計的一部分。通過這種方式,我們在定位的準(zhǔn)確性和生成內(nèi)容的準(zhǔn)確性上都達到了一個可接受的程度。
哪些訓(xùn)練數(shù)據(jù)影響了這次預(yù)測?
當(dāng)我們構(gòu)建并訓(xùn)練了代碼模型后,我們希望它能夠自動定位代碼編輯的需求,并最終集成到 IDE 中。然而,我們發(fā)現(xiàn)在某些情況下,模型的表現(xiàn)并沒有達到我們的預(yù)期。為了解決這個問題,我們首先需要進行訓(xùn)練歸因分析,以了解為什么模型會做出特定的預(yù)測。
我們想要回答的核心問題是:為什么模型認(rèn)為某行代碼需要修改,或者需要插入代碼?為了解決這個問題,我們從三個角度進行思考:樣本歸因、表征歸因和仿真驗證。
歸因問題在機器學(xué)習(xí)領(lǐng)域是一個經(jīng)典問題。我們想要了解的是,哪些訓(xùn)練數(shù)據(jù)真正影響了模型的預(yù)測。當(dāng)我們面對一個嚴(yán)格的數(shù)學(xué)問題陳述時,我們可以這樣表述問題:給定一個訓(xùn)練樣本 Zi,如果我們對這個樣本進行權(quán)重調(diào)整(增加或減少 ?),模型會發(fā)生什么變化?因為模型是在看到數(shù)據(jù)后才進行神經(jīng)元調(diào)整的,所以我們想要了解哪些預(yù)測相關(guān)的神經(jīng)元是由哪些數(shù)據(jù)調(diào)整的。
在數(shù)學(xué)層面上,這個問題可以通過一個公式來描述。我們有一個測試集 _X_test 和一個訓(xùn)練集 _X_train.我們想要了解 _X_train 和 _X_test 之間的關(guān)系。如果我們發(fā)現(xiàn) _X_train 和 _X_test 的值是一個大的正數(shù),這意味著如果我們更多地訓(xùn)練 _X_train 這個樣本,模型在預(yù)測 _X_test 這個樣本時的表現(xiàn)會變得更好。相反,如果 _X_train 和 _X_test 的值是一個大的負(fù)數(shù),比如說 -0.9,這意味著如果我們更多地訓(xùn)練 _X_train 這個樣本,_X_test 這個測試樣本的預(yù)測會變得更糟,說明這兩個樣本之間存在矛盾。如果 _X_train 和 _X_test 的影響因素是 0,那就意味著無論我們增加還是減少對 _X_train 的訓(xùn)練,對 _X_test 的預(yù)測都沒有影響。
要理解模型預(yù)測的影響關(guān)系,我們可以從理論上推導(dǎo)出三個決定性因素。首先,模型對測試樣本 _X_test 的擬合程度會影響其預(yù)測。每個測試樣本都有其損失函數(shù)和標(biāo)簽,模型在擬合這些樣本時會朝某個方向移動,這個方向反映了參數(shù)空間的調(diào)整。
其次,模型對訓(xùn)練樣本 _X_train 的擬合方向也是一個重要因素。如果模型在擬合 _X_test 和 _X_train 時方向一致,那么它們之間會有正向影響;如果方向相反,則會產(chǎn)生負(fù)向影響;如果方向的夾角為零,則它們之間沒有影響。
最后,Hessian 矩陣及其逆矩陣代表了所有樣本之間的交互效應(yīng)。Hessian 矩陣是損失函數(shù)對所有參數(shù)的二階導(dǎo)數(shù)的矩陣,其逆矩陣反映了樣本間的相互作用。然而,計算 Hessian 矩陣的逆在實際中是非常困難的,尤其是當(dāng)模型參數(shù)達到百萬或千萬級別時。為了解決這個問題,我們提出了一種改進的想法,即通過多次變異模型來模擬 Hessian 矩陣的效果。我們可以通過在參數(shù)空間上進行抽樣來模擬 Hessian 矩陣,觀察模型在多次變異后對訓(xùn)練樣本和測試樣本的影響。如果變異后的模型在訓(xùn)練樣本和測試樣本上都顯示出對抗性或正相關(guān) / 負(fù)相關(guān)的影響,那么我們就可以認(rèn)為它們之間存在相互影響。
通過這種技術(shù),我們發(fā)現(xiàn)模型預(yù)測中的一些問題并不總是源于模型架構(gòu),而是可能源自訓(xùn)練數(shù)據(jù)集本身。例如,在開源數(shù)據(jù)集上運行模型時,我們可能會發(fā)現(xiàn)模型的某些錯誤預(yù)測實際上可以歸因于訓(xùn)練數(shù)據(jù)的標(biāo)注問題。例如,在服裝分類任務(wù)中,開源數(shù)據(jù)集可能會將非常相似的服裝款式標(biāo)注為不同的類別,而人類觀察者可能會認(rèn)為這些款式是相近的。這種令人困惑的標(biāo)注會影響模型預(yù)測的性能。為此我們設(shè)計了新的影響函數(shù)在很多開源數(shù)據(jù)集上找到了很多標(biāo)注 bug, 并發(fā)表在了 NeurIPS’22 的會議論文《Debugging and Explaining Metric Learning Approaches: An Influence FunctionBased Perspective》上。
將影響函數(shù)應(yīng)用于代碼編輯生成任務(wù)
我們將影響函數(shù)應(yīng)用于代碼編輯生成任務(wù)中,以評估每個預(yù)測背后的有益和有害訓(xùn)練樣本。有益的訓(xùn)練樣本是指那些通過增加訓(xùn)練量可以提升特定測試樣本表現(xiàn)的樣本,而有害樣本則是指增加訓(xùn)練量會降低某些測試樣本表現(xiàn)的樣本。我們發(fā)現(xiàn),對于任何一個測試樣本,有害樣本和有益樣本的數(shù)量通常都非常少。
通過這種方式,我們可以發(fā)現(xiàn)模型預(yù)測的具體影響。例如,當(dāng)我們的模型預(yù)測需要將代碼中的版本號從 0.01 更改為 0.02 時,使用影響函數(shù)進行歸因分析,我們可以看到與數(shù)字變動相關(guān)的訓(xùn)練樣本,這與模型的表征空間是相關(guān)的。
在函數(shù)調(diào)用中添加參數(shù)時,模型應(yīng)該定位到代碼窗口中的某一行,并預(yù)測需要替換的行以添加類似的參數(shù)。對于這樣的測試樣本,模型的預(yù)測和歸因分析將揭示出形狀相似的代碼標(biāo)注,指出在語法上需要添加子節(jié)點。這種歸因分析有助于我們理解哪些訓(xùn)練樣本對預(yù)測有重大貢獻,從而發(fā)現(xiàn)可能存在的標(biāo)注問題。例如,我們可能會發(fā)現(xiàn)原本認(rèn)為相似的代碼樣本實際上在語義上有很大差異,這表明我們的標(biāo)注可能存在問題,或者標(biāo)注的語義不夠豐富。
此外,在代碼編輯中,commit message 的質(zhì)量非常重要。相似的 commit 或者過長的 commit 可能會導(dǎo)致信息量減少,從而形成打架效應(yīng)。這意味著,為了提高代碼編輯的質(zhì)量,我們需要確保 commit message 的書寫質(zhì)量非常高,避免使用過于冗長或含糊不清的描述。
我們覺得未來可能會有好幾個方向可以嘗試,第一是通過影響函數(shù),可以幫助我們?nèi)プ鰯?shù)據(jù)分析,判斷到底哪些是臟數(shù)據(jù),或者說非預(yù)期的訓(xùn)練數(shù)據(jù)產(chǎn)生了壞的影響。第二個是當(dāng)產(chǎn)生壞的影響之后,有可能我們需要對整個數(shù)據(jù)進行重標(biāo)注,所以我們也在嘗試在訓(xùn)練過程當(dāng)中動態(tài)地去更新某一些標(biāo)注,因為我們永遠(yuǎn)不能保證人標(biāo)的東西就一定是對的,或者說預(yù)期的標(biāo)注就是我們想要的。最后是想去觀測,如果有些訓(xùn)練樣本有非常高的互影響的話,就意味著整個訓(xùn)練數(shù)據(jù)集有可能是冗余的。
我們大量地在收集數(shù)據(jù)集,但是數(shù)據(jù)集過大真的是件好事嗎?對此我們其實也是存疑的,我們有沒有可能利用一個小但質(zhì)量非常高的數(shù)據(jù)集產(chǎn)出一樣的效果?這對模型訓(xùn)練效率的影響其實是非常大的。
表征歸因
在討論完樣本歸因之后,我們來談?wù)劚碚鳉w因。表征歸因是深度學(xué)習(xí)的核心,因為深度學(xué)習(xí)本質(zhì)上是表征學(xué)習(xí)。無論是處理圖像、聲音還是文本,深度學(xué)習(xí)的目標(biāo)是將這些輸入轉(zhuǎn)換成向量,然后進行矩陣運算。
以文本為例,深度學(xué)習(xí)模型需要將每個單詞映射到向量空間中。在這個空間里,語義相近的詞匯(如“男孩”和“女孩”)的表征應(yīng)該彼此接近,而語義相距較遠(yuǎn)的詞匯(如“貓”和“狗”)的表征則應(yīng)該相距較遠(yuǎn)。在自然語言處理(NLP)中,我們希望模型能夠通過單詞的 embedding 來捕捉這種語義關(guān)系。
如果我們能夠訓(xùn)練模型,使其對每個樣本或單詞的表征具有這樣的語義效果,那么模型就能逐漸發(fā)展出接近人類的預(yù)測能力,從而能夠進行更自然的交流。然而,我們面臨的一個主要挑戰(zhàn)是,真實的表征空間可能是 512 維、1024 維或 768 維,而人類很難直觀理解高維空間中的變化。模型訓(xùn)練初期,樣本的表征通常是隨機分布在高維空間中的。隨著訓(xùn)練的進行,這些表征會逐漸變化,最終形成一種分布,反映出人類的理解能力。我們可以將模型訓(xùn)練過程視為樣本表征在高維空間中的運動。一開始,這些表征是無序的,但最終會形成一個有結(jié)構(gòu)的分布。我們希望能夠在二維空間中幫助人們理解這些表征是如何變化的,例如,貓和狗的表征是否真的接近。這將能為提供巨大的信息量,幫助我們更好地理解和改進模型。
在過去的工作中,我們的目標(biāo)是將模型的訓(xùn)練過程可視化。模型訓(xùn)練本質(zhì)上是樣本表征在高維空間中的變化過程,但由于這些維度通常是數(shù)百甚至數(shù)千維,這使得直觀理解變得困難。因此,我們希望能夠?qū)⑦@一過程投影到二維空間,使人們能夠直觀地看到,例如,兩只貓的樣本表征如何逐漸靠近,而貓和狗的樣本表征如何逐漸遠(yuǎn)離。將訓(xùn)練過程轉(zhuǎn)化為二維動畫后,我們不僅可以觀察到模型在表征空間中的運動,而且還可以與動畫進行交互和分析。
在模型訓(xùn)練過程中,我們通過可視化技術(shù)觀察到了一個有趣的現(xiàn)象,即干凈數(shù)據(jù)和噪音數(shù)據(jù)在表征空間中的運動軌跡存在顯著差異。例如,在某個訓(xùn)練階段,我們可以將橘黃色的點視為干凈數(shù)據(jù),而黑色的點代表噪音數(shù)據(jù)。
如果我們觀察到最后一個訓(xùn)練階段,比如模型學(xué)習(xí)"apple"這個詞匯時,會發(fā)現(xiàn)無論是干凈數(shù)據(jù)還是噪音數(shù)據(jù),模型最終都能達到很高的準(zhǔn)確度。然而,它們在訓(xùn)練過程中的運動軌跡卻大相徑庭。干凈數(shù)據(jù)在經(jīng)過一兩次訓(xùn)練迭代后,很快就能定位到它應(yīng)該在的區(qū)域。相比之下,噪音數(shù)據(jù)則表現(xiàn)得像“釘子戶”,在初始位置上停留很長時間,直到訓(xùn)練的后期,由于模型內(nèi)部的某種“拉力”作用,它們才最終被拉回到適當(dāng)?shù)奈恢谩?/p>
這種現(xiàn)象不僅揭示了噪音數(shù)據(jù)在訓(xùn)練過程中的頑固性,也為我們提供了一種新的思路,即如何在訓(xùn)練過程中有效地去除噪音。通過觀察數(shù)據(jù)在表征空間中的運動,我們可以識別出那些不易被模型正確學(xué)習(xí)的噪音樣本,并采取相應(yīng)措施。
回到代碼任務(wù)本身,我們注意到基于檢索的生成(RAG)是一個非常熱門的領(lǐng)域。在這種情況下,檢索能力變得至關(guān)重要。在這個語義空間中,我們可以觀察到代碼表征的分布情況,同樣也可以觀察到代碼描述的表征分布。這種映射允許我們在給定一個自然語言描述時,在整個語義空間中搜索與其最接近的代碼表征。這樣,與描述最相關(guān)的代碼就可以被檢索出來。
基本上,這是一種在高維空間中進行代碼檢索的方法。通過這種方式,我們可以根據(jù)代碼的自然語言描述快速找到相應(yīng)的代碼實現(xiàn),從而提高代碼檢索的效率和準(zhǔn)確性。這種方法利用了深度學(xué)習(xí)模型的能力,將文本描述和代碼映射到同一個高維空間,使得相關(guān)代碼的檢索變得更加直接和有效。
高層語義編輯距離
在深入研究模型訓(xùn)練過程中的表征時,我們有時會發(fā)現(xiàn)模型可能只是學(xué)習(xí)到了表面現(xiàn)象,而并沒有真正理解人類所理解的概念。例如,當(dāng)我們探討高層語義編輯距離時,可以通過比較兩個序列或字符串來觀察這一點。
我們可以將字符串進行匹配,就像在本科課程中學(xué)到的字符串匹配算法那樣。這種方法也可以應(yīng)用于代碼,因為代碼中的每個 token 也都有一個高維的語義表征向量。例如,return 這個詞在代碼中會有一個語義表示,我們可以計算兩個 return 之間的語義相似度,從而判斷它們在語義上是否大致相似。
通過這種方式,我們可以對整篇代碼進行理解。如果我們使用像 CodeBERT 這樣的模型來訓(xùn)練代碼,使用表征距離或高維空間的語義表征來對齊兩篇代碼。但是,在訓(xùn)練的初期,代碼可以被正確對齊,但在訓(xùn)練的后期,模型可能會將 version download 這個詞與 if 的表征關(guān)聯(lián)得最近,而將 data 的表征與 return 的表征關(guān)聯(lián)得更近。
這種現(xiàn)象表明,盡管模型似乎學(xué)習(xí)到了預(yù)測代碼和描述之間相似性的能力,但它的理解仍然與人類的理解存在較大差距。這提示我們在模型訓(xùn)練和評估時,需要更加關(guān)注模型是否真正理解了代碼的語義,而不僅僅是表面形式上的相似性。
通過深入分析表征,我們意識到在模型訓(xùn)練過程中需要加強代碼和描述之間的對齊能力。目前,我們主要采用對比學(xué)習(xí)的方法來訓(xùn)練模型,但為了進一步提升模型的性能,我們計劃在訓(xùn)練中加入更多的對齊機制。
仿真驗證(數(shù)字孿生)
這部分我們想討論的是一種稱為仿真驗證的技術(shù),也就是數(shù)字孿生。在模型訓(xùn)練完成后,我們經(jīng)常會遇到模型的評估指標(biāo),如準(zhǔn)確率、召回率和 F1 分?jǐn)?shù)等,看起來非常高的情況。這些數(shù)字并不總能代表模型在實際應(yīng)用中能顯著提升程序員的工作效率。有時候,即使模型的 BLEU 分?jǐn)?shù)只差一點點,程序員可能仍需花費大量時間進行調(diào)整。另一方面,即使 BLEU 分?jǐn)?shù)差異很大,也不一定意味著模型的預(yù)測結(jié)果不對。這是一個非常微妙的問題。為了解決這個問題,我們提出了數(shù)字孿生驗證技術(shù)。
在我們與字節(jié)跳動的合作中,我們進行了用戶實驗,讓學(xué)生實際使用我們的工具進行編碼。我們發(fā)現(xiàn),即使在學(xué)術(shù)環(huán)境中,驗證模型的預(yù)測是否真正有用是一項工作量非常龐大的工作。因此,我們希望通過代碼提交,即編輯歷史的一個結(jié)果,來恢復(fù)過去的開發(fā)過程。
我們稱這個項目為“Historian”,就像考古學(xué)家通過文物來還原歷史一樣,我們希望通過已知的代碼提交來恢復(fù)程序員過去的代碼編輯過程。在這個過程中,我們需要解決一些問題,例如兩個編輯之間可能存在的偏序關(guān)系,確定哪個編輯先發(fā)生,哪個后發(fā)生。通過恢復(fù)整個代碼編輯的開發(fā)過程,我們可以在這個過程中引入模型,并觀察在什么情況下模型真正有助于提升生產(chǎn)力,或者是否實際上在拖累開發(fā)。我們需要評估模型的表現(xiàn)是否真的有助于提高效率,或者它是否與不使用模型時的表現(xiàn)相當(dāng)。
基本思路:從提交歷史重現(xiàn)“當(dāng)年的”開發(fā)過程
在我們的工作中,我們建立了一個復(fù)雜的工作流程,旨在通過提交歷史來重現(xiàn)程序員當(dāng)年的開發(fā)過程。這個流程的出發(fā)點是確定在何種程度的 BLEU 分?jǐn)?shù)下,模型應(yīng)該采取下一步行動。我們的目標(biāo)是利用歷史記錄來創(chuàng)建一個虛擬的程序員,這個虛擬的程序員能夠基于單個提交(commit)恢復(fù)出多種可能的編輯過程。在這些編輯過程中,我們的模型將被引入。
我們允許對這個虛擬程序員的行為進行配置,例如:在檢查推薦時需要花費多少時間?如果推薦錯誤,他將被延誤多長時間?如果推薦正確,他將花費多少時間進行審查?我們會根據(jù)不同情況來設(shè)定這些參數(shù)。
在這個過程中,我們會模擬實際的編輯場景。例如,如果我們輸入一個描述并產(chǎn)生編輯,這個過程可能需要 77 秒,這包括了第一次編輯、加載語言模型的時間(因為模型不是憑空產(chǎn)生的),以及推薦編輯位置所需的時間。如果我們的推薦正確,我們將計算產(chǎn)生的延遲;如果錯誤,我們將計算延誤的時間。我們還會模擬用戶檢測推薦所需的時間。通過這樣的模擬,我們可以與正常的編輯過程進行比較,以確定模型是在幫助用戶還是影響用戶。
通過這種方式,我們基本上可以觀察到,當(dāng)模型被應(yīng)用于實際的開發(fā)過程時,所有的性能指標(biāo),如準(zhǔn)確率和召回率,實際上都會出現(xiàn)一定程度的下降。這是因為在現(xiàn)實世界中,模型的表現(xiàn)受到多種因素的影響,包括與人類用戶的交互。
這個就是我們的 SE for (AI for SE)框架,旨在探索和改進人工智能在軟件工程中的應(yīng)用。在這個框架中,我們預(yù)見到未來業(yè)界將越來越多地采用這種模式。程序員的工作方式正在發(fā)生變化,他們不再只是調(diào)用和開發(fā) API 或修改第三方庫,而是可能會需要收集訓(xùn)練數(shù)據(jù)來微調(diào)模型,就像調(diào)整第三方庫一樣。模型本質(zhì)上是一種特殊的第三方庫,程序員在未來可能需要學(xué)習(xí)如何編寫更有效的提示(prompt)來與這些模型交互。這可能會形成新的工作模式。隨著這些新工作流程的出現(xiàn),我們面臨著如何進一步提升和賦權(quán)這些模式的問題。目前的模型是概率模型,每次輸出可能并不穩(wěn)定,同時還需要解決模型輸出的幻覺問題。
為了解決這些問題,我們嘗試提出了一些方法。例如,樣本歸因可以幫助我們追溯并理解對特定預(yù)測產(chǎn)生貢獻的訓(xùn)練樣本。通過分析學(xué)習(xí)后的樣本表征,我們可以在表征空間上進行更深入的交互式分析。
我們還提出了一個仿真驗證過程,也就是數(shù)字孿生的概念。通過創(chuàng)建一個虛擬的程序員來進行編輯操作,我們可以模擬實際的開發(fā)過程,并觀察模型在其中的作用。我們希望這種虛擬仿真的方法能夠幫助程序員或大型企業(yè)驗證模型的實際效用。如果我們想在生產(chǎn)環(huán)境中引入一個新模型,我們需要說服生產(chǎn)團隊這個模型確實能夠帶來產(chǎn)能增值。通過數(shù)字孿生技術(shù),我們可以模擬模型在實際開發(fā)過程中的表現(xiàn),從而預(yù)估它可能帶來的效益。
展望:AI 原生的軟件工程實踐
隨著人工智能時代的到來,軟件工程的實踐將發(fā)生根本性變化。過去,編程主要是為了交付軟件產(chǎn)品。但在 AI 時代,編程不僅僅是為了交付,它還具有數(shù)據(jù)標(biāo)注的意義。我們編寫的每一行代碼、提交的每一個 commit、撰寫的每一個需求,都可能被用來訓(xùn)練模型。這意味著代碼編輯和整個編輯過程實際上在無形中完成了數(shù)據(jù)的標(biāo)注工作。
由于模型訓(xùn)練對數(shù)據(jù)質(zhì)量有很高的要求,我們預(yù)見未來將出現(xiàn)一種 AI 原生的軟件工程實踐。我們將利用現(xiàn)有的數(shù)據(jù)來訓(xùn)練模型,然后評估這些模型是否符合我們的預(yù)期。有了新模型后,我們可以反向工作,利用模型預(yù)測的好壞來評估過去的編程實踐是否合適。這個過程類似于梯度下降,從模型預(yù)測到生產(chǎn)過程或代碼標(biāo)注的反向優(yōu)化。我們可以通過模型的性能和對數(shù)據(jù)質(zhì)量的分析,反過來指導(dǎo)整個開發(fā)實踐,告訴我們何時應(yīng)該如何編寫代碼、如何記錄代碼歷史,或者如何提出問題。
以前,我們通常依據(jù)一些軟性指標(biāo)來推薦最佳實踐,未來我們將有更硬性的理由來證明為何要這樣編寫代碼。因為這樣做可以使模型訓(xùn)練得更好。通過這種方式,我們可以不斷調(diào)整實踐,形成一個 AI 原生的軟件工程范式,最終推動整個過程的自動化。
本文來源:36氪
文章轉(zhuǎn)載于其他網(wǎng)絡(luò),如有侵權(quán)請聯(lián)系我們及時刪除!