
데이터를 넘길 때 키 값과 함께 넘겨줘야 함
→ 서버 입장에서는 데이터를 받을 때 넘겨받은 게 어떤 데이터인지 명시해주지 않으면 무슨 데이터인지 알 수 없음: 구분자를 반드시 넣어주어야 함!
사용자가 직접 작성한 값이 value에 담기게 되기 때문에 사용자가 입력하는 input에서는 절대 작성하면 안 되지만 checkbox나 radio(라디오버튼)처럼 사용자가 선택하는 형태라면 무엇을 선택했는지 서버에 알려줘야 하기 때문에 value값을 반드시 적어주어야 한다.
type="submit"과 button tag





데이터베이스라는 공간에서 데이터를 꺼내볼 수도 있고 삭제할 수도 있고 수정할 수도 있음
'테이블' 개념
아이디, 비밀번호 입력해 로그인 하는 건 사실 '조회'만 하는 거지만 보안 문제 때문에 로그인에는 post를 사용한다!










font-familyfont-sizefont-weightfont-style.html 파일로 쓰고 있기 때문에(확장자가 .html 파일) CSS 코드를 쓰기 위해서는 style 태그를 꼭 열어줘야 함
</tag name>)와 같은 의미

*로 표시되며 웹 문서 내 모든 요소 선택.) 기호로 표시되며 특정한 요소 선택 # 기호로 표시되며 특정한 요소 선택
UX / UI와 연결됨
UX(User eXperience): 사용자 경험 → 느낌, 태도, 행동
예: 경험상 사이트 오른쪽 위에 로그인이 있음
여기 있는 태그가 두 번째 태그임 → 컴퓨터는 모름 → 알려주기: id/class
UI(User Interface): 예쁘게 디자인을 배치하는 것


클래스는 공유할 수 있는 그룹, 아이디는 나만 가지고 있는 고유한 값


for i, offset in enumerate(offset_mapping): → i는 미니배치 내에서 몇 번째 샘플인지를 의미answer = answers[i] → i번째 샘플의 정답answers[i]에 들어 있음 → start_char = answer["answer_start"][0]end_char = start_char + len(answer["text"][0])sequence_ids = inputs.sequence_ids(i) → [None, 0, 0, 0, 0, 0, 0, 0, None, 1, 1, 1, 1, 1, 1, 1, 1, 1, None, None, ..., None] 형태from transformers import DataCollatorForSeq2Seq
data_collator = DataCollatorForSeq2Seq(tokenizer=tokenizer, model=checkpoint)
DataCollator의 역할과 기능
예시 :
[1,2,3]->[1,2,3, -100, -100]
예시: 디코더 입력 생성 →타겟 시퀀스를 한 칸씩 오른쪽으로 시프트하고, 시작 토큰을 추가
- 입력:
["Hello", "World"]- 디코더 입력:
[<start>, "Hello", "World"]- 레이블:
["Hello", "World", <end>]
Tokenizer의 역할과 기능
텍스트 분할 (Tokenization)
예시:
"Hello, how are you?" →["Hello", ",", "how", "are", "you", "?"]
숫자 변환 (Encoding)
예시:
["Hello", "world"]→[101, 7592, 2088, 102]
특수 토큰 추가
[CLS], [SEP] 등 모델에 필요한 특수 토큰을 추가한다.패딩 (Padding)
Attention Mask 생성
디코딩 (Decoding)
라벨에서 패딩된 부분을 -100으로 설정하는 의미
-100은 PyTorch와 같은 딥러닝 프레임 워크에서 손실 함수 계산 시 자동으로 무시되며, 그레디언트 계산에서도 제외된다.Tokenizer와 DataCollator의 관계
실행 문장1 if 조건식 else 실행 문장 2# 나이가 18살 이상이면 '성인입니다'. 그렇지 않으면 '미성년자입니다'를 출력
age=25
print(성인입니다.) if age >= 18 else print("미성년자입니다.")
!pip install -q rouge_score
!pip install -q evaluate
import numpy as np
from evaluate import load
metrics = load("rouge")
def compute_metrics(eval_pred):
# eval_pred: trainer가 평가 시 넘겨주는 튜플 값 → (예측, 정답값)
predictions, labels = eval_pred
# 일부 환경에서 예측값이 튜플 형태로 출력될 때가 있음 → (prediction, )
# 조건문 달아주고 시작하기: 예측값이 튜플 형태로 들어올 때까지 고려
if isinstance(predictions, tuple):
predictions = predictions[0]
# ROUGE 평가 지표는 "자연어를 비교"하는 평가 지표 → 모델이 출력한 숫자를 비교하지 않음 → 예측값을 문자로 변환하여 비교
# 예측 값 문자열로 변환(decode)
decode_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True) # skip_special_tokens=True: 특수 토큰 제거
# 정답 값 문자열로 변환(decode)
# -100 → 패딩, 특수 토큰 값들은 디코딩 불가능
# -100 숫자 값은 토크나이저가 가지고 있는 정수 ID로 저장되어 있음
# 만약에 저장이 안 되어 있다면 0으로 치환
pad_id = tokenizer.pad_token_id if tokenizer.pad_token_id is not None else 0 # 예외를 방지하는 안전 장치
# labels 배열에서 -100인 값들을 모두 pad_id로 변환
# np.where(조건, 참, 거짓)
labels_for_decode = np.where(labels != -100, labels, pad_id)
# 치환된 정답값들을 문자열로 변환
decode_labels = tokenizer.batch_decode(labels_for_decode, skip_special_tokens=True)
# 문자열로 변환한 데이터를 비교 → 유사도를 계산
result = metrics.compute(
predictions=decode_preds
, references=decode_labels
, use_stemmer=True # 어간 추출 → run / running이 비슷한 유사도를 가지게 됨
)
# 패딩을 제외한 예측값의 길이
# pred != pad_id → 패딩이 아닌 값은 True(1), 패딩은 False(1)
prediction_lens = [np.count_nonzero(pred != pad_id) for pred in predictions]
# 예측값의 평균 길이 확인
# 길이 값이 있으면 평균을, 없으면 0.0을 담아주기
result["gen_len"] = float(np.mean(prediction_lens)) if prediction_lens else 0.0
# 값 v가 실수 또는 정수인지 검사 → 실수, 정수가 아니라면 그대로 출력
# 실수, 정수라면 반올림(소수점 4번째) 진행
return {k: round(v, 4) if isinstance(v,(int,float)) else v for k, v in result.items()}
[batch_size, seq_length, num_labels]였기 때문에 pred = np.argmax(logits, axis=2)로 처리했지만 여기는 predictions에 들어오는 형태가 다름predictions는 이런 형태를 하고 있음:
predictions = ( np.array([[ 101, 2023, 2003, 1037, 2742, 102], # 토큰 ID 예시 [ 101, 2057, 2293, 2023, 3185, 102]]), None )첫 번째 요소는 실제 tokenizer로 디코딩할 대상(토큰 ID), 두 번째 요소는 None이거나 logits 관련 추가 정보일 수 있음
따라서 아래와 같이 첫 번째만 꺼내서 쓰는 것if isinstance(predictions, tuple): predictions = predictions[0]
eval_pred가 넘어오는 형태Trainer나 Seq2SeqTrainer에서 compute_metrics를 실행할 때, eval_pred는 아래 형태로 전달됩니다.
eval_pred = (predictions, labels)
여기서
predictions: 모델의 출력 (토큰 ID 또는 로짓 값) labels: 정답 토큰 ID하지만 Seq2SeqTrainer에서는 경우에 따라 predictions가 튜플로 들어올 수 있습니다. 예를 들어:
predictions = (
np.array([[ 101, 2023, 2003, 1037, 2742, 102], # 토큰 ID 예시
[ 101, 2057, 2293, 2023, 3185, 102]]),
None
)
이 경우 첫 번째 요소는 실제 tokenizer로 디코딩할 대상(토큰 ID),
두 번째 요소는 None이거나 logits 관련 추가 정보일 수 있습니다.
그래서 이럴 때:
if isinstance(predictions, tuple):
predictions = predictions[0]
로 첫 번째만 꺼내 쓰는 거죠.
실제 predictions는 보통 토큰 ID(정수) 배열입니다. 예를 들어:
predictions = np.array([
[101, 2023, 2003, 1037, 2742, 102, 0, 0], # 0이 pad_id
[101, 2057, 2293, 2023, 3185, 102, 0, 0]
])
여기서 각 숫자는 토크나이저의 vocabulary ID입니다.
예를 들어 BERT 토크나이저에서:
[CLS] (시작 토큰) [SEP] (종료 토큰) [PAD] (패딩 토큰) "this" "is" "test"이걸 디코딩하면:
decode_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)
결과는:
[
"this is a test",
"we love this model"
]
labels도 비슷하게 ID 배열이지만, Trainer 내부에서는 패딩 토큰을 -100으로 채우는 경우가 많습니다.
이유는 loss 계산 시 패딩 부분은 무시하기 위해서 입니다.
예시:
labels = np.array([
[101, 2023, 2003, 1037, 2742, 102, -100, -100],
[101, 2057, 2293, 2023, 3185, 102, -100, -100]
])
compute_metrics에서:
labels_for_decode = np.where(labels != -100, labels, pad_id)
를 하면:
labels_for_decode = [
[101, 2023, 2003, 1037, 2742, 102, 0, 0],
[101, 2057, 2293, 2023, 3185, 102, 0, 0]
]
이제 tokenizer.batch_decode로 자연어로 변환 가능해집니다.
즉, predictions 값은 보통 (배치 크기, 시퀀스 길이) 형태의 numpy.ndarray이거나 경우에 따라 (ndarray, None) 형태의 튜플로 들어옵니다.
| 상황 | 예시 형태 |
|---|---|
| 일반적인 경우 | np.ndarray([[101, 2023, ...], [101, 2057, ...]]) |
| 튜플로 들어오는 경우 | (np.ndarray([[101, ...], ...]), None) |
{
'rouge1': 0.456789,
'rouge2': 0.217891,
'rougeL': 0.392812,
'rougeLsum': 0.391923,
'gen_len': 72.3333333333
}
| 항목 | 평가 기준 | 의미 | 값 범위 | 해석 기준 |
|---|---|---|---|---|
rouge1 | unigram | 단어 단위 내용 일치도 | 0 ~ 1 | 0~0.3: 낮음 / 0.3~0.5: 보통 이하 / 0.5~0.7: 양호 / 0.7~0.9: 우수 / 0.9~1.0: 사람 수준 |
rouge2 | bigram | 문맥·연속성 일치도 | 0 ~ 1 | 보통 rouge1보다 낮음. 문맥이 잘 맞으면 0.3 이상, 0.5↑이면 매우 양호 |
rougeL | LCS | 순서를 유지한 구조적 일치도 | 0 ~ 1 | 문장 구조 보존 정도 평가. 0.5↑이면 문장 구조를 잘 유지한 편 |
rougeLsum | LCS+문장 | 문장 단위 구조적 일치도 | 0 ~ 1 | 여러 문장 요약에서 구조적 유사도를 평가. rougeL과 유사하지만 문장 단위로 더 세밀 |
gen_len | 토큰 수 | 평균 생성 길이(패딩 제외) | ≥ 0 (정수/실수) | 요약의 평균 길이. 너무 짧으면 정보 손실, 너무 길면 불필요한 내용 포함 가능 |

labels = np.where(labels != -100, labels, tokenizer.pad_token_id)
decode_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
🤔 tokenizer.pad_token_id가 None인 경우에는 아래와 같은 에러가 납니다.
TypeError: int() argument must be a string, a bytes-like object or a real number, not 'NoneType'
pad_id = tokenizer.pad_token_id or 0
labels = np.where(labels != -100, labels, pad_id)
decode_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
import numpy as np
from evaluate import load
metrics = load("rouge")
def compute_metrics(eval_pred):
predictions, labels = eval_pred
# 예측값이 튜플로 들어오는 경우 처리 → 예측값이 튜플 형태일 경우 첫 번째 요소 사용
if isinstance(predictions, tuple):
predictions = predictions[0]
# pad_token_id 안전 처리 (없으면 0 사용)
pad_id = tokenizer.pad_token_id or 0
# 예측값 디코딩
decoded_preds = tokenizer.batch_decode(predictions, skip_special_tokens=True)
# 정답값 디코딩: -100(무시 토큰)을 pad_id로 치환 후 디코딩
labels = np.where(labels != -100, labels, pad_id)
decoded_labels = tokenizer.batch_decode(labels, skip_special_tokens=True)
# ROUGE 점수 계산
result = metrics.compute(
predictions=decode_preds,
references=decode_labels,
use_stemmer=True
)
# 평균 생성 길이
result["gen_len"] = float(np.mean([np.count_nonzero(p != pad_id) for p in predictions])) or 0.0
# 결과 소수점 4자리까지 반올림 (정수, 실수만)
return {k: round(v * 100, 4) if isinstance(v, (int, float)) else v
for k, v in result.items()}
import numpy as np
from evaluate import load
metrics = load("rouge")
def compute_metrics(eval_pred):
preds, labels = eval_pred
if isinstance(preds, tuple):
preds = preds[0]
pad_id = tokenizer.pad_token_id or 0
# -100을 pad_id로 치환 후 디코딩
decoded_preds = tokenizer.batch_decode(preds, skip_special_tokens=True)
decoded_labels = tokenizer.batch_decode(np.where(labels != -100, labels, pad_id), skip_special_tokens=True)
result = metrics.compute(predictions=decoded_preds, references=decoded_labels, use_stemmer=True)
result["gen_len"] = float(np.mean([np.count_nonzero(p != pad_id) for p in preds])) or 0.0
return {k: round(v * 100, 4) if isinstance(v, (int, float)) else v for k, v in result.items()}
특징
1. 중간 변수 최소화
labels_for_decode, prediction_lens 등 별도 변수 없이 계산 시 바로 처리tokenizer.pad_token_id가 None이면 자동으로 0 사용import numpy as np
from evaluate import load
metrics = load("rouge")
def compute_metrics(eval_pred):
p, l = eval_pred
if isinstance(p, tuple): p = p[0]
pad = tokenizer.pad_token_id or 0
result = metrics.compute(
predictions=tokenizer.batch_decode(p, skip_special_tokens=True),
references=tokenizer.batch_decode(np.where(l != -100, l, pad), skip_special_tokens=True),
use_stemmer=True
)
result["gen_len"] = float(np.mean([np.count_nonzero(x != pad) for x in p])) or 0.0
return {k: round(v * 100, 4) if isinstance(v, (int, float)) else v for k, v in result.items()}
✨ 특징:
1. 변수 최소화 → preds, labels_for_decode 같은 중간 변수 없이 바로 처리
2. 배치 디코딩과 -100 치환을 한 줄에서 처리
3. 평균 생성 길이 계산도 한 줄에서 처리
4. 안전하게 pad_token_id 없으면 0 사용
이 버전은 실험 및 빠른 프로토타입 제작에는 좋지만, 프로덕션에서는 가독성이 좋은 이전 “초간결 안전 버전”을 쓰길 권장
import numpy as np
from evaluate import load
# 필요한 지표들 로드 (예: rouge, bleu, meteor)
metric_names = ["rouge", "bleu", "meteor"]
metrics = [load(name) for name in metric_names]
def compute_metrics(eval_pred):
preds, labels = eval_pred
if isinstance(preds, tuple):
preds = preds[0]
pad_id = tokenizer.pad_token_id or 0
decoded_preds = tokenizer.batch_decode(preds, skip_special_tokens=True)
decoded_labels = tokenizer.batch_decode(np.where(labels != -100, labels, pad_id), skip_special_tokens=True)
results = {}
# 각 메트릭별 계산 및 결과 병합
for metric in metrics:
# 지표 이름에 따라 옵션 다르게 처리
if metric.info.name == "rouge":
# ROUGE 특성상 어간 추출(use_stemmer) 옵션 별도 적용 가능
result = metric.compute(predictions=decoded_preds, references=decoded_labels, use_stemmer=True)
else:
# 그 외 지표는 기본 compute 호출
result = metric.compute(predictions=decoded_preds, references=decoded_labels)
results.update(result)
# 생성 길이 평균 계산
results["gen_len"] = float(np.mean([np.count_nonzero(p != pad_id) for p in preds])) or 0.0
# 결과 100배 후 소수점 4자리 반올림 (숫자에 한함)
return {k: round(v * 100, 4) if isinstance(v, (int, float)) else v for k, v in results.items()}
✅ 사용 및 확장 방법
rouge, bleu 등은 Hugging Face evaluate 라이브러리에서 로드해야 사용 가능합니다.from evaluate import load
rouge = load("rouge")
bleu = load("bleu")
# meteor = load("meteor") 등 필요하면 추가💡 특징 요약
gen_len)를 포함해서 딕셔너리 형태로 반환하는 함수를 만든 것metrics.compute() (여기서는 evaluate 라이브러리의 rouge 로드)를 호출하면 보통 다음과 같이 숫자(float) 값들이 나옵니다:
{
"rouge1": 0.5234, # ROUGE-1 F1 score
"rouge2": 0.3123, # ROUGE-2 F1 score
"rougeL": 0.4981, # ROUGE-L F1 score
"rougeLsum": 0.5002 # 문장 단위(Lsum) 기준 ROUGE-L
}
여기에 compute_metrics에서 gen_len 키를 추가합니다:
{
"rouge1": 0.5234,
"rouge2": 0.3123,
"rougeL": 0.4981,
"rougeLsum": 0.5002,
"gen_len": 18.7
}
마지막 return 구문에서
return {k: round(v, 4) if isinstance(v,(int,float)) else v for k, v in result.items()}
를 사용
즉,
v가 정수(int)나 소수(float)이면 → 소수점 4자리까지 반올림str, list, dict 등) → 원래 값 그대로 반환
rouge_score의 compute 기본 설정(F1 score 반환)에서는 보통 숫자만 나옵니다.
그런데 다음 상황에서는 숫자가 아닌 값이 반환될 수 있습니다:
metrics.compute(..., use_aggregator=False) 옵션 사용
→ 개별 샘플별 점수를 리스트 형태로 반환
예:
{
"rouge1": [0.5, 0.4, 0.6],
"rouge2": [0.3, 0.25, 0.35],
...
}
ROUGE 점수가 아닌 다른 항목을 추가한 경우
예를 들어 "predictions" 또는 "references"를 그대로 dict에 넣는다면
값이 문자열 리스트가 될 수 있음:
{
"rouge1": 0.5234,
"references": ["요약문 예시1", "요약문 예시2"]
}
빈 prediction이나 reference가 있을 경우
특정 조건에서 metric 라이브러리가 예외 메시지나 None을 줄 수 있음.
compute_metrics 반환 예시일반적인 요약 모델 학습 후 평가 시
{
'rouge1': 0.5234,
'rouge2': 0.3123,
'rougeL': 0.4981,
'rougeLsum': 0.5002,
'gen_len': 18.7
}
use_aggregator=False 사용 시 (숫자가 아닌 값 포함)
{
'rouge1': [0.53, 0.48, 0.52],
'rouge2': [0.32, 0.30, 0.28],
'rougeL': [0.50, 0.49, 0.51],
'rougeLsum': [0.50, 0.47, 0.49],
'gen_len': 18.7
}
→ 여기서 리스트는 int/float가 아니므로 그대로 출력됨
from transformers import AutoModelForSeq2SeqLM, Seq2SeqTrainingArguments, Seq2SeqTrainer
# 모델 객체 생성
model = AutoModelForSeq2SeqLM.from_pretrained(checkpoint)
# 하이퍼파라미터 정의
training_args = Seq2SeqTrainingArguments(
output_dir="./results/my_awesome_billsum_model"
, eval_strategy="steps"
, learning_rate=2e-5
, weight_decay=0.01
, load_best_model_at_end=True
, per_device_train_batch_size=16
, per_device_eval_batch_size=16
, save_total_limit=3
, num_train_epochs=4
, predict_with_generate=True
, fp16=True
, logging_strategy="steps"
, logging_steps=2
, push_to_hub=False
)
trainer = Seq2SeqTrainer(
model=model
, args=training_args
, train_dataset=tokenized_billsum["train"]
, eval_dataset=tokenized_billsum["test"]
, processing_class=tokenizer
, data_collator=data_collator
, compute_metrics=compute_metrics
)
trainer.train()
실행 화면 예시:

# 학습한 결과를 허깅페이스에 업로드
%cd /content/drive/MyDrive/Colab Notebooks/NLP
# 허깅페이스 로그인
from huggingface_hub import login
# 파일 형태의 api_key 불러오기
with open("./key/huggingface_api_key", 'r') as f:
api_key = f.read().strip()
login(token=api_key)
# 허깅페이스 업로드
repo_id = "★유저아이디★/my_billsum_summarize_model"
trainer.save_model(repo_id)
model.save_pretrained(repo_id)
tokenizer.save_pretrained(repo_id)
trainer.push_to_hub(repo_id)
# task="summarization"
from transformers import pipeline
checkpoint_mymodel="be2be2/my_billsum_summarize_model"
summarization = pipeline(
task="summarization"
, model=checkpoint_mymodel
, tokenizer=checkpoint_mymodel
)
# 요약할 문장
text = """summarize: The Inflation Reduction Act lowers prescription drug costs,
health care costs, and energy costs. It's the most aggressive action on tackling
the climate crisis in American history, which will lift up American workers and
create good-paying, union jobs across the country. It'll lower the deficit and
ask the ultra-wealthy and corporations to pay their fair share. And no one making
under $400,000 per year will pay a penny more in taxes."""
result = summarization(text)
result
Your max_length is set to 200, but your input_length is only 103. Since this is a summarization task, where outputs shorter than the input are typically wanted, you might consider decreasing max_length manually, e.g. summarizer('...', max_length=51)
[{'summary_text': "the Inflation Reduction Act lowers prescription drug costs, health care costs, and energy costs . it's the most aggressive action on tackling the climate crisis in American history . no one making under $400,000 per year will pay a penny more in taxes."}]
T5 모델에서 텍스트 요약 시 prefix를 학습할 때와 평가할 때 다르게 써도 작동하는 이유는 다음과 같습니다.
1. T5는 모든 NLP 작업을 텍스트-투-텍스트(text-to-text) 포맷으로 통일하여 처리하는 인코더-디코더 트랜스포머 모델입니다. 각 작업에 대응하는 특정 prefix(예: "summarize: ")를 입력 문장 앞에 붙여서 모델이 해당 작업을 수행하도록 지시합니다. 즉, prefix는 모델에게 현재 어떤 작업을 해야 하는지 알려주는 역할을 합니다.
2. 학습 시에는 모델이 다양한 작업(prefix 포함)을 학습하면서 입력과 출력의 매핑을 익힙니다. 이때 prefix는 작업의 정체성을 암시하는 신호 역할을 하여, 모델이 그에 맞는 출력을 생성하도록 도와줍니다.
3. 평가(추론) 시에는 학습된 모델이 입력에 붙은 prefix를 보고 어떤 작업을 수행할지 결정합니다. 만약 평가 시 prefix가 다른 형태이거나 없더라도, 근본적으로는 학습 시와 유사한 조건 하에서 텍스트 변환(task 수행)이 이루어지기 때문에 어느 정도 작동합니다. 즉, prefix의 변화가 있더라도 모델이 문맥과 작업 유형을 이해하는 데 큰 문제를 일으키지 않는 경우가 많습니다.
4. 다만, prefix를 학습 때와 평가 때 다르게 사용할 경우 성능 차이가 있을 수 있습니다. 이는 prefix가 작업 신호로서 모델에 영향을 주기 때문인데, 동일한 prefix를 사용하는 것이 보통 더 빠른 수렴과 더 좋은 성능에 도움이 됩니다.
5. 사전학습 단계 역시 여러 작업에서 다양한 prefix를 포함하여 진행되는데, 이로 인해 모델이 prefix가 다소 다르게 주어져도 작업 수행에 유연하게 대응할 수 있는 특성을 가지게 됩니다.
왜 prefix를 다르게 써도 모델이 제대로 작동하는 걸까요?
간단히 말해, T5 모델은 prefix를 작업 지시어로 사용하는데, 학습과 평가는 모두 텍스트 입력-출력 매핑이라는 점에서 동일한 원리를 따르며, 사전학습과 다중 작업 학습 덕분에 prefix가 다소 변해도 모델이 작동하게 설계되어 있기 때문입니다.
즉, prefix를 다르게 써도 작동하는 것은 모델이 prefix를 통해 작업을 인식하지만, 그 근본적 작동 원리는 텍스트-투-텍스트 변환이고, 사전 학습부터 다양한 prefix를 접했기 때문에 어느 정도 유연성을 갖고 있기 때문입니다. 다만, 동일한 prefix를 사용하면 더 좋은 성능과 빠른 수렴이 기대됩니다.
T5 모델과 같은 사전 학습된 언어 모델이 prefix를 다르게 써도 제대로 작동하는 이유는, 모델이 학습 당시 다양한 prefix와 여러 작업을 접하며 여러 형태의 작업 지시어에 유연하게 반응하도록 학습되었기 때문입니다. 즉, prefix는 모델에게 수행할 작업을 알려주는 신호 역할을 하는데, 모델 내부에서는 입력 텍스트와 prefix를 함께 해석하며 텍스트 변환을 진행하기 때문에, prefix가 약간 다르더라도 모델이 작업의 맥락을 이해하고 적절히 대응할 수 있습니다.
더 구체적으로, prefix tuning과 prompt tuning 기법에서 볼 수 있듯이, 모델은 입력값 앞에 붙은 prefix(또는 학습 가능한 prefix 토큰 시퀀스)를 통해 작업에 맞는 출력을 생성하도록 설계되어 있지만, 사전 학습과 다양한 작업에서 여러 형태의 prefix를 경험했기 때문에 어느 정도 다름을 허용합니다. 때문에 학습 시와 평가 시 prefix가 달라도 근본적으로 입력-출력 매핑 원리가 유지되면서 모델이 작동할 수 있습니다.
또한, prefix tuning 기법은 입력 시퀀스마다 학습 가능한 prefix를 넣고 나머지 모델 파라미터는 고정시키는 식으로 미세조정을 하여 모델이 특정 작업에 더 잘 적응하도록 돕는데, 이로 인해 prefix가 약간 변경되어도 학습된 패턴에 의해 작업 수행이 가능해지는 것입니다.
요약하면,
- 모델은 다양한 prefix에 대해 학습 및 사전 학습되었고
- prefix는 작업 신호 역할을 하지만 텍스트 변환 자체는 입력-출력 매핑이라는 근본 원리에 기반함
- 따라서 prefix가 다소 달라도 모델이 문맥과 작업 유형을 이해해 작동할 수 있음
- 다만, 동일한 prefix를 사용하는 것이 보통 더 좋은 성능을 냅니다.
# 요약할 문장
text2 = """summarize long sentences The Inflation Reduction Act lowers prescription drug costs,
health care costs, and energy costs. It's the most aggressive action on tackling
the climate crisis in American history, which will lift up American workers and
create good-paying, union jobs across the country. It'll lower the deficit and
ask the ultra-wealthy and corporations to pay their fair share. And no one making
under $400,000 per year will pay a penny more in taxes."""
result2 = summarization(text2)
result2
Your max_length is set to 200, but your input_length is only 104. Since this is a summarization task, where outputs shorter than the input are typically wanted, you might consider decreasing max_length manually, e.g. summarizer('...', max_length=52)
[{'summary_text': "the Inflation Reduction Act lowers prescription drug costs, health care costs, and energy costs . it's the most aggressive action on tackling the climate crisis in American history . no one making under $400,000 per year will pay a penny more in taxes."}]
→ 2초 걸림('summarize:' 는 3초)
# 요약할 문장
text3 = """aaa The Inflation Reduction Act lowers prescription drug costs,
health care costs, and energy costs. It's the most aggressive action on tackling
the climate crisis in American history, which will lift up American workers and
create good-paying, union jobs across the country. It'll lower the deficit and
ask the ultra-wealthy and corporations to pay their fair share. And no one making
under $400,000 per year will pay a penny more in taxes."""
result3 = summarization(text3)
result3
Your max_length is set to 200, but your input_length is only 105. Since this is a summarization task, where outputs shorter than the input are typically wanted, you might consider decreasing max_length manually, e.g. summarizer('...', max_length=52)
[{'summary_text': "the Inflation Reduction Act lowers prescription drug costs, health care costs, and energy costs . it's the most aggressive action on tackling the climate crisis in American history . no one making under $400,000 per year will pay a penny more in taxes."}]
→ 4초 걸림
T5 모델을 본인이 가진 데이터로 학습할 때, 만약 prefix를 "summarize long sentences"로 설정하여 학습했다면, 평가(추론) 시에는 반드시 그 정확한 prefix를 사용해야 가장 좋은 성능을 기대할 수 있습니다.
그 이유는 다음과 같습니다:
따라서, 본인의 데이터로 "summarize long sentences" prefix를 사용하여 학습했다면, 평가할 때도 가능하면 반드시 그 동일한 prefix를 입력에 포함시키는 것이 권장됩니다. 다른 prefix를 쓰면 작동할 수는 있으나 성능이 떨어지거나 원하는 작업 출력이 안 나올 위험이 있습니다.
요약:
| 상황 | Prefix 사용 권장 방법 | 결과 |
|---|---|---|
| 학습과 평가 모두 동일 prefix 사용 | "summarize long sentences" | 최적 성능 발휘 |
| 평가 시 다른 prefix 사용 | 다른 prefix 사용 가능하나 권장하지 않음 | 성능 저하, 작업 혼동 가능성 있음 |
즉, "무조건" 동일 prefix를 써야 하는 것은 아니지만, 최선의 성능과 안정적인 결과를 위해서는 학습 때 사용한 prefix를 그대로 쓰는 것이 좋습니다.
Cloud Quest: Cloud Practitioner