[LLM-MT 시리즈] (논문 구현🪄) DRT: Deep Reasoning Translation via Long Chain-of-Thought

Judy·2025년 7월 20일
0
post-thumbnail

Source 문장을 입력하면 Target 언어로 번역한 결과와 번역 과정(Thinking trace)을 생성하는 'DRT' 모델 구현

구현 동기

번역 + CoT = 품질 향상

LLM이 추론을 할 때, 추론 과정에서 CoT 를 거치면 답변 품질이 더 좋아진다는 것은 널리 알려진 사실이다.
번역 또한 마찬가지로, CoT 를 거치면 특히 복잡한 의미 파악이 필요한 문장을 번역하는 데에 효과적이다.

CoT 유/무에 따른 ChatGPT 번역 품질 예시는 다음과 같다.

  • Src : "그 사람 말투가 좀 이상하지 않아?"

  • Tgt : "Doesn't the way they talk sound a bit strange?"

  • CoT ➡️ Tgt : "Don’t you think the way he talks is a little strange?"

    • 프롬프트 :

      다음 CoT 를 거쳐서 주어진 문장을 영어로 번역해 줘
      
      "그 사람 말투가 좀 이상하지 않아?"
      
      <CoT>
      Step 1: 문장의 화자가 전달하려는 의도는?
      Step 2: 문장의 구조는?
      Step 3: 핵심 어휘를 영어로 자연스럽게 표현하면?
      Step 4: 자연스럽고 뉘앙스를 살린 영어 문장을 생성하라.
    • 결과 :

      Step 1: 의도
      → 그 사람 말투가 이상하다는 의심 + 공감 유도 질문
      
      Step 2: 구조
      → 평서문 + 부가 의문
      “말투가 이상하지” + “않아?”
      
      Step 3: 핵심 어휘
      
      말투: way of speaking, how he talks
      
      이상하다: weird, strange, off
      
      ~하지 않아?: Don’t you think…?
      
      Step 4: 번역
      ✅ "Don’t you think the way he talks is a little strange?"

한계 : 리소스 증가

번역 과정에서 CoT 를 적용하면 번역 품질이 더 좋아질 수 있지만,
CoT 는 필연적으로 LLM이 더 긴 내용을 추론(생성)해야 한다는 뜻이다.
이 때문에 답변을 생성하기까지 더 많은 시간이 걸리고, API 를 사용할 경우 더 많은 토큰을 소모하여 서비스 비용이 증가한다.

해결 : CoT 거쳐 번역하는 모델 구현

그렇다면, LLM 에 CoT 프롬프트를 추가하는 대신
LLM 모델이 번역 중 CoT(thought) 하도록 학습시켜서 사용해 보자!

논문에서 참고한 내용

논문 내용 중 구현에 필요한 점 위주로 정리했습니다.

3줄 요약

  • ✨ 이 논문은 문학 번역과 같은 어려운 기계 번역 작업에 'long chain-of-thought' 추론을 적용한 DRT 모델을 제안합니다. (영어 ➡️ 중국어)

  • 🛠️ DRT는 번역가, 조언자, 평가자로 구성된 멀티 에이전트 프레임워크와 GPT-4o를 활용하여 반복적인 'long thought' 번역 과정을 포함하는 학습 데이터를 생성합니다.

  • 🏆 이 학습 데이터로 SFT 한 DRT 모델은 'long thought' 없이 학습된 기존 LLM 및 SFT 모델 대비 우수한 문학 번역 성능을 보여 생성된 데이터와 방법론의 효과를 입증했습니다.

Multi-Agent 를 이용한 DRT Dataset 구축

영어 문학 데이터셋으로부터 번역하기 어려운 문장을 추출해 초기 데이터셋을 구축하고,
다음 작업을 수행하는 Agent 를 각각 만들고 서로 연결해 Multi-Agent 를 구축한다.

이어서 초기 데이터셋을 Multi-Agent 에 한 문장씩 <input> 넣어서
번역문 <output> 과 Thinking trace <thought> 를 얻는다.
최종적으로 {<input> : <output> - <thought>} 쌍으로 구성된 최종 DRT 데이터셋을 구축한다.

DRT model SFT

