自然语言处理-文本聚类

1 实验介绍

1.1 关于本实验

文本聚类是指将相似度较大的文档分成一类,通过将自然语言文字信息转换成数学信息,以高维空间点的形式展现出来,通过计算点的距离远近进行聚类,簇内点的距离尽量近,但簇与簇之间的点要尽量远。实验介绍了K-Means聚类算法的原理和步骤,主成分分析方法用于数据降维,并介绍了K-Means的ARI和轮廓系数两个评估指标。最后给出英文文本聚类实例。

1.2 实验目的

了解文本聚类的原理和流程;理解K-Means算法、主成分分析算法原理;理解K-Means评价指标的意义;掌握K-Means文本聚类的应用。

2 K-Means算法

2.1 算法原理

K均值聚类算法(K-Means)由Stuart Lloyd于1957年提出,是一种迭代求解的聚类分析算法。K-Means通过样本之间的距离的均值,把相似度高的样本聚成一簇,形成不同的类别。该算法最大的特点是简单,便于理解,运算速度快,但是只能应用于连续型数据,并且必须在聚类前指定分类数。

实现K-Means聚类算法的步骤如下。

步骤1:确定最终聚类数,给出K值。

步骤2:随机选定K个值,计算每一个样本到K个值的距离,将样本点归到最相似的类中,分成K个簇,产生“质心”一簇中所有数据的均值。

步骤3:反复计算K个簇的质心,直到质心不再改变,最终确定每个样本所属的类别以及每个类的质心。

2.2 数学理论实现

K-Means算法运行过程如下图所示。

k-means算法运行步骤如下所示:

步骤1:初始数据集如图中(a)所示,确定k=2。

步骤2:随机选择两个点作为质心-----圆点和叉号,如图中(b)所示。

步骤3:计算样本与圆点质心和叉号质心的距离,标记每个样本的类别,如图中©所示。

步骤4:反复迭代,标记圆点和叉号各自新的质心,如图中(d)所示

步骤5:直到质心不再改变,最终得到两个类别,如图中(e)和(f)所示。

在这里插入图片描述

2.3 Python实现

【任务1】Python实现K-Means算法举例

import numpy as np
import matplotlib.pyplot as plt
import random
def get_distance(p1, p2):
    diff = [x-y for x, y in zip(p1, p2)]
    distance = np.sqrt(sum(map(lambda x: x**2, diff)))
    return distance
# 计算多个点的中心
# cluster = [[1,2,3], [-2,1,2], [9, 0 ,4], [2,10,4]]
def calc_center_point(cluster):
    N = len(cluster)
    m = np.matrix(cluster).transpose().tolist()
    center_point = [sum(x)/N for x in m]
    return center_point
# 检查两个点是否有差别
def check_center_diff(center, new_center):
    n = len(center)
    for c, nc in zip(center, new_center):
        if c != nc:
            return False
    return True
 
# K-means算法的实现
def K_means(points, center_points):
    N = len(points)         # 样本个数
    n = len(points[0])      # 单个样本的维度
    k = len(center_points)  # k值大小
    tot = 0
    while True:             # 迭代
        temp_center_points = []           # 记录中心点
        clusters = []       # 记录聚类的结果
        for c in range(0, k):
            clusters.append([]) # 初始化
        #针对每个点,寻找距离其最近的中心点(寻找组织)
        for i, data in enumerate(points):
            distances = []
            for center_point in center_points:
                distances.append(get_distance(data, center_point))
            index = distances.index(min(distances)) # 找到最小距离的那个中心点的索引
            clusters[index].append(data)    #中心点代表的簇,里面增加一个样本
        tot += 1
        print(tot, '次迭代', clusters)
        k = len(clusters)
        colors = ['r.', 'g.', 'b.', 'k.', 'y.']  # 颜色和点的样式
        for i, cluster in enumerate(clusters):
            data = np.array(cluster)
            data_x = [x[0] for x in data]
            data_y = [x[1] for x in data]
            plt.subplot(2, 3, tot)
            plt.plot(data_x, data_y, colors[i])
            plt.axis([0, 1000, 0, 1000])
 
        # 重新计算中心点
        for cluster in clusters:
            temp_center_points.append(calc_center_point(cluster))
        # 在计算中心点的时候,需要将原来的中心点算进去
        for j in range(0, k):
            if len(clusters[j]) == 0:
                temp_center_points[j] = center_points[j]
        # 判断中心点是否发生变化
        for c, nc in zip(center_points, temp_center_points):
            if not check_center_diff(c, nc):
                center_points = temp_center_points[:]   # 复制一份
                break
        else:   # 如果没有变化,退出迭代,聚类结束
            break
    plt.show()
    return clusters # 返回聚类的结果
  
