brunch

You can make anything
by writing

C.S.Lewis

by florent Jun 07. 2024

1년간 거대 언어 모델을 개발하면서 배운 것들

생성형 AI

이 글은 OREILLY 내에 업로드된 What We Learned from a Year of Building with LLMs (Part 1)의 내용을 번역, 요약, 의역, 재구성한 글입니다.


원문: https://www.oreilly.com/radar/what-we-learned-from-a-year-of-building-with-llms-part-i/



주요 내용:


1. 전략적인 프롬프트 작성법(Prompting)

2. RAG(검색 증강 생성, Retrieval Augmented Generation)

3. 워크플로우(workflow) 최적화

4. 평가와 모니터링



제작: Midjourney



전략적인 프롬프트 작성법(Prompting)


1. 효과적인 프롬프트를 작성하려면 기본적인 프롬프트 작성 원칙에 집중해야 한다.


새로운 애플리케이션을 개발하려고 한다면, 우선적으로 프롬프트를 잘 작성하는 것부터 시작하는 것을 추천한다. LLM을 다룰 때, 프롬프트의 중요성을 과소평가하기도 하고, 과대평가하기도 한다. 잘 써진 프롬프트는 때론 그 어떤 방법들보다 더 효과적인 도구가 되기도 하지만, 프롬프트에 너무 집중하다보면 프롬프트에만 집중하게 되어 과도한 개발 공수를 들이게 되어 주객되는 경우도 있다.


(1) n-샷 프롬프트(n-shot prompting) 기반 맥락 학습 (in-context learning)

 n-shot 프롬프트를 통한 맥락 학습이란, LLM에게 몇 가지 예시를 제공하여 맥락을 학습한 후 맥락에 맞는 결과물을 생성할 수 있도록 유도하는 것이다. 여기서 n은 언어 모델에게 제공되는 예시 혹은 맥락의 개수를 뜻한다.          

 n이 너무 낮으면 모델이 특정 예에 너무 고정되어 일반화 능력이 떨어질 수 있다. 일반적으로 n ≥ 5를 목표로 하는 것이 좋으며, 몇십 개까지도 괜찮았다.          

모델에게 맥락으로 제공되는 예시는 반드시 추후에 입력될 프롬프트들의 대표성과 분포도를 지니고 있어야 한다. 예를 들어, 영화 요약기를 만들고 있다면 다양한 영화 장르에 대한 표본들을 모델에 제공하여 실제로 여러 장르에 대해서 무리없이 작동할 수 있도록 해야 한다.          

예시를 제공할 때 반드시 입력값-결과값을 짝지어서 제공할 필요는 없으며, 대다수의 경우에는 원하는 결과값에 대한 형식을 제공하는 것으로 충분하다.          

만약 LLM이 특정 도구나 외부 앱을 사용하고 있다면, 제공되는 예시는 반드시 그 도구 혹은 앱을 활용하는 방식으로 만들어져야 한다.          


(2) 생각의 사슬(chain of thought) 프롬프트 추가

생각의 사슬이란, 언어 모델이 최종 결과값을 제시하기 전에 결과값이 나온 사고 근거 과정들을 나열하게 함으로써 결과값의 질을 개선시키는 방법을 의미한다.          

일반적으로 '단계적으로 생각하는 방식으로 결과값을 만들어주세요.'와 같은 프롬프트를 지시사항으로 포함하곤 하지만, 이러한 생각의 사슬 관련 지시사항을 더 구체적으로 만드는 것이 결과물의 질을 더 크게 높여줄 수 있다.          

예를 들어, 미팅 회의록을 요약하는 앱을 만들 때, '1단계: 후속 조치 사항과 이에 상응하는 담당자를 나열하세요. 2단계: 상세 사항들이 실제로 미팅 회의록과 일치하는지 확인하세요. 3. 해당 정보들을 회의 주요 논점들과 합쳐 요약해주세요.'와 같이 상세하게 생각의 사슬 프롬프트를 제공하면 결과물의 질이 대폭 개선될 수 있다.          

 생각의 사슬 프롬프트는 언어 모델이 추론 중에 정확히 무슨 일이 일어나는지에 대해 상당한 논쟁이 있어,  이 기술의 효과에 대한 의구심을 가진 사람들도 많긴 하다. 하지만 이 기술을 실험해보는 것이 좋다.          


(3) 관련 자료 제공

관련 자료 제공은 모델의 지식 기반을 확장하고 환각 현상(halluciation)을 줄이며 사용자의 신뢰를 높이는 강력한 메커니즘으로, 주로 검색 증강 생성(RAG)을 통해 활용된다.          

관련 자료를 '제공'한다는 것은 단순히 모델에게 자료를 제시하는 것으로 그치는 것이 아니라, 모델에게 자료를 우선적으로 사용하고 직접 참조하거나 언급하도록 지시해야 한다. 이러한 지시는 모델이 자료를 '제대로 근거삼아' 결과물을 낼 수 있도록 도와줄 수 있다.          



2. 입력값과 결과값을 구조화한다.


구조화된 입력갑과 출력값은 모델이 입력값을 더 잘 이해할 수 있도록 도와줄 뿐만 아니라, 다운스트림(downstream, 데이터 처리, 변형, 산출) 단계에서 원하는 내용과 형태의 결과값을 만들 수 있게 하는 데에 도움이 된다. 직렬화 형식(serialization fromatting)의 입력값을 모델에게 전달하면, 모델은 모델 내 토큰 간의 관계를 더욱 구체화하고 추가적인 메타데이터(예: 유형)를 생성하여 더욱 섬세한 결과값 도출이 가능해지며. 구체적으로 구조화된 입력값은 모델 내의 학습 데이터를 직접적으로 연결시켜 더 뚜렷한 맥락을 가질 수 있게 된다.


인공지능이 결과물을 만드는 방식은 우리가 SQL을 작성하는 것과 유사하다. SQL을 작성하기 위해서는 SQL이 다루는 데이터의 스키마(schema)를 구체화해야 하고, 그 구체화된 스키마 내에서 필요한 데이터가 위치한 DB와 테이블을 찾아 SQL을 작성한다. . 즉, 모델도 사용자의 요청을 제대로 이해하고, 그 요청을 해당하는 데이터 유형의 결과값으로 산출하기 위해서는 구조화된 입력값이 필요하다. 구조화된 입력값은 작업을 명확하게 표현해줄 뿐만 아니라, 모델이 만들 결과값의 형식에도 영향을 미치기 때문에 더 나은 결과값을 생성할 확률을 높여준다.


*참고: 클로드(Claude)는 xml 형식을 선호하며, GPT는 마크다운(markdown) 혹은 JSON 형식을 선호한다.


구조화된 결과값은 사용자의 시스템의 다운스트림 구성 요소와의 통합 과정을 단순화시켜 줄 수 있다. 구조화된 출력값을 활용하려면 Instructor나 Outlines를 활용하는 게 좋다. (LLM API SDK를 가져오는 경우 Instructor, 자가 호스팅 모델에 Huggingface를 가져오는 경우 Outlines)



3. 하나의 작업만을 잘 수행하는 작은 프롬프트'들'로 만들 것


소프트웨어 개발에서 가장 지양해야할 것은 단일 클래스나 함수가 모든 것을 수행하는 '신 객체(God object)'다. 이는 프롬프트를 작성할 때도 마찬가지다. 일반적으로 프롬프트는 간단하게 시작하지만, 여러 지침과 예시가 포함되기 시작하고, 성능을 개선하고 더 많은 예외를 처리하려 하면서 복잡해진다. 더 많은 지시사항, 다단계 추론이 계속 붙여져 나가면서 한 프롬프트가 2,000 토큰의 괴물이 되어버린다. 더 최악인 것은, 이렇게 지시사항을 더 붙이고 성능이 저하된다는 것이다. (GoDaddy는 이것이 LLM을 다룰 때 가장 큰 도전과제라고 말했다.)


소프트웨어 개발시 시스템과 코드를 단순하게 유지하기 위해 단계를 쪼개놓듯이, 프롬프트도 단계를 나누어 작성하는 작업이 필요하다. 예를 들어, 회의록 요약기를 만든다고 했을 때, (1) 구조화된 형식으로 주요 결정 사항, 후속 조치 및 담당자 추출 (2) 추출 정보가 전체 회의록 내용과 일치하는지 확인 (3) 구조화된 형식으로 간결한 요약 생성으로 나누면 간단하고 이해하기 쉬운 프롬프트로 나뉠 수 있으며, 모델 또한 이 각 단계를 개별적으로 평가할 수 있게 된다.



4. 맥락 설계의 필요성


언어 모델에게 전달한 맥락의 양에 대해 심사숙고할 필요가 있다. '맥락'을 조각상이라고 생각해야 한다. 미켈란젤로처럼, 맥락을 '깎아내고 다듬으며' 본질적인 정보를 전달해야하지, 맥락위에 또 맥락을 덧붙이는 방식은 비효율적이다. 언어 모델에게 전달한 최종 프롬프트, 모든 컨텍스트 구성 및 메타 프롬프트 및 RAG 결과를 빈 페이지에 놓고 읽어보면, 맥락 정보들에 대해 다시 생각하는 데 정말 큰 도움이 된다. 이 방법을 사용하면 중복, 불필요한 정보, 자기모순적인 언어 및 형식이 잘못된 부분을 발견할 수 있다.


맥락 정보들의 구조 또한 최적화에 큰 영향을 미친다. 문서가 뒤섞인 가방이 사람에게 도움이 되지 않듯이, 정돈되지 않은 맥락을 언어 모델에게 전달하는 것은 비효율적이다. 맥락 정보 자체뿐만 아니라, 맥락 정보간의 관계를 살펴 구조화 하는 것도 중요하다.



RAG(검색 증강 생성, Retrieval Augmented Generation)


프롬프트 작성 외에도 LLM을 유도하는 또 다른 효과적인 방법은 지식 자체를 언어 모델에 제공하는 것입으로, 이를 검색 기반 생성(RAG)이라고 한다. 많은 AI 실무자들은 RAG가 지식 제공 및 결과물 개선에 효과적이며, 파인 튜닝(fine-tuning)에 비해 훨씬 적은 노력과 비용이 들어 효율적이라고 말한다.


RAG 기반 결과물의 품질은 제공되는 문서의 질에 달려있다.


1. RAG의 품질을 개선하기 위해 관련성 지표를 주목해야 한다.


