Views
No views yet

Source Abstracts (concatenated text)
|
v
SBERT Encoder (thenlper/gte-large, 1024-dim)
|
v
Sbert2Prompt (Linear -> LayerNorm -> GELU -> Linear -> LayerNorm)
|
v
16 Soft Prompt Tokens (2048-dim each)
|
v
LoRA-adapted Llama-3.2-1B-Instruct (rank=64, alpha=128)
|
v
4 Structured Output Fields| Field | Label | Description |
|---|---|---|
| Abstract | ABSTRACT | Multi-sentence synthesis of the cluster's research findings |
| Overview | OVERVIEW | Concise 2-3 sentence summary of the cluster theme |
| Title | TITLE | Descriptive research area title (8-15 words) |
| Headline | HEADLINE | Short punchy label (3-7 words) |
meta-llama/Llama-3.2-1B-Instructthenlper/gte-large (1024-dim sentence embeddings)| Metric | Score |
|---|---|
| Semantic Similarity | 0.755 |
| Format Compliance | 0.875 |
| Coherence | 0.994 |
| Composite | 0.863 |
1import torch
2import torch.nn as nn
3from sentence_transformers import SentenceTransformer
4from transformers import AutoTokenizer, AutoModelForCausalLM
5from peft import PeftModel, LoraConfig
6from huggingface_hub import hf_hub_download, snapshot_download
7import json
8
9# Download model files
10model_dir = snapshot_download("jimnoneill/BSG_CyLlama")
11
12# Load config
13with open(f"{model_dir}/config.json") as f:
14 config = json.load(f)
15
16# Load SBERT encoder
17sbert = SentenceTransformer(config["sbert_model_name"])
18
19# Load prompt generator (Sbert2Prompt with LayerNorm)
20class Sbert2Prompt(nn.Module):
21 def __init__(self, sbert_dim, llama_hidden_dim, prompt_length=16):
22 super().__init__()
23 self.prompt_length = prompt_length
24 self.llama_hidden_dim = llama_hidden_dim
25 self.projection = nn.Sequential(
26 nn.Linear(sbert_dim, llama_hidden_dim * 2),
27 nn.LayerNorm(llama_hidden_dim * 2),
28 nn.GELU(),
29 nn.Dropout(0.1),
30 nn.Linear(llama_hidden_dim * 2, llama_hidden_dim * prompt_length),
31 nn.LayerNorm(llama_hidden_dim * prompt_length),
32 )
33
34 def forward(self, sbert_emb):
35 B = sbert_emb.size(0)
36 out = self.projection(sbert_emb)
37 return out.view(B, self.prompt_length, self.llama_hidden_dim)
38
39device = "cuda" if torch.cuda.is_available() else "cpu"
40
41prompt_gen = Sbert2Prompt(
42 config["embedding_dim"],
43 config["llama_hidden_dim"],
44 config["prompt_length"]
45)
46prompt_gen.load_state_dict(torch.load(f"{model_dir}/prompt_generator.pt", map_location=device))
47prompt_gen = prompt_gen.to(device).eval()
48
49# Load LoRA-adapted LLM
50tokenizer = AutoTokenizer.from_pretrained(f"{model_dir}/model")
51base_model = AutoModelForCausalLM.from_pretrained(
52 config["model_name"], torch_dtype=torch.float16, device_map=device
53)
54model = PeftModel.from_pretrained(base_model, f"{model_dir}/model")
55model.eval()
56
57# Generate summaries for a cluster of abstracts
58abstracts = [
59 "We studied the role of gut microbiota in inflammatory bowel disease...",
60 "Our findings demonstrate that fecal microbiota transplantation can...",
61 "Metagenomic analysis revealed significant dysbiosis patterns in..."
62]
63combined_text = " ".join(abstracts)
64
65# Encode with SBERT
66embedding = sbert.encode([combined_text], convert_to_tensor=True).to(device)
67
68# Generate soft prompts
69with torch.no_grad():
70 soft_prompts = prompt_gen(embedding.float())
71
72# Build generation prompt with theme instruction
73theme_instruction = (
74 "Provide a comprehensive overview covering key findings, "
75 "methodology, significance, and broader context."
76)
77
78for label in config["labels"]:
79 generation_prompt = (
80 f"<|begin_of_text|><|start_header_id|>system<|end_header_id|>\n"
81 f"You are a scientific summarization assistant. {theme_instruction}\n"
82 f"<|eot_id|><|start_header_id|>user<|end_header_id|>\n"
83 f"Summarize the following research cluster.\n"
84 f"Source: {combined_text[:2000]}\n"
85 f"<|eot_id|><|start_header_id|>assistant<|end_header_id|>\n"
86 f"{label}: "
87 )
88
89 input_ids = tokenizer(generation_prompt, return_tensors="pt").input_ids.to(device)
90 input_embeds = model.get_input_embeddings()(input_ids)
91
92 # Prepend soft prompts
93 input_embeds = torch.cat([soft_prompts.half(), input_embeds], dim=1)
94 attention_mask = torch.ones(input_embeds.shape[:2], device=device)
95
96 with torch.no_grad():
97 outputs = model.generate(
98 inputs_embeds=input_embeds,
99 attention_mask=attention_mask,
100 max_new_tokens=200 if label == "ABSTRACT" else 80,
101 temperature=0.7,
102 do_sample=True,
103 top_p=0.9,
104 repetition_penalty=1.15,
105 )
106
107 result = tokenizer.decode(outputs[0], skip_special_tokens=True)
108 print(f"{label}: {result}")BSG_CyLlama/
bsg_cyllama_logo.png # Logo
config.json # Model configuration
prompt_generator.pt # Sbert2Prompt weights (265 MB)
model/
adapter_config.json # LoRA adapter configuration
adapter_model.safetensors # LoRA weights (173 MB)
tokenizer.json # Tokenizer
tokenizer_config.json # Tokenizer config
special_tokens_map.json # Special tokens
chat_template.jinja # Chat templatetorch>=2.0
transformers>=4.40
peft>=0.10
sentence-transformers>=2.0
huggingface-hub1@software{bsg_cyllama_2026,
2 title={BSG CyLlama: Corpus-Level Scientific Summarization},
3 author={O'Neill, Jim},
4 year={2026},
5 url={https://huggingface.co/jimnoneill/BSG_CyLlama},
6 version={2.0.0}
7}