# 随机获取一个样本集,用于测试K-means算法
def get_test_data():
    N = 1000
     # 产生点的区域
    area_1 = [0, N / 4, N / 4, N / 2]
    area_2 = [N / 2, 3 * N / 4, 0, N / 4]
    area_3 = [N / 4, N / 2, N / 2, 3 * N / 4]
    area_4 = [3 * N / 4, N, 3 * N / 4, N]
    area_5 = [3 * N / 4, N, N / 4, N / 2]
    areas = [area_1, area_2, area_3, area_4, area_5]
    k = len(areas)
     # 在各个区域内,随机产生一些点
    points = []
    for area in areas:
        rnd_num_of_points = random.randint(50, 200)
        for r in range(0, rnd_num_of_points):
            rnd_add = random.randint(0, 100)
            rnd_x = random.randint(area[0] + rnd_add, area[1] - rnd_add)
            rnd_y = random.randint(area[2], area[3] - rnd_add)
            points.append([rnd_x, rnd_y])
      # 自定义中心点,目标聚类个数为5,因此选定5个中心点
    center_points = [[0, 250], [500, 500], [500, 250], [500, 250], [500, 750]]
    return points, center_points
if __name__ == '__main__':
    points, center_points = get_test_data()
    clusters = K_means(points, center_points)
    #print('#######最终结果##########')
    #for i, cluster in enumerate(clusters):
        #print('cluster ', i, ' ', cluster)

程序运行结果如下:

在这里插入图片描述

3 主成分分析

3.1算法原理

在多变量的问题中,变量之间往往存在信息重叠,通过正交变换将一组可能存在相关性的变量转换为一组线性不相关的变量,转换后的变量称为主成分。主成分分析(Principal Component Snalysis,PCA)是找出数据中重主要的特征代替原有数据,保持原有数据的方差信息,通常用于高维数据的降维、数据压缩和预处理。

PCA具有以如下优点:

(1)仅以方差衡量信息量。

(2)各主成分之间正交,可消除原始数据成分间相互影响的因素。

(3)计算方法简单,主要运算是特征值分解,易于实现。

PCA算法的主要缺点如下:

(1)主成分各个特征维度的含义具有一定的模糊性。

(2)非主成分也可能含有对样本差异的重要信息。

3.2 composition参数

Sklearn 提供decomposition.PCA用于主成分分析,具体语法形式如下所示:

PCA(n_components=n)

参数取值有小数和整数之分,取值为小数,表示保留百分之多少的信息;取值为整数,则表示减少到多少特征。

【任务2】n_components举例

import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from sklearn.datasets import make_blobs
# make_blobs:多类单标签数据集,为每个类分配一个或多个正态分布的点集
# X为样本特征,Y为样本簇类别, 共1000个样本,每个样本3个特征,共4个簇
X, y = make_blobs(n_samples=10000, n_features=3, centers=[[3,3,3],[0,0,0], [1,1,1], [2,2,2]], cluster_std=[0.2, 0.1, 0.2, 0.2], random_state =9)
fig = plt.figure()
ax = Axes3D(fig, rect=[0, 0, 1, 1], elev=30, azim=20)
plt.scatter(X[:, 0], X[:, 1], c=y, s=10, cmap='viridis')  # Adjust 's' parameter

