Spaces:
Runtime error
Runtime error
File size: 7,224 Bytes
2f9ac86 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | import gradio as gr
import torch
import numpy as np
from pathlib import Path
import re
from Model import OmniPathWithInterTaskAttention
from transformers import AutoModelForCausalLM, AutoTokenizer
import tempfile
import os
# 设备设置
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
print(f"使用设备: {device}")
# 预加载模型(避免重复加载)
@torch.no_grad()
def load_models():
"""预加载必要的模型"""
# 1. 加载分类模型
ckpt_path = "best_model.pth"
if not Path(ckpt_path).exists():
raise FileNotFoundError(f"找不到模型文件: {ckpt_path}")
ckpt = torch.load(ckpt_path, map_location=device)
label_mappings = ckpt.get('label_mappings', None)
if not label_mappings:
raise ValueError("checkpoint 中缺少 label_mappings")
ck_cfg = ckpt.get('config', {})
feature_dim = 512 # 根据你的实际特征维度调整
hidden_dim = int(ck_cfg.get('hidden_dim', 256))
dropout = float(ck_cfg.get('dropout', 0.3))
use_inter_task_attention = bool(ck_cfg.get('use_inter_task_attention', True))
inter_task_heads = int(ck_cfg.get('inter_task_heads', 4))
classification_model = OmniPathWithInterTaskAttention(
label_mappings=label_mappings,
feature_dim=feature_dim,
hidden_dim=hidden_dim,
dropout=dropout,
use_inter_task_attention=use_inter_task_attention,
inter_task_heads=inter_task_heads
).to(device)
classification_model.load_state_dict(ckpt['model_state_dict'], strict=False)
classification_model.eval()
# 2. 加载文本生成模型
llm_model_name = "Qwen/Qwen3-0.6B"
tokenizer = AutoTokenizer.from_pretrained(llm_model_name)
llm_model = AutoModelForCausalLM.from_pretrained(
llm_model_name,
torch_dtype="auto",
device_map="auto"
)
return classification_model, llm_model, tokenizer, label_mappings
# 预加载模型
classification_model, llm_model, tokenizer, label_mappings = load_models()
def build_prompt(pred_names, pred_scores):
"""构建提示词"""
def get_pred(task_name):
name = pred_names.get(task_name, "N/A")
score = pred_scores.get(task_name, 0.0)
return f"{name} (confidence: {score:.1%})"
cancer_type = get_pred('cancer_type')
pathologic_stage = get_pred('pathologic_stage')
clinical_stage = get_pred('clinical_stage')
histological_type = get_pred('histological_type')
prompt = (
"You are a professional medical report generator. "
"Based on the patient's pathological classification and diagnostic model results, "
f"the cancer_type is {cancer_type}, "
f"pathologic_stage is {pathologic_stage}, "
f"clinical_stage is {clinical_stage}, "
f"and histological_type is {histological_type}. "
"Please write a concise English summary describing the diagnosis, staging interpretation, and general clinical implications as a short paragraph. "
"Avoid placeholders and avoid repeating words."
)
return prompt
def generate_description(predictions, confidences):
"""生成描述"""
prompt = build_prompt(predictions, confidences)
messages = [{"role": "user", "content": prompt}]
text = tokenizer.apply_chat_template(
messages,
tokenize=False,
add_generation_prompt=True,
enable_thinking=False
)
model_inputs = tokenizer([text], return_tensors="pt").to(llm_model.device)
generated_ids = llm_model.generate(
**model_inputs,
max_new_tokens=500, # 减少token数量以加快生成速度
do_sample=True,
temperature=0.7,
)
output_ids = generated_ids[0][len(model_inputs.input_ids[0]):].tolist()
try:
index = len(output_ids) - output_ids[::-1].index(151668)
except ValueError:
index = 0
content = tokenizer.decode(output_ids[index:], skip_special_tokens=True).strip("\n")
return {"prompt": prompt, "description": content}
def process_npy_file(npy_file):
"""处理上传的NPY文件"""
if npy_file is None:
return "请先上传NPY文件", "", ""
try:
# 读取NPY文件
arr = np.load(npy_file.name, allow_pickle=False)
if not isinstance(arr, np.ndarray) or arr.ndim != 2:
return "错误: NPY文件必须是二维特征矩阵", "", ""
features = torch.from_numpy(arr).float()
# 提取短ID
p = Path(npy_file.name)
m = re.search(r'(TCGA-[A-Z0-9]{2}-[A-Z0-9]{4})', p.name.upper())
short_id = m.group(1) if m else p.stem[:12]
# 推理
feat_batch = features.unsqueeze(0).to(device)
outputs = classification_model(feat_batch)
# 解码结果
pred_names, pred_scores = {}, {}
for task_name, logits in outputs.items():
probs = torch.softmax(logits[0], dim=-1)
idx = int(torch.argmax(probs).item())
classes = label_mappings[task_name]['classes']
class_name = classes[idx] if 0 <= idx < len(classes) else str(idx)
pred_names[task_name] = class_name
pred_scores[task_name] = float(probs[idx].item())
# 生成描述
desc_result = generate_description(pred_names, pred_scores)
# 格式化结果显示
results_text = f"患者ID: {short_id}\n\n预测结果:\n"
for task, name in pred_names.items():
results_text += f"- {task}: {name} (置信度: {pred_scores.get(task, 0.0):.3f})\n"
return results_text, desc_result['prompt'], desc_result['description']
except Exception as e:
return f"处理过程中出现错误: {str(e)}", "", ""
# 创建Gradio界面
with gr.Blocks(theme=gr.themes.Soft()) as demo:
gr.Markdown("""
# 🏥 医学病理诊断分析系统
上传病理特征NPY文件,获取AI辅助诊断结果和医学描述。
""")
with gr.Row():
with gr.Column():
file_input = gr.File(
label="上传NPY特征文件",
file_types=[".npy"],
type="filepath"
)
analyze_btn = gr.Button("开始分析", variant="primary")
with gr.Column():
results_output = gr.Textbox(
label="分析结果",
lines=10,
max_lines=20
)
with gr.Row():
prompt_output = gr.Textbox(
label="提示词 (Prompt)",
lines=4,
max_lines=6
)
with gr.Row():
description_output = gr.Textbox(
label="AI生成描述",
lines=6,
max_lines=10
)
# 示例文件
gr.Examples(
examples=[["example.npy"]], # 你需要提供一个示例NPY文件
inputs=file_input,
label="点击使用示例文件"
)
analyze_btn.click(
fn=process_npy_file,
inputs=file_input,
outputs=[results_output, prompt_output, description_output]
)
if __name__ == "__main__":
demo.launch(share=True) |