Sakhanda Wire
NVDA $223.96 +2.27% MSFT $499.99 +0.03% GOOGL $354.30 -0.96% META $592.10 +0.37% AMZN $274.48 +0.82%
← До новин

Аналіз тональності IMDb за допомогою DistilBERT + LoRA, базові моделі TF-IDF, калібрування, інтерпретованість, тестування стійкості та напівкероване навчання

У цьому посібнику ми розробляємо наскрізний робочий процес аналізу тональності, використовуючи великий набір даних рецензій на фільми IMDb від Stanford NLP, і порівнюємо класичне машинне навчання з параметрично ефективним донавчанням трансформера. Спочатку ми налаштовуємо відтворюване середовище та перевіряємо набір даних на порядок класів, перекіс довжини рецензій, витік дублікатів і артефакти попередньої обробки, перш ніж навчати сильну базову модель TF-IDF і логістичної регресії. Потім ми донавчаємо DistilBERT за допомогою LoRA через PEFT, оцінюємо його за допомогою точності, макро-F1, ROC-AUC, матриць помилок і ROC-кривих, а також досліджуємо вибір порога й калібрування ймовірностей за допомогою очікуваної похибки калібрування та аналізу надійності. Окрім основних метрик, ми аналізуємо впевнені помилки, ефективність на рецензіях різної довжини, послівну оклюзійну значущість і усічення початку проти кінця тексту, щоб зрозуміти, як модель формує свої прогнози та де обмеження довгого контексту впливають на результативність. Нарешті, ми використовуємо нерозмічену частину IMDb для псевдорозмітки на основі впевненості, порівнюємо отриману напівконтрольовану модель із нашою базовою моделлю та зберігаємо об’єднаний трансформер для повторного використання в задачах визначення тональності.

Копіювати кодСкопійованоСкористайтеся іншим браузером
import importlib.util, subprocess, sys, os, time, random, warnings, inspect, hashlib
warnings.filterwarnings("ignore")
os.environ["TOKENIZERS_PARALLELISM"] = "false"
os.environ["WANDB_DISABLED"] = "true"
_REQUIRED = {
   "transformers": "transformers",
   "datasets": "datasets",
   "peft": "peft",
   "accelerate": "accelerate",
   "sklearn": "scikit-learn",
}
_missing = [pkg for mod, pkg in _REQUIRED.items() if importlib.util.find_spec(mod) is None]
if _missing:
   print(f"Installing: {', '.join(_missing)} ...")
   subprocess.run([sys.executable, "-m", "pip", "install", "-q", *_missing], check=True)
   print("Done. (If imports fail below, restart the runtime and re-run.)\n")
import numpy as np
import pandas as pd
import torch
import matplotlib.pyplot as plt
from datasets import load_dataset
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import make_pipeline
from sklearn.metrics import (accuracy_score, f1_score, roc_auc_score,
                            classification_report, confusion_matrix, roc_curve)
from transformers import (AutoTokenizer, AutoModelForSequenceClassification,
                         TrainingArguments, Trainer, DataCollatorWithPadding,
                         EarlyStoppingCallback, set_seed)
from peft import LoraConfig, get_peft_model, TaskType
def _disable_torchao_probe():
   patched = []
   try:
       import peft.import_utils as _piu
       _piu.is_torchao_available = lambda: False
       patched.append("peft.import_utils")
   except Exception:
       pass
   for _name, _mod in list(sys.modules.items()):
       if _name.startswith("peft") and hasattr(_mod, "is_torchao_available"):
           _mod.is_torchao_available = lambda: False
           patched.append(_name)
   return patched
try:
   import torchao as _tao
   _v = getattr(_tao, "__version__", "?")
   if tuple(int(x) for x in _v.split(".")[:2]) < (0, 16):
       print(f"[compat] torchao {_v} < 0.16 -> disabling PEFT's torchao probe: "
             f"{', '.join(_disable_torchao_probe())}")
