k-means算法又称k均值,顾名思义就是通过多次求均值而实现的聚类算法。是一种无监督的机器学习方法,即无需知道所要搜寻的目标,而是直接通过算法来得到数据的共同特征。其具体算法思想如下图所示:
1、首先在图中随机选取3个点
2、然后把距离这三个点最近的其他点归为一类
3、取当前类的所有点的均值,作为中心点
4、更新距离中心点最近的点
5、再次计算被分类点的均值作为新的中心点
6、再次更新距离中心点最近的点
通过不断重复上述步骤直至无法再进行更新为止时聚类完成。
了解了算法思想后,我们接下来进入正题,以下是具体的实现步骤,大致分为四步:
步骤一、对文本进行切词和去除停用词。(jieba)
步骤二、计算文本特征并构建 VSM(向量空间模型)。
步骤三、使用 K-means 算法进行聚类。
步骤四、对新文档进行分类并计算分类成功率
步骤一:对文本进行切词和去除停用词(jieba)
原始数据集如下图所示:(为了方便统计对文件名进行了修改)
数据集规模为200,包含类别为:股票、教育、体育和星座等四种类型(样本数目各为50)的文本。
股票类文本示例:
我们可以注意到文本中有许多空格,符号,数字以及一些语气词等影响聚类的效果,因此我们采用github上的jieba分词对文本进行预处理,同时利用网上下的停用词文档结合正则表达式去除语气词和数字等,去除后的效果如下图所示:
停用词文档示例:
该部分的代码片段如下:
def buildSW():
'''停用词的过滤'''
typetxt=open('***') #停用词文档地址
texts=['\u3000','\n',' '] #爬取的文本中未处理的特殊字符
'''停用词库的建立'''
for word in typetxt:
word=word.strip()
texts.append(word)
return texts
def buildWB(texts):
'''语料库的建立'''
for i in range(0,len(all_file)):
filename=all_file[i]
filelabel=filename.split('.')[0]
labels.append(filelabel) #名称列表
file_add='***'+ filename #数据集地址
doc=open(file_add,encoding='utf-8').read()
data=jieba.cut(doc) #文本分词
data_adj=''
delete_word=[]
for item in data:
if item not in texts: #停用词过滤
# value=re.compile(r'^[0-9]+$')#去除数字
value = re.compile(r'^[\u4e00-\u9fa5]{2,}$')#只匹配中文2字词以上
if value.match(item):
data_adj+=item+' '
else:
delete_word.append(item)
corpus.append(data_adj) #语料库建立完成
# print(corpus)
return corpus
步骤二、计算文本特征并构建 VSM(向量空间模型)
在此简单介绍一下TF-IDF:
TF-IDF即逆文本频率指数,是一种统计方法,用以评估一个词对于一个语料库中一份文件的重要程度。词的重要性随着在文件中出现的次数正比增加,同时随着它在语料库其他文件中出现的频率反比下降。
也就是说一个词在某一文档中出现次数比较多,其他文档没有出现,说明该词对该文档分类很重要。
然而如果其他文档也出现比较多,说明该词区分性不大,就用IDF来降低该词的权重。
数学算法:
TF-IDF与一个词在文档中的出现次数成正比,与该词在整个语言中的出现次数成反比
TF-IDF = TF (词频) * IDF(逆文档频率)
词频:TF = 词在文档中出现的次数 / 文档中总词数
逆文档频率:IDF = log(语料库中文档总数 / 包含该词的文档数 +1 )
因此这一步我们需要用到sklearn这个库,具体思想是构建一个i行j列的矩阵,其中i代表待聚类的文本数量,j则代表词的数目。
词频分析结果如下图所示:
该部分代码如下:
def countIdf(corpus):
vectorizer=CountVectorizer()#该类会将文本中的词语转换为词频矩阵,矩阵元素a[i][j] 表示j词在i类文本下的词频
transformer=TfidfTransformer()#该类会统计每个词语的tf-idf权值
tfidf=transformer.fit_transform(vectorizer.fit_transform(corpus))#第一个fit_transform是计算tf-idf,第二个fit_transform是将文本转为词频矩阵
weight=tfidf.toarray()#将tf-idf矩阵抽取出来,元素a[i][j]表示j词在i类文本中的tf-idf权重
return weight
步骤三、使用 K-means 算法进行聚类
思想前面已经说过在此不再复述直接上代码:
def Kmeans(weight,clusters,correct):
mykms=KMeans(n_clusters=clusters)
y=mykms.fit_predict(weight)
result=[]
for i in range(0,clusters):
label_i=[]
gp=0
jy=0
xz=0
ty=0
for j in range(0,len(y)):
if y[j]==i:
label_i.append(labels[j])
type=labels[j][0:2]
if(type=='gp'):
gp+=1
elif(type=='jy'):
jy+=1
elif(type=='xz'):
xz+=1
elif(type=='ty'):
ty+=1
max=jy
type='教育'
if(gp>jy):
max=gp
type='股票'
if(max<xz):
max=xz
type='星座'
if(max<ty):
max=ty
type='体育'
correct[0]+=max
result.append('类别'+'('+type+')'+':'+str(label_i))
return result
在此需说明一点,由于k-means属于无监督机器学习方法,因此事先只能制定结果聚类的数目(在此为4),而无法为每一类指定具体的类别名,为了便于统计我们在分类完成之后,以该类中最多的一类文本来为此类命名,以此来测量结果的准确性。
步骤四、对新文档进行分类并计算分类成功率
分类的结果如下图所示:
经过多次测试正确率大致在86%左右
以下是完整代码:
# -*- coding: utf-8 -*-
import os
import re
from os import listdir
import jieba
from sklearn import feature_extraction
from sklearn.feature_extraction.text import TfidfTransformer
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.cluster import KMeans
all_file=listdir('***') #获取文件夹中所有文件名#数据集地址
outputDir="***" #结果输出地址
labels=[] #用以存储名称
corpus=[] #空语料库
size=200#测试集容量
def buildSW():
'''停用词的过滤'''
typetxt=open('***') #停用词文档地址
texts=['\u3000','\n',' '] #爬取的文本中未处理的特殊字符
'''停用词库的建立'''
for word in typetxt:
word=word.strip()
texts.append(word)
return texts
def buildWB(texts):
'''语料库的建立'''
for i in range(0,len(all_file)):
filename=all_file[i]
filelabel=filename.split('.')[0]
labels.append(filelabel) #名称列表
file_add='***'+ filename #数据集地址
doc=open(file_add,encoding='utf-8').read()
data=jieba.cut(doc) #文本分词
data_adj=''
delete_word=[]
for item in data:
if item not in texts: #停用词过滤
# value=re.compile(r'^[0-9]+$')#去除数字
value = re.compile(r'^[\u4e00-\u9fa5]{2,}$')#只匹配中文2字词以上
if value.match(item):
data_adj+=item+' '
else:
delete_word.append(item)
corpus.append(data_adj) #语料库建立完成
# print(corpus)
return corpus
def countIdf(corpus):
vectorizer=CountVectorizer()#该类会将文本中的词语转换为词频矩阵,矩阵元素a[i][j] 表示j词在i类文本下的词频
transformer=TfidfTransformer()#该类会统计每个词语的tf-idf权值
tfidf=transformer.fit_transform(vectorizer.fit_transform(corpus))#第一个fit_transform是计算tf-idf,第二个fit_transform是将文本转为词频矩阵
weight=tfidf.toarray()#将tf-idf矩阵抽取出来,元素a[i][j]表示j词在i类文本中的tf-idf权重
# word=vectorizer.get_feature_names()#获取词袋模型中的所有词
# for j in range(len(word)):
# if weight[1][j]!=0:
# print(word[j], weight[1][j])
return weight
def Kmeans(weight,clusters,correct):
mykms=KMeans(n_clusters=clusters)
y=mykms.fit_predict(weight)
result=[]
for i in range(0,clusters):
label_i=[]
gp=0
jy=0
xz=0
ty=0
for j in range(0,len(y)):
if y[j]==i:
label_i.append(labels[j])
type=labels[j][0:2]
if(type=='gp'):
gp+=1
elif(type=='jy'):
jy+=1
elif(type=='xz'):
xz+=1
elif(type=='ty'):
ty+=1
max=jy
type='教育'
if(gp>jy):
max=gp
type='股票'
if(max<xz):
max=xz
type='星座'
if(max<ty):
max=ty
type='体育'
correct[0]+=max
result.append('类别'+'('+type+')'+':'+str(label_i))
return result
def output(result,outputDir,clusters):
outputFile='out'
type='.txt'
count=0
while(os.path.exists(outputDir+outputFile+type)):
count+=1
outputFile='out'+str(count)
doc = open(outputDir+outputFile+type, 'w')
for i in range(0,clusters):
print(result[i], file=doc)
print('本次分类总样本数目为:'+str(size)+' 其中正确分类数目为:'+str(correct[0])+' 正确率为:'+str(correct[0]/size), file=doc)
doc.close()
texts=buildSW()
corpus=buildWB(texts)
weight=countIdf(corpus)
clusters=4
correct=[0]#正确量
result=Kmeans(weight,clusters,correct)
output(result,outputDir,clusters)
print('finish')
其他资源:
1、文本数据集下载地址:
链接:https://pan.baidu.com/s/1zR5ymSBZ5wF0KJVpYVSb5g提取码:5e76
2、jieba分词:https://github.com/fxsjy/jieba
3、sklearn安装:
python命令:
pip install -U scikit-learn
参考博客:
【1】爱编程真是太好了,聚类k-means算法详解 ,
【2】懒骨头707,机器学习之文本分类,
【3】无语_人生,Python基于Kmeans算法实现文本聚类的简单练习,
【4】liuxuejiang158,python scikit-learn计算tf-idf词语权重,