第93课:NLP基础:词嵌入、TF-IDF与Word2Vec
学习目标
完成本课学习后,你将能够:
- 理解文本向量化的核心思想及其在NLP任务中的重要性。
- 掌握TF-IDF算法的原理,并能使用Python库计算文档的TF-IDF向量。
- 初步理解Word2Vec(CBOW和Skip-gram)模型的思想,并能使用预训练词向量或进行简单训练。
- 能够比较不同文本表示方法(如词袋模型、TF-IDF、词嵌入)的优缺点及适用场景。
- 完成一个简单的基于文本向量的相似度计算或分类任务。
核心概念
在自然语言处理中,计算机无法直接理解“苹果”、“喜欢”这样的词语。我们需要一种方法将文本转换为计算机可以处理的数字形式——这就是文本向量化。本课我们将学习三种经典且强大的技术:词袋模型/TF-IDF 和 Word2Vec。
1. 从词袋模型到TF-IDF
想象一个场景:我们有两篇文章,一篇关于“机器学习”,另一篇关于“烹饪”。最直观的想法是统计每个词出现的次数,这就是词袋模型。但简单计数有个问题:像“的”、“是”、“在”这类词(停用词)在所有文档中都频繁出现,对区分文档主题毫无帮助。
TF-IDF (Term Frequency-Inverse Document Frequency) 就是为解决这个问题而生。它认为:
- 一个词在当前文档中出现的次数越多(TF),且在整个文档集合中出现的文档数越少(IDF),这个词就越重要。
- TF(词频)= 词在当前文档中出现的次数 / 当前文档的总词数。
- IDF(逆文档频率)= log(总文档数 / 包含该词的文档数)。
- TF-IDF值 = TF * IDF。
TF-IDF衡量的是一个词对当前文档的独特性和重要性,是一种基于统计的、无监督的文本表示方法。
2. Word2Vec:让词语拥有“含义”
TF-IDF虽然比简单词频好,但它将每个词视为独立的符号,无法捕捉“国王”和“王后”之间的语义关系。词嵌入应运而生,它将每个词映射到一个稠密的、低维的实数向量空间,使得语义相似的词在向量空间中的位置也相近。
Word2Vec 是谷歌在2013年提出的经典词嵌入模型,其核心思想是上下文决定语义。它包含两种主要架构:
- CBOW:根据上下文词(窗口内的词)预测中心词。好比给定句子“
我 ___ 吃苹果”,让你预测中间可能是什么词(喜欢、讨厌、想…)。 - Skip-gram:与CBOW相反,根据中心词预测上下文词。给定中心词“喜欢”,预测周围可能出现“我”、“吃”、“苹果”等词。
通过神经网络在大量文本上训练,模型最终学习到每个词对应的向量(词向量)。这些向量能编码丰富的语义信息,并且具有“国王 - 男人 + 女人 ≈ 王后”这样的神奇数学特性。
代码示例
下面我们将使用Python的scikit-learn计算TF-IDF,使用gensim演示Word2Vec。
# 示例文本数据:一组关于机器学习和烹饪的句子
documents = [
"机器学习是人工智能的核心领域。",
"深度学习是机器学习的一个分支。",
"神经网络是深度学习的基础模型。",
"我喜欢用Python进行数据科学。",
"烹饪美食需要新鲜的食材和耐心。",
"制作美味的意大利面是一门艺术。",
"机器学习模型需要大量的数据来训练。",
"掌握烹饪技巧可以提升生活品质。"
]
# ========================
# 第一部分:使用 TF-IDF
# ========================
from sklearn.feature_extraction.text import TfidfVectorizer
import pandas as pd
# 1. 初始化TF-IDF向量化器,自动处理分词(对于中文,这里假设已分好词或使用char_wb模式)
# 实际中文处理需要先分词,这里用空格模拟分词结果
documents_tokenized = [" ".join(list(doc.replace("。", "").replace(",", ""))) for doc in documents] # 简单模拟分词
# 更真实的中文处理应使用jieba:`import jieba; doc_cut = “ “.join(jieba.cut(doc))`
tfidf_vectorizer = TfidfVectorizer()
tfidf_matrix = tfidf_vectorizer.fit_transform(documents_tokenized)
# 2. 查看特征名(词汇表)和TF-IDF矩阵形状
print("词汇表(特征):", tfidf_vectorizer.get_feature_names_out())
print("TF-IDF矩阵形状:", tfidf_matrix.shape) # (文档数, 词汇表大小)
# 3. 将矩阵转换为DataFrame便于查看
tfidf_df = pd.DataFrame(tfidf_matrix.toarray(),
index=[f"文档{i+1}" for i in range(len(documents))],
columns=tfidf_vectorizer.get_feature_names_out())
print("\nTF-IDF矩阵 (前5个特征):")
print(tfidf_df.iloc[:, :5])
# 4. 计算文档相似度(以余弦相似度为例)
from sklearn.metrics.pairwise import cosine_similarity
# 计算第一篇文档与所有文档的相似度
doc_similarities = cosine_similarity(tfidf_matrix[0:1], tfidf_matrix)
print("\n第一篇文档与其他文档的相似度:", doc_similarities[0])
# ========================
# 第二部分:使用 Word2Vec
# ========================
from gensim.models import Word2Vec
import jieba # 用于中文分词
import logging
logging.basicConfig(format='%(asctime)s : %(levelname)s : %(message)s', level=logging.INFO)
# 1. 数据准备:对文档进行分词
documents_cut = [list(jieba.cut(doc)) for doc in documents]
print("\n分词后的示例文档:", documents_cut[0])
# 2. 训练Word2Vec模型 (这里用我们自己的小数据,实际应用应在大规模语料上训练或使用预训练模型)
# 参数说明:
# vector_size: 词向量维度,例如100, 300
# window: 上下文窗口大小
# min_count: 词频低于此值的词将被忽略
# sg: 0为CBOW, 1为Skip-gram
model = Word2Vec(documents_cut, vector_size=100, window=3, min_count=1, sg=1, epochs=100)
# 3. 使用模型
# 获取某个词的向量
word_vector = model.wv['机器学习']
print(f"\n'机器学习'的词向量形状: {word_vector.shape}")
print(f"'机器学习'向量的前5维: {word_vector[:5]}")
# 查找相似词
if '机器学习' in model.wv:
similar_words = model.wv.most_similar('机器学习', topn=3)
print("\n与'机器学习'最相似的词:", similar_words)
else:
print("词'机器学习'不在模型词汇表中,可能是min_count设置过高或数据太小。")
# 计算两个词之间的相似度
try:
sim = model.wv.similarity('机器学习', '深度学习')
print(f"\n'机器学习'与'深度学习'的相似度: {sim:.4f}")
except KeyError as e:
print(f"计算相似度时出错,词 {e} 不在词汇表中。")
# (进阶)在实际项目中,更常使用在大规模语料上预训练好的词向量
# 例如:`from gensim.models import KeyedVectors; pretrained_model = KeyedVectors.load_word2vec_format('path/to/your/pretrained_vectors.bin', binary=True)`
实践练习
练习1:TF-IDF文档分析
给定一个新的文档集合:
new_documents = [
“金融市场的波动受多种因素影响。”,
“股票投资需要谨慎分析公司财报。”,
“机器学习算法可以预测市场趋势。”,
“烹饪不同文化的菜肴需要了解香料。”,
“烘焙面包是一种结合科学与艺术的活动。”
]
要求:
- 使用
jieba进行分词。 - 计算这组文档的TF-IDF矩阵。
- 找出整个文档集合中TF-IDF值最高的前5个词(在整个语料库中最具区分度的词)。
练习2:Word2Vec语义探索
使用上面代码训练的model(或在更大语料上训练一个),完成以下任务:
- 找出与“神经网络”最相似的5个词。
- 尝试计算“Python”和“编程”之间的相似度(如果词存在)。
- 思考:为什么在我们的小数据集上,有些词可能找不到或效果不佳?
练习3:文本分类应用(综合)
假设你有一组新闻标题及其类别(体育、科技、美食),数据如下:
news_data = [
(“世界杯决赛今晚举行,球迷热情高涨。”, “体育”),
(“新赛季NBA球队实力排行榜公布。”, “体育”),
(“最新AI模型在语言理解上取得突破。”, “科技”),
(“量子计算研究获得重要进展。”, “科技”),
(“米其林大厨分享经典法式甜点食谱。”, “美食”),
(“在家也能做出地道的四川麻婆豆腐。”, “美食”)
]
要求:使用TF-IDF将标题向量化,并尝试使用一个简单的分类器(如逻辑回归)对标题进行分类。评估其效果(可以使用留一法交叉验证,因为数据量很小)。
常见错误
-
忽略文本预处理:直接将原始文本(尤其是中文)交给TF-IDF或Word2Vec,不进行分词、去停用词、去标点。这会导致模型性能大打折扣。解决方案:在中文NLP中,第一步永远是使用
jieba等工具分词。 -
Word2Vec参数设置不当:
min_count设置过高,在小数据集上可能过滤掉大部分词。vector_size(向量维度)设置不合理。维度太小无法捕捉语义,太大则计算慢且可能过拟合。- 在小数据集上从零训练Word2Vec效果通常很差,因为它需要大量上下文。最佳实践是加载在海量语料(如维基百科、新闻)上预训练好的词向量。
-
混淆有监督与无监督:TF-IDF和Word2Vec(训练过程)都是无监督的,它们不依赖于类别标签。它们生成的特征可以用于后续的有监督学习任务(如分类、聚类),但本身不是分类器。
-
期望词向量完美无缺:词向量会从训练数据中学习到偏见(如性别、种族刻板印象)。此外,一词多义的问题(如“苹果”可能是水果或公司)在静态词向量中无法解决,需要后续的ELMo、BERT等上下文词向量模型。
小结
本课我们学习了NLP中两种基石性的文本表示技术:
- TF-IDF:一种基于统计的方法,通过词频和逆文档频率为词加权,能够有效突出文档中的关键词,但无法捕捉词与词之间的语义关系。
- Word2Vec:一种基于深度学习的方法,通过神经网络将词映射为稠密向量,使语义相近的词在向量空间中靠近。它为理解词的语义提供了强大工具。
核心区别:TF-IDF是文档级别的、稀疏的、基于共现统计的特征;Word2Vec是词级别的、稠密的、基于语义相似性学习的特征。
在实际应用中,常常将两者结合:先用TF-IDF筛选出重要词,再用预训练词向量获取其语义表示。这些技术是理解更先进模型(如Transformer、BERT)的基石,它们都建立在“将文本转化为向量”这一核心思想之上。在下一课中,我们将看到如何通过注意力机制进一步突破这些方法的局限。