Skip to content

Repository files navigation

基于对比实验的科研文献分析系统

NLP 大作业 · 应用型选题

一句话概述

输入一篇学术论文 PDF,同时跑纯 LLM(DeepSeek V4 Pro)和纯小模型(SciBERT)两条 pipeline,对比两者的分类/实体识别/摘要生成性能,输出对比报告。

为什么这样做

原"Agent 编排 + 4 个 NLP 模块"架构存在三个致命问题:

  1. 所有模型受 512 token 限制,实际只能分析摘要而非全文
  2. Agent 层是硬编码字符串,没有真实的 LLM 推理决策
  3. 多模块级联误差传播,端到端有效性能可能低于 50%

新方案将项目定位从"构建一个审稿系统"转为**"做一个对比实验"**:用标准 NLP 测试集(PeerRead / SciERC / SciTLDR)作为 ground truth,定量回答"LLM 是否已经好到可以替代专业小模型"。

详见 docs/adr/0001-llm-vs-small-model-comparison.md。

快速开始

1. 环境配置

# 安装 uv(如果没有)
pip install uv

# 同步依赖
uv sync

国内镜像加速(如果 uv sync 下载很慢):

# PyPI 清华镜像
uv sync --index-url https://pypi.tuna.tsinghua.edu.cn/simple

# HuggingFace 镜像(下载模型时用)
export HF_ENDPOINT=https://hf-mirror.com

GPU 训练需要 CUDA 版 PyTorch(uv sync 默认装 CPU 版):

# 安装 CUDA 12.1 版 PyTorch(覆盖 CPU 版本)
uv pip install --reinstall torch --index-url https://download.pytorch.org/whl/cu121

# 验证 CUDA 可用
python -c "import torch; print(torch.cuda.is_available())"

2. 设置 API Key

cp .env.example .env
# 编辑 .env,填入 DeepSeek API key

3. 跑一轮对比

uv run python compare.py --pdf pdfs/your-paper.pdf

输出 comparison_report.json,包含两条 pipeline 的全部结果。

4. 服务器训练流程

从本机传代码到服务器:

# 本机打包
git bundle create paperreview.bundle --all
scp paperreview.bundle user@server:/home/user/

服务器上完整流程:

# 1. 解包
git clone paperreview.bundle Multi_Agent_PaperReview
cd Multi_Agent_PaperReview

# 2. 环境 (HF 镜像 + PyPI 镜像)
export HF_ENDPOINT=https://hf-mirror.com
uv sync --index-url https://pypi.tuna.tsinghua.edu.cn/simple

# 3. 下载数据 (也从 HuggingFace,走镜像)
uv run python prepare_data.py --all

# 4. 确认 GPU
nvidia-smi

# 5. 训练分类器
uv run python -m train.train_classifier \
    --data data/peerread_train.json \
    --output checkpoints/classifier \
    --epochs 5 --batch_size 16 --device cuda --freeze_layers 10

# 6. 训练 NER
uv run python -m train.train_ner \
    --train_data data/scierc_train.json \
    --val_data data/scierc_val.json \
    --output checkpoints/ner \
    --epochs 10 --batch_size 16 --device cuda --freeze_layers 10

# 7. 评测小模型
uv run python -c "
import json, torch
from src.classifier.model import SciBERTMultiTaskClassifier
from src.classifier.dataset import PeerReadDataset
from src.evaluation.eval_classifier import evaluate_classifier

with open('data/peerread_train.json') as f:
    samples = json.load(f)[:200]
dataset = PeerReadDataset(samples)
model = SciBERTMultiTaskClassifier(pretrained=True).to('cuda')
model.load_state_dict(torch.load('checkpoints/classifier/best_model.pt'))
print('Classifier F1:', evaluate_classifier(model, dataset, device='cuda'))
"

# 8. 配 API key
cp .env.example .env
# 编辑 .env 填 DEEPSEEK_API_KEY

# 9. 跑对比
uv run python compare.py --pdf /path/to/paper.pdf --output report.json

项目结构

├── src/
│   ├── preprocessing/       # PDF 解析 + 滑动窗口分块
│   ├── llm/                 # DeepSeek API(OpenAI 兼容协议)
│   ├── classifier/          # SciBERT 多任务分类器
│   ├── ner/                 # BiLSTM-CRF 命名实体识别
│   ├── summarizer/          # TextRank 摘要 + 检查清单规则引擎
│   └── evaluation/          # 评测:F1 / ROUGE / BERTScore
├── train/                   # 训练入口
├── tests/                   # 39 个单元测试
├── compare.py               # 主 CLI 入口
├── CONTEXT.md               # 术语表
├── docs/adr/                # 架构决策记录
└── pyproject.toml           # uv 依赖管理

两条 Pipeline 详解

Pipeline A:纯 LLM(DeepSeek V4 Pro)

PDF → 全文文本 → 4 次独立 API 调用:
  1. classify(text)    → {domains, method_type}
  2. extract(text)     → {entities: [{text, type}]}
  3. summarize(text)   → {background, contributions, ...}
  4. checklist(text)   → [{category, status, detail}]
  • 每次调用的 prompt 模板在 src/llm/prompts.py
  • API 客户端在 src/llm/client.py
  • 支持自动重试(最多 2 次)

Pipeline B:纯小模型

