| |
| import os |
| import gradio as gr |
| from transformers import GPT2LMHeadModel, GPT2Tokenizer, TextDataset, DataCollatorForLanguageModeling |
| from transformers import Trainer, TrainingArguments |
| import re |
| import torch |
|
|
| MODEL_DIR = "./fine_tuned_model" |
| BASE_MODEL = "uer/gpt2-chinese-cluecorpussmall" |
|
|
| |
| def finetune_model(progress=gr.Progress()): |
| if os.path.exists(MODEL_DIR): |
| return "已存在微调好的模型,无需重复微调!直接切换到「测试」或「机器人」模式即可。" |
|
|
| progress(0, desc="正在读取家谱文件...") |
| with open("genealogy.txt", encoding="utf-8") as f: |
| text = f.read() |
|
|
| |
| blocks = re.split(r"\n\d+——|\n故祖考|\n故祖妣|\n孝男|\n孝媳|\n孝孙|\n曾孙|\n外曾孙|\n外玄孙", text) |
| examples = [] |
| for block in blocks[1:]: |
| if len(block) < 10: |
| continue |
| |
| name_match = re.search(r"[\u4e00-\u9fa5]{2,4}", block[:15]) |
| name = name_match.group() if name_match else "某人" |
|
|
| born = re.search(r"生[於于]\s*([^\n地名]+)", block) |
| died = re.search(r"(死[於于]|卒于|去世于)\s*([^\n地名]+)", block) |
| place = re.search(r"地名\s*([^\n]+)", block) |
|
|
| born = born.group(1).strip() if born else "未知" |
| place = place.group(1).strip() if place else "未知" |
| died = died.group(2).strip() if died else None |
|
|
| examples.append(f"查询: {name}出生时间和地点?\n回答: {name}出生于{born},地点{place}。") |
| if died: |
| examples.append(f"查询: {name}去世时间?\n回答: {name}去世于{died}。") |
|
|
| data_text = "\n\n".join(examples) |
| with open("train.txt", "w", encoding="utf-8") as f: |
| f.write(data_text) |
|
|
| progress(0.3, desc="加载 tokenizer 和数据集...") |
| tokenizer = GPT2Tokenizer.from_pretrained(BASE_MODEL) |
| dataset = TextDataset(tokenizer=tokenizer, file_path="train.txt", block_size=128) |
| data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False) |
|
|
| progress(0.6, desc="开始微调(预计40–90分钟,请耐心等待勿关闭页面)...") |
| model = GPT2LMHeadModel.from_pretrained(BASE_MODEL) |
|
|
| training_args = TrainingArguments( |
| output_dir="./results", |
| overwrite_output_dir=True, |
| num_train_epochs=3, |
| per_device_train_batch_size=2, |
| save_steps=500, |
| logging_steps=20, |
| save_total_limit=1, |
| ) |
|
|
| trainer = Trainer( |
| model=model, |
| args=training_args, |
| data_collator=data_collator, |
| train_dataset=dataset, |
| ) |
| trainer.train() |
|
|
| progress(1.0, desc="保存模型...") |
| model.save_pretrained(MODEL_DIR) |
| tokenizer.save_pretrained(MODEL_DIR) |
|
|
| return "微调成功!模型已保存。现在可以切换到「测试」或「机器人」模式使用啦!" |
|
|
| |
| def load_model(): |
| if not os.path.exists(MODEL_DIR): |
| return None, None |
| tokenizer = GPT2Tokenizer.from_pretrained(MODEL_DIR) |
| model = GPT2LMHeadModel.from_pretrained(MODEL_DIR) |
| return model, tokenizer |
|
|
| model, tokenizer = load_model() |
|
|
| def chat(message, history): |
| if model is None: |
| return "模型尚未微调完成!请先在上面选择「微调模型」运行一次。" |
| |
| prompt = "\n".join([f"查询: {h[0]}\n回答: {h[1]}" for h in history if h[1]] + [f"查询: {message}\n回答:"]) |
| inputs = tokenizer.encode(prompt, return_tensors="pt") |
| outputs = model.generate( |
| inputs, |
| max_length=inputs.shape[1] + 120, |
| do_sample=True, |
| temperature=0.7, |
| top_p=0.9, |
| no_repeat_ngram_size=3, |
| pad_token_id=tokenizer.eos_token_id |
| ) |
| reply = tokenizer.decode(outputs[0], skip_special_tokens=True) |
| return reply.split("回答:")[-1].strip() |
|
|
| |
| with gr.Blocks(theme=gr.themes.Soft(), title="冯氏家谱智能查询") as demo: |
| gr.Markdown("# 冯氏家谱智能查询机器人\n一次上传,永久使用,三个模式一键切换") |
|
|
| mode = gr.Radio( |
| ["微调模型", "测试模型", "正式机器人"], |
| label="请选择当前模式", |
| value="正式机器人" if os.path.exists(MODEL_DIR) else "微调模型" |
| ) |
|
|
| with gr.Column(visible=False) as finetune_box: |
| gr.Markdown("### 第一步:点击下方按钮开始微调(只需执行一次,耐心等待40–90分钟)") |
| btn = gr.Button("开始微调模型(免费CPU可完成)", variant="primary", size="lg") |
| output = gr.Textbox(label="微调日志", lines=20) |
| btn.click(finetune_model, None, output) |
|
|
| with gr.Column(visible=True) as chat_box: |
| gr.ChatInterface( |
| fn=chat, |
| title=None, |
| description="直接问:冯达尊出生地、冯乔福几个儿子、冯宗德姐妹是谁……", |
| examples=[ |
| "冯达尊出生时间和地点?", |
| "冯乔源的孩子有哪些?", |
| "冯宗福的兄弟姐妹是谁?", |
| "冯永明是哪一支的?", |
| "冯仁杰的父亲是谁?" |
| ] |
| ) |
|
|
| def update_interface(choice): |
| if choice == "微调模型": |
| return gr.update(visible=True), gr.update(visible=False) |
| else: |
| return gr.update(visible=False), gr.update(visible=True) |
|
|
| mode.change(update_interface, mode, [finetune_box, chat_box]) |
|
|
| |
| demo.launch() |