이 품질을 평가할 가장 중요한 지표는 관련성(relevance)이다. 이는 일반적으로 순위 지표인 평균 상호 순위MRR(Mean Reciprocal Rank) 또는 NDCG(Normalized Discounted Cumulative Gain)로 정량화된다. MRR은 시스템이 순위 목록에서 첫 번째 관련 결과를 얼마나 잘 배치하는지를 평가하고, NDCG는 모든 결과의 관련성과 그 위치를 고려한다. 예를 들어, 사용자가 작성한 리뷰들을 검색하여 특정 영화 리뷰 요약을 생성하는 경우, 특정 영화에 대한 리뷰를 더 높은 순위로, 다른 영화에 대한 리뷰는 제외하는 방식처럼 말이다. 소프트웨어 개발에서 일반적으로 쓰이는 추천 기능의 작동 방식처럼, 검색된 항목의 순위는 LLM이 다운스트림 작업에 큰 영향을 미친다.


2. RAG의 품질을 개선하기 위해 정보 밀도를 고려해야 한다.


두 문서가 동일하게 관련성이 있다면, 더 간결하고 불필요한 세부 사항이 적은 문서를 선택하는 것이 효율적이다. 영화 리뷰 요약을 다시 예시로 들면, '영화 대본 정보', '모든 사용자들의 리뷰 정보'들이 정보 관련성을 지닐 수 있겠지만, 가장 많이 추천된 리뷰나 전문가 리뷰의 정보가 더 밀도있는 정보를 제공할 것이다.


3. RAG의 품질을 개선하기 위해 문서의 구체성(the level of a detail)을 고려해야 한다.


일반 텍스트로 SQL 쿼리를 생성하는 RAG 시스템을 구축한다고 가정해보자. 테이블 스키마와 열 이름만 모델에게 제공할 수도 있겠지만, 열에 설명과 대표 값을 덧붙이면 모델이 이해하는 데에 큰 도움이 될 것이다. 이렇듯, 문서 및 지식으로 제공하는 정보의 구체성이 LLM의 성능에 영향을 미칠 수 있다.



키워드 기반 검색은 큰 도움이 될 수 있다.


벡터 임베딩은 마법처럼 모든 검색 문제를 해결하지 않습니다. 실제로 가장 중요한 작업은 의미적 유사성 검색으로 재정렬하기 전에 수행됩니다. 임베딩은 BM25 또는 전체 텍스트 검색에 비해 개선 목적으로는 한계가 있습니다. - 아라빈드 스리니바스, Perplexity.ai CEO
우리는 고객과 파트너에게 몇 달 동안 이를 전달해 왔습니다. 순진한 임베딩으로의 최근접 이웃 검색은 매우 난잡한(noisy) 검색 결과를 초래하며, 키워드 기반 접근 방식으로 시작하는 것이 좋습니다.- 베양 리우, Sourcegraph CTO


많은 실무자들이 RAG를 적용할 때 임베딩을 기반으로 하지만, 키워드 기반의 작동이 오히려 더 효율적일 수 있다. 임베딩은 강력한 대안이 될 수 있지만 만능 해결책은 아니다. 먼저, 임베딩은 높은 수준의 의미적 유사성을 포착하는 데 뛰어나지만, 특정 키워드 기반 쿼리(예: 이름, 약어, ID 등)에서는 어려움을 겪는다. BM25와 같은 키워드 기반 검색은 이러한 상황을 위해 개발되었다.


또한, 쿼리와 일치하는 키워드를 보면 되기 때문에, 키워드 검색이 문서가 검색된 이유를 이해하기가 더 쉽다. 반면, 임베딩 기반 검색은 해석하기가 매우 어렵다. 연산의 관점에서도. Lucene과 OpenSearch와 같은 시스템 덕분에 키워드 검색은 일반적으로 계산 효율성이 더 높다.



키워드 검색과 임베딩을 섞을 것


