Chinaunix首页 | 论坛 | 博客
  • 博客访问: 53807
  • 博文数量: 14
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 152
  • 用 户 组: 普通用户
  • 注册时间: 2014-02-06 21:55
文章分类

全部博文(14)

文章存档

2015年(2)

2014年(12)

我的朋友

分类: Python/Ruby

2014-02-23 16:53:03

KMeans是聚类中常用的一种方法,原理简单,速度快,缺点是必须确定聚类的数目k。
在文本聚类中,一般将每个词算作一个单独的特征,所以特征数目是很大的。如果将文档名作为行,词作为列,通常这个文档-词矩阵是很稀疏的。在K-Means计算中,需要把一个文档的所有词表示成向量,其元素是词的权重(tf-idf等),元素的index是词的索引。在python里,这个向量最好用dict来表示,这样可以避免因矩阵稀疏而存储太多的0。
在<<集体智慧编程>>第三章里有KMeans的python实现,但这个实现有几个缺点:
a. 词向量是用list存的,文档数目一大很容易内存不足(存储太多无用的0)
b. 每个文档词向量的模(cosine的分母)没有提前计算,而是在循环中重复计算

下边是改后的程序,我的输入是一个文件夹,里边每个文件为一个文档,文件名是文档编号.txt(如1.txt), 内容是(词:tfidf值),如:
演讲 0.375399
励志 0.337192
马云 0.242213

点击(此处)折叠或打开

  1. from PIL import Image,ImageDraw
  2. import os, codecs, random
  3. from math import sqrt
  4. #Kmeans聚类,sparse matrix 实现
  5. #保存row的模
  6. row_norms={}
  7. def readfile(dirname):
  8.     rows={}
  9.     for f in os.listdir(dirname):
  10.         fr=codecs.open(dirname+f, 'r', encoding='utf-8')
  11.         #mod=0
  12.         tw_dict={}
  13.         norm=0
  14.         for line in fr:
  15.             items=line.split('\t')
  16.             token=items[0].strip()
  17.             #一个中文字符,舍弃
  18.             if len(token)<2:
  19.                 continue
  20.             w=float(items[1].strip())
  21.             norm+=w**2
  22.             tw_dict[token]=w
  23. #取出文档编号
  24.         rows[int(f[:-4])]=tw_dict
  25.         row_norms[int(f[:-4])]=sqrt(float(norm))
  26.     print len(rows)
  27.     return rows
  28.         #vector_norm[f]=math.sqrt(norm)
  29. #向量模
  30. def norm(vec):
  31.     return sqrt(sum([pow(v,2) for k, v in vec.items()]))
  32. #v1: row, norm_v1: row的norm; v2:聚类中心点, norm_v2:中心点的norm
  33. def cosine(v1, norm_v1, v2, norm_v2):
  34.     # 交集w乘积
  35.     if norm_v1==0 or norm_v2==0:
  36.         return 1.0
  37.     dividend=0
  38.     #sum1Sq=0
  39.     for k, v in v1.items():
  40.         if k in v2:
  41.             dividend+=v*v2[k]
  42.         #sum1Sq+=pow(v, 2)

  43.     # Sums of the squares
  44.     #sum2Sq=sum([pow(v,2) for k, v in v2.items()])    
  45.     #if sum1Sq==0 or sum2Sq==0:
  46.         #return 1.0
  47.     #else:
  48.     return 1.0-dividend/(norm_v1*norm_v2)

  49. #计算数据各个维度的限制
  50. def rows_range(rows):
  51.     tokens={}
  52.     for k, tw in rows.items():
  53.         for t, w in tw.items():
  54.             if t not in tokens:
  55.                 tokens[t]=[]
  56.             tokens[t].append(w)
  57.     res={}
  58.     for t, wl in tokens.items():
  59.         res[t]=(min(wl), max(wl))
  60.     return res

  61. #生成随机初始点
  62. def random_vec(ranges):
  63.     res={}
  64.     for k, v in ranges.items():
  65.         res[k]=random.random()*(v[1]-v[0])+v[0]
  66.     return res

  67. #计算类中心
  68. def center(clust, rows):
  69.     res={}
  70.     s=len(clust)
  71.     for c in clust:
  72.         for k, v in rows[c].items():
  73.             if not k in res:
  74.                 res[k]=0.0
  75.             res[k]+=v
  76.     for k, v in res.items():
  77.         res[k]=v/s
  78.     return res

  79. def kcluster(rows,distance=cosine,k=4):
  80.     # Determine the minimum and maximum values for each point
  81.     ranges=rows_range(rows)
  82.     # Create k randomly placed centroids
  83.     clusters=[]
  84.     for i in range(k):
  85.         clusters.append(random_vec(ranges))

  86.     clusteres_norm=[]
  87.     for i in range(k):
  88.         clusteres_norm.append(norm(clusters[i]))
  89.     lastmatches=None
  90.     for t in range(20):
  91.         print 'Iteration %d' % t
  92.         bestmatches=[[] for i in range(k)]

  93.         # Find which centroid is the closest for each row
  94.         for j in rows.keys():
  95.             #print 'iter row:', j
  96.             row=rows[j]
  97.             row_norm=row_norms[j]
  98.             bestmatch=0
  99.             min_dis=9999999
  100.             for i in range(k):
  101.                 d=distance(row, row_norm, clusters[i],clusteres_norm[i])
  102.                 if d<min_dis:
  103.                     bestmatch=i
  104.                     min_dis=d
  105.             bestmatches[bestmatch].append(j)

  106.         # If the results are the same as last time, this is complete
  107.         if bestmatches==lastmatches:
  108.             break
  109.         lastmatches=bestmatches

  110.         # Move the centroids to the average of their members

  111.         for i in range(k):
  112.             clusters[i]=center(bestmatches[i], rows)

  113.     return bestmatches

  114. if __name__ == '__main__':
  115.     corpus_dir='D:/python_workspace/corpus_res2/sigram/'
  116.     rows=readfile(corpus_dir)
  117.     print 'create vectorspace'
  118.     n=29
  119.     clust=kcluster(rows,k=n)
  120.     fw=open('D:/python_workspace/corpus_res2/kmeans_'+str(n)+'.txt', 'w')
  121.     for c in clust:
  122.         fw.write('%s\n' % ','.join([str(v) for v in c]))


阅读(10882) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~