DRT 데이터셋을 llm backbone 모델에 Supervised Fine-Tuning 시켜서
Mutli-Agent 나 프롬프트 없이도 DRT 모델만으로 thinking trace 를 거쳐 번역한다.

Backbone 모델 정보:

  • DRT-7B: Qwen2.5-7B-Instruct
  • DRT-8B: Llama-3.1-8B-Instruct
  • DRT-14B: Qwen2.5-14B-Instruct

정리

  • 'long chain-of-thought' 추론을 적용한 DRT 모델을 개발해 문학 작품 번역에 적용
  • DRT 모델 학습용 데이터셋 구축에 Multi-Agent 사용
  • DRT 데이터로 SFT 학습한 모델은 'long thought' 없이 학습된 기존 LLM 및 SFT 모델과 비교해 문학 번역 성능이 더 좋다!

구현

초기 데이터셋

한국어 문학 데이터셋은 마땅한 게 없기도 했고, 초기 데이터셋 구축에 시간과 API 비용이 너무 많이 들어서
직역하기 어렵고, 은유나 비유 등이 포함된 문장 데이터셋으로
5년 전에 내가 직접 구축한 한-영 관용구 포함 문장 데이터셋 'KISS💋' 를 사용했다.
'KISS' 는 'Korean-english Idioms in Sentences dataSet' 의 약자로, 한국어 관용구가 포함된 문장과 이를 번역한 영어 문장 쌍 총 7500개로 구성된 데이터셋이다.

Multi-Agent 설계

논문에서 각각의 Agent 의 역할을 설명했고, 부록으로 프롬프트까지 제공했는데
정작 Multi-Agent 의 워크플로우 그림이 없다. 😒

