코드 요약은 코드 스니펫을 자연어로 설명하는 작업입니다. 일반적인 코드 요약 작업에는 새로운 코드베이스 탐색, 새로운 프로그래밍 언어 학습, 코드 코멘트 생성 및 함수 설명 생성 등이 포함됩니다. 코드 스니펫의 요약을 생성하는 과정은 자연어 문서의 텍스트 요약을 생성하는 과정과 유사합니다. 다만 요약을 생성하는 대규모 언어 모델(LLM)이 달성하려는 기본 논리를 식별하는 동시에 읽고 있는 프로그래밍 언어를 이해해야 한다는 점에서 차이가 있습니다.
코드 요약은 자동 소스 코드 요약 수행, 문서화를 위한 자연어 요약 생성 또는 대규모 코드베이스 분석을 통해 소프트웨어 유지 관리에 도움을 주는 소프트웨어 개발의 중요한 요소입니다. 트랜스포머 기반 접근 방식을 사용하는 최신 LLM 모델은 코드 요약 모델로 작동하거나 코드 생성도 수행할 수 있습니다 이러한 기능이 가능한 이유는 모델이 코드와 코멘트, 그리고 해당 코드의 문서를 포함하는 GitHub 리포지토리 등의 소스로 구축된 대규모 데이터 세트로 학습되었기 때문입니다.
LLM이 널리 사용되기 전에는 코드 요약을 위해 코드 의미를 분석하고 코드의 각 식별자에 대한 추상 구문 트리(AST)를 생성한 다음, 이를 사용해 문서를 생성하는 방식이 필요했습니다.1,2 딥 러닝과 신경망의 등장 이후 컴퓨터 과학에 뿌리를 둔 접근 방식은 신경망 기계 번역의 방법론을 더 많이 차용한 접근 방식으로 대체되었습니다.3,4
트랜스포머 모델의 경우 컨텍스트 윈도우가 클수록 더 나은 결과를 얻을 수 있습니다. Granite-8B-Code-Instruct-128K와 같은 최신 기술 수준의 Granite Code 모델 다수는 128K 컨텍스트 윈도우를 갖추고 있습니다. 컨텍스트 윈도우가 크면 모델이 작업 메모리에 더 많은 텍스트를 유지할 수 있습니다. 이를 통해 긴 대화, 장문의 문서 또는 코드베이스에서 핵심 순간과 세부 정보를 추적할 수 있습니다. 이러한 작업 메모리를 바탕으로 LLM 기반 챗봇은 현재 시점과 더 긴 맥락 모두에서 자연스러운 응답을 생성할 수 있어, 사람의 평가와 평가 지표 모두에서 더 작은 컨텍스트 윈도우를 가진 모델보다 뛰어난 성능을 보일 수 있습니다.5
컨텍스트 윈도우가 크면 모델이 작업 메모리에 더 많은 텍스트를 유지할 수 있어, 긴 대화나 장문의 문서 또는 코드베이스에서 핵심 순간과 세부 정보를 추적하는 데 도움이 됩니다.
ChatGPT가 처음 도입되었을 때 컨텍스트 윈도우는 4,000토큰이었습니다. 대화가 채팅 인터페이스의 3,000단어 제한을 넘어서면 챗봇은 할루시네이션을 일으키고 주제에서 벗어날 가능성이 컸습니다. 현재 표준은 32,000토큰이며, 업계는 128,000토큰으로 전환하고 있습니다. 이는 약 250페이지 분량의 책에 해당합니다. IBM은 현재 128,000토큰 윈도우를 갖춘 Granite 모델 두 개를 제공하고 있으며, 더 많은 모델이 출시될 예정입니다.
이 단계에서는 Jupyter Notebook에 액세스할 수 있도록 IBM 계정을 생성하는 과정을 안내합니다.
1. IBM Cloud 계정을 사용하여 watsonx.ai에 로그인합니다.
2. +를 클릭하여 새 프로젝트를 만듭니다.
a. 빈 프로젝트 만들기를 선택합니다.
b. 이름 필드에 프로젝트 이름을 입력합니다.
c. 프로젝트 자산을 저장할 Cloud Object Storage가 아직 생성되어 있지 않다면 새로 생성합니다.
d. 만들기를 선택합니다.
3. Jupyter Notebook을 생성합니다.
a. 프로젝트 환경에서 자산 탭을 선택합니다.
b. 새 자산을 클릭합니다.
c. 왼쪽 패널에서 모델 작업 옵션을 선택합니다.
d. Python 및 R 노트북을 사용한 데이터 및 모델 작업을 클릭합니다.
e. 이름 필드에 노트북 이름을 입력합니다. 구성은 Python의 Runtime 23.1(4 vCPU, RAM 16GB)을 선택하여 지정합니다.
f. 만들기를 선택합니다.
4. watsonx.ai Runtime 인스턴스와 API 키를 설정합니다.
a. watsonx.ai Runtime 서비스 인스턴스를 생성합니다. 적절한 리전을 선택하고 무료 인스턴스인 Lite 요금제를 선택합니다.
b. API 키를 생성합니다.
c. watsonx.ai Runtime 서비스 인스턴스를 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 # 관측 데이터가 모두 0이면 nanmedian은 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) # 스케일링된 데이터, loc 또는 scale에 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)
# 인코더가 텍스트를 출력 토큰으로 변환하도록 합니다.
inputs = tokenizer(chat, return_tensors="pt").input_ids
start_index = inputs.shape[-1]
# 출력 토큰을 생성합니다.
output = model.generate(inputs, max_new_tokens=1500)
# 디코더가 출력 토큰을 텍스트로 변환하도록 합니다.
output = tokenizer.batch_decode(output[start_index:])
# 배치를 순회하며 출력합니다. 이 예제에서 배치 크기는 1입니다.
print(output[0])
생성된 요약을 확인해 보겠습니다.
답변: 이 함수는 사분위 범위(IQR) 방법을 사용하여 데이터를 정규화합니다. 이 함수는 data와 weights라는 두 개의 인수를 사용합니다. data 인수는 형태가 (batch_size, sequence_length, hidden_size)인 텐서입니다. 여기서 batch_size는 배치의 샘플 수, sequence_length는 입력 시퀀스의 길이, hidden_size는 은닉 상태의 차원 수를 의미합니다. weights 인수는 형태가 (batch_size, sequence_length)인 텐서이며, 각 요소는 0 또는 1로, data 텐서의 해당 요소가 관측된 값인지 누락된 값인지를 나타냅니다. 먼저 함수는 data 및 weights 텐서의 형태가 같은지 확인합니다. 그런 다음 torch.nanmedian 및 torch.nanquantile 함수를 사용하여 관측된 데이터 포인트, 즉 가중치가 1인 데이터 포인트의 중앙값과 IQR을 계산합니다. torch.nanmedian 함수는 지정된 차원(self.dim)을 따라 중앙값을 계산하고, torch.nanquantile 함수는 25번째와 75번째 백분위수를 계산합니다. 다음으로 함수는 정규화된 분포의 위치(loc) 및 스케일(scale) 매개변수를 계산합니다. 위치는 관측된 데이터 포인트의 중앙값으로 설정되고, 스케일은 관측된 데이터 포인트의 IQR로 설정됩니다. 관측된 데이터 포인트가 모두 0이면 함수는 위치를 0으로 설정합니다. 또한 0으로 나누는 것을 방지하기 위해 스케일이 최소값(self.minimum_scale) 이상이 되도록 합니다. 마지막으로 함수는 위치를 빼고 스케일로 나누어 정규화된 데이터(scaled_data)를 계산합니다. keepdim 인수가 False로 설정되면 함수는 위치 및 스케일 텐서에서 지정된 차원을 제거합니다. 또한 함수는 정규화된 데이터, 위치 및 스케일 텐서에 NaN 값이 없고 스케일에 0이 없는지 확인하는 단언문을 수행합니다. 이 함수는 정규화된 데이터, 위치 및 스케일 텐서가 포함된 튜플을 반환합니다.
이 설명은 매우 상세하며, 제공한 함수와 그 기반 알고리즘을 정확하게 설명합니다.
문서화를 생성하는 것은 코드 요약의 강력한 활용 사례입니다. 향후 코드를 읽는 모든 사용자에게 코드를 설명할 뿐 아니라 pydoc과 같은 도구를 사용해 문서를 생성하는 데에도 도움이 되기 때문입니다.
이제 Granite Code에 함수의 독스트링을 생성하도록 프롬프트를 제공할 수 있습니다. 컨텍스트 윈도우가 더 크기 때문에 함수 전체와 독스트링 생성 요청을 함께 제공할 수 있습니다.
다음 함수에 대해 올바른 형식의 Python 독스트링을 작성하세요.
Granite Code는 Python 형식의 우수한 문서를 반환합니다.
지정된 키와 종료 문자를 사용하여 제공된 HTML 문자열에서 JSON 객체를 추출합니다. 인수: html(str): JSON 객체를 추출할 입력 HTML 문자열입니다. key(str): HTML 문자열에서 JSON 객체의 시작 위치를 찾는 데 사용하는 키입니다. num_chars (int, 선택 사항): 추출된 JSON 객체에 포함할 키 뒤의 문자 수입니다. 기본값은 2입니다. stop (str, 선택 사항): HTML 문자열에서 JSON 객체의 끝을 표시하는 문자입니다. 기본값은 '"'입니다. 반환값: str: 문자열로 반환되는 추출된 JSON 객체입니다.
원한다면 대규모 컨텍스트 윈도우에 여전히 남아 있는 이전 프롬프트를 재사용할 수 있습니다.
chat = "이전 함수를 클래스 메서드로 다시 작성하고 독스트링을 포함하세요." # 인코더가 텍스트를 출력 토큰으로 변환하도록 합니다. input = tokenizer(chat, return_tensors="pt").input_ids start_index = inputs.shape[-1] # 출력 토큰을 생성합니다. output = model.generate(inputs, max_new_tokens=1500) # 디코더가 출력 토큰을 텍스트로 변환하도록 합니다. output = tokenizer.batch_decode(output[start_index:]) # 배치를 순회하며 출력합니다. 이 예제에서 배치 크기는 1입니다. print(output[0])
그러면 다음이 반환됩니다.
class HtmlJsonExtractor: def init(self, html): """ 제공된 HTML 문자열로 HtmlJsonExtractor 객체를 초기화합니다. Args: html (str): JSON 객체를 추출할 입력 HTML 문자열입니다. """ self.html = html @classmethod def extract(cls, html, key, num_chars=2, stop='"'): """ 지정된 키와 종료 문자를 사용하여 제공된 HTML 문자열에서 JSON 객체를 추출합니다. 인수: html(str): JSON 객체를 추출할 입력 HTML 문자열입니다. key(str): HTML 문자열에서 JSON 객체의 시작 위치를 찾는 데 사용하는 키입니다. num_chars (int, 선택 사항): 추출된 JSON 객체에 포함할 키 뒤의 문자 수입니다. 기본값은 2입니다. stop (str, 선택 사항): HTML 문자열에서 JSON 객체의 끝을 표시하는 문자입니다. 기본값은 '"'입니다. 반환: str: 문자열로 반환되는 추출된 JSON 객체입니다. """ pos_begin = html.find(key) + len(key) + num_chars pos_end = html.find(stop, pos_begin) return html[pos_begin:pos_end]
이제 모델은 적절한 이름의 클래스를 생성하고 이전 코드를 해당 클래스의 메서드로 만들었으며, 독스트링도 삽입했습니다.
이제 코드 스니펫 최적화를 지원하도록 모델에 프롬프트를 제공해 보겠습니다.
chat = """다음 코드를 최적화하세요: def fib(n): if (n < 2): return 1 else: return fib(n-1) + fib(n-2)"""
Granite Code는 다음을 반환합니다.
제공된 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) 복잡도를 제시했을 뿐 아니라, 개선된 구현도 제공했습니다. 검토와 설명을 제공하는 것은 요약 기능과 보다 일반적인 코드 생성을 활용하여 개발자가 코드를 개선하도록 돕는 방법입니다.
이 튜토리얼에서는 코드 요약을 살펴보고, 128,000토큰으로 확장된 컨텍스트 윈도우를 갖춘 Granite Code 모델을 사용하여 Python 코드에 대한 설명을 생성했습니다. 또한 프롬프트를 사용하여 새 문서를 생성하고, 코드 조각을 클래스로 감싸며, 확장된 컨텍스트 윈도우를 사용하여 해당 문서를 새 코드 창에 추가했습니다. 마지막으로 Granite Code가 코드 스니펫을 분석하고 요약하며 개선 방법을 설명하도록 했습니다.
IBM® Bob와 함께 안전하고 의도를 이해하는 AI 개발 파트너를 활용하여 소프트웨어 딜리버리를 가속화하세요.
엔터프라이즈 환경에 적합한 툴을 사용하여 AI 애플리케이션을 더욱 빠르게 개발, 배포 및 관리하세요.
지능형 AI 현대화로 레거시 시스템을 새롭게 혁신하세요.
1 Sonia Haiduc, Jairo Aponte, Andrian Marcus, “Supporting program comprehension with source code summarization,” ICSE ‘10: Proceedings of the 32nd ACM/IEEE International Conference on Software Engineering https://doi.org/10.1145/3377811.3380383.
2 Paul W. McBurney, Collin McMillan, “Automatic Source Code Summarization of Context for Java Methods,” https://ieeexplore.ieee.org/document/7181703.
3 Chen Lin, Zhichao Ouyang, Junqing Zhuang, Jianqiang Chen, Hui Li, Rongxin Wu, “Improving Code Summarization with Block-wise Abstract Syntax Tree Splitting” IEEE/ACM, International Conference on Program Comprehension (ICPC 2021) https://arxiv.org/abs/2103.07845.
4 Jian Zhang, Xu Wang, Hongyu Zhang, Hailong Sun, Xudong Liu, “Retrieval-based neural source code summarization,” ICSE ‘10: Proceedings of the 32nd ACM/IEEE International Conference on Software Engineering, 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, “Large Language Models for Software Engineering: A Systematic Literature Review”, https://arxiv.org/abs/2308.10620.