当前位置: 首页 > news >正文

维基百科文章爬虫和聚类:高级聚类和可视化

一、说明 

        维基百科是丰富的信息和知识来源。它可以方便地构建为带有类别和其他文章链接的文章,还形成了相关文档的网络。我的 NLP 项目下载、处理和应用维基百科文章上的机器学习算法。

在我的上一篇文章中,KMeans 聚类应用于一组大约 300 篇维基百科文章。如果没有任何预期的标签,则只能通过检查哪些文章被分组在一起以及哪个单词最常出现来接近聚类结果。结果并不令人信服,例如有关人工智能的文章与有关太空探索的文章归为一类。

为了提高聚类结果,本文实现了三个不同的目标。首先,可视化文档矢量化结果并绘制聚类。其次,应用不同的矢量化方法。第三,使用额外的聚类算法。

本文的技术背景是Python v3.11scikit-learn v1.2.2。所有示例也应该适用于较新的库版本。

本文最初出现在我的博客admantium.com上。

二、相关境况

        本文是有关使用 Python 进行 NLP 的博客系列的一部分。在我之前的文章中,我介绍了如何使用该WikipediaReader对象从“机器学习”、“航天器”和“Python(编程语言)”文章中下载 100 个子页面,创建了约 300 篇文章的语料库。这些文章,仅仅是文本文件,由一个对象进一步处理WikipediaCorpus,以总结所有文章的整体。然后创建一个 Pandas DataFrame 对象,其中包含每篇文章的标题、文本、预处理文本和标记。最后,将预处理后的文本矢量化并用作聚类算法的输入。

2.1 目标 1:可视化

第一个目标是更好地理解文档的向量空间。在到目前为止的文章系列中,使用 SciKit Learn 内置的 .zip 文件将词袋词典转换为向量DictVectorizer。以下代码片段显示了它的应用并给出了对结果向量的印象:

from sklearn.feature_extraction import DictVectorizer vectorizer = DictVectorizer(sparse= False ) 
x_train = vectorizer.fit_transform(X[ 'bow' ]) print ( type (x_train)) 
#numpy.ndarray print (x_train) 
#[[ 15. 0. 10 . ... 0.0.0.] 
# [662. 0. 430. ... 0. 0. 0.] 
# [316. 0.143....0.0.0.] 
#... 
#[319. 0.217. ... 0.0.0.] 
# [158. 0.147. ... 0.0.0.] 
# [328. 0. 279. ... 0. 0. 0.]] print (x_train.shape) 
# (272, 52743) print (vectorizer.get_feature_names_out()) 
# array([',', ',1', '. ', ..., 'zy', 'zygomaticus', 'zygote'], dtype=object) print ( len (vectorizer.get_feature_names_out())) 
# 52743

如您所见,生成的向量有 52743 个维度。

为了绘制它们,我们将使用 PCA 进行降维,然后绘制它。通过以下代码实现对训练数据应用 2D PCA,然后绘制绘图:

import matplotlib.pyplot as plt 
from sklearn.decomposition import PCA def  pca_reduce ( vec_list,dimensions= 2 ): return PCA(dimensions).fit_transform(vec_list) def  d2_plot ( data ): plt.plot(data, 'o' ) d2_plot( pca_reduce(x_train, 2 ))

数据点没有明显的分离,它们或多或少以 y 轴为中心,具有零值和一些异常值。当使用另一种降维机制时,这个图会改变吗?

以下代码将TruncatedSVD应用于数据。

import matplotlib.pyplot as plt 
from sklearn.decomposition import TruncatedSVD def  pca_reduce ( vec_list,dimensions= 2 ): return TruncatedSVD(dimensions, n_iter= 40 ).fit_transform(vec_list) def  d2_plot ( data ): plt.plot(data, '. ' ) d2_plot(pca_reduce(x_train, 2 ))

该图看起来有点不同,y 轴上值小于 0 的异常值较少。

最后,让我们以 3D 图表的形式查看数据。

import matplotlib.pyplot as plt 
from sklearn.decomposition import PCA def  pca_reduce ( vec_list,dimensions ): return PCA(dimensions).fit_transform(vec_list) def  d3_plot ( data ): Fig = plt.figure() ax = Fig.add_subplot(projection = '3d' ) for _, v in  enumerate (data[: 90 ]): ax.scatter(v[ 0 ],v[ 1 ], v[ 2 ],marker= '.' , color= 'r' ) for _, v in  enumerate (data[ 90 : 180 ]): ax.scatter(v[ 0 ],v[ 1 ], v[ 2 ],marker= '.' , color= 'g' ) for _, v在 enumerate (data[ 180 :]) 中:ax.scatter(v[ 0 ],v[ 1 ], v[ 2 ],marker = '.' , color= 'b' ) plt.show() d3_plot(pca_reduce( x_train, 3 ))

在此图中,数据点也非常密切相关。

这些可视化显示了使用词袋文档表示的一个明显缺点:生成的向量彼此接近,使得聚类算法难以正确分离文档。为了更好地区分数据,我们需要使用另一种向量化方法并直观地比较得到的向量空间。

2.2 目标 2:应用不同的矢量化方法

        假设更改向量化方法可以导致更好的分离向量,从而获得更好的聚类,本节介绍两种不同的向量化方法:Tfidf 和 WordVectors。

2.2.1 Tfidf矢量化

        SciKit Learn 具有内置的 Tfidf Vector,可应用于原始文本数据。preprocessed在该项目的设置过程中,生成了原始数据的特殊表示,其中所有单词都表示为其引理,并且删除了大多数停用词。将根据该数据计算 Tfidf 向量。

        这是相关代码:

