全部代码及完整版实验报告: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;而使用朴素贝叶斯分类器预测时,需要计算各种条件概率,时间复杂度自然会稍微高一些。