代码摘要是指生成代码片段的自然语言描述的过程。常见的代码摘要任务包括探索新的代码库、学习新的编程语言,以及生成代码注释和函数说明。生成代码片段摘要的过程类似于生成自然语言文档的文本摘要。不同之处在于,生成摘要的大语言模型(LLM)不仅需要理解所读取的编程语言,还需要识别代码试图实现的底层逻辑。
代码摘要是软件开发中的重要组成部分,可通过自动生成源代码摘要、为文档创建自然语言说明,或解析大型代码库来帮助软件维护。采用基于转换器方法的新型 LLM 模型既可用于代码摘要,也可用于代码生成。这些功能之所以能够实现,是因为模型经过了大规模数据集训练,而这些数据集来源包括 GitHub 存储库,其中包含代码、注释以及相关文档。
在 LLM 普及之前,代码摘要方法需要解析代码语义,并为代码中的每个标识符生成抽象语法树(AST),随后利用这些信息生成文档。1,2随着深度学习和神经网络的发展,基于传统计算机科学的方法逐渐被采用神经机器翻译方法的方案所取代。3,4
对于转换器模型,更大的上下文窗口通常能够带来更好的效果。许多最新的先进 Granite™ Code 模型(例如 Granite-8B-Code-Instruct-128K)都支持 128K 上下文窗口。更大的上下文窗口使模型能够在工作记忆中保留更多文本。这有助于模型跟踪长时间对话、长篇文档或大型代码库中的关键内容和细节。这种工作记忆能力使基于 LLM 的聊天机器人能够生成既符合当前上下文,又能保持长期连贯性的回复,并通过人工评估和指标评测超越上下文窗口较小的模型。5
更大的上下文窗口使模型能够在工作记忆中保留更多文本,从而帮助跟踪冗长对话、长篇文档或大型代码库中的关键内容和细节。
ChatGPT 首次推出时,其上下文窗口大小为 4,000 个词元。如果对话超过聊天界面 3,000 字的限制,聊天机器人可能会产生幻觉并偏离主题。如今,行业标准为 32,000 个词元,并正在向 128,000 个词元迈进。这大约相当于一本 250 页书籍的篇幅。IBM 目前已有两款支持 128,000 词元上下文窗口的 Granite 模型,更多模型也即将推出。
在此步骤中,我们将指导您创建 IBM 帐户,以访问 Jupyter Notebook。
1. 使用您的 IBM Cloud® 帐户登录 watsonx.ai™。
2. 单击“+”以创建新项目。
a. 选择“创建空项目”。
b. 在“名称”字段中输入项目名称。
c. 如果尚未创建,请创建一个 Cloud Object Storage 实例,用于存储项目资产。
d. 选择“创建”。
3. 创建一个 Jupyter Notebook。
a. 在项目环境中选择“资产”选项卡。
b. 单击“新建资产”。
c. 在左侧面板中选择“使用模型”选项。
d. 单击“使用 Python 和 R notebook 处理数据和模型”。
e. 在“名称”字段中输入 Notebook 名称。选择 Python Runtime 23.1(4 个 vCPU,16 GB RAM)作为运行配置。
f. 选择“创建”。
4. 设置 watsonx.ai 运行时实例和 API 密钥
a.创建一个 watsonx.ai 运行时 服务实例(选择适当的区域并选择精简计划,这是一个免费实例)。
b. 生成一个API密钥。
c. 将 watsonx.ai 运行时服务实例与您在 watsonx.ai 中创建的项目关联。
首先,我们将安装开源 Hugging Face Hub 库,以下载模型:
现在,我们可以下载 Granite-8B-Code-Instruct-128K:
现在,我们可以开始使用 Granite Code Instruct。
我们来向模型提供一个来自 GluonTS 库的较为复杂的函数调用示例,该示例取自其 GitHub 存储库。这是一大段需要粘贴到提示中的代码,因此我们会将其存储在一个变量中:
ll_func_2 = """ def call( self, data: torch.Tensor, weights: torch.Tensor ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: assert ( data.shape == weights.shape ), "data and observed_indicator must have same shape" with torch.no_grad(): observed_data = torch.where(weights== 1, data, torch.nan) med = torch.nanmedian(observed_data,dim=self.dim,keepdim=True).values q1 = torch.nanquantile(observed_data,0.25, dim=self.dim,keepdim=True) q3 = torch.nanquantile(observed_data,0.75, dim=self.dim,keepdim=True) iqr = q3 - q1 # if observed data is all zeros, nanmedian returns nan loc = torch.where(torch.isnan(med),torch.zeros_ like(med),med) scale = torch.where(torch.isnan(iqr),torch.ones_ like(iqr),iqr) scale = torch.maximum(scale,torch.full_Like(iqr,self.minimum_scale)) scaled_data = (data - loc) /scale if not self.keepdim: loc = torch.squeeze(loc,dim=self.dim) scale = torch.squeeze(scale,Dim=self.dim) # 断言缩放后的数据、本地数据或缩放数据中没有 nan assert not torch.any(torch.isnan(scaled_data)) assert not torch.any(torch.isnan(loc)) assert not torch.any(torch.isnan(scale)) assert not torch.any(scale== 0) return scaled_data, loc, scaledef call( self, data: torch.Tensor, weights: torch.Tensor ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]: assert ( data.shape == weights.shape ), "data and observed_indicator must have same shape"
现在,我们可以通过对聊天内容进行词元化处理,然后调用 model.generate(),让 Granite Code 生成摘要
chat = [
{ "role": "user", "content": "Explain what this function does" + ll_func_2 },
]
chat = tokenizer.apply_chat_template(chat,tokenize=False, add_generation_prompt=True)
# have the encoder turn text into output tokens
inputs = tokenizer(chat, return_tensors="pt").input_ids
start_index = inputs.shape[-1]
# generate output tokens
output = model.generate(inputs,max_new_tokens=1500)
# have the decoder turn output tokens into text
output = tokenizer.batch_decode(output[start_index:])
# loop over the batch to print, in this example the batch size is 1
print(output[0])
我们来查看生成的摘要:
答案:此函数使用四分位距(IQR)方法执行数据归一化。它需要两个参数:数据和权重。数据参数是一个形状为 (batch_size, sequence_length, hidden_size) 的张量,其中 batch_size 表示一个批次中的样本数量,sequence_length 表示输入序列的长度,hidden_size 表示隐藏状态的维度。权重参数是一个形状为 (batch_size, sequence_length) 的张量,其中每个元素为 0 或 1,用于指示数据张量中的对应元素是否已观测或缺失。 该函数首先检查数据和权重张量是否具有相同的形状。然后,它使用 torch.nanmedian 和 torch.nanquantile 函数计算已观测数据点(即权重为 1 的数据点)的中位数和 IQR。torch.nanmedian 函数沿指定维度(`self.dim`)计算中位数,而 torch.nanquantile 函数计算第 25 和第 75 百分位数。接下来,该函数计算归一化分布的位置参数(loc)和尺度参数(scale)。位置设置为已观测数据点的中位数,尺度设置为已观测数据点的 IQR。如果所有已观测数据点均为零,该函数会将位置设置为零。该函数还确保尺度不小于最小值(self.minimum_scale),以避免除零错误。最后,该函数通过减去位置并除以尺度来计算归一化数据(scaled_data)。如果 keepdim 参数设置为 False,该函数会从位置和尺度张量中移除指定维度。该函数还通过断言确保归一化数据、位置和尺度张量不包含 NaN 值,并确保尺度不为零。该函数返回一个包含归一化数据、位置和尺度张量的元组。
该描述内容详尽,准确说明了我们提供的函数及其底层算法。
生成文档是代码摘要的一项重要应用,因为它不仅可以帮助未来阅读代码的用户理解代码,还可以借助 pydoc 等工具自动生成文档。
现在,我们可以向 Granite Code 提出请求,让其为函数生成文档字符串。得益于更大的上下文窗口,我们可以同时提供完整函数代码以及生成文档字符串的请求。
请为以下函数编写格式规范的 Python 文档字符串:
Granite Code 可生成格式规范的 Python 文档:
使用指定的键和结束字符,从给定的 HTML 字符串中提取 JSON 对象。Args: html (str): The input HTML string to extract the JSON object from. key (str): The key used to locate the start of the JSON object in the HTML string. num_chars (int, optional): The number of characters after the key to include in the extracted JSON object.Defaults to 2. stop (str, optional): The character that marks the end of the JSON object in the HTML string.Defaults to '"'. Returns: str: The extracted JSON object as a string.
现在,如果需要,我们可以重新使用之前的提示,因为它仍保留在较大的上下文窗口中:
chat = "Rewrite the previous function to be a class method and include the docstrings." # have the encoder turn text into output tokens inputs = tokenizer(chat, return_tensors="pt").input_ids start_index = inputs.shape[-1] # generate output tokens output = model.generate(inputs,max_new_tokens=1500) # have the decoder turn output tokens into text output = tokenizer.batch_decode(output[start_index:]) # loop over the batch to print, in this example the batch size is 1 print(output[0])
This returns
class HtmlJsonExtractor: def init(self, html): """ Initializes the HtmlJsonExtractor object with the given HTML string. Args: html (str): The input HTML string to extract JSON objects from. """ self.html = html @classmethod def extract(cls, html, key, num_chars=2, stop='"'): """ Extracts a JSON object from the given HTML string using the specified key and stop character. Args: html (str): The input HTML string to extract the JSON object from. key (str): The key used to locate the start of the JSON object in the HTML string. num_chars (int, optional): The number of characters after the key to include in the extracted JSON object.Defaults to 2. stop (str, optional): The character that marks the end of the JSON object in the HTML string. Defaults to '"'. Returns: str: The extracted JSON object as a string. """ pos_begin = html.find(key)+ len(key) + num_chars pos_end = html.find(stop,pos_begin) return html[pos_begin:pos_end]
模型现在已经创建了一个名称合适的类别,将之前的代码转换为该类的方法,并添加了相应的文档字符串。
现在,我们来向模型提出请求,帮助我们优化一段代码:
chat = """Please optimize the following code: def fib(n): if (n < 2): return 1 else: return fib(n-1) + fib(n-2)"""
Granite 代码返回:
给定的 Python 函数 fib(n) 使用递归方式计算第 n 个斐波那契数。但是,该实现的时间复杂度为 O(2^n),对于较大的输入而言效率较低。我们可以使用动态规划或记忆化方法对其进行优化,将时间复杂度降低至 O(n)。以下是该函数的优化版本:
```python
def fib_optimized(n, memo={}):
if n < 2:
return 1
elif n not in memo:
memo[n] = fib_optimized(n-1, memo) + fib_optimized(n-2, memo)
return memo[n]
```
此优化版本使用字典 memo 存储之前计算出的斐波那契数。在计算第 n 个斐波那契数时,该函数首先检查该数是否已计算并存储在 memo 中。如果已存在,则直接返回存储的值,而无需重新计算,从而节省时间并提升性能。
在这里,模型不仅解释了该函数在效率方面存在的问题,并分析了其 Big-O 复杂度,还提供了更优的实现方案。提供代码审查和解释是利用代码摘要能力的一种方式,也是更广泛代码生成流程的一部分,可帮助开发人员改进代码。
在本教程中,您学习了代码摘要,并使用具有 128k 词元扩展上下文窗口的 Granite Code 模型生成 Python 代码说明。我们还使用提示生成新的文档,为代码片段添加类别,并利用扩展后的上下文窗口将该文档附加到新的代码上下文中。最后,我们让 Granite Code 分析并总结代码片段,并说明可以如何对其进行改进。
借助您的 AI 合作伙伴 IBM® Bob,加速软件交付,实现安全的意图感知型开发。
利用企业级工具更快地开发、部署和管理 AI 应用程序。
用智能 AI 现代化重新构想旧版系统。
1 Sonia Haiduc、Jairo Aponte、Andrian Marcus,“支持程序理解与源代码摘要”, ICSE '10:第 32 届 ACM/IEEE 国际软件工程会议论文集 https://doi.org/10.1145/3377811.3380383。
2 Paul W. McBurney, Collin McMillan,《Java 方法上下文的自动源代码摘要》, https://ieeexplore.ieee.org/document/7181703。
3 Chen Lin、Zhichao Ouyang、Junqing Zhuang、Jianqiang Chen、Hui Li、Rongxin Wu,《利用块状抽象语法树拆分改进代码摘要》IEEE/ACM,国际程序理解会议(ICPC 2021) https://arxiv.org/abs/2103.07845。
4 Jian Zhang、Xu Wang、Hongyu Zhang、Hailong Sun、Xudong Liu,“基于检索的神经源代码摘要”,ICSE '10:第 32 届 ACM/IEEE 软件工程国际会议论文集,https://doi.org/10.1145/1810295.1810335。
5 Xinyi Hou、Yanjie Zhao、Yue Huang、Zhou Yang、Kailong Wang、Li Li、Xiapu Luo、David Jin、John Grundy、Haoyu Wang,《软件工程的大语言模型:系统文献综述》, https://arxiv.org/abs/2308.10620。