from sklearn.feature_extraction.text import TfidfVectorizerx_train = X['preprocessed'].tolist()vectorizer = TfidfVectorizer()
x_train = vectorizer.fit_transform(x_train)print(x_train.shape)
# (272, 40337)print(x_train)
# (0, 1002) 0.010974360184074128
# (0, 5031) 0.011294684416460914
# (0, 30935) 0.013841666362619034
# (0, 1004) 0.010228010133798603
# (0, 22718) 0.009819505656781956
# (0, 1176) 0.012488241517746365
# (0, 4398) 0.012488241517746365
# (0, 8803) 0.015557383558602929
# (0, 36287) 0.028985349686940432

生成的向量空间只有 40337 维。对结果向量应用 2D 和 3D PCA 得出以下图表:

2D 图表显示了数据点之间更清晰的分离,而在 3D 图表中,我们看到红色和绿色标记点之间存在一些差异。

2.2.2 词向量向量化

        词向量用多维值表示每个词,该多维值表示其在训练的语料库材料的上下文中的含义。正如前面的文章所述,存在不同的预训练词向量表示,Gensim 库提供它们方便的下载。

        在以下示例中,使用具有 50 个维度的 Glove Gigaword 预训练向量。DataFrame 对象已经定义了一个token从预处理文本派生的列表(只有引理,没有停用词),并从中创建一个包含单词向量的新列。

import gensim.downloader as api
import numpy as npvocab = corpus.vocab()
vector_lookup = api.load('glove-wiki-gigaword-50')
word_vector(tokens):return np.array([vector_lookup[token]for token in tokensif token in vocab and token in vector_lookup])X['word_vector'] = X['tokens'].apply(lambda tokens: word_vector(tokens))

运行此代码会生成以下增强的 DataFrame 对象:

在目前的形式中,词向量具有不同的长度。详细打印它们的长度并绘制直方图是通过以下代码实现的:

word_vector_length = np.array([len(tokens) for tokens in X['word_vector'].to_numpy().flatten()])print(word_vector_length[:5])
# [760, 157, 7566, 2543, 2086]bins=int(np.max(word_vector_length)/1000)plt.hist(x=word_vector_length,  bins=bins, density=False)
plt.show()print(f'Mean: {word_vector_length.mean()}')
# Mean: 2248.904411764706

直方图清楚地表明较短的文章文本是常态:

要应用 PCA,需要填充和截断向量。我选择最大字长为 6000,这意味着填充/截断为 300000。

def pad_word_vectors(vec_list, padding_value):res = []for vec in vec_list:con = np.array([v for v in vec]).reshape(-1)con_padded = np.pad(con, (0, padding_value))con_truncated = con_padded[:padding_value]res.append(con_truncated)return np.array(res)def pca_reduce(vec_list, n_components):return PCA(n_components).fit_transform(vec_list)X = pd.read_pickle('ml29_01_word_vectors.pkl')x_train = X['word_vector'].to_numpy()
x_train_padded = pad_word_vectors(x_train,300000)
x_train_2d = pca_reduce(x_train_padded,2)
x_train_3d = pca_reduce(x_train_padded,3)

截断和填充的向量表示为 2D 和 3D 向量:

两张图都显示了数据点的清晰分离。

总结一下:DictVectorizer词袋的首字母使文档彼此非常接近。使用 Tfidf,尤其是 WordVectors 显然可以更好地分发文档。

2.3 目标 3:应用聚类算法

KMeans 只是众多聚类算法中的一种。根据这篇关于主题建模的博客文章的建议,应该根据数据的可分离程度来选择聚类算法。例如,K 均值适用于小数据集上的低维值,而当簇的密度和大小可变且数据通常为高维时,基于密度的空间聚类 (DBSCAN) 效果更好。这两种方法都创建平面集群,而另一组算法创建层次集群,例如Ward或由HDBSCAN Paython 库实现的基于层次密度的空间聚类方法。

基于此,我决定使用DBSCAN和OPtics算法,文档称其更适合大型数据集。KMeans 用作基线。

2.3.1 使用 Tfidf 向量进行聚类

KMeans 与 Tfidf

使用 KMeans 时,需要预先给出预期的簇数。尝试不同的数字并检查结果文档分布是关键。

下面的代码展示了创建8个集群的结果:

model = KMeans(n_clusters=8, random_state=0, n_init="auto").fit(x_train)print(model)
# KMeans(n_init='auto', random_state=0)print(model.get_params())
#{'algorithm': 'lloyd', 'copy_x': True, 'init': 'k-means++', 'max_iter': 300, 'n_clusters': 8, 'n_init': 'auto', 'random_state': 0, 'tol': 0.0001, 'verbose': 0}print(model.labels_)
#[4 6 6 6 6 4 2 4 2 4 2 4 2 2 2 2 2 2 2 2 4 4 4 4 4 4 2 4 3 4 0 6 5 6 3 2 4
# 1 4 5 4 0 1 2 1 1 2 2 0 6 2 1 2 1 5 5 2 2 7 2 5 5 5 5 5 4 4 2 4 1 2 2 2 2
# 5 2 2 2 4 0 5 5 2 6 6 2 5 0 0 5 0 1 4 4 2 5 0 2 2 2 6 6 4 6 0 0 5 2 2 4 4
# 0 0 5 1 1 1 1 6 2 0 2 2 5 4 2 4 4 4 1 2 1 2 2 2 0 4 4 4 4 2 4 3 6 3 3 7 7
# 3 3 1 2 2 2 2 4 4 1 1 4 4 2 2 2 0 2 2 4 6 6 2 4 0 0 7 6 4 6 7 2 4 6 6 1 7
# 4 1 1 1 4 7 4 4 4 4 4 6 5 7 4 4 7 2 6 4 5 6 6 6 6 6 3 4 5 1 1 5 3 3 5 1 6
# 3 3 6 1 6 6 1 1 6 6 6 6 3 3 2 3 1 3 1 3 3 6 1 6 5 6 5 3 6 6 1 3 3 3 3 5 6
# 5 6 6 3 7 2 3 2 1 4 6 3 1]

视觉表示显示簇的分离不均匀:

让我们尝试创建 5 个集群。

model = KMeans(n_clusters=8, random_state=0, n_init="auto").fit(x_train)print(model.labels_)
# [0 2 2 2 4 0 2 0 4 3 3 4 3 3 3 3 3 3 3 3 0 0 0 0 0 4 3 4 4 4 3 4 2 4 4 3 4
#  4 0 2 3 0 1 3 4 2 4 3 0 4 3 1 3 2 2 2 3 3 0 3 2 2 2 4 4 4 0 3 0 2 3 3 3 3
#  1 3 3 3 4 3 1 4 3 2 2 3 2 0 0 2 0 1 4 4 3 2 0 3 2 2 2 2 0 2 2 0 1 3 2 4 0
#  0 3 4 1 1 1 1 2 3 3 3 3 4 0 3 4 0 0 1 3 1 3 3 3 3 0 0 0 0 3 0 4 2 4 4 0 0
#  4 4 4 3 3 3 3 4 0 1 1 4 0 3 3 3 3 3 3 4 1 2 3 4 0 3 0 4 4 2 0 3 0 2 1 1 0
#  4 1 1 2 4 0 0 0 0 0 0 2 2 0 0 0 0 4 4 4 2 2 2 2 1 2 4 0 2 1 1 2 4 4 4 1 2
#  4 4 2 2 2 2 2 1 4 2 2 2 4 4 3 4 1 4 1 4 4 2 2 2 2 2 2 4 4 2 2 4 4 4 4 2 2
#  2 2 2 4 0 3 4 3 2 0 4 2 1]

5 个簇的直方图显示出明显的分离:

在 3D 图中绘制文档也很有前途:

2.4 DBSCAN 与 Tfidf

以下代码片段加载 pickles 数据、提取预处理文本并应用 Tfidf 矢量器。然后,通过实例化 SciKit learn 对象来创建 DBSCAN 算法。

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.cluster import DBSCAN
import pandas as pdX = pd.read_pickle('ml29_01.pkl')
x_train = X['preprocessed'].tolist()
vectorizer = TfidfVectorizer()
x_train = vectorizer.fit_transform(x_train)

使用没有任何参数化的 DBSCAN 会产生非常令人惊讶的结果:

model = DBSCAN()
print(model.get_params())
# {'algorithm': 'auto', 'eps': 0.5, 'leaf_size': 30, 'metric': 'euclidean', 'metric_params': None, 'min_samples': 5, 'n_jobs': None, 'p': None}print(model.labels_)
# [-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
# -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
# ...
#  -1 -1 -1 -1 -1 -1 -1 -1]

该值-1表示数据噪声太大,无法聚类。这是什么原因呢?是因为 Tfidf 向量稀疏吗?这可以通过 TruncatedSVD 来解决。

from sklearn.decomposition import TruncatedSVDdef pca_reduce(vec_list, n_components):return TruncatedSVD(n_components).fit_transform(vec_list)x_train_3d = pca_reduce(x_train, 3)
model = DBSCAN().fit(x_train_3d)print(model.get_params())
# {'algorithm': 'auto', 'eps': 0.5, 'leaf_size': 30, 'metric': 'euclidean', 'metric_params': None, 'min_samples': 5, 'n_jobs': None, 'p': None}print(model.labels_)
# [0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
# 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
# ...
# 0 0 0 0 0 0 0 0 0 0 0 0 0]

现在,所有文档都被放入唯一的向量中。

三、带 Tfidf 的光学器件

光学算法无法在sparseTfODF 矢量化器返回的矩阵类型上运行。需要预先通过应用 Numpy 转换方法将其转换为稠密矩阵。

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.cluster import OPTICSX = pd.read_pickle('ml29_01.pkl')
x_train = X['preprocessed'].tolist()
vectorizer = TfidfVectorizer()
x_train = vectorizer.fit_transform(x_train).todense()

应用具有一个参数的 OPTICS 算法可得出以下结果:

model = OPTICS(min_samples=10).fit(np.array(x_train))print(model.get_params())
#{'algorithm': 'auto', 'cluster_method': 'xi', 'eps': None, 'leaf_size': 30, 'max_eps': inf, 'memory': None, 'metric': 'minkowski', 'metric_params': None, 'min_cluster_size': None, 'min_samples': 10, 'n_jobs': None, 'p': 2, 'predecessor_correction': True, 'xi': 0.05}print(model.labels_)
#[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
# 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
# ...
# 0 0 0 0 0 0 0 0 0 0 0 0 0]

不幸的是,所有文档都被再次放入一个且只有一个簇中。

四、Tfidf使用总结

        使用 Tfidf 向量仅显示 KMeans 聚类的良好结果。尤其是5个簇时,可以实现明显的分离。相反,DBSCAN 和 Optics 仅将所有文档放置在同一簇中。

4.1 使用 WordVector 进行聚类-带有词向量的 KMeans

        应用字向量遵循与上述相同的过程,从 DataFrame 加载其表示,然后对 300.000 个值(6000 个字)应用填充和截断。

        这次,KMeans 展现了一个惊喜:当使用 300.000 长度的向量时,所有文档也被放入一个簇中:

x_train_padded = pad_word_vectors(x_train,300000)n_clusters = 5
model = KMeans(n_clusters, random_state=0, n_init="auto").fit(x_train_padded )print(model.labels_)
# [2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
#  2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
#  2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2
#  2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 4 2 2 2 2
#  2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 0 2 2 2 2 2 2 2 2 2 2 2 2
#  2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
#  2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2
#  2 2 2 2 2 2 2 2 2 2 2 2 2]

将它们简化为 3D 向量可实现良好的分离:

x_train_padded = pad_word_vectors(x_train,300000)
x_train_3d = pca_reduce(x_train_padded,3)n_clusters = 5
model = KMeans(n_clusters, random_state=0, n_init="auto").fit(x_train_3d)print(model.labels_)
#[0 0 1 2 2 2 2 0 0 0 0 4 0 3 0 4 0 2 4 3 4 4 1 0 3 4 0 3 4 0 0 0 4 4 4 4 0
# 4 2 0 0 0 3 2 4 4 2 3 0 4 0 3 0 3 1 2 0 0 0 0 4 0 0 1 4 2 1 1 2 1 0 0 0 0
# 0 2 2 4 0 4 0 0 2 2 3 4 4 4 0 0 0 0 2 2 4 2 1 4 1 2 2 2 3 4 1 3 0 0 3 0 4
# 4 0 0 2 1 4 2 1 4 0 0 3 0 4 2 2 2 4 1 0 0 1 4 3 0 2 3 4 4 0 4 4 2 0 2 0 0
# 4 3 1 0 0 4 0 4 2 1 2 2 3 0 0 0 4 0 0 3 0 2 0 3 0 0 0 3 0 0 0 3 0 2 4 2 0
# 3 0 0 2 4 0 3 1 0 0 4 4 3 0 0 1 0 3 4 4 1 2 0 1 0 4 3 3 2 4 0 1 1 0 0 1 3
# 3 2 3 2 4 0 4 1 4 4 0 0 4 4 4 2 4 0 0 2 2 3 1 1 2 1 1 0 0 4 1 3 4 3 3 1 2
# 1 3 3 0 3 3 4 4 4 2 0 4 1]

以下是所有簇的直方图和 3D 表示:

另外,让我们看看集群 1 和 5 的词云。

可以看出,集群1是关于Python、软件、系统和模型的。

第 5 组是关于航天器、卫星和太空的。这种区别对我来说看起来很有希望。

4.2 数据库扫描

        让我们看看DBSCAN如何处理新的集群数据。

x_train_padded = pad_word_vectors(x_train,300000)model = DBSCAN().fit(x_train_3d)print(model.labels_)
# [-1  0 -1 -1 -1 -1 -1  0 -1 -1  0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
#  -1 -1 -1 -1 -1 -1  0 -1 -1 -1 -1 -1 -1 -1 -1 -1  0 -1 -1 -1 -1 -1 -1 -1
#   0 -1 -1 -1  0 -1 -1 -1 -1  0  0 -1 -1  0  0 -1 -1 -1 -1 -1 -1 -1  0 -1
#  -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
#  -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1  0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
#   0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
#  -1 -1 -1  0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1  0  0 -1  0 -1 -1
#  -1 -1 -1 -1 -1  0  0 -1  0 -1 -1 -1 -1 -1 -1 -1  0 -1 -1 -1 -1 -1 -1 -1
#  -1 -1 -1 -1 -1 -1  0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
#  -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
#  -1 -1 -1 -1 -1 -1 -1 -1 -1  0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
#  -1 -1 -1 -1 -1 -1 -1 -1]

        不幸的是,使用 300.000 维将所有结果放入 1 个簇中。通过 3D PCA 减少向量,一些文档仍然被检测为-1,噪声太大的数据。

        为了改善 DBSCAN 结果,我尝试了不同的参数,例如DBSCAN(eps=1.0, min_samples=10, algorithm='brute',但无济于事 - 聚类结果仍然存在。另一种选择是定义自定义距离函数,但我没有在本文的范围内应用它。

五、光学

        使用 300.000 维向量时,OPTICS 算法显示相同的结果:

x_train_padded = pad_word_vectors(x_train,300000)model = OPTICS(min_samples=10).fit(np.array(x_train_padded))
print(model.labels_)
# [-1  0 -1 -1 -1 -1 -1  0  0 -1  0 -1 -1 -1  0 -1 -1 -1 -1 -1 -1 -1 -1 -1
#  -1 -1 -1 -1 -1 -1  0 -1 -1 -1 -1 -1 -1 -1 -1 -1  0  0 -1 -1 -1 -1 -1 -1
#   0 -1 -1 -1  0 -1 -1 -1  0  0  0 -1 -1  0  0 -1 -1 -1 -1 -1 -1 -1  0 -1
#   0 -1  0 -1 -1 -1 -1 -1  0 -1 -1 -1 -1 -1 -1 -1  0 -1  0  0 -1 -1 -1 -1
#  -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1  0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
#   0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
#  -1 -1 -1  0 -1 -1 -1 -1  0 -1 -1 -1 -1 -1 -1 -1 -1 -1  0  0 -1  0  0 -1
#  -1 -1 -1 -1 -1  0  0 -1  0 -1 -1 -1 -1 -1 -1 -1  0 -1  0 -1 -1 -1 -1 -1
#  -1 -1 -1 -1 -1 -1  0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1  0
#  -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1  0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
#  -1 -1 -1 -1 -1 -1 -1 -1 -1  0  0 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1
#  -1 -1 -1 -1 -1  0 -1 -1]

        但在绘制 3D 版本时,簇开始出现。

x_train_3d = pca_reduce(x_train_padded,3)model = OPTICS(min_samples=10).fit(np.array(x_train_3d))
print(model.labels_)
# [-1  2  5 -1 -1 -1 -1  2  2  0  2  3 -1 -1  1  3 -1 -1  4 -1  4  3  5 -1
#  -1  3  0 -1  4  1  2  0 -1  4 -1 -1 -1  4 -1 -1  2  2 -1 -1 -1 -1 -1 -1
#   2 -1  1 -1  2 -1  5 -1  2  2  2  1  4  2  2  5  4 -1  5  5 -1  5  2  0
#   2  0  2 -1 -1  3  0 -1  2  1 -1 -1 -1  3  3 -1  2  0  2  2 -1 -1  4 -1
#   5 -1  5 -1 -1 -1 -1  3 -1 -1  1  2 -1 -1  3  3 -1  1 -1  5  4 -1  5  3
#   2 -1 -1  0 -1 -1 -1 -1 -1  5 -1  1  5  4 -1  1 -1 -1  4  4  1  4 -1 -1
#  -1 -1 -1  2  3 -1  5  1  1  4  0 -1 -1  5 -1 -1 -1  0  2  2 -1  2  2 -1
#  -1 -1 -1 -1  0  2  2 -1  2  1 -1 -1  1 -1 -1 -1  2 -1  2  1 -1  4  0 -1
#   5 -1  1  3  3 -1  2  1  5 -1 -1  4 -1 -1 -1  1  5 -1 -1 -1 -1 -1 -1  2
#  -1  5 -1  1  5 -1 -1 -1 -1 -1 -1  2 -1  5  4 -1 -1  1  3 -1  4 -1 -1  1
#  -1 -1 -1 -1  5  5 -1  5  5  2  2 -1  5 -1 -1 -1 -1 -1 -1 -1 -1 -1  0 -1
#  -1 -1 -1 -1 -1  2 -1 -1]

        经过一些参数调整,我最终得到了以下表示:

model = OPTICS(min_samples=5, metric='minkowski').fit(np.array(x_train_3d))print(model.get_params())
# {'algorithm': 'auto', 'cluster_method': 'xi', 'eps': None, 'leaf_size': 30, 'max_eps': inf, 'memory': None, 'metric': 'minkowski', 'metric_params': None, 'min_cluster_size': None, 'min_samples': 5, 'n_jobs': None, 'p': 2, 'predecessor_correction': True, 'xi': 0.05}print(model.labels_)
# [-1 -1 18 -1 -1 12 -1 -1  3 -1  4  8  6 15 -1  9  6 -1 10 -1 10  8 -1 -1
#  -1  8  0 16 10 -1  4  0 11 10  9 11 -1 10 13 -1  5 -1 15 13 11  9 14 -1
#   5  9 -1 15 -1 -1 -1 -1  3  5 -1 -1 10 -1 -1 18 10 -1 -1 -1 -1 -1 -1  0
#  -1  0  2 14 -1  7  0 10  3 -1 14 13 -1  8 -1  9  5  0  3  3 -1 -1 10 -1
#  -1 11 -1 -1 13 -1 15 -1 -1 16 -1  4 16 -1 -1 -1 -1 -1 -1 18 10 13 18  7
#   4 -1 17 -1 11 -1 -1 12 11 -1 -1  1 -1 10 -1 -1 12 17 10 10 -1 10  9 -1
#   6 12 -1 -1  7 -1 18  1 -1 11  0 -1 -1 -1 -1 14 15  0  4  4 11  5  3 -1
#  -1 -1  6 16 -1  4  4 -1 -1 -1 -1 16  1 13 -1 13  5 17  2  1 13 10  0 -1
#  -1 -1  1  7  8 -1  5 -1 -1 -1 15 10  9 19 13 -1 -1 -1  9 -1 -1 13  9  2
#  19 18  6 -1 -1 15 17 12 -1 13  9  2 11 -1 10 11  6 -1  8  9 10 -1  9 -1
#   7 14 12 -1 18 -1 13 18 19  4  2 -1 18 15 11 17 15 19 -1 19 -1 -1  0 -1
#  15 11 10 11 14  3 11 -1]

直方图和 3D 绘图显示簇是分开的:

 WordVectors 使用总结

        WordVectors 产生了令人惊讶的结果。一般来说,使用 300.000 维的原始向量是不适用的:由每个文本的词序决定的数据方差太大,无法找到有意义的聚类。需要应用降维技术。然后,KMeans 再次显示出最好的分离度,其次是 OPTICS。

六、结论

        文档分类结果取决于输入数据的形状以及分类算法。在将简单的词袋向量与 KMeans 结合使用时出现相当令人失望的结果后,本文将 Tfidf 和词向量与 KMeans、DBSCAN 和 Optics 结合起来。这些实验中最关键的学习点是:a)Tfidf 提供了干净的分离,无需降维即可使用,b)WordVectors 只能在降维后应用,c)KMeans 提供良好的聚类结果,无需任何参数化, d) OPTICS 也提供了很好的集群,但其参数需要调整。

相关文章:

维基百科文章爬虫和聚类:高级聚类和可视化

一、说明 维基百科是丰富的信息和知识来源。它可以方便地构建为带有类别和其他文章链接的文章,还形成了相关文档的网络。我的 NLP 项目下载、处理和应用维基百科文章上的机器学习算法。 在我的上一篇文章中,KMeans 聚类应用于一组大约 300 篇维基百科文…...

springboot智慧导诊系统源码:根据患者症状匹配挂号科室

一、系统概述 医院智慧导诊系统是在医疗中使用的引导患者自助就诊挂号,在就诊的过程中有许多患者不知道需要挂什么号,要看什么病,通过智慧导诊系统,可输入自身疾病的症状表现,或选择身体部位,在经由智慧导诊…...

Shell脚本如何使用 for 循环、while 循环、break 跳出循环和 continue 结束本次循环

Shell脚本如何使用 for 循环、while 循环、break 跳出循环和 continue 结束本次循环 下面是一个简单的 Shell 脚本示例,演示了如何使用 for 循环、while 循环、break 跳出循环和 continue 结束本次循环。 #!/bin/bash# For循环 echo "For循环示例:…...

n个人排成一圈,数数123离队

#include<stdio.h> int main() { int i, n100,k0,j0,a[1000]{0};//k&#xff1a;数数123的变量&#xff0c;j记录离开队列人数的变量scanf("%d",&n);for(int ii0; ii<n; ii){ for( i0; i<n; i){// printf("wei%d ",i);if((a[i]0)&&…...

深度学习基础回顾

深度学习基础 浅层网络 VS 深层网络深度学习常用的激活函数Sigmoid 函数ReLU 函数Softplus 函数tanh函数 归纳偏置CNN适用数据归纳偏置 RNN适用数据归纳偏置 浅层网络 VS 深层网络 浅层神经网络参数过多&#xff0c;导致模型的复杂度和计算量很高&#xff0c;难以训练。而深层…...

【Vue】修改组件样式并动态添加样式

文章目录 目标修改样式动态添加/删除样式样式不生效 注意&#xff1a;类似效果el-step也可以实现&#xff0c;可以不用手动实现。这里只是练习。 目标 使用组件库中的组件&#xff0c;修改它的样式并动态添加/删除样式。 修改样式 组件中的一些类可能添加样式无法生效。如Ele…...

GO设计模式——12、外观模式(结构型)

目录 外观模式&#xff08;Facade Pattern&#xff09; 外观模式的核心角色&#xff1a; 优缺点 使用场景 代码实现 外观模式&#xff08;Facade Pattern&#xff09; 外观模式&#xff08;Facade Pattern&#xff09;又叫作门面模式&#xff0c;是一种通过为多个复杂的子…...

一.初始typescript

什么是ts 首先我们要确认typescript是一个语言&#xff0c;是等同于JavaScript层级得&#xff0c;并不是一些人认为得是JavaScript得类型规范工具或者插件。 ts与js的差异 从type script这个名字就可以看出&#xff0c;ts其实是JavaScript的一个类型化超集&#xff0c;它增…...

mp3的播放

1.这段vue代码会播放声音&#xff0c;但是会有audio标签 <template><div><audio id"myAudio" controls><source src"./test.mp3" type"audio/mp3" />Your browser does not support the audio tag.</audio></…...

mixamo根动画导入UE5问题:滑铲

最近想做一个跑酷游戏&#xff0c;从mixamo下载滑铲动作后&#xff0c;出了很多动画的问题。花了两周时间&#xff0c;终于是把所有的问题基本上都解决了。 常见问题&#xff1a; 1.【动画序列】人物不移动。 2.【动画序列】人物移动朝向错误。 3.【蒙太奇】人物移动后会被拉回…...

容器资源视图隔离 —— 筑梦之路

先做个记录&#xff0c;抽空再整理 K8s 部署 Lxcfs 准入控制器&#xff0c;实现容器中资源单独可见 - 「Johny」PlayGround Kubernetes 中利用 LXCFS 控制容器资源可见性 - 码农教程 容器资源可视化隔离的实现方法_51CTO博客_容器隔离技术 Lxcfs在容器集群中的使用-腾讯云开…...

浅析嵌入式GUI框架-LVGL

LVGL (Light and Versatile Graphics Library) 是最流行的免费开源嵌入式图形库&#xff0c;可为任何 MCU、MPU 和显示类型创建漂亮的 UI 嵌入式GUI框架对比 Features/框架LVGLFlutter-elinuxArkUI(鸿蒙OS)AWTKQTMIniGUIemWinuC/GUI柿饼UI跨平台是是鸿蒙OS平台是是是是是是设备…...

Unity 关于SetParent方法的使用情况

在设置子物体的父物体时&#xff0c;我们使用SetParent再常见不过了。 但是通常我们只是使用其中一个语法&#xff1a; public void SetParent(Transform parent);使用改方法子对象会保持原来位置&#xff0c;跟使用以下方法效果一样&#xff1a; public Transform tran; ga…...

Linux系统上RabbitMQ安装教程

一、安装前环境准备 Linux&#xff1a;CentOS 7.9 RabbitMQ Erlang 1、系统内须有C等基本工具 yum install build-essential openssl openssl-devel unixODBC unixODBC-devel make gcc gcc-c kernel-devel m4 ncurses-devel tk tc xz socat2、下载安装包 1&#xff09;首先&a…...

ES通过抽样agg聚合性能提升3-5倍

一直以来&#xff0c;es的agg聚合分析性能都比较差&#xff08;对应sql的 group by&#xff09;。特别是在超多数据中做聚合&#xff0c;在搜索的条件命中特别多结果的情况下&#xff0c;聚合分析会非常非常的慢。 一个聚合条件&#xff1a;聚合分析请求的时间 search time a…...

c++详解栈

一.什么是栈 堆栈又名栈&#xff08;stack&#xff09;&#xff0c;它是一种运算受限的数据结构&#xff08;线性表&#xff09;&#xff0c;只不过他和数组不同&#xff0c;数组我们可以想象成一个装巧克力的盒子&#xff0c;你想拿一块巧克力&#xff0c;不需要改变其他巧克…...

Zabbix结合Grafana打造高逼格监控系统

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…...

Linux设备树

一、起源 减少垃圾代码 减轻驱动开发工作量 驱动代码和设备信息分离 参考Open Fireware设计 用来记录硬件平台中各种硬件设备的属性信息 二、基本组成 两种源文件&#xff1a; xxxxx.dts dts是device tree source的缩写xxxxx.dtsi dtsi是device tree source include的缩…...

计算机方向的一些重要缩写和简介

参考&#xff1a; 深度学习四大类网络模型 干货|机器学习超全综述&#xff01; 机器学习ML、卷积神经网络CNN、循环神经网络RNN、马尔可夫蒙特卡罗MCMC、生成对抗网络GAN、图神经网络GNN——人工智能经典算法 MLP&#xff08;Multi Layer Perseption&#xff09;用在神经网络中…...

ardupilot开发 --- git 篇

一些概念 工作区&#xff1a;就是你在电脑里能看到的目录&#xff1b;暂存区&#xff1a;stage区 或 index区。存放在 &#xff1a;工作区 / .git / index 文件中&#xff1b;版本库&#xff1a;本地仓库&#xff0c;存放在 &#xff1a;工作区 / .git 中 关于 HEAD 是所有本地…...

Linux基础命令练习2

案例2&#xff1a;创建命令练习 请在/root创建三个目录分别为student、file、stu18 请在/opt创建三个文本文件分别为1.txt、a.txt、stu.txt 案例3&#xff1a;复制、删除、移动 在目录/opt下创建一个子目录 etime 在目录/opt/etime/创建文件readme.txt,利用vim写入内容 …...

Vue阶段笔记(有js包)

目录 1.要先上传Vue的js包&#xff0c;包的路径在这&#xff1a; 2.获取 3.定义Vue接管的区域和他所要实现的内容 #整体代码如下&#xff1a; Vue的指令(被绑定得必须有声明) #v-bind #v-model #v-on #V-ifV-else-ifV-elseV-show #v-show #v-for 1.要先上传Vue的js包&…...

执行npm run dev报Error: error:0308010C:digital envelope routines::unsupported问题

vue2element-ui项目&#xff0c;在执行npm run dev的时候突然报错&#xff1a; (node:19424) [DEP0111] DeprecationWarning: Access to process.binding(http_parser) is deprecated. (Use node --trace-deprecation ... to show where the warning was created) Er…...

解决微信小程序中 ‘nbsp;‘ 空格不生效的问题

在微信小程序开发中&#xff0c;我们经常会使用 来表示一个空格。这是因为在 HTML 中&#xff0c;空格会被解析为一个普通字符&#xff0c;而不会产生实际的空白间距。而 是一种特殊的字符实体&#xff0c;它被解析为一个不可见的空格&#xff0c;可以在页面上产生真正的空…...

vue el-select封装及使用

基于Element UI的el-select组件进行封装的。该组件实现了一个下拉选择框&#xff0c;具有许多可配置的属性和事件 创建组件index.vue (src/common-ui/select/index.vue) <template><el-selectref"select"v-model"hValue":allow-create"allo…...

了解linux计划任务

本章主要介绍如何创建计划任务 使用 at 创建计划任务 使用 crontab 创建计划任务 有时需要在某个指定的时间执行一个操作&#xff0c;此时就要使用计划任务了。计划任务有两种&#xff1a; 一个是at计划任务&#xff0c;另一个是 crontab计划任务。 下面我们分别来看这两种计…...

等待和通知

引入 由于线程是抢占式执行的,因此线程之间的执行的先后顺序难以预知 但是实际开发中我们希望合理协调多个线程之间执行的先后顺序. 这里的干预线程先后顺序,并不是影响系统的调度策略(内核里调度线程,仍然是无序调度). 就是相当于在应用程序代码中,让后执行的线程主动放弃被…...

vscode 如何将正则匹配到的字符前批量加字符

最近想用vscode将正则匹配到的东西签名批量https&#xff0c;替换时可以用$1来替换正则匹配到的字符串&#xff0c;如下所示...

上个月暴涨34.6%后,SoundHound AI股票现在还能买入吗?

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 揭开SoundHound AI股价波动的原因 S&P Global Market Intelligence的数据显示&#xff0c;在摆脱了10月份的大幅下跌后&#xff0c;SoundHound AI的股价在11月份实现了34.6%的涨幅。 原因是该公司公布了稳健的第三季…...

Termux+Hexo结合内网穿透轻松实现安卓手机搭建博客网站发布公网访问

文章目录 前言 1.安装 Hexo2.安装cpolar3.远程访问4.固定公网地址 前言 Hexo 是一个用 Nodejs 编写的快速、简洁且高效的博客框架。Hexo 使用 Markdown 解析文章&#xff0c;在几秒内&#xff0c;即可利用靓丽的主题生成静态网页。 下面介绍在Termux中安装个人hexo博客并结合…...

程序员的养生指南(生命诚可贵,一人永流传!珍惜生命,从你我做起)

作为程序员&#xff0c;我们经常需要长时间坐在电脑前工作&#xff0c;这对我们的身体健康造成了很大的影响。为了保持健康&#xff0c;我们需要采取一些养生措施来延寿。下面是我个人的一些养生经验和建议&#xff0c;希望能对大家有所帮助。 1、合理安排工作时间&#xff1a;…...

FP独立站怎么搭建?看这一篇就够了!强烈建议收藏!

在2023疫情结束年&#xff0c;商家为了在跨境电商市场上获取更多的份额&#xff0c;FP建站需求大军席卷而来&#xff0c;越来越多的创业者和企业开始涉足跨境电商独立站领域&#xff0c;尤其是FP独立站&#xff0c;FP商家想要通过FP独立站、FP广告投放&#xff0c;FP支付&#…...

【华为OD题库-068】找出经过特定点的路径长度-java

题目 输入一个字符串&#xff0c;都是以大写字母组成&#xff0c;每个相邻的距离是1&#xff0c;第二行输入一个字符串&#xff0c;表示必过的点。 说明 每个点可过多次。求解经过这些必过点的最小距离是多少? 示例1 输入输出示例仅供调试&#xff0c;后台判题数据一般不包含示…...

高性能队列框架-Disruptor使用、Netty结合Disruptor大幅提高数据处理性能

高性能队列框架-Disruptor 首先介绍一下 Disruptor 框架&#xff0c;Disruptor是一个通用解决方案&#xff0c;用于解决并发编程中的难题&#xff08;低延迟与高吞吐量&#xff09;&#xff0c;Disruptor 在高并发场景下性能表现很好&#xff0c;如果有这方面需要&#xff0c;…...

Linux学习笔记3 xshell(lnmp)

xshell能连接虚拟机的前提是真机能够ping通虚拟机网址 装OpenSSL依赖文件 [rootlocalhost nginx-1.12.2]# yum -y install openssl pcre-devel 依赖检测[rootlocalhost nginx-1.12.2]# ./configure [rootlocalhost nginx-1.12.2]# yum -y install zlib [rootlocalhost n…...

