weka StringToWordVector是如何选择词典的

最近在做基于内容的推荐,试了几种方法:向量空间模型(用lucene实现)、贝叶斯分类、聚类(用weka的SimpleKMeans)。

用聚类做推荐,我的思路是首先将所有文本进行聚类,如果一类中有用户读过的书,则将这类中他没有读过的书推荐给他。

文本聚类实现是用的weka,首先用StringToWordVector过滤器,将文本转化为向量,然后用SimpleKMeans进行聚类。但聚类的效果不太理想,经常有一类包含50%以上的数据。虽然对这一类多聚几次也能把数据分开。

向量的维度选择很重要,也就是选那些词做为基本的维度。这一步是用StringToWordVector实现的,这个过滤器,选取一些词,然后将文本转化为向量。我以为选取的过程是用tf-idf算的,也就是保留tf-idf较大的前多少个词。查看源码后发现不是这样的。

下面这个方法就是选取词典的方法:

 /**
  * determines the dictionary.
  */
 private void determineDictionary() {
    //太长了,省略掉,大家去看源码
 }

有个内部类,用于统计每个词的词频和文档频率:

/**
* Used to store word counts for dictionary selection *based on a threshold.
*/
private class Count implements Serializable, RevisionHandler {
    /** for serialization. */
    static final long serialVersionUID = 2157223818584474321L;

    /** the counts. */
    public int count, docCount;

    /**
     * the constructor.
     * 
     * @param c  the count
     */
    public Count(int c) {
        count = c;
    }
}

选取词典的大概步骤如下:
1、首先分词,然后统计每个词的词频和文档频率,存储在TreeMap dictionaryArr[0]中,key是词,value是Count,其中有词频和文档频率;
2、然后将所有词频存于一个int array[]中,然后非降序排序。
3、然后根据要的词数和总次数,确定一个值int prune[0],下面要选取的词的词频必须大于等于这个值。

所以字典不是根据tf*idf的值选取的,而是根据tf。所以如果想要根据tf-idf选取词典就要稍微修改下程序。

—————-下面不太重要—————
其中上面第二步中排序用的是如下代码:

   /**
     * sorts an array.
     * 
     * @param array
     *            the array to sort
     */
    public static void sortArray(int[] array) {

        int i, j, h, N = array.length - 1;

        for (h = 1; h <= N / 9; h = 3 * h + 1)
            ;

        for (; h > 0; h /= 3) {
            for (i = h + 1; i <= N; i++) {
                int v = array[i];
                j = i;
                while (j > h && array[j - h] > v) {
                    array[j] = array[j - h];
                    j -= h;
                }
                array[j] = v;
            }
        }
    }

这个好像是希尔排序,但排序有些小问题,如下测试结果是错误的:

    @Test
    public void testArray() {
        int[] array = {3, 1, 3, 2, 5, 4, 0};
        StringToWordVector.sortArray(array);
        System.out.println(Arrays.toString(array));
    }

结果:[3, 0, 1, 2, 3, 4, 5]
所以大家用这个类时改下这个排序方法。


如果想让最后的向量是tf*idf的值,记得设置filter.setOutputWordCounts(true);

你可能感兴趣的:(数据挖掘)