Nanny7's picture
feat: initial commit for B Map AI backend and Gemma LLM service
2f86d9b
Raw History Blame Contribute Delete
5.26 kB
# Copyright 2026 DeepMind Technologies Limited.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Convert PubMedQA JSON data to JSONL format for HD text diffusion SFT.
Reads raw PubMedQA JSON from source directory and writes JSONL files with
fields:
- prompt: Formatted question + context
- response: Short answer (yes/no/maybe)
- long_response: Long-form answer
- short_answer: Ground truth label (yes/no/maybe) for evaluation
Usage:
python convert_pubmedqa.py --output_dir=./pubmedqa_jsonl
"""
import json
import os
from typing import Any
from absl import app
from absl import flags
_INPUT_DIR = flags.DEFINE_string(
"input_dir",
"gemma/diffusion/hackable_diffusion_adapter/data/pubmedqa",
"Source directory containing PubMedQA JSON files.",
)
_OUTPUT_DIR = flags.DEFINE_string(
"output_dir",
"",
"Output directory for JSONL files. Required.",
)
_MAX_CONTEXT_CHARS = flags.DEFINE_integer(
"max_context_chars",
4000,
"Maximum total characters for concatenated context paragraphs.",
)
def _read_file(path: str) -> str:
"""Read a file using standard Python file I/O."""
with open(path, "r", encoding="utf-8") as f:
return f.read()
def _write_file(path: str, content: str) -> None:
"""Write a file using standard Python file I/O."""
os.makedirs(os.path.dirname(path), exist_ok=True)
with open(path, "w", encoding="utf-8") as f:
f.write(content)
def _format_prompt(question: str, contexts: list[str]) -> str:
"""Format a PubMedQA example into a clean prompt.
Concatenates all context paragraphs (truncating if needed) with the question.
Args:
question: The question string.
contexts: A list of context paragraph strings.
Returns:
The formatted prompt string.
"""
context_text = "\n".join(contexts)
if len(context_text) > _MAX_CONTEXT_CHARS.value:
context_text = context_text[: _MAX_CONTEXT_CHARS.value] + "..."
return f"Context:\n{context_text}\n\nQuestion: {question}"
def _convert_json_to_examples(data: dict[str, Any]) -> list[dict[str, Any]]:
"""Convert PubMedQA nested JSON to flat list of examples."""
examples = []
for pubmed_id, entry in data.items():
question = entry.get("QUESTION", "")
contexts = entry.get("CONTEXTS", [])
short_answer = entry.get("final_decision", "")
long_answer = entry.get("LONG_ANSWER", "")
prompt = _format_prompt(question, contexts)
examples.append({
"pubmed_id": str(pubmed_id),
"prompt": prompt,
"response": short_answer,
"long_response": long_answer,
"short_answer": short_answer,
})
return examples
def _write_jsonl(examples: list[dict[str, Any]], output_path: str) -> None:
"""Write examples as JSONL (one JSON object per line)."""
lines = [json.dumps(ex, ensure_ascii=False) for ex in examples]
content = "\n".join(lines) + "\n"
_write_file(output_path, content)
print(f"Wrote {len(examples)} examples to {output_path}")
def main(argv):
del argv
if not _OUTPUT_DIR.value:
raise ValueError("--output_dir is required.")
input_dir = _INPUT_DIR.value
output_dir = _OUTPUT_DIR.value
# --- Load full PQA-L dataset (1000 expert-labeled examples) ---
pqal_path = os.path.join(input_dir, "ori_pqal.json")
print(f"Reading PQA-L data from {pqal_path}...")
pqal_data = json.loads(_read_file(pqal_path))
# --- Load official test set ground truth (to get test IDs) ---
test_gt_path = os.path.join(input_dir, "test_ground_truth.json")
print(f"Reading test ground truth from {test_gt_path}...")
test_gt_data = json.loads(_read_file(test_gt_path))
test_ids = set(test_gt_data.keys())
# --- Split full dataset into train and test ---
# ori_pqal.json contains all 1000 PQA-L examples, including the 500 that
# are officially designated as the test set. We split them using test_ids.
train_data = {k: v for k, v in pqal_data.items() if k not in test_ids}
test_data = {k: v for k, v in pqal_data.items() if k in test_ids}
# --- Convert ---
train_examples = _convert_json_to_examples(train_data)
test_examples = _convert_json_to_examples(test_data)
overlap = set(e["pubmed_id"] for e in train_examples) & set(
e["pubmed_id"] for e in test_examples
)
if overlap:
raise ValueError(
f"Train/test overlap detected ({len(overlap)} IDs)! This should not"
" happen after filtering."
)
print(f"Training examples: {len(train_examples)} (after removing test IDs)")
print(f"Test examples: {len(test_examples)}")
# --- Write JSONL ---
_write_jsonl(train_examples, os.path.join(output_dir, "pubmedqa_train.jsonl"))
_write_jsonl(test_examples, os.path.join(output_dir, "pubmedqa_test.jsonl"))
print("Done!")
if __name__ == "__main__":
app.run(main)