文本表示方法
文本表示是自然语言处理(NLP)中的基础任务,它将非结构化的文本数据转化为计算机可以处理的数值形式。
本文将系统介绍 NLP 中常用的文本表示方法,从传统方法到现代深度学习技术,帮助读者全面理解这一核心概念。
传统文本表示
词袋模型(Bag of Words)
词袋模型是最简单的文本表示方法之一,它将文本视为一个无序的词汇集合。
基本概念
- 忽略词语顺序和语法,只关注词语是否出现
- 构建词汇表,统计每个词在文档中出现的次数
- 最终表示为一个高维稀疏向量
代码示例
实例
from sklearn.feature_extraction.text import CountVectorizer
corpus = [
'This is the first document.',
'This document is the second document.',
'And this is the third one.',
'Is this the first document?'
]
vectorizer = CountVectorizer()
X = vectorizer.fit_transform(corpus)
print(vectorizer.get_feature_names_out())
print(X.toarray())
corpus = [
'This is the first document.',
'This document is the second document.',
'And this is the third one.',
'Is this the first document?'
]
vectorizer = CountVectorizer()
X = vectorizer.fit_transform(corpus)
print(vectorizer.get_feature_names_out())
print(X.toarray())
优缺点分析
✅ 优点:
- 实现简单,计算效率高
- 适用于小规模数据集和简单任务
❌ 缺点:
- 忽略词序和语义信息
- 高维稀疏性问题
- 无法处理同义词和多义词
TF-IDF
TF-IDF(Term Frequency-Inverse Document Frequency)是对词袋模型的改进,考虑了词语在整个语料库中的重要性。
计算公式
- TF(词频):
词在文档中出现的次数 / 文档总词数
- IDF(逆文档频率):
log(文档总数 / 包含该词的文档数)
- TF-IDF = TF × IDF
代码实现
实例
from sklearn.feature_extraction.text import TfidfVectorizer
tfidf_vectorizer = TfidfVectorizer()
X_tfidf = tfidf_vectorizer.fit_transform(corpus)
print(tfidf_vectorizer.get_feature_names_out())
print(X_tfidf.toarray())
tfidf_vectorizer = TfidfVectorizer()
X_tfidf = tfidf_vectorizer.fit_transform(corpus)
print(tfidf_vectorizer.get_feature_names_out())
print(X_tfidf.toarray())
优缺点
✅ 优点:
- 降低常见词的影响,突出重要词
- 比简单词袋模型效果更好
❌ 缺点:
- 仍然无法捕捉语义关系
- 高维问题依然存在
N-gram 模型
N-gram 模型考虑了词语的顺序信息,通过连续n个词的组合来表示文本。
常见类型
- Unigram (1-gram):单个词
- Bigram (2-gram):两个连续词的组合
- Trigram (3-gram):三个连续词的组合
代码示例
实例
bigram_vectorizer = CountVectorizer(ngram_range=(2, 2))
X_bigram = bigram_vectorizer.fit_transform(corpus)
print(bigram_vectorizer.get_feature_names_out())
X_bigram = bigram_vectorizer.fit_transform(corpus)
print(bigram_vectorizer.get_feature_names_out())
优缺点
✅ 优点:
- 捕捉局部词序信息
- 可以表示短语和固定搭配
❌ 缺点:
- 维度爆炸问题更严重
- 仍然无法处理长距离依赖
词向量表示
Word2Vec 原理与实现
Word2Vec 是一种基于神经网络的词向量表示方法,由 Google 在 2013 年提出。
两种模型架构
- CBOW(Continuous Bag of Words):通过上下文预测当前词
- Skip-gram:通过当前词预测上下文
代码实现
实例
from gensim.models import Word2Vec
sentences = [["cat", "say", "meow"], ["dog", "say", "woof"]]
model = Word2Vec(sentences, vector_size=100, window=5, min_count=1, workers=4)
# 获取词向量
vector = model.wv['cat']
# 找相似词
similar_words = model.wv.most_similar('cat')
sentences = [["cat", "say", "meow"], ["dog", "say", "woof"]]
model = Word2Vec(sentences, vector_size=100, window=5, min_count=1, workers=4)
# 获取词向量
vector = model.wv['cat']
# 找相似词
similar_words = model.wv.most_similar('cat')
特点
- 低维稠密向量(通常50-300维)
- 可以捕捉词语的语义和语法关系
- 支持向量运算(如:king - man + woman ≈ queen)
GloVe 词向量
GloVe(Global Vectors for Word Representation)结合了全局统计信息和局部上下文窗口的优点。
核心思想
- 基于词共现矩阵
- 优化目标是使两个词的向量点积等于它们共现次数的对数
与 Word2Vec 对比
特性 | Word2Vec | GloVe |
---|---|---|
训练方式 | 局部窗口 | 全局统计 |
计算效率 | 较高 | 较低 |
小数据集表现 | 较好 | 一般 |
大数据集表现 | 好 | 更好 |
FastText
FastText 是 Facebook 开发的词向量模型,特点是考虑子词(subword)信息。
主要特点
- 将词表示为字符n-gram的集合
- 可以处理未登录词(OOV)
- 特别适合形态丰富的语言
代码示例
实例
from gensim.models import FastText
model = FastText(sentences, vector_size=100, window=5, min_count=1, workers=4)
# 即使单词不在词典中也能获得向量
vector = model.wv['unseenword']
model = FastText(sentences, vector_size=100, window=5, min_count=1, workers=4)
# 即使单词不在词典中也能获得向量
vector = model.wv['unseenword']
上下文感知的表示
ELMo 模型
ELMo(Embeddings from Language Models)是最早的上下文相关词表示方法之一。
核心特点
- 基于双向LSTM语言模型
- 词语的表示取决于整个输入句子
- 生成多层表示(可以组合不同层次的语义)
架构示意图
BERT 及其变体
BERT(Bidirectional Encoder Representations from Transformers)是 Google 提出的预训练语言模型。
关键创新
- Transformer 架构
- 掩码语言模型(MLM)训练目标
- 下一句预测(NSP)任务
常见变体
- RoBERTa:优化训练策略
- DistilBERT:轻量版BERT
- ALBERT:参数共享减少模型大小
代码示例
实例
from transformers import BertTokenizer, BertModel
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertModel.from_pretrained('bert-base-uncased')
inputs = tokenizer("Hello, my dog is cute", return_tensors="pt")
outputs = model(**inputs)
last_hidden_states = outputs.last_hidden_state
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = BertModel.from_pretrained('bert-base-uncased')
inputs = tokenizer("Hello, my dog is cute", return_tensors="pt")
outputs = model(**inputs)
last_hidden_states = outputs.last_hidden_state
预训练语言模型概述
现代NLP主要使用预训练+微调范式:
- 预训练阶段:在大规模语料上训练通用语言表示
- 微调阶段:在特定任务数据上调整模型参数
模型比较
模型 | 发布时间 | 主要特点 |
---|---|---|
Word2Vec | 2013 | 静态词向量 |
GloVe | 2014 | 全局统计+局部窗口 |
ELMo | 2018 | 双向LSTM,上下文相关 |
BERT | 2018 | Transformer,双向上下文 |
GPT-3 | 2020 | 单向Transformer,生成能力强 |
文档级表示
Doc2Vec
Doc2Vec 是 Word2Vec 的扩展,可以直接学习文档的向量表示。
两种模型
- PV-DM(Distributed Memory):类似CBOW,加入文档ID
- PV-DBOW(Distributed Bag of Words):类似Skip-gram
代码示例
实例
from gensim.models import Doc2Vec
from gensim.models.doc2vec import TaggedDocument
documents = [TaggedDocument(doc, [i]) for i, doc in enumerate(corpus)]
model = Doc2Vec(documents, vector_size=100, window=5, min_count=1, workers=4)
vector = model.infer_vector(["new", "document", "text"])
from gensim.models.doc2vec import TaggedDocument
documents = [TaggedDocument(doc, [i]) for i, doc in enumerate(corpus)]
model = Doc2Vec(documents, vector_size=100, window=5, min_count=1, workers=4)
vector = model.infer_vector(["new", "document", "text"])
句向量与文档向量
常用方法
- 平均法:对词向量取平均
- SIF:平滑逆频率加权平均
- BERT句向量:使用[CLS]标记或平均所有词向量
代码示例(使用Sentence-BERT)
实例
from sentence_transformers import SentenceTransformer
model = SentenceTransformer('all-MiniLM-L6-v2')
sentences = ["This is an example sentence", "Each sentence is converted"]
embeddings = model.encode(sentences)
model = SentenceTransformer('all-MiniLM-L6-v2')
sentences = ["This is an example sentence", "Each sentence is converted"]
embeddings = model.encode(sentences)
主题模型(LDA)
潜在狄利克雷分配(LDA)是一种无监督的主题建模方法。
基本原理
- 将文档表示为多个主题的混合
- 每个主题是词语的概率分布
- 通过变分推断或Gibbs采样学习
代码示例
实例
from sklearn.decomposition import LatentDirichletAllocation
from sklearn.feature_extraction.text import CountVectorizer
vectorizer = CountVectorizer()
X = vectorizer.fit_transform(corpus)
lda = LatentDirichletAllocation(n_components=2)
lda.fit(X)
from sklearn.feature_extraction.text import CountVectorizer
vectorizer = CountVectorizer()
X = vectorizer.fit_transform(corpus)
lda = LatentDirichletAllocation(n_components=2)
lda.fit(X)
应用场景
- 文档聚类
- 内容推荐
- 文本摘要
总结
文本表示方法的发展经历了从简单统计到深度学习的演进:
- 传统方法:简单高效,适合小规模数据
- 词向量:捕捉语义关系,维度低
- 上下文感知模型:动态表示,效果最好但计算成本高
- 文档表示:从词级别扩展到文档级别
选择文本表示方法时应考虑:
- 任务需求(是否需要语义理解)
- 数据规模
- 计算资源
- 语言特性
随着大语言模型的发展,文本表示技术仍在快速演进,但理解这些基础方法对于掌握NLP仍然至关重要。
点我分享笔记