CSV 데이터에 대한 질의응답 벤치마킹
요약
본 글은 CSV와 같은 표 형식(tabular) 데이터를 대상으로 하는 질의응답 시스템 구축 및 평가 방법을 심층적으로 다룹니다. 초기에는 Streamlit 앱과 LangSmith를 활용하여 사용자 질문을 수집하고, 최종적으로 OpenAI 함수 호출과 Python REPL, 리트리버에 접근 가능한 커스텀 에이전트를 개발했습니다.
핵심 포인트
- 표 형식 데이터(CSV) 기반 질의응답 시스템 구축 방법을 제시합니다.
- LangSmith를 사용하여 실제 사용자의 질문 데이터를 수집하는 과정이 중요합니다.
- LLM을 활용하여 자연어 형태의 출력을 평가하는 방법론을 다룹니다.
- 최종 솔루션은 OpenAI 함수와 REPL 접근이 가능한 커스텀 에이전트입니다.
이 글은 다소 긴 포스팅입니다. 표 형식(tabular) 데이터를 대상으로 하는 질의응답에 대한 심층 분석입니다. 본문에서는 CSV 데이터를 사용하고 논하지만, 이와 관련된 많은 아이디어가 SQL 데이터에도 적용됩니다. 다음 내용을 다룹니다:
배경 동기: 왜 이것이 흥미로운 작업인가초기 적용: 좋은 분포의 실제 질문을 수집하기 위해 간단한 Streamlit 앱을 설정하는 방법초기 솔루션: 우리의 초기 솔루션과 몇 가지 개념적 고려 사항LangSmith를 사용한 디버깅: 사람들이 어떤 질문을 하는지, 그리고 우리의 초기 솔루션이 어떤 문제점을 가졌는지평가 설정: 우리가 솔루션을 평가한 방법**개선된 솔루션: 우리가 도달한 최종 개선된 솔루션
살짝 미리 보여드리자면, 우리가 도달한 개선된 솔루션은 OpenAI 함수를 사용하고 Python REPL과 리트리버(retriever)라는 두 가지 도구에 접근할 수 있는 커스텀 에이전트였습니다.
우리는 피드백을 수집하는 데 사용했던 앱, 데이터셋, 평가 스크립트를 이 저장소에서 오픈 소스했습니다. 또한 이 블로그의 내용을 안내하는 YouTube 비디오도 제작했으니, 그것이 더 취향에 맞으시다면 참고하세요.
배경 동기
현재 텍스트 데이터에 대한 질의응답은 상당히 표준화된 방식이 있습니다. 반면, 개선을 요청받는 일관된 영역 중 하나는 표 형식(CSV) 데이터와 관련이 있습니다. 많은 기업 데이터가 CSV 파일에 담겨 있으며, 여기에 자연어 인터페이스를 노출하면 쉽게 인사이트를 얻을 수 있게 합니다. 문제는 이를 어떻게 달성해야 할지 명확하지 않다는 것입니다.
몇 주 전 우리는 이 부분에 집중하기로 결정했지만, 곧 문제에 부딪혔습니다. 사람들이 CSV 데이터에 대해 어떤 유형의 질문을 할 것으로 예상하는지 실제로 알지 못했고, 이러한 애플리케이션을 평가할 좋은 방법도 없었습니다.
💡
LLM 애플리케이션의 평가는 종종 데이터 부족과 메트릭(metrics) 부족 때문에 어렵습니다.
기존의 머신러닝(machine learning)에서는 일반적으로 입력과 출력 데이터셋을 가지고 모델을 학습시키고 평가하는 방식으로 진행했습니다. 하지만 LLM은 놀라운 제로샷 러너(zero-shot learner)이기 때문에, 이제는 단순히 아이디어만으로 프롬프트(prompt)를 사용하여 데이터를 전혀 사용하지 않고도 애플리케이션을 빠르게 구축할 수 있게 되었습니다. 이는 개발자가 새로운 애플리케이션을 신속하게 만들 수 있다는 점에서 엄청난 강력함을 제공하지만, 동시에 평가에 필요한 데이터가 부족하다는 어려움을 야기합니다. 이것이 바로 저희가 LangSmith를 구성하면서 데이터셋을 만드는 과정을 최대한 쉽게 만들고자 한 이유입니다.
마찬가지로, LLM 애플리케이션을 평가할 만한 좋은 메트릭(metrics)도 자주 없습니다. 출력물은 종종 자연어(natural language) 형태이며, BLEU나 ROUGE 같은 전통적인 NLP(Natural Language Processing) 메트릭으로는 적절하지 않습니다. 그렇다면 어떤 것이 자연어를 이해하는 데 뛰어날까요? 바로 LLM입니다! 저희는 LLM을 활용한 평가에 매우 낙관적이며, LLM을 사용하여 평가를 수행하는 여러 평가기(evaluator)들을 구축하는 데 투자했습니다.
그렇다면 이러한 아이디어들을 표 형식 데이터에 대한 질문 답변 기능을 개선하는 작업에 어떻게 적용했을까요? 다음 섹션에서 더 자세히 다루겠지만, 높은 수준에서는 다음과 같이 진행했습니다:
- LangSmith를 사용하여 흥미로운 데이터 포인트를 식별하고 이를 활용하여 예시 데이터셋을 구성했습니다.
- LLM을 사용하여 정확성을 평가했습니다.
초기 애플리케이션
먼저, 질문과 정답(ground truth)으로 이루어진 데이터셋을 만드는 작업을 시작했습니다. 여기서 문제의 일부는 사람들이 자신들의 표 형식 데이터에 어떤 종류의 질문을 하고 싶어 할지조차 알 수 없었다는 점이었습니다. 저희는 몇 가지 추측을 하거나, 질문을 생성하여 합성 질문(synthetic questions)을 시도해 볼 수도 있었습니다. 하지만 저희는 실제 사용자가 어떤 종류의 질문을 하고 싶어 하는지에 대한 탐색(exploration)을 함께 하고자 했기 때문에, 실제 질문에 최적화하는 것을 목표로 했습니다.
💡
앱을 출시하기 전에 사용자들이 어떻게 상호작용할지 추측하기 어려울 수 있습니다. 추측하기보다는, 빠르게 그리고 일찍 출시하여 실제 데이터를 수집하는 전략이 한 가지 방법입니다.
이를 위해 저희는 빠른 데모 애플리케이션을 구축하여 공개하기로 결정했습니다. 그러면 실제 사용자 질문과 그들이 받은 답변에 대한 모든 피드백을 기록할 것입니다. 피드백을 수집하기 위해 애플리케이션에 간단한 '좋아요(thumps up)'/'싫어요(thumps down)' 버튼을 추가했습니다. 저희는 LangSmith를 사용하여 모든 상호작용과 피드백을 모니터링하고, 그 후 상호작용 내용을 수동으로 검토하여 흥미로운 것들로 구성된 데이터셋을 만들었습니다. 이는 LangSmith UI에서 쉽게 할 수 있습니다. 모든 로그에 '데이터셋에 추가(Add to Dataset)' 버튼이 있습니다.
또한 어떤 유형의 데이터를 수집할지에 대한 질문도 있었습니다. 저희는 두 가지 접근 방식을 고려했습니다: (1) 사용자가 자신의 CSV를 업로드하고 그 데이터에 대해 질문하게 하거나, (2) CSV를 고정하고 그 위에 질문들을 모으는 것입니다. 저희는 몇 가지 이유로 (2)를 선택했습니다. 첫째, 사람들이 가지고 놀기 더 간단할 것이며, 이는 더 많은 응답으로 이어질 가능성이 높습니다. 둘째, 평가하기가 더 쉬울 것입니다. 셋째, 저희는 특히 사용자 질문을 기록하고 확인하는 것을 원했고, 누군가가 업로드할 수 있는 기밀 CSV를 대상으로 하고 싶지 않았습니다. 하지만 이것은 몇 가지 단점도 있습니다. 사용해야 할 CSV를 선택해야 하며, 이 CSV가 다른 CSV들(데이터의 크기와 형태뿐만 아니라 사람들이 질문하고 싶어 하는 내용 면에서도)을 대표하지 못할 수도 있습니다.
저희 예시 애플리케이션을 위해 고전적인 타이타닉 데이터셋(Titanic dataset)을 선택했습니다. 이는 타이타닉호에 탑승했던 모든 승객과 생존 여부에 대한 기록이며, 종종 예시 데이터 과학 프로젝트에 사용됩니다. 저희는 이 간단한 애플리케이션을 Streamlit으로 만들고 세상에 공개하여 사람들에게 피드백을 요청했습니다. 호스팅된 앱은 여기에서, 소스 코드는 여기에서 볼 수 있습니다.
이를 통해 저희는 약 400개의 상호작용 데이터를 수집했습니다. 그중 약 200개에는 어떤 형태의 피드백이 있었습니다. LangSmith를 사용하여 부정적인 피드백을 받은 데이터 포인트(그리고 일부는 긍정적인 피드백)를 깊이 파고들어 수동으로 레이블링하고, 저희가 만든 데이터셋에 추가했습니다. 저희는 약 50개의 데이터 포인트를 확보할 때까지 이 과정을 반복했습니다.
이제 저희 시스템을 개선할 차례였습니다! 어떻게 개선했는지에 대해 이야기하기 전에, 먼저 (1) 초기 시스템이 무엇이었는지, (2) 어떤 문제점들이 있었는지, 그리고 (3) 개선 사항을 측정하기 위해 시스템을 어떻게 평가할 것인지부터 논의하겠습니다.
초기 솔루션
Titanic 데이터셋은 여러 종류의 컬럼이 혼합되어 있습니다. 숫자형(age, number of siblings, fare), 범주형(station embarked, cabin) 컬럼과 텍스트 컬럼(name) 하나가 있습니다.
사람의 이름이 매우 많은 양의 텍스트는 아니지만, 여전히 텍스트 기반으로 충분하여 일부 문제를 일으킬 수 있습니다. 예를 들어
2번 항목을 좀 더 자세히 살펴보면, CSV의 한 행(row)을 문서(document)로 표현하는 몇 가지 방법이 있습니다. JSON으로 표현하거나, CSV 형태로 표현하거나, 아니면 저희가 결국 하게 된 것처럼 서식화된 텍스트 조각으로 표현할 수 있습니다. 구체적으로 말씀드리자면, 다음과 같은 값을 가진 CSV 행이 있다고 가정해 봅시다: {"col1": "foo", "col2": "bar"}
이것을 서식화하면 다음과 같이 보입니다:
col1: foo
col2: bar
이것이 별로 흥미롭지 않아 보일 수도 있지만, LLM 애플리케이션의 큰 부분은 데이터를 LLM에 가장 효과적으로 전달하기 위한 적절한 데이터 엔지니어링(data engineering)입니다. 저희는 특히 값이 텍스트 값을 포함할 수 있는 경우, 이러한 표 형식(tabular) (그리고 JSON) 데이터 표현 방식이 가장 효율적이라는 것을 경험적으로 발견했습니다.
질의 언어 (Query Language)
검색(retrieval) 외에도, 사람들은 일종의 질의 언어가 필요한 질문을 할 것이라고 생각했습니다. 예를 들어
데이터프레임(dataframe)을 반환할 것입니다. 이 데이터프레임은 프롬프트에 삽입되어 언어 모델(language model)로 전달되었습니다. 이때 중요한 질문이 생겼습니다. 바로 그 데이터프레임을 언어 모델로 전달하기 위해 문자열(string) 형태로 어떻게 포맷팅해야 하는가 하는 문제였습니다.
이는 C128 객실에 누가 있었는지와 같은 질문에 답하는 데 중요했습니다. 반환된 데이터프레임은 올바른 행으로 필터링되어 모든 관련 정보를 반환하기를 기대했습니다. 앱을 출시하기 전에 저희는 이런 종류의 질문들을 테스트했고 잘 작동했습니다. 하지만 앱을 출시하고 응답들을 살펴보기 시작했을 때, 이 유형의 많은 질문들에서 심각하게 실패하고 있다는 것을 알아차렸습니다.
저희는 LangSmith를 사용하여 트레이스(traces)를 검사하며 무슨 일이 일어나고 있는지 파악하려고 했습니다. 올바른 쿼리가 생성되고 있기는 했지만... 그 데이터프레임이 프롬프트로 전달될 때 포맷팅이 망가지고 있었습니다. 저희는 다음과 같은 모습일 것으로 예상했습니다:

하지만 대신 다음과 같은 모습을 하고 있었습니다:

추가 디버깅 끝에, 데이터프레임을 문자열로 표현하는 방식이 어떤 플랫폼에서 사용하느냐에 따라 달라질 수 있다는 것을 발견했습니다. 이 경우, 로컬 환경과 Streamlit cloud 간에 다르게 표현되고 있었습니다. 추가 디버깅을 거쳐, 다음과 같은 매개변수들을 지정함으로써 이러한 불일치를 해결할 수 있음을 알아냈습니다:
pd.set_option('display.max_rows', 20')<br>pd.set_option('display.max_columns', 20)
이것을 수행하자 많은 문제가 해결되었습니다! 또한 이는 LangSmith가 LLM 문제 디버깅에 얼마나 도움이 될 수 있는지를 보여줍니다. LLM 애플리케이션을 프로토타입에서 프로덕션으로 가져오는 주요 부분은 프롬프트 엔지니어링(prompt engineering)과 데이터 엔지니어링(data engineering)입니다. 데이터를 LLM에 전달할 때 정확히 어떤 모습인지 이해하는 것은 성능 문제를 디버깅하는 데 매우 중요합니다. 저희는 LangSmith의 여러 사용자들로부터, LangSmith를 사용하여 LLM의 정확한 입력값을 더 주의 깊게 검사한 후에야 이러한 유형의 데이터 엔지니어링 문제가 발견되었다는 이야기를 들었습니다.
💡
만약 데이터가 언어 모델에 명확하게 전달되지 않으면, 언어 모델이 그것을 추론하고 수정하는 것이 매우 까다로워집니다. LangSmith를 사용하여 최종 텍스트가 합리적으로 보이는지 확인하고 모든 데이터 처리 단계를 디버깅하는 것은 여기서 버그를 포착할 수 있는 좋은 방법입니다.
Limited kork
기능성
우리가 kork에 제공한 함수 세트가 사용자가 할 질문의 긴 꼬리(long tail) 부분을 커버하기에는 충분하지 않았다는 것이 밝혀졌습니다. 이에 대한 두 가지 잠재적 해결책이 있습니다. 첫째, kork에 더 많은 함수를 추가해 볼 수 있습니다. 둘째, Python REPL을 사용하는 것으로 되돌아갈 수 있습니다.
평가 설정
따라서 우리는 실제 사례 데이터셋을 구축했습니다. 또한 일부 수동 디버깅을 수행하여 오류가 발생하는 영역을 식별했으며 개선할 아이디어를 가지고 있습니다. 우리가 실제로 개선했는지 측정하려면 정확히 어떻게 해야 할까요?
AI 자동 생성 콘텐츠
본 콘텐츠는 LangChain Blog의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기