spaCy

技术栈
AI 框架
nlpindustrialtokenizationnerdependency-parsingpipeline

概览

spaCy

spaCy 是由 Explosion AI 开发的工业级自然语言处理库,设计理念是"开箱即用、极快速度"。它提供多语言预训练模型,一条 Pipeline 完成分词、词性标注、命名实体识别、依存句法分析等全套 NLP 任务。

核心价值:

  • 零配置 Pipelinenlp = spacy.load("zh_core_web_sm") 即可处理中文
  • 极速推理:Cython 实现,比 NLTK 快 100x+,生产环境首选
  • Transformer 融合spacy-transformers 接入 BERT 提升精度
  • 可扩展组件:EntityRuler / SpanCategorizer 等可插拔组件
  • 规则 + 统计混合:Matcher 做规则,NER 做统计,互补

适用场景: 文本预处理、信息抽取、知识图谱构建、合规审查、简历解析。

安装

环境准备

  • Python:>= 3.8(推荐 3.10)
  • 系统:Linux / macOS / Windows(部分模型仅 Linux/macOS)

安装命令

最小安装

pip install spacy

安装语言模型

# 英文(小/中/大/Transformer)
python -m spacy download en_core_web_sm      # 12 MB,适合入门
python -m spacy download en_core_web_md      # 40 MB,含词向量
python -m spacy download en_core_web_trf     # 460 MB,BERT 精度

# 中文
python -m spacy download zh_core_web_sm      # 中文小模型
python -m spacy download zh_core_web_trf     # 中文 Transformer

# 日文/德文/法文等
python -m spacy download ja_core_news_sm
python -m spacy download de_core_news_sm
python -m spacy download fr_core_news_sm

Transformer 增强(可选)

pip install spacy-transformers

验证安装

import spacy
nlp = spacy.load("en_core_web_sm")
doc = nlp("Apple is looking at buying U.K. startup for $1 billion.")
for ent in doc.ents:
    print(ent.text, ent.label_)

常见安装问题

Q1: Can't find model 'en_core_web_sm'

未下载模型。运行 python -m spacy download en_core_web_sm。如网络问题,从 https://github.com/explosion/spacy-models/releases 下载 .whl 手动安装。

Q2: 中文模型 zh_core_web_sm 分词不准

小模型基于规则分词,精度有限。换用 zh_core_web_trf(BERT 级分词)或 jieba 预处理。

Q3: OSError: [E050] Can't find model

虚拟环境切换后模型不可见。重新在目标环境 python -m spacy download ...

示例

spaCy 10 行代码:NER + 依存句法 + 可视化

目标

用 spaCy 一条 Pipeline 完成:分词、词性标注、命名实体识别 (NER)、依存句法分析,并用 displaCy 可视化。

完整代码

import spacy
from spacy import displacy

# ─── 1. 加载模型 ───
nlp = spacy.load("en_core_web_sm")

# ─── 2. 处理文本 ───
text = "Elon Musk announced that Tesla will build a new factory in Shanghai next year, investing $2 billion."
doc = nlp(text)

# ─── 3. 分词 + 词性 + 依存分析 ───
print("=" * 70)
print(f"{'Token':<12} {'POS':<10} {'依存关系':<16} {'Head':<12}")
print("=" * 70)
for token in doc:
    print(f"{token.text:<12} {token.pos_:<10} {token.dep_:<16} {token.head.text:<12}")

# ─── 4. 命名实体识别 ───
print("\n" + "=" * 40)
print("命名实体 (NER):")
print("=" * 40)
for ent in doc.ents:
    print(f"  {ent.text:<25} | {ent.label_:<10} | {spacy.explain(ent.label_)}")

# ─── 5. 名词短语 ───
print("\n名词短语:")
for chunk in doc.noun_chunks:
    print(f"  → {chunk.text}")

# ─── 6. 依存句法可视化 ───
displacy.render(doc, style="dep", jupyter=False, options={"compact": True})
# 或保存到文件:
# displacy.serve(doc, style="dep")  # 启动 Web 服务器

# ─── 7. NER 可视化 ───
displacy.render(doc, style="ent", jupyter=False)
# displacy.serve(doc, style="ent")

运行步骤

pip install spacy
python -m spacy download en_core_web_sm
python spacy_demo.py

预期输出

======================================================================
Token        POS        依存关系           Head
======================================================================
Elon         PROPN      compound          Musk
Musk         PROPN      nsubj             announced
announced    VERB       ROOT              announced
that         SCONJ      mark              build
...
Shanghai     PROPN      pobj              in
...
$            SYM        quantmod          billion
2            NUM        compound          billion
billion      NUM        pobj              of
.            PUNCT      punct             announced

