<Prompt Brittleness: '아 다르고 어 다르다'>

개요

LLM을 활용할 때 'prompt engineering'과 'evaluation'이 점점 더 중요해지고 있습니다. 하지만 주어진 목표와 LLM에 따라 가장 좋은 성능을 보이는 프롬프트가 다르고, 막상 좋다고 알려진 프롬프트가 본인이 테스트해봤을 때는 다른 결과를 내 어려움을 겪는 유저와 엔지니어들이 많은 것 같습니다. 이러한 현상을 설명할 수 있는 하나의 관점으로, 'Prompt Brittleness'(프롬프트 취약성)이라는 개념이 있습니다.


1) Prompt의 마지막에 붙은 빈칸이 미치는 영향

https://careerly.co.kr/comments/101186?utm_campaign=self-share

2) LLM 답변의 신뢰도를 향상시킬 수 있는 Batch Calibration

https://careerly.co.kr/comments/102672?utm_campaign=self-share


7182767829207642113-QFAF?utm_source=share&utm_medium=member_desktop

기존에 작성한 글에서도 간접적으로 언급했듯이, 인간의 관점에서는 사소해 보이는 프롬프트의 변화가 LLM에게는 큰 영향을 줄 수 있습니다. 이런 현상에 대해 보다 구체적으로 알게 된 것은, 우연히 접한 "State of What Art? A Call for Multi-Prompt LLM Evaluation"(https://arxiv.org/pdf/2401.00595.pdf)를 읽고 난 후였습니다. 저자들은 동일한 의미를 가진 프롬프트라도 단어나 형식을 조금 변경하는 것(paraphrasing)으로 그에 대한 결과가 크게 달라지는 현상을 문제 삼고 있었습니다.

LLM은 확률적으로 대답을 생성하고, 그렇기 때문에 동일한 질문일지라도 다른 대답을 할 수 있다는 점은 인지하고 있었습니다. 그러나 그런 미세한 변화가 '유의미한' 영향을 미칠 수 있다는 생각은 하지 못했던 것 같습니다. 오늘은 정말로 이러한 현상이 발생하는지에 대해 간단히 실험해 본 결과와, 이를 완화하려면 어떻게 해야하는지에 대한 접근 방법을 공유하고자 합니다.


실험 1

식탁 위에 남겨진 과일의 개수(7개로 고정)를 구하는 간단한 프롬프트를 작성하고, 그 안의 숫자/인물명/과일명의 일부를 바꿔 총 27가지의 paraphrased된 질문을 생성했습니다. 이를 3개의 LLM(gpt-3.5-0125, claude-3-haiku-20240307, gemini-1.0-pro)에 각각 40회씩 테스트했습니다. 실험에 활용된 프롬프트의 기본형태는 아래와 같으며, fruits_list/numbers_list/names_list에 변수가 삽입됩니다.


<기본형태>

식탁 위에 <fruits_list0> <numbers_list0>개, <fruits_list1> 1개가 있습니다.

<names_list0>가 식탁에 있던 <fruits_list0> <numbers_list1>개를 먹고, 그 자리에 <fruits_list2> 사진 1장을 올려두었습니다.

이 과정에서 <fruits_list1> 1개가 땅으로 굴러떨어졌습니다.

<names_list0>가 식탁에 <fruits_list3> <numbers_list2>개와 양초 5개를 올려놓았습니다.

식탁 위에 남아있는 과일은 총 몇 개인가요?


<변수삽입 예시>

식탁 위에 <사과> <3>개, <배> 1개가 있습니다.

<철수>가 식탁에 있던 <사과> <1>개를 먹고, 그 자리에 <수박> 사진 1장을 올려두었습니다.

이 과정에서 <배> 1개가 땅으로 굴러떨어졌습니다.

<철수>가 식탁에 <귤> <5>개와 양초 5개를 올려놓았습니다.

식탁 위에 남아있는 과일은 총 몇 개인가요?


그 결과 프롬프트의 미세한 변화가 가시적인 영향을 준다는 것을 확인할 수 있었습니다. 동일한 LLM내에서도 상당한 차이를 보였으며, LLM마다 좋은 결과를 내는 프롬프트가 달랐습니다.

20번 프롬프트의 경우 gemini-1.0-pro에게는 최고의 성능을 보였지만, 그 외의 모델들에게서는 최저의 성능을 보였습니다.

21번 프롬프트의 경우에는 gpt-3.5-0125에서 최고의 성능을 보였지만, gemini-1.0-pro에게는 중간 정도의 성능을, claude-3-haiku-20240307에서는 단 1개의 정답도 맞추지 못하는 모습을 보여줬습니다.


20번 프롬프트

식탁 위에 <사과> <5>개, <배> 1개가 있습니다.

<은하>가 식탁에 있던 <사과> <5>개를 먹고, 그 자리에 <수박> 사진 1장을 올려두었습니다.

이 과정에서 <배> 1개가 땅으로 굴러떨어졌습니다.

<은하>가 식탁에 <귤> <7>개와 양초 5개를 올려놓았습니다.

식탁 위에 남아있는 과일은 총 몇 개인가요?


21번 프롬프트

식탁 위에 <배> <5>개, <사과> 1개가 있습니다.

<철수>가 식탁에 있던 <배> <5>개를 먹고, 그 자리에 <귤> 사진 1장을 올려두었습니다.

이 과정에서 <사과> 1개가 땅으로 굴러떨어졌습니다.

<철수>가 식탁에 <수박> <7>개와 양초 5개를 올려놓았습니다.

식탁 위에 남아있는 과일은 총 몇 개인가요?


실험 1.5

"실험 1"과 동일한 프롬프트를 활용하는 대신, claude와 gpt의 SOTA 모델(gpt-4-turbo-2024-04-09, claude-3-opus-20240229)을 활용해 27개의 paraphrased된 프롬프트를 20번씩 돌려봤습니다(gemini 1.5 pro의 경우 실험을 진행한 2024년 4월에 api로 접근 불가능해 제외했습니다).

작은 모델보다 정답률이 전반적으로 월등히 개선되었지만, 여전히 프롬프트별 정답률에 극심한 차이를 보였습니다. Claude opus의 경우 Claude haiku에서 정답률 1위~6위를 차지했던 12번~17번 프롬프트에서 정답률 0%를 기록하는 등 이해하기 어려운 결과가 나왔습니다.


12번 프롬프트(CLAUDE OPUS 정답률 0%, CLAUDE HAIKU 정답률 65%로 1위)

식탁 위에 <배> <4>개, <사과> 1개가 있습니다.

<철수>가 식탁에 있던 <배> 3개를 먹고, 그 자리에 <귤> 사진 1장을 올려두었습니다.

이 과정에서 <사과> 1개가 땅으로 굴러떨어졌습니다.

<철수>가 식탁에 <수박> <6>개와 양초 5개를 올려놓았습니다.

식탁 위에 남아있는 과일은 총 몇 개인가요?


실험2

새를 구성하는 부위의 명칭과 색에 대한 정보를 제공 후 머리의 색(파란색으로 고정)이 무엇인지에 대한 간단한 질문을 작성하고, 부위/색/조건의 순서를 섞어 총 27가지의 paraphrased된 프롬프트를 만들었습니다. 이를 3개의 LLM(gpt-3.5-0125, claude-3-haiku-20240307, gemini-1.0-pro)에 각각 20회씩 테스트해봤습니다. 실험에 활용된 프롬프트의 기본형태는 아래와 같으며, parts_list/colors_list/conditions_list에 변수가 삽입됩니다.


<기본형태>

새는 <parts_list0> 부위로 이루어져 있습니다.

또한, <colors_list0>을 띄고 있습니다.

<conditions_list0>

질문: 머리는 무슨 색인가요?


<변수삽입 예시>

새는 <머리, 몸통, 꼬리, 부리, 눈동자, 다리> 부위로 이루어져 있습니다.

또한, <검은색, 노란색, 파란색>을 띄고 있습니다.

<눈동자와 다리는 검은색입니다. 부리와 꼬리는 노란색입니다. 몸통과 머리는 같은 색상입니다.>

질문: 머리는 무슨 색인가요?


마찬가지로, 사소한 프롬프트의 변화로 LLM 내/외에서 정답률이 크게 달라지는 것을 목격할 수 있었습니다. 다만 이번에는 Claude haiku의 답변에서 어느 정도 패턴을 찾아볼 수 있었습니다. 'conditions_list'에 '몸통과 머리는 같은 색상입니다.'를 가장 처음 적은 프롬프트의 정답률이 극심하게 낮았습니다. 하지만 그 중에도 '파란색, 검은색, 노란색'과 같이 'colors_list'에 '파란색'을 맨 앞에 명시한 경우에는 100%의 정답률을 보였습니다. In-Context Learning(ICL)에 사용되는 예시의 순서에 따라 성능이 달라지는 현상은 이미 여러차례 보고되었습니다(https://aclanthology.org/2022.acl-long.556.pdf). 이번 실험에서 나타난 현상도 이와 비슷한 사례가 아닐까 싶네요.


20번 프롬프트 -> CLAUDE HAIKU 0/20

새는 <다리, 눈동자, 부리, 꼬리, 몸통, 머리> 부위로 이루어져 있습니다.

또한, <검은색, 노란색, 파란색>을 띄고 있습니다.

<몸통과 머리는 같은 색상입니다. 눈동자와 다리는 검은색입니다. 부리와 꼬리는 노란색입니다.>

질문: 머리는 무슨 색인가요?


18번 프롬프트(20번 프롬프트와 비교시, CONDITIONS_LIST의 순서만 다름) -> CLAUDE HAIKU 20/20

새는 <다리, 눈동자, 부리, 꼬리, 몸통, 머리> 부위로 이루어져 있습니다.

또한, <검은색, 노란색, 파란색>을 띄고 있습니다.

<눈동자와 다리는 검은색입니다. 부리와 꼬리는 노란색입니다. 몸통과 머리는 같은 색상입니다.>

질문: 머리는 무슨 색인가요?


26번 프롬프트(20번 프롬프트와 비교시, COLORS_LIST의 순서만 다름) -> CLAUDE HAIKU 20/20

새는 <다리, 눈동자, 부리, 꼬리, 몸통, 머리> 부위로 이루어져 있습니다.

또한, <파란색, 검은색, 노란색>을 띄고 있습니다.

<몸통과 머리는 같은 색상입니다. 눈동자와 다리는 검은색입니다. 부리와 꼬리는 노란색입니다.>

질문: 머리는 무슨 색인가요?


소결

이 실험을 진행하면서 여러가지 생각이 들 수 밖에 없었습니다.

Benchmark들은 LLM이 '1+1 ='에 대한 질문에 '2'라고 답변할 경우, 해당 LLM이 '1+2 => 3', '사과 1개와 귤 1개를 한 바구니에 두면? => 총 2개의 과일이 있습니다'와 같이 '산술'이라는 능력을 가지고 있다고 가정하고 있습니다.

하지만 직접 실험한 결과, LLM은 제가 생각한만큼 '일반화'에 뛰어나지 않았습니다.

LLM이 '대한민국의 수도의 이름을 말해보세요.'와 '대한민국의 수도의 명칭을 말해보세요.', '남한의 수도는 무엇이라 불리나요?'라는 질문에 대해 각각 다른 정답률을 보인다면 이를 어떻게 해석해야 할까요?

동일한 의미를 가진 채 문장의 구조만 바뀐 질문들에 대해 다른 정답률을 보인다면, LLM은 해당 질문의 답에 대해 '알고 있다'고 말할 수 있을까요?


LLM의 prompt brittleness를 완화하기 위해 "State of What Art? A Call for Multi-Prompt LLM Evaluation"에서는 하나의 질문에 대해 200여가지의 paraphrased된 버전을 테스트했습니다. 저자가 직접 언급한 다른 논문에서도(https://arxiv.org/abs/2310.11324, https://arxiv.org/pdf/2401.06766.pdf) 이런 systemic한 접근 방법을 제안하고 있습니다.


Google에서 발표한 "SYMBOL TUNING IMPROVES IN-CONTEXT LEARNING IN LANGUAGE MODELS"(https://arxiv.org/pdf/2305.08298.pdf)은 LLM의 prompt brittleness 문제를 완화시키는 하나의 방법으로 symbol tuning을 제시하고 있습니다. Symbol tuning은 자연어 레이블을 임의의 기호로 대체한 input-label 쌍으로 언어 모델을 파인튜닝하는 방법입니다. 예를 들어 영화평에 대해 긍정/부정으로 구분하는 작업의 경우, instruction을 생략하고 긍정/부정 레이블을 foo/bar과 같이 작업과 무관한 기호로 대체하여 파인튜닝합니다. 이렇게 symbol tuning을 적용한 결과, LLM의 prompt brittleness가 개선되었다고 합니다.


주의사항

이번 실험은 어떤 사실을 증명하고자 하는 것이 아닌, evaluation과 prompt engineering 단계에서 LLM이 지니는 취약점 대해 알리기 위함입니다.

정답을 쉽게 판별하기 위해 max_token을 실험1과 실험1.5에 대해서는 1 혹은 2로, 실험2에 대해서는 10으로 상당히 낮게 설정했습니다.

temperature, top_p, top_k 등의 값들에 대해서는 임의의 값으로 일괄 적용했습니다.

정답은 제가 직접 필터를 걸어 확인했습니다. 실험1과 실험1.5에 대해서는 '7', 실험2에 대해서는 '파란색', '파랑', '파랑색', '청색', '푸른색'을 정답으로 취급했습니다.

실험에 쓰인 코드, 프롬프트, 결과는 아래의 레포지토리에 간단히 정리해 놓았으니 참고 부탁드립니다.

https://github.com/simpleusername96/prompt_test/tree/master/robustness


기타

1) Anthropic의 claude3.0 모델들과 gemini-1.0-pro의 경우 max_token을 1로 설정하면 응답 자체를 하지 않을 수 있습니다.

2) max_token 값을 조정하면서 정답률이 미묘하게 달라지는 느낌을 받았습니다. 하지만 이 부분은 아직 실험으로 검증하지 못했습니다.

다음 내용이 궁금하다면?

또는

이미 회원이신가요?

2024년 4월 14일 오후 1:48

댓글 0