说明:本文依据《中文自然语言处理入门实战》完成。目前网上有不少转载的课程,我是从GitChat上购买。
这两节课分别是分类和聚类问题,刚才突然大脑短路,在想分类和聚类的区别。
- 分类就是根据文本的特征或属性,划分到已有的类别中。也就是说,这些类别是已知的,通过对已知分类的数据进行训练和学习,找到这些不同类的特征,再对未分类的数据进行分类。
- 聚类就是你压根不知道数据会分为几类,通过聚类分析将数据或者说用户聚合成几个群体,那就是聚类了。聚类不需要对数据进行训练和学习。
分类属于监督学习,聚类属于无监督学习。
第七课 基于ML的中文短文本聚类
文本聚类是将一个个文档由原有的自然语言文字信息转化成数学信息,以高维空间点的形式展现出来,通过计算哪些点距离比较近,从而将那些点聚成一个簇,簇的中心叫做簇心。一个好的聚类要保证簇内点的距离尽量的近,但簇与簇之间的点要尽量的远。
图中,以 K、M、N 三个点分别为聚类的簇心,将结果聚为三类,使得簇内点的距离尽量的近,但簇与簇之间的点尽量的远。
源码给的太垃圾了,我还是自己来做吧,这节课其实就是对文本进行聚类,通过K-mean完成,最后可视化。
在这里,我载入的是CNKI的一批音乐教育文章的题目,我们来看一下这些短文本的聚类。
文本格式如下
从音乐治疗起步 向文化传承发展——音乐教育改革与民族音乐文化传承论坛在北川中学举行
展望21世纪学科教育——“学科教育展望丛书”简介
浅谈钢琴后备人才的培养
致力于两岸音乐教育交流的台湾陈功雄教授
从基础教育需求看高师音乐专业人才的培养
高等音乐教育中理论课教学模式分析与改革
中国民族音乐教育的主体建设与整合意识
试论民族音乐学家的“守土职责”
from sklearn.feature_extraction.text import CountVectorizer, TfidfTransformer
from sklearn.manifold import TSNE
from sklearn.cluster import KMeans
# from data_utils import *
import jieba
import matplotlib.pyplot as plt
import pandas as pd
from sklearn.decomposition import PCA
# bigram分词
segment_bigram = lambda text: " ".join([word + text[idx + 1] for idx, word in enumerate(text) if idx < len(text) - 1])
# 结巴中文分词
segment_jieba = lambda text: " ".join(jieba.cut(text))
stopwords = pd.read_csv(r'C://Users//01//Desktop//stopwords.txt', index_col=False, quoting=3, sep="\t",
names=['stopword'], encoding='utf-8')
stopwords = stopwords['stopword'].values
corpus = []
with open(r"C://Users//01//Desktop//titledata.txt", "r", encoding="utf-8") as f:
for line in f:
# 去掉标点符号
segs = jieba.lcut(line)
segs = [v for v in segs if not str(v).isdigit()] # 去数字
segs = list(filter(lambda x: x not in stopwords, segs)) # 去掉停用词
segs.remove('\n')
segs=''.join(segs) #这一步很关键,因为list没有lower(),之前教程上也没说,就一直报错。
corpus.append(segs)
# print(segs)
计算tf-idf设为权重
vectorizer=CountVectorizer()
transformer=TfidfTransformer()
tfidf=transformer.fit_transform(vectorizer.fit_transform(corpus))
获取词袋模型中的所有词语特征,如果特征数量非常多的情况下可以按照权重降维
word=vectorizer.get_feature_names()
print("word feature length: {}".format(len(word)))
导出权重,到这边就实现了将文字向量化的过程,矩阵中的每一行就是一个文档的向量表
tfidf_weight=tfidf.toarray()
为什么用tfidf值作为权重?
将文本向量化的方式其实有很多,最简单的就是one-hot方式,在之前的文章中也讲过这种方式的实现原理,如果不用TF-IDF设置权重,那么,后面进行文本向量化之后的矩阵值只有0、1两种,词与词之间的权重没有进行区分,所以用这种方式设置权重。
numClass = 10
clf = KMeans(n_clusters=numClass, max_iter=10000, init='k-means++', tol=1e-6)
pca = PCA(n_components=10)
TnewData = pca.fit_transform(tfidf_weight)
s = clf.fit(TnewData)
def plot_cluster(result, newData, numClass):
plt.figure(2)
Lab = [[] for i in range(numClass)]
index = 0
for labi in result:
Lab[labi].append(index)
index += 1
color = ['oy', 'ob', 'og', 'cs', 'ms', 'bs', 'ks', 'ys', 'yv', 'mv', 'bv', 'kv', 'gv', 'y^', 'm^', 'b^', 'k^',
'g^'] * 3
for i in range(numClass):
x1 = []
y1 = []
for ind1 in newData[Lab[i]]:
# print ind1
try:
y1.append(ind1[1])
x1.append(ind1[0])
except:
pass
plt.plot(x1, y1, color[i])
# 绘制初始中心点
x1 = []
y1 = []
for ind1 in clf.cluster_centers_:
try:
y1.append(ind1[1])
x1.append(ind1[0])
except:
pass
plt.plot(x1, y1, "rv") # 绘制中心
plt.show()
pca = PCA(n_components=10) # 输出10维
newData = pca.fit_transform(tfidf_weight) # 载入N维
result = list(clf.predict(TnewData))
plot_cluster(result, newData, numClass)
降维使用了 PCA,按照教程的要求试试 TSNE,TSNE 保留下的属性信息,更具代表性,也即最能体现样本间的差异,但是 TSNE 运行极慢,PCA 则相对较快。
from sklearn.manifold import TSNE
ts = TSNE(2)
newData = ts.fit_transform(tfidf_weight)
result = list(clf.predict(TnewData))
plot_cluster(result, newData, numClass)
为了更好的表达和获取更具有代表性的信息,在展示(可视化)高维数据时,更为一般的处理,常常先用 PCA 进行降维,再使用 TSNE
newData = PCA(n_components=10).fit_transform(tfidf_weight) # 载入N维
newData = TSNE(2).fit_transform(newData)
result = list(clf.predict(TnewData))
plot_cluster(result, newData, numClass)