일단 논문 저자가 허깅페이스에 올려둔 모델을 다운받아서 예시 문장을 넣으니 다음과 같이 출력한다. (영 ➡️ 중)

  • 예시 문장 : "The mother, with her feet propped up on a stool, seemed to be trying to get to the bottom of that answer, whose feminine profundity had struck her all of a heap."

  • 출력 (원본) :

    <thought>
    I start by identifying the key elements of the English sentence that need to be translated accurately. I pick out the keywords: 'mother', 'feet', 'stool', 'trying', 'bottom', 'answer', 'feminine', 'profundity', and 'struck'. I translate these keywords into Korean: 'mother' becomes '母亲', 'feet' becomes '脚', 'stool' becomes '凳子', 'trying' becomes '试图', 'bottom' becomes '底', 'answer' becomes '回答', 'feminine' becomes '女性的', 'profundity' becomes '深刻性', and 'struck' becomes '打动'.
    With these translations in mind, I attempt to translate the entire sentence: 母亲,她的脚放在凳子上,似乎在试图弄清那个回答的底细,这个回答的女性深刻性让她一时间被打动了. I review this translation and realize that while it captures the essence of the original sentence, some parts feel awkward and slightly inaccurate. The phrase 'whose feminine profundity had struck her all of a heap' is translated as '这个回答的女性深刻性让她一时间被打动了', which doesn't fully convey the sense of being deeply moved or struck by the depth of the response.
    I think about how to make the translation more natural and accurate. I revise the sentence to: 母亲,她的脚放在凳子上,似乎在试图弄清那番回答的真相,这番回答的女性深邃让她深受感动. This revised translation feels more natural and accurately conveys the original meaning. The phrase '这番回答的女性深邃让她深受感动' better captures the sense of being deeply moved by the profundity of the response.
    I review the revised translation and find it smooth and clear, effectively conveying the intended message. However, I notice a minor issue with the word '真相' (truth), which might not be the best choice here. I decide to use '底细' (details) instead, as it aligns better with the original text.
    Finally, I settle on the translation: 母亲,双脚搭在凳子上,似乎在努力探究那番回答的真谛,这番回答的女性深邃让她深受感动. This final translation feels right, capturing the nuances and emotional depth of the original English sentence.
    </thought>
    <output>
    母亲,双脚搭在凳子上,似乎在努力探究那番回答的真谛,这番回答的女性深邃让她深受感动。
    </output>
  • 출력 (번역) :

    <thought>
    저는 먼저 영어 문장에서 정확히 번역해야 할 핵심 요소들을 식별하는 것부터 시작합니다.
    주요 키워드를 뽑아냅니다: ‘mother’, ‘feet’, ‘stool’, ‘trying’, ‘bottom’, ‘answer’, ‘feminine’, ‘profundity’, ‘struck’.
    이 키워드들을 한국어로 번역해 봅니다:
    ‘mother’는 ‘母亲’, ‘feet’는 ‘脚’, ‘stool’는 ‘凳子’, ‘trying’은 ‘试图’, ‘bottom’은 ‘底’,
    ‘answer’는 ‘回答’, ‘feminine’은 ‘女性的’, ‘profundity’는 ‘深刻性’, ‘struck’는 ‘打动’이 됩니다.
    
    이러한 번역을 염두에 두고 전체 문장을 번역해 봅니다:
    母亲,她的脚放在凳子上,似乎在试图弄清那个回答的底细,这个回答的女性深刻性让她一时间被打动了.
    이 번역을 검토하면서, 전체적인 의미는 잘 전달되지만 일부 표현이 어색하거나 조금 부정확하다는 것을 느낍니다.
    예를 들어,
    "whose feminine profundity had struck her all of a heap"라는 표현은
    “这个回答的女性深刻性让她一时间被打动了”로 번역되어 있는데,
    이는 깊이 감동받은 느낌을 충분히 전달하지 못하고 있습니다.
    
    좀 더 자연스럽고 정확한 번역을 고민합니다.
    그래서 문장을 이렇게 수정해 봅니다:
    母亲,她的脚放在凳子上,似乎在试图弄清那番回答的真相,这番回答的女性深邃让她深受感动.
    
    이 수정된 문장은 좀 더 자연스럽고 원문의 의미를 잘 전달하는 것처럼 느껴집니다.
    ‘这番回答的女性深邃让她深受感动’라는 표현은
    그녀가 응답의 깊이에 의해 감명받았다는 느낌을 더 잘 담아냅니다.
    
    수정된 번역을 다시 살펴보니 전체적으로 부드럽고 명확하며, 전달하려는 메시지를 효과적으로 담고 있습니다.
    하지만 '真相'(진상/진실)이라는 단어가 다소 어울리지 않을 수 있다는 점이 눈에 띕니다.
    그래서 원문에 더 잘 맞는 표현으로 ‘底细’(자세한 내막)로 바꾸기로 합니다.
    
    결국 최종 번역은 이렇게 정리됩니다:
    母亲,双脚搭在凳子上,似乎在努力探究那番回答的真谛,这番回答的女性深邃让她深受感动.
    
    이 최종 번역은 원문이 가진 뉘앙스와 감정의 깊이를 잘 포착한 것처럼 느껴집니다.
    </thought>
    <output>
    엄마는 두 발을 의자 위에 올려놓고, 그 답변의 진의를 열심히 탐구하는 듯했다.
    그 답변에 담긴 여성적인 깊이에 그녀는 깊이 감동했다.
    </output>

<thought> 을 Multi-Agent 로 생성하는 셈인데..
출력 결과를 문단별로 분석해 보니 아마도 워크플로우가 이렇게 설계된 것 같다.

  1. 주요 키워드 추출 후 번역
  2. 키워드를 참고해 전체 문장 번역 후 번역 결과 검토, 평가
  3. 번역 결과 수정(재번역)
  4. 번역 결과 검토, 평가
  5. 번역 결과 검토, 평가, 수정(재번역)
  6. 최종 번역

이 워크플로우를 참고해 논문에서 설명하는 각 Agent 의 역할과 기능을 정리했다.

  1. Translator (Word-level) : 1개의 문장에 대해 중요한 단어 추출 후 중국어로 번역, {영단어 : 중국어 번역 단어} JSON 쌍 생성
  2. Translator (Preliminary): {영단어 : 중국어 번역 단어} 쌍을 참고해 문장을 중국어로 직역
  3. Advisor : 번역을 평가하고 의견과 제안 제공
  4. Evaluator : 번역에 대해 0점에서 100점까지 평가하고, 그 이유를 제시
  5. Translator : 영어 문장을 중국어로 재번역
  6. 3~5 반복, 종료 조건에 도달하면 반복 종료.
  7. Long Thought Reformulator : 전체 번역 과정을 긴 1인칭 자기 성찰 설명(현재 시제 사용)으로 다듬어서 Long CoT 생성

