log predictions

This commit is contained in:
wassname
2025-10-16 15:22:49 +08:00
parent 6f96552117
commit a656366935
7 changed files with 905 additions and 13 deletions
+1 -1
View File
@@ -26,7 +26,7 @@ Usage
```py
# make sure you OPENROUTER_API_KEY is set in your .env file
uv sync
uv run nbs/simple_icm.py
uv run src/simple_icm.py
```
## Results
+280
View File
@@ -64,3 +64,283 @@ Theory clarification (in Analysis section):
- Epistemic = ensemble variance (model uncertainty) ?
- Aleatoric = consistency failures (data ambiguity) ?
- Evidence weights = structural confidence (relationship strength) ?
# 2025-10-16 13:57:48
## Idea context
Absolutely, that clarification makes total sense—it streamlines the backprop idea significantly and keeps things focused on what you're trying to achieve (e.g., propagating evidence and consistency for unsupervised labels, inspired by ICM and your graph/rollout notes). Let me confirm I understand your setup, then break down how to implement it, compare it to graphs, and refine the complexity/gain ratings based on this "LLM-as-prior-provider" approach. This keeps the LLM out of the optimization loop (smart, since most LLMs like Llama or Claude aren't easily differentiable), treating it as a fixed oracle for generating initial priors or prediction tuples upfront.
### Confirmed Understanding of Your Backprop Setup
- **Core Components**:
- **Learnable Labels**: These are the only trainable parameters (e.g., soft probabilities for each example's label, like [prob_yes, prob_no]).
- **Loss Function**: Something "weird" (custom) computed over n-shot examples and prediction tuples. For instance, the loss could measure mutual predictability: For each tuple (a set of n-shot context labels + a target prediction), compute how well the predicted label matches the "true" (learnable) one, perhaps using cross-entropy or ranking losses. This encourages coherence across the dataset.
- **LLM's Role**: The LLM provides fixed priors (e.g., initial logprob-based scores like -(logprob_neg - logprob_pos) for each label) and/or generates the prediction tuples (e.g., by querying the LLM once to get few-shot predictions for various contexts). These are pre-computed and not updated during backprop—the LLM isn't in the graph or optimization.
- **No LLM in the Graph**: Optimization is just over the small set of labels, using gradients from the loss to update them iteratively. This is efficient for small graphs (e.g., 100-1000 examples) and aligns with your idea of propagating confidence (e.g., if a label leads to more consistent predictions, it gets reinforced via the loss).
This is reminiscent of techniques in papers like CCS (Contrast-Consistent Search) or even Fabien's probe experiments, where labels are optimized for internal consistency without external supervision. Your twist (using n-shot tuples and logprob rankings as priors) could make it more robust to outdated rollouts or scattered samples.
### How This Backprop Approach Works (Pros/Cons vs. Graphs)
- **High-Level Flow**:
1. **Pre-Compute from LLM**: Use the LLM to generate:
- Priors: For each example, get logprobs for "yes" vs. "no" (or positive/negative), compute scores like score = logprob_pos - logprob_neg (positive favors "yes"). Treat these as initial values or regularization terms— they're rankings, not calibrated probs, as you noted.
- Prediction Tuples: Generate a bunch of n-shot contexts (e.g., sample subsets of examples, feed to LLM as few-shot prompts, get predicted labels/probs for a target example). This creates a dataset of tuples like (context_labels, target_prediction).
2. **Define Learnable Labels**: Initialize a tensor of soft labels (e.g., from priors).
3. **Custom Loss**: For each tuple, plug in the current learnable labels (for the context) and compare to the LLM's pre-computed prediction (for the target). Loss could be:
- Predictability term: Cross-entropy between predicted prob and learnable target label.
- Consistency term: Penalize inconsistencies (e.g., if labels violate logical rules).
- Prior term: KL divergence or L2 to the LLM priors, to anchor optimization.
- Your propagation idea: Add a term that rewards labels leading to "more confident/consistent" downstream predictions (e.g., lower entropy in tuples that depend on it).
4. **Backprop and Optimize**: Run gradient descent on the labels to minimize the total loss. Since the graph is small, it converges fast.
- **Pros**:
- Flexible for your "weird" losses (e.g., incorporating rollout datedness or graph weights as factors in the loss).
- Handles soft labels and rankings naturally (e.g., use pairwise ranking losses like margin loss on logprob differences to avoid calibration issues).
- Efficient: No LLM calls during training—just math on pre-computed data.
- **Cons vs. Explicit Graphs**:
- Less interpretable than a graph (harder to visualize dependencies), but more automated (gradients handle propagation implicitly).
- If dependencies are sparse/simple, graphs (with BP) might be overkill but easier to debug. Backprop shines if you want to learn extra params (e.g., evidence weights) or handle non-linear losses.
- **Is This Reinventing Graphs?**: Partially—it's a differentiable analog. The n-shot tuples implicitly define a graph (contexts as "neighbors," predictions as edge messages), and backprop propagates evidence like BP does. But you're not explicitly building/maintaining a graph structure, so it's a hybrid: graph-inspired but optimization-focused. Not reinventing—more like adapting (e.g., similar to how GNNs turn graphs into backprop-able nets). If you find backprop getting messy, fallback to explicit graphs is easy.
### Implementation Suggestions
Here's a quick PyTorch sketch to get you started. Assume you have pre-computed `priors` (list of scores) and `tuples` (list of dicts with 'context_indices', 'target_index', 'llm_pred_prob' for yes/no).
```python
import torch
import torch.nn.functional as F
# Setup
num_labels = 100 # Number of examples/labels
labels = torch.nn.Parameter(torch.randn(num_labels, 2)) # Learnable logits [yes, no] per label
priors = torch.tensor([precomputed_scores]) # Shape: (num_labels, 2), from LLM logprobs
tuples = [...] # List of {'context_indices': [i,j,k], 'target_index': m, 'llm_pred': [prob_yes, prob_no]}
# Custom loss function
def coherence_loss(labels, tuples, priors, alpha=0.1, beta=0.1):
loss = 0.0
labels_soft = F.softmax(labels, dim=1) # Soft probs
# Predictability over tuples
for t in tuples:
# Simplified: Average context labels as "input" (or use more complex aggregation)
context_avg = labels_soft[t['context_indices']].mean(dim=0)
target_prob = labels_soft[t['target_index']]
pred_loss = F.cross_entropy(context_avg.unsqueeze(0), target_prob.unsqueeze(0)) # Or ranking loss
# Reward consistency/confidence: e.g., lower entropy if this label boosts downstream
entropy_penalty = -torch.sum(target_prob * torch.log(target_prob)) # Lower is more confident
loss += pred_loss + beta * entropy_penalty
# Prior regularization (e.g., treat as soft targets)
prior_loss = F.kl_div(F.log_softmax(labels, dim=1), priors.softmax(dim=1), reduction='batchmean')
# Add consistency constraints (e.g., logical rules)
# consistency_loss = ... (custom, e.g., penalize if label_i + label_j >1 for mutex pairs)
return loss / len(tuples) + alpha * prior_loss # + consistency_loss
# Optimize (use 2nd-order as you suggested for small size)
optimizer = torch.optim.LBFGS([labels], lr=0.1) # Or Adam for first-order
for epoch in range(10): # LBFGS often converges in few steps
def closure():
optimizer.zero_grad()
loss = coherence_loss(labels, tuples, priors)
loss.backward()
return loss
optimizer.step(closure)
print(f"Epoch {epoch}: Loss {closure().item()}")
# Final labels: argmax or softmax
final_labels = torch.argmax(F.softmax(labels, dim=1), dim=1)
```
- **Tweaks**:
- **For Rankings/Calibration**: Replace cross-entropy with a ranking loss (e.g., `torch.nn.MarginRankingLoss` on logprob differences) to treat LLM outputs as rankings.
- **Learning Weights**: Add learnable params like `evidence_weights = torch.nn.Parameter(torch.ones(num_evidence_types))` and multiply them into the loss terms.
- **Efficiency**: With num_labels=100 and 1000 tuples, this runs in seconds on CPU. L-BFGS is perfect for small params (as you said).
- **Landmark Calibration**: Post-optimization, use a few "landmark" examples (e.g., known easy/hard ones) to scale the final probs.
### Updated Ratings (Complexity and Expected Gain)
Adjusted for this simplified backprop (no LLM in the loop):
- **Complexity: Medium** (Down from High—pre-computing makes it easier, but custom loss debugging adds some work. ~1-2 hours to prototype if you're PyTorch-familiar.)
- **Expected Gain: Medium-High** (Same as before—could outperform simple accumulation on fuzzy tasks like TruthfulQA by learning nuanced propagations. Matches Fabien's ~100% PGR potential if your priors capture salient concepts. Gains depend on how well tuples cover dependencies; test vs. baseline ICM.)
This setup feels like a great balance—powerful enough for your ideas without overcomplicating. If it works on a small test (e.g., 20 examples from the GitHub repo), scale it up! What dataset are you thinking of using first, or do you want help refining the loss function? 😊
----
Below, I'll address each part of your query step by step, building on our discussion. I'll start with explanations for landmark calibration, pairwise ranking losses, and the consistency measures (with learnable weights). Then, I'll provide a full example code snippet for the loss function, incorporating all of this. The code expands on my previous PyTorch sketch, making it more concrete and tailored to your ideas (e.g., n-shot prediction tuples, logprob ratios as priors, multiple consistency terms with weights).
This setup assumes:
- You have pre-computed data from the LLM: `priors` (logprob-based scores/ratios for each label) and `tuples` (n-shot contexts with LLM-generated predictions).
- Labels are learnable parameters (e.g., logits for yes/no).
- Optimization is over these labels to minimize a custom loss that encourages coherence, using gradients for propagation.
The result is an efficient, backprop-based label optimizer that's lightweight (runs fast on small datasets) and aligns with ICM-style unsupervised elicitation.
### 1. Landmark Calibration for Logprob Ratios as Priors
Landmark calibration is a way to adjust raw LLM logprobs (which are often miscalibrated, as discussed in the LessWrong thread) using a few "landmark" examples with known properties (e.g., easy/true cases where you expect high confidence, or hard/false ones for low confidence). This turns uncalibrated logprobs into more reliable priors before optimization. It's not strictly necessary (you can skip it and rely on rankings), but it helps if you want priors to act like soft probabilities.
- **How to Do It Pre-Optimization**:
1. Select 5-20 landmark examples (e.g., from your dataset or synthetic ones: obvious "yes" math problems from GSM8K for high prob_yes, common misconceptions from TruthfulQA for low).
2. Query the LLM for logprobs on these.
3. Fit a simple calibrator: E.g., temperature scaling (find a temp `T` that minimizes cross-entropy on landmarks) or Platt scaling (linear transform: scaled_logit = a * logit + b).
4. Apply to all logprob ratios: prior_score = (logprob_pos - logprob_neg) / T (or whatever your ratio is).
5. Convert to soft priors: prior_probs = softmax([prior_score, -prior_score]) for [yes, no].
- **Integration as Priors in PyTorch**:
- These calibrated ratios become fixed tensors (not parameters)—you'll use them in a regularization term (e.g., KL loss) to pull the learnable labels toward them.
- **Will KL Loss Make This Happen End-to-End?** Yes! By adding a KL divergence term in the loss (as in my previous sketch), the optimization will naturally balance the priors with other terms (e.g., coherence). No need to make priors learnable—they're anchors. During backprop, gradients from KL will propagate to update labels toward calibrated values. If you want to learn how much to trust priors, add a learnable weight (e.g., `alpha` in the loss) and optimize it too (via hyperparam sweep or as a parameter).
- **When to Skip**: If treating logprobs as rankings (via pairwise losses—see below), calibration is less critical. Just use raw differences as relative strengths.
Example calibration code (pre-optimization):
```python
import torch
import torch.nn.functional as F
# Assume landmarks: list of (logprob_pos, logprob_neg, true_label) # true_label=1 for yes
def calibrate_temperature(logprobs_pos, logprobs_neg, true_labels, temps=[0.5, 1.0, 2.0]):
best_temp, best_loss = None, float('inf')
for T in temps:
scaled_logits = torch.tensor([(p - n) / T for p, n in zip(logprobs_pos, logprobs_neg)])
preds = F.softmax(scaled_logits.unsqueeze(1), dim=1)[:, 0] # Prob yes (assuming binary)
loss = F.binary_cross_entropy(preds, torch.tensor(true_labels).float())
if loss < best_loss:
best_temp, best_loss = T, loss
return best_temp
# Apply to all priors
temp = calibrate_temperature(landmark_pos, landmark_neg, landmark_true)
priors = torch.tensor([(p - n) / temp for p, n in zip(all_logprobs_pos, all_logprobs_neg)]) # Shape: (num_labels,)
priors = F.softmax(torch.stack([priors, -priors], dim=1), dim=1) # To [prob_yes, prob_no]
```
### 2. Pairwise Ranking Losses (e.g., Margin Loss on Logprob Differences)
Pairwise ranking losses treat logprobs as relative rankings (e.g., "is yes better than no?") rather than absolute probabilities, avoiding calibration issues entirely. This is reliable because LLM logprobs are better for comparisons (as you noted—they're often used for ranking preferences in RLHF).
- **How It Works**:
- Instead of cross-entropy (which assumes calibrated probs), you define pairs of options and penalize if the ranking doesn't match expectations.
- Use `torch.nn.MarginRankingLoss` (or `torch.nn.PairwiseMarginRankingLoss`): It takes two scores (e.g., score_yes and score_no) and a target (1 if yes > no, -1 if no > yes, 0 if tie). Loss = max(0, -target * (score1 - score2) + margin). This enforces a margin between better/worse options.
- **What Are the Pairs?**:
- **Per-Label Pairs**: For each label, pair its "yes" vs. "no" logprob differences (e.g., from priors or tuple predictions). Target=1 if the learnable label leans yes (e.g., labels_soft[:,0] > 0.5).
- **Across-Tuple Pairs**: For a prediction tuple, pair the LLM's predicted prob_yes vs. prob_no, and compare to the learnable target label. Or pair different tuples' predictions to enforce mutual predictability (e.g., "if context A predicts yes, it should rank higher than context B predicting no").
- **Propagation Pairs**: To capture "downstream consistency," pair a label's score with aggregated scores from dependent tuples (e.g., if this label is in many contexts, ensure its ranking boosts overall coherence).
- **Why Avoid Calibration Issues?**: It only cares about order (e.g., yes ranks above no by at least margin=1.0), not absolute values. Great for your logprob ratios.
In code, it'll be a term in the loss (see full example below).
### 3. Measures of Consistency with Learnable/Hyperparam Weights
Yes, modularizing the loss into weighted consistency measures is a smart way to prevent "cheating" (e.g., superficial solutions, as in ICM's logical constraints). You can make weights learnable (as parameters) for end-to-end adaptation, or sweep them as hyperparameters (safer if worried about overfitting/cheating). Start with 3-5 measures, weighted by a vector (e.g., [w_direct, w_downstream, w_mutual]).
- **Suggested Measures** (with your ideas incorporated):
- **Direct Consistency**: Penalizes labels that flip-flop (e.g., variance across similar tuples). Weight: High if you have noisy priors.
- **Downstream Consistency/Contribution**: Measures how much a label improves predictions in dependent tuples (e.g., lower loss/entropy when this label is in the context). This propagates "if this label led to more confident predictions" as you described.
- **Mutual Predictability**: As in ICM/Fabien's work—how well one label predicts another (e.g., cross-entropy between predicted and target in tuples).
- **Others to Consider**:
- **Logical Consistency**: Penalize violations of rules (e.g., mutex labels can't both be yes).
- **Prior Fidelity**: KL to calibrated priors (as above).
- **Entropy/Confidence**: Reward low-entropy (confident) labels, but only if they contribute to coherence.
- **Learnable Weights?**: Yes—make them parameters (e.g., `weights = torch.nn.Parameter(torch.ones(3))`) and include in optimization. Or sweep (e.g., grid search [0.1, 0.5, 1.0] per weight) to avoid cheating. Learning is efficient for small #weights.
### 4. Full Example Code for Loss (and Optimization)
Here's an integrated PyTorch example. It includes landmark calibration (pre-step), pairwise ranking loss, multiple consistency measures with learnable weights, and LBFGS for fast convergence (2-5x faster than Adam on small problems, as you referenced—great for <1000 labels).
```python
import torch
import torch.nn as nn
import torch.nn.functional as F
# Pre-compute calibrated priors (from landmark calibration, as above)
# Assume priors: torch.Tensor (num_labels, 2) # [prob_yes, prob_no]
# tuples: list of dicts, e.g., {'context_indices': [0,1,2], 'target_index': 3, 'llm_pred': [0.7, 0.3]}
# Setup learnables
num_labels = len(priors)
labels = nn.Parameter(torch.randn(num_labels, 2)) # Logits [yes, no]
consistency_weights = nn.Parameter(torch.ones(3)) # Learnable: [w_direct, w_downstream, w_mutual]
# Custom loss function
def coherence_loss(labels, tuples, priors, margin=1.0):
labels_soft = F.softmax(labels, dim=1) # Soft probs [num_labels, 2]
weights = F.softplus(consistency_weights) # Ensure positive
loss = 0.0
# Measure 1: Direct Consistency (e.g., low variance across tuples for same target)
direct_loss = 0.0
for target in set(t['target_index'] for t in tuples): # Group by target
target_preds = torch.stack([labels_soft[target] for t in tuples if t['target_index'] == target])
direct_loss += target_preds.var(dim=0).mean() # Variance penalty
direct_loss /= num_labels or 1
# Measure 2: Downstream Consistency (how much label contributes to low-entropy downstream)
downstream_loss = 0.0
for t in tuples:
context_soft = labels_soft[t['context_indices']].mean(dim=0) # Aggregated context
target_soft = labels_soft[t['target_index']]
entropy = -torch.sum(target_soft * torch.log(target_soft + 1e-8)) # Lower is better
downstream_loss += entropy # Or use as reward (negative)
downstream_loss /= len(tuples) or 1
# Measure 3: Mutual Predictability (cross-entropy between context avg and target)
mutual_loss = 0.0
for t in tuples:
context_soft = labels_soft[t['context_indices']].mean(dim=0).unsqueeze(0)
target_soft = labels_soft[t['target_index']].unsqueeze(0)
mutual_loss += F.cross_entropy(context_soft, target_soft)
mutual_loss /= len(tuples) or 1
# Combine weighted consistencies
consistency_loss = (weights[0] * direct_loss + weights[1] * downstream_loss + weights[2] * mutual_loss) / weights.sum()
# Pairwise Ranking Loss (on logprob differences, e.g., per-tuple pairs)
ranking_loss = 0.0
rank_loss_fn = nn.MarginRankingLoss(margin=margin)
for t in tuples:
# Pair: LLM pred_yes vs. pred_no; target=1 if learnable leans yes
score_yes = torch.tensor(t['llm_pred'][0]).log() - torch.tensor(t['llm_pred'][1]).log() # Logprob diff
score_no = -score_yes # Opposite
target = 1 if labels_soft[t['target_index'], 0] > 0.5 else -1
ranking_loss += rank_loss_fn(score_yes, score_no, torch.tensor(target))
ranking_loss /= len(tuples) or 1
# Prior Regularization (KL to calibrated priors)
prior_loss = F.kl_div(F.log_softmax(labels, dim=1), priors, reduction='batchmean')
# Total loss
loss = consistency_loss + 0.5 * ranking_loss + 0.1 * prior_loss # Tune coefficients
return loss
# Optimize with LBFGS for fast convergence
optimizer = torch.optim.LBFGS([labels, consistency_weights], lr=0.1, max_iter=20)
for epoch in range(10):
def closure():
optimizer.zero_grad()
loss = coherence_loss(labels, tuples, priors)
loss.backward()
return loss
optimizer.step(closure)
print(f"Epoch {epoch}: Loss {closure().item()}")
# Final: argmax labels
final_labels = torch.argmax(F.softmax(labels, dim=1), dim=1)
```
- **Notes on the Code**:
- **Customization**: Add more measures (e.g., logical constraints as `if label_i >0.5 and label_j >0.5: penalty +=1`). Tune margins/coeffs.
- **Efficiency**: LBFGS converges 2-5x faster (often in 5-20 steps) on small setups. If learnable weights cause issues (cheating), fix them and sweep.
- **Testing**: Run on a small subset (e.g., 10 labels, 50 tuples) to debug.
This should give you a solid starting point—flexible, incorporates your ideas, and ties back to the paper's coherence maximization. If you test it and share results (or need tweaks, like adding more terms), let me know! 😊
## Further specific implementation details
We can take `nbs/05_backprop.py` which loads `Path("outputs/icm/evidence_test_predictions.jsonl")` which are the LLM predictions over TruthfulQA. Now we can keep it simple, and keep the loss function flexible and hackable (without being defensive about expections ,we can fix them instead of worry about them).
So first lets plan, if you took at `nbs/05_backprop.py` you see it loads the LLM predictions, and then creates a `tuples` list of dicts with keys. Could you strip out what we don't need, and instead add this pytorch backprop idea, with multiple things wighted in the loss functions? How would you do it, high level?
+318 -6
View File
@@ -12,10 +12,12 @@ import numpy as np
import pandas as pd
from loguru import logger
from collections import defaultdict
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
logger.info("Imports complete")
# %% [code]
# Load TruthfulQA data with labels
from src.data.truthfulqa import load_truthfulqa
@@ -32,13 +34,18 @@ data[0]
# %%
# Save predictions as JSONL
output_path = Path("../outputs/icm/evidence_test_predictions.jsonl")
output_path = Path("outputs/icm/evidence_test_predictions.jsonl")
preds = list(srsly.read_jsonl(output_path))
df_preds = pd.DataFrame(preds)
preds[0]
# replace with outputs.
output_path = Path("./outputs/icm/truthfulqa/icm_final_labels.parquet")
# Index(['uid', 'prompt', 'vanilla_label', 'consistency_id', 'consistency_key', 'label', 'score'],
df_preds = pd.read_parquet(output_path)
df_preds
# {'target_uid': 20,
# 'target_idx': 20,
# 'score': 0.9999999999991981,
@@ -52,4 +59,309 @@ preds[0]
# ... (more context entries)
# ],
# 'variations': {'reversed': False, 'reordered': True}}
# %%
# %% [code]
# Prep data for backprop
# Create DF from original data for joining
df_data = pd.DataFrame(data)
df_data.set_index('uid', inplace=True)
# Extract unique uids (assuming all from data)
unique_uids = sorted(df_data.index.tolist())
num_labels = len(unique_uids)
uid_to_idx = {uid: idx for idx, uid in enumerate(unique_uids)}
# Compute priors: avg raw_logprob_diff per target_uid
priors_diff = defaultdict(list)
for pred in preds:
target_uid = pred['target_uid']
diff = pred['raw_logprob_diff']
priors_diff[target_uid].append(diff)
# Average diffs, default to 0 if no preds
# FIXME should we say mean /std
avg_diffs = {uid: np.mean(priors_diff.get(uid, [0.0])) / (1+np.std(priors_diff.get(uid, [1.0]))) for uid in unique_uids}
scale = 10.0 # Normalize large logprobs
prior_logits = torch.tensor([[ -d / scale, d / scale ] for d in avg_diffs.values()]) # [logit_0, logit_1]
# priors = F.softmax(prior_logits, dim=1) # Soft priors [num_labels, 2]
priors = prior_logits
# Build tuples: subsample to 500 for speed
tuples = []
for pred in preds: # Subsample
context_uids = [c['uid'] for c in pred['context']]
valid_context = [u for u in context_uids if u in uid_to_idx] # Filter valid
if len(valid_context) > 0 and pred['target_uid'] in uid_to_idx:
tuples.append({
'context_uids': valid_context,
'target_uid': pred['target_uid'],
'llm_pred_diff': pred['raw_logprob_diff'],
'llm_pred_score': pred['score'] # Proxy for prob_1
})
# Join with original data for consistency_id (add to tuples or separate)
# For now, create consistency_groups: dict of lists of uids per consistency_id
consistency_groups = defaultdict(list)
for uid, row in df_data.iterrows():
consistency_groups[row['consistency_id']].append(uid)
print(f"Num labels: {num_labels}, Num tuples: {len(tuples)}")
print(f"Sample prior: {priors[0]}")
print(f"Sample tuple: {tuples[0] if tuples else 'None'}")
# %% [code]
# Define learnables
# Learnable labels: logits for binary [0,1] per uid
labels = nn.Parameter(prior_logits)
# Fixed weights for loss terms (hackable: adjust here)
loss_weights = {
'mutual': 1.0,
'ranking': 0.5,
'prior': 0.1,
'direct': 0.5,
'entropy': 0.1
}
print(f"Labels shape: {labels.shape}")
print(f"Loss weights: {loss_weights}")
# %% [code]
# Define modular loss function
def coherence_loss(labels, tuples, priors, consistency_groups, uid_to_idx, loss_weights, scale=1000.0, verbose=False):
"""
Custom loss for unsupervised label optimization.
Combines multiple terms to encourage coherence:
- Mutual Predictability: Context labels should predict target (CE loss).
- Pairwise Ranking: Align learned rankings with LLM logprob diffs.
- Prior KL: Anchor to LLM priors.
- Direct Consistency: Low variance within groups.
- Entropy: Encourage confident (low-entropy) labels.
Weighted sum for flexibility; adjust loss_weights dict to tune.
"""
# FIXME learning weightson the loss can lead to rewards hacking, e.g. learn 1 for easy loss, and 0 for hard ones
soft_labels = F.softmax(labels, dim=1) # [num_labels, 2]
rank_loss_fn = nn.MarginRankingLoss(margin=1.0)
total_loss = 0.0
num_tuples = len(tuples)
terms = {}
# Build graph: lists for directed edges (context -> target) with weights
# No extra deps; use loop-based aggregation for small num_labels (~800)
num_nodes = soft_labels.shape[0]
weighted_context = torch.zeros_like(soft_labels) # [num_nodes, 2]
degrees = torch.zeros(num_nodes) # For normalization
for t in tuples:
target_idx = uid_to_idx[t['target_uid']]
context_indices = torch.tensor([uid_to_idx[u] for u in t['context_uids']])
if len(context_indices) == 0:
continue
# Offline weighting: closeness to majority (L2 dist to mean context soft)
context_soft_local = soft_labels[context_indices] # [n_context, 2]
majority = context_soft_local.mean(dim=0)
dists = torch.norm(context_soft_local - majority.unsqueeze(0), dim=1)
weights = 1.0 / (dists + 1e-5) # Higher for closer
weights = weights / weights.sum() # Normalize
# Aggregate: weighted sum to target
weighted_context[target_idx] += (soft_labels[context_indices] * weights.unsqueeze(1)).sum(dim=0)
degrees[target_idx] += 1.0 # Count incoming (simple, since weights normalized per tuple)
# Normalize (avg if degrees >0)
mask = degrees > 0
weighted_context[mask] /= degrees[mask].unsqueeze(1)
# Updated Mutual: Use weighted_context for CE
mutual_loss = 0.0
for t in tuples:
target_idx = uid_to_idx[t['target_uid']]
if degrees[target_idx] == 0:
continue
context_agg = weighted_context[target_idx]
target_soft = soft_labels[target_idx]
# Forward
mutual_loss += F.cross_entropy(context_agg.unsqueeze(0), target_soft.unsqueeze(0))
# Reverse
mutual_loss += F.cross_entropy(target_soft.unsqueeze(0), context_agg.unsqueeze(0))
terms['mutual'] = mutual_loss / max(num_tuples, 1) / 2
# Pairwise Ranking: Enforce ranking on learnable diff vs LLM pred diff
# Intuition: LLM logprobs are better for relative rankings than absolute probs; ensure learned prob ranking matches LLM's scaled logprob diff.
# Simple English: "The LLM ranked 'yes' higher than 'no' for this example—make sure your learned label agrees on which is stronger, with a safety margin to avoid ties. Like forcing your guesses to match the model's confidence order, not exact numbers."
ranking_loss = 0.0
for t in tuples:
target_idx = uid_to_idx[t['target_uid']]
llm_diff = t['llm_pred_diff'] / scale
score1 = soft_labels[target_idx, 1] # Prob 1
score2 = soft_labels[target_idx, 0] # Prob 0
target_rank = 1 if llm_diff > 0 else -1
if target_rank == -1:
ranking_loss += rank_loss_fn(score2, score1, torch.tensor(1.0))
else:
ranking_loss += rank_loss_fn(score1, score2, torch.tensor(1.0))
terms['ranking'] = ranking_loss / max(num_tuples, 1)
# Prior KL: Pull toward priors
# Intuition: Anchor learned labels to fixed LLM priors (averaged logprob diffs) via KL on logits to prevent drift from model's initial biases.
# Simple English: "Don't stray too far from the model's original hunches (its logprob biases for each example). Pull labels back toward these starting points gently, like a rubber band keeping you grounded in what the model already 'knows'."
prior_loss = F.kl_div(F.log_softmax(labels, dim=1), priors, reduction='batchmean')
terms['prior'] = prior_loss
# Direct Consistency: Penalize variance within consistency groups
# Intuition: Labels in the same group (e.g., related questions via consistency_id) should agree; penalize soft label variance for local stability.
# Simple English: "Related examples (like paraphrases) should have similar labels—don't let them vary wildly. Average their probabilities and punish if they're all over the place, ensuring the model doesn't contradict itself on similar stuff."
direct_loss = 0.0
num_groups = 0
for group_uids in consistency_groups.values():
group_indices = [uid_to_idx.get(u) for u in group_uids if u in uid_to_idx]
if len(group_indices) > 1:
group_soft = soft_labels[torch.tensor(group_indices)]
direct_loss += group_soft.var(dim=0).mean()
num_groups += 1
terms['direct'] = direct_loss / max(num_groups, 1)
# Entropy: Penalize high entropy in targets (encourage confident labels)
# Intuition: Reward low-entropy (decisive) labels for targets, proxying downstream confidence from coherent propagation.
# Simple English: "Make labels decisive (mostly yes or no, not 50/50 unsure). For each target, calculate how 'spread out' its probability is and add a small penalty if it's too wishy-washy—pushes toward bold, consistent choices that build confidence across predictions."
entropy_loss = 0.0
for t in tuples:
target_idx = uid_to_idx[t['target_uid']]
target_soft = soft_labels[target_idx]
entropy = -(target_soft * torch.log(target_soft + 1e-8)).sum()
entropy_loss += entropy
terms['entropy'] = entropy_loss / max(num_tuples, 1)
# New: Reward context for good evidence
reward_loss = 0.0
for t in tuples:
target_idx = uid_to_idx[t['target_uid']]
context_indices = torch.tensor([uid_to_idx[u] for u in t['context_uids']])
if len(context_indices) == 0:
continue
target_soft = soft_labels[target_idx]
target_entropy = -(target_soft * torch.log(target_soft + 1e-8)).sum()
reward = 1.0 / (target_entropy + 1e-5) # Higher for confident target
context_soft = soft_labels[context_indices]
context_entropy = -(context_soft * torch.log(context_soft + 1e-8)).sum(dim=1).mean()
reward_loss += -reward * context_entropy # Reward low context entropy
terms['reward'] = reward_loss / max(num_tuples, 1)
# Weighted sum (include 'reward')
weighted_loss = sum(loss_weights.get(k, 0.0) * terms[k] for k in terms)
total_loss = weighted_loss / (sum(loss_weights.values()) + loss_weights.get('reward', 0.0) or 1e-5)
# For hacking: print terms
if torch.is_grad_enabled() and verbose:
print(f"Loss terms: { {k: v.item() if hasattr(v, 'item') else v for k,v in terms.items()} }")
return total_loss
# Test with dummy
dummy_loss = coherence_loss(labels, tuples[:1], priors, consistency_groups, uid_to_idx, loss_weights)
print(f"Sample loss: {dummy_loss.item()}")
# %% [code]
def run_backprop_experiment(labels, tuples, priors, consistency_groups, uid_to_idx, loss_weights, df_data, df_preds, unique_uids, avg_diffs):
"""
Run backprop experiment with given weights, return metrics.
"""
# Copy labels
current_labels = labels.clone().detach().requires_grad_(True)
# optimizer = optim.LBFGS([current_labels], lr=0.1, max_iter=20)
optimizer = optim.AdamW([current_labels], lr=0.1)
losses = []
epochs = 10
for epoch in tqdm(range(epochs), desc="Optimizing"):
def closure():
optimizer.zero_grad()
loss = coherence_loss(current_labels, tuples, priors, consistency_groups, uid_to_idx, loss_weights, verbose=False)
loss.backward()
return loss
loss_val = optimizer.step(closure)
losses.append(loss_val.item())
# Post-process
final_soft = F.softmax(current_labels, dim=1)
final_hard = torch.argmax(final_soft, dim=1).cpu().numpy()
# Output DF in memory
output_data = []
for uid in unique_uids:
idx = uid_to_idx[uid]
row = df_data.loc[uid].to_dict()
row['uid'] = uid
row['learned_soft_0'] = float(final_soft[idx, 0].detach())
row['learned_soft_1'] = float(final_soft[idx, 1].detach())
row['learned_hard'] = int(final_hard[idx])
row['prior_diff'] = avg_diffs.get(uid, 0.0)
output_data.append(row)
df_output = pd.DataFrame(output_data)
acc = (df_output['learned_hard'] == df_output['vanilla_label']).mean() if 'vanilla_label' in df_output.columns else 0.0
# LLM acc
llm_acc = 0.0
if not df_preds.empty and 'target_uid' in df_preds.columns and 'vanilla_label' in df_data.columns:
df_preds_local = df_preds.copy()
df_preds_local['hard_llm_pred'] = (df_preds_local['raw_logprob_diff'] > 0).astype(int)
common_preds = df_preds_local.merge(df_data.reset_index()[['uid', 'vanilla_label']], left_on='target_uid', right_on='uid', how='inner')
if not common_preds.empty:
llm_acc = (common_preds['hard_llm_pred'] == common_preds['vanilla_label']).mean()
# ICM corr
icm_corr = 0.0
output_dir = Path("outputs/backprop")
icm_path = output_dir.parent / "icm/truthfulqa/icm_final_labels.parquet"
if icm_path.exists():
df_icm = pd.read_parquet(icm_path)
if 'uid' in df_icm.columns and 'label' in df_icm.columns:
df_icm.set_index('uid', inplace=True)
common_uids = set(df_output['uid']).intersection(df_icm.index)
if common_uids and len(common_uids) > 1:
backprop_hard_common = df_output[df_output['uid'].isin(common_uids)]['learned_hard'].values
icm_labels_common = df_icm.loc[list(common_uids), 'label'].values
icm_corr = np.corrcoef(backprop_hard_common, icm_labels_common)[0, 1]
return {
'acc': acc,
'icm_corr': icm_corr,
'final_loss': losses[-1] if losses else 0.0,
'llm_acc': llm_acc
}
# %% [code]
# Experiment with loss weights: Loop over variations, print key results
# Baseline weights
base_weights = {
'mutual': 1.0,
'ranking': 0.5,
'prior': 0.1,
'direct': 0.5,
'entropy': 0.1,
'reward': 0.2
}
# Example: Vary mutual weight (one at a time, as suggested)
mutual_variations = [0.1, 0.5, 1.0, 2.0]
results = {}
for mw in mutual_variations:
weights = base_weights.copy()
weights['mutual'] = mw
print(f"\n--- Testing mutual_weight = {mw} ---")
res = run_backprop_experiment(labels, tuples, priors, consistency_groups, uid_to_idx, weights, df_data, df_preds, unique_uids, avg_diffs)
results[mw] = res
print(f"Mutual {mw}: Acc {res['acc']:.4f}, ICM Corr {res['icm_corr']:.4f}, Final Loss {res['final_loss']:.4f}, LLM Acc {res['llm_acc']:.4f}")
+1
View File
@@ -40,6 +40,7 @@ dependencies = [
"tenacity>=9.1.2",
"termcolor>=3.1.0",
"tiktoken>=0.11.0",
"torch>=2.9.0",
"tqdm>=4.67.1",
"trueskill>=0.4.5",
"typer[all]>=0.19.2",
+1 -1
View File
@@ -10,7 +10,7 @@ def is_consistent(group_uids, demos):
# Get labeled items only
labeled = [(uid, demos[uid]['label'], demos[uid]['consistency_key'])
for uid in group_uids if demos[uid]['label'] is not None]
for uid in group_uids if demos[uid].get('label', None) is not None]
if len(labeled) < 2:
return True # Can't be inconsistent with <2 labels
+35 -5
View File
@@ -19,7 +19,7 @@ from dataclasses import dataclass, asdict
import dotenv
from loguru import logger
from openrouter_wrapper.logprobs import openrouter_completion_wlogprobs, get_logprobs_choices, LogprobsNotSupportedError # User's wrapper
from typing import List, Optional, Tuple, Callable, Literal
from typing import List, Optional, Tuple, Callable, Literal, Any
import asyncio
from aiocache import cached
from itertools import combinations
@@ -79,6 +79,15 @@ class Config:
out_dir: Path = Path("./outputs/icm") # Directory to save outputs
@dataclass
class PredictionLog:
uid: str
target: int
score: float
pred_label: bool
context: List[List[Any]]
import simple_parsing
C: Config = simple_parsing.parse(Config)
@@ -140,6 +149,13 @@ logger.info("Initialized labels: {}", {k: v['pred_label'] for k, v in demonstrat
# %% [code]
# Predict label using in-context prompting
@dataclass
class PredictionLog:
uid: str
target: int
score: float
pred_label: bool
context: List[List[PredictionLog]]
def print_messages(messages):
return "\n".join([f"**{m['role'].upper()}**: {m['content']}" for m in messages])
@@ -224,11 +240,15 @@ Reasoning for UID {example_uid}, labelled {len(labeled)}:
logger.warning(f"Choices not returned for UID {example_uid}, may indicate model confusion. choice_logp={choice_logp}. Instead we got these top logprobs: {top_logp} and \nmessages: ...`{print_messages(messages)[-90:]}`\nthis output:`{model_response}`")
score = choice_logp["A"] - choice_logp["B"]
predicted = 1 if score > 0 else 0
return predicted, float(score)
context = [PredictionLog(uid=demo['uid'], target=demo['prompt'], score=demo['score'], pred_label=demo['pred_label'], context=[]) for demo in relevant_demos]
pred_log = PredictionLog(uid=example_uid, target=predicted, score=float(score), pred_label=bool(predicted), context=context)
return predicted, float(score), pred_log
except Exception as e:
raise e
logger.exception(f"API error: {e}")
return random.choice([0, 1]), 0.0
return random.choice([0, 1]), 0.0, None
# %% [code]
@@ -424,13 +444,19 @@ async def run_icm(demonstrations, config=C):
tasks = [predict_label(uid, current_labeled, config, verbose=verbose, all_demos=demonstrations)
for uid in candidate_uids]
results = await asyncio.gather(*tasks)
with open(out_dir / "predictions.jsonl", "a") as f:
for _, _, pred_log in results:
if pred_log is not None:
json.dump(asdict(pred_log), f)
f.write('\n')
# Evaluate each candidate's energy delta
best_uid = None
best_delta = float('-inf')
best_temp_demos = None
for uid, (new_label, score) in zip(candidate_uids, results):
for uid, (new_label, score, _) in zip(candidate_uids, results):
temp_demos = deepcopy(demonstrations)
temp_demos[uid]['pred_label'] = new_label
temp_demos[uid]['score'] = score
@@ -475,6 +501,8 @@ async def run_icm(demonstrations, config=C):
logger.info("Stopping early.")
except asyncio.CancelledError:
logger.info("Asyncio task cancelled.")
return demonstrations, energies, accuracies
@@ -482,7 +510,7 @@ async def run_icm(demonstrations, config=C):
# Run the algorithm
try:
final_demos, energies, accuracies = asyncio.run(run_icm(demonstrations, C))
except Exception as e:
except (asyncio.CancelledError, KeyboardInterrupt) as e:
logger.exception(f"Error during ICM run: {e}")
final_demos = demonstrations
energies = []
@@ -502,6 +530,7 @@ logger.info("Inconsistencies: {}", final_metrics['num_inconsistent'])
df = pd.DataFrame(final_demos).T
df.to_parquet(out_dir / "icm_final_labels.parquet")
df_labeled = df.dropna(subset='pred_label').sort_values(by='score', key=np.abs, ascending=False)
df_labeled_disagreed = df_labeled[df_labeled['vanilla_label'] != df_labeled['pred_label']]
@@ -512,6 +541,7 @@ for uid, row in df_labeled_disagreed.iterrows():
print(f"\n## Candidate: {row['prompt']}\nICM Set: {'A' if row['pred_label']==1 else 'B'}, Vanilla Set: {'A' if row['vanilla_label']==1 else 'B'}, score={row['score']}\n")
print(f"\nFinal labeled examples saved to {out_dir / 'icm_final_labels.parquet'}")
print(f"Predictions log saved to {out_dir / 'predictions.jsonl'}")
# %% [code]
# Simple visualization (requires matplotlib)
Generated
+269
View File
@@ -1833,6 +1833,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/29/40/646448b5ad66efec097471bd5ab25f5b08360e3f34aecbe5c4fcc6845c01/mistralai-1.9.10-py3-none-any.whl", hash = "sha256:cf0a2906e254bb4825209a26e1957e6e0bacbbe61875bd22128dc3d5d51a7b0a", size = 440538, upload-time = "2025-09-02T07:44:37.5Z" },
]
[[package]]
name = "mpmath"
version = "1.3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e0/47/dd32fa426cc72114383ac549964eecb20ecfd886d1e5ccf5340b55b02f57/mpmath-1.3.0.tar.gz", hash = "sha256:7a28eb2a9774d00c7bc92411c19a89209d5da7c4c9a9e227be8330a23a25b91f", size = 508106, upload-time = "2023-03-07T16:47:11.061Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/43/e3/7d92a15f894aa0c9c4b49b8ee9ac9850d6e63b03c9c32c0367a13ae62209/mpmath-1.3.0-py3-none-any.whl", hash = "sha256:a0b2b9fe80bbcd81a6647ff13108738cfb482d481d826cc0e02f5b35e5c88d2c", size = 536198, upload-time = "2023-03-07T16:47:09.197Z" },
]
[[package]]
name = "multidict"
version = "6.6.4"
@@ -1971,6 +1980,34 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/a0/c4/c2971a3ba4c6103a3d10c4b0f24f461ddc027f0f09763220cf35ca1401b3/nest_asyncio-1.6.0-py3-none-any.whl", hash = "sha256:87af6efd6b5e897c81050477ef65c62e2b2f35d51703cae01aff2905b1852e1c", size = 5195, upload-time = "2024-01-21T14:25:17.223Z" },
]
[[package]]
name = "networkx"
version = "3.4.2"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version < '3.11' and sys_platform == 'linux'",
"python_full_version < '3.11' and sys_platform != 'linux'",
]
sdist = { url = "https://files.pythonhosted.org/packages/fd/1d/06475e1cd5264c0b870ea2cc6fdb3e37177c1e565c43f56ff17a10e3937f/networkx-3.4.2.tar.gz", hash = "sha256:307c3669428c5362aab27c8a1260aa8f47c4e91d3891f48be0141738d8d053e1", size = 2151368, upload-time = "2024-10-21T12:39:38.695Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b9/54/dd730b32ea14ea797530a4479b2ed46a6fb250f682a9cfb997e968bf0261/networkx-3.4.2-py3-none-any.whl", hash = "sha256:df5d4365b724cf81b8c6a7312509d0c22386097011ad1abe274afd5e9d3bbc5f", size = 1723263, upload-time = "2024-10-21T12:39:36.247Z" },
]
[[package]]
name = "networkx"
version = "3.5"
source = { registry = "https://pypi.org/simple" }
resolution-markers = [
"python_full_version >= '3.12' and sys_platform == 'linux'",
"python_full_version >= '3.12' and sys_platform != 'linux'",
"python_full_version == '3.11.*' and sys_platform == 'linux'",
"python_full_version == '3.11.*' and sys_platform != 'linux'",
]
sdist = { url = "https://files.pythonhosted.org/packages/6c/4f/ccdb8ad3a38e583f214547fd2f7ff1fc160c43a75af88e6aec213404b96a/networkx-3.5.tar.gz", hash = "sha256:d4c6f9cf81f52d69230866796b82afbccdec3db7ae4fbd1b65ea750feed50037", size = 2471065, upload-time = "2025-05-29T11:35:07.804Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/eb/8d/776adee7bbf76365fdd7f2552710282c79a4ead5d2a46408c9043a2b70ba/networkx-3.5-py3-none-any.whl", hash = "sha256:0030d386a9a06dee3565298b4a734b68589749a544acbb6c412dc9e2489ec6ec", size = 2034406, upload-time = "2025-05-29T11:35:04.961Z" },
]
[[package]]
name = "nodeenv"
version = "1.9.1"
@@ -2133,6 +2170,140 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/af/11/0cc63f9f321ccf63886ac203336777140011fb669e739da36d8db3c53b98/numpy-2.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:2e267c7da5bf7309670523896df97f93f6e469fb931161f483cd6882b3b1a5dc", size = 12971844, upload-time = "2025-09-09T15:58:57.359Z" },
]
[[package]]
name = "nvidia-cublas-cu12"
version = "12.8.4.1"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/dc/61/e24b560ab2e2eaeb3c839129175fb330dfcfc29e5203196e5541a4c44682/nvidia_cublas_cu12-12.8.4.1-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:8ac4e771d5a348c551b2a426eda6193c19aa630236b418086020df5ba9667142", size = 594346921, upload-time = "2025-03-07T01:44:31.254Z" },
]
[[package]]
name = "nvidia-cuda-cupti-cu12"
version = "12.8.90"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f8/02/2adcaa145158bf1a8295d83591d22e4103dbfd821bcaf6f3f53151ca4ffa/nvidia_cuda_cupti_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ea0cb07ebda26bb9b29ba82cda34849e73c166c18162d3913575b0c9db9a6182", size = 10248621, upload-time = "2025-03-07T01:40:21.213Z" },
]
[[package]]
name = "nvidia-cuda-nvrtc-cu12"
version = "12.8.93"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/05/6b/32f747947df2da6994e999492ab306a903659555dddc0fbdeb9d71f75e52/nvidia_cuda_nvrtc_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:a7756528852ef889772a84c6cd89d41dfa74667e24cca16bb31f8f061e3e9994", size = 88040029, upload-time = "2025-03-07T01:42:13.562Z" },
]
[[package]]
name = "nvidia-cuda-runtime-cu12"
version = "12.8.90"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0d/9b/a997b638fcd068ad6e4d53b8551a7d30fe8b404d6f1804abf1df69838932/nvidia_cuda_runtime_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:adade8dcbd0edf427b7204d480d6066d33902cab2a4707dcfc48a2d0fd44ab90", size = 954765, upload-time = "2025-03-07T01:40:01.615Z" },
]
[[package]]
name = "nvidia-cudnn-cu12"
version = "9.10.2.21"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux'" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/ba/51/e123d997aa098c61d029f76663dedbfb9bc8dcf8c60cbd6adbe42f76d049/nvidia_cudnn_cu12-9.10.2.21-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:949452be657fa16687d0930933f032835951ef0892b37d2d53824d1a84dc97a8", size = 706758467, upload-time = "2025-06-06T21:54:08.597Z" },
]
[[package]]
name = "nvidia-cufft-cu12"
version = "11.3.3.83"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/1f/13/ee4e00f30e676b66ae65b4f08cb5bcbb8392c03f54f2d5413ea99a5d1c80/nvidia_cufft_cu12-11.3.3.83-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d2dd21ec0b88cf61b62e6b43564355e5222e4a3fb394cac0db101f2dd0d4f74", size = 193118695, upload-time = "2025-03-07T01:45:27.821Z" },
]
[[package]]
name = "nvidia-cufile-cu12"
version = "1.13.1.3"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/bb/fe/1bcba1dfbfb8d01be8d93f07bfc502c93fa23afa6fd5ab3fc7c1df71038a/nvidia_cufile_cu12-1.13.1.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1d069003be650e131b21c932ec3d8969c1715379251f8d23a1860554b1cb24fc", size = 1197834, upload-time = "2025-03-07T01:45:50.723Z" },
]
[[package]]
name = "nvidia-curand-cu12"
version = "10.3.9.90"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/fb/aa/6584b56dc84ebe9cf93226a5cde4d99080c8e90ab40f0c27bda7a0f29aa1/nvidia_curand_cu12-10.3.9.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:b32331d4f4df5d6eefa0554c565b626c7216f87a06a4f56fab27c3b68a830ec9", size = 63619976, upload-time = "2025-03-07T01:46:23.323Z" },
]
[[package]]
name = "nvidia-cusolver-cu12"
version = "11.7.3.90"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "nvidia-cublas-cu12", marker = "sys_platform == 'linux'" },
{ name = "nvidia-cusparse-cu12", marker = "sys_platform == 'linux'" },
{ name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/85/48/9a13d2975803e8cf2777d5ed57b87a0b6ca2cc795f9a4f59796a910bfb80/nvidia_cusolver_cu12-11.7.3.90-py3-none-manylinux_2_27_x86_64.whl", hash = "sha256:4376c11ad263152bd50ea295c05370360776f8c3427b30991df774f9fb26c450", size = 267506905, upload-time = "2025-03-07T01:47:16.273Z" },
]
[[package]]
name = "nvidia-cusparse-cu12"
version = "12.5.8.93"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "nvidia-nvjitlink-cu12", marker = "sys_platform == 'linux'" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/c2/f5/e1854cb2f2bcd4280c44736c93550cc300ff4b8c95ebe370d0aa7d2b473d/nvidia_cusparse_cu12-12.5.8.93-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:1ec05d76bbbd8b61b06a80e1eaf8cf4959c3d4ce8e711b65ebd0443bb0ebb13b", size = 288216466, upload-time = "2025-03-07T01:48:13.779Z" },
]
[[package]]
name = "nvidia-cusparselt-cu12"
version = "0.7.1"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/56/79/12978b96bd44274fe38b5dde5cfb660b1d114f70a65ef962bcbbed99b549/nvidia_cusparselt_cu12-0.7.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:f1bb701d6b930d5a7cea44c19ceb973311500847f81b634d802b7b539dc55623", size = 287193691, upload-time = "2025-02-26T00:15:44.104Z" },
]
[[package]]
name = "nvidia-nccl-cu12"
version = "2.27.5"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/6e/89/f7a07dc961b60645dbbf42e80f2bc85ade7feb9a491b11a1e973aa00071f/nvidia_nccl_cu12-2.27.5-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ad730cf15cb5d25fe849c6e6ca9eb5b76db16a80f13f425ac68d8e2e55624457", size = 322348229, upload-time = "2025-06-26T04:11:28.385Z" },
]
[[package]]
name = "nvidia-nvjitlink-cu12"
version = "12.8.93"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f6/74/86a07f1d0f42998ca31312f998bd3b9a7eff7f52378f4f270c8679c77fb9/nvidia_nvjitlink_cu12-12.8.93-py3-none-manylinux2010_x86_64.manylinux_2_12_x86_64.whl", hash = "sha256:81ff63371a7ebd6e6451970684f916be2eab07321b73c9d244dc2b4da7f73b88", size = 39254836, upload-time = "2025-03-07T01:49:55.661Z" },
]
[[package]]
name = "nvidia-nvshmem-cu12"
version = "3.3.20"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3b/6c/99acb2f9eb85c29fc6f3a7ac4dccfd992e22666dd08a642b303311326a97/nvidia_nvshmem_cu12-3.3.20-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d00f26d3f9b2e3c3065be895e3059d6479ea5c638a3f38c9fec49b1b9dd7c1e5", size = 124657145, upload-time = "2025-08-04T20:25:19.995Z" },
]
[[package]]
name = "nvidia-nvtx-cu12"
version = "12.8.90"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a2/eb/86626c1bbc2edb86323022371c39aa48df6fd8b0a1647bc274577f72e90b/nvidia_nvtx_cu12-12.8.90-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5b17e2001cc0d751a5bc2c6ec6d26ad95913324a4adb86788c944f8ce9ba441f", size = 89954, upload-time = "2025-03-07T01:42:44.131Z" },
]
[[package]]
name = "omegaconf"
version = "2.3.0"
@@ -3452,6 +3623,15 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/dd/44/4356cc64246ba7b2b920f7c97a85c3c52748e213e250b512ee8152eb559d/sentry_sdk-2.39.0-py2.py3-none-any.whl", hash = "sha256:ba655ca5e57b41569b18e2a5552cb3375209760a5d332cdd87c6c3f28f729602", size = 370851, upload-time = "2025-09-25T09:15:36.35Z" },
]
[[package]]
name = "setuptools"
version = "80.9.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/18/5d/3bf57dcd21979b887f014ea83c24ae194cfcd12b9e0fda66b957c69d1fca/setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c", size = 1319958, upload-time = "2025-05-27T00:56:51.443Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a3/dc/17031897dae0efacfea57dfd3a82fdd2a2aeb58e0ff71b77b87e44edc772/setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922", size = 1201486, upload-time = "2025-05-27T00:56:49.664Z" },
]
[[package]]
name = "shellingham"
version = "1.5.4"
@@ -3607,6 +3787,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/58/f8/e2cca22387965584a409795913b774235752be4176d276714e15e1a58884/starlette-0.27.0-py3-none-any.whl", hash = "sha256:918416370e846586541235ccd38a474c08b80443ed31c578a418e2209b3eef91", size = 66978, upload-time = "2023-05-16T10:59:53.927Z" },
]
[[package]]
name = "sympy"
version = "1.14.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mpmath" },
]
sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2bb238361cd4ae6125a569b4db67cd9e79846ba2d68c/sympy-1.14.0.tar.gz", hash = "sha256:d3d3fe8df1e5a0b42f0e7bdf50541697dbe7d23746e894990c030e2b05e72517", size = 7793921, upload-time = "2025-04-27T18:05:01.611Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" },
]
[[package]]
name = "tabulate"
version = "0.9.0"
@@ -3718,6 +3910,67 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/6e/c2/61d3e0f47e2b74ef40a68b9e6ad5984f6241a942f7cd3bbfbdbd03861ea9/tomli-2.2.1-py3-none-any.whl", hash = "sha256:cb55c73c5f4408779d0cf3eef9f762b9c9f147a77de7b258bef0a5628adc85cc", size = 14257, upload-time = "2024-11-27T22:38:35.385Z" },
]
[[package]]
name = "torch"
version = "2.9.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "filelock" },
{ name = "fsspec" },
{ name = "jinja2" },
{ name = "networkx", version = "3.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" },
{ name = "networkx", version = "3.5", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" },
{ name = "nvidia-cublas-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-cuda-cupti-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-cuda-nvrtc-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-cuda-runtime-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-cudnn-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-cufft-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-cufile-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-curand-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-cusolver-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-cusparse-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-cusparselt-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-nccl-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-nvjitlink-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-nvshmem-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "nvidia-nvtx-cu12", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "setuptools", marker = "python_full_version >= '3.12'" },
{ name = "sympy" },
{ name = "triton", marker = "platform_machine == 'x86_64' and sys_platform == 'linux'" },
{ name = "typing-extensions" },
]
wheels = [
{ url = "https://files.pythonhosted.org/packages/bb/86/245c240d2138c17ed572c943c289056c2721abab70810d772c6bf5495b28/torch-2.9.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:030bbfe367379ae6a4ae4042b6c44da25383343b8b3c68abaa9c7231efbaf2dd", size = 104213554, upload-time = "2025-10-15T15:45:59.798Z" },
{ url = "https://files.pythonhosted.org/packages/58/1d/fd1e88ae0948825efcab7dd66d12bec23f05d4d38ed81573c8d453c14c06/torch-2.9.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:51cb63902182a78e90886e8068befd8ea102af4b00e420263591a3d70c7d3c6c", size = 899795167, upload-time = "2025-10-15T15:47:12.695Z" },
{ url = "https://files.pythonhosted.org/packages/63/5a/496197b45c14982bef4e079b24c61dc108e3ab0d0cc9718dba9f54f45a46/torch-2.9.0-cp310-cp310-win_amd64.whl", hash = "sha256:3f6aad4d2f0ee2248bac25339d74858ff846c3969b27d14ac235821f055af83d", size = 109310314, upload-time = "2025-10-15T15:46:16.633Z" },
{ url = "https://files.pythonhosted.org/packages/58/b0/2b4e647b0fc706e88eb6c253d05511865578f5f67b55fad639bf3272a4a1/torch-2.9.0-cp310-none-macosx_11_0_arm64.whl", hash = "sha256:413e1654c9203733138858780e184d9fc59442f0b3b209e16f39354eb893db9b", size = 74452019, upload-time = "2025-10-15T15:46:04.296Z" },
{ url = "https://files.pythonhosted.org/packages/58/fe/334225e6330e672b36aef23d77451fa906ea12881570c08638a91331a212/torch-2.9.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:c596708b5105d0b199215acf0c9be7c1db5f1680d88eddadf4b75a299259a677", size = 104230578, upload-time = "2025-10-15T15:46:08.182Z" },
{ url = "https://files.pythonhosted.org/packages/05/cc/49566caaa218872ec9a2912456f470ff92649894a4bc2e5274aa9ef87c4a/torch-2.9.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:51de31219c97c51cf4bf2be94d622e3deb5dcc526c6dc00e97c17eaec0fc1d67", size = 899815990, upload-time = "2025-10-15T15:48:03.336Z" },
{ url = "https://files.pythonhosted.org/packages/74/25/e9ab21d5925b642d008f139d4a3c9664fc9ee1faafca22913c080cc4c0a5/torch-2.9.0-cp311-cp311-win_amd64.whl", hash = "sha256:dd515c70059afd95f48b8192733764c08ca37a1d19803af6401b5ecad7c8676e", size = 109313698, upload-time = "2025-10-15T15:46:12.425Z" },
{ url = "https://files.pythonhosted.org/packages/b3/b7/205ef3e94de636feffd64b28bb59a0dfac0771221201b9871acf9236f5ca/torch-2.9.0-cp311-none-macosx_11_0_arm64.whl", hash = "sha256:614a185e4986326d526a91210c8fc1397e76e8cfafa78baf6296a790e53a9eec", size = 74463678, upload-time = "2025-10-15T15:46:29.779Z" },
{ url = "https://files.pythonhosted.org/packages/d1/d3/3985739f3b8e88675127bf70f82b3a48ae083e39cda56305dbd90398fec0/torch-2.9.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:e5f7af1dc4c0a7c4a260c2534f41ddaf209714f7c89145e644c44712fbd6b642", size = 104107898, upload-time = "2025-10-15T15:46:20.883Z" },
{ url = "https://files.pythonhosted.org/packages/a5/4b/f4bb2e6c25d0272f798cd6d7a04ed315da76cec68c602d87040c7847287f/torch-2.9.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:01cff95ecd9a212ea2f141db28acccdceb6a4c54f64e6c51091146f5e2a772c6", size = 899738273, upload-time = "2025-10-15T15:50:04.188Z" },
{ url = "https://files.pythonhosted.org/packages/66/11/c1c5ba6691cda6279087c35bd626536e4fd29521fe740abf5008377a9a02/torch-2.9.0-cp312-cp312-win_amd64.whl", hash = "sha256:4582b162f541651f0cb184d3e291c05c2f556c7117c64a9873e2ee158d40062b", size = 109280887, upload-time = "2025-10-15T15:46:26.228Z" },
{ url = "https://files.pythonhosted.org/packages/dd/5f/b85bd8c05312d71de9402bf5868d217c38827cfd09d8f8514e5be128a52b/torch-2.9.0-cp312-none-macosx_11_0_arm64.whl", hash = "sha256:33f58e9a102a91259af289d50525c30323b5c9ae1d31322b6447c0814da68695", size = 74478983, upload-time = "2025-10-15T15:46:39.406Z" },
{ url = "https://files.pythonhosted.org/packages/c2/1c/90eb13833cdf4969ea9707586d7b57095c3b6e2b223a7256bf111689bcb8/torch-2.9.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:c30a17fc83eeab346913e237c64b15b5ba6407fff812f6c541e322e19bc9ea0e", size = 104111330, upload-time = "2025-10-15T15:46:35.238Z" },
{ url = "https://files.pythonhosted.org/packages/0e/21/2254c54b8d523592c25ef4434769aa23e29b1e6bf5f4c0ad9e27bf442927/torch-2.9.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:8f25033b8667b57857dfd01458fbf2a9e6a6df1f8def23aef0dc46292f6aa642", size = 899750243, upload-time = "2025-10-15T15:48:57.459Z" },
{ url = "https://files.pythonhosted.org/packages/b7/a5/5cb94fa4fd1e78223455c23c200f30f6dc10c6d4a2bcc8f6e7f2a2588370/torch-2.9.0-cp313-cp313-win_amd64.whl", hash = "sha256:d037f1b4ffd25013be4a7bf3651a0a910c68554956c7b2c92ebe87c76475dece", size = 109284513, upload-time = "2025-10-15T15:46:45.061Z" },
{ url = "https://files.pythonhosted.org/packages/66/e8/fc414d8656250ee46120b44836ffbb3266343db424b3e18ca79ebbf69d4f/torch-2.9.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e4e5b5cba837a2a8d1a497ba9a58dae46fa392593eaa13b871c42f71847503a5", size = 74830362, upload-time = "2025-10-15T15:46:48.983Z" },
{ url = "https://files.pythonhosted.org/packages/ed/5f/9474c98fc5ae0cd04b9466035428cd360e6611a86b8352a0fc2fa504acdc/torch-2.9.0-cp313-cp313t-manylinux_2_28_aarch64.whl", hash = "sha256:64693568f5dc4dbd5f880a478b1cea0201cc6b510d91d1bc54fea86ac5d1a637", size = 104144940, upload-time = "2025-10-15T15:47:29.076Z" },
{ url = "https://files.pythonhosted.org/packages/2d/5a/8e0c1cf57830172c109d4bd6be2708cabeaf550983eee7029291322447a0/torch-2.9.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:f8ed31ddd7d10bfb3fbe0b9fe01b1243577f13d75e6f4a0839a283915ce3791e", size = 899744054, upload-time = "2025-10-15T15:48:29.864Z" },
{ url = "https://files.pythonhosted.org/packages/6d/28/82c28b30fcb4b7c9cdd995763d18bbb830d6521356712faebbad92ffa61d/torch-2.9.0-cp313-cp313t-win_amd64.whl", hash = "sha256:eff527d4e4846e6f70d2afd8058b73825761203d66576a7e04ea2ecfebcb4ab8", size = 109517546, upload-time = "2025-10-15T15:47:33.395Z" },
{ url = "https://files.pythonhosted.org/packages/ff/c3/a91f96ec74347fa5fd24453fa514bc61c61ecc79196fa760b012a1873d96/torch-2.9.0-cp313-none-macosx_11_0_arm64.whl", hash = "sha256:f8877779cf56d1ce431a7636703bdb13307f5960bb1af49716d8b179225e0e6a", size = 74480732, upload-time = "2025-10-15T15:47:38.002Z" },
{ url = "https://files.pythonhosted.org/packages/5c/73/9f70af34b334a7e0ef496ceec96b7ec767bd778ea35385ce6f77557534d1/torch-2.9.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:7e614fae699838038d888729f82b687c03413c5989ce2a9481f9a7e7a396e0bb", size = 74433037, upload-time = "2025-10-15T15:47:41.894Z" },
{ url = "https://files.pythonhosted.org/packages/b7/84/37cf88625901934c97109e583ecc21777d21c6f54cda97a7e5bbad1ee2f2/torch-2.9.0-cp314-cp314-manylinux_2_28_aarch64.whl", hash = "sha256:dfb5b8cd310ba3436c7e14e8b7833ef658cf3045e50d2bdaed23c8fc517065eb", size = 104116482, upload-time = "2025-10-15T15:47:46.266Z" },
{ url = "https://files.pythonhosted.org/packages/56/8e/ca8b17866943a8d4f4664d402ea84210aa274588b4c5d89918f5caa24eec/torch-2.9.0-cp314-cp314-manylinux_2_28_x86_64.whl", hash = "sha256:b3d29524993a478e46f5d598b249cd824b7ed98d7fba538bd9c4cde6c803948f", size = 899746916, upload-time = "2025-10-15T15:50:40.294Z" },
{ url = "https://files.pythonhosted.org/packages/43/65/3b17c0fbbdab6501c5b320a52a648628d0d44e7379f64e27d9eef701b6bf/torch-2.9.0-cp314-cp314-win_amd64.whl", hash = "sha256:71c7578984f5ec0eb645eb4816ac8435fcf3e3e2ae1901bcd2f519a9cafb5125", size = 109275151, upload-time = "2025-10-15T15:49:20.715Z" },
{ url = "https://files.pythonhosted.org/packages/83/36/74f8c051f785500396e42f93542422422dfd874a174f21f8d955d36e5d64/torch-2.9.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:71d9309aee457bbe0b164bce2111cd911c4ed4e847e65d5077dbbcd3aba6befc", size = 74823353, upload-time = "2025-10-15T15:49:16.59Z" },
{ url = "https://files.pythonhosted.org/packages/62/51/dc3b4e2f9ba98ae27238f0153ca098bf9340b2dafcc67fde645d496dfc2a/torch-2.9.0-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c08fb654d783899e204a32cca758a7ce8a45b2d78eeb89517cc937088316f78e", size = 104140340, upload-time = "2025-10-15T15:50:19.67Z" },
{ url = "https://files.pythonhosted.org/packages/c0/8d/b00657f8141ac16af7bb6cda2e67de18499a3263b78d516b9a93fcbc98e3/torch-2.9.0-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:ec8feb0099b2daa5728fbc7abb0b05730fd97e0f359ff8bda09865aaa7bd7d4b", size = 899731750, upload-time = "2025-10-15T15:49:36.673Z" },
{ url = "https://files.pythonhosted.org/packages/fc/29/bd361e0cbb2c79ce6450f42643aaf6919956f89923a50571b0ebfe92d142/torch-2.9.0-cp314-cp314t-win_amd64.whl", hash = "sha256:695ba920f234ad4170c9c50e28d56c848432f8f530e6bc7f88fcb15ddf338e75", size = 109503850, upload-time = "2025-10-15T15:50:24.118Z" },
]
[[package]]
name = "tornado"
version = "6.5.2"
@@ -3758,6 +4011,20 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/00/c0/8f5d070730d7836adc9c9b6408dec68c6ced86b304a9b26a14df072a6e8c/traitlets-5.14.3-py3-none-any.whl", hash = "sha256:b74e89e397b1ed28cc831db7aea759ba6640cb3de13090ca145426688ff1ac4f", size = 85359, upload-time = "2024-04-19T11:11:46.763Z" },
]
[[package]]
name = "triton"
version = "3.5.0"
source = { registry = "https://pypi.org/simple" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0b/eb/09e31d107a5d00eb281aa7e6635ca463e9bca86515944e399480eadb71f8/triton-3.5.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5d3b3d480debf24eaa739623c9a42446b0b77f95593d30eb1f64cd2278cc1f0", size = 170333110, upload-time = "2025-10-13T16:37:49.588Z" },
{ url = "https://files.pythonhosted.org/packages/3d/78/949a04391c21956c816523678f0e5fa308eb5b1e7622d88c4e4ef5fceca0/triton-3.5.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f34bfa21c5b3a203c0f0eab28dcc1e49bd1f67d22724e77fb6665a659200a4ec", size = 170433488, upload-time = "2025-10-13T16:37:57.132Z" },
{ url = "https://files.pythonhosted.org/packages/f5/3a/e991574f3102147b642e49637e0281e9bb7c4ba254edb2bab78247c85e01/triton-3.5.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9e71db82261c4ffa3921cd050cd5faa18322d2d405c30eb56084afaff3b0833", size = 170476535, upload-time = "2025-10-13T16:38:05.18Z" },
{ url = "https://files.pythonhosted.org/packages/6c/29/10728de8a6e932e517c10773486b8e99f85d1b1d9dd87d9a9616e1fef4a1/triton-3.5.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e6bb9aa5519c084a333acdba443789e50012a4b851cd486c54f0b8dc2a8d3a12", size = 170487289, upload-time = "2025-10-13T16:38:11.662Z" },
{ url = "https://files.pythonhosted.org/packages/5c/38/db80e48b9220c9bce872b0f616ad0446cdf554a40b85c7865cbca99ab3c2/triton-3.5.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c83f2343e1a220a716c7b3ab9fccfcbe3ad4020d189549200e2d2e8d5868bed9", size = 170577179, upload-time = "2025-10-13T16:38:17.865Z" },
{ url = "https://files.pythonhosted.org/packages/ff/60/1810655d1d856c9a4fcc90ee8966d85f552d98c53a6589f95ab2cbe27bb8/triton-3.5.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da0fa67ccd76c3dcfb0bffe1b1c57c685136a6bd33d141c24d9655d4185b1289", size = 170487949, upload-time = "2025-10-13T16:38:24.881Z" },
{ url = "https://files.pythonhosted.org/packages/fb/b7/1dec8433ac604c061173d0589d99217fe7bf90a70bdc375e745d044b8aad/triton-3.5.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:317fe477ea8fd4524a6a8c499fb0a36984a56d0b75bf9c9cb6133a1c56d5a6e7", size = 170580176, upload-time = "2025-10-13T16:38:31.14Z" },
]
[[package]]
name = "trueskill"
version = "0.4.5"
@@ -3850,6 +4117,7 @@ dependencies = [
{ name = "tenacity" },
{ name = "termcolor" },
{ name = "tiktoken" },
{ name = "torch" },
{ name = "tqdm" },
{ name = "trueskill" },
{ name = "typer" },
@@ -3897,6 +4165,7 @@ requires-dist = [
{ name = "tenacity", specifier = ">=9.1.2" },
{ name = "termcolor", specifier = ">=3.1.0" },
{ name = "tiktoken", specifier = ">=0.11.0" },
{ name = "torch", specifier = ">=2.9.0" },
{ name = "tqdm", specifier = ">=4.67.1" },
{ name = "trueskill", specifier = ">=0.4.5" },
{ name = "typer", extras = ["all"], specifier = ">=0.19.2" },