except Exception:
   _disable_torchao_probe()
SEED        = 42
MODEL_NAME  = "distilbert-base-uncased"
MAX_LEN     = 256
N_TRAIN     = 5000
N_EVAL      = 2000
N_UNSUP     = 3000
EPOCHS      = 2
BATCH       = 16
LR          = 3e-4
FULL_RUN    = False
if FULL_RUN:
   N_TRAIN, N_EVAL, EPOCHS = 25000, 25000, 3
set_seed(SEED); random.seed(SEED); np.random.seed(SEED); torch.manual_seed(SEED)
DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
print("=" * 79)
print(f"device={DEVICE} | torch={torch.__version__} | "
     f"gpu={torch.cuda.get_device_name(0) if DEVICE=='cuda' else 'n/a'}")
print("=" * 79)
t0 = time.time()
raw = load_dataset("stanfordnlp/imdb")
print(raw, f"\nloaded in {time.time()-t0:.1f}s\n")
print("--- example (truncated) ---")
print("label:", raw["train"][0]["label"], "|", raw["train"][0]["text"][:300], "...\n")
first_labels = np.array(raw["train"]["label"][:5])
last_labels  = np.array(raw["train"]["label"][-5:])
print(f"TRAP #1 - split ordering: first 5 labels {first_labels}, "
     f"last 5 labels {last_labels}  -> ALWAYS shuffle before subsampling.")
train_full = raw["train"].shuffle(seed=SEED)
test_full  = raw["test"].shuffle(seed=SEED)
train_ds   = train_full.select(range(min(N_TRAIN, len(train_full))))
eval_ds    = test_full.select(range(min(N_EVAL, len(test_full))))
print(f"   after shuffle+subsample: train balance = "
     f"{np.bincount(train_ds['label'])}, eval balance = {np.bincount(eval_ds['label'])}")
lens = np.array([len(t.split()) for t in train_full["text"]])
q = np.percentile(lens, [50, 75, 90, 95, 99])
print(f"\nTRAP #2 - length (words): median={q[0]:.0f} p75={q[1]:.0f} p90={q[2]:.0f} "
     f"p95={q[3]:.0f} p99={q[4]:.0f} max={lens.max()}")
print(f"   ~{(lens > MAX_LEN*0.75).mean()*100:.1f}% of reviews exceed MAX_LEN={MAX_LEN} "
     f"tokens (rough words->tokens factor 1.3). Section 9 measures what that costs.")
h_tr = {hashlib.md5(t.encode()).hexdigest() for t in raw["train"]["text"]}
h_te = {hashlib.md5(t.encode()).hexdigest() for t in raw["test"]["text"]}
print(f"\nTRAP #3 - leakage: {len(h_tr & h_te)} exact duplicate reviews across "
     f"train/test; {len(raw['train'])-len(h_tr)} dupes inside train itself.")
def clean(t):
   return t.replace("<br />", " ").replace("<br/>", " ").strip()
plt.figure(figsize=(11, 3.2))
plt.subplot(1, 2, 1)
plt.hist(np.clip(lens, 0, 1000), bins=60)
plt.axvline(MAX_LEN, ls="--", color="k", label=f"MAX_LEN={MAX_LEN}")
plt.title("Review length (words, clipped at 1000)"); plt.legend()
plt.subplot(1, 2, 2)
plt.bar(["neg", "pos"], np.bincount(raw["train"]["label"]))
plt.title("Train class balance (perfectly balanced)")
plt.tight_layout(); plt.show()

Ми налаштовуємо середовище Colab, встановлюємо необхідні бібліотеки, застосовуємо виправлення сумісності PEFT–torchao та встановлюємо детерміновані початкові значення для відтворюваних експериментів. Ми завантажуємо набір даних Stanford IMDb, перемішуємо та зменшуємо вибірки навчальної й тестової частин, а також перевіряємо баланс класів, розподіл довжин рецензій, витік дублікатів і HTML-артефакти. Крім того, ми візуалізуємо довжини рецензій і частоти міток, щоб зрозуміти структуру набору даних перед побудовою будь-яких моделей.

