Claude 3.5 Sonnet와 W&B Weave를 활용한 arXiv 논문 자동 PDF 요약
Anthropic의 API와 W&B Weave를 활용해 Chain-of-Density를 적용한 arXiv 논문용 자동 PDF 요약 시스템을 만드는 방법을 알아보세요. 이 글은 AI 번역본입니다. 오역이 있을 수 있으니 댓글로 알려주세요.
Created on September 15|Last edited on September 15
Comment
그곳에 게재되는 모든 논문을 전부 읽는 것은 당연히 불가능합니다. 그래서 오늘의 목표는 arXiv 논문에 최적화된 자동 PDF 요약 및 질의응답 시스템을 구축하는 것이며, 이를 활용하기 위해 W&B Weave 그리고 Anthropic의 API.

구현 세부 사항에 들어가기 전에, 아래는 우리 모델이 생성한 출력 예시입니다(가독성을 위해 약간의 서식을 적용했습니다).
SliCK은 PCorrect에 기반한 세밀한 4범주 체계(HighlyKnown, MaybeKnown, WeaklyKnown, Unknown)를 통해 LLM 지식 통합의 한계를 보완하며, 지식 유형에 대한 미묘한 이해로 P(True)(Kadavath et al., 2022)를 능가합니다. 다중 예시 샘플링(Nex=10, 4샷 프롬프트, Nsample=16, T=0.5)은 Unknown 예시 식별을 개선하여, Unknown 비율 35% 조건에서 P(True) 방법 대비 파인튜닝 후 정확도 2%를 달성합니다.
분리된 학습/평가 분할을 갖춘 통제된 클로즈드북 QA는 능력 불일치(Huang et al., 2023)를 해결하여 지식 통합을 정밀하게 검증할 수 있게 한다. Exact Match 평가를 통해 패러프레이즈 탐지 문제를 완화한다. 선형 회귀 모델(Accuracy = β0 + βkn * (Nkn / |D|) + βunk * (Nunk / |D|))은 새로운 지식의 영향을 정량화하며, 분포 내 R² = 0.86, OOD에서 0.95를 보여 정성적 평가를 능가한다.
분포 외 평가에서 보지 못한 7개 관계를 사용한 결과, Unknown 예시의 영향이 유사한 추세를 보였습니다(OOD에서는 6포인트 하락, 분포 내에서는 14포인트 하락)로, 피상적 정렬 가설(Zhou et al., 2023)에 의문을 제기합니다. 학습 동역학 분석에서는 Unknown 예시가 더 느리게 적합됨을 보여줍니다(조기 종료 시 Known 75% 대비 Unknown 25%), 이는 LLM 지식 획득 관련 문헌의 공백을 보완합니다.
범주별 파인튜닝은 MaybeKnown 예시가 HighlyKnown만 사용하는 접근(40.5%)보다 더 우수함(정확도 43.6%)을 보여 주며, 단순한 지식 범주 가정을 흔듭니다. 불확실성 표현 리레이블링은 표준 파인튜닝에서 나타나는 과적합을 완화하여(정확도 43.0%에서 38.8%로 하락하는 데 비해) 정확도 61.8%를 유지합니다.
SliCK의 장점은 다음과 같습니다: 세분화된 범주화, 견고한 다중 예시 샘플링, 통제된 실험 설계, 정량적 영향 분석, 분포 내 및 OOD에 대한 포괄적 평가, 실용적 완화 전략(조기 종료, 필터링), 그리고 불확실성 표현 통합. 이러한 진전은 지식 통합과 파인튜닝 관행에서의 현재 과제를 해결하면서, LLM의 성능과 신뢰성을 향상시키기 위한 정밀하고 실증적 근거에 기반한 접근을 제공합니다.
서론은 이 정도로 하고, 이제 사용할 도구들을 소개하겠습니다:
PDF 요약 프로젝트를 위한 도구
- W&B Weave: 하나 LLMOps 플랫폼 해결책은 LLM 구성 요소의 매끄러운 통합과 효율적인 데이터 계보 추적입니다.
- arXiv API: 사용자 쿼리를 바탕으로 관련 논문을 검색하기 위해.
- PDF 처리연구 논문에서 텍스트와 시각적 콘텐츠를 추출하기.
- 밀도 연쇄 요약: 점점 더 밀도 높고 유익한 요약을 생성하는 기법."
Weave는 입력을 자동으로 캡처합니다 Request 및 출력 Response Anthropic API의 오브젝트를 사용하여, 실험을 매우 간단하게 만들 수 있도록 import weave; weave.init(...)
💡
우리 프로젝트의 목표:
다음을 수행할 수 있는 파이프라인을 만들고자 합니다:
- 사용자 정의 쿼리에 LLM 기반 쿼리 향상을 적용해 고도화한 뒤, 관련 arXiv 논문을 가져옵니다.
- PDF에서 텍스트와 이미지를 추출해 처리하고, LLM의 비전 기능을 활용해 이미지 내용을 텍스트로 변환합니다.
- 사용자 지정 지시에 따라 다층 반복 정제를 적용해 논문의 간결하고 기술적인 요약을 생성합니다.
- LLM을 심판으로 활용해 응축된 답변을 평가하고, 모델 선택과 같은 매개변수를 최적화하며, Chain of Density를 이용한 반복 정제가 어느 지점에서 더 이상 유의미한 이득을 주지 않는지 파악합니다.
Claude 3.5 Sonnet
Claude 3.5 Sonnet, Anthropic의 최신 모델(2024년 7월 11일 기준)은 이전 세대 대비 성능과 연산 효율이 크게 향상되었으며, Claude 3 Opus보다 두 배 빠르게 동작합니다. 고급 추론과 코딩 같은 복잡한 작업에서 뛰어나고, 차트와 그래프 해석, 이미지에서 텍스트를 전사하는 등 강화된 비전 기능을 제공하여 다양한 기술적 활용에 필수적입니다. 우리의 사용 사례에 이상적입니다.
Chain of Density 요약이란 무엇인가요?
Chain of Density 요약은 NLP 기법 문서에서 “밀도”(중요한 정보의 집중도)를 평가해 가장 유익한 문장을 식별하고 순위를 매깁니다. 그런 다음 상위 문장들을 선별해 핵심 아이디어와 주요 내용을 유지하는 간결한 요약을 생성합니다.
작동 방식은 다음과 같습니다:
- 초기 요약 생성먼저 엔터티가 드문 요약으로 시작합니다. 이 초기 요약은 다음을 사용해 생성됩니다. 대규모 언어 모델 (LLM) GPT-4와 유사하지만 세부 정보는 최소한으로 포함합니다.
- 누락된 엔터티 식별다음으로 초기 요약을 검토하여 누락된 핵심 세부 정보나 엔터티를 식별합니다. 이러한 엔터티는 본문의 주요 내용을 이해하는 데 필수적입니다.
- 연쇄 프롬프트추가 프롬프트를 구성하여 요약의 정보 밀도를 반복적으로 높입니다. 중요한 점은, 각 프롬프트가 요약 길이를 늘리지 않으면서 더 많은 엔터티를 추가하는 것을 목표로 한다는 것입니다.
- 실행과 정제이렇게 연쇄된 프롬프트를 LLM에 다시 입력해 더 조밀한 요약을 생성합니다. 이 과정은 요약이 간결하면서도 정보가 풍부해질 때까지 반복적으로 진행되어 점차 정교해집니다(이름의 유래가 여기에서 비롯됩니다).
- 최종 검토: 최종 요약은 가독성과 일관성을 유지하면서 모든 핵심 내용을 담아야 합니다. 이 과정은 정보량과 명료성의 균형을 맞춰, 요약이 과도하게 조밀하거나 따라가기 어렵지 않도록 보장합니다.
Chain-of-Density 요약을 사용하는 이유
이 방법을 사용하면 정보가 조밀하면서도 간결한 요약을 만들 수 있어, 정밀하고 상세한 정보 추출이 필요한 작업에 이상적입니다. 이는 바로 arXiv의 학술 논문을 요약할 때 요구되는 요소이기도 합니다.
파이프라인을 구축해 봅시다.
Weave와 Anthropic 설정 방법
PDF 전처리와 요약 파이프라인을 종합적으로 추적할 수 있도록 Weave 프로젝트를 먼저 초기화하겠습니다:
import weaveweave.init("arxiv-paper-summarization-anthropic")
이 초기화는 Weave의 실험 추적 기능을 설정하여 함수 입력과 출력, 파이프라인 성능(비용과 속도 포함)을 기록하고 분석할 수 있게 합니다.
다음으로 Anthropic 클라이언트를 초기화해 보겠습니다:
import anthropicimport osclient = anthropic.Anthropic(api_key=os.getenv("ANTHROPIC_API_KEY"))
Weave는 Anthropic과 통합되어 Anthropic 클라이언트에서 발생하는 LLM 요청과 응답을 자동으로 기록하며, 토큰 사용량, 관련 비용, 요청/응답 내용에 대한 유용한 인사이트를 제공합니다. 이 통합을 통해 모델 성능과 리소스 활용에 대한 정밀한 분석이 가능합니다.
선택 사항: API로 arXiv 논문 가져오는 방법(클릭하여 펼치기)
요약할 문서 선택 및 직렬화
우리는 Arxiv 논문을 표현하기 위해 Pydantic 모델을 사용하여 타입 안정성과 손쉬운 데이터 검증을 보장합니다. Weave에서의 예시는 다음과 같습니다:

코드:
from datetime import datetime, timezonefrom pydantic import BaseModel, Fieldfrom typing import List, Optionalclass Author(BaseModel):full_name: strclass Link(BaseModel):href: strtitle: Optional[str] = Nonerel: Optional[str] = Nonecontent_type: Optional[str] = Noneclass ArxivPaper(BaseModel):entry_id: strupdated: datetimepublished: datetimetitle: strauthors: List[Author]summary: strcomment: Optional[str] = Nonejournal_ref: Optional[str] = Nonedoi: Optional[str] = Noneprimary_category: strcategories: List[str]links: List[Link]pdf_url: Optional[str] = Nonedef __getitem__(self, key):return getattr(self, key)arxiv_paper = ArxivPaper(entry_id="<http://arxiv.org/abs/2406.04744v1>",updated=datetime(2024, 6, 7, 8, 43, 7, tzinfo=timezone.utc),published=datetime(2024, 6, 7, 8, 43, 7, tzinfo=timezone.utc),title="CRAG -- Comprehensive RAG Benchmark",authors=[Author(full_name="Xiao Yang"), Author(full_name="Kai Sun"), Author(full_name="Hao Xin")],summary="The long summary from the paper",doi="10.48550/arXiv.2406.04744",primary_category="cs.CL",categories=["cs.CL"],links=[Link(href="<https://arxiv.org/abs/2406.04744>", title="Abstract", rel="alternate"),Link(href="<https://arxiv.org/pdf/2406.04744>", title="pdf", rel="related")]pdf_url="https://arxiv.org/pdf/2406.04744")
메모리 효율적인 PDF 로딩 및 처리
우리는 사용합니다 PyPDF2.PdfReader PDF 조작을 위한 것으로, PDF 콘텐츠에 대한 파이썬다운 인터페이스를 제공합니다:
import requestsimport ioimport PyPDF2def load_pdf(arxiv_result):pdf_url = arxiv_result["pdf_url"]response = requests.get(pdf_url)pdf_file = io.BytesIO(response.content)pdf_reader = PyPDF2.PdfReader(pdf_file)return pdf_reader
위 함수는 PDF 콘텐츠를 스트리밍 방식으로 처리하여, 이 프로젝트처럼 파일이 큰 경우에도 메모리를 효율적으로 사용할 수 있습니다.
PDF의 벡터 그래픽 처리
PDF에는 벡터 그래픽(즉, SVG)이 포함될 수 있습니다. PyPDF2 이미지로 인식되지 않습니다. 이를 포착하기 위해 새로운 접근 방식을 사용합니다. 요지는, 이 함수가 벡터 그래픽이 있는 페이지의 “스크린샷”을 찍어 Claude가 처리할 수 있는 형식으로 변환한다는 것입니다.
from pdf2image import convert_from_bytesfrom PIL import Imageimport base64def convert_vector_graphic_page_to_image(pdf_page: PyPDF2.PageObject, scale_factor: float = 0.5) -> Optional[str]:def get_object(obj):return obj.get_object() if isinstance(obj, PyPDF2.generic.IndirectObject) else objresources = get_object(pdf_page.get('/Resources', {}))xobject = get_object(resources.get('/XObject', {}))for obj in xobject.values():obj = get_object(obj)if isinstance(obj, dict) and obj.get('/Subtype') == '/Form': # Indicates a vector graphicpdf_bytes = io.BytesIO()PyPDF2.PdfWriter().add_page(pdf_page).write(pdf_bytes)pdf_bytes.seek(0)images = convert_from_bytes(pdf_bytes.getvalue(), fmt='png')if images:image = images[0]new_size = tuple(int(dim * scale_factor) for dim in image.size)image = image.resize(new_size, Image.LANCZOS)img_byte_arr = io.BytesIO()image.save(img_byte_arr, format='PNG')img_str = base64.b64encode(img_byte_arr.getvalue()).decode("utf-8")data_url = f"data:image/png;base64,{img_str}"return data_urlreturn None
Claude 3.5 Sonnett로 이미지를 텍스트 설명으로 변환하기
우리 파이프라인에서는 Claude의 고급 비전 능력을 활용해 학술 논문의 이미지를 해석합니다. 이 과정은 시각 정보를 손쉽게 처리하고 분석할 수 있는 텍스트 데이터로 변환하는 데 필수적입니다. 우리는 상황에 따라 두 가지 접근 방식을 사용합니다. 단일 독립 그림(figure)에 대한 처리와, 여러 벡터 그래픽이 포함될 수 있는 전체 PDF 페이지에 대한 처리입니다.