주요 Agent 3종 (Advisor, Evaluator, Translator)의 수식은 다음과 같은데

  • (3) Translation Refine Loop: 반복 단계 k에서 Agent들이 협력하여 번역을 정제합니다.
    • Advisor : 이전 번역 tk1t_{k-1}을 평가하고 피드백 fk1f^{k-1} 제공
    • Evaluator : tk1t_{k-1}에 대해 점수 sk1s^{k-1} 부여
    • Translator : tk1t_{k-1}, fk1f^{k-1}, sk1s^{k-1}을 고려하여 새 번역 tkt_k 생성
  • 루프는 점수 sk1s^{k-1}이 임계값에 도달하거나 최대 반복 횟수에 이르면 중단됩니다.

워크플로우와 수식에서 개인적으로 의아한 점들이 있었다.

  1. 비효율적인 워크플로우
    논문에서 Agent 를 언급한 순서대로라면 이렇게 되는데,
    단어 번역 → 문장 직역 → loop(Advisor → Evaluator → Translator) → Long Thought Reformulator
    문장 직역 직후에 Advisor 에서 피드백을 받고, 바로 Evaluator 로 넘긴다?
    만약 Evaluator 가 바로 통과시킬만한 문장이라면 피드백을 받는 과정이 불필요한 셈인데?

  2. 새 번역을 생성할 때 상대적으로 덜 중요한 요소 사용
    논문의 프롬프트에서 Evaluator 는 평가 점수와, 점수를 매긴 이유를 함께 설명하는데,
    그렇다면 Evaluator 의 결과를 재번역에 반영한다면 평가 점수가 아니라 평가 이유를 반영해야 맞지 않나?
    수식에서는 평가 점수인 ss 를 고려해 새 번역 tkt_k 를 생성하는데, 점수보다는 해당 점수를 매긴 이유를 고려해야 하지 않을까?

따라서 나는 논문과 조금 구현 방향이 달라지더라도 더 효율적이고, 인간의 작업 과정을 더 충실하게 모사하는 형태로 Multi-Agent 를 구현했다.

  • 동작 순서 : 단어 번역 → 문장 직역 → loop(translator → Evaluator → 조건문(loop 반복 조건 체크) → Advisor) → Long Thought Reformulator ➡️ 최종 저장
  • 문장을 직역한 직후에는 translator 에서 재번역을 하지 않고 바로 evaluator 로 넘어가서 즉시 번역문을 평가하고, threshold 이상의 점수를 얻으면 즉시 loop 가 종료되도록 하여 번역 효율 극대화
  • evaluator 가 평가 점수 ss 와 평가 이유 rr 을 생성하고, 이후 Agent 들이 평가 점수 ss 대신 평가 이유 rr 을 참고하도록 수정
  • Advisor 도 번역 피드백을 제공할 때, 단순히 번역문뿐만이 아니라 evaluator 의 평가 이유 rr 도 함께 고려해 피드백을 제공
  • Advisor 의 피드백에 rr이 포함된 셈이므로 Translator 는 rr 없이 ff 만 참고하여 재번역

(지금 생각해 보니 개선한 워크플로우도 여전히 비효율적이고
덕분에 생성되는 데이터의 정확도가 다소 떨어졌을 수도 있겠지만
구현 당시 나는 이게 최선이었다. 추후 추가 개선 예정!)

이 모든 과정을 거쳤는데 API 사용량 한계 + 개발 기간의 한계로 인해
KISS 의 7500문장으로부터 DRT 학습용 데이터 총 1638 개를 생성했다.

SFT

이렇게 얻은 DRT 데이터셋을 LLM 모델에 SFT 시켜서 최종적으로 DRT 를 수행하는 모델을 구현했다.
LLM backbone 은 논문과 똑같이 Qwen/Qwen2.5-7B-Instruct 을 사용했고,
학습에 Unsloth 프레임워크를 이용해 학습 시간을 크게 줄였다. (A100 에서 20분 내 완료)
파라미터는 Unsloth 튜토리얼에서 제공하는 값을 그대로 사용했는데
논문에서 사용한 파라미터 값을 참고해서 더 최적화했다면 좋았을 것 같다.

평가

타 번역 서비스와의 비교 및 인간 평가

