前言

今天是最后一个 Project 了,完整代码见 https://github.com/zong4/AILearning。

词袋模型(Bag-of-Words)

之前的文章有提到词袋模型做垃圾邮件分类效果挺好的,所以先用这方法,核心代码如下。

1
2
3
4
5
6
7
8
9
10
11
# 提取特征
vectorizer = CountVectorizer()
X_train = vectorizer.fit_transform(train_data['message'])
X_test = vectorizer.transform(test_data['message'])

y_train = train_data['label']
y_test = test_data['label']

# 训练模型
model = MultinomialNB()
model.fit(X_train, y_train)

构建词汇表

遍历训练集中的所有邮件文本,统计出现的所有不重复的词汇,这些词汇构成了词汇表(Vocabulary)。

例如,训练集中的邮件文本包含 “hello”、“world”、“spam”、“offer” 等词汇,将这些词汇收集起来形成词汇表,词汇表的大小决定了后续文本向量的维度。

文本向量化

对于训练集中的每一封邮件,统计词汇表中每个词汇在该邮件中出现的次数,将这些次数按照词汇表中词汇的顺序排列,形成一个向量,这个向量就是该邮件的词袋表示。

例如,词汇表为 [“hello”, “world”, “spam”, “offer”],某封邮件中 “hello” 出现了 2 次,“world” 出现了 1 次,“spam” 出现了 0 次,“offer” 出现了 3 次,那么该邮件的词袋向量就是 [2, 1, 0, 3]。

通常,由于词汇表可能非常大,而大部分邮件中只会包含其中一小部分词汇,因此词袋向量是一个稀疏向量,在实际应用中,常使用稀疏矩阵来存储这些向量,以节省内存。

朴素贝叶斯分类器

基于贝叶斯定理和特征条件独立假设,计算每个词汇在正常邮件和垃圾邮件中出现的概率,从而对新邮件进行分类。

Word Embedding

由于词袋模型不考虑语义,只是单纯的计算词频,所以自然还会有一种靠语义来判断的方法,那就是词嵌入。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 定义函数计算文本的 CLS 嵌入,使用 bert_model
def get_cls_embedding(text):
inputs = tokenizer(text, return_tensors='pt')
with torch.no_grad():
outputs = bert_model(**inputs)
cls_embedding = outputs.last_hidden_state[:, 0, :].numpy()
return cls_embedding

# 计算训练集和测试集的嵌入
train_embeddings = [get_cls_embedding(text).flatten() for text in train_data['message']]
test_embeddings = [get_cls_embedding(text).flatten() for text in test_data['message']]
print(test_embeddings)

# 划分训练集和验证集
X_train = train_embeddings
y_train = train_data['label']
X_test = test_embeddings
y_test = test_data['label']

# 训练逻辑回归模型,使用一个不同的变量名,比如 logreg_model
logreg_model = LogisticRegression()
logreg_model.fit(X_train, y_train)

因为需要的是句子的语义,而不是每个单词的语义,所以需要计算文本的 CLS 嵌入。

结果

词袋模型的结果如下。

1
2
3
4
5
6
7
8
              precision    recall  f1-score   support

0 0.99 0.99 0.99 1205
1 0.96 0.95 0.95 187

accuracy 0.99 1392
macro avg 0.97 0.97 0.97 1392
weighted avg 0.99 0.99 0.99 1392

词嵌入模型的结果如下。

1
2
3
4
5
6
7
8
              precision    recall  f1-score   support

0 0.99 1.00 0.99 1205
1 0.98 0.95 0.96 187

accuracy 0.99 1392
macro avg 0.99 0.97 0.98 1392
weighted avg 0.99 0.99 0.99 1392

看上去词嵌入模型的效果更好一些,但是当我输入这样一句话 “you have won £1000 cash! call to claim your prize.”,词嵌入模型的预测结果是有效邮件,而词袋模型的预测结果是垃圾邮件。

为什么会出现这种情况呢?因为词嵌入模型是基于语义的,而这句话中的 “won”、“prize” 等词汇在正常邮件中也可能出现,所以词嵌入模型无法准确判断这句话是垃圾邮件。

而词袋模型是基于词频的,这句话中的 “won”、“prize” 等词汇在垃圾邮件中出现的概率更高,所以词袋模型能够准确判断这句话是垃圾邮件。

词袋模型就适合这种有敏感词汇的场景,宁可错杀,不可放过。