Fine tuning에 필요한 Data 준비 작업 강의 리뷰

Fine tuning에 필요한 Data 준비 작업 에 대한 강의 리뷰가 오늘의 포스팅입니다.
LLM을 Prompting에 기반하여 접근하는 방식은 조금 다루어 보았으니 이제는 Fine tuning에 대해 알아보려고 합니다.
Fine tuning을 말 그대로 번역하면 미세조정 정도로 번역해 볼 수 있습니다.
모든 Base model에는 Base model을 학습시킬 때 사용한 파라미터들이 있고 그 외에 여분의 파라미터들을 제공합니다.
Fine tuning에서는 기존에 학습된 파라미터를 건드리지 않고 그 외로 제공되는 여분의 파라미터들에 대한 최적화를 통해 나의 목적에 맞는 맥락을 LLM에 learning 시킬 수 있습니다.
오늘 posting은 LLM learning의 사전 단계로 Fine tuning을 하기 위한 데이터를 준비하는 과정을 적어봅니다.
그리고 그 중 기술적인 접근이 필요한 Tokenization/Padding/Truncating에 대해 정리합니다.

포스팅은 deeplearning.ai사와 lamini사가 공동으로 준비한 Finetuning LLM강의를 기반으로 합니다.

Fine tuning에 필요한 Data 준비 작업 – Tokenizatoin

Token
Token

Token화란 말은 AI뿐만 아니라 가상자산을 이야기 할때도 등장하는 단어입니다.
현실세계에 존재하는 사물을 컴퓨터가 다룰 수 있는 데이터로 변환하는 일을 tokenization 이라하고 그렇게 컴퓨터가 다룰 수 있는 하나의 정보단위를 Token이라 말씀드릴 수 있겠습니다.
Large language model은 인간이 사용하는 언어를 정보화 하고 컴퓨터가 주어진 입력에 맞추어 나올 수 있는 가장 높은 확률의 정보 연결을 출력해 주는 일을 하는 프로그램이라고 이야기 할 수 있습니다.
결국 컴퓨터 프로그램 입장에서는 입력으로 주어진 정보들을 분해하여 더이상 분해할 수 없는 의미를 가진 정보들의 연결을 분석합니다.
그리고 이런 입력이 주어졌을때 학습된 내용을 기반으로 가장 확률이 높은 출력이 될 수 있는 정보들의 연결을 만들어 냅니다.
이런 일을 하기 위해서는 인간이 사용하는 말안의 정보나 의미들을 컴퓨터에 사용될 수 있는 정보로 대응시키는 일이 필요하며 컴퓨터에서 사용될 수 있는 정보의 형태는 바로 숫자입니다.
따라서 Tokenization이란 사람이 사용하는 말을 의미를 가진 최소 형태로 분리하고 각 분리된 의미들을 숫자에 대응시키는 작업을 의미합니다.

Fine tuning에 필요한 Data 준비 작업 – Padding과 Truncating

Preparation for cooking
Preparation

패딩은 패딩 점퍼처럼 무엇인가를 덧붙이는 것을 의미합니다. 아울러 Truncating은 이와는 반대로 잘라내는 것을 의미합니다.
Padding과 Truncating이 중요한 이유가 있습니다.
우리는 잘 알지 못하고 ChatGPT를 쓰지만 이런 LLM에 입력을 하기위해서는 입력의 길이가 정해져 있어야 합니다.
아울러 계산의 효율성과 일관된 계산 결과를 얻기 위한 것이 중요하기 때문입니다.
일반적으로 길이가 변하는 정보들의 연결을 계산하는 것은 계산의 효율성이 떨어지고 그로 인한 계산의 결과가 부정확하기 때문입니다. 따라서 이런 정보들의 연결을 일정한 길이의 모임들로 처리하는 일이 필요합니다.
이런 처리 방법을 Vectoring, 그리고 반복적인 일을 수행한다고 하여 batch processing 이라고도 합니다.

Padding

Padding작업을 하기 위하여 padding에 사용할 특별한 token이 결정되어 있어야 합니다.
실제로 HuggingFace의 transformer library를 사용하면 이런 작업을 쉽게 수행할 수 있습니다.
라이브러리를 사용하는 이유에 대한 배경지식이 있다면 좀 더 잘 사용할 수 있지 않나 생각합니다.
Padding을 하는 이유는 입력정보의 길이를 일정하게 하기 위해서 입니다.
일정한 길이를 가진 정보들은 batch 라고 하는 job을 적용하기 쉽기 때문입니다.
아울러 길이를 맞추기 위하여 끼워넣는 token은 의미를 갖고 있는 token과 달리 의미가 없다는 것을 모델이 인식할 수 있습니다.

Truncating

이에 비해 Truncating 미세조정을 하기로 한 모델의 최대 입력 token 개수를 넘는 문장을 중간에 자르는 일을 말합니다.
이런 일을 하는 가장 중요한 이유는 LLM 모델이 처리할 수 있는 최대 token의 갯수에는 제약이 있기 때문입니다.
LLM 학습에서 가장 중요한 요소인 속도 측면에서 길이가 변화하는 token의 모임들을 처리함으로써 얻을 수 있는 이득보다 계산 속도 및 효율성을 얻는 이득이 훨씬 크기 때문입니다.
그러나 이렇게 연결된 의미의 모임을 잘라내는 것으로 인한 오류가 발생할 확률이 높기 때문에 이를 극복하기 위한 잘 알려진 몇가지 전략들이 있습니다.
지능형 Truncation: 정해진 길이를 넘으면 단순히 문장끝을 잘라내는 방법보다 문장 앞과 뒤를 같은 정도로 잘라내는 방법을 사용하기도 합니다.
스코어링 기법의 적용은 중요한 의미를 가진 token들에 대해 가중치를 적용하여 중요한 부분만 사용하는 경우도 있습니다.
슬라이딩 윈도우 기법( Sliding Window Approach): 길이가 긴 문서에 대해 적용할 수 있는 기법입니다. 문서의 내용을 몇개의 서로 겹치는 조각으로 분해 한 후 각 조각에 대해 tokenization을 수행하고 나중에 이를 결합하는 방식입니다.

Encoding 그리고 Encoding시 padding과 truncating 적용 예

아래는 위에 설명한 Encoding과 Encoding시 발생하는 padding과 truncating이 실제로 어떻게 발생하는지 HuggingFace의 transformer library를 사용하여 시험해 본 예입니다.
사용한 text는 아래와 같습니다.

"The cat sat on the mat.”
"The quick brown fox jumps over the lazy dog.”
“Hi."
"The quick brown fox jumps over the lazy dog, while the clever mouse watches from afar."

EleutherAI/pythia-70m 모델에 사용된 tokenizer를 사용하여 위의 시험 text를 token으로 변화해본 결과입니다.

list_texts = ["The cat sat on the mat.", "The quick brown fox jumps over the lazy dog.", "Hi.","The quick brown fox jumps over the lazy dog, while the clever mouse watches from afar."]
encoded_texts = tokenizer(list_texts)
print("Encoded several texts: ", encoded_texts["input_ids"])
=========================================================================================

Encoded several texts:  [[510, 5798, 2206, 327, 253, 1111, 15], [510, 3158, 8516, 30013, 27287, 689, 253, 22658, 4370, 15], [12764, 15], [510, 3158, 8516, 30013, 27287, 689, 253, 22658, 4370, 13, 1223, 253, 19080, 6521, 28654, 432, 6706, 274, 15]]

단어들이 숫자로 mapping 되는 것을 볼 수 있습니다.
경우에 따라 ~ing와 같은 시간에 대한 정보를 포함하는 단어는 단어 자체의 token과 시간에 대한 정보의 token으로 분리될 수 있습니다.

tokenizer에 padding token을 eos token으로 지정하여 padding을 수행한 경우 아래와 같이 동일한 길이를 갖는 token의 모임들이 생성됩니다.