PDF → 全文文本 → 滑动窗口分块 → 三条独立路径:
  1. SciBERT(frozen) 分类器(每窗口预测 → 投票聚合)→ {domains, method_type}
  2. SciBERT(frozen) + BiLSTM(256×2) + CRF(每窗口标注 → 偏移合并)→ {entities}
  3. TextRank 抽取式摘要 + 规则引擎 → {summary, checklist}
  • 分类器:src/classifier/model.py — SciBERT(frozen emb+10层) + Domain head + Method head(dropout=0.3)
  • NER:src/ner/model.py — SciBERT(frozen emb+10层) + BiLSTM(256×2) + CRF(9 类 BIO 标签,dropout=0.4)
  • 摘要:src/summarizer/textrank.py — TF-IDF / SciBERT 句子编码 + PageRank + MMR
  • 检查清单:src/summarizer/checklist.py — 6 类规则检查

模块接口速查

PDF 解析

from src.preprocessing.pdf_parser import parse_pdf
text = parse_pdf("paper.pdf")  # → str

MinerU CLI 优先(输出 markdown),PyMuPDF 降级。

滑动窗口

from src.preprocessing.sliding_window import chunk_text
chunks = chunk_text(text, window_size=512, overlap=128)
# → [Chunk(text="...", start=0, end=512, index=0), ...]

LLM 调用

from src.llm.client import LLMClient
client = LLMClient(api_key="sk-xxx")
client.classify(text)          # → {domains, method_type}
client.extract_entities(text)  # → {entities, relations}
client.summarize(text)         # → {background, contributions, ...}
client.check_manifest(text, entities, summary)  # → [{category, status, detail}]

分类器

from src.classifier.model import SciBERTMultiTaskClassifier
model = SciBERTMultiTaskClassifier(pretrained=True)
model.predict_text("This paper...")
# → {domains: ["NLP"], method_type: "Empirical", domain_probs: {...}, method_probs: {...}}

NER

from src.ner.model import BiLSTMCRFNER
model = BiLSTMCRFNER(pretrained=True)
model.predict(input_ids, attention_mask)
# → {entities: [{type: "MODEL", start: 0, end: 1, text: ""}]}

摘要与检查清单

from src.summarizer.textrank import TextRankSummarizer
from src.summarizer.checklist import ChecklistEngine

s = TextRankSummarizer()
s.summarize(text, num_sentences=5)       # → List[str]
s.structured_summary(text)                # → {background, contributions, ...}

e = ChecklistEngine()
e.generate(entities, paper_text=text)     # → [{category, status, detail}]

数据准备

PeerRead(分类训练)

从 https://github.com/allenai/PeerRead 下载,处理为 JSON 列表:

[
  {"text": "标题+摘要", "domains": ["NLP"], "method_type": "Empirical"},
  ...
]

SciERC(NER 训练)

从 https://github.com/dwadden/LOSTIN 下载,处理为 JSON 列表:

[
  {
    "tokens": ["We", "use", "BERT", "on", "SQuAD"],
    "entities": [
      {"text": "BERT", "type": "MODEL", "start": 2, "end": 3},
      {"text": "SQuAD", "type": "DATASET", "start": 4, "end": 5}
    ]
  },
  ...
]

SciTLDR(摘要评测)

从 https://github.com/neulab/SciTLDR 下载,处理为 JSON 列表:

[
  {"text": "论文全文", "summary": "参考TLDR摘要"},
  ...
]

训练

训分类器

uv run python -m train.train_classifier \
    --data data/peerread_train.json \
    --output checkpoints/classifier \
    --epochs 5 --batch_size 16 --freeze_layers 10

训 NER

uv run python -m train.train_ner \
    --train_data data/scierc_train.json \
    --val_data data/scierc_val.json \
    --output checkpoints/ner \
    --epochs 10 --batch_size 16 --freeze_layers 10

训练过程中会输出每 epoch 的 val F1,自动早停并保存最佳模型。

5090 上这两个训练各约 1-2 小时(取决于数据量)。

评测

# 分类评测
from src.evaluation.eval_classifier import evaluate_classifier
from src.classifier.dataset import PeerReadDataset

dataset = PeerReadDataset(test_samples)
results = evaluate_classifier(model, dataset)
# → {domain_macro_f1: 0.85, method_accuracy: 0.88, per_domain_f1: {...}}

# NER 评测
from src.evaluation.eval_ner import evaluate_ner
results = evaluate_ner(ner_model, scierc_dataset)
# → {entity_f1: 0.80, report: "..."}

# 摘要评测
from src.evaluation.eval_summarizer import evaluate_summarizer
results = evaluate_summarizer(summarizer, references, texts)
# → {rouge1: 0.42, rouge2: 0.18, rougeL: 0.38}

运行测试

uv run pytest -v              # 全部 39 个测试
uv run pytest tests/test_llm.py -v    # 只跑 LLM 模块

团队分工

姓名 负责模块 关键文件
覃疆楠(队长) 小模型训练 + 评测 src/classifier/, src/ner/, train/, src/evaluation/
李明远 数据处理 + 摘要 src/summarizer/, 数据预处理脚本
许世典 PDF + LLM + CLI src/preprocessing/, src/llm/, compare.py

术语表

核心术语定义见 CONTEXT.md。关键概念:

  • 论文画像:论文用了哪些数据集/模型/指标/方法的客观清单
  • 检查清单:6 类结构化检查项(数据集多样性、Baseline、消融、代码、伦理、指标),每项为 ok/partial/missing/unchecked
  • 纯 LLM 方案:DeepSeek 分 4 次调用完成全部分析
  • 纯小模型方案:SciBERT 分类 + BiLSTM-CRF NER + TextRank 摘要,本地推理

参考文档

  • CONTEXT.md — 完整术语表与设计决策
  • docs/adr/0001-llm-vs-small-model-comparison.md — 架构决策记录
  • 开题报告.md — 原始开题报告(选题背景、创新点)
  • report.md — 可行性评估报告(SOTA 对比、数据集分析)
  • INSPECTION_REPORT.md — 原架构检验报告(记录了重做的原因)

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages