前言

今天的机器学习课程主要围绕两个经典算法展开:DBSCAN聚类算法和TF-IDF文本向量化。DBSCAN作为一种基于密度的聚类方法,能够自动发现任意形状的簇并识别噪声点;而TF-IDF则是文本挖掘中最基础的特征提取方式,用于衡量词语在文档中的重要性。本文将结合三个实战案例,详细讲解这两个算法的原理、代码实现和应用场景。

一、DBSCAN密度聚类实战(啤酒数据)

1.1 算法原理简述

DBSCAN(Density-Based Spatial Clustering of Applications with Noise)的核心思想是:聚类由高密度区域生长而成。

  • 核心点:半径eps内至少包含min_samples个样本
  • 边界点:在核心点邻域内但自身不满足核心点条件
  • 噪声点:既不是核心点也不是边界点

1.2 代码详解

from sklearn.cluster import DBSCAN
import pandas as pd
from sklearn import metrics
from sklearn.preprocessing import MinMaxScaler
import numpy as np

# 读取啤酒数据(包含卡路里、钠、酒精、成本四个特征)
beer = pd.read_table("data.txt", sep=" ", encoding="utf-8", engine="python")
X_raw = beer[["calories", "sodium", "alcohol", "cost"]]

# 0-1归一化(消除量纲影响)
scaler = MinMaxScaler(feature_range=(0, 1))
X = scaler.fit_transform(X_raw)

# 交叉验证选取最优eps(邻域半径)
scores = []
e_param = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7]
for i in e_param:
    db = DBSCAN(eps=i, min_samples=2)
    db.fit(X)
    labels = db.labels_
    # 使用轮廓系数评估聚类效果(-1~1,越高越好)
    score = metrics.silhouette_score(X, labels)
    scores.append(score)
    print(f"eps={i}, score={score}")

# 选择轮廓系数最大的eps
best_e = e_param[np.argmax(scores)]
print(f"最优eps为{best_e}")

# 用最优参数重新建模
db = DBSCAN(eps=best_e, min_samples=2)
db.fit(X)
labels = db.labels_

# 将聚类结果添加回原数据框
beer['cluster_db'] = labels
beer.sort_values('cluster_db')

# 最终轮廓系数评分
score = metrics.silhouette_score(X, labels)
print(f"最终轮廓系数:{score}")

1.3 关键知识点

参数含义调优建议
eps邻域半径过小导致大量噪声,过大则合并不同簇
min_samples核心点最小邻域样本数通常设为2~4,数据量大时可适当增加
轮廓系数评估聚类内聚度和分离度越接近1表示聚类效果越好

二、TF-IDF文本向量化

2.1 原理说明

TF-IDF = 词频(TF) × 逆文档频率(IDF)

  • TF:某词在文档中出现的频率
  • IDF:log(总文档数 / 包含该词的文档数)

核心思想:一个词在文档中出现越多,在其他文档中出现越少,则其重要性越高。

2.2 基础代码实现

from sklearn.feature_extraction.text import TfidfVectorizer
import pandas as pd

# 读取语料(每行一个文档)
inFile = open(r".\task2_1.txt", "r")
corpus = inFile.readlines()

# 创建TF-IDF向量化器
vectorizer = TfidfVectorizer()
tfidf = vectorizer.fit_transform(corpus)

# 获取所有特征词
wordlist = vectorizer.get_feature_names_out()
print("词表:", wordlist)

# 转为DataFrame格式(词为行,文档为列)
df = pd.DataFrame(tfidf.T.todense(), index=wordlist)
print(df)

# 输出每个文档中词的TF-IDF(按值降序排列)
for j in range(len(corpus)):
    featurelist = df.iloc[:, j].to_list()
    resdict = {}
    for i in range(0, len(wordlist)):
        resdict[wordlist[i]] = featurelist[i]
    resdict = sorted(resdict.items(), key=lambda x: x[1], reverse=True)
    print(f"文档{j+1}关键词排序:{resdict}")

三、项目实战:红楼梦120回关键词提取

3.1 第一步:分词与预处理(demo02)

import pandas as pd
import os
import jieba

# 1. 读取分卷文件
filePaths = []
fileContents = []
for root, dirs, files in os.walk(r".\红楼梦\分卷"):
    for name in files:
        filePath = os.path.join(root, name)
        filePaths.append(filePath)
        f = open(filePath, "r", encoding="utf-8")
        lines = f.readlines()
        text = "".join(lines[1:])  # 跳过第一行(卷名)
        fileContents.append(text)
        f.close()

corpos = pd.DataFrame({
    "filePath": filePaths,
    "fileContent": fileContents
})

# 2. 加载自定义词典和停用词
jieba.load_userdict(r"./红楼梦/红楼梦词库.txt")
stopwords = pd.read_csv(r"./红楼梦分析/StopwordsCN.txt", 
                        encoding='utf-8', engine='python', index_col=False)

# 3. 逐回分词并过滤停用词
file_to_jieba = open(r'./红楼梦/分词后汇总.txt', 'w', encoding='utf-8')
for index, row in corpos.iterrows():
    juan_ci = ''
    fileContent = row['fileContent']
    segs = jieba.cut(fileContent)
    for seg in segs:
        if seg not in stopwords.stopword.values and len(seg.strip()) > 0:
            juan_ci += seg + ' '
    file_to_jieba.write(juan_ci + '\n')
file_to_jieba.close()

3.2 第二步:计算每回TF-IDF(demo03)

from sklearn.feature_extraction.text import TfidfVectorizer
import pandas as pd

# 读取分词后的结果(每行一回)
inFile = open(r".\红楼梦\分词后汇总.txt", "r", encoding='utf-8')
corpus = inFile.readlines()

# 计算TF-IDF
vectorizer = TfidfVectorizer()
tfidf = vectorizer.fit_transform(corpus)
wordlist = vectorizer.get_feature_names_out()

# 转为DataFrame
df = pd.DataFrame(tfidf.T.todense(), index=wordlist)

# 输出每回Top10关键词
for j in range(len(corpus)):
    featurelist = df.iloc[:, j].to_list()
    resdict = {}
    for i in range(0, len(wordlist)):
        resdict[wordlist[i]] = featurelist[i]
    resdict = sorted(resdict.items(), key=lambda x: x[1], reverse=True)
    print(f"第{j+1}回的核心关键词:{resdict[0:10]}")

3.3 运行结果示例(第1回)

第1回的核心关键词:[('甄士隐', 0.45), ('英莲', 0.38), ('僧人', 0.32), 
                    ('道人', 0.30), ('石头', 0.28), ('太虚', 0.25), ...]

四、知识点总结

算法应用场景核心优势注意事项
DBSCAN客户分群、异常检测、地理信息聚类无需预设簇数、可识别噪声eps和min_samples需调参
TF-IDF搜索引擎、关键词提取、文本分类简单高效、可解释性强需结合分词和停用词过滤

五、进阶学习建议

  1. DBSCAN:可尝试使用k-distance图辅助选取最优eps
  2. TF-IDF:可结合CountVectorizer对比词袋模型效果

六、结语

今天的内容涵盖了无监督聚类和文本特征提取两大机器学习核心领域。DBSCAN让我们能够从数据中发现自然形成的群体结构,而TF-IDF则为文本数据提供了量化的分析基础。

更多推荐