Копіювати кодСкопійованоСкористайтеся іншим браузером
print("\n" + "=" * 79 + "\n3. TF-IDF BASELINE\n" + "=" * 79)
Xtr = [clean(t) for t in train_ds["text"]]; ytr = np.array(train_ds["label"])
Xte = [clean(t) for t in eval_ds["text"]];  yte = np.array(eval_ds["label"])
t0 = time.time()
tfidf_clf = make_pipeline(
   TfidfVectorizer(ngram_range=(1, 2), min_df=2, max_features=300_000,
                   sublinear_tf=True, strip_accents="unicode"),
   LogisticRegression(C=8.0, max_iter=2000, n_jobs=-1),
)
tfidf_clf.fit(Xtr, ytr)
p_tfidf = tfidf_clf.predict_proba(Xte)[:, 1]
acc_tfidf = accuracy_score(yte, p_tfidf > 0.5)
auc_tfidf = roc_auc_score(yte, p_tfidf)
print(f"trained in {time.time()-t0:.1f}s -> acc={acc_tfidf:.4f}  auc={auc_tfidf:.4f}")
vec, lr = tfidf_clf.steps[0][1], tfidf_clf.steps[1][1]
feats, coefs = np.array(vec.get_feature_names_out()), lr.coef_[0]
order = np.argsort(coefs)
print("\nmost NEGATIVE n-grams:", ", ".join(feats[order[:12]]))
print("most POSITIVE n-grams:", ", ".join(feats[order[-12:]][::-1]))
print("\n" + "=" * 79 + "\n4. LoRA FINE-TUNING\n" + "=" * 79)
tok = AutoTokenizer.from_pretrained(MODEL_NAME)
def tokenize(batch):
   return tok([clean(t) for t in batch["text"]], truncation=True, max_length=MAX_LEN)
tr_tok = (train_ds.map(tokenize, batched=True, remove_columns=["text"])
                 .rename_column("label", "labels"))
ev_tok = (eval_ds.map(tokenize, batched=True, remove_columns=["text"])
                .rename_column("label", "labels"))
base = AutoModelForSequenceClassification.from_pretrained(
   MODEL_NAME, num_labels=2,
   id2label={0: "NEGATIVE", 1: "POSITIVE"},
   label2id={"NEGATIVE": 0, "POSITIVE": 1},
)
lora_cfg = LoraConfig(
   task_type=TaskType.SEQ_CLS,
   r=16, lora_alpha=32, lora_dropout=0.05,
   target_modules=["q_lin", "v_lin"],
   modules_to_save=["pre_classifier", "classifier"],
)
try:
   model = get_peft_model(base, lora_cfg)
except ImportError as e:
   _disable_torchao_probe()
   print(f"[compat] retrying after backend probe failure: {e}")
   model = get_peft_model(base, lora_cfg)
model.print_trainable_parameters()
def compute_metrics(eval_pred):
   logits, labels = eval_pred
   probs = torch.softmax(torch.tensor(logits), dim=-1).numpy()[:, 1]
   preds = (probs > 0.5).astype(int)
   return {"accuracy": accuracy_score(labels, preds),
           "f1_macro": f1_score(labels, preds, average="macro"),
           "roc_auc": roc_auc_score(labels, probs)}