한국어 문장 input 을 모델에 넣어 추론시키면 번역하기까지의 과정thought 과 영어로 번역한 결과 output 을 생성한다.
프롬프트는 원작자들의 GitHub(및 HuggingFace) 를 참고해 언어쌍만 바꿔서 똑같이 사용했다.

result = translate_drt_ollama.invoke([
	SystemMessage(content="You are a philosopher skilled in deep thinking, accustomed to exploring complex problems with profound insight."),
	HumanMessage(content=f"Please translate the following text from Korean to English:\n{source_text}")
]).content.strip()
  • input : "나는 남자 보는 눈이 높아서 차은우와 화촉을 밝힐 예정이다."

  • output : "I have a keen eye for men and plan to marry Cha Eun-woo with a ceremonial candlelighting."

  • thought :

    • I start by selecting keywords and phrases from the Korean sentence: "남자" (man), "보는 눈" (eye for men), "높아" (high), "차은우" (Cha Eun-woo, a male K-pop idol singer), "화촉" (bridal candle), "밝힐" (to light up) and "예정이다" (plan to do).

    • I translate these keywords into English: man, eye for men, high, Cha Eun-woo, bridal candle, to light up, plan to do.

    • Next, I attempt a preliminary translation of the Korean sentence: "I am a person with a high standard when it comes to judging men, so I plan to light the bridal candle for Cha Eun-woo."

    • As I review this translation, I feel that some parts need clarification. The phrase "judging men" may not be clear enough in English and might confuse the reader.

    • I consider rephrasing the sentence: "I have a keen eye for men and plan to marry Cha Eun-woo with a ceremonial candlelighting."

    • This version is more natural and easier to understand, conveying the original meaning clearly. The term "keen eye" is used instead of "judging," which may be less familiar in this context.

    • With these adjustments, I finalize my translation: "I have a keen eye for men and plan to marry Cha Eun-woo with a ceremonial candlelighting." This version effectively communicates the meaning and intent behind the Korean sentence while maintaining fluency and clarity.

의도적으로 관용구가 2개나 포함되고, 고유명사(사람 이름) 을 넣은 예시 문장을 만들어 테스트해 보았는데
키워드를 추출하는 첫번째 단계에서 주요 관용구 키워드인 '화촉' 이 잘 추출되었고
고유명사 '차은우' 또한 잘 추출되었음은 물론 K-pop 아이돌 가수라는 정보까지 덤으로 생성되었다.
(Qwen 은 대체 학습을 어떤 데이터셋으로 했길래..!?)
아무래도 차은우는 국보니까.. 😍

다른 번역기 및 LLM 서비스의 번역 결과와 비교하면 다음과 같다.

구분문장
Input나는 남자 보는 눈이 높아서 차은우와 화촉을 밝힐 예정이다.
OutputI have a keen eye for men and plan to marry Cha Eun-woo with a ceremonial candlelighting.
Qwen2.5-7B-Instruct(Backbone)I have high standards for men, so I plan to light the wedding altar with Cha Eun-woo.
번역기 AI have a high eye for men, so I'm going to light up Cha Eun-woo and his touch.
번역기 BI have high standards for men, so I plan on getting married to Cha Eun-woo.
번역기 CI'm a man's man, so I'm going to light up Cha Eun Woo and Hwak Hyeok.
LLM AI have high standards when it comes to men, so I plan to marry Cha Eun-woo.
LLM BI have high standards when it comes to men, so I'm planning to tie the knot with Cha Eun-woo.
LLM CI have high standards when it comes to men, so I'm going to marry Cha Eun-woo.

전통적인 번역 서비스와 비교해 LLM을 사용한 번역이 더 정확함은 물론
LLM B 는 '화촉을 밝히다' 에 대응되는 관용구인 'tie the knot' 으로 번역하는 센스를 발휘했다.
그러나 '화촉을 밝히다' 의 불을 밝히는 공감각적 심상을 살린 번역은 DRT 가 유일했다.

