南理工数据挖掘大作业(SyskillWebert)

全部代码及完整版实验报告:https://download.csdn.net/download/qq_33614902/10442074

1 认知数据

这次的实验内容来源于一个应用问题。我们希望挖掘现有的网页内容和用户评级,以预测出其他网页的用户评级。数据集中一共有327个样本,每个样本包含1个的网页源代码和1个用户评级。数据集分为两部分,一部分包含287个样本,作为训练集;另一部分包含40个样本,作为测试集。最终的目的是改进学得模型,提高其预测的准确率。

2 数据预处理

网页中正面或者负面的关键词对网页的评级有很大影响。程序中构造了一种将网页的HTML源转换成0、1特征向量的函数,每个属性都有一个值,该值指示一个特定的关键词是否存在(如果存在则为1,不存在则为0)。

2.1 选取关键词

简单的来说,一个经常出现在评级为hot页面上的关键词很少出现在评级为cold的页面上;反之亦然。我们通过计算信息增益的方式来选取关键词。

具体的计算公式如下:

前人的论文报告表明,当关键词个数在75至150之间时,学到的模型表现最好。

2.2 提取特征向量

关于如何从网页的HTML文件中提取合适的特征向量,具体的做法如下:

1、将选取的关键词存入一个字符串数组String keywords[]中。

2、将HTML文件中的所有字母转换成小写字母,并提取出其中的字符串存入Set<String> set中。(使用Set集合的原因是Set内部采用的是非常高效的平衡检索二叉树:红黑树,插入、查找效率比用其他容器高。)

3、遍历keywords[]中的每个元素,若set集合中存在该元素,对应的特征为1,否则为0。

2.3 代码实现

代码实现部分只给出核心代码,完整代码可参见源代码。

数据预处理的具体实现在Preprocessing.java中。

2.3.1 提取关键词

读取关键字,并存入数组中。

File file = newFile("src/test/load_key");

                    BufferedReader br = newBufferedReader(new FileReader(file));

                    String str = null;

                    while ((str = br.readLine())!= null)

                    {

                           str = str.trim();

                           keywords =str.split(",");

                    }

                    br.close();

2.3.2 处理HTLM文件

对网页的源码做词法分析,并放入set集合中。