_ta = inspect.signature(TrainingArguments.__init__).parameters
_eval_key = "eval_strategy" if "eval_strategy" in _ta else "evaluation_strategy"
ta_kwargs = dict(
   output_dir="./imdb_lora", learning_rate=LR,
   per_device_train_batch_size=BATCH, per_device_eval_batch_size=BATCH * 2,
   num_train_epochs=EPOCHS, weight_decay=0.01, warmup_ratio=0.06,
   logging_steps=50, save_strategy="epoch", save_total_limit=1,
   load_best_model_at_end=True, metric_for_best_model="accuracy",
   fp16=(DEVICE == "cuda"), report_to="none", seed=SEED,
)
ta_kwargs[_eval_key] = "epoch"
_tr = inspect.signature(Trainer.__init__).parameters
_tok_key = "processing_class" if "processing_class" in _tr else "tokenizer"
trainer = Trainer(
   model=model, args=TrainingArguments(**ta_kwargs),
   train_dataset=tr_tok, eval_dataset=ev_tok,
   data_collator=DataCollatorWithPadding(tok),
   compute_metrics=compute_metrics,
   callbacks=[EarlyStoppingCallback(early_stopping_patience=2)],
   **{_tok_key: tok},
)
t0 = time.time()
trainer.train()
print(f"\nfine-tuned in {(time.time()-t0)/60:.1f} min")

Ми навчаємо сильну базову модель TF-IDF і логістичної регресії та аналізуємо найвпливовіші позитивні й негативні n-грами, щоб створити інтерпретований орієнтир. Потім ми токенізуємо рецензії IMDb і налаштовуємо DistilBERT з адаптерами LoRA, які оновлюють лише невелику частину параметрів моделі, залишаючи основну частину базової моделі замороженою. Для ефективного донавчання трансформера ми використовуємо Trainer від Hugging Face з динамічним доповненням, ранньою зупинкою, змішаною точністю та кількома метриками оцінювання.

Копіювати кодСкопійованоСкористайтеся іншим браузером
print("\n" + "=" * 79 + "\n5. EVALUATION\n" + "=" * 79)
pred_out = trainer.predict(ev_tok)
p_lora = torch.softmax(torch.tensor(pred_out.predictions), dim=-1).numpy()[:, 1]
y_true = np.array(pred_out.label_ids)
yhat = (p_lora > 0.5).astype(int)
print(classification_report(y_true, yhat, target_names=["neg", "pos"], digits=4))
cm = confusion_matrix(y_true, yhat)
fig, ax = plt.subplots(1, 2, figsize=(11, 4))
ax[0].imshow(cm, cmap="Blues")
for i in range(2):
   for j in range(2):
       ax[0].text(j, i, cm[i, j], ha="center", va="center", fontsize=14)
ax[0].set_xticks([0, 1], ["pred neg", "pred pos"])
ax[0].set_yticks([0, 1], ["true neg", "true pos"]); ax[0].set_title("Confusion matrix")
for name, p in [("TF-IDF", p_tfidf), ("DistilBERT+LoRA", p_lora)]:
   fpr, tpr, _ = roc_curve(y_true, p)
   ax[1].plot(fpr, tpr, label=f"{name} (AUC={roc_auc_score(y_true, p):.4f})")
ax[1].plot([0, 1], [0, 1], "k--", lw=0.8)
ax[1].set_xlabel("FPR"); ax[1].set_ylabel("TPR"); ax[1].set_title("ROC"); ax[1].legend()
plt.tight_layout(); plt.show()
print("\n" + "=" * 79 + "\n6. THRESHOLD & CALIBRATION\n" + "=" * 79)
ths = np.linspace(0.05, 0.95, 91)
accs = [(y_true == (p_lora > t)).mean() for t in ths]
best_t = ths[int(np.argmax(accs))]
print(f"acc@0.50 = {accs[45]:.4f} | best threshold = {best_t:.2f} -> acc = {max(accs):.4f}")
def expected_calibration_error(probs, labels, n_bins=10):
   """ECE: |confidence - accuracy| averaged over confidence bins."""
   conf = np.maximum(probs, 1 - probs)
   correct = (probs > 0.5).astype(int) == labels
   bins = np.linspace(0, 1, n_bins + 1)
   ece, xs, ys = 0.0, [], []
   for lo, hi in zip(bins[:-1], bins[1:]):
       m = (conf > lo) & (conf <= hi)
       if m.sum() == 0:
           continue
       ece += m.mean() * abs(conf[m].mean() - correct[m].mean())
       xs.append(conf[m].mean()); ys.append(correct[m].mean())
   return ece, np.array(xs), np.array(ys)
