自由文本处理
生成:Gemini-2.5-pro, 整理:fyerfyer
自由文本处理
1. 相关概念
自由文本 (Free Text) 指的是那些非结构化的文本数据,例如网页、数据库中的评论字段、文章等。
与关系型数据或图谱不同,自由文本的核心挑战在于它缺乏“易于提取”的结构。
这并非说自由文本没有结构,恰恰相反,它拥有丰富的语义结构,但理解这种结构通常需要人类水平的智能和大量的外部上下文知识,这对于通用数据科学工具来说是一个巨大挑战。
在数据科学中,处理自由文本的主要目标是:在不完全“理解”文本深层含义的情况下,提取出有用的信息。
这与“自然语言处理 (NLP)”或“自然语言理解 (NLU)”领域的目标不同,后者追求真正地、深入地理解语言的语法和语义。
例如,要判断一条电影评论是正面还是负面,我们通常不需要理解复杂的句子结构,只需通过简单的关键词查找即可:
"stunning"
(惊艳的),"marvelous"
(非凡的) → 正面"hollow"
(空洞的),"heartless"
(无情的) → 负面
尽管语言可以很复杂(如 "not at all boring"),但通常简单的关键词足以捕捉大部分文本的情感倾向。
在开始具体方法的讨论前,我们先定义下面的概念:
- 文档 (Document): 单个独立的自由文本当位,可以是一篇文章、一条评论等。
- 词 (Word/Term): 文本中的基本单元,通常由空格隔开,也包括标点符号。
- 语料库 (Corpus): 文档的集合。
- 词汇表 (Vocabulary): 语料库中所有不重复的词的集合。
2. 词袋模型与TF-IDF
核心思想
词袋模型 (Bag of Words, BoW) 是数据科学中最常见的文档表示方法。其核心思想是:一个文档仅由它所包含的词的集合(以及词的频率)来描述,完全忽略词的顺序和语法。
这本质上是文档的一种“词云 (word cloud)”视图。尽管丢弃了大量信息,但它在实践中效果惊人地好。
词频 (Term Frequency, TF)
词频表示一个词在文档中出现的次数。我们可以将一个语料库表示为一个 词频矩阵 (Term Frequency Matrix) 。
这是一个 的矩阵,其中 是文档数量, 是词汇表的大小。矩阵中的元素 表示词汇表中第 个词在第 个文档中出现的频率。
逆文档频率 (Inverse Document Frequency, IDF)
一个明显的问题是,像 "the", "is", "of" 这样的常见词会主导整个向量,但它们通常不携带太多特定信息。IDF 就是为了解决这个问题而生的。
它的核心思想是:一个词的重要性与它在语料库中出现的频率成反比。越是在少数文档中才出现的词,越有可能是“重要”的关键词。
其最常见的计算公式为:
如果一个词在所有文档中都出现,它的 值就是 。
TF-IDF
TF-IDF (Term Frequency-Inverse Document Frequency) 就是将词频和逆文档频率结合起来。它通过将词频矩阵的每一列(代表每个词)乘以其对应的 IDF 权重来实现。
这样,我们就得到了一个加权的词袋表示,其中频繁出现但无意义的词的权重被降低,而稀有且重要的词的权重被提升。
余弦相似度 (Cosine Similarity)
有了文档的向量表示(无论是 TF 还是 TF-IDF),我们就可以计算它们之间的相似度。最常用的度量是余弦相似度。
它计算的是两个向量之间夹角的余弦值,公式如下:
其结果范围在 之间。值为 1 表示两个文档的向量表示完全相同;值为 0 表示它们没有任何共同的词。
简单实现
以下面这个简单文档为例:
documents = ["the goal of this lecture is to explain the basics of free text processing",
"the bag of words model is one such approach",
"text processing via bag of words"]
我们先根据文档内容整理出词汇表:
document_word = [doc.split() for doc in documents]
vocab = sort(set(sum(document_word, [])))
vocab_dict = {k, i for i, k in enumerate(vocab)}
然后根据词汇表和文档内容,计算出具体的 和 :
X_tf = np.zeros((len(documents), len(vocab)), dtype=int)
for i, doc in enumerate(vocab_dict):
for word in doc:
X_tf[i, vocab_dict[word]] += 1
idf = np.log(X_tf.shape[0] / X_tf.astype(bool).sum(axis=0))
3. 词嵌入与word2vec
词袋模型的局限性
词袋模型有一个根本性的缺陷:它无法表达词与词之间的相似性。
在 BoW 模型中,每个词被表示为一个巨大的“独热 (one-hot)”向量。在这个向量空间里,"pittsburgh" 和 "boston" 之间的距离,与 "pittsburgh" 和 "database" 之间的距离是完全一样的。这显然不符合我们的直觉。
词嵌入 (Word Embeddings)
词嵌入解决了这个问题。它将每个词表示为一个低维、稠密的向量(例如,一个300维的向量),而不是一个庞大而稀疏的独热向量。
在这个新的向量空间中:
- 向量之间的欧氏距离能够对应到词义的相似性。"pittsburgh" 和 "boston" 的向量会非常接近,而它们与 "database" 的向量会相距很远。
- 向量的维度 通常远小于词汇表的大小。
word2vec
word2vec 是创建词嵌入最著名的算法之一。其核心思想可以通俗地理解为:训练一个模型,让它能够根据一个词来“预测”其上下文(周围的词)。
这个想法的直觉在于:如果两个词(如 "pittsburgh" 和 "boston")经常出现在相似的上下文中(例如,都和 "city", "technology", "eastern US" 等词一起出现),那么为了做出准确的预测,模型就必须将这两个词的向量表示变得非常相似。
词向量的惊人特性:类比推理
词嵌入最令人惊讶的特性之一是它们在向量空间中表现出的类比关系。最经典的例子是: king - man + woman ≈ queen
同样,在笔记的例子中,我们可以进行这样的类比: Pennsylvania : Pittsburgh -> Massachusetts : ?
通过向量运算 ,我们得到的新向量最相似的词之一就是 "boston"。
这种捕捉语义关系的能力是一种涌现特性 (emergent property),并非模型训练时明确优化的目标,这使得词嵌入非常强大。
词向量袋 (Bag of Word Vectors)
有了词嵌入,我们可以用一种新的方式来表示整个文档:将文档中所有词的词向量相加。
这种表示方法继承了词嵌入的优点。即使两个文档没有任何共同的词(例如,一个用 "excellent", "new",另一个用 "great", "cuisine"),只要它们讨论的是相似的主题,它们的文档向量也会非常相似。
4. N-gram 语言模型
超越词袋模型
词袋模型完全忽略了词序。因此,下面两个含义截然相反的句子在 BoW 模型看来是完全一样的:
- "great movie and not boring"
- "boring movie and not great"
为了捕捉词序信息,我们需要语言模型。
概率语言模型
一个概率语言模型的目标是计算一个词序列(即一个句子或文档)出现的概率。这可以分解为预测每个词在其前面所有词出现过的条件下的概率:
N-gram 模型
由于考虑所有前面的词在计算上非常困难,N-gram 模型做了一个简化的假设(马尔可夫假设):一个词的出现只与它前面的 个词有关。
- n=1 (Unigram): 词的出现是独立的,不考虑上下文。
- n=2 (Bigram): 只考虑前一个词。
- n=3 (Trigram): 考虑前两个词。
在语料库中,这些概率可以通过简单的计数来估计:
平滑技术 (Smoothing)
如果一个 n-gram 从未在训练语料库中出现过,它的概率会是零。这会导致整个句子的概率也变为零。为了解决这个问题,我们需要平滑技术。
拉普拉斯平滑 (Laplace Smoothing) 是最简单的一种,它给每个可能的词都增加一个小的计数值 :
其中 是词汇表的大小。
评估语言模型:困惑度 (Perplexity)
困惑度是评估语言模型好坏最常用的指标。它衡量的是模型对测试数据的不确定性。困惑度越低,模型性能越好。
其公式为测试集概率的几何平均值的倒数:
在实际计算中,通常使用对数形式以避免浮点数下溢:
重要提示:评估模型时必须使用验证集 (validation set) 或测试集,而不是训练集。因为在训练集上,n 越大,模型拟合得越好,困惑度会一直降低,但这是一种过拟合现象,并不能反映模型真实的泛化能力。
Comments