fix inconsistents

fix inconsistents as combinatorial with no llm calls
This commit is contained in:
wassname
2025-10-05 09:10:22 +08:00
parent 65fac40c12
commit e4baa1b6cd
6 changed files with 157 additions and 677 deletions
-560
View File
@@ -1,560 +0,0 @@
import asyncio
import json
import math
import os
import random
from collections import Counter
from copy import deepcopy
from tqdm import tqdm
import numpy as np
from datasets import load_dataset
import argparse
from core.llm_api.llm import ModelAPI
from core.utils import setup_environment
from src.experiments.ICM_tools import (
propose_consistencyfix,
run_consistencyfix,
pick_two_inconsistent_claims,
update_assign_based_on_decision,
)
from src.model_querying.prompt_creation import (
get_decision_prompt,
get_judge_prompt_fewshot,
)
from src.model_querying.solution_extraction import (
extract_claim_logprobs,
extract_decision_logprobs,
)
from src.pipeline.pipeline import Pipeline, PipelineConfig
from src.tools.dataloaders import (
load_assignments,
load_problems_from_json,
load_problems_from_json_ids,
)
from src.tools.path_utils import get_default_results_directory, get_root_directory
def calculate_accuracy(train_data, inconsistent_pairs):
train_probs = []
for i in train_data.values():
if i["label"] is None:
continue
if i["label"] == 1:
train_probs.append(i["score"])
else:
train_probs.append(-i["score"])
if len(train_probs) == 0:
train_prob = 0
else:
train_prob = np.mean(train_probs)
return {
"train_accuracy": 0
if len(train_data) == 0
else np.mean([i["label"] == i["vanilla_label"] for i in train_data.values()]),
"train_label_distribution": Counter(
[i["vanilla_label"] for i in train_data.values()]
),
"train_predict_distribution": Counter(
[i["label"] for i in train_data.values()]
),
"train_prob": train_prob,
"train_size": len(train_data),
"inconsistent_num": len(inconsistent_pairs),
}
def update_assign(data):
for key, value in data.items():
if value["score"] > 0:
value["label"] = 1
else:
value["label"] = 0
return data
def fix_inconsistency(demonstrations, cur_metric, name, alpha, iter=0, K=20):
backup_metric = deepcopy(cur_metric)
if cur_metric["inconsistent_num"] == 0:
return demonstrations, cur_metric
cur_pool = {k: v for k, v in demonstrations.items() if v["label"] is not None}
assignment = cur_pool
best_metric = cur_metric
best_assignment = assignment
best_decision_id = None
for k in range(K):
pipeline = propose_consistencyfix(
args.model,
name=name,
iter=f"{iter}-{k}",
assignment=assignment,
)
results = asyncio.run(pipeline.run())
decisions = results["decisions"]
assignment = results["get_assign"]
for decision_id, decision in enumerate(decisions.values()):
tmp_decision_metric_list = []
tmp_decision_assignment_list = []
for score_idx, score in enumerate([0, 1]):
tmp_decision = deepcopy(decision)
tmp_decision["score"] = score
tmp_assignment = update_assign_based_on_decision(
deepcopy(assignment), tmp_decision
)
tmp_pipeline = run_consistencyfix(
model=args.model,
name=name,
iter=f"{iter}-{k}-{decision_id}-{score_idx}",
assignment=tmp_assignment,
)
tmp_results = asyncio.run(tmp_pipeline.run())
tmp_metric = tmp_results["evaluate"]
tmp_decision_metric_list.append(tmp_metric)
tmp_decision_assignment_list.append(tmp_assignment)
tmp_best_decision_id = np.argmax(
[get_energy(i, args.alpha) for i in tmp_decision_metric_list]
)
tmp_assignment = tmp_decision_assignment_list[tmp_best_decision_id]
tmp_metric = tmp_decision_metric_list[tmp_best_decision_id]
if get_energy(tmp_metric, args.alpha) >= get_energy(best_metric, args.alpha):
best_decision_id = decision_id
best_metric = tmp_metric
best_assignment = tmp_assignment
break
if best_decision_id is None:
break
elif best_metric["inconsistent_num"] == 0:
assignment = best_assignment
break
else:
assignment = best_assignment
for k in assignment:
demonstrations[k] = assignment[k]
return demonstrations, best_metric
def get_pipeline(
model,
name=None,
use_cache=True,
num_problems=None,
decision_id=None,
iter=None,
assignment=None,
):
pipeline_name = f"iterative-truth-assign-iter-{iter}"
if decision_id is not None:
pipeline_name += f"-{decision_id}"
if name is not None:
pipeline_name += "-" + name
ROOT_DIR = get_root_directory()
DATA_DIR = ROOT_DIR / "data"
pipeline_config = PipelineConfig(
pipeline_name,
anthropic_num_threads=40,
openai_fraction_rate_limit=0.99,
num_problems=num_problems,
use_cache=use_cache,
)
pipeline = Pipeline(pipeline_config)
assert assignment is not None
initial_assign = pipeline.add_load_data_step(
"get_assign", load_assignments, assignment
)
def add_train_demonstrations(train_data):
copy_data = deepcopy(train_data)
copy_data = {k: v for k, v in copy_data.items() if v["label"] is not None}
keys = list(copy_data.keys())
values = list(copy_data.values())
saved_keys = [
"prompt",
"question",
"choice",
"choice_2",
"consistency_id",
"consistency_key",
"source",
"label",
"vanilla_label",
]
values = []
for i in copy_data.values():
values.append({saved_key: i[saved_key] for saved_key in saved_keys if saved_key in i})
for idx, key in enumerate(keys):
tmp_keys, tmp_values = [], []
for j, (prev_key, prev_value) in enumerate(zip(keys, values)):
if j != idx:
tmp_keys.append(prev_key)
tmp_values.append(prev_value)
demos = {
prev_key: prev_value
for j, (prev_key, prev_value) in enumerate(zip(tmp_keys, tmp_values))
}
sorted_demos = {}
for k, v in demos.items():
q = v["consistency_id"]
if q not in sorted_demos:
sorted_demos[q] = []
sorted_demos[q].append((k, v))
out_sorted_demos = {}
for group in sorted_demos.values():
for k, v in group:
out_sorted_demos[k] = v
copy_data[key]["demonstration"] = out_sorted_demos
return copy_data
merged_train_data = pipeline.add_transformation_step(
"add_train_demonstration",
add_train_demonstrations,
dependencies=[initial_assign],
)
get_train_preds = pipeline.add_query_step(
"get_train_preds",
model,
get_judge_prompt_fewshot,
extract_claim_logprobs,
dependencies=[merged_train_data],
logprobs=20,
max_tokens=1,
use_cache=use_cache,
)
pick_claims = pipeline.add_transformation_step(
"pick_two_inconsistent_claims",
pick_two_inconsistent_claims,
dependencies=[initial_assign],
)
eval_preds = pipeline.add_eval_step(
"evaluate",
calculate_accuracy,
dependencies=[get_train_preds, pick_claims],
)
return pipeline
async def predict_assignment(model, example, demonstrations):
demos = [
v
for k, v in demonstrations.items()
if k != example["uid"] and v["label"] is not None
]
anthropic_requests = [
model_api(
model,
get_judge_prompt_fewshot(
example,
demos,
pipeline=False,
),
logprobs=20,
max_tokens=1,
parse_fn=extract_claim_logprobs,
)
]
responses = await asyncio.gather(*anthropic_requests)
score = responses[0][0]["score"]
new_label = score > 0
return int(new_label)
def get_temperature(
iteration, initial_temp, final_temp, decay_rate, schedule="exp"
):
"""
Calculate the temperature for simulated annealing.
Parameters:
- iteration: Current iteration number.
- initial_temp: Initial temperature.
- decay_rate: Rate at which the temperature decreases.
Returns:
- Current temperature.
"""
if schedule == "exp":
return max(final_temp, initial_temp * (decay_rate**iteration))
elif schedule == "log":
return max(final_temp, initial_temp / (1 + 2 * np.log(1 + iteration)))
else:
assert False
def get_energy(metric, alpha):
return alpha * metric["train_prob"] - metric["inconsistent_num"]
def get_args():
parser = argparse.ArgumentParser()
parser.add_argument("--alpha", type=float, default=30)
parser.add_argument("--seed", type=int, default=27565976)
parser.add_argument("--testbed", type=str, default="gsm8k")
parser.add_argument("--model", type=str, default="meta-llama/Llama-3.1-70B")
parser.add_argument("--batch_size", type=int, default=256)
parser.add_argument("--num_seed", type=int, default=8)
parser.add_argument("--K", type=int, default=3000)
parser.add_argument("--consistency_fix_K", type=int, default=10)
parser.add_argument("--decay", type=float, default=0.99)
parser.add_argument("--initial_T", type=float, default=10)
parser.add_argument("--final_T", type=float, default=0.01)
parser.add_argument("--scheduler", type=str, default="log")
args = parser.parse_args()
return args
def load_data(args):
if args.testbed == "alpaca":
with open(get_root_directory() / "data/train_alpaca.json") as f:
train = json.load(f)
template = """Human: {question}
Response 1: {choice}
Response 2: {choice_2}
Claim: Response 1 is more helpful and harmless than Response 2.
I think this claim is """
for i in train:
i['source'] = 'alpaca'
i['consistency_key'] = 'A>B' if i['label'] else 'B>A'
i['prompt'] = template.format(question=i['question'], choice=i['choice'].replace("\n", ""), choice_2=i['choice_2'].replace("\n", ""))
args.GROUP_SIZE = 2
elif args.testbed == 'gsm8k':
with open(get_root_directory() / "data/train_gsm8k.json") as f:
train = json.load(f)
template = """Question: {question}
Claim: {answer}
I think this claim is """
for i in train:
i['source'] = 'gsm8k'
i['consistency_key'] = i['answer']
i['prompt'] = template.format(question=i['question'], answer=i['choice'])
args.GROUP_SIZE = 4
elif args.testbed == 'truthfulQA':
with open(get_root_directory() / "data/train_truthfulqa.json") as f:
train = json.load(f)
template = """Question: {question}
Claim: {answer}
I think this claim is """
for i in train:
i['source'] = 'truthfulQA'
i['consistency_key'] = 'A' if i['label'] else 'B'
i['prompt'] = template.format(question=i['question'], answer=i['choice'])
args.GROUP_SIZE = 4
elif args.testbed == 'truthfulQA-preference':
with open(get_root_directory() / "data/train_truthfulqa_preference.json") as f:
train = json.load(f)
template = """Question: {question}
Answer 1: {choice}
Answer 2: {choice_2}
Claim: Answer 1 is more truthful than Answer 2.
I think this claim is """
for i in train:
i['source'] = 'truthfulQA-preference'
i['consistency_key'] = 'A>B' if i['label'] else 'B>A'
i['prompt'] = template.format(question=i['question'], choice=i['choice'], choice_2=i['choice_2'])
args.GROUP_SIZE = 2
train_map = {}
for i in train:
if i['consistency_id'] not in train_map:
train_map[i['consistency_id']] = []
train_map[i['consistency_id']].append(i)
out = []
for key in train_map:
out += train_map[key]
train = out
# sample a batch of batch_size datapoints
fewshot_ids = random.sample(
list(range(len(train)// args.GROUP_SIZE)), args.batch_size // args.GROUP_SIZE
)
fewshot_ids = [
i * args.GROUP_SIZE + j for i in fewshot_ids for j in range(args.GROUP_SIZE)
]
return train, fewshot_ids
def initialize(train, fewshot_ids, args):
demonstrations = {}
unlabeled_ids = []
whole_ids = []
seed_ids = []
random_init_labels = [1] * (args.num_seed // 2) + [0] * (args.num_seed // 2)
random.shuffle(random_init_labels)
for id, i in enumerate(fewshot_ids):
item = train[i]
item["vanilla_label"] = item["label"] # store dataset labels to measure agreement during the searching process
item["uid"] = id
whole_ids.append(item["uid"])
if id >= args.num_seed: # set labels to None
item["label"] = None
item["type"] = "predict"
unlabeled_ids.append(item["uid"])
else: # set random labels
item["type"] = "seed"
item["label"] = random_init_labels[id]
seed_ids.append(item["uid"])
demonstrations[id] = item
return demonstrations, unlabeled_ids, whole_ids, seed_ids
def main(args):
train, fewshot_ids = load_data(args)
demonstrations, unlabeled_ids, whole_ids, seed_ids = initialize(train, fewshot_ids, args)
cur_metric = {
"train_prob": -1e6,
"inconsistent_num": 100000,
"train_accuracy": 1.0,
"train_predict_distribution": {"0": 0, "1": 0},
"train_label_distribution": {"0": 0, "1": 0},
}
print('init random labels = ', Counter([i['label'] for i in demonstrations.values() if i['type'] == 'seed']), 'init label acc = ', np.mean([i['label'] == i['vanilla_label'] for i in demonstrations.values() if i['type'] == 'seed']))
name = f"{args.testbed}-llama70b-K{args.K}-bc{args.batch_size}_seed{args.seed}-initialsize{args.num_seed}-weighted{args.alpha}-decay{args.decay}-initialT{args.initial_T}-finalT{args.final_T}-scheduler{args.scheduler}"
iter = 0
flip_cnt = 0
example_id = 0
for _ in tqdm(range(args.K), desc="searching"):
cur_pool = {
k: v for k, v in demonstrations.items() if v["label"] is not None
}
initial_demos = deepcopy(demonstrations)
if iter == 0:
pipeline = get_pipeline(
args.model,
name=name,
num_problems=None,
iter=iter,
assignment=cur_pool,
)
results = asyncio.run(pipeline.run())
cur_metric = results["evaluate"]
demonstrations, cur_metric = fix_inconsistency(
demonstrations, cur_metric, name, args.alpha, iter=iter, K=args.consistency_fix_K
)
cur_pool = {
k: v for k, v in demonstrations.items() if v["label"] is not None
}
while True: # weighted sampling
candidates_ids = whole_ids
weights = [1 for _ in range(len(candidates_ids))]
for i in candidates_ids:
if i in cur_pool:
same_consistency_group_ids = [j for j in candidates_ids if demonstrations[j]["consistency_id"] == demonstrations[i]["consistency_id"]]
for j in same_consistency_group_ids:
if j not in cur_pool:
weights[j] = 100
example_id = random.choices(candidates_ids, k=1, weights=weights)[0]
break
new_label = asyncio.run(
predict_assignment(
args.model,
demonstrations[example_id],
cur_pool,
)
)
if demonstrations[example_id]["label"] != new_label:
tmp_demonstrations = deepcopy(demonstrations)
tmp_demonstrations[example_id]["label"] = new_label
dummy_metric = {
"train_prob": -1e6,
"inconsistent_num": 100000,
"train_accuracy": 1.0,
"train_predict_distribution": {"0": 0, "1": 0},
"train_label_distribution": {"0": 0, "1": 0},
}
tmp_demonstrations, _ = fix_inconsistency(
tmp_demonstrations,
dummy_metric,
name + "newlabelexplore",
args.alpha,
iter=iter,
K=10,
)
tmp_pool = {
k: v
for k, v in tmp_demonstrations.items()
if v["label"] is not None
}
pipeline = get_pipeline(
model=args.model,
name=name,
num_problems=None,
iter=iter,
assignment=tmp_pool,
)
results = asyncio.run(pipeline.run())
metric = results["evaluate"]
T = get_temperature(
flip_cnt, args.initial_T, args.final_T, args.decay, schedule=args.scheduler
)
print(f"iter = {iter}, pool size = {len(cur_pool)}, cur acc = {cur_metric['train_accuracy']}, new acc = {metric['train_accuracy']}, cur score = {get_energy(cur_metric, args.alpha)}, new score = {get_energy(metric, args.alpha)}, cur inconsistent num = {cur_metric['inconsistent_num']}, new inconsistent num = {metric['inconsistent_num']}")
print('cur label distribution = ', Counter([i['label'] for i in demonstrations.values() if i['label'] is not None]))
print('new label distribution = ', Counter([i['label'] for i in tmp_demonstrations.values() if i['label'] is not None]))
accept_prob = math.exp((get_energy(metric, args.alpha) - get_energy(cur_metric, args.alpha)) / T)
print("accept prob = ", accept_prob)
if random.random() < accept_prob:
print("accept")
demonstrations = tmp_demonstrations
flip_cnt += 1
cur_metric = metric
with open(f"log_{name}.jsonl", "a") as f:
f.write(json.dumps({
"iter": iter,
"flip_cnt": flip_cnt,
"acc": cur_metric['train_accuracy'],
"score": get_energy(cur_metric, args.alpha),
}) + "\n")
else:
print("reject")
print("=" * 100)
iter += 1
if __name__ == "__main__":
setup_environment(logger_level="error")
model_api = ModelAPI(anthropic_num_threads=20, openai_fraction_rate_limit=0.99)
args = get_args()
print("task: ", args.testbed)
random.seed(args.seed)
main(args)