Set<String>set_file_word = new HashSet<String>();

             try

             {

                    File file = new File(url);

                    BufferedReader br = newBufferedReader(new FileReader(file));

                    String str = null;

                    while ((str = br.readLine())!= null)

                    {

                           str.trim();

                           str =str.toLowerCase();

                           String f_word ="";

                           for (int i = 0; i< str.length();)

                           {

                                  if(Character.isLetter(str.charAt(i)))

                                  {

                                         f_word= "";

                                         for (;i < str.length();)

                                         {

                                                if(Character.isLetter(str.charAt(i)))

                                                {

                                                       f_word= f_word + str.charAt(i);

                                                       i++;

                                                }else

                                                {

                                                       set_file_word.add(f_word);

                                                       break;

                                                }

                                         }

                                  } else

                                  {

                                         i++;

                                  }

                           }

                    }

2.3.3 提取特征向量

for (int i = 0;i < k_l; i++)

             {

                    if(set_file_word.contains(keywords[i]))

                    {

                           train_arr[m][i] = 1;

                    } else

                           train_arr[m][i] = 0;

             }

2.4 时间效率

如上图所示数据预处理的总时间为233ms。样本总数为327,则每个样本的预处理时间为0.713ms。

3 朴素贝叶斯分类器

具体的原理参考周志华老师的《机器学习》第七章。

3.1 算法分析

朴素贝叶斯分类是一种十分简单的分类算法,叫它朴素贝叶斯分类是因为这种方法的思想真的很朴素,朴素贝叶斯的思想基础是这样的:对于给出的待分类项,求解在此项出现的条件下各个类别出现的概率,哪个最大,就认为此待分类项属于哪个类别。

朴素贝叶斯分类的正式定义如下:

1、设为一个待分类项,而每个a为x的一个特征属性。

2、有类别集合。

3、计算,,…,。

4、如果,,…,,则。

那么现在的关键就是如何计算第三步中的各个条件概率。我们可以这么做:

1、找个一个已知分类的待分类项集合,这个集合叫做训练样本集。

2、统计得到在各类别下各个特征属性的条件概率估计。即

3、如果各个特征属性是条件独立的,则根据贝叶斯定理有如下推导:

因为分母对于所有类别为常数,因为我们只要将分子最大化即可。又因为各特征属性是条件独立的,所以有

3.2 代码实现

代码实现部分只给出核心代码,完整代码可参见源代码。

朴素贝叶斯分类器的具体实现在NaiveBayesClassifier.java中。

   public static void byes()// 贝叶斯分类器

      {

             int k_l = Preprocessing.k_l;

             int train_l =Preprocessing.train_l;

             int total_l =Preprocessing.total_l;

             double accuracy = 0;

             int num_correct = 0;

      Vector<Integer> v_cold = new Vector<Integer>();

             Vector<Integer> v_middle =new Vector<Integer>();

             Vector<Integer> v_hot = newVector<Integer>();

             // 统计各类别样本的数量

             for (int i = 0; i < train_l;i++)

             {

                    if(Preprocessing.train_arr[i][k_l] == 1)

                    {

                           v_cold.add(i);

                    } else if(Preprocessing.train_arr[i][k_l] == 2)

                    {

                           v_middle.add(i);

                    } else

                    {

                           v_hot.add(i);

                    }

             }

             // 统计各中情况的训练样本的数量

             int[][][] num = newint[3][k_l][2];// 如num[1][1][1]存储属于第2个类别的第2个特征值为1的训练样本的个数

             for (int i = 0; i < train_l;i++)

             {

                    int cla =Preprocessing.train_arr[i][k_l] - 1;// 所属类别

                    for (int j = 0; j < k_l;j++)

                    {

                           int character =Preprocessing.train_arr[i][j];// 特征值的取值0或者1

                           num[cla][j][character]++;

                    }

             }

             //

             for (int i = train_l; i <total_l; i++)

             {

                    double p_cold = 1.0 *v_cold.size() / train_l;

                    double p_middle = 1.0 *v_middle.size() / train_l;

                    double p_hot = 1.0 *v_hot.size() / train_l;

                    int cla =Preprocessing.train_arr[i][k_l];// 真实分类

                    String real_rating = null;

                    if (cla == 1)

                           real_rating ="cold";

                    else if (cla == 2)

                           real_rating ="middle";

                    else

                           real_rating ="hot";

                    for (int j = 0; j < k_l;j++)// 计算每种分类分别对应的概率

                    {

                           int character =Preprocessing.train_arr[i][j];                   

                           p_cold = p_cold * 1.0* num[0][j][character] / v_cold.size();

                           p_middle = p_middle *1.0 * num[1][j][character] / v_middle.size();

                           p_hot = p_hot * 1.0 *num[2][j][character] / v_hot.size();

                    }

                    System.out.println(p_cold +"  " + p_middle + "  " + p_hot);

                    double p =Math.max(Math.max(p_cold, p_middle), p_hot);

                    //printAnswer

                    String stringCla;

                    if(p == p_cold)

                           stringCla =toStringCla(1);

                    else if(p == p_middle)

                           stringCla =toStringCla(2);

                    else

                           stringCla =toStringCla(3);

                    System.out.println("使用贝叶斯分类器预测URL= " +Preprocessing.set_tra_url[i] + " 的网站用户评级为:"+stringCla);

                    System.out.print("网站的真实评级为:" + real_rating);

}

3.3 时间效率及精度

3.3.1 时间效率

使用贝叶斯分类器建立模型及预测数据的总时间为13ms,共预测了40个样本,那么对每个样本的预测时间为0.325ms。

3.3.2 精度

测试集样本数量为40,预测正确数量为31,预测准确率为77.5%

4 决策树

具体的原理参考周志华老师的《机器学习》第四章。

4.1 算法分析

一般的,一颗决策树包含一个根结点、若干个内部结点和若干个叶结点;叶结点对应于决策结果,其他每个结点则对应于一个属性测试;每个结点包含的样本集合根据属性测试的结果被划分到子结点中;根结点包含样本全集。从根结点到每个叶结点的路径对应了一个判定测试序列。决策树学习的目的是为了产生一个泛化能力强,即处理未见示例能力强的决策树,其基本流程遵循简单且直观的“分而治之”策略,如图4.1所示。

图4.1 决策树学习基本算法

4.2 代码实现

代码实现部分只给出核心代码,完整代码可参见源代码。

决策树的具体实现在Decision.java中。

4.2.1 生成决策树函数

   public static voidgenerateTree(LinkedList<Integer[]> data, ArrayList<Integer> attri,Node parent, Integer faAttri)

      {

             Node subnode = new Node();

             subnode.setFatherAttribution(faAttri);

             subnode.setIsLeaf(false);

             subnode.setParent(parent);// 设置当前结点的父结点

             parent.addChildren(subnode);// 设置当前结点为父结点的孩子结点

             if (true)

             {

                    int count = -1;

                    Integer cla = 0;

                    boolean flag = true;

                    for (Integer[] subdata : data)

                    {

                           ++count;

                           if (count == 0)

                           {

                                  cla = subdata[k_l];

                           } else

                           {

                                  if (!subdata[k_l].equals(cla))

                                  {

                                         flag = false;

                                         break;

                                  }

                           }

                    }

                    if (flag)

                    {

                           subnode.setIsLeaf(true);

                           subnode.setName(String.valueOf(cla));

                           ++count_node;

                           return;

                    }

             }

             // attri为空,或者所有数据在attri上取值相同,将subnode标记为叶结点,其类别标记为D中样本数最多的类

             if (attri == null || attri.size()== 0 || isLike(data, attri))

             {

                    subnode.setIsLeaf(true);

                    int cla = maxCountCla(data);

                    subnode.setName(String.valueOf(cla));

                    ++count_node;

                    return;

             }

             // 从A中选择最优化分属性a

             int attriIndex = maxGain(data,attri);

             subnode.setAttribution(newInteger(attriIndex));

             for (int val = 0; val < 2;val++)

             {

                    LinkedList<Integer[]>newData = new LinkedList<Integer[]>();

                    ArrayList<Integer>newAttri = new ArrayList<Integer>();

                    for (Integer inte : attri)

                    {

                           if (inte.equals(newInteger(attriIndex)))

                           {

                                  continue;

                           }

                           newAttri.add(inte);

                    }

                    for (Integer[] ddata : data)

                    {

                           if(ddata[attriIndex].equals(new Integer(val)))

                           {

                                  newData.add(ddata);

                           }

                    }

                    if (newData == null || newData.size()== 0)

                    {

                           Node nnode = newNode();

                           nnode.setFatherAttribution(newInteger(val));

                           nnode.setParent(subnode);

                           subnode.addChildren(nnode);

                           int claa =maxCountCla(newData);

                           nnode.setIsLeaf(true);

                           nnode.setName(String.valueOf(claa));

                           ++count_node;

                           return;

                    } else

                    {

                           generateTree(newData,newAttri, subnode, new Integer(val));

                    }

             }

      }

4.2.2属性上的值是否相同

判断样本在某些属性上的值是否相同。

   publicstatic Boolean isLike(LinkedList<Integer[]> data,ArrayList<Integer> attri)

      {

             if (data == null || data.size()<= 1)

             {

                    return true;

             }

             for (Integer index : attri)

             {

                    int count = -1;

                    Integer attVal = null;

                    for (Integer[] subdata : data)

                    {

                           count++;

                           if (count == 0)

                           {

                                  attVal = subdata[index];

                                  continue;

                           }

                           if (!attVal.equals(subdata[index]))

                           {

                                  return false;

                           }

                    }

             }

             return true;

      }

4.2.3 样本最多的类

返回样本最多的类。

   publicstatic int maxCountCla(LinkedList<Integer[]> data)

      {

             int[] cou_cla = new int[4];

             Arrays.fill(cou_cla, 0);

             for (Integer[] subdata : data)

             {

                    cou_cla[subdata[k_l]]++;

             }

             int max = Math.max(cou_cla[1],Math.max(cou_cla[2], cou_cla[3]));

             for (int i = 1; i < 4; i++)

             {

                    if (max == cou_cla[i])

                           return cou_cla[i];

             }

             return cou_cla[3];

      }

4.2.4 计算信息增益

计算最大的信息增益,并获取对应的属性。

   publicstatic int maxGain(LinkedList<Integer[]> data, ArrayList<Integer> attri)

      {

             // 只需要计算ai=1,ai=0时,(D1/D)*(Ent(D1)) + (D0/D)*(Ent(D0))

             int minIndex = -1;

             double minEnt = 10000;

             int count = -1;

             for (Integer index : attri)

             {

                    count++;

                    double nowEnt = 0.0;

                    index = (int) index;

                    int[] dv = new int[2];

                    Arrays.fill(dv, 0);

                    int[][] couCla = new int[2][4];//如couCla[0][3],代表在该属性上值为0,类别为3的样本总数

                    for (int i = 0; i < 2; i++)

                    {

                           Arrays.fill(couCla[i],0);

                    }

                    for (int val = 0; val <2; val++)

                    {

                           for (Integer[] subdata: data)

                           {

                                  if (subdata[index].equals(newInteger(val)))

                                  {

                                         dv[val]++;

                                         couCla[val][subdata[k_l]]++;

                                  }

                           }

                    }

                    double[][] entK = new double[2][4];

                    for (int val = 0; val <2; val++)

                    {

                           for (int i = 1; i< 4; i++)

                           {

                                  if (couCla[val][i]== 0)

                                  {

                                         entK[val][i]= 0;

                                         continue;

                                  }

                                  double p = couCla[val][i]* 1.0 / (couCla[val][1] + couCla[val][2] + couCla[val][3]);// 每个类别所占的比例

                                  entK[val][i] =-(p * (Math.log(p) / Math.log(2.0)));

                           }

                           nowEnt = nowEnt + 1.0* dv[val] / data.size() * (entK[val][1] + entK[val][2] + entK[val][3]);//

                    }

                    if (count == 0 || nowEnt< minEnt)

                    {

                           minEnt = nowEnt;

                           minIndex = index;

                    }

             }

             return minIndex;

      }

4.3 时间效率及精度

4.3.1时间效率

训练集中样本数量为287,生成决策树模型的时间为31ms;测试集中样本数量为40,对测试集的预测时间为0ms。

这个可以理解,因为一旦生成决策树模型,那么对每个测试集中的样本预测时,算法的时间复杂度仅与决策树的高度h有关。

4.3.2 精度

测试集数量为40,预测正确的数量为9,预测的准确率为22.5%。

5 分析和结论

以下分析和结论基于本实验的特定数据集和本人所编写的程序。

1)从获取模型所需时间、分类的精度来看,朴素的贝叶斯分类方法比没有剪枝处理的决策树算法表现要好。

一方面,我们的训练集很小,属性很多,高偏差/低方差的分类器(朴素贝叶斯分类器)更有优势。由于数据集很小,即使条件独立假设不成立,NB在实际中仍然表现的非常好。但是,随着训练集的增大,高偏差的分类器并不能训练出非常准确的模型,所以低偏差/高方差的分类器将会胜出;

另一方面,没做剪枝处理的决策树在这样小规模的训练集上表现得不可能很好。在本实验中训练数据仅287个,生成的决策树中共有133个结点,其中叶子结点的个数为67个。

2)从分类所需时间来看,决策树算法表现比朴素贝叶斯分类方法更好。

原因很简单,用决策树来预测时,每个测试样本的预测时间仅与树的高度h有关,又因为测试集样本的数量仅为40,所以预测所需时间几乎为0ms;而使用朴素贝叶斯分类器预测时,需要计算各种条件概率,时间复杂度自然会稍微高一些。



猜你喜欢

转载自blog.csdn.net/qq_33614902/article/details/80479695
今日推荐