ํ ์คํธ ๋ถ๋ฅ๋ฅผ ์งํํ๋ ์ค, ์น๊ตฌ์๊ฒ ์ข์ ์์ด๋์ด๋ฅผ ํ๋ ๋ฐ์๋ค
๋จผ์ ๊ต์ฐจ๊ฒ์ฆ์ ๋๋ฆฌ๋ฉด์ ๊ฐ ํด๋๋น ๊ฒ์ฆ์ ์ ๋ํด ์์ธกํ ๋ผ๋ฒจ๊ณผ ํ๋ฅ ๊ฐ์ ์ ์ฅํ๋ฉด ๋ฒ ์ด์ค๋ผ์ธ์ด ์๋ณธ ์ ์ฒด ๋ฐ์ดํฐ์ ์ ๋ํด ๋ฌธ์ฅ๋ง๋ค ์ด๋ค ๋ต์ ์ผ๋ง์ ํ๋ฅ ๋ก ์์ธกํ๋์ง๋ฅผ ์ ์ ์๊ฒ ๋๋ค
๊ทธ๋ฆฌ๊ณ ๋ชป๋ง์ถ ๋ฌธ์ฅ๋ค์ ๋ํด์ ์ด ํ๋ฅ ๊ฐ p๋ฅผ 1-p ํํ๋ก ๋ณํํด์ฃผ๋ฉด ์ผ๋ง๋ ์ ๋ต์ ํ์ ํ์ง ๋ชปํ๋์ง, ์ฆ ์ค๋ต์ ๋ํด ์ผ๋ง๋ ํ์ ํ๋์ง๋ฅผ ๋ํ๋ธ๋ค
์ด๋ฅผ ํตํด ์ป์ ์ ์๋ ๊ธฐ๋ํจ๊ณผ๋ก๋ ์๊ธฐ ์ ๋ต ํด๋์ค์ ์ ๋งคํ ํ๋ฅ ๋ก ํ๋จํ๊ฑฐ๋ ์์ ๋ฎ์ ํ๋ฅ ๋ก ์๋ฒฝํ๊ฒ ์ ๋ต์ ํ๋ฆฌ๋ ์ผ์ด์ค๋ค์ ์์งํ์ฌ ์ฌ๊ธฐ์ ๋ํด ํธ๋ค๋งํ๋, ๋ชจ๋ธ์ด ๋ฐ์ดํฐ๋ฅผ ๋ณผ ๋ ์ฝ์ ์ ํ์ ํ์ฌ ์ด๋ฅผ ์ง์ ์ ์ผ๋ก ๊ฐ์ ํ ์ ์๋ ๋ฐฉ๋ฒ์ด์ง ์์๊น ๋ผ๋ ์๊ฐ์ด ๋ค์๋ค
์ด๋ฌํ ๊ฐ๋ ์ ์ฐพ์๋ณด๋๊น Hard Nagative Mining, ๋ชจ๋ธ์ด ์ค๋ต์ ๋ธ, ์์ธก์ ์๋ชปํ ์ด๋ ค์ด ์ํ์ ์ถ์ถํ๋ ๋ฐฉ๋ฒ์ด๋ผ ํ๋ค
์ฃผ๋ก ๋น์ ์ชฝ์ ์ฌ์ฉ๋๋ ๊ฐ๋ ์ด๊ธด ํ์ง๋ง ์ด๋ฌํ ๊ฐ๋ ์์ฒด๋ ํ ์คํธ, ๋จธ์ ๋ฌ๋ ๋ฑ์์๋ ์ ์ฌํ๊ฒ ํ์ฉํ ์ ์์ด๋ณด์ธ๋ค
์ฌ์ฉํ๋ ํ ์คํธ ๋ฐ์ดํฐ์ ์ ๋ถ๊ท ํ์ด ์กด์ฌํ๊ธฐ์ ๋จ์ํ ํ ์คํธ ์ฆ๊ฐ๋ง์ ์ ์ฉํ๋ ค ํ์ผ๋ ์ฌ๊ธฐ์ ์ ๋ชฉ์์ผ ๋ชจ๋ธ์ด ์ ์์ธกํ์ง ๋ชปํ๋ ์ทจ์ฝํ ๋ฐ์ดํฐ์ ๋ํด ์ฆ๊ฐ์ ํด์ฃผ๋ ๊ฒ, Targeted Augmentation ์ผ๋ก ์ ์๋ฏธํ ์ฑ๋ฅ ํฅ์์ด ์๋์ง ์คํํด๋ณด๋ ค ํ๋ค
https://stydy-sturdy.tistory.com/27
[๊ฐ์ฒด ํ์ง] Hard Negative Mining ์ด๋?
Object Detection task์์ bounding box๋ฅผ ๋ฝ์ผ๋ฉด ์์ฒ ๊ฐ๋ฅผ ๋ฝ๊ฒ ๋๋ค. ์์ฒ ๊ฐ์ Bounding Box ์์ ์ฐ๋ฆฌ๊ฐ ์ฐพ๊ณ ์ ํ๋ ๋ฌผ์ฒด ํน์ ๊ฐ์ฒด๊ฐ ์๋ ๋ฐ์ค๊ฐ ํ๊ท ์์ญ ๊ฐ ์๋ค๊ณ ๊ฐ์ ํ๋ฉด ๋๋จธ์ง ๋ฐ์ค๋ค ์ฆ, ๋ฌผ
stydy-sturdy.tistory.com
OHEM: Training Region-based Object Detectors with Online Hard Example Mining
์ด๋ฒ ํฌ์คํ ์์๋ OHEM(Online Hard Example Mining)๋ ผ๋ฌธ์ ๋ฆฌ๋ทฐํด๋ณด๊ฒ ๋ค.์ผ๋ฐ์ ์ผ๋ก object detection์์ ๋ฐฐ๊ฒฝ์์ญ์ ํด๋นํ๋ region proposals์ ์๊ฐ ๋ ๋ง์ ํด๋์ค ๋ถ๊ท ํ์ด ๋ฐ์ํ๊ณ , ์ด ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๊ธฐ
velog.io
1. ํต์ฌ ๊ฐ๋
- ๋ชจ๋ธ์ด ์ฝํ ๋ฐ์ดํฐ (=hard sample) ๋ฅผ ์ฐพ์๋ธ๋ค.
→ ์ ๋ต ํด๋์ค ํ๋ฅ (p_true)์ด ๋ฎ์ ์ํ = ๋ชจ๋ธ์ด ๋ถํ์คํ๊ฑฐ๋ ํ๋ฆฐ ์ํ - ๊ทธ ์ํ๋ค์ ์ง์ค ์ฆ๊ฐํ๋ค.
→ Synonym ๊ต์ฒด, ๋ฒ์ญ ํ ๋ณต์, LLM์ ์ด์ฉํ ๋ฌธ์ฅ ์ฌ์์ฑ ๋ฑ์ผ๋ก ๋ค์ํ - ๋ค์ ํ์ต์์ผ ์ฑ๋ฅ ๋น๊ต
→ ์ ์ฒด ์ฆ๊ฐ๋ณด๋ค ํจ์จ์ ์ด๊ณ , ๋ชจ๋ธ ์ฝ์ ์ ๋ณด์ํจ
2. ๊ตฌํ ์ ์ฐจ
Step 1. ๊ต์ฐจ ๊ฒ์ฆ ์์ธก ๊ฒฐ๊ณผ ์ป๊ธฐ (Out-of-Fold Prediction)
- ๋ชจ๋ธ ํ์ต ํ, ๊ต์ฐจ๊ฒ์ฆ์ ํตํด ๊ฐ ์ํ์ ์์ธก ํ๋ฅ ์ ์ป๋๋ค.
- ์ด๋ฅผ ํตํด “ํด๋น ์ํ์ ํ์ต์ ์ฌ์ฉํ์ง ์์ fold”์์์ ์์ธก ๊ฒฐ๊ณผ๋ฅผ ํ๋ณด → ๊ณผ์ ํฉ ๋ฐฉ์ง
์๋๋ ํ๊น ํ์ด์ค์ ๋ชจ๋ธ ๋ฐ ํ ํฌ๋์ด์ ๋ฅผ ์ฌ์ฉํ์ฌ ํด๋๋ง๋ค ๋ชจ๋ธ์ ์๋ก ๋ถ๋ฌ์์ ํ์ต์ํค๊ณ ๊ฒ์ฆ์ ์ ๋ํ ์์ธก ๋ผ๋ฒจ๊ณผ ํ๋ฅ ์ ์ ์ฅํ๋ค
๊ต์ฐจ๊ฒ์ฆ์๋ ์๊ฐ์ด ์ค๋ ๊ฑธ๋ฆฌ๊ธฐ ๋๋ฌธ์ ๋งค๋ฒ ๋๋ฆด ์ ์์ผ๋ฏ๋ก csv๋ก ์ ์ฅํด๋๋ ๊ฒ์ด ์ข๋ค
# ==========================================================
# 3. ๋ชจ๋ธ ๋ฐ ํ ํฌ๋์ด์ ์ค์
# ==========================================================
MODEL_NAME = "kykim/bert-kor-base" # ๋ํ ํ์ฉ ๋ชจ๋ธ ์ค ํ๋
tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
# ==========================================================
# 4. K-Fold ์ค์
# ==========================================================
NUM_FOLDS = 10
RANDOM_STATE = 42
skf = StratifiedKFold(n_splits=NUM_FOLDS, shuffle=True, random_state=RANDOM_STATE)
oof_preds = np.zeros(len(df), dtype=int)
oof_probs = np.zeros((len(df), 4)) # ๊ฐ์ ํด๋์ค 4๊ฐ
fold_accuracies = []
# ==========================================================
# 5. Fold๋ณ ํ์ต ๋ฐ OOF ์์ธก
# ==========================================================
for fold, (train_idx, val_idx) in enumerate(skf.split(X, y)):
print(f"\n===== Fold {fold+1} / {NUM_FOLDS} =====")
X_train, X_val = [X[i] for i in train_idx], [X[i] for i in val_idx]
y_train, y_val = [y[i] for i in train_idx], [y[i] for i in val_idx]
train_dataset = ReviewDataset(X_train, y_train, tokenizer)
val_dataset = ReviewDataset(X_val, y_val, tokenizer)
test_dataset = ReviewDataset(test_texts, labels=None, tokenizer=tokenizer)
model = AutoModelForSequenceClassification.from_pretrained(MODEL_NAME, num_labels=4)
training_args = TrainingArguments(
output_dir=f"./results/fold_{fold+1}",
eval_strategy="epoch",
save_strategy="epoch",
load_best_model_at_end=True,
metric_for_best_model="accuracy",
greater_is_better=True,
save_total_limit=1,
per_device_train_batch_size=128,
per_device_eval_batch_size=256,
num_train_epochs=3,
learning_rate=5e-5,
warmup_steps=500,
weight_decay=0.05,
seed=RANDOM_STATE,
fp16=torch.cuda.is_available(),
logging_strategy="epoch", # ← ๋ก๊ทธ ๋จ๊ธฐ๋ ์ฃผ๊ธฐ ์ง์
report_to="none", # ← wandb๋ tensorboard๋ก ์ ๋ณด๋ผ ๊ฒฝ์ฐ
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset=val_dataset,
data_collator=DataCollatorWithPadding(tokenizer=tokenizer),
compute_metrics=compute_metrics,
)
trainer.train()
# ๊ฒ์ฆ ๋ฐ์ดํฐ ์์ธก
preds_output = trainer.predict(val_dataset)
preds = np.argmax(preds_output.predictions, axis=1)
probs = torch.nn.functional.softmax(torch.tensor(preds_output.predictions), dim=1).numpy()
# OOF ๊ฒฐ๊ณผ ์ ์ฅ
oof_preds[val_idx] = preds
oof_probs[val_idx] = probs
acc = accuracy_score(y_val, preds)
fold_accuracies.append(acc)
print(f"Fold {fold+1} Accuracy: {acc:.4f}")
# ํ
์คํธ์
์์ธก
print(f"→ Fold {fold+1} Test inference ์ค...")
test_output = trainer.predict(test_dataset)
test_probs = torch.nn.functional.softmax(torch.tensor(test_output.predictions), dim=1).numpy()
test_preds = np.argmax(test_probs, axis=1)
# CSV๋ก ์ ์ฅ
test_pred_df = pd.DataFrame({
"ID": test_ids,
"pred": test_preds
})
test_prob_df = pd.DataFrame(test_probs, columns=[f"class_{i}_prob" for i in range(4)])
test_prob_df.insert(0, "ID", test_ids)
test_pred_df.to_csv(f"test_fold_{fold+1}_predictions.csv", index=False)
test_prob_df.to_csv(f"test_fold_{fold+1}_probabilities.csv", index=False)
print(f"โ
Fold {fold+1} ํ
์คํธ ์์ธก ๊ฒฐ๊ณผ ์ ์ฅ ์๋ฃ.")
# ==========================================================
# 6. OOF ์ฑ๋ฅ ํ์ธ
# ==========================================================
# ์๋ณธ ๋ฐ์ดํฐ์ ์์ธก์ ๋น๊ตํ์ฌ ํด๋์ ์ ์ฒด์ ์ธ ์ฑ๋ฅ์ ์ฒดํฌํจ
oof_acc = accuracy_score(y, oof_preds)
print(f"\nโ
Overall OOF Accuracy: {oof_acc:.4f}")
print(f"Fold Accuracies: {fold_accuracies}")
โ
Overall OOF Accuracy: 0.8605
Fold Accuracies: [0.8553166763509588, 0.860945671121441, 0.8615630447414294, 0.8647951772225451, 0.8609819872167345, 0.860691458454387, 0.8604009296920395, 0.8593789722171782, 0.858979480660977, 0.8622480479389868]
โ
OOF ๊ฒฐ๊ณผ ํฌํจํ CSV ์ ์ฅ ์๋ฃ
์ด 38404๊ฐ์ hard case ํ์ง๋จ
- ์๋ ์๋ณธ ๋ฐ์ดํฐ์ ์์ธก์ ๋น๊ตํด์ Accuracy๋ฅผ ํ์ธํ๋ ๋ถ๋ถ๋ ์๋ค. ์ด๋ฒ 10ํด๋ ์งํํ์ ๋ Accuracy๋ 0.86 ์ ๋
- ์ด๊ฑด ๋ฒ ์ด์ค๋ผ์ธ์ด ์๋ณธ ๋ฐ์ดํฐ ์ ์ฒด์ ๋ํด ์ด๋ ์ ๋์ ์ ํ๋๋ก ์์ธกํ๋์ง ์ฐธ๊ณ ํ๋๋ฐ ์ฌ์ฉํ๋ฉด ์ข์ ๊ฒ ๊ฐ๋ค
Step 2. Hard Sample ์๋ณ ๋ฐ ๋ถ์
์๊ธฐ ์ ๋ต ํด๋์ค์ ํ๋ฅ (p_true)์ด ๋ฎ์์๋ก ๋ชจ๋ธ์ด ๋ถํ์คํ ์ํ์ด๋ค.
์ด๋ 1 - p_true ๊ฐ์ด ํด์๋ก “์ด๋ ค์ด ์ํ”.
# ==========================================================
# 7. Hard Case ํ์
# ==========================================================
df["oof_pred"] = oof_preds
df["is_correct"] = (df["label"] == df["oof_pred"])
df["oof_confidence"] = oof_probs.max(axis=1)
df.to_csv("./oof_results_with_confidence.csv", index=False)
print("โ
OOF ๊ฒฐ๊ณผ ํฌํจํ CSV ์ ์ฅ ์๋ฃ")
# consistently ํ๋ฆฐ ์ํ๋ง ํํฐ๋ง
hard_cases = df[~df["is_correct"]].sort_values("oof_confidence")
print(f"\n์ด {len(hard_cases)}๊ฐ์ hard case ํ์ง๋จ")
hard_cases[["ID", "review_normalized", "label", "oof_pred", "oof_confidence"]].head(10)
- Hard Case ํ์์ ๊ฒฝ์ฐ ํ๋ฅ ์ด ๋๊ณ ๋ฎ์๊ณผ ์๊ด์์ด ๋ต์ ํ๋ฆฐ ๊ฒ์ ๋ํด์๋ง ์ ์ฉํ์ฌ ์ ์ฒด ์ค ๋ช ๊ฐ๋ฅผ ํ๋ ธ๋์ง๋ฅผ ์ฒดํฌํ๋ค
p_true = oof_probs[np.arange(len(df)), df["label"].values]
df["p_true"] = p_true
df["hardness"] = 1 - df["p_true"]
p_true = oof_probs[np.arange(len(df)), df["label"].values]
- ์ด ํ ์ค์ ๊ฐ ์ํ์ ๋ํด, ์ ๋ต ํด๋์ค์ ํด๋นํ๋ ํ๋ฅ ๊ฐ์ ๋ฝ์๋ธ ๊ฒ
- oof_probs ์๋ ๊ต์ฐจ ๊ฒ์ฆ์ ํ๋ฉด์ ๊ฒ์ฆ์ ๋ผ๋ฒจ๋ง๋ค์ ์์ธก๊ฐ์ด ๋ด๊ฒจ์์
- oof_probs[i] → ์ํ i์ ๋ํ ๋ชจ๋ธ์ softmax ํ๋ฅ ์์ธก ๋ฒกํฐ (์: [0.1, 0.7, 0.1, 0.1])
- df["label"].values[i] → ์ํ i์ ์ค์ ์ ๋ต ํด๋์ค (์: 1)
- ๋ฐ๋ผ์ oof_probs[i, df["label"][i]] → ๋ชจ๋ธ์ด ์ ๋ต ํด๋์ค์ ํ ๋นํ ํ๋ฅ (์ฌ๊ธฐ์๋ 0.7)
- ๋ง์ฝ ๋ชจ๋ธ์ด ์๋ชป ์์ธกํ๋ค๋ฉด ์ด ํ๋ฅ ์ด ๋งค์ฐ ๋ฎ๊ฒ ๋์ค๊ธฐ๋ ํจ
p_true
- ๋ชจ๋ธ์ด ๊ฐ ์ํ์ “์ง์ง ์ ๋ต ํด๋์ค”์ ์ผ๋ง๋ ํ์ ์ ๊ฐ์ก๋์ง๋ฅผ ๋ํ๋ด๋ ๊ฐ
- ๊ฐ์ ๋ฒ์๋ 0~1์ด๋ฉฐ, 1์ ๊ฐ๊น์ธ์๋ก ๋ชจ๋ธ์ด ์ ๋ต์ ๊ฐํ๊ฒ ํ์ ํ๋ค๋ ๋ป
hardness
- ๋ชจ๋ธ์ด ์ ๋ต์ ๋ํด ์ผ๋ง๋ “์ด๋ ค์ํ๋๊ฐ”
- ๋ชจ๋ธ์ด ์ ๋ต ํ๋ฅ (p_true) ์ ๋๊ฒ ์์ธกํ๋ฉด → p_true ↑ → hardness ↓ (์ฌ์ด ์ํ)
- ๋ชจ๋ธ์ด ์ ๋ต ํ๋ฅ (p_true) ์ ๋ฎ๊ฒ ์์ธกํ๋ฉด → p_true ↓ → hardness ↑ (์ด๋ ค์ด ์ํ)
- 0.9 → ๋งค์ฐ ์ด๋ ค์ด ์ํ (๊ฑฐ์ ํ๋ฆด ๋ปํ๊ฑฐ๋ ์ค์ ๋ก ํ๋ ธ์ ํ๋ฅ ์ด ๋์)
- 0.5 → ์ค๊ฐ ์ ๋๋ก ๋ถํ์ค
- 0.1 → ๋งค์ฐ ์ฌ์ด ์ํ (๊ฑฐ์ ํ์คํ๊ฒ ๋ง์ถค)
- p_true, 1-p_ture ๊ฐ์ ๊ณ์ฐ ํ ๋ฐ์ดํฐ ํ๋ ์์ ์ถ๊ฐ
- ์ต์ข ์ ์ผ๋ก ์๋์ ๊ฐ์ ํํ๋ก ๊ธฐ์กด์ ๋ฐ์ดํฐ์ ์ ์์ธก ๋ผ๋ฒจ, ์ ๋ต ์ฌ๋ถ, ํ๋ฅ ๊ฐ๋ค๊ณผ ์์ธก์ ์ด๋ ค์ ๋ฑ์ด ์ถ๊ฐ๋์๋ค

- ์ด๋, ์๋ฅผ ๋ค์ด p_true < 0.4 ๋๋ ์์ 20% ์ ๋ ๋ฑ ์ด๋ ์ ๋๊น์ง “hard” ์ํ๋ก ๊ฐ์ฃผํ ๊ฒ์ธ์ง ๊ธฐ์ค์ ์ ํด์ผํ๋ค
- ์ด Hard Sample์ ๋จ์ ์์งํ๋ ์ ์์ ๋๋์ ์๋๋ค
- ์ด๋ค ํด๋์ค ๋ถํฌ๋ฅผ ๋๊ณ ์๋์ง, ์ด๋ค ๋ฌธ์ฅ, ๋จ์ด๋ค๋ก ์ด๋ฃจ์ด์ ธ ์๋์ง ๋ถ์์ ํด์ฃผ๋ ๊ฒ์ด ํ์ํ๋ค
# ์์ 20% hard samples
threshold = df["hardness"].quantile(0.8)
hard_top = df[df["hardness"] >= threshold].sort_values("hardness", ascending=False)
hard_top.head(20)
threshold = df["hardness"].quantile(0.8)
- hardness ๋ถํฌ์์ ์์ 20% ๊ฒฝ๊ณ๊ฐ (80๋ฒ์งธ ๋ฐฑ๋ถ์์) ๋ฅผ ์๋ฏธ
- ์ฆ, hardness ๊ฐ์ด ์ ์ฒด ์ค ์์ 20% ์์ ๋ค์ด๊ฐ๋ ๊ธฐ์ค์ ์ ์ฐพ๋ ๊ฒ

- ํ๋ฆฌ๊ฒ๋ง ์์ธกํ ํ๋์ผ์ด์ค๋ ์ด 38404๊ฐ, hardness๋ฅผ ๋์ ์์ผ๋ก ์์ 20%๋ ์ด 55072๊ฐ์ ๋ฌํ๋ค
- ์ ์ฒด ๋ฐ์ดํฐ์ ์ด ๋๋ต 27๋ง๊ฐ์์ ์๊ฐํ๋ฉด ๊ฝค ์ ์ง์์ ์ํ๋ค์ ์ ๋๋ก ๋ง์ถ์ง ๋ชปํ๋ค๊ณ ๋ณผ ์ ์๋ค
- ๊ทธ ์ค ๊ฐ์ฅ hardness๊ฐ ๋์ ์๋ถํฐ 20๊ฐ๋ฅผ ๋ถ๋ฌ์๋ค
- ์ฒซ๋ฒ์งธ ๋ฌธ์ฅ์ ์ ๋ต์ด 0๋ฒ์ธ๋ฐ 2๋ฒ์ผ๋ก ์๋ชป ์์ธกํ์๋ค
- ๊ทธ๋ฆฌ๊ณ 2๋ฒ์ผ๋ก ์๋ชป ์์ธกํ ํ๋ฅ ์ ๋ฌด๋ ค 0.99... ์ ๋ต์ ์์ธกํ ํ๋ฅ ์ 0.000036์ผ๋ก ๋งค์ฐ๋งค์ฐ๋งค์ฐ ๋ฎ๊ฒ ์์ธกํ๊ธฐ์ ๋ชจ๋ธ์ด ์ด๋ ต๊ฒ ๋ณด์์์ ํ์ธํ ์ ์๋ค
๊ทธ๋ผ ๋ชป ๋ง์ถ๋ ์์ 20% ํ๋ ์ํ์์ ํด๋์ค๋ณ ๊ฐ์, ๋ถํฌ๋??

| label | count |
| 0 | 12341 |
| 1 | 12460 |
| 2 | 17771 |
| 3 | 12500 |
Step 3. Hard Sample ๊ธฐ๋ฐ ์ฆ๊ฐ
ํ ์คํธ ์ฆ๊ฐ์๋ ์ฌ๋ฌ ๊ฐ์ง ๋ฐฉ๋ฒ๋ค์ด ์กด์ฌํ๋ค
๊ทธ ์ค ์ผ๋ถ ์ฌ์ฉํ ๋ฐฉ๋ฒ๋ค๋ง ๊ฐ๋จํ๊ฒ ์ ๋ฆฌํ์๋ค
(A) ๋ฒ์ญ ๊ธฐ๋ฐ ์ฆ๊ฐ (Back-translation)
- ํ๊ตญ์ด → ์์ด → ํ๊ตญ์ด ๋ฒ์ญ
- ๋ฌธ์ฅ ๊ตฌ์กฐ์ ์ผ๋ถ ๋จ์ด ๋ณ๊ฒฝ → ์๋ฏธ ์ ์ง
- ํ๊น ํ์ด์ค์ ์๋ ๋ค์ํ ๋ฒ์ญ ๋ชจ๋ธ๋ค ํ์ฉ ๊ฐ๋ฅ
- ์ํ์ ๊ธธ์ด๊ฐ ๋๋ฌด ์งง์ ์ผ์ด์ค๋ ๋ฒ์ญ์ด ์๋๊ณ ๋ฐ์ดํฐ ํ์ง์ด ๋ ์ ํ๋ ์ ์๊ธฐ์ ํํฐ๋งํด์ฃผ๋ ๊ฒ์ด ์ข์ ๊ฒ์ผ๋ก ๋ณด์ธ๋ค
- ex) 2.3 / ์๊ณผ ๊ฒ / ์ํด ๋ฑ๋ฑ..
ํํฐ๋ง
(1) ์ํ ๊ธธ์ด ๊ธฐ์ค
- ์งง์ ๋ฌธ์ฅ, ํ๋ ๋จ์ด๋ง ์๋ ๋ฆฌ๋ทฐ๋ Back-Translation ํจ๊ณผ ๊ฑฐ์ ์์ → ์๋ฏธ ํผ์ ๊ฐ๋ฅ
- ์: ๊ธธ์ด 5~10์ ์ดํ(ํ ํฐ ๊ธฐ์ค) ์ํ์ ์ฆ๊ฐ ์ ์ธ
min_len = 10 # ํ ํฐ ์ ๊ธฐ์ค
hard_top_filtered = hard_top[hard_top['review_normalized'].str.len() >= min_len]
(2) ๋ชจ๋ธ ํ์ ๋(confidence) ๊ธฐ์ค
- ์ด๋ฏธ ๋ง์ถ ์ํ์ธ๋ฐ confidence๊ฐ ๋๋ค๋ฉด ์ฆ๊ฐ ํ์ ์์
- ์: confidence < 0.7 → ์ฆ๊ฐ ํ๋ณด
hard_top_filtered = hard_top_filtered[hard_top_filtered['oof_confidence'] < 0.7]
(3) ๋ชจ๋ธ ์์ธก ์ด๋ ค์ ๊ธฐ์ค
- ์์ธก์ด ์ด๋ ค์ด ๊ธฐ์ค๊ฐ, hardness๊ฐ ์ผ์ ๊ธฐ์ค๋ณด๋ค ๋์ ๋ ๋ ํ์ต์ ์ ํ ์ ์๊ฒ๋ ์ ์ฌํ ๋ฐ์ดํฐ๋ฅผ ์ถ๊ฐํด์ฃผ๋ฉด ์ ํจํ ๊ฒ์ผ๋ก ํ๋จ
- ์: hardness > 0.6 → ์ฆ๊ฐ ํ๋ณด
hard_top_filtered = hard_top_filtered[hard_top_filtered['hardness'] > 0.6]
(4) ํด๋์ค ๋น์จ ๊ธฐ์ค
- ์์ ํด๋์ค ์ฐ์ ์ฆ๊ฐ: 1(์ฝํ ๋ถ์ )๊ณผ 3(๊ฐํ ๊ธ์ )
hard_top_filtered = hard_top_filtered[hard_top_filtered['label'].isin([1, 3])]
์๋๋ ํ๊ตญ์ด -> ์์ด -> ํ๊ตญ์ด๋ก ๋ฒ์ญ์ ๊ฑฐ์ณ ์ฆ๊ฐ์ํค๋ ์ํ ์ฝ๋์ด๋ค
from transformers import MBartForConditionalGeneration, MBart50TokenizerFast
# ๋ชจ๋ธ ๋ฐ ํ ํฌ๋์ด์ ๋ก๋
mbart_model_name = "facebook/mbart-large-50-many-to-many-mmt"
model = MBartForConditionalGeneration.from_pretrained(mbart_model_name)
tokenizer = MBart50TokenizerFast.from_pretrained(mbart_model_name)
import torch
def mbart_translate(texts, src_lang="ko_KR", tgt_lang="en_XX"):
translated_texts = []
for text in texts:
tokenizer.src_lang = src_lang
encoded = tokenizer(text, return_tensors="pt", truncation=True, padding=True)
generated_tokens = model.generate(**encoded, forced_bos_token_id=tokenizer.lang_code_to_id[tgt_lang])
translated_texts.append(tokenizer.decode(generated_tokens[0], skip_special_tokens=True))
return translated_texts
# ์์ ํ๋ ์ํ
hard_samples = [
"์ด ๋ฌธ์ฅ์ ๊ฐ์ ๋ถ์ ๋ชจ๋ธ์ ํ
์คํธํ๊ธฐ ์ํ ์์์
๋๋ค.",
"๋ชจ๋ธ์ด ์ ๋ง์ถ์ง ๋ชปํ๋ ํ๋ ์ผ์ด์ค๋ฅผ ๋ถ์ํด์ผ ํฉ๋๋ค."
]
# ํ๊ตญ์ด → ์์ด
ko2en = mbart_translate(hard_samples, src_lang="ko_KR", tgt_lang="en_XX")
print("ํ๊ตญ์ด → ์์ด:", ko2en)
# ์์ด → ํ๊ตญ์ด (Back-translation)
back_translated = mbart_translate(ko2en, src_lang="en_XX", tgt_lang="ko_KR")
print("Back-translation (ํ๊ตญ์ด → ์์ด → ํ๊ตญ์ด):", back_translated)
ํ๊ตญ์ด → ์์ด:
['This sentence is an example of testing an emotion-analysis model.',
"You have to analyze the hard case where the model doesn't fit."]
Back-translation (ํ๊ตญ์ด → ์์ด → ํ๊ตญ์ด):
['์ด ๋ฌธ์ฅ์ ๊ฐ์ ๋ถ์ ๋ชจ๋ธ์ ์ํํ๋ ์์
๋๋ค.',
'๋ชจ๋ธ์ด ๋ง์ง ์๋ ์ด๋ ค์ด ๊ฒฝ์ฐ๋ฅผ ๋ถ์ํด์ผ ํฉ๋๋ค.']
- ๋ณด๋ค์ํผ ์ ์ฌํ ๋ฌธ์ฅ์ด ์ถ๋ ฅ์ผ๋ก ๋์จ ๊ฒ์ ํ์ธํ ์ ์๋ค
- ์์ ์์ ์ฝ๋๋ ์ผ๋ง ์๋๋ ๋ฌธ์ฅ๋ค์ด๋ผ์ ๊ฐ๋จํ๊ฒ for๋ฌธ ๋๋ฆฌ์ง๋ง ์ค์ ๋ก๋ ์ฒ์์ ๋ง๋จ์์ ๋ฌธ์ฅ๋ค์ ๋๋ ค์ผํ๊ธฐ ๋๋ฌธ์ ์ ๋ ๊ฒ ์ฐ๋ฉด ์๋๋ค..
ํํฐ๋ง ๋ฐ Back-translation ์ ์ฉ ์ฝ๋
from transformers import MBartForConditionalGeneration, MBart50Tokenizer
import torch
from torch.utils.data import Dataset, DataLoader
from tqdm import tqdm
import pandas as pd
# ==========================================================
# 1. ๋ชจ๋ธ ๋ฐ ํ ํฌ๋์ด์ ๋ก๋
# ==========================================================
mbart_model_name = "facebook/mbart-large-50-many-to-many-mmt"
model = MBartForConditionalGeneration.from_pretrained(mbart_model_name, torch_dtype=torch.float16)
tokenizer = MBart50Tokenizer.from_pretrained(mbart_model_name)
model = torch.compile(model)
device = "cuda" if torch.cuda.is_available() else "cpu"
model.to(device)
# ==========================================================
# 2. Dataset ์ ์
# ==========================================================
class TextDataset(Dataset):
def __init__(self, texts):
self.texts = texts
def __len__(self):
return len(self.texts)
def __getitem__(self, idx):
return self.texts[idx]
# ==========================================================
# 3. Collate ํจ์ (๋ฐฐ์น ๋จ์ ํ ํฌ๋์ด์ง)
# ==========================================================
def collate_fn(batch_texts, src_lang, tokenizer, device):
tokenizer.src_lang = src_lang
encoded = tokenizer(
batch_texts,
return_tensors="pt",
padding=True,
truncation=True,
max_length=128
)
encoded = {k: v.to(device) for k, v in encoded.items()}
return encoded
# ==========================================================
# 4. DataLoader ๊ธฐ๋ฐ ๋ฒ์ญ ํจ์
# ==========================================================
def mbart_translate_dataloader(texts, src_lang="ko_KR", tgt_lang="en_XX", batch_size=32, num_workers=2):
dataset = TextDataset(texts)
dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=False, num_workers=num_workers)
translated_texts = []
for batch_texts in tqdm(dataloader, desc=f"Translating {src_lang} → {tgt_lang}"):
encoded = collate_fn(batch_texts, src_lang, tokenizer, device)
with torch.no_grad():
generated_tokens = model.generate(
**encoded,
forced_bos_token_id=tokenizer.lang_code_to_id[tgt_lang],
max_length=256
)
decoded = [tokenizer.decode(t, skip_special_tokens=True) for t in generated_tokens]
translated_texts.extend(decoded)
return translated_texts
# ==========================================================
# 5. ํํฐ๋ง + ์ฆ๊ฐ ํจ์ (DataLoader ๊ธฐ๋ฐ)
# ==========================================================
def augment_hard_samples_filtered_dataloader(df_hard, n_aug=1,
min_len=10, max_confidence=0.7,
target_labels=[1,3], top_hardness=0.7,
batch_size=32, num_workers=2):
"""
df_hard: ํ๋ ์ํ ๋ฐ์ดํฐํ๋ ์ (review_normalized, label, oof_confidence, hardness ํฌํจ)
n_aug: ๊ฐ ์ํ๋น ์์ฑํ ์ฆ๊ฐ ์
"""
# ํํฐ๋ง
df_filtered = df_hard[
(df_hard['label'].isin(target_labels)) &
(df_hard['oof_confidence'] <= max_confidence) &
(df_hard['review_normalized'].str.len() >= min_len)
]
threshold = df_filtered['hardness'].quantile(top_hardness)
df_filtered = df_filtered[df_filtered['hardness'] >= threshold]
print(f"์ฆ๊ฐ ๋์ ์ํ ์: {len(df_filtered)}")
aug_texts = []
aug_labels = []
for _ in tqdm(range(n_aug), desc="Augmentation rounds"):
ko_texts = df_filtered['review_normalized'].tolist()
# ํ๊ตญ์ด → ์์ด → ํ๊ตญ์ด
en_texts = mbart_translate_dataloader(ko_texts, src_lang="ko_KR", tgt_lang="en_XX",
batch_size=batch_size, num_workers=num_workers)
back_texts = mbart_translate_dataloader(en_texts, src_lang="en_XX", tgt_lang="ko_KR",
batch_size=batch_size, num_workers=num_workers)
aug_texts.extend(back_texts)
aug_labels.extend(df_filtered['label'].tolist())
return aug_texts, aug_labels
# ==========================================================
# 6. ์์ ์ฌ์ฉ
# ==========================================================
# hard_top_filtered: ๊ธฐ์กด ํํฐ๋ง ํ ํ๋ ์ํ ๋ฐ์ดํฐํ๋ ์
aug_texts, aug_labels = augment_hard_samples_filtered_dataloader(
df_hard=hard_top,
n_aug=1,
min_len=10,
max_confidence=0.7,
target_labels=[1,3],
top_hardness=0.7,
batch_size=128,
num_workers=8 # CPU ์ฝ์ด ์ฌ์ ์์ผ๋ฉด 4~8 ์ถ์ฒ
)
print(f"์ฆ๊ฐ ์๋ฃ ์ํ ์: {len(aug_texts)}")
print("์์ ์ฆ๊ฐ ๋ฌธ์ฅ:", aug_texts[:5])
์ฆ๊ฐ ๋์ ์ํ ์: 3889
Translating ko_KR → en_XX: 100%|โโโโโโโโโโ| 31/31 [15:53<00:00, 30.76s/it]
Translating en_XX → ko_KR: 100%|โโโโโโโโโโ| 31/31 [08:43<00:00, 16.87s/it]
Augmentation rounds: 100%|โโโโโโโโโโ| 1/1 [24:36<00:00, 1476.75s/it]
์ฆ๊ฐ ์๋ฃ ์ํ ์: 3889
์์ ์ฆ๊ฐ ๋ฌธ์ฅ:
['์์๋์? plot ์ ๋ณด์ค ์ ์์ฃ . ๊ทธ๋ ๋ฉ์ฒญ์ด๊ฐ ์๋๋ผ, ์๋ค์ด ์๊ณ , ์กฐ์นด๊ฐ ์๊ณ , ์๊ธฐ๊ฐ ์์ต๋๋ค.',
'๋ชจ๋ ๊ฒ์ด ์ข์๊ณ , ๋๋ ์ข์์ต๋๋ค.',
'๋ค๋ฅธ ๋์ฐํ ๋ธ๋ญ๋ฒ์คํฐ๋ณด๋ค ๋ ๋ซ๊ณ , ๋ ์๊ฐํ๊ฒ ํด์ฃผ๊ณ , ์ ํ์ด 7๋
๋์ Phuket์์ ์ด์๋ ๊ฒ์ ๋ ์ฌ๋ฆฌ๊ฒ ํด์ฃผ๊ณ , ํธ์๋ฅดํ ๋ฆฌ์ฝ๋ก ๊ฐ๋ ๊ฒ์ ๋ ์ฌ๋ฆฌ๊ฒ ํด์ฃผ์ฃ .',
'๊ทธ๋ ์ง ์๋ค๋ฉด, ์ ๋ ๋ฉ์ง ์ ์ ๋ธ๋๋ ํผํธ๋ฅผ ๋ฌด์ฅ๋ณต์ ์
๊ณ ๋งค๋ ฅ์ ์ด๊ณ ์ธํฅ์ ์ธ ์บ๋ฆญํฐ๋ก ์
์ ๊ฒ์
๋๋ค.',
'์ ์ผํ ๋จ์ ์ ์ด ์ํ๊ฐ ๋๋ฌด ์งง๋ค๋ ๊ฒ์
๋๋ค.']
- ์์ ์ฝ๋๋ฅผ ํตํด ํํฐ๋ง๋ Hard Case๋ 3889๊ฑด
- ํ๊ตญ์ด โก๏ธ ์์ด โก๏ธ ํ๊ตญ์ด๋ก 2๋ฒ์ ๋ฒ์ญ์ ๊ฑฐ์น๋๋ฐ 24๋ถ ์ ๋๊ฐ ๊ฑธ๋ ธ๋ค
- ์ฆ๊ฐ ๊ฒฐ๊ณผ์ ์ผ๋ถ๋ฅผ ํ์ธํด๋ณด๋ฉด ๋ฒ์ญํ์ ๋ ํน์ ์ ๋ฌธ์ฒด์ ๋๋์ด ์์ด์ ๋๋ฌด ๋ง์ด ์ฆ๊ฐํ ๊ฒฝ์ฐ ๋ฒ์ญ์ฒด์ ๋ํด ํธํฅ์ด ์ปค์ ธ์ ๊ตฌ์ด์ฒด์ ๋น๋ฌธ์ ๋ํ ์์ธก์ด ๋จ์ด์ง ํ๋ฅ ์ด ์ปค์ง๋ฏ๋ก ์ด ๋ถ๋ถ ์ฃผ์ํด์ผ ํ๋ค
(B) LLM ๊ธฐ๋ฐ ์ฆ๊ฐ (ํฉ์ฑ ์์ฑ)
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch, pandas as pd
from sentence_transformers import SentenceTransformer, util
import torch.nn.functional as F
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
from tqdm import tqdm
# =========================
# ์ค์
# =========================
PROMPT_TEMPLATE = """๋ค์ ๋ฌธ์ฅ์ ์์ฐ์ค๋ฝ๊ฒ ์๋ฏธ๊ฐ ๊ฐ์ ๋ค๋ฅธ ํํ์ผ๋ก 2๊ฐ์ง ์จ์ค.
๊ตฌ์ด์ฒด๋ ์ผ์์ ์ธ ํํ์ ์ฌ์ฉํ๊ณ , ๋ฌธ๋ฒ์ ์ผ๋ก ์ฝ๊ฐ ํ๋ ค๋ ์์ฐ์ค๋ฌ์ฐ๋ฉด ์ข์.
์๋ฌธ: {sentence}
"""
LLM_MODEL = "spow12/Ko-Qwen2-7B-Instruct"
CLS_MODEL = "./best_model"
# CLS_MODEL = "klue/roberta-base"
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
BATCH_SIZE_LLM = 8 # LLM ๋ฐฐ์น ํฌ๊ธฐ
BATCH_SIZE_SBERT = 16 #SBERT, ๊ฐ์ฑ ๋ถ๋ฅ ๋ฐฐ์น ํฌ๊ธฐ
# =========================
# ๋ชจ๋ธ ๋ก๋
# =========================
tokenizer_llm = AutoTokenizer.from_pretrained(LLM_MODEL)
model_llm = AutoModelForCausalLM.from_pretrained(
LLM_MODEL,
torch_dtype=torch.float16,
device_map="auto",
offload_folder="./offload" # CPU๋ก ์์ ์คํ๋ก๋
)
sbert = SentenceTransformer("jhgan/ko-sroberta-multitask").to(DEVICE)
tokenizer_cls = AutoTokenizer.from_pretrained(CLS_MODEL)
model_cls = AutoModelForSequenceClassification.from_pretrained(CLS_MODEL).to(DEVICE)
# =========================
# ํจ์ ์ ์
# =========================
def generate_paraphrases_batch(sentences, num_return=3):
prompts = [PROMPT_TEMPLATE.format(sentence=s) for s in sentences]
inputs = tokenizer_llm(prompts, padding=True, truncation=True, return_tensors="pt").to(model_llm.device)
outputs = model_llm.generate(
**inputs,
max_new_tokens=128,
temperature=0.9,
top_p=0.9,
do_sample=True,
num_return_sequences=num_return,
eos_token_id=tokenizer_llm.eos_token_id
)
decoded = tokenizer_llm.batch_decode(outputs, skip_special_tokens=True)
# ๊ฒฐ๊ณผ๋ฅผ 2์ฐจ์ ๋ฆฌ์คํธ๋ก ๋ณํ: [ [๋ฌธ์ฅ1, ๋ฌธ์ฅ2, ...], [๋ฌธ์ฅ1, ...], ... ]
result_batch = []
for i in range(0, len(decoded), num_return):
result_batch.append([r.split("์๋ฌธ:")[-1].strip() for r in decoded[i:i+num_return]])
return result_batch
def is_similar_batch(originals, candidates, threshold=0.80):
# originals: list of str
# candidates: list of str
orig_emb = sbert.encode(originals, batch_size=BATCH_SIZE_SBERT, convert_to_tensor=True)
cand_emb = sbert.encode(candidates, batch_size=BATCH_SIZE_SBERT, convert_to_tensor=True)
sim_matrix = util.cos_sim(orig_emb, cand_emb)
return sim_matrix.diagonal() >= threshold # ๊ฐ ํ๋ณด์ ์๋ฌธ ์ ์ฌ๋
def is_same_emotion_batch(originals, candidates, labels):
inputs = tokenizer_cls(candidates, padding=True, truncation=True, return_tensors="pt").to(DEVICE)
with torch.no_grad():
probs = F.softmax(model_cls(**inputs).logits, dim=-1)
pred_labels = torch.argmax(probs, dim=-1).cpu().tolist()
return [pred_labels[i] == labels[i] for i in range(len(labels))]
def remove_duplicates(texts, threshold=0.95):
if len(texts) <= 1:
return texts
vectorizer = TfidfVectorizer().fit_transform(texts)
sim_matrix = cosine_similarity(vectorizer)
keep = []
for i in range(len(texts)):
if not any(sim_matrix[i][j] > threshold for j in keep):
keep.append(i)
return [texts[i] for i in keep]
# =========================
# ์ฆ๊ฐ ๋ฃจํ (๋ฐฐ์น ์ต์ ํ)
# =========================
augmented_data = []
minority_texts = minority_df["review"].tolist()
minority_labels = minority_df["label"].tolist()
for i in tqdm(range(0, len(minority_texts), BATCH_SIZE_LLM)):
batch_texts = minority_texts[i:i+BATCH_SIZE_LLM]
batch_labels = minority_labels[i:i+BATCH_SIZE_LLM]
# 1. LLM์ผ๋ก paraphrase ์์ฑ
batch_paraphrases = generate_paraphrases_batch(batch_texts, num_return=2)
for j, orig in enumerate(batch_texts):
candidates = batch_paraphrases[j]
labels = [batch_labels[j]] * len(candidates)
# 2. ์๋ฏธ ์ ์ฌ๋ ๊ฒ์ฌ
sim_mask = is_similar_batch([orig]*len(candidates), candidates)
# 3. ๊ฐ์ ์ผ๊ด์ฑ ๊ฒ์ฌ
emotion_mask = is_same_emotion_batch([orig]*len(candidates), candidates, labels)
# 4. ํํฐ๋ง
filtered = [c for k, c in enumerate(candidates) if sim_mask[k] and emotion_mask[k]]
# 5. ์ค๋ณต ์ ๊ฑฐ
filtered = remove_duplicates(filtered)
# 6. ์ ์ฅ
for t in filtered:
augmented_data.append({"review": t, "label": batch_labels[j]})
aug_df = pd.DataFrame(augmented_data)
aug_df.to_csv("augmented_data_batch.csv", index=False)
- ์ ์ํ ์ ์ผ๋ก๋ Back-Translation, LLM ์ฆ๊ฐ ๋ชจ๋ ๋ชจ๋ธ์ ํตํด ์์ํ์ ๋ฝ์๋ด๋ ๊ฒ์ด๊ธฐ ๋๋ฌธ์ ๊ท๋ชจ๊ฐ ์๋ ๋ชจ๋ธ์ ์ฌ์ฉํ๋ฉฐ, ๋ช ๋ง ๊ฑด ์ด์์ผ๋ก ์ฆ๊ฐ์ ํ๊ณ ์ ํ๋ค๋ฉด ๊ฝค ๋ง์ ๋ฆฌ์์ค(GPU)๋ฅผ ์ฌ์ฉํด์ผ ๋น ๋ฅด๊ฒ ์ฆ๊ฐ์ ํ ์ ์๋ค
- ์ปดํจํ ๋ฆฌ์์ค๊ฐ ๋ฐ์ณ์ฃผ์ง ์๋๋ค๋ฉด ์ ๋ง ํ๋ฃจ์ข ์ผ ์ฆ๊ฐํ๋๋ฐ๋ง ์๋ฒ๋ฅผ ๋๋ ค์ผ...
- 16,000๊ฑด Back-Translation์ ์ ์ฉํ๋๋ฐ 6์๊ฐ ์ ๋ ๊ฑธ๋ ธ๋ค
๊ฐ ํ ์คํธ๋ง๋ค ๋ผ๋ฒจ์ ๋ํ ํ๋ฅ ๊ฐ์ ์ด์ฉํด ๋ชจ๋ธ์ด ๋ฐ์ดํฐ์ ์ ๋ํด ์ผ๋ง๋ ์์ธก์ ์ํ๊ณ ๋ชปํ๋์ง๋ฅผ ๊ตฌ๋ณํด๋ผ ์ ์์๋ค
ํ๋ฅ ๊ฐ์ด๋ผ๋ ์์น๋ฅผ ํตํด ์ด๋ ๊ฒ ํ์์ ์ผ๋ก ๋ถ์๊ณผ ์ฌ๋ฌ ์ก์ ๋ค์ ์ทจํด๋ณผ ์ ์๋ค๋ ๊ฝค ํฅ๋ฏธ๋ก์ ๋ค
Hard Case์ ๋ํด์ ๋ ์ฌ๋์๋ ๋ถ์์ ์งํํ ์ ์์์ผ๋ฉด ์ข์์ํ ๋ฐ ์ด ๋ถ๋ถ์์ ๋งํ๋ค
๋ถ์์ ํตํด ์ธ์ฌ์ดํธ๋ฅผ ๋ฝ์๋ด์ผ ๋ญ๊ฐ๋ฅผ ๋ ํด๋ณผ ๊ฑธ ์ฐพ์ํ ๋ฐ ํ ์คํธ ๋ถ์์ ๋ํด์๋ ์ ๋ง ๊ธฐ์ด์ ์ธ ๋ฐฉ๋ฒ๋ค ๋ฐ์ ๋ ์ค๋ฅด์ง ์์ ์ฐธ ์์ฌ์ ๋ค
์ง๊ธ์ ํ์คํฌ๊ฐ ๋น๊ต์ ๊ฐ๋จํ ๋ถ๋ฅ๋ผ์ ์ด๋ ๊ฒ ์ฌ์ฉ ๊ฐ๋ฅํ์ง๋ง ์ค์ LLM์ ์ด์ฉํด ๋ฌธ์ฅ์ ์์ฑํ๋ ๊ฒฝ์ฐ์๋ ์ํ๋ ๋ชฉ์ ๊ณผ ๋ค๋ฅธ๋ฐ ์ ์ฌํด๋ณด์ด๋ ๋ฌธ์ฅ๋ค์ ํธ๋ค๋งํ๋ ๊ฒ์ด ์ค์ํ๋ค๊ณ ํ๋ค
์๋ ๊ธ๋ ๋์ค์ ์ฐธ๊ณ ํ ๊ฒธ ์ ์ฅ
https://sjkoding.tistory.com/102
[LLM] Text Embedding๋ชจ๋ธ ํ์ธํ๋์ ์ํ Hard Negative Mining ๋ฐฉ๋ฒ๋ก ํต์ฌ ์ ๋ฆฌ
๋ง์ง๋ง ํฌ์คํ ์ดํ ์ด๋๋ง 5๊ฐ์์ ์๊ฐ์ด ํ๋ ๋๋ฐ, ์ฌ์ค ์ด ์ฌ์ด์ ํ์ฌ ์ด์ง๊ณผ ์ ์์ ํ๋๋ด ๋ธ๋ก๊ทธ๋ฅผ ์ ๊ฒฝ์ฐ์ง ๋ชปํ์ต๋๋ค.๊ธฐ์กด์๋ LLM ์ฑ๋ด ๊ตฌ์ถ์ ์ํ ์๋น์ค๋ฅผ ๊ฐ๋ฐํ๋ค๋ฉด, ํ์ฌ๋ RAG
sjkoding.tistory.com
'AI' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| [Object Detection] Fast R-CNN ์ดํด๋ณด๊ธฐ (0) | 2025.12.01 |
|---|---|
| [Object Detection] R-CNN ์ดํด๋ณด๊ธฐ (+SPPNet) (0) | 2025.11.27 |
| ํ ์คํธ feature vector ์ดํด๋ณด๊ธฐ (0) | 2025.10.29 |
| VGGNet (ICLR 2015) ์์ฝ ๋ฐ ๋ฆฌ๋ทฐ (0) | 2024.01.29 |