程序运行结果如下:

<matplotlib.collections.PathCollection at 0x7f679564df30>

在这里插入图片描述

投影后三个特征维度的方差为98.3%,0.85%,0.83%,第一个特征占了绝大多数比例,下面是主成分分析进行特征降维,对n_components取值分为如下两种情况。

情况一:n_components取整数。

#从三维降到二维,选择前两个特征,而抛弃第三个特征。

from sklearn.decomposition import PCA
pca = PCA(n_components=2)    #减少到2个特征
pca.fit(X)
print(pca.explained_variance_ratio_)
print(pca.explained_variance_)
X_new = pca.transform(X)
plt.scatter(X_new[:, 0], X_new[:, 1],marker='o')  #将转化后的数据分布可视化
plt.show()

程序运行结果如下:

[0.98318212 0.00850037]
[3.78521638 0.03272613]

在这里插入图片描述

情况二:n_components取小数。

#保存95%的信息。

pca = PCA(n_components=0.95)    #保留95%的信息
pca.fit(X)
print(pca.explained_variance_ratio_)
print(pca.explained_variance_)
print(pca.n_components_)

程序运行结果如下:

[0.98318212]
[3.78521638]
1

只有第一个投影特征被保留。这是由于第一个主成分占投影特征的方差比例高达98%,只选择这一个特征维度便可以浪花一满足95%的阈值。

3.3 对鸢尾花数据降维

【任务3】使用sklearn实现对鸢尾花数据降维,将原先四维特征数据降维为二维。

import matplotlib.pyplot as plt
from sklearn.decomposition import PCA
from sklearn import datasets
iris = datasets.load_iris() 
x = iris.data
y = iris.target
print(x)

#加载PCA算法,设置降维后主成分数目为2
pca = PCA(n_components=2)    
reduced_x=pca.fit_transform(x)
print(reduced_x)

red_x,red_y=[],[]
blue_x,blue_y =[],[]
green_x,green_y=[],[]
for i  in range(len(reduced_x)):
    if  y[i] ==0:
        red_x.append(reduced_x[i][0])
        red_y.append(reduced_x[i][1])
    elif y[i] ==1:
        blue_x.append(reduced_x[i][0])
        blue_y.append(reduced_x[i][1])
    else:
        green_x.append(reduced_x[i][0])
        green_y.append(reduced_x[i][1])
plt.scatter(red_x,red_y,c='r',marker ='x')
plt.scatter(blue_x,blue_y,c='b',marker ='D')
plt.scatter(green_x,green_y,c='g',marker ='.')
plt.show()

程序运行结果如下:

在这里插入图片描述

4 K-Means评估指标

Sklearn提供调整兰德系数和轮廓系数用于评价K-Means的性能,确定最佳K值。

4.1 调整兰德系数

当数据具有新属类别,采用调整兰德系数(Adjusted Rand Index,ARI)指标来评价K-Means的性能。ARI取值范围为[一1,1],值越大意味着聚类结果与真实情况越吻合。

Sklearn提供了adjusted_rand_score()函数计算ARI。

adjusted_rand_score (y_test,y_pred)

y_true:真实值。 y_pred:预测值。

【任务5】ARI举例

from sklearn.metrics import adjusted_rand_score
y_true=[3,-0.5,2,7]
y_pred=[2.5,0.0,2,8]
print(adjusted_rand_score(y_true,y_pred))

程序运行结果如下:

1.0

4.2 轮廓系数

当数据没有所属类别时,使用轮廓系数(Silhouette Coefficient)度量聚类效果。轮廓系数兼顾聚类的凝聚度和分离度,取值范围为[一1,1],数值越大,聚类效果越好。

对于任意点i的轮廓系数,数学公式如下。

在这里插入图片描述

参数说明如下:

(1)a(i)是指点i到所有簇内其他点的距离的平均值。

(2)b(i)是指点i到与相邻最近的一簇内的所有点的平均距离的最小值。

计算轮廓系数的具体步骤如下。

步骤1:对于已聚类数据中的第i个样本X(i),计算X(i)与其同一个类簇中的所有其他样本距离的平均值,记作a(i),用于量化簇内的凝聚度。

步骤2:选取X(i)外的一个簇b,计算X()与簇b中所有样本的平均距离,遍历所有其他簇,找到最近平均距离的那个簇,记作b(i),用于量化簇间的分离度。

步骤3:对于样本X(i),计算轮廓系数S(i)。

由轮廓系数S(i)的计算公式可知,如果S(i)小于0,说明X(i)与其簇内元素的平均距离大于最近的其他簇,表示簇类效果不好;如果α(i)趋于0,或者b(i)足够大,那么S(i)趋近1,说明聚类效果比较好。

Sklearn提供了silhouette_scoret()计算所有点的平均轮廓系数,函数如下。

silhouette_score(X,labels)

X:特征值。 labels:被聚类标记的目标值。

【任务6】轮廓系数举例

# 生成数据模块
from sklearn.datasets import make_blobs
# k-means模块
from sklearn.cluster import KMeans
# 评估指标——轮廓系数,前者为所有点的平均轮廓系数,后者返回每个点的轮廓系数
from sklearn.metrics import silhouette_score, silhouette_samples
import numpy as np
import matplotlib.pyplot as plt
# 生成数据
x_true, y_true = make_blobs(n_samples= 600 , n_features= 2, centers= 4, random_state= 1)

# 绘制出所生成的数据
plt.figure(figsize= (6, 6))
plt.scatter(x_true[:, 0], x_true[:, 1], c= y_true, s= 10)
plt.title("Origin data")
plt.show()

# 根据不同的n_centers进行聚类
n_clusters = [x for x in range(3, 6)]
for i in range(len(n_clusters)):
    # 实例化k-means分类器
    clf = KMeans(n_clusters= n_clusters[i], n_init=10)
    y_predict = clf.fit_predict(x_true)
    
    # 绘制分类结果
    plt.figure(figsize= (6, 6))
    plt.scatter(x_true[:, 0], x_true[:, 1], c= y_predict, s= 10)
    plt.title("n_clusters= {}".format(n_clusters[i]))
    ex = 0.5
    step = 0.01
    xx, yy = np.meshgrid(np.arange(x_true[:, 0].min() - ex, x_true[:, 0].max() + ex, step),
                         np.arange(x_true[:, 1].min() - ex, x_true[:, 1].max() + ex, step))
    zz = clf.predict(np.c_[xx.ravel(), yy.ravel()])
    zz.shape = xx.shape
    
    plt.contourf(xx, yy, zz, alpha= 0.1)
    plt.show()
    
    # 打印平均轮廓系数
    s = silhouette_score(x_true, y_predict)
    print("When cluster= {}\nThe silhouette_score= {}".format(n_clusters[i], s))    
    #silhouette_samples()返回每个点的轮廓系数,计算轮廓系数为正的点的个数。
    n_s_bigger_than_zero = (silhouette_samples(x_true, y_predict) > 0).sum()
    print("{}/{}\n".format(n_s_bigger_than_zero, x_true.shape[0]))

程序运行结果如下:

在这里插入图片描述

在这里插入图片描述

When cluster= 3
The silhouette_score= 0.6009420412542107
595/600

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

When cluster= 4
The silhouette_score= 0.637556444143356
599/600

在这里插入图片描述

When cluster= 5
The silhouette_score= 0.5628971136582673
600/600

5 K_Means英文文本聚类

采用K-Means进行英文文本聚类步骤如下。

第一步:使用Pandas的DataFrame进行数据格式化。

第二步:使用NLTK的语料库对英文文本进行分词和去除停止词。

