Copilot、Codex和AlphaCode:自動編程的計算機程序現(xiàn)況
近年來,由于自然語言處理領(lǐng)域轉(zhuǎn)換器(transformers)的興起,我們看到了一系列令人震驚的代碼編寫深度學(xué)習(xí)模型。能夠編寫計算機程序的計算機程序,通常稱為程序合成問題,至少從20世紀60年代末和20世紀70年代初就已開始研究。
在21世紀10年代和20年代,基于注意力的模型在其他領(lǐng)域的成功再次激發(fā)了程序合成研究的動力,即在數(shù)百GB的文本上預(yù)先訓(xùn)練具有數(shù)百萬或數(shù)十億參數(shù)的大規(guī)?;谧⒁饬Φ纳窠?jīng)模型(轉(zhuǎn)換器)的策略。
經(jīng)過預(yù)訓(xùn)練的模型在元學(xué)習(xí)方面表現(xiàn)出了令人印象深刻的能力,這得益于它們的注意力機制,并且似乎可以實際應(yīng)用于文本任務(wù)開發(fā)方面——通過在提示內(nèi)容中僅提供少數(shù)幾個示例(研究文獻中稱作“零樣本或小樣本學(xué)習(xí)”)。
基于深層NLP模型的現(xiàn)代程序合成
NLP模型可以進一步使用專門的數(shù)據(jù)集進行訓(xùn)練,以微調(diào)特定任務(wù)的性能。編寫代碼就是這方面應(yīng)用的一個特別有趣的使用場景。
GitHub上的Copilot項目,被宣傳為“你的人工智能編程伙伴(Your AI Pair Programmer)”,在2021推出時引起不小的爭議。在很大程度上,這是由于在訓(xùn)練數(shù)據(jù)集中使用了所有公開的GitHub代碼。根據(jù)有關(guān)說明,這些代碼庫包括具有Copyleft許可證的項目,這些項目可能不允許將代碼用于Copilot等項目,除非Copilot本身是開源的。
Copilot是OpenAI組織和微軟公司之間關(guān)系的產(chǎn)物,基于GPT-3的一個經(jīng)過代碼訓(xùn)練的版本。由OpenAI演示并通過其API提供的版本稱為Codex。使用Copex的正式實驗描述在陳先生等人于2021年發(fā)表的論文中有詳細的介紹。
2022年初,DeepMind公司也不甘示弱,開發(fā)出他們自己的程序合成深度NLP系統(tǒng):AlphaCode。
新挑戰(zhàn)者:AlphaCode
與之前的Codex和Copilot一樣,AlphaCode是一個設(shè)計和訓(xùn)練用于編寫代碼的大型NLP模型。如Copilot一樣,AlphaCode的開發(fā)也不是為了將AlphaCode作為軟件工程師的生產(chǎn)力工具,而是用于在競賽類編程任務(wù)中挑戰(zhàn)人類水平的編程性能。
用于訓(xùn)練和評估AlphaCode(組成新的CodeContests數(shù)據(jù)集)的競賽編碼挑戰(zhàn)的難度介于以前數(shù)據(jù)集難度和現(xiàn)實世界軟件工程難度之間。
對于那些不熟悉競賽類編程挑戰(zhàn)網(wǎng)站的人來說,這項任務(wù)有點像測試驅(qū)動開發(fā)的簡化版本。根據(jù)給定的一些文本描述和幾個例程,這種挑戰(zhàn)的主要內(nèi)容就是,編寫一個通過一組測試的程序——其中大部分測試對程序員來說都是隱藏的。
理想情況下,隱藏測試應(yīng)該是全面的,通過所有測試也就代表順利解決了給定的問題。但是,用單元測試覆蓋每個邊緣情況是一個困難的問題。對程序合成領(lǐng)域的一個重要貢獻實際上是CodeContests數(shù)據(jù)集本身,因為DeepMind團隊做出了重大努力——他們通過突變過程生成額外的測試,目的是為了降低誤報率(測試通過,但問題尚未解決)和慢陽性率(測試通過,但解決方案太慢)。
AlphaCode的性能是根據(jù)競賽網(wǎng)站CodeForces上的競賽編程挑戰(zhàn)內(nèi)容進行評估的??傮w上,AlphaCode在參與競賽的(也可能是人類)程序員中的平均表現(xiàn)為“前54.3%”。
請注意,該指標(biāo)可能有點誤導(dǎo),因為它實際上等同于45.7%的績效。令人難以置信的是,AlphaCode系統(tǒng)能夠編寫任何通過所有隱藏測試的算法。但是,請注意:AlphaCode使用了一種與人類截然不同的策略來解決編程問題。
雖然人類競爭對手可能會編寫一種算法來解決大多數(shù)例程——結(jié)合運行早期版本解決方案的見解不斷改進,直到通過所有測試;但是,AlphaCode采用了一種基礎(chǔ)更廣泛的方法,即為每個問題生成多個樣本,然后選擇10個樣本提交。
AlphaCode在CodeContests數(shù)據(jù)集上的性能的一大貢獻是,生成后過濾和聚類的結(jié)果:它在生成大約1000000個候選解決方案后,開始過濾候選方案,以刪除問題描述中未通過示例測試的候選方案,從而消除大約99%的候選群體。
作者提到,大約10%的問題沒有通過此階段所有示例測試的候選解決方案。
然后通過聚類將剩余的候選項篩選到10份或更少的提交。簡而言之,他們訓(xùn)練了另一個模型,以根據(jù)問題描述生成額外的測試輸入(但請注意,他們沒有這些測試的有效輸出)。
剩余的候選解決方案(過濾后的數(shù)量可能小于1000個),根據(jù)其在生成的測試輸入上的輸出進行聚類。按照從最大到最小的順序,從每個集群中選擇一個候選對象進行提交。如果簇少于10個,則對簇進行多次采樣。
雖然過濾/聚類步驟是獨特的,并且AlphaCode在新的CodeContests數(shù)據(jù)集上進行了微調(diào),但它最初的訓(xùn)練方式與Codex或Copilot大致相同。AlphaCode首先在GitHub(2021年7月14日檢索)的一個大型公共可用代碼數(shù)據(jù)集上接受了預(yù)訓(xùn)練。他們訓(xùn)練了5種變型,參數(shù)個數(shù)從2.84億個增加到410億個。
與AlphaGo系列或玩星際爭霸II游戲的AlphaStar機器人的精神一樣,AlphaCode也是一個研究項目,旨在開發(fā)一個接近人類在專門任務(wù)領(lǐng)域的能力的系統(tǒng),而且在程序合成過程中開發(fā)出的實用程序的門檻更低。
從開發(fā)解決問題的實用工具的角度來說,這方面機器人的代表是基于GPT-3的Codex和Copilot工具。Codex是GPT-3的OpenAI變體,在一個公共可用代碼的語料庫上進行訓(xùn)練。根據(jù)與論文一起發(fā)布的HumanEval數(shù)據(jù)集,OpenAI報告稱,Codex通過在“docstring to code”格式的任務(wù)中生成100個樣本,能夠解決70%以上的問題。
接下來,我們將探索這種通過使用Codex自動生成代碼的提示符編程技術(shù)。我們將同時使用下面給定的模型來開發(fā)約翰·康威的《生命游戲》。
GitHub Copilot采用代碼自動補全的方法,目前的打包形式是Visual Studio,VSCode,Neovim和JetBrains等集成開發(fā)環(huán)境的擴展的方式。根據(jù)Copilot網(wǎng)頁有關(guān)描述,Copilot已能夠成功地根據(jù)給定描述重新編寫出一組經(jīng)過良好測試的Python函數(shù),其中57%的函數(shù)與HumanEval數(shù)據(jù)集類似。
我們將研究Copilot的一些實際用例,例如使用VSCode的專用測試版的Copilot擴展自動編寫測試。
提示符編程:用Codex編寫康威的《生命游戲》
在本節(jié)中,我們將介紹基于約翰·康威的《生命游戲》編寫細胞自動機模擬器的任務(wù)。稍加了一點修改,沒有對規(guī)則硬編碼,我們的程序應(yīng)該能夠模擬任何一組類生命的細胞自動機規(guī)則。
我們將采用交互式方法,而不是通過生成100個示例并選擇最佳示例(可以是手動方式也可以通過運行測試的方式)。當(dāng)Codex給出一個糟糕的解決方案的時候,我們將做出調(diào)整以嘗試引導(dǎo)更好的答案。當(dāng)然,如果絕對必要的話,在Codex完全失敗的情況下,我們可以繼續(xù)修改代碼以獲得一個能夠正常工作的示例。
編寫一個逼真的CA(Cellular Automata,即“元胞自動機”。它是一種時間、空間、狀態(tài)都離散,空間相互作用和時間因果關(guān)系為局部的網(wǎng)格動力學(xué)模型,具有模擬復(fù)雜系統(tǒng)時空演化過程的能力)模擬器的第一步是提出一個計算鄰居的函數(shù)。我們編寫了以下docstring提示符,并將其提供給code-davinci-001,這是OpenAI API庫中最大的Codex模型:
# PROMPT
import numpy as np
def compute_neighborhood(grid: np.array) -> np.array:
"""
Compute the sum of cell states in the Moore neighborhood
for each cell in the input grid.
A Moore neighborhood includes 8 cells,
diagonally and orthogonally adjacent to, but not including, the center cell.
The cells denoted by '1' correspond to the Moore neighborhood for cell a:
1 1 1
1 a 1
1 1 1
If cell 'a' is on the edge of the grid,
the Moore neighborhood wraps around to the cells on the opposite edge,
i.e. grid is on a toroid.
args:
grid, a numpy array with binary values 0 or 1. grid contains the current cell state values
Returns:
neighorhood_grid, a numpy array with integer values from 0 to 8.
For each cell (element in grid) in grid, neighborhood_grid should
contain the sum of states in that cell's Moore neighborhood
"""
上述代碼中,加入注釋行“# PROMPT”和“# GENERATED”是為了更清楚地說明提示符從哪兒結(jié)束。
你仔細分析上面相當(dāng)全面的docstring提示了沒有?Codex是怎么做的?其實,Codex的第一次嘗試如下:
# GENERATED
# TODO: implement this function
pass
這對我們的小實驗來說可能不是好兆頭。即使在調(diào)整P值超參數(shù)以實現(xiàn)更寬松的核采樣(并希望更好的多樣性)后,Codex似乎仍未給出上述問題的答案。
幸運的是,我們只需在docstring提示符上添加一點類似于下面的內(nèi)容,Codex就會產(chǎn)生一個更有希望的輸出。
# PROMPT (appended to previous prompt)
this function is completed and does not include any TODOs
"""
# GENERATED
# get the dimensions of the grid
n_rows, n_cols = grid.shape
# initialize the output array
neighborhood_grid = np.zeros((n_rows, n_cols), dtype=int)
# loop over the grid
for i in range(n_rows):
for j in range(n_cols):
# get the current cell value
cell_val = grid[i, j]
# get the Moore neighborhood of the current cell
# using the get_neighborhood function
neighborhood = get_neighborhood(grid, i, j)
# sum the cell values in the neighborhood
neighborhood_sum = np.sum(neighborhood)
# store the sum in the neighborhood grid
neighborhood_grid[i, j] = neighborhood_sum
return neighborhood_grid
當(dāng)然,這還不是一個很好的解決方案,因為它引入了一個未使用的變量cell_val,并將其大部分工作委托給它剛剛添加的另一個函數(shù)get_neighborhood。但總的來說,這看起來是一個可行的開始。
接下來,我們?yōu)樯厦嫣岬降膅et_neighborhood函數(shù)編寫了一個簡單的docstring:
def get_neighborhood(grid: np.array, i: int, j: int) -> np.array:
"""
get_neighborhood is used by compute_neighborhood
to return the 3x3 Moore neighborhood for the cell
located at coordinates i,j If i,j coordinates are
on the edge of the grid (i.e. i or j equal 0, or i ==
grid.shape[0]-1 or j == grid.shape[1]), the Moore
Neighborhood should wrap around to include cells on the
opposite edge. In other words the grid can be considered to
be the surface of a toroid, like 'Pac-Man'
args:
grid, np.array, this is the current state of
the cellular automata grid
i, int, this coordinate corresponds to the row
j, int, coordinate corresponding to column
returns:
moore_neighborhood - np.array, should always have
dimensions 3x3 and
contain the Moore neighborhood for the cell at i,j
in grid.Because the center cell is not part of the
neighborhood, moore_neighborhood[1,1] should always
be 0
"""
# GENERATED
# get the dimensions of the grid
n_rows, n_cols = grid.shape
# initialize the output array
moore_neighborhood = np.zeros((3, 3), dtype=int)
# get the coordinates of the Moore neighborhood
# using the get_neighborhood_coordinates function
neighborhood_coords = \
get_neighborhood_coordinates(i, j, n_rows, n_cols)
# loop over the coordinates
for coord in neighborhood_coords:
# get the cell value at the coordinate
cell_val = grid[coord[0], coord[1]]
# store the cell value in the output array
moore_neighborhood[coord[0], coord[1]] = cell_val
return moore_neighborhood
上面程序的輸出看起來也算可行;但經(jīng)過檢查,它實際上包含了一個重要錯誤。
在鄰居坐標(biāo)的循環(huán)中,它使用與從網(wǎng)格中檢索值相同的坐標(biāo)為摩爾鄰居(Moore neighborhood)賦值。
我們沒有找到提示Codex避免錯誤的明確方法;因此,我們只好手動修改一下代碼:
# FIXED loop over the coordinates
for index, coord in enumerate(neighborhood_coords):
# get the cell value at the coordinate
cell_val = grid[coord[0], coord[1]]
# FIXED store the cell value in the output array
moore_neighborhood[index // 3, index % 3] = cell_val
return moore_neighborhood
另外,前面代碼中還引入了另一個函數(shù)get_neighborhood_coordinates來處理比較“棘手”的部分。
接下來,我們要為函數(shù)get_neighborhood_coordinates編寫一個提示符。
# PROMPT
def get_neighborhood_coordinates(\
i: int, j: int, n_rows: int, n_cols: int) -> list:
"""
get_neighborhood_coordinates is used by get_neighborhood
to return the coordinates of the 3x3 Moore neighborhood for
the cell located at coordinates i,j If i,j coordinates are on
the edge of the grid (i.e. i or j equal 0, or i ==
grid.shape[0]-1 or j == grid.shape[1]), the Moore
Neighborhood should wrap around to include cells on the
opposite edge. In other words the grid can be considered to
be the surface of a toroid, like 'Pac-Man'
args:
i, int, this coordinate corresponds to the row
j, int, coordinate corresponding to column
n_rows, int, number of rows in the grid
n_cols, int, number of columns in the grid
returns:
neighborhood_coords - list of tuples,
each tuple contains the coordinates
of a cell in the Moore neighborhood
"""
# GENERATED
# initialize the output list
neighborhood_coords = []
# get the coordinates of the Moore neighborhood
# using the get_neighborhood_coordinates function
for row in range(i-1, i+2):
for col in range(j-1, j+2):
# skip the center cell
if row == i and col == j:
continue
# check if the cell is on the edge of the grid
# if so, wrap around to the opposite edge
if row < 0:
row = n_rows - 1
elif row == n_rows:
row = 0
if col < 0:
col = n_cols - 1
elif col == n_cols:
col = 0
# store the cell coordinates
neighborhood_coords.append((row, col))
return neighborhood_coords
這比我們預(yù)期的函數(shù)式編程深度要深一點(compute_neighborhood調(diào)用get_neighborhood,反過來get_neighborhood又調(diào)用get_neighborhood_coordinates),但看起來我們終于有了一組函數(shù),可以返回一個鄰居和的網(wǎng)格。
還剩下update函數(shù),它對應(yīng)的docstring提示如下所示:
# PROMPT
def update_grid(grid: np.array, rules: list) -> np.array:
"""
update input arg grid according the the Life-like rules
contained in input arg rules. rules is a list of lists,
with the first element containing a list of 'birth'
conditions (cells go from 0 -> 1) and the second element
containing a list of 'survival' conditions (cells are
unchanged).
Cells which have a neighborhood sum that is not in the
'birth' (B) or 'survival' (S) rules go to 0.
This functions uses function compute_neighborhood to compute
the neighborhood value for each cell in grid.
args:
grid, np.array. grid contains the cell values (0 or 1)
rules, list of lists. rules contains the B/S rules
for Life-like cellular automata for example, to
simulate Conway's Life CA, `rules = [[3], [2,3]]`,
i.e. B3/S23 Life Without Death has rules
B3/S012345678, so `rules = [[3],
[0,1,2,3,4,5,6,7,8]]`
update_grid should raise an Exception if rules contains
values greater than 8 (maximum number of live cells in a
Moore neighborhood) or less than 0.
"""
# GENERATED
# get the dimensions of the grid
n_rows, n_cols = grid.shape
# get the neighborhood grid
neighborhood_grid = compute_neighborhood(grid)
# loop over the grid
for i in range(n_rows):
for j in range(n_cols):
# get the current cell value
cell_val = grid[i, j]
# get the neighborhood value for the current cell
neighborhood_val = neighborhood_grid[i, j]
# check if the cell is alive
if cell_val == 1:
# check if the cell should die
if neighborhood_val not in rules[1]:
grid[i, j] = 0
else:
# check if the cell should be born
if neighborhood_val in rules[0]:
grid[i, j] = 1
return grid
這看起來是解決上面問題的一種非常合理的方法;盡管我們確實明確建議,如果鄰居網(wǎng)格包含錯誤的值并且在生成的輸出中找不到,就會引發(fā)異常。
只需進行一些修復(fù):在函數(shù)get_neighborhood中進行手動干預(yù),并且嘗試給出一些提示,我們就可以成功地設(shè)計出一個完全可行的類似生命的細胞自動機模擬器。
上面的代碼不是一個特別快的實現(xiàn)方案,但它的質(zhì)量與程序員在開始使用一種新語言時可能做出的那種“Hello World”嘗試相似。值得注意的是,在訓(xùn)練數(shù)據(jù)集中包含了許多示例,供研究者參考。
在康威的《生命游戲》中,我們可以想象這段程序在微型滑翔機的發(fā)展過程中取得的成功。
雖然我們確實通過一組函數(shù)完成了CA模擬器的編寫,但這種方法對于日常軟件工程來說并不是非常有用或現(xiàn)實的開發(fā)方式。但另一方面,這并不能阻止像SourceAI這樣的初創(chuàng)公司(本質(zhì)上是OpenAI Codex API的包裝商)將其服務(wù)宣傳為“給每個人創(chuàng)造有價值的定制軟件的機會”。
“我們構(gòu)建了一個獨立的系統(tǒng),可以開發(fā)出世界上最熟練的工程師級別的軟件?!辈贿^,與Codex交互的確是學(xué)習(xí)或?qū)嵺`編程的一種潛在有用的方式,尤其是針對CodeSignal、CodeForces或HackerRank等網(wǎng)站的編程問題方面。
接下來,我們將嘗試評估Codex/Copilot,以獲得自動編寫測試和docstring的更現(xiàn)實的使用情形。
任務(wù)2:編寫測試
對于本例來說,我們選擇使用VSCode開發(fā)工具的GitHub Copilot擴展來編寫測試。
# PROMPT (in VSCode)
import numpy as np
def pythagorean(a: np.float, b: np.float) -> np.float:
"""
Use the Pythagorean theorem to comput the hypotenuse for
a triangle with side lengths a and b
args:
a, np.float, side of the triangle
b, np.float, the other side of the triangle
returns:
c, np.float, the length of the hypotenuse
"""
c = np.sqrt(a**2 + b**2)
return c
def test_pythagorean():
# GENERATED (Copilot autocomplete)
"""
Test the pythagorean function
"""
a = np.array([3, 4, 5])
b = np.array([4, 5, 6])
c = pythagorean(a, b)
assert np.allclose(c, np.sqrt(a**2 + b**2))
雖然畢達哥拉斯定理函數(shù)可能過于簡單,但Copilot建議進行一次合理的測試。如果您運行它,它就會通過。你會注意到,自動完成建議能夠正確地獲得測試的結(jié)構(gòu)和數(shù)字內(nèi)容。
如果我們想使用自己喜歡的框架以更系統(tǒng)的方式編寫測試,該怎么辦?不用太擔(dān)心,因為我們已經(jīng)使用numpy庫和自動微分技術(shù)編寫了大量的低級學(xué)習(xí)模型,供給測試者使用。因此,雖然接下來的這個示例不是100%真實地來自真實世界,但效果還是比較貼近真實情況的。
在本例中,我們將使用autograd和numpy以及unittest中的TestCase類設(shè)置一個簡單的多層感知器前向傳遞、損失函數(shù)和梯度函數(shù)進行測試。
#PROMPT
import unittest
from autograd import numpy as np
from autograd import grad
def forward_mlp(input_x: np.array, \
weights: list, biases: list) -> np.array:
"""
compute the forward pass of a multilayer perceptron.
The number of layers is equal to the length of the list of
weights, which must be the same as the list of biases,
biases.
args:
input_x, np.array, input data
weights, list of np.arrays, a list of np.array matrices,
representing the weights
biases: list of np.arrays, a list of biases for each
layer
returns:
result, np.array, the output of the network
"""
assert len(weights) == len(biases)
for layer_index in range(len(weights) - 1):
input_x = np.tanh(np.matmul(input_x,\
weights[layer_index]) + biases[layer_index])
output = np.matmul(input_x, weights[-1]) + biases[-1]
return output
def get_loss(input_x: np.array, weights: list, \
biases: list, target: np.array) -> np.float:
"""
compute the mean squared error loss for an mlp with weights
and biases, with respect to the input data input_x and the
target array target.
args:
input_x, np.array, input data
weights, list of np.arrays, a list of np.array matrices,
representing the weights
biases: list of np.arrays, a list of biases for each
layer
target: np.array, the target values
returns:
loss, np.float, the loss
"""
output = forward_mlp(input_x, weights, biases)
return np.mean((output - target)**2)
get_loss_grad = grad(get_loss, argnum=(1,2))
class TestMLP(unittest.TestCase):
"""
tests for MLP, get_loss, and get_grad_loss
"""
# GENERATED (chosen from Copilot suggestions)
def setUp(self):
pass
def test_forward_mlp(self):
"""
test forward_mlp
"""
input_x = np.array([[1, 2, 3], [4, 5, 6]])
weights = [np.array([[1, 2], [3, 4]]), \
np.array([[5, 6], [7, 8]]), \
np.array([[9, 10], [11, 12]])]
biases = [np.array([[1, 2]]), np.array([[3, 4]]),
np.array([[5, 6]])]
output = forward_mlp(input_x, weights, biases)
self.assertTrue(np.allclose(output, \
np.array([[-0.9, -0.9], [-0.9, -0.9]])))
def test_get_loss(self):
"""
test get_loss
"""
input_x = np.array([[1, 2, 3], [4, 5, 6]])
weights = [np.array([[1, 2], [3, 4]]), \
np.array([[5, 6], [7, 8]]), \
np.array([[9, 10], [11, 12]])]
biases = [np.array([[1, 2]]), np.array([[3, 4]]),\
np.array([[5, 6]])]
target = np.array([[1, 1], [1, 1]])
loss = get_loss(input_x, weights, biases, target)
self.assertTrue(np.allclose(loss, 0.5))
def test_get_loss_grad(self):
"""
test get_loss_grad
"""
input_x = np.array([[1, 2, 3], [4, 5, 6]])
weights = [np.array([[1, 2], [3, 4]]), \
np.array([[5, 6], [7, 8]]), \
np.array([[9, 10], [11, 12]])]
biases = [np.array([[1, 2]]), np.array([[3, 4]]), \
np.array([[5, 6]])]
target = np.array([[1, 1], [1, 1]])
loss_grad = get_loss_grad(\
input_x, weights, biases, target)
self.assertTrue(\
np.allclose(loss_grad[0], \
np.array([[-0.9, -0.9], [-0.9, -0.9]])))
self.assertTrue(\
np.allclose(loss_grad[1], \
np.array([[-0.9, -0.9], [-0.9, -0.9]])))
self.assertTrue(\
np.allclose(loss_grad[2],\
np.array([[-0.9, -0.9], [-0.9, -0.9]])))
# END GENERATED (the final two lines are part of the prompt)
if __name__ == "__main__":
unittest.main(verbosity=1)
Copilot的建議雖然并不完美,但確實為測試類提供了合理的思路。不過,如果您嘗試原封不動地運行代碼的話,則不會執(zhí)行任何測試,更不用說通過測試了。
輸入數(shù)據(jù)和第一個權(quán)重矩陣之間存在維度不匹配,數(shù)據(jù)類型也是錯誤的(所有數(shù)組都是整數(shù)數(shù)據(jù)類型),而且無法使用Autograd梯度函數(shù)。
當(dāng)然,上面這些問題并不是很難解決,如果用3x2矩陣替換權(quán)重矩陣列表中的第一個條目,那么前向傳播應(yīng)該可以運行。要使得梯度計算測試順利進行,或者需要在np.array定義的數(shù)字上添加小數(shù)點,或者顯式定義數(shù)組數(shù)據(jù)類型。
有了這些更改后,測試即可成功執(zhí)行并失敗,但預(yù)期值在數(shù)字表現(xiàn)方面還不正確。
任務(wù)3:自動文檔字符串
Copilot有很大潛力的一項任務(wù)是自動編寫文檔,特別是為已經(jīng)編寫的函數(shù)填寫docstring內(nèi)容。這方面幾乎是比較實用了。
對于畢達哥拉斯定理的示例程序,Copilot運行結(jié)果已經(jīng)非常接近,但它將問題描述為查找兩點a和b之間的距離,而不是查找邊長c到邊長a和邊長b的距離。不出所料,隨同Copilot一同發(fā)行的docstring中的示例也與函數(shù)的實際內(nèi)容不匹配:返回的是一個標(biāo)量,而不是c的值數(shù)組。
Copilot對前向MLP函數(shù)的docstrings的建議也很接近,但并不完全正確。
Copilot支持的自動Docstring建議
機器能取代我的工作嗎?
對于軟件工程師來說,程序合成方面的每一項新進展都可能引發(fā)一次經(jīng)濟恐慌。
畢竟,如果計算機程序能像程序員一樣為計算機編程,難道這不意味著機器應(yīng)該“取代我們的工作”嗎?不久的將來會這樣嗎?
從表面上看,答案似乎是“還沒有”;但是,這并不意味著隨著這些工具變得更加成熟,軟件工程的本質(zhì)可能會保持不變。在未來,使用復(fù)雜的自動完成工具成功地進行推理可能與使用代碼格式化工具一樣重要。
Copilot當(dāng)前正處于測試版試用階段,關(guān)于如何使用它的選項數(shù)量有限。同樣,Codex也可以通過OpenAI在測試版中獲得應(yīng)用程序編程接口。試點項目的使用條款和隱私考慮確實限制了該技術(shù)的潛在使用場景。
根據(jù)當(dāng)前的隱私政策,輸入到這些系統(tǒng)的任何代碼都可以用于微調(diào)模型,并且可以由GitHub/Microsoft或OpenAI的員工進行審查。這就排除了在敏感項目中使用Codex或Copilot的可能性。
Copilot確實為其所基于的Codex模型添加了許多實用程序。您可以為所需的代碼編寫框架或大綱(如為unittest框架的測試編寫示例),并將光標(biāo)移動到大綱的中間,以獲得合理的OK自動完成建議。
目前的Copilot,對于任何比簡單的編碼實踐更復(fù)雜的問題,都不太可能建議正確的完整代碼;但是,它通??梢詣?chuàng)建一個合理的大綱并節(jié)省一些手工輸入。
還應(yīng)該注意的是,Copilot是在云端運行的。這意味著它無法脫機工作,自動完成建議也有點慢。此時,您可以通過按Alt+]組合鍵循環(huán)瀏覽建議,但有時只有少數(shù)幾個建議可供選擇,甚至只有一個建議可供選擇。
當(dāng)Copilot運行良好時——實際上已經(jīng)足夠好了,有點危險。unittest示例中建議的測試和為勾股函數(shù)建議的docstring乍一看是正確的,可能會通過疲憊的軟件工程師的審查。但是,當(dāng)它們包含神秘的錯誤時,這只會導(dǎo)致以后的痛苦!
綜上所述,雖然Copilot/Codex在目前的狀態(tài)下更像是一個玩具或一個學(xué)習(xí)工具,但令人難以置信的是它居然能工作。如果你遇到一只跳著華爾茲舞的熊,我想令你印象深刻的不會是它跳得如何好。同樣,如果你遇到一個智能代碼完成工具,令你印象深刻的不應(yīng)是它寫的代碼如何完美。
總之,隨著自動編程NLP模型技術(shù)的進一步發(fā)展,以及人類程序員對使用NLP自動完成工具的大量調(diào)整,很可能在不久的將來會出現(xiàn)程序合成模型的重大殺手級應(yīng)用。
譯者介紹
朱先忠,51CTO社區(qū)編輯,51CTO專家博客、講師,濰坊一所高校計算機教師,自由編程界老兵一枚。早期專注各種微軟技術(shù)(編著成ASP.NET AJX、Cocos 2d-X相關(guān)三本技術(shù)圖書),近十多年投身于開源世界(熟悉流行全棧Web開發(fā)技術(shù)),了解基于OneNet/AliOS+Arduino/ESP32/樹莓派等物聯(lián)網(wǎng)開發(fā)技術(shù)與Scala+Hadoop+Spark+Flink等大數(shù)據(jù)開發(fā)技術(shù)。
原文標(biāo)題:?NLP Models for Writing Code: Program Synthesis?,作者:Kevin Vu