ece, cx, cy = expected_calibration_error(p_lora, y_true)
print(f"Expected Calibration Error = {ece:.4f}  (0 = perfectly calibrated)")
plt.figure(figsize=(9, 3.2))
plt.subplot(1, 2, 1); plt.plot(ths, accs); plt.axvline(best_t, ls="--", color="r")
plt.xlabel("threshold"); plt.ylabel("accuracy"); plt.title("Threshold sweep")
plt.subplot(1, 2, 2); plt.plot([0.5, 1], [0.5, 1], "k--", lw=0.8)
plt.plot(cx, cy, "o-"); plt.xlabel("mean confidence"); plt.ylabel("empirical accuracy")
plt.title(f"Reliability diagram (ECE={ece:.3f})")
plt.tight_layout(); plt.show()

Ми оцінюємо донавчену модель DistilBERT-LoRA за допомогою метрик класифікації, матриці помилок і ROC-кривих, безпосередньо порівнюючи її показник ROC-AUC із базовою моделлю TF-IDF. Ми перебираємо пороги класифікації, щоб визначити, чи забезпечує стандартний поріг імовірності 0,5 найкращу точність на нашій оцінювальній вибірці. Також ми обчислюємо очікувану похибку калібрування та будуємо діаграму надійності, щоб виміряти, наскільки передбачена моделлю впевненість відповідає її фактичній правильності.

Копіювати кодСкопійованоСкористайтеся іншим браузером
print("\n" + "=" * 79 + "\n7. ERROR ANALYSIS\n" + "=" * 79)
err = pd.DataFrame({
   "text": eval_ds["text"], "y": y_true, "p_pos": p_lora,
   "n_words": [len(t.split()) for t in eval_ds["text"]],
})
err["pred"] = (err.p_pos > 0.5).astype(int)
err["correct"] = err.pred == err.y
err["confidence"] = np.maximum(err.p_pos, 1 - err.p_pos)
print("--- 3 most CONFIDENT mistakes (where the model is confidently wrong) ---")
for _, r in err[~err.correct].nlargest(3, "confidence").iterrows():
   print(f"\n[true={'pos' if r.y else 'neg'} pred={'pos' if r.pred else 'neg'} "
         f"conf={r.confidence:.3f} words={r.n_words}]")
   print(clean(r.text)[:400].replace("\n", " "), "...")
err["bucket"] = pd.qcut(err.n_words, 4, labels=["short", "med", "long", "v.long"])
by_len = err.groupby("bucket", observed=True).agg(acc=("correct", "mean"), n=("correct", "size"))
print("\n--- accuracy by review length (truncation hurts long reviews) ---")
print(by_len.to_string())
print("\n" + "=" * 79 + "\n8. OCCLUSION SALIENCY\n" + "=" * 79)
infer_model = model.merge_and_unload()
infer_model.to(DEVICE).eval()
@torch.no_grad()
def predict_proba(texts, bs=64):
   out = []
   for i in range(0, len(texts), bs):
       enc = tok([clean(t) for t in texts[i:i + bs]], truncation=True,
                 max_length=MAX_LEN, padding=True, return_tensors="pt").to(DEVICE)
       out.append(torch.softmax(infer_model(**enc).logits, dim=-1)[:, 1].cpu().numpy())
   return np.concatenate(out)
def occlusion(text, max_words=60):
   words = clean(text).split()[:max_words]
   base = predict_proba([" ".join(words)])[0]
   variants = [" ".join(words[:i] + words[i + 1:]) for i in range(len(words))]
   dropped = predict_proba(variants)
   return words, base - dropped, base
sample = err[err.correct].nlargest(1, "confidence").iloc[0]
words, contrib, base_p = occlusion(sample.text)
print(f"P(positive) for the full excerpt = {base_p:.3f} "
     f"(true label = {'pos' if sample.y else 'neg'})\n")
