当你打开新闻App时,首页总能精准推送你感兴趣的科技动态;当企业需要监控舆情时,系统可以自动将海量报道按财经、体育、政治等类别归档——这些场景背后,往往活跃着一个经典算法的身影:朴素贝叶斯分类器。这个诞生于18世纪的数学公式,如今在文本分类领域依然大放异彩。本文将用50行Python代码,带你从零实现一个能自动识别新闻主题的分类器。
工欲善其事,必先利其器。我们需要准备以下工具包:
python复制import numpy as np
from sklearn.datasets import fetch_20newsgroups
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.model_selection import train_test_split
新闻数据集选用经典的20 Newsgroups,包含约2万篇新闻文档,均匀分为20个主题类别。实际项目中,你可能会遇到这样的数据结构:
| 字段名 | 类型 | 说明 |
|---|---|---|
| text | str | 新闻正文内容 |
| target | int | 分类标签(0-19) |
| target_names | str | 分类名称(如'sci.space') |
提示:初次接触NLP项目时,建议先用小规模数据测试。可以设置
subset='train'只加载训练集,减少内存消耗。
计算机无法直接理解"区块链"或"世界杯"的含义,我们需要将文本转换为特征向量。最常用的方法是词袋模型(BoW):
python复制vectorizer = CountVectorizer(
stop_words='english', # 过滤无意义停用词
max_features=5000, # 保留最高频的5000个词
ngram_range=(1,2) # 同时考虑单词和双词组合
)
X = vectorizer.fit_transform(newsgroups.data)
这个过程实际上构建了一个巨大的词频矩阵:
| 文档ID | 单词1 | 单词2 | ... | 单词n |
|---|---|---|---|---|
| 1 | 2 | 0 | ... | 1 |
| 2 | 1 | 3 | ... | 0 |
| ... | ... | ... | ... | ... |
注意:实践中会发现某些词如"said"、"would"几乎出现在所有文档中,但信息量很低。这时可以引入TF-IDF加权:
python复制from sklearn.feature_extraction.text import TfidfTransformer
tfidf = TfidfTransformer()
X = tfidf.fit_transform(X)
让我们抛开sklearn,手动实现一个朴素贝叶斯分类器。核心数学公式其实非常简单:
$$
P(y|x) = \frac{P(x|y)P(y)}{P(x)}
$$
具体实现分为三个关键步骤:
python复制class NaiveBayes:
def fit(self, X, y):
self.classes = np.unique(y)
# 计算先验概率P(y)
self.prior = {c: np.mean(y == c) for c in self.classes}
# 计算条件概率P(x|y)
self.likelihood = {}
for c in self.classes:
X_c = X[y == c]
# 拉普拉斯平滑,避免零概率问题
self.likelihood[c] = (X_c.sum(axis=0) + 1) / (X_c.sum() + X.shape[1])
python复制 def predict(self, X):
preds = []
for x in X:
posteriors = []
for c in self.classes:
# 对数概率防止下溢
log_prior = np.log(self.prior[c])
log_likelihood = np.sum(np.log(self.likelihood[c]) * x)
posteriors.append(log_prior + log_likelihood)
preds.append(self.classes[np.argmax(posteriors)])
return np.array(preds)
由于文本数据通常是高维稀疏矩阵,我们可以优化计算:
python复制from scipy.sparse import csr_matrix
def sparse_log_prob(X, prob):
"""高效计算稀疏矩阵的对数概率"""
log_prob = np.log(prob)
return X.dot(log_prob.T)
将数据集划分为训练集和测试集:
python复制X_train, X_test, y_train, y_test = train_test_split(
X, newsgroups.target, test_size=0.2, random_state=42
)
评估指标除了准确率,文本分类还需要关注:
python复制from sklearn.metrics import classification_report
model = NaiveBayes()
model.fit(X_train, y_train)
preds = model.predict(X_test)
print(classification_report(y_test, preds,
target_names=newsgroups.target_names))
典型输出可能显示:
code复制 precision recall f1-score support
sci.electronics 0.92 0.85 0.88 392
sci.medical 0.89 0.93 0.91 396
soc.religion 0.95 0.91 0.93 398
在实际新闻分类项目中,你可能会遇到这些挑战:
数据不平衡问题:
新词与领域术语:
多语言混合内容:
python复制# 示例:混合中英文的处理
CountVectorizer(token_pattern=r'\b[a-zA-Z\u4e00-\u9fa5]+\b')
实时分类性能优化:
python复制from sklearn.naive_bayes import MultinomialNB
from sklearn.pipeline import make_pipeline
# 生产环境推荐管道
pipeline = make_pipeline(
CountVectorizer(max_features=10000),
TfidfTransformer(),
MultinomialNB(alpha=0.1) # 平滑系数
)
我在实际项目中发现,适当加入n-gram特征(如"人工智能"比单独的"人工"和"智能"更有意义)能使准确率提升5-8%。但也要注意控制特征维度,避免模型过于庞大。