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