分享几个可以免费使用GPT工具

1. 国产可以使用GPT3.5和4.0的网站&#xff0c;每日有免费的使用额度&#xff0c;响应速度&#xff0c;注册时不用使用手机号&#xff0c;等个人信息&#xff0c;注重用户隐私&#xff0c;好评&#xff01; 一个好用的ChatGPT系统 &#xff0c;可以免费使用3.5 和 4.0https://…...

一篇文章带你快速入门 Nuxt.js 服务端渲染

1. Nuxt.js 概述 1.1 我们一起做过的SPA SPA&#xff08;single page web application&#xff09;单页 Web 应用&#xff0c;Web 不再是一张张页面&#xff0c;而是一个整体的应用&#xff0c;一个由路由系统、数据系统、页面&#xff08;组件&#xff09;系统等等&#xff0…...

导入JDBC元数据到Apache Atlas

前言 前期实现了导入MySQL元数据到Apache Atlas, 由于是初步版本&#xff0c;且功能参照Atlas Hive Hook&#xff0c;实现的不够完美 本期对功能进行改进&#xff0c;实现了导入多种关系型数据库元数据到Apache Atlas 数据库schema与catalog 按照SQL标准的解释&#xff0c;…...

大数据项目——基于Django/协同过滤算法的房源可视化分析推荐系统的设计与实现

大数据项目——基于Django/协同过滤算法的房源可视化分析推荐系统的设计与实现 技术栈&#xff1a;大数据爬虫/机器学习学习算法/数据分析与挖掘/大数据可视化/Django框架/Mysql数据库 本项目基于 Django框架开发的房屋可视化分析推荐系统。这个系统结合了大数据爬虫、机器学…...

[网鼎杯 2020 朱雀组]phpweb1

提示 call_user_func()函数先通过php内置函数来进行代码审计绕过system&#xff08;##不止一种方法&#xff09; 拿到题目养成一个好的习惯先抓个包 从抓到的包以及它首页的报错来看&#xff0c;这里死活会post传输两个参数func以及p func传输函数&#xff0c;而p则是传输参数的…...

深度学习之注意力机制

注意力机制与外部记忆 注意力机制与记忆增强网络是相辅相成的&#xff0c;神经网络去从内存中或者外部记忆中选出与当前输入相关的内容时需要注意力机制&#xff0c;而在注意力机制的很多应用场景中&#xff0c;我们的外部信息也可以看作是一个外部的记忆 这是一个阅读理解任务…...

WordPress:解决xmlrpc.php被扫描爆破的风险

使用WordPress的朋友都知道&#xff0c;一些【垃圾渣渣】会利用xmlrpc.php文件来进行攻击&#xff0c;绕过WP后台错误登录次数限制进行爆破。虽然密码复杂的极难爆破&#xff0c;但及其占用服务器资源。 方法一、利用宝塔防火墙&#xff08;收费版&#xff09; 一般可以直接使…...

Fiddler抓包模拟器(雷电模拟器)

Fiddler设置 List item 打开fiddler,的options 点击OK,重启fiddler 模拟器 更改网络设置 IP可以在电脑上终端上查看 然后在模拟器浏览器中输入IP:端口 安装证书...

RepidJson将内容写入文件

使用 RapidJSON 将内容写入文件的步骤如下&#xff1a; 创建一个 rapidjson::Document 对象&#xff0c;将需要写入文件的内容存储到其中。创建一个 rapidjson::StringBuffer 对象来保存 JSON 字符串。将 rapidjson::Document 对象转换为 JSON 字符串&#xff0c;并将其放入 r…...

Endnote使用教程

原由 最近要进行开题报告&#xff0c;要求不低于60文献的阅读与引用&#xff0c;单独插入引入我觉得是非常繁琐的事情&#xff0c;所以就借助Endnote这个工具&#xff0c;减少我们的工作量。 使用方法 第一步&#xff1a;先新建一个数据库&#xff0c;这样子可以在这个数据库…...

java中用Thead创建线程和用Runnable创建线程的区别是什么?

在 Java 中&#xff0c;创建线程的两种主要方式是通过继承 Thread 类和通过实现 Runnable 接口。下面是它们之间的主要区别&#xff1a; 1. 继承 Thread 类&#xff1a; class MyThread extends Thread {public void run() {// 线程执行的代码} }// 创建并启动线程 MyThread …...

0013Java程序设计-基于Vue的上课签到系统的设计与实现

文章目录 **摘 要**目录系统设计4.2学生签到4.3 签到信息列表4.4 用户信息管理5.1系统登录5.1.1 登录5.1.2 清除用户登记记录5.1.3 登录拦截 5.2用户管理5.2.2 用户添加5.2.3 用户编辑5.2.4 用户删除5.2.5 用户分页 5.3签到信息5.3.1签到信息列表 5.4学生签到5.4.1学生签到 开发…...

2.修改列名与列的数据类型

修改字段名与字段数据类型 1.修改字段名 有时&#xff0c;在我们建好一张表后会突然发现&#xff0c;哎呀&#xff01;字段名貌似写错了&#xff01;怎么办&#xff1f;要删了表再重新建一个新表吗&#xff1f;还是要删了这个字段再新建一个新的字段&#xff1f; 都不用&…...

[Firefly-Linux] RK3568 Ubuntu固件分区详解

RK为了方便开发与产品定制&#xff0c;自己定义了一套固件的分区&#xff0c;这些分区信息存放在parameter.txt文件中&#xff0c;Firefly参考这个文件定义了自己的Ubuntu分区&#xff0c;文件为parameter-ubuntu.txt&#xff0c;存放于Linux_SDK的device/rockchip/rk356x目录下…...

SpringBoot项目访问resources下的静态资源

1.新建一个配置文件夹&#xff0c;放配置类 2.编辑 WebMvcConfig.java package com.southwind.configuration;import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; import or…...