독립 이미지부터 시작하겠습니다.
시나리오 1: 독립 이미지
아래 프롬프트는 과학 도표에서 포괄적이고 기술적으로 정확한 정보를 추출하도록 설계되었습니다.
1. 구조화된 분석번호 매기기 목록은 Claude가 도표를 체계적으로 검토하도록 안내하여 모든 중요한 측면이 빠짐없이 다뤄지도록 합니다.
2. 기술적 초점“자세한 기술적 설명”을 명시적으로 요청하면, Claude가 도메인 특화 언어를 사용하고 피상적인 관찰을 피하도록 유도할 수 있습니다.
3. 다재다능성: 이 프롬프트는 다양한 연구 분야의 여러 유형의 과학 도표에 유연하게 적용됩니다.
4. 정량적 강조정량적 정보에 특별한 주의를 기울이며, 이는 과학 연구에서 매우 중요합니다.
5. 문맥적 이해방법론, 함의, 한계를 질문함으로써, 도형을 더 넓은 연구 ���락 속에서 해석하도록 Claude를 유도합니다.
6. 정밀 지침최종 지침은 모호하거나 일반적인 관찰이 아니라 구체적이고 과학적으로 관련 있는 정보를 제공하도록 Claude를 유도합니다.
@weave.op()def process_figure_image(data_url: str, model: str = "claude-3-5-sonnet-20240620") -> str:img_str = data_url.split(",")[1]prompt = """Analyze this image as if it's a figure from a scientific research paper. Provide a detailed technical description addressing:1. Type of figure (e.g., graph, diagram, flowchart, experimental setup)2. Key components or variables represented3. Relationships or trends depicted4. Quantitative information (if present)5. Methodology or process illustrated (if applicable)6. Potential implications or conclusions that can be drawn7. Any limitations or assumptions evident in the figureFocus on technical accuracy and relevance to scientific research. Avoid general descriptions and concentrate on the specific scientific content presented."""response = client.messages.create(model=model,max_tokens=4096,messages=[{"role": "user","content": [{"type": "image", "source": {"type": "base64", "media_type": "image/png", "data": img_str}},{"type": "text", "text": prompt}]}])return response.content[0].text
시나리오 2: 벡터 그래픽이 포함된 전체 PDF 페이지
아래 프롬프트는 전체 PDF 페이지에서 정보를 추출하는 복잡한 작업에 맞춰 최적화되었습니다.
1. 맥락 설정이 문장은 이미지가 PDF의 전체 페이지임을 Claude에게 알려 분석을 위한 전제를 마련합니다.
2. 집중된 주의Claude에게 벡터 그래픽 도형이나 차트만 식별하고 집중하도록 지시합니다. 이는 혼합 콘텐츠가 있는 페이지에서 특히 중요합니다.
3. 이미지별 구조적 분석각 도형마다 독립형 도형 분석 방식과의 일관성을 유지하면서, 상세한 기술 분석을 요청합니다.
4. 여러 이미지 처리: 하나의 페이지에 여러 도형이 있을 가능성을 고려하여, 출력의 구조가 명확하도록 프롬프트를 설계합니다.
5. 제외 지침Claude에게 텍스트와 기타 비벡터 그래픽 요소는 명시적으로 무시하고, 관심 대상인 시각적 데이터에만 집중하도록 지시합니다.
6. 기술적 초점: 최종 지침은 정확하고 기술적인 서술을 유지하며 높은 과학적 관련성을 확보해야 함을 강조합니다.
@weave.op()def process_vector_image_pdf(data_url: str, model: str = "claude-3-5-sonnet-20240620") -> str:img_str = data_url.split(",")[1]prompt = """This image is a full page from a scientific paper PDF, converted to PNG format. It may contain one or more vector graphic figures or charts. Your task is to:1. Identify and focus solely on the vector graphic figures or charts within the page.2. For each identified figure or chart, provide a detailed technical analysis addressing:a. Type of figure (e.g., graph, diagram, flowchart)b. Key components or variables representedc. Relationships or trends depictedd. Quantitative information (if present)e. Methodology or process illustrated (if applicable)f. Potential implications or conclusions that can be drawn3. Ignore any text or other elements on the page that are not part of the vector graphic figures.4. If multiple figures are present, analyze each separately and clearly indicate which figure you are describing.Focus on providing accurate, technical descriptions of the vector graphic content only."""response = client.messages.create(model=model,max_tokens=4096,messages=[{"role": "user","content": [{"type": "image", "source": {"type": "base64", "media_type": "image/png", "data": img_str}},{"type": "text", "text": prompt}]}])return response.content[0].text
이 두 프롬프트는 학술 논문의 비구조적 시각 데이터와 구조화되어 분석 가능한 텍스트 데이터 사이를 잇는 핵심적인 연결 고리 역할을 합니다. 이 접근법은 다음을 가능하게 합니다:
- 도표에서 구조적이고 기술적인 정보를 추출하여, 자동화된 문헌 검토나 연구 논문에서의 데이터 추출과 같은 작업에 유용하게 활용합니다.
- 과학 문서에서 텍스트 설명과 시각 요소를 연결한 풍부한 멀티모달 데이터셋의 생성.
- 과학 도해 해석에 특화된 모델의 추가 학습 가능성.
- 과학 문서 처리에서 흔한 난제인 복잡하고 다요소로 구성된 PDF 페이지의 처리.
- 다양한 과학 분야 전반에서의 확장성으로, 서로 다른 유형의 연구 논문에 맞게 시스템을 유연하게 적응시킬 수 있습니다.
이는 밀도 체인 요약 과정이 텍스트와 시각 요소 양쪽의 정보를 모두 반영하도록 보장하며, 텍스트와 도해가 모두 풍부한 연구가 많은 우리의 프로젝트에 필수적입니다.
포괄적인 이미지 추출 및 처리
위의 모든 단계를 하나로 통합하여 전체 PDF를 처리하는 단일 함수를 구성합니다:
import filetype@weave.op()def extract_images(paper, model="claude-3-5-sonnet-20240620"):"""Extract text and images from PDF content."""pdf_reader = load_pdf(paper)all_images = []aspect_ratio_sizes = {(1, 1): (1092, 1092),(3, 4): (951, 1268),(2, 3): (896, 1344),(9, 16): (819, 1456),(1, 2): (784, 1568)}def get_closest_aspect_ratio(width, height):img_ratio = width / heightreturn min(aspect_ratio_sizes.keys(), key=lambda x: abs(x[0]/x[1] - img_ratio))for page in pdf_reader.pages:images = []for image in page.images:img_data = image.datakind = filetype.guess(img_data)if kind is None:print("Cannot guess file type!")continue# Resize image if necessaryimg = Image.open(io.BytesIO(img_data))closest_ratio = get_closest_aspect_ratio(img.width, img.height)new_size = aspect_ratio_sizes[closest_ratio]if img.width != new_size[0] or img.height != new_size[1]:img = img.resize(new_size, Image.LANCZOS)# Convert resized image back to bytesimg_byte_arr = io.BytesIO()img.save(img_byte_arr, format=img.format if img.format else 'PNG')img_data = img_byte_arr.getvalue()img_str = base64.b64encode(img_data).decode("utf-8")data_url = f"data:{kind.mime};base64,{img_str}"try:images.append({"image": data_url, "description": process_figure_image(data_url, model=model)})except Exception as e:print(f"Error processing image: {e}")images.append({"image": data_url, "description": ""})vector_graphics_image_data_url = convert_vector_graphic_page_to_image(page)if vector_graphics_image_data_url:images.append({"image": vector_graphics_image_data_url, "description": process_vector_image_pdf(vector_graphics_image_data_url, model=model)})all_images.append(images)return all_images
참고: 이 함수는 래스터 이미지와 벡터 그래픽을 모두 적절히 처리하고, Claude를 사용해 상세한 설명을 생성하며, 각 이미지가 Claude의 입력 크기 요구사항과 정확히 일치하도록 보장합니다.
이미지를 텍스트 설명으로 대체하기
마지막으로, 이미지 설명을 논문 본문에 통합합니다. 아래 함수는 각 페이지에서 텍스트를 추출한 뒤, 적절한 위치에 이미지 설명을 삽입하여 시각 자료까지 포함한 논문의 전체 내용을 포괄적으로 표현한 텍스트를 생성합니다:
@weave.op()def replace_images_with_descriptions(paper, images):pdf_reader = load_pdf(paper)text = ""for page_num, page in enumerate(pdf_reader.pages):text += page.extract_text() + "\n\n"if images[page_num] and len(images[page_num]) > 0:text += f"\n\n[Image Descriptions for page {page_num+1}]\n"for image_num, image in enumerate(images[page_num]):text += f"\n[Image {image_num+1}]: {image['description']}\n"text += "[END OF IMAGE DESCRIPTIONS]\n"return text
Weave에서 보면 다음과 같습니다:

컨텍스트 윈도우에 대한 추가 설명
PDF의 길이가 LLM의 컨텍스트 길이를 초과할 수 있으며, 반대로 긴 컨텍스트 모델에 PDF 전체를 넘기더라도 텍스트를 과도하게 전달하면 중요한 정보가 누락될 수 있습니다.
| Models | Context Window | Input Cost / 1M tokens | Output Cost / 1M tokens |
|---|---|---|---|
| Claude 3 Opus | 200,000 | $15.00 | $75.00 |
| Claude 3 Sonnet | 200,000 | $3.00 | $15.00 |
| Claude 3 Haiku | 200,000 | $0.25 | $1.25 |
| Claude 3.5 Sonnet | 200,000 | $3 | $15 |
기억해 둘 만한 점:

요약에 접근하는 방식에는 여러 층위가 있습니다. 접근법을 정교화해 가면서 각 층위에서의 품질을 면밀히 점검해야 합니다.
Chain-of-Density 요약 구현
이제 Chain‑of‑Density를 구현할 준비가 되었습니다. 곧 이어지는 코드와 상세 설명과 함께, Weave에서 어떻게 보이는지도 확인할 수 있습니다:

우리 요약 파이프라인의 핵심은 다음 함수들에 구현되어 있습니다:
1. 이전 요약과 문서에서 현재 누락된 엔티티를 바탕으로 요약을 생성합니다.
The summarize_current_summary 단계:
- 우리의 Chain‑of‑Density 구현의 토대를 이룹니다
- 정교하게 설계한 프롬프트로 언어 모델을 안내합니다
- 모델이 새로운 기술적 엔티티를 식별하도록 지시합니다
- 새로운 엔티티를 요약에 통합합니다
- 주어진 지시에 대한 관련성을 유지하면서 전체 정보 밀도를 높입니다
코드:
@weave.op()def summarize_current_summary(document, instruction, current_summary="", iteration=1, model="claude-3-5-sonnet-20240620"):# Define the maximum number of tokens for the model's responsemax_tokens = 4096# Construct the prompt for the LLMprompt = f"""Document: {document}Current summary: {current_summary}Instruction to focus on: {instruction}Iteration: {iteration}Generate an increasingly concise, entity-dense, and highly technical summary from the provided document that specifically addresses the given instruction using the below approach:1. Carefully read the current summary and the instruction.2. Identify 1-3 new, important technical entities or ideas from the original text that:- Are directly relevant to the instruction- Are not yet present in the current summary- Add significant, specific information to the summary- Are preferably 5 words or fewer- May include methodologies, algorithms, metrics, or key findings- Ensure to include this in the output before the summary3. Write a new summary that:- Incorporates the newly identified entities/ideas- Retains all crucial information from the current summary- Increases overall information density- Remains focused on addressing the instruction- Utilizes the response window of {max_tokens} tokensGuidelines:- Prioritize technical accuracy and specificity over general readability- Use precise terminology, domain-specific jargon, and include quantitative details where relevant- Ensure all information is directly related to the instruction- Make every word count: rewrite to improve density and make space for new technical entities- Employ fusion, compression, and removal of less informative phrases to increase density- Never drop entities or technical details from the current summary that are relevant to the instruction- Maintain coherence while maximizing information densityYour goal is to create a summary that is noticeably denser, more technical, and more informative than the previous one, utilizing the response window of {max_tokens} tokens while staying laser-focused on the instruction. The summary should be suitable for an expert audience in the field."""# Make the API call to the LLMresponse = anthropic_client.messages.create(model=model,max_tokens=max_tokens,messages=[{"role": "user", "content": prompt}])# Return the generated summaryreturn response.content[0].text
2. N회 반복하여 고밀도 요약을 누적합니다
The iterative_density_summarization 단계:
- 반복적 정제 과정을 오케스트레이션합니다
- 반복적으로 호출합니다 summarize_current_summary
- 각 반복의 출력을 다음 반복의 입력으로 사용합니다
- 기술적 세부사항을 점진적으로 축적할 수 있도록 합니다
- 정보 밀도를 점진적으로 높입니다
코드:
@weave.op()def iterative_density_summarization(document, instruction, current_summary, density_iterations, model):# Initialize a list to store summaries from each iterationiteration_summaries = []# Iterate through the specified number of density iterationsfor iteration in range(1, density_iterations + 1):# Generate a new summary based on the current summary and documentcurrent_summary = summarize_current_summary(document, instruction, current_summary, iteration, model)# Add the new summary to the list of iteration summariesiteration_summaries.append(current_summary)# Print the current iteration and summary for monitoringprint(f"Iteration {iteration}:\n{current_summary}\n")# Return the final summary and the list of all iteration summariesreturn current_summary, iteration_summaries
3. 누적된 결과를 바탕으로 최종 한 번 더 검토하여 출력물을 정리합니다
The final_summary 단계:
- 반복 과정을 마친 뒤 최종 응축 단계를 수행합니다
- 요약 길이를 30~40% 줄이는 것을 목표로 합니다
- 모든 핵심 기술적 내용을 유지합니다
- 지시사항에 대한 관련성을 유지하면서 정보 밀도를 최대화하도록 최적화합니다
코드:
@weave.op()def final_summary(instruction, current_summary, model):# Construct the prompt for the final summary generationprompt = (f"""Given this summary:{current_summary}And this instruction to focus on:{instruction}Create an extremely dense, final summary that captures all key technical information in the most concise form possible, while specifically addressing the given instruction. Follow these guidelines:1. Aim to reduce length by 30-40% while retaining all critical technical content relevant to the instruction.2. Prioritize highly specific methodologies, algorithms, metrics, and findings that directly address the instruction.3. Preserve precise quantitative data, including statistical significance and error margins where applicable and relevant to the instruction.4. Maintain the use of domain-specific terminology and technical jargon pertinent to the instruction.5. Ensure that all key entities and concepts from the original summary that relate to the instruction are represented.6. Use compact phrasing and remove any remaining non-essential information that doesn't directly contribute to addressing the instruction.7. If relevant to the instruction, include brief mentions of limitations, assumptions, or conflicting viewpoints.8. Optimize for information density while maintaining coherence for an expert audience, always keeping the focus on the given instruction.The final summary should be a highly concentrated, technical distillation of the research that specifically addresses the given instruction, suitable for specialists in the field.""")# Make the API call to the LLM for the final summaryresponse = anthropic_client.messages.create(model=model,max_tokens=4096,messages=[{"role": "user", "content": prompt}])# Return the generated final summaryreturn response.content[0].text
4. 체인 오브 덴시티 요약으로 통합하기
The chain_of_density_summarization 단계:
- 요약 프로세스의 주요 진입점 역할을 합니다
- 전체 요약 파이프라인을 조정합니다
- 반복 요약을 시작합니다
- 최종 응축을 적용합니다
- 다음을 포함한 포괄적인 결과 세트를 반환합니다:
- 최종 요약
- 누적 요약
- 모든 중간 요약
코드:
@weave.op()def chain_of_density_summarization(document, instruction, current_summary="", model="claude-3-5-sonnet-20240620", density_iterations=2):# Perform iterative density summarizationcurrent_summary, iteration_summaries = iterative_density_summarization(document, instruction, current_summary, density_iterations, model)# Generate the final, highly condensed summaryfinal_summary_text = final_summary(instruction, current_summary, model)# Print the final summary for monitoringprint(f"Final Summary:\n{final_summary_text}\n")# Return a dictionary containing all generated summariesreturn {"final_summary": final_summary_text,"accumulated_summary": current_summary,"iteration_summaries": iteration_summaries,}
종합하면, 이 구현은 Chain‑of‑Density 기법을 활용해 점점 더 밀도 높고 정보량이 많은 요약을 생성합니다.
요약을 반복적으로 정제하고 기술적 개체와 아이디어에 초점을 맞춤으로써, 특정 지시에 맞춘 간결하면서도 매우 정보량이 높은 요약을 생성합니다. 이 과정은 기술적 정확성, 도메인 특화 용어, 정량적 세부 정보를 우선시하여, 전문가 독자를 위한 복잡한 과학 문서를 요약하는 데 특히 적합합니다.
W&B Weave에서의 동작 방식
우리의 요약을 점검하고 LLM 평가를 비교하는 등 다양한 작업에 W&B Weave를 사용할 것입니다.
Weave 모델 객체
먼저 요약 파이프라인을 캡슐화할 모델 객체를 만들어야 합니다. Weave에서의 예시는 다음과 같습니다:

아래 클래스는 우리의 요약 파이프라인을 Weave 모델로 캡슐화합니다. 상속을 통해 weave.Model 그리고 사용하여 @weave.op() decorator을 통해 입력, 출력, 코드 변경 사항의 자동 버전 관리와 추적을 활성화합니다. 이를 통해 실험을 손쉽게 재현하고, 서로 다른 모델 버전이나 파라미터 설정 간의 결과를 비교할 수 있습니다.
class ArxivChainOfDensityPipeline(weave.Model):model: str = "claude-3-5-sonnet-20240620" density_iterations: int = 3 def __init__(self, model: str = "claude-3-5-sonnet-20240620", density_iterations: int = 3):super().__init__()self.model = modelself.density_iterations = density_iterations@weave.op()def predict(self, paper: ArxivPaper, instruction: str) -> dict:extracted_images = extract_images(paper)cleaned_text = replace_images_with_descriptions(paper, extracted_images)result = chain_of_density_summarization(cleaned_text, instruction, model=self.model, density_iterations=self.density_iterations)return result
평가 데이터셋
다음으로, 예시 arXiv 논문과 지침을 사용해 평가 데이터셋을 만들어 봅시다. 대략 다음과 같은 형태가 됩니다:

아래 코드는 평가를 위해 논문, 지침, 원문 요약을 결합한 Weave Dataset 객체를 생성합니다. The weave.Dataset 클래스를 사용하면 평가 데이터를 버전 관리하고 추적할 수 있어 실험의 재현성을 보장합니다. 또한 데이터셋을 다음과 함께 게시하면 weave.publish()을 통해 데이터셋을 공개하여 향후 사용 및 비교가 가능하도록 합니다.
eval_papers = [arxiv_paper3]eval_instructions = ["Summarize the key methodologies and novel contributions of this research, focusing on their potential impact in the field.",]eval_data = list(product(eval_papers, eval_instructions))dataset = weave.Dataset(name="we-paper-reading-eval-data", rows=[{"paper": arxiv_paper, "instruction": instruction, "summary": arxiv_paper.summary} for arxiv_paper, instruction in eval_data])weave.publish(dataset)
정제 과정 전반에서 반복 요약 파이프라인 평가하기
다음으로, 요약의 품질을 평가하기 위한 여러 지표를 구현하겠습니다.