top = np.argsort(np.abs(contrib))[-15:]
plt.figure(figsize=(7, 5))
plt.barh(range(len(top)), contrib[top],
        color=["tab:green" if contrib[i] > 0 else "tab:red" for i in top])
plt.yticks(range(len(top)), [words[i] for i in top])
plt.xlabel("Δ P(positive) when the word is removed")
plt.title("Occlusion saliency — green pushes POSITIVE, red pushes NEGATIVE")
plt.tight_layout(); plt.show()
print("\n" + "=" * 79 + "\n9. HEAD vs TAIL TRUNCATION\n" + "=" * 79)
probe = err.nlargest(600, "n_words")
W = 180
head_txt = [" ".join(clean(t).split()[:W]) for t in probe.text]
tail_txt = [" ".join(clean(t).split()[-W:]) for t in probe.text]
yp = probe.y.values
acc_head = ((predict_proba(head_txt) > 0.5).astype(int) == yp).mean()
acc_tail = ((predict_proba(tail_txt) > 0.5).astype(int) == yp).mean()
print(f"on the {len(probe)} longest reviews, using only {W} words:")
print(f"  first {W} words -> acc {acc_head:.4f}")
print(f"  last  {W} words -> acc {acc_tail:.4f}")
print("  Practical takeaway: if the tail wins, feed head+tail to the model or "
     "raise MAX_LEN, rather than blindly truncating from the left.")

Ми аналізуємо найвпевненіші неправильні передбачення моделі та групуємо рецензії за довжиною, щоб виявити закономірності помилок, пов’язані з усіченням, і складні приклади. Ми об’єднуємо адаптери LoRA з базовою моделлю та застосовуємо оклюзію «вилучення одного слова» для оцінювання того, які слова спрямовують окремі передбачення до позитивної або негативної тональності. Потім ми порівнюємо передбачення на основі початкових і кінцевих частин довгих рецензій, щоб визначити, де міститься найсильніша інформація про тональність.

Копіювати кодСкопійованоСкористайтеся іншим браузером
print("\n" + "=" * 79 + "\n10. PSEUDO-LABELLING\n" + "=" * 79)
unsup = raw["unsupervised"].shuffle(seed=SEED).select(range(N_UNSUP))
p_uns = predict_proba(unsup["text"])
keep = (p_uns > 0.95) | (p_uns < 0.05)
pl_texts = [clean(t) for t, k in zip(unsup["text"], keep) if k]
pl_labels = (p_uns[keep] > 0.5).astype(int)
print(f"kept {keep.sum()}/{N_UNSUP} pseudo-labels at conf>0.95 "
     f"(balance: {np.bincount(pl_labels)})")
aug = make_pipeline(
   TfidfVectorizer(ngram_range=(1, 2), min_df=2, max_features=300_000,
                   sublinear_tf=True, strip_accents="unicode"),
   LogisticRegression(C=8.0, max_iter=2000, n_jobs=-1),
).fit(Xtr + pl_texts, np.concatenate([ytr, pl_labels]))
acc_aug = accuracy_score(yte, aug.predict(Xte))
print(f"TF-IDF baseline      : {acc_tfidf:.4f}")
print(f"TF-IDF + pseudo-labels: {acc_aug:.4f}  (Δ {acc_aug-acc_tfidf:+.4f})")
print("Caveat: gains are bounded by the teacher. Self-training also amplifies "
     "the teacher's biases — always validate on clean, held-out data.")
print("\n" + "=" * 79 + "\n11. SAVE & INFER\n" + "=" * 79)
SAVE_DIR = "./imdb-distilbert-lora-merged"
infer_model.save_pretrained(SAVE_DIR); tok.save_pretrained(SAVE_DIR)
print(f"saved merged model to {SAVE_DIR}/  (load with "
     f"AutoModelForSequenceClassification.from_pretrained('{SAVE_DIR}'))")
demos = [
   "A masterclass in tension. The final act left the whole theatre silent.",
   "Two hours I will never get back. Wooden acting, incoherent plot.",
   "It's not the disaster the trailer promised, but it never really lands either.",
]
for d, p in zip(demos, predict_proba(demos)):
   print(f"  P(pos)={p:.3f} -> {'POSITIVE' if p > 0.5 else 'NEGATIVE'} | {d}")
print("\n" + "=" * 79)
print(f"SUMMARY (n_train={N_TRAIN}, n_eval={N_EVAL}, max_len={MAX_LEN})")
print("=" * 79)
print(pd.DataFrame([
   {"model": "TF-IDF + LogReg", "accuracy": acc_tfidf, "roc_auc": auc_tfidf},
   {"model": "TF-IDF + pseudo-labels", "accuracy": acc_aug, "roc_auc": float("nan")},
   {"model": "DistilBERT + LoRA", "accuracy": accuracy_score(y_true, yhat),
    "roc_auc": roc_auc_score(y_true, p_lora)},
]).to_string(index=False))
print("""
NEXT EXPERIMENTS
 - Set FULL_RUN = True for the real 25k/25k benchmark (~40 min on a T4).
 - Swap MODEL_NAME to 'roberta-base' (target_modules=['query','value']) or
   'answerdotai/ModernBERT-base' for an 8k context window — no truncation.
 - Head+tail truncation: first 128 + last 128 tokens, motivated by section 9.
 - Ablate LoRA rank r in {4, 8, 16, 64} and plot accuracy vs trainable params.
 - Replace the pseudo-label teacher with an ensemble and iterate self-training.
 - Push to the Hub: huggingface_hub.login() then infer_model.push_to_hub(...).
""")

Ми використовуємо донавчений трансформер для створення псевдорозмітки з високою впевненістю для прикладів із нерозміченої частини IMDb і додаємо ці приклади до навчального корпусу TF-IDF. Ми порівнюємо доповнений класифікатор із початковою базовою моделлю, щоб визначити, чи покращує напівконтрольоване самонавчання точність передбачень. Нарешті, ми зберігаємо об’єднану модель DistilBERT і токенізатор, виконуємо визначення тональності для власних рецензій і підсумовуємо результати всіх моделей, розроблених у цьому посібнику.

Підсумовуючи, ми розробили ретельний конвеєр класифікації тональності, який виходить далеко за межі простого донавчання трансформера та подання показника точності. Ми створили конкурентоспроможну базову модель TF-IDF, ефективно навчили DistilBERT за допомогою LoRA та оцінили як якість передбачень, так і надійність імовірностей, водночас визначивши, як довжина рецензії, усічення та дуже впевнені помилки впливають на ефективність у реальних умовах. Ми також інтерпретували окремі передбачення за допомогою оклюзійної значущості, перевірили, чи зосереджена інформація про тональність на початку або в кінці довгих рецензій, і розширили контрольоване навчання за допомогою псевдорозмітки з високою впевненістю на нерозміченому наборі даних.


Перегляньте ПОВНИЙ КОД тут. Також підписуйтеся на нас у Twitter і не забудьте приєднатися до нашого сабреддіту про машинне навчання зі 150 тисячами учасників та підписатися на нашу розсилку. Зачекайте! Ви користуєтеся Telegram? тепер ви також можете приєднатися до нас у Telegram.

Потрібно співпрацювати з нами для просування вашого репозиторію GitHub, сторінки Hugging Face, запуску продукту, вебінару тощо? Зв’яжіться з нами

Перекладено автоматично з англійської. Оригінал статті — за посиланням нижче.

Вперше опубліковано виданням MarkTechPost

Читати оригінал на MarkTechPost ↗

Текст і зображення належать MarkTechPost і наводяться тут із зазначенням авторства та посиланням на оригінальну публікацію.

← До новин

Ще новини

Усі останні новини