대부분의 경우, 하이브리드 방식이 가장 잘 작동한다. 딱딱 맞아떨어지는 단어들은 키워드 검색 기반의 키워드 매칭을 진행하고, 동의어, 상위어 및 철자 오류, 복합 양식(ex. 이미지)은 임베딩으로 처리하는 것처럼 말이다. (Shortwave는 쿼리 재작성, 키워드 + 임베딩 검색 및 순위 지정 등을 포함한 RAG 파이프라인 구축 방법을 공유했다. https://www.shortwave.com/blog/deep-dive-into-worlds-smartest-email-ai/)


새로운 지식 정보를 적용할 때에는 파인 튜닝보다 RAG가 효과적이다.


RAG와 미세 조정은 모두 LLM에 새로운 정보를 통합하고 특정 작업에서 성능을 향상시키는 데 사용할 수 있다. 그러면 무엇을 먼저 사용하는 게 좋을까? 최근 연구에 따르면 RAG가 더 낫다고 한다. 한 연구에서는 MMLU와 최신 정보를 기반으로 RAG와 비지도 파인 튜닝(unsupervised fine tuning)을 비교하여 평가했는데, 연구 결과, RAG가 모든 면에서 더 나은 성능을 보였다.


성능 측면 외에도, RAG는 몇 가지 실용적인 이점을 제공한다. RAG는 파인 튜닝에 비해 검색 인덱스를 최신 상태로 유지하기가 더 쉽고 저렴하다. 뿐만 아니라, 검색 인덱스가 악영향을 끼치거나 편향된 콘텐츠가 포함되어 문제가 있는 경우, 해당 문서를 쉽게 제거하거나 수정할 수 있다.


또한, RAG는 검색 방법에 세분화된 제어를 수행할 수 있게 해준다. 예를 들어, 여러 조직을 위한 RAG 시스템을 호스팅하는 경우, 검색 인덱스를 분할하여 각 조직이 자신의 인덱스에서만 문서를 검색할 수 있도록 할 수 있다. 이를 통해 한 조직의 정보를 다른 조직에 노출하는 것을 방지할 수 있다.


롱 컨텍스트 모델(long-context model)이 RAG를 쓸모없게 만들지는 않을 것이다.


롱 컨텍스트 모델이 여러 문서를 분석하거나 PDF와 대화하는 등의 사용 사례에서 게임 체인저가 될 것은 분명하긴 하지만, RAG는 그래도 나름대로의 이점을 계속 유지할 것이다.


먼저, 10M 토큰의 컨텍스트 윈도우가 있더라도 모델에 입력할 정보를 선택하는 방법이 여전히 필요하다. 방대한 텍스트 속에서 정보를 찾아내는 능력(NIAH, Needle-in-a-haystack)을 차치하고, 그렇게 큰 컨텍스트를 가지고 효과적으로 추론하는 언어 모델이나 설득력 있는 데이터를 아직 보지 못했다. 오히려 RAG와 같이 좋은 검색(및 순위 지정) 논리가 없으면 모델을 혼란스럽게 하거나 완전히 관련 없는 정보로 컨텍스트 윈도우를 채울 위험이 있다.


마지막으로 비용 문제가 있다. 트랜스포머(Transformer)의 추론 비용은 컨텍스트 길이에 따라 기하급수적으로 증가한다. 전체 데이터를 읽어야만 질문에 답할 수 있는 모델이 '있는 것' 자체는 좋은 아이디어가 아니다. RAM을 사용하는 방식을 생각해보면 된다. 수십 테라바이트의 RAM을 사용하는 컴퓨팅 인스턴스가 있지만, 우리는 여전히 디스크에서 읽고 쓴다.



워크플로우(workflow) 최적화


LLM을 프롬프트하는 것은 시작에 불과하다. 제대로 된 LLM 앱을 개발하려면 프롬프트를 넘어 제대로된 전체적인 워크플로우가 필요하다. 예를 들어, 복잡한 작업을 어떻게 간단한 작업으로 분류할지, 성능을 높이고 비용을 줄이기 위해 파인 튜닝이나 캐싱을 언제 적용해야 할지처럼 말이다.


단계별 반복 "흐름"은 큰 성능 향상을 가져올 수 있다.


AlphaCodium은 단일 프롬프트에서 다단계 워크플로우로 전환하여, CodeContests에서 GPT-4의 정확도를 19%에서 44%로 증가시켰다. 워크플로우는 다음과 같았다.


- 문제에 대해 생각하기

- Public 테스트에서 추론

- 가능한 솔루션 생성

- 솔루션 순위 매기기

- Synthetic 테스트 생성

- Public 및 Synthetic 테스트에서 솔루션 반복


우리는 이제 큰 프롬프트를 여러 작은 프롬프트로 나눔으로써 더 나은 결과를 얻을 수 있음을 알고 있다.


명확한 목표를 가진 작은 작업은 최고의 성능을 보여줄 수 있다. 모든 프롬프트가 구조화된 출력을 기반으로 할 필요는 없지만, 구조화된 출력은 모델 간 상호 작용을 조율하는 시스템과의 인터페이스에 큰 도움이 될 수 있다.


시도해 볼 만한 것들

- 미리 구체적인 단계들을 계획, 계획 유효성 검사

- 사용자가 기입한 프롬프트를 모델에게 친숙한 형태로 다시 작성하기.

- 에이전트 동작을 선형 체인, DAG, State-Machine으로 처리하기.

- 프롬프트 엔지니어링을 평가하기


현재로서는 결정론적 워크플로우가 우선적이다.


AI는 사용자의 요청과 환경에 동적으로 반응할 수 있지만, 그 반응은 비결정론적 특성을 지니기 때문에 실제 활용에 어려움을 준다. AI가 수행하는 각 작업들은 실패할 가능성이 있으며, 그러한 실패나 오류를 복구하는 것은 거의 불가능하다. 따라서 AI가 여러 단계 작업을 성공적으로 완료할 가능성은 단계 수가 증가함에 따라 기하급수적으로 감소한다. 그래서 AI를 실제 서비스에 배포하여 사용하기가 어려운 것이다.


현재 가장 실용적인 접근 방식은 AI 시스템이 결정론적 계획을 생성한 다음, 이를 구조화된 방식으로 재현할 수 있도록 실행하는 것이다. 가장 처음 단계에서는 높은 수준의 목표에 관련된 프롬프트를 제공하여 에이전트가 계획을 생성하게 한다. 그 다음, 계획을 결정론적으로 실행한다. 이렇게 하면 각 단계가 더 예측 가능하고 신뢰할 수 있게 된다. 또한, 이러한 접근 방식의 이점은 다음과 같다.


- AI가 생성한 계획은 프롬프트나 파인튜닝을 위한 예시로 사용될 수 있다.

- 결정론적 실행은 시스템을 더 신뢰할 수 있게 하여 테스트 및 디버깅이 더 쉬워진다. 또한, 실패할 경우, 계획의 특정 단계로 바로 추적할 수 있도록 해준다.

- 생성된 계획은 유향 비순환 그래프(DAG)로 표현될 수 있으며, 이는 정적 프롬프트에 비해 새로운 상황에 더 쉽게 이해되고 적응할 수 있다.


AI 모델을 다루는 방식은 주니어 엔지니어를 관리하는 방식과 유사하기 때문에, AI를 가장 잘 구축하는 사람은 주니어 엔지니어를 탁월하게 관리한 경험을 가진 사람일 수 있다. 매니저는 주니어에게 명확한 목표와 구체적인 계획을 제공하지, 막연한 개방형 지시를 제공하지 않는다. AI도 마찬가지다.


온도 매개 변수(temperatrue parameter)가 아닌 방식으로 다양한 결과값을 얻을 것


사용자가 이전에 구매한 제품 목록을 제공하면 카탈로그에서 구매할 제품을 추천하는 LLM 파이프라인을 작성한다고 가정해 보자. 프롬프트를 여러 번 실행할 때, 결과 추천이 너무 유사하다는 것을 알게 됐다. 그러면 일반적으로 온도 매개 변수를 증가시켜 결과값을 다양하게 만들려고 할 것이다. 하지만 온도를 높이면 결과값은 다양해지지만 성능면에서 실패하는 경우가 있다.


사용자에게 정말 적합한 제품이 온도 매개 변수로 제외되어 결과값에 절대 안 나타나는 경우처럼 말이다. 동일한 몇 가지 제품이 과도하게 언급되는 경우도 있다. 또는, 온도가 너무 높으면 존재하지 않는 제품(또는 의미 없는 텍스트)을 참조하는 결과값을 전달할 수도 있다.


다시 말해, 온도를 높이면 해서 당신이 기대하고 있는 확률 분포 내에서 결과값이 나올 보장을 해줄 수 없다는 것이다. 그렇다면 성능을 유지하면서도 다양한 결과값을 낼 방법은 무엇이 있을까?


가장 간단한 방법은 프롬프트 내 요소를 조정하는 것이다. 예를 들어, 프롬프트 템플릿에 과거 구매 목록을 포함하는 경우, 프롬프트에 이 목록을 삽입할 때마다 목록 내 항목의 순서를 섞으면 꽤 다양한 결과값을 얻을 수 있다. 또는 목록의 일부만을 제공하여 중복된 결과값을 방지할 수도 있다. LLM에게 최근 목록의 항목을 피하도록 지시하거나, 최근 추천과 유사한 결과값을 거부하고 다시 샘플링하여 응답을 다양화할 수 있다.


또 다른 효과적인 전략은 프롬프트에서 사용하는 구문을 다양화하는 것이다. 예를 들어, "사용자가 정기적으로 사용할 제품을 선택하세요" 또는 "사용자가 친구에게 추천할 가능성이 높은 제품을 선택하세요"와 같은 구문을 포함하여 집중해야 할 맥락을 변경함으로써 추천 제품의 다양성을 높일 수 있다.



캐싱은 과소평가됐다.


캐싱은 동일한 입력값에 대한 응답을 다시 계산할 필요성을 제거하여 비용을 절감하고 결과물을 빠르게 제공할 수 있게 해준다. 또한, 결과값이 이전에 가드레일(guardrail)에 통과한 경우, 검증된 응답을 제공하여 유해하거나 부적절한 콘텐츠를 제공할 위험을 줄일 수 있다.


간단한 캐싱 접근법은 처리 중인 항목에 고유 ID를 사용하는 것이다. 예를 들어, 새로운 기사나 제품 리뷰를 요약하는 경우, 요청이 들어오면 캐시에 요약이 있는지 확인한다. 있으면 즉시 반환하고, 없으면 생성하여 가드레일을 통과시킨 후 제공하며, 향후 요청을 위해 캐시에 저장한다.


개방형 쿼리의 경우, 검색 분야에서 캐싱을 활용하는 기술을 차용할 수 있다. 자동 완성 및 철자 교정과 같은 기능은 사용자 입력을 정규화하여 캐시 적중률을 높이는 데 도움이 된다.



파인 튜닝이 필요한 경우


아무리 정교하게 프롬프트를 설계할지라도 충분하지 않은 작업이 있을 수 있다. 그렇다면 특정 작업에 대해 모델을 파인 튜닝하는 것이 필요할 수 있다. 다음과 같은 예시처럼 말이다.


(1) 허니콤의 자연어 쿼리 어시스턴트: 초기에는 "프로그래밍 매뉴얼"을 프롬프트와 함께 n-shot 예제로 제공하여 맥락 학습을 시켰고, 모델을 파인 튜닝함으로써 특정 언어의 구문 및 규칙에 대한 출력이 향상되었음


(2) 리챗의 루시: LLM은 프론트엔드가 올바르게 렌더링할 수 있도록 구조화된 데이터와 비구조화된 데이터를 결합한 매우 특정한 형식의 응답을 생성해야 했고, 파인 튜닝을 통해 이를 수행


미세 조정은 효과적일 수 있지만 상당한 비용이 든다. 파인 튜닝을 할 데이터를 주석을 달고, 파인 튜닝을 진행하며 평가해야 하며, 자체 호스팅도 필요하다. 따라서 이러한 업무를 수행해낼 높은 초기 비용이 가치가 있는지 고려하는 것이 필수적이다. 프롬프트로 달성하고자 하는 작업의 90%까지 도달할 수 있다면, 파인 튜닝은 투자 가치가 없을 수 있다. 하지만 파인 튜닝이 필수적이라고 결정했다면, 주석 데이터 수집 비용을 줄이기 위해 합성 데이터로 생성하거나 오픈 소스 데이터를 부트스트랩할 수 있다.




평가와 모니터링


실제 입력/출력 샘플로 단언(assertion) 기반 단위 테스트 작성


단언 단위로 구성된 실제 입력값과 결과값의 표본을 기반으로 테스트를 진행하는 것이다. 결과값에 대한 기대치는 최소 세 가지 기준을 기반으로 해야한다. '세 가지'라는 것이 임의적으로 보일 수 있지만, 시작하기에 실용적인 숫자다. 예를 들어, 3가지보다 적으면 작업이 충분히 정의되지 않았거나 너무 개방형임을 시사한다. 이러한 단위 테스트는 프롬프트 편집, RAG에 새로운 컨텍스트 추가 등 파이프라인의 모든 변경 사항에 적용되어야 한다.


모든 응답에 포함하거나 제외할 단언으로 시작하는 것이 좋다. 또한 단어, 항목, 문장 수가 일정 범위 내에 있는지 확인하는 검사를 수행하는 것도 좋다. 단언의 형태는 AI가 생성하는 결과값에 따라 달라질 수 있다. 예를 들어, 코드 생성 평가에서는 생성된 코드를 실행하고 런타임 상태가 사용자 요청에 충분한지 확인하는 실행-평가 방법이 효과적이다.


예를 들어, 사용자가 foo라는 이름의 새로운 함수를 요청했다면, AI가 생성한 코드를 실행한 후 foo가 호출 가능해야 한다. 실행-평가 과정에서 하나의 도전 과제는 AI의 코드가 종종 목표 코드와 약간 다른 실행 환경을 남겨둔다는 점이다. 이러한 경우에는, 해당 조건을 완화시켜 적용하여 어떤 실행 가능한 답변이든 만족할 수 있도록 하는 것이 효과적일 수 있다.


마지막으로, 의도한 대로 제품을 사용해보면서(ex. 도그푸딩)하면 실제와 비슷한 데이터를 기반으로 실패하는 경우를 빠르게 파악할 수 있다. 이 접근 방식은 약점을 식별하는 데 도움이 될 뿐만 아니라, 여러 목적으로 쓰일 수 있는 유용한 표본 데이터를 제공한다.



LLM-as-Judge, 쓸만하지만 만능은 아니다.


LLM-as-Judge, 즉 LLM을 평가하기 위해 LLM을 사용하는 방법은 일부에게 회의적으로 받아들여지긴 하지만, 잘 구현된 경우 인간의 판단과 상당히 유사한 상관관계를 보이며, 새로운 프롬프트나 기술의 성능에 대한 사전 지식을 구축하는 데 도움이 될 수 있다. 구체적으로 참고해야할 점은, 쌍별 비교(control vs. treatment)를 할 때, LLM은 '어느 쪽이 더 나은지'에 대해서는 잘 판단하지만 '얼마나 차이가 나는지'에 대해서는 성능이 좋지 않다.


LLM-as-Judge를 최대한 활용하는 방법:

(1) 쌍별 비교 사용: 리커트 척도 대신, 두 가지 옵션을 제시하고 더 나은 것을 선택하도록 요청한다. 이는 더 안정적인 결과로 이어지는 경향이 있다.

(2) 위치 편향 제어: 제시된 옵션의 순서가 LLM의 결정을 편향시킬 수 있다. 이를 완화하기 위해, 쌍별 비교를 두 번 수행하고 각 쌍의 순서를 바꿔 결과가 일관적인지 확인한다.

(3) 동점 허용: 일부 경우에는 두 옵션이 동일하게 좋을 수 있기 때문에, LLM이 동점을 선언하도록 허용하여 임의로 승자를 선택하지 않도록 한다.

(4) 생각의 사슬 사용: LLM에게 최종 선호도를 제공하기 전에 결정을 설명하도록 요청하면 평가의 신뢰성을 높일 수 있다. 또한, 이를 통해 더 약하지만 빠른 LLM을 사용해도 유사한 결과를 얻을 수 있도록 해준다. 이 부분이 해당하는 파이프라인는 배치 모드로 작동하기 때문에 CoT의 추가 대기 시간은 문제가 되지 않는다.

(5) 응답 길이 제어: LLM은 더 긴 응답을 선호하는 경향이 있다. 이를 완화하기 위해, 응답 쌍들이 길이 면에서 유사한지 확인할 필요가 있다.


LLM-as-Judge을 잘 활용하는 방식 중 하나는 새로운 프롬프트 전략에 대한 회귀(regression)를 확인하는 것이다. 결과값들의 집합을 추적한 후, 이러한 결과값들의 예제를 새로운 프롬프트 전략으로 다시 실행하고 LLM-as-Judge를 사용하여 새로운 전략이 어디에서 실패할 수 있는지 빠르게 평가할 수 있다.


LLM-as-Judge 방식을 기반으로 결과값을 개선시킨 후 다시 결과값을 만드는 방식을 총 3번 반복했을 때 결과값의 만족도가 68%에서 94%로 개선된 경우도 있다. 하지만 LLM-as-Judge는 만능 해결책이 아니다. 언어의 미묘한 뉘앙스 측면에서는 아무리 강력한 AI 모델조차도 제대로 평가하는 데 실패할 수 있다. 또한, 기존 분류 및 보상 모델이 LLM-as-Judge보다 높은 정확도를 달성할 수도 있으며, 비용과 대기 시간이 더 낮을 수 있다. 코드 생성의 경우, LLM-as-Judge는 실행 평가와 같은 직접적인 평가 전략보다 효과적이지 않을 수 있다.



결과값에 대한 "인턴 테스트" 평가


언어 모델에 대한 정확한 입력값을 넣고 결과값을 생성했을 때, 이 결과값들을 평균적인 수준의 대학생에게 평가하라고 하면 제대로 수행될 수 있을까?


"못 한다."인 경우

- LLM이 필요한 지식을 결여했기 때문에 컨텍스트를 풍부하게 할 방법을 고려해야 한다.

- 컨텍스트를 개선할 방법이 없다면, 현대 LLM에게 너무 어려운 작업일 수 있다.


"그렇다"인 경우

- "그렇다"지만 시간이 좀 걸릴 것 같으면, 작업의 복잡성을 줄여보는 것이 좋다. (ex. 없앨 작업이 있는지, 템플릿화할 수는 없는지)

- 문제 없이 "그렇다"인 경우, 모델이 무엇을 잘못하고 있는지 추적하고, 모델에게 응답 전후에 설명을 요청하여 모델의 사고 과정을 이해하면 된다.



평가를 너무 강조해도 전체 성능에 악영향을 끼칠 수 있다.


측정하는 행위 자체가 목표가 되면, 그것은 좋은 측정이 되지 않는다.
- 굿하트의 법칙


NIAH 평가는 모델의 컨텍스트 크기가 커질 때 모델의 회상 능력을 측정하기 위해 진행되는데, 너무 강조된 나머지 모델에 평가를 위한 프롬프트들이 주렁주렁 달리는 경우가 있다. (ex. Google의 Gemini 1.5 리포트)


평가에 대한 과도한 강조는 추출 및 요약 작업에서 성능 저하로 이어질 수 있다. 이러한 LLM은 모든 문장에 주의를 기울이도록 파인 튜닝되어, 최종 출력에 중요하지 않은 세부 사항과 방해 요소를 포함할 수 있다. 예를 들어, 요약 작업에서 사실 일관성에 대해 너무 강조하게 되면, 요약의 구체성을 약화시킬 수 있다. 반대로, 글쓰기나 말하기 대본 작성을 위해 창조적인 측면을 강조하는 평가 기준이 적용되면, 사실의 불일치를 야기할 수 있다.


그리고 평가에 대한이는 다른 평가 및 사용 사례에도 적용될 수 있습니다. 예를 들어, 요약 작업. 사실 일관성에 대한 강조는 요약이 덜 구체적이 되도록 하여 사실적으로 일관성이 낮을 가능성을 줄이고, 아마도 관련성도 낮을 수 있습니다. 반면, 글쓰기 스타일 및 웅변에 대한 강조는 사실 불일치를 도입할 수 있는 더 화려하고 마케팅 유형의 언어로 이어질 수 있습니다.



주석을 이진 작업 또는 쌍별 비교로 단순화해야 한다.


결과값에 대해 개방형 또는 리커트 척도로 평가하는 것은 인지적으로 부담이 크다. 그래서 해당 방법으로 수집된 데이터는 변동성 때문에 더 난잡하고 유용성이 떨어지는 경우가 많다. 효과적인 접근 방식은 작업을 단순화하고 평가자의 인지적 부담을 줄이는 것이다. 이진 분류 및 쌍별 비교처럼 말이다.


이진 분류에서는 평가자가 모델 출력에 대해 간단한 예/아니요 판단을 내리도록 요청한다. 생성된 요약이 출처 문서와 사실적으로 일치하는지, 제안된 응답이 관련성이 있는지, 독성이 있는지 등을 판단하도록 할 수 있다. 리커트 척도에 비해 이진 결정은 더 정확하고 평가자 간 일관성이 높으며 처리량이 높다. 이는 Doordash가 메뉴 항목을 태그하는 레이블링 대기열을 설정한 방식이다.


쌍별 비교에서는 평가자에게 결과값 응답 쌍을 제시하고 어느 쪽이 더 나은지 묻는 것이다. 인간이 "A가 B보다 낫다"라고 말하는 것이 A 또는 B 개별적으로 점수를 매기는 것보다 더 쉽기 때문에, 이는 더 빠르고 신뢰할 수 있는 주석을 이끌어 낸다. Llama2 논문 저자인 토마스 시알롬은 쌍별 비교가 감독된 파인 튜닝 데이터(작성된 응답 수집)보다 빠르고 저렴하다고 확인했다. (쌍별 비교는 단위당 $3.5, 파인 튜닝 비용은 단위당 $25)


레이블링 가이드를 작성하기 시작했다면, Google 및 Bing 검색의 참조 가이드를 참고하는 것이 좋다.



참조가 없는 평가는 가드레일로도 사용 가능하다.


가드레일은 부적절하거나 유해한 콘텐츠를 잡는 데 도움이 되고, 평가는 모델 출력의 품질과 정확성을 측정하는 데 도움이 된다. 참조가 없는 평가는 동전의 양면과도 같다. 참조가 없는 평가는 입력 프롬프트와 모델의 응답만으로 출력 품질을 평가할 수 있다.


예를 들어, 요약물 평가에서는 입력 문서를 고려하여 요약의 사실 일관성 및 관련성을 평가할 수 있다. 요약이 이러한 기준에서 낮은 점수를 받으면 사용자에게 표시하지 않기로 선택할 수 있으며, 효과적으로 평가를 가드레일로 사용할 수 있다. 유사하게, 참조가 없는 번역 평가는 인간이 번역한 참조 없이 번역 품질을 평가할 수 있으며, 다시 한 번 이를 가드레일로 사용할 수 있다.



LLM은 응답하지 말아야 할 때도 응답하는 경우가 많다.


LLM을 다룰 때 주요 문제는 모델이 응답하지 말아야 할 때도 결과물을 생성하는 경향이 있다는 것이다. 무해하지만 의미 없는 응답을 내놓거나, 위험한 콘텐츠와 같은 더 심각한 결함을 초래할 수도 있다. 예를 들어, 문서에서 특정 속성이나 메타데이터를 추출하도록 요청하면, LLM은 실제로 존재하지 않는 값을 자신있게 제공할 수 있다. 또는, 비영어 문서를 컨텍스트로 제공했다고 해서 영어가 아닌 언어로 응답할 수도 있다.


LLM이 "해당 없음" 또는 "알 수 없음" 응답을 반환하도록 프롬프트를 시도할 수 있지만, 이는 완벽하지 않다. 로그 확률이 제공되더라도, 결과물의 품질의 지표로서는 부적절하다. 로그 확률은 결과값에서 토큰이 나타날 가능성을 나타내지만, 생성된 텍스트의 정확성을 반영하지는 않는다. 오히려, 쿼리에 응답하고 일관된 응답을 생성하도록 훈련된 모델의 경우, 로그 확률은 잘 조정되지 않을 수 있다. 따라서 로그 확률이 높다고 해서 결과값이 유창하고 일관성이 있다는 것을 의미하지는 않으며, 정확하거나 관련성이 있다는 것을 의미하지 않는다.


신중한 프롬프트 엔지니어링이 일부 도움을 줄 수 있지만, 원하지 않는 출력을 감지하고 필터링/재생성하는 강력한 가드레일을 보완해야할 필요성이 있다. 예를 들어, OpenAI는 증오 발언, 자해 또는 성적 콘텐츠와 같은 안전하지 않은 응답을 식별할 수 있는 콘텐츠 조정 API를 제공한다. 유사하게, 개인 식별 정보를 감지하는 수많은 패키지가 있다. 가드레일의 한 가지 이점은 사용 사례와 무관하게 적용할 수 있다는 것이며, 특정 언어의 모든 출력에 널리 적용할 수 있다는 것이다. 또한, 정확한 검색을 통해 관련 문서가 없는 경우 시스템이 결정론적으로 "모르겠습니다"라고 응답할 수 있게 할 수 있다.


LLM은 예상치 못한 경우에도 결과값을 생성하지 못할 수 있다. 간단한 이유로는 API 제공자의 긴 대기 시간이 있으며, 복잡한 이유로는 콘텐츠 조정 필터에 의해 출력이 차단되는 경우까지 다양합니다. 따라서 디버깅 및 모니터링을 위해 일관되게 입력값 및 결과값을 기록하는 것이 중요하다.



환각은 지독한 문제다.


콘텐츠의 안전성이나 개인 식별 정보에 대한 문제는 많은 주목을 받을 수 있기 때문에 드물게 발생하지만, 일관성 문제는 감지하기 어려울 뿐더러 끈질기게 지속된다. 단순한 작업에서도 5-10%의 확률로 환각이 발생하며, LLM 제공자에 따르면, 요약과 같은 간단한 작업에서도 이를 2% 이하로 낮추기 어려울 수 있다고 한다.


이를 해결하기 위해, 프롬프트 엔지니어링(생성 전)과 사실 불일치 가드레일(생성 후)을 결합할 수 있다. 프롬프트 엔지니어링에서는 생각의 사슬과 같은 기술이 환각을 줄이는 데 도움이 된다. 그런 다음, 사실 불일치 가드레일을 적용하여 요약의 사실성을 평가하고 환각을 필터링하거나 재생성하는 것이다. 일부 경우, 환각은 결정론적으로 감지할 수도 있다. RAG 검색에서 리소스를 사용하는 경우, 출력이 구조화되어 있고 리소스를 식별하면 입력 컨텍스트에서 수동으로 확인할 수 있다.




작가의 이전글 24년 6월 6일 흠터레스팅 테크 뉴스
브런치는 최신 브라우저에 최적화 되어있습니다. IE chrome safari