第三步:使用np.vectorizer进行向量化处理。

第四步:使用Tfidfvectorizer构造词袋模型。

第五步:使用cosine_similarity计算余弦相似度,构造相关性矩阵。

第六步:使用K-Means算法进行聚类操作。

【任务6】k_means英文文本聚类

import pandas as pd
import numpy as np
import re      #正则表达式
import nltk 
corpus = ['The sky is blue and beautiful.',
 'Love this blue and beautiful sky!',
          'The quick brown fox jumps over the lazy dog.',
          'The brown fox is quick and the blue dog is lazy!',
          'The sky is very blue and the sky is very beautiful today',
          'The dog is lazy but the brown fox is quick!'
]
labels = ['weather', 'weather', 'animals', 'animals', 'weather', 'animals']
 
corpus = np.array(corpus)
corpus_df = pd.DataFrame({'Document': corpus, 'categoray': labels})
 
# 载入英文的停用词表
stopwords = nltk.corpus.stopwords.words('english')
# 建立词分割模型
cut_model = nltk.WordPunctTokenizer()
# 定义分词和停用词去除的函数
def Normalize_corpus(doc):
    # 去除字符串中结尾的标点符号
    doc = re.sub(r'[^a-zA-Z0-9\s]', '', string=doc)
    # 字符串变小写格式
    doc = doc.lower()
    # 去除字符串两边的空格
    doc = doc.strip()
    # 进行分词操作
    tokens = cut_model.tokenize(doc)
    # 使用停止用词表去除停用词
    doc = [token for token in tokens if token not in stopwords]
    # 将去除停用词后的字符串使用' '连接,为了接下来的词袋模型做准备
    doc = ' '.join(doc)
    return doc

Normalize_corpus = np.vectorize(Normalize_corpus)
# 调用函数进行分词和去除停用词
corpus_norm = Normalize_corpus(corpus)

# TF-idf模型
from sklearn.feature_extraction.text import TfidfVectorizer
Tf = TfidfVectorizer(use_idf=True)
Tf.fit(corpus_norm)
vocs = Tf.get_feature_names_out()
corpus_array = Tf.transform(corpus_norm).toarray()
corpus_norm_df = pd.DataFrame(corpus_array, columns=vocs)
#print(corpus_norm_df)

#计算余弦相似度 
#使用cosine_similarity计算余弦相似度 ,构造相关性矩阵
from sklearn.metrics.pairwise import cosine_similarity
similarity_matrix = cosine_similarity(corpus_array)
similarity_matrix_df = pd.DataFrame(similarity_matrix)

# Kmeans聚类 
from sklearn.cluster import KMeans
model = KMeans(n_clusters=2, n_init=10)     #聚类为2
model.fit(np.array(similarity_matrix))
print(model.labels_)
corpus_norm_df['k_labels'] = np.array(model.labels_)
print(corpus_norm_df)

程序运行结果如下:

[1 1 0 0 1 0]
beautiful blue brown dog fox jumps lazy
0 0.604749 0.518224 0.000000 0.000000 0.000000 0.000000 0.000000
1 0.455454 0.390289 0.000000 0.000000 0.000000 0.000000 0.000000
2 0.000000 0.000000 0.375653 0.375653 0.375653 0.542607 0.375653
3 0.000000 0.357850 0.417599 0.417599 0.417599 0.000000 0.417599
4 0.357583 0.306421 0.000000 0.000000 0.000000 0.000000 0.000000
5 0.000000 0.000000 0.447214 0.447214 0.447214 0.000000 0.447214

​ love quick sky today k_labels
0 0.000000 0.000000 0.604749 0.000000 1
1 0.657873 0.000000 0.455454 0.000000 1
2 0.000000 0.375653 0.000000 0.000000 0
3 0.000000 0.417599 0.000000 0.000000 0
4 0.000000 0.000000 0.715166 0.516505 1
5 0.000000 0.447214 0.000000 0.000000 0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值