특이사항

  • Backbone 모델인 'Qwen2.5-7B-Instruct' 은 비교적 직설적인 표현을 사용했다.
    'light the wedding altar' 는 '결혼 제단에 불을 밝히다' 인데, ChatGPT 의 평가는 다음과 같다.
    • 영어권에서는 "to light the altar with someone"이라는 표현이 로맨틱하거나 상징적인 결혼 암시로 사용되지는 않습니다. 즉, 특별한 관용구는 아니고, 비교적 직설적인 묘사입니다.
    • 약간은 문학적이거나 드라마 대사처럼 들릴 수 있습니다.
  • LLM A 의 경우 의역을 많이 한 버전을 몇 가지 추가로 생성해 줬는데,
    그 중 '"My standards for men are sky-high — obviously, I'll be lighting the wedding candles with Cha Eun-woo."' 도 있기는 했다.

리소스 비교 평가

이런저런 이유로 반드시 DRT를 번역에 도입해야 한다고 가정하자.

만약 Multi-Agent 를 이용해 번역할 문장에 DRT 를 적용한다면 다음 과정을 거쳐야 한다.
단어 번역 → 문장 직역 → loop(translator → Evaluator → 조건문(loop 반복 조건 체크) → Advisor) → Long Thought Reformulator ➡️ 최종 저장

만약 운이 좋아 번역한 결과가 한 번에 Evaluator 를 통과해 Advisor 도 loop 반복도 없이
Long Thought Reformulator 를 거쳐 최종적으로 번역된다고 해도
단어 번역 → 문장 직역 → translator → Evaluator → Long Thought Reformulator = 총 5개의 Agent 를 실행해야 한다.

이는 다시 말해, DRT 를 적용하려면 최소 5번 이상 LLM을 실행해야 한다는 뜻이므로 단순히 LLM에 프롬프트를 주어 번역하는 경우와 비교해 최소 5배 이상의 리소스가 필요하다는 뜻이다.
또한, 이 DRT 는 이전 Agent 의 결과를 참조하여 순차적으로 이루어지므로 병렬처리도 불가능하여 총 수행 시간도 크게 증가한다.

그러므로 만약 반드시 DRT를 번역에 도입해야 하는 상황이라면 Multi-Agent 를 그대로 사용하지 않고, LLM backbone 모델에 DRT 데이터셋을 학습시켜 사용하는 것을 권장한다.

한 가지 더 고려해야 할 점은 DRT 데이터셋 구축에 드는 리소스이다.
앞서 설명한 이유로 인해 1개의 DRT 데이터를 만들기 위해 최소 5회 이상의 LLM 호출이 필요하며, 1000개의 DRT 데이터를 만들기 위해서는 5000회의 LLM 호출이 필요하다.
일부 작업에 오픈소스 sLLM 을 사용해 리소스를 절감할 수 있겠으나, 나같은 개인 연구자로서는 로컬에 ollama 로 올릴 수 있는 sLLM 은 7b 크기가 최대이며, 수행 시간도 유료 LLM API 에 비해 길다.

이렇듯 DRT 데이터셋 구축 단계부터 적지 않은 비용과 시간이 투입되므로
실험이나 개발을 하기 전에 우선 DRT가 과연 내가 하려는 작업에 적합한지 충분히 검토하고 나서 시도하는 것을 추천한다.

반대로 말하면, 장기적이고 대량의 데이터를 사용하는 작업에 DRT가 효과가 있는 경우
DRT 모델을 개발해 도입하면 Agent 5개 대신 모델 1개만으로 DRT를 수행할 수 있으므로 리소스를 5배로 감축할 수 있다.

이슈 & 해결

과도한 리소스

⚠️ 이슈
앞서 말했듯 DRT 데이터셋을 구축하기 위해 Multi-Agent 를 이용하면 최소 5회 이상 LLM을 호출하므로 적지 않은 토큰 비용과 수행 시간이 든다.

✅ 해결
만약 주요 키워드를 추출하는 DRT의 첫 번째 단계에서 고유명사 등은 제외하고 단어만 추출해도 된다면 LLM 대신 토크나이저를 사용하는 방법을 고려할 수 있다.
그 다음 단계에서는 앞서 추출한 주요 키워드를 참고해 직역하는데, 이 단계까지는 사용 토큰량이 적기 때문에 더 적은 토큰을 지원하는 저렴한 LLM API 를 사용하면 LLM 비용을 다소 절감할 수 있다.

또한 DRT 데이터셋 구축 과정에서 수행 시간을 줄이기 위해 배치와 멀티쓰레드를 도입해 실행 한 번에 batch size * max_workers 개의 문장에 Multi-Agent 를 적용해 DRT 데이터셋을 더 빠르게 얻을 수 있도록 했다.

batch_size = 10  # 한 배치당 문장 수
max_workers = 8   # 동시 실행할 배치 수

대용량 데이터 처리 중 작업 중단

⚠️ 이슈
대용량 데이터를 처리하는 중에는 몇 가지 이슈로 인해 작업이 중단되곤 한다.
대표적으로 유료 LLM API 요청 횟수가 증가하면 서버로부터 '429 too many requests' 에러를 리턴받기도 하고,
기타 예상하지 못한 이슈로 인해 코드 실행이 중단되면 지금까지 생성된 DRT 데이터는 모두 저장되지 못해 손실된다.
물론 DRT 데이터를 1개 얻을 때마다 파일에 저장하면 손실을 막을 수 있으나, 기본적으로 파일 i/o 하는 데에는 시간이 걸리고 잦은 i/o는 시스템에 과부하를 주기 때문에 작업 시간이 증가하고 freezing (일명 '컴퓨터 멈춤') 이 발생할 수 있다.

✅ 해결
가장 간단한 해결 방법은 fallback model 을 추가하는 것이다.
이렇게 하면 사용중이던 LLM이 에러를 리턴할 경우 미리 fallback model 로 지정해 둔 다른 모델로 전환하여 사용할 수 있으므로 모델 이슈로 중간에 작업이 끊기면 다른 모델을 사용해 작업을 지속할 수 있다.

except Exception as e:
	if self.fallback_llm and "rate" in str(e).lower():
		logger.warning(f"🔄 {self.primary_model}에서 오류 발생 ({e}), {self.fallback_model}으로 전환합니다.")
		return self.fallback_llm.invoke(messages, config=config)

작업이 중단될 경우 지금까지 얻은 DRT 데이터가 날아가는 것을 막아야 하지만, DRT 데이터를 얻을 때마다 저장하면 잦은 i/o 로 인한 문제들이 발생하므로 최대한 작업 중간에 저장할 수 있도록 자동화했다.
앞서 데이터셋 구축 작업에 배치와 멀티쓰레드를 적용한 이유는 병렬처리를 통해 수행 시간을 줄이려는 의도도 있었지만, 배치가 끝날 때마다 파일에 저장하도록 구현해 배치 단위로 파일 i/o가 이루어져 적당히 중간중간 저장되게(?) 구현하려는 의도도 있었다.

데이터 분포 편향

⚠️ 이슈
내가 만든 'KISS' 데이터셋은 관용구가 ㄱ-ㅎ 순으로 정렬된 데이터셋이다.
시간관계상 7500개의 문장 중 앞부분을 차지하는 문장 1638개만으로 DRT 데이터셋을 구축해서 사용했더니 DRT데이터셋에 포함된 관용구는 ㄱ-ㄷ로 시작하는 것밖에 없었다.

✅ 해결
재실험을 한다면 KISS 데이터를 랜덤하게 섞어서 DRT 데이터를 생성하거나,
생성한 데이터를 다시 랜덤하게 섞어서 SFT 를 할 예정이다.

학습데이터와 다른 SFT 출력값

⚠️ 이슈
우선 정석대로 HuggingFace 의 TRL 라이브러리를 사용해 SFT 를 시도했다.
from trl import SFTTrainer 를 사용하고 QLoRA 도 적용했는데, 모델 학습 결과를 테스트해 보니 포맷이 의도와 다르다!

테스트 코드

from transformers import AutoTokenizer, AutoModelForCausalLM
import torch

# ✅ 병합된 모델 로컬 경로에서 로드
model = AutoModelForCausalLM.from_pretrained(
    "./qwen-sft-merged",
    torch_dtype=torch.float16,
    device_map="auto",
    trust_remote_code=True
)

# ✅ 토크나이저는 base 모델에서 가져오기
tokenizer = AutoTokenizer.from_pretrained(
    "Qwen/Qwen2.5-7B-Instruct",
    trust_remote_code=True
)

# ✨ 테스트용 프롬프트
messages = [
    {"role": "system", "content": "You are a philosopher skilled in deep thinking."},
    {"role": "user", "content": "Please translate the following text from Korean to English:\n식은 죽 먹기"}
]

# 🧠 Qwen 스타일 prompt 생성
prompt = tokenizer.apply_chat_template(
    messages,
    tokenize=False,
    add_generation_prompt=True
)

# 💫 Tokenize & GPU에 넣기
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)

# 🚀 Inference (빠르게, 확실하게)
with torch.no_grad():
    generated_ids = model.generate(
        **inputs,
        max_new_tokens=128,
        do_sample=False,  # greedy
        pad_token_id=tokenizer.eos_token_id,
        use_cache=True
    )

# 📤 디코딩
output = tokenizer.decode(generated_ids[0], skip_special_tokens=True)

print("\n🧠 모델 출력:\n", output)

결과
<thought>, <output> 태그가 없다.

 The phrase "식은 죽 먹기" is a Korean idiom that translates to English as "eating rice that has already been cooked." This expression is used to describe a situation where someone is forced to do something they have already done before, or when someone is forced to repeat a task that they have already completed.

In more detail, the term "식은" (sigeun) means "already" or "previously," and "죽" (juk) refers to "rice." The word "먹기" (meogki) means "to eat." So literally, it suggests eating rice that has already been prepared. 

This idiom conveys the idea of unnecessary repetition or doing something that has already been done, often with a hint of frustration or resignation. For example, if a manager assigns a task to an employee that the employee has already finished, you might say, "It's like eating rice that has already been cooked," implying that the work was redundant.

Here is the translation:
"It's like eating rice that has already been cooked." 

✅ 해결
일단 구현 시간이 부족해서 Unsloth 프레임워크를 이용해 빠르게 학습했고, 결과도 잘 나왔지만 unsloth 를 이용했더니 왜 출력이 지정된 포맷대로 나오지 않는지 아직 확인하지 못했다.
학습 데이터가 너무 적고, 빠른 시간 안에 학습하기 위해 파라미터를 조절했기 때문에 기존 TRL SFTTrainer 를 사용해 학습했을 때 충분히 학습이 안 되어서일수도 있고, inference 할 때 내가 프롬프트를 잘못 줬을 수도 있다. 🥲
다시 실험할 때 코드 확인해봐야지..

결론

  • DRT 는 정해진 몇 단계를 거쳐 번역함으로써 번역의 품질을 높이는 효과가 있다.
  • DRT 모델을 직접 개발해 사용할 경우 데이터셋 구축 단계부터 적지 않은 비용과 시간이 투입되므로 우선 DRT가 과연 내가 하려는 작업에 적합한지 충분히 검토하고 나서 시도하는 것을 추천한다.
  • DRT 데이터셋 구축 단계에서 리소스를 줄이기 위해 일부 로직의 LLM을 토크나이저나 저렴한 LLM으로 교체할 수 있다.
  • DRT 데이터셋 구축 시간을 줄이기 위해 배치와 멀티쓰레드를 도입해 병렬처리를 적용할 수 있다.
  • 대용량 데이터 처리 작업 과정에서 파일 i/o 가 적당한 빈도로 수행되도록 구현한다.
profile
NLP Researcher, AI Engineer

1개의 댓글

comment-user-thumbnail
2025년 7월 20일

간단한 회고

  1. 내가 데이터셋은 곧잘 만드는데, 모델 튜닝 및 서빙은 좀 약한 것 같다.
    SFTTrainer inference 결과가 학습데이터와 다른 것도 문제고, 이 문제를 트러블슈팅하지 못하는 것도 문제고.
    서빙은 장비를 좀더 맘껏 쓸 수 있다면 ollama 이외에 vllm 이나 triton 같은 것도 충분히 시행착오를 겪으며 배울 수 있을텐데 🥲
    현재 참여중인 OSSCA HuggingFace 프로젝트에서 가능하다면 FT와 서빙을 좀 배워야겠다.
    안그래도 면접 볼 때마다 MLOps 질문 자주 받으니.. 🥲🥲

  2. 논문 작성 계획
    이왕 논문 하나 통째로 구현한 김에 실험 좀 보강해서 논문을 쓰려고 하는데...
    방향성을 어떻게 가져갈지 아이디어 정리를 먼저 해야겠다.
    데이터셋 구축이야 시간과 api 비용만 문제고.. 생각보다 LLM 서비스들이 번역을 잘 해줘서 내가 만든 DRT 모델이 더 뛰어난 점을 찾아야 한다...!

답글 달기