tokenizer.pad_token = tokenizer.eos_token 
encoded_texts_longest = tokenizer(list_texts, padding=True)
print("Using padding: ", encoded_texts_longest["input_ids"])
=========================================================================================
Using padding:  [[510, 5798, 2206, 327, 253, 1111, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [510, 3158, 8516, 30013, 27287, 689, 253, 22658, 4370, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0], [12764, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [510, 3158, 8516, 30013, 27287, 689, 253, 22658, 4370, 13, 1223, 253, 19080, 6521, 28654, 432, 6706, 274, 15]]

위의 tokenization만 수행한 결과는 문장의 길이에 비례하여 토큰들의 길이가 정해졌지만 padding 옵션을 true로 설정한 경우 19라는 일정한 길이로 인코딩이 수행되는 것을 알 수 있습니다.

encoded_texts_truncation = tokenizer(list_texts, max_length=7, truncation=True)
print("Using truncation: ", encoded_texts_truncation[“input_ids”])
=========================================================================================
Using truncation:  [[510, 5798, 2206, 327, 253, 1111, 15], [30013, 27287, 689, 253, 22658, 4370, 15], [12764, 15], [19080, 6521, 28654, 432, 6706, 274, 15]]

이번에는 tokenization을 수행할 때 일부러 최대 길이를 7로 고정시키고 이 이상 넘는 부분은 잘라내는 “Truncation”을 수행한 결과입니다.
예에서 볼 수 있듯이 모든 token들의 길이가 7로 고정되어 출력되는 것을 볼 수 있습니다.
Truncation을 하는 기준은 별도로 지정하지 않는 한 문장의 끝을 처리합니다.

tokenizer.truncation_side = "left"
encoded_texts_truncation_left = tokenizer(list_texts, max_length=7, truncation=True)
print("Using left-side truncation: ", encoded_texts_truncation_left[“input_ids”])
=========================================================================================
Using left-side truncation:  [[510, 5798, 2206, 327, 253, 1111, 15], [30013, 27287, 689, 253, 22658, 4370, 15], [12764, 15], [19080, 6521, 28654, 432, 6706, 274, 15]]

같은 조건을 사용했으나 이번 tokenization 결과는 왼쪽을 기준으로 truncation을 수행한 결과입니다.
주로 왼쪽에 의미없는 token들이 모여있다고 판단될 때 사용할 수 있는 방법입니다.

encoded_texts_both = tokenizer(list_texts, max_length=7, truncation=True, padding=True)
print("Using both padding and truncation: ", encoded_texts_both[“input_ids"])
=========================================================================================
Using both padding and truncation:  [[510, 5798, 2206, 327, 253, 1111, 15], [30013, 27287, 689, 253, 22658, 4370, 15], [12764, 15, 0, 0, 0, 0, 0], [19080, 6521, 28654, 432, 6706, 274, 15]]

어떤 방식이 최선의 방식인가?

Best solution?
Best solution?

왼쪽을 잘라낼 것인가 아니면 오른쪽을 잘라낼 것인가 아니면 양쪽 끝을 같이 잘라낼 것인가에 대한 질문에 대한 완벽한 대답을 얻을 수는 없습니다.
각 기업들이 사용하는 데이터들과 사용되는 자연어들의 본질적인 성격에 따라 case by case일것으로 판단됩니다.
현재로서는 접근할 수 있는 제일 좋은 방법은 일단 많은 비용을 지불하더라도 활용가능한 모든 token들을 사용하여 최적의 결론을 도출한 후 다양한 시험을 통하여 어떤 방식이 우리가 원하는 목적에 부합하는지 알아내는것이 최적의 조합을 찾아내야 합니다.
이 목적을 달성하기 위해서는 빨리 데이터를 tuning하고 그에 따른 fine tuning을 빨리 수행하는 것이 문제에 접근하는 현실적인 방안입니다.
이의 달성을 위해서는 이른바 “Operation/Deployment”를 위한 “pipeline”이 필요하며 효율적인 작업 수행을 위한 자동화 infrastructure가 필요합니다.
다음 번 포스팅에서는 Google Cloud 기반으로 어떻게 이런 pipeline을 구축할 수 있는지 다른 강좌 리뷰를 통해 정리해 보도록 하겠습니다.

Similar Posts

Leave a Reply