- 관련성
- 기술적 품질
- 간결성
여기에는 몇 가지 이점이 있습니다. 첫째, 요약 품질의 미묘한 측면을 포착하는 데 도움이 됩니다. 또한 주어진 지시를 요약이 얼마나 잘 충족하는지에 대한 총체적 평가를 제공하며, 간결성을 고려하면서 기술적 정확성도 함께 평가합니다.
문제가 된 코드:
@weave.op()def score_summary(summary, summary_type, instruction, model):openai_client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))# Construct a detailed prompt for the GPT model to evaluate the summary prompt = f"""Evaluate the quality of the following {summary_type} based on how well it addresses the given instruction. Use the scoring rules below to calculate three numerical scores between 0 and 10.Instruction: {instruction}{summary_type}:{summary}Scoring Rules:1. Relevance (0-5): [Detailed scoring criteria for relevance]2. Technical Quality (0-5): [Detailed scoring criteria for technical quality]3. Conciseness (0-5): [Detailed scoring criteria for conciseness]Provide your evaluation in the following JSON format:{{ "relevance": {{ "score": <float> }}, "technical_quality": {{ "score": <float> }}, "conciseness": {{ "score": <float> }}}}Ensure your response is ONLY valid JSON. Do not include any other text outside the JSON object.Ensure you have the keys: relevance, technical_quality, conciseness, each containing only a score.Ensure each score is a float between 0 and 10, using the scoring rules provided above.""" # Make an API call to the GPT model for evaluation response = openai_client.chat.completions.create(model=model,messages=[{"role": "user", "content": prompt}],response_format={"type": "json_object"})# Parse and return the JSON response return json.loads(response.choices[0].message.content)
다음 함수는 여러 요약에 걸친 점수 분포를 분석합니다. 각 항목(관련성, 기술적 품질, 간결성)에 대해 평균 점수와 “꼬리 비율”(상위 5% 점수의 평균을 전체 평균과 비교한 값)을 계산합니다.
이는 잠재적 이상치나 뛰어난 고품질 요약을 식별하는 데 도움이 되기 때문에 유용합니다. 또한 전체 요약 프로세스의 성능을 파악하고, 모델이 뛰어난 부분과 개선이 필요한 부분을 함께 밝혀 줍니다.
코드:
@weave.op()def calculate_long_tail_stats(scores):if not scores:return None aspects = ['relevance', 'technical_quality', 'conciseness']stats = {}for aspect in aspects:try:# Handle different input formats (list of lists or list of dicts) if isinstance(scores[0], list):flattened_scores = [score[aspect]['score'] for sublist in scores for score in sublist]elif isinstance(scores[0], dict):flattened_scores = [score[aspect]['score'] for score in scores]else:print(f"Unexpected format for scores: {scores}")return None # Calculate statistics for each aspect stats[aspect] = {"mean": np.mean(flattened_scores),"tail_ratio": np.mean(sorted(flattened_scores)[-max(1, int(len(flattened_scores)*0.05)):]) / np.mean(flattened_scores),}except Exception as e:print(f"Error calculating stats for {aspect}: {str(e)}")stats[aspect] = None return stats
다음 함수는 반복 과정 전반에서 요약의 개선 정도를 평가합니다. 개선이 0 또는 음수로 돌아서는 체감 구간을 보여 주는 지표와, 각 단계별 누적 개선도를 함께 살펴봅니다.
이는 체인 오브 덴시티 과정에서 반복 횟수를 최적화하고, 추가 반복이 더 이상 유의미한 개선으로 이어지지 않는 시점을 판단하는 데 도움이 됩니다.
@weave.op()def analyze_iteration_impact(scores):if len(scores) < 2:return {aspect: {"diminishing_returns_point": 0, "cumulative_improvement": 0} for aspect in ['relevance', 'technical_quality', 'conciseness']}aspects = ['relevance', 'technical_quality', 'conciseness']results = {}for aspect in aspects:aspect_scores = [s[aspect]['score'] for s in scores]improvements = [aspect_scores[i+1] - aspect_scores[i] for i in range(len(aspect_scores)-1)]results[aspect] = {"diminishing_returns_point": next((i for i, imp in enumerate(improvements) if imp <= 0), len(improvements)),"cumulative_improvement": sum(improvements),}return results
다음 코드 블록은 개선에 가장 효과적인 반복 횟수 범위를 결정합니다. 개선도의 이동 평균을 사용해 지속적인 향상을 식별하고, 개선이 특정 임계값을 초과하는 최적의 구간을 찾는 것을 목표로 합니다.
@weave.op()def find_optimal_improvement_range(scores):if len(scores) < 3:return {aspect: {"optimal_range_start": 0, "optimal_range_end": 0, "score_at_start": 0, "score_at_end": 0, "improvement_in_range": 0} for aspect in ['relevance', 'technical_quality', 'conciseness']}aspects = ['relevance', 'technical_quality', 'conciseness']results = {}for aspect in aspects:aspect_scores = [s[aspect]['score'] for s in scores]improvements = [aspect_scores[i+1] - aspect_scores[i] for i in range(len(aspect_scores)-1)]# Calculate moving average of improvements window_size = min(3, len(aspect_scores) - 1)moving_avg = np.convolve(improvements, np.ones(window_size), 'valid') / window_size# Find range where improvements are above a threshold threshold = 0.1 * np.mean(improvements)above_threshold = [i for i, avg in enumerate(moving_avg) if avg >= threshold]if not above_threshold:optimal_start, optimal_end = 0, 0 else:optimal_start = above_threshold[0]optimal_end = above_threshold[-1] + 1 results[aspect] = {"optimal_range_start": optimal_start,"optimal_range_end": optimal_end,"score_at_start": aspect_scores[optimal_start],"score_at_end": aspect_scores[optimal_end] if optimal_end < len(aspect_scores) else aspect_scores[-1],"improvement_in_range": sum(improvements[optimal_start:optimal_end])}return results
다음으로, 가장 높은 품질의 요약을 만들어 내는 반복 구간을 식별합니다. 각 품질 항목에서 최고 점수에 이르는 구간을 찾고, 그 범위 내의 누적 개선도 함께 고려합니다. 이를 통해 최종 요약 품질에 가장 크게 기여하는 반복 단계를 파악하고, 요약 프로세스 최적화에도 도움을 얻을 수 있습니다.
@weave.op()def find_optimal_score_range(scores):if len(scores) < 2:return {aspect: {"optimal_range_start": 0, "optimal_range_end": 0, "highest_score": 0, "improvement_in_range": 0} for aspect in ['relevance', 'technical_quality', 'conciseness']}aspects = ['relevance', 'technical_quality', 'conciseness']results = {}for aspect in aspects:aspect_scores = [s[aspect]['score'] for s in scores]improvements = [aspect_scores[i+1] - aspect_scores[i] for i in range(len(aspect_scores)-1)]highest_score = max(aspect_scores)highest_score_index = aspect_scores.index(highest_score)# Find the best range leading up to the highest score best_start = 0 best_end = highest_score_indexbest_improvement = sum(improvements[:highest_score_index])for start in range(highest_score_index):current_improvement = sum(improvements[start:highest_score_index])if current_improvement > best_improvement:best_start = startbest_improvement = current_improvementresults[aspect] = {"optimal_range_start": best_start,"optimal_range_end": highest_score_index,"score_at_start": aspect_scores[best_start],"score_at_end": highest_score,"improvement_in_range": best_improvement}return results
이제 모든 요약 반복 단계에서 산출된 점수를 집계하고 분석합니다. 이를 통해 체인 오브 덴시티 반복 전 과정에 걸친 요약 품질의 변화를 전체적으로 파악할 수 있습니다. 이러한 분석을 바탕으로 프로세스의 전반적 효과를 이해하고, 품질 향상에 기여하는 추세를 식별할 수 있습니다.
@weave.op()def process_iteration_summaries(model_output, instruction, model):iteration_scores = [score_summary(summary, f"Iteration Summary {i+1}", instruction, model)for i, summary in enumerate(model_output["iteration_summaries"])]return {"long_tail_stats": calculate_long_tail_stats(iteration_scores),# Additional analyses can be added here if needed }
마지막 함수는 요약 품질을 평가하는 메인 진입점 역할을 합니다. 앞서 정의한 모든 지표를 결합해 포괄적으로 평가하며, 반복 단계별 요약, 누적 요약, 최종 요약을 함께 분석합니다.
이는 요약 파이프라인의 성능을 다각도로 면밀히 평가하고, 요약 품질의 다양한 측면에 대한 통찰을 제공하며, 우리의 체인 오브 덴시티 전체 프로세스의 효과성을 검증합니다.
@weave.op()def quality_scorer(instruction, model_output, model="gpt-4o"):scores = {"iteration_summaries_analysis": {},"accumulated_summary": {},"final_summary": {}}try:# Process iteration summaries scores["iteration_summaries_analysis"] = process_iteration_summaries(model_output, instruction, model)# Score accumulated summary scores["accumulated_summary"] = score_summary(model_output["accumulated_summary"], "Accumulated Summary", instruction, model)# Score final summary scores["final_summary"] = score_summary(model_output["final_summary"], "Final Summary", instruction, model)# Flatten the scores dictionary for easier analysis flattened_scores = {}for key, value in scores.items():if isinstance(value, dict):flattened_scores[key] = flatten_dict(value)else:flattened_scores[key] = valuescores = flatten_dict(flattened_scores)except Exception as e:print(f"Error in quality_scorer: {str(e)}")scores["error"] = str(e)return scores
종합하면, 이러한 평가 지표들은 우리의 체인 오브 덴시티 요약 파이프라인의 품질과 효과성을 평가하기 위한 견고한 틀을 제공합니다. 프로세스의 다양한 단계에서 요약 품질의 여러 측면을 살펴봄으로써, 접근 방식의 강점과 약점을 통찰하고, 개선이 필요한 영역을 식별하며, 최대 효과를 위해 요약 프로세스를 최적화할 수 있습니다.
평가 실행하기
최근 Weave 기능 중에서 우리가 특히 자랑스러워하는 것 하나는 평가 비교이는 전체 수준과 개별 예시 모두에서 성능을 파악하고, LLM 출력물을 비교하며 심층 분석할 수 있는 시각적이고 인터랙티브한 방법입니다.

설정은 다음과 같습니다:
models = ["claude-3-opus-20240229","claude-3-haiku-20240307","claude-3-5-sonnet-20240620"]evaluation = weave.Evaluation(dataset=dataset, scorers=[quality_scorer])for model in models:arxiv_chain_of_density_pipeline = ArxivChainOfDensityPipeline(model=model, density_iterations=8)await evaluation.evaluate(arxiv_chain_of_density_pipeline)
본질적으로, 이 코드는 Weave Evaluation 객체를 설정하고 우리 목록에 있는 각 모델에 대해 평가를 실행합니다.
결과 및 출력
우리 모델들의 성능을 서로 비교하면 다음과 같습니다:
| Model | Strengths | Weaknesses | Best Use Case |
|---|---|---|---|
| claude-3-sonnet-20240320 | • Highest relevance (4.1944) • Best technical quality (4.2500) • Most comprehensive summaries | • Lowest conciseness (3.5833) • Highest latency (556.4930 ms) • Highest token usage (271,323) | Applications prioritizing summary quality over computational efficiency |
| claude-3-opus-20240229 | • Balanced performance • Best conciseness (4.0000) • Good relevance (4.0556) and technical quality (4.0000) | • Moderate latency (478.1886 ms) • Moderate token usage (221,489) | Scenarios requiring a balance between summary quality and resource usage |
| claude-3-haiku-20240307 | • Lowest latency (241.1572 ms) • Lowest token usage (204,852) | • Lowest relevance (3.4444) • Lowest technical quality (3.4167) • Lowest conciseness (3.5444) | Applications with strict latency requirements or limited computational resources |
다음은 우리 파이프라인이 작동하는 또 다른 예시입니다:
실험 설정: 다양한 NLP 작업에서 다중 샷 ICL을 위해 Gemini 1.5 Pro(컨텍스트 100만 토큰) 사용. 방법론: K-샷 프롬프트에 대해 복원 추출 무작위 샘플링, 시드 3–5개, 그리디 디코딩, KV 캐싱. 평가: 작업별 지표, 유의성 검정을 위한 페어드 부트스트랩 리샘플링, 본페로니 보정.
핵심 결과:
1. 저자원 기계번역: 997샷 ICL로 벰바(28.3% → 47.7%, p<0.001, d=2.8)와 쿠르드어(39.5% → 44.0%, p<0.01, d=1.2)가 향상되었습니다.
2. 요약(XSum): 50샷 ICL에서 ROUGE-L 32.1% ± 0.4%(95% 신뢰구간 [31.3%, 32.9%])로 정점에 도달했습니다.
3. GPQA: 125샷 ICL로 정확도 43.8%(95% 신뢰구간 [41.2%, 46.4%])를 달성했으며, Claude-3 Opus와 유사한 성능을 보였습니다(p=0.08).
4. BIG-Bench Hard: 강화된 ICL이 3샷 CoT보다 우수한 성능을 보였습니다(83% 대 72.1%, p<0.001, d=1.7).
5. 감성 분석: 2048샷 ICL로 정확도 95.2%(95% 신뢰구간 [94.1%, 96.3%])를 달성하여 라벨 뒤바뀜 문제를 극복했습니다.
6. 고차원 분류: 2048샷 ICL이 k-NN 성능에 근접했습니다(N=16: 89.7% ± 1.2%, N=64: 79.8% ± 1.8%).
7. 순차 패리티: 8192샷 ICL로 정확도 40.2%(95% 신뢰구간 [38.9%, 41.5%])를 달성했으며, GPT-2 Medium을 능가했습니다(p<0.001, d=2.1).
시간 소거(Chrono-ablation): 특정 샷 수를 넘으면 성능이 정체됩니다(XSum: 50샷, MATH: 125샷, 추가 샷에 대해 p>0.05).
제한사항:
1. 예시 순서 민감도: MATH500 성능은 하위 영역별로 크게 달랐습니다(ANOVA p<0.001, 10가지 순서에서 표준편차 3.2%).
2. 다음 토큰 예측 손실은 ICL 성능 예측에 신뢰할 수 없습니다(r=-0.18, p=0.23).
3. Gemini 1.5 Pro에 집중함으로 인해 일반화 가능성이 제한됩니다.
4. 요약에서의 환각: XSum에서 날조 비율이 1샷에서는 0%에서 500샷에서는 18.7%로 증가했으며, χ² 검정 결과 p<0.001입니다.
5. 통계적 검정력 한계: 일부 비교에서 작은 효과 크기(d<0.3)에 대해 검정력이不足합니다(1-β<0.8).
통계적 견고성:
여러 시드(3~5개)로는 견고한 분석에 부족합니다.
MT의 표준편차(0.1%~0.5%)를 보고하고, 오차 막대로 시각화했습니다.
변동계수: 2.1%(GPQA)에서 8.7%(BIG-Bench Hard)까지.
핵심 지표에 대해 부트스트랩 95% 신뢰구간을 제공합니다.
방법론적 혁신사항:
1. 강화된 ICL이 인간 근거를 포함한 퓨샷 ICL보다 우수했습니다(MATH: Δ=5.2%, p<0.001, d=1.3).
2. 비지도 ICL은 도메인별 효과를 보였습니다(MATH: 상대 성능 83%, p<0.01).
향후 과제로는 통계적 엄밀성 강화(G*Power 분석, 허위발견률 통제), 교차 모델 검증 고도화, 프롬프트 엔지니어링 최적화, 평가 지표 정교화, 포괄적 소거 실험 수행, 환각 성향의 정량화가 필요합니다.
선택 사항: 고급 청킹 기법(클릭하여 펼치기)
결론
체인 오브 덴시티 프롬프트는 보조 이미지가 있는 촘촘하고 복잡한 텍스트를 이해하는 데 최적의 기법으로 드러났습니다. 실제로 위에서 설명한 전체 접근 방식은 다음과 같은 사용 사례로 손쉽게 확장할 수 있습니다:
- 임상시험 데이터처럼 방대한 연구 코퍼스를 검토하기
- 간결한 개요를 제공하여 동료 심사를 지원하기
- 어떤 분야에서든 관련 연구의 검색과 발견 향상
특筆할 점으로, 우리의 구현은 텍스트와 시각 요소를 모두 처리할 수 있고, 견고한 평가 지표를 갖추었으며, arXiv가 아닌 사용 사례에도 손쉽게 맞춤화할 수 있다는 점을 기억할 가치가 있습니다.
읽어 주셔서 감사합니다. 직접 모두 시도해 보고 싶다면, 마지막으로 Colab 링크를 다시 드립니다:
Add a comment