============================================
命名实体 (NER):
============================================
  Elon Musk                 | PERSON     | People, including fictional
  Tesla                     | ORG        | Companies, agencies
  Shanghai                  | GPE        | Countries, cities, states
  next year                 | DATE       | Absolute or relative dates
  $2 billion                | MONEY      | Monetary values

教程

spaCy 入门教程:Pipeline 组件与自定义 NER

1. spaCy 的 Pipeline 架构

spaCy 采用模块化 Pipeline 设计,每个组件有确定的输入和输出:

Text → tokenizer → tagger → parser → ner → ... → Doc

查看当前 Pipeline:

import spacy
nlp = spacy.load("en_core_web_sm")
print(nlp.pipe_names)
# ['tok2vec', 'tagger', 'parser', 'attribute_ruler', 'lemmatizer', 'ner']

2. 组件详解

组件 功能 输出
tokenizer 分词 Token 序列
tok2vec 词→向量 Token 向量
tagger 词性标注 token.pos_, token.tag_
parser 依存句法 token.dep_, token.head
ner 命名实体 doc.ents
lemmatizer 词形还原 token.lemma_

3. Matcher:规则匹配

from spacy.matcher import Matcher

nlp = spacy.load("en_core_web_sm")
matcher = Matcher(nlp.vocab)

# 模式:形容词 + 名词
pattern = [{"POS": "ADJ"}, {"POS": "NOUN"}]
matcher.add("AdjNoun", [pattern])

doc = nlp("The quick brown fox jumps over the lazy dog")
matches = matcher(doc)
for match_id, start, end in matches:
    print(doc[start:end])  # quick fox, brown fox, lazy dog

4. EntityRuler:自定义实体规则

from spacy.pipeline import EntityRuler

nlp = spacy.load("en_core_web_sm")
ruler = nlp.add_pipe("entity_ruler", before="ner")

patterns = [
    {"label": "PRODUCT", "pattern": "iPhone 15"},
    {"label": "PRODUCT", "pattern": [{"LOWER": "macbook"}, {"LOWER": "pro"}]},
    {"label": "SKILL", "pattern": [{"LOWER": {"IN": ["python", "java", "rust"]}}]},
]
ruler.add_patterns(patterns)

doc = nlp("I use Python and Rust on my MacBook Pro.")
print([(ent.text, ent.label_) for ent in doc.ents])
# [('Python', 'SKILL'), ('Rust', 'SKILL'), ('MacBook Pro', 'PRODUCT')]

5. 自定义 NER 训练

import spacy
from spacy.training import Example
import random

# 从空白模型开始
nlp = spacy.blank("en")
ner = nlp.add_pipe("ner")
ner.add_label("TECH")

# 训练数据
TRAIN_DATA = [
    ("I love PyTorch", {"entities": [(7, 14, "TECH")]}),
    ("TensorFlow is from Google", {"entities": [(0, 10, "TECH")]}),
    ("FastAPI makes APIs easy", {"entities": [(0, 7, "TECH")]}),
]

# 开始训练
optimizer = nlp.begin_training()
for epoch in range(30):
    random.shuffle(TRAIN_DATA)
    losses = {}
    for text, annots in TRAIN_DATA:
        doc = nlp.make_doc(text)
        example = Example.from_dict(doc, annots)
        nlp.update([example], drop=0.5, losses=losses)
    if epoch % 10 == 0:
        print(f"Epoch {epoch}: Loss {losses['ner']:.4f}")

# 测试
doc = nlp("I started learning PyTorch yesterday")
print([(ent.text, ent.label_) for ent in doc.ents])

6. spaCy vs NLTK vs Stanza

维度 spaCy NLTK Stanza
速度 ⭐⭐⭐ 极快 ⭐ 慢 ⭐⭐ 中等
易用性 ⭐⭐⭐ ⭐⭐ ⭐⭐
学术支持 较少 丰富(教材) 丰富(Stanford)
生产就绪 ⭐⭐⭐ ⭐⭐
多语言 75+ 有限 70+

思考题

  1. spaCy 的 doc.ents 和 Matcher 都是提取实体,什么时候用哪个?
  2. 为什么 Pipeline 顺序重要?把 ner 放在 parser 前面会怎样?
  3. 训练自定义 NER 时,多少条标注数据才能达到可用精度?

参考资料

暂无参考文献