| # ParaRater: Enhancing Cross-Lingual Transfer in LLMs with Meta-Learning |
|
|
| **ParaRater** is a data selection method that enhances cross-lingual transfer by **selecting the most valuable parallel pairs**, forming high-impact parallel corpora with two meta-learned raters. |
|
|
| ## Raters |
|
|
| This is the repository of trained rater models of ParaRater. Raters are trained based on `Qwen3-Embedding-0.6B`. |
|
|
| ## Usage |
| Each pair of Rater1 and Rater2 trained for a specific target language can be jointly used to filter English corpora. |
| ```python |
| |
| import argparse |
| import torch |
| import pandas as pd |
| import pyarrow.parquet as pq |
| from transformers import AutoTokenizer, AutoModelForSequenceClassification |
| |
| def percentile_ranks(scores): |
| # higher=better -> percentile in [0,1], 1.0 is best |
| order = torch.argsort(scores, descending=True) |
| ranks = torch.empty_like(order, dtype=torch.float) |
| ranks[order] = torch.arange(len(scores), dtype=torch.float) |
| denom = max(1, len(scores) - 1) |
| return 1.0 - ranks / denom |
| |
| @torch.no_grad() |
| def batched_logits(texts, tokenizer, model, batch_size=64, max_length=512, device="cuda" if torch.cuda.is_available() else "cpu"): |
| model.to(device).eval() |
| out_scores = [] |
| for i in range(0, len(texts), batch_size): |
| batch = texts[i:i+batch_size] |
| enc = tokenizer(batch, padding=True, truncation=True, |
| max_length=max_length, return_tensors="pt").to(device) |
| logits = model(**enc).logits.squeeze(-1) # (B,) for class_num=1 |
| out_scores.append(logits.cpu()) |
| return torch.cat(out_scores, dim=0) |
| |
| def main(): |
| ap = argparse.ArgumentParser() |
| ap.add_argument("--parquet", required=True, help="Input parquet path with column 'text'.") |
| ap.add_argument("--rater1", default="pararater_rater1_en-ar", help="Rater1.") |
| ap.add_argument("--rater2", default="pararater_rater2_en-ar", help="Rater2.") |
| ap.add_argument("--save_parquet", default=None, help="Optional output parquet for kept samples.") |
| ap.add_argument("--batch_size", type=int, default=64) |
| ap.add_argument("--max_length", type=int, default=512) |
| args = ap.parse_args() |
| |
| # 1) Load data |
| df = pq.read_table(args.parquet).to_pandas() |
| assert "text" in df.columns, "Parquet must have a 'text' column." |
| texts = df["text"].astype(str).tolist() |
| |
| # 2) Load raters |
| tok = AutoTokenizer.from_pretrained(args.rater1, trust_remote_code=True) |
| r1 = AutoModelForSequenceClassification.from_pretrained(args.rater1, trust_remote_code=True) |
| r2 = AutoModelForSequenceClassification.from_pretrained(args.rater2, trust_remote_code=True) |
| |
| # 3) Score -> percentile ranks |
| s1 = batched_logits(texts, tok, r1, batch_size=args.batch_size, max_length=args.max_length) |
| s2 = batched_logits(texts, tok, r2, batch_size=args.batch_size, max_length=args.max_length) |
| p1 = percentile_ranks(s1) # 1.0 best |
| p2 = percentile_ranks(s2) |
| |
| # 4) Rule: keep if (p1 >= 0.6) and (p2 <= p1 - 0.2) |
| top = p1 >= 0.6 |
| drop = p2 <= (p1 - 0.2) |
| keep_mask = (top & drop).numpy() |
| |
| kept = df.loc[keep_mask] |
| print(f"Total: {len(df)} | Rater1 top-0.6: {int(top.sum().item())} | Kept(final): {keep_mask.sum()}") |
| |
| if args.save_parquet: |
| kept.to_parquet(args.save_parquet, index=False) |
| print(f"Saved: {args.save_parquet}") |
| |
| if __name__ == "__main__": |
| main() |
| |
| ``` |