周志华《机器学习》课后习题(第七章):贝叶斯分类

简介: 周志华《机器学习》课后习题(第七章):贝叶斯分类

7.1 试使用极大似然法估算回瓜数据集 3.0 中前 3 个属性的类条件概率.


答:

以第一个属性色泽为例,其值计数如下:

  • 色泽 乌黑 浅白 青绿
  • 好瓜
  • 否 2 4 3
  • 是 4 1 3

image.png


其他两个属性同理。


7.2* 试证明:条件独立性假设不成立时,朴素贝叶斯分类器仍有可能产生最优贝叶斯分类器.


答:


image.png

image.png


只有在两者决策边界之间(浅黄色区域),其分类情况是不同的,在其他区域,朴素贝叶斯分类结果和最优贝叶斯的分类结果是相同的,因此即便属性之间独立性假设不成立,朴素贝叶斯在某些条件(本例中就是属性概率分布在两者相交区域之外)下任然是最优贝叶斯分类器。

参考:

《On the Optimality of the Simple Bayesian Classifier under Zero-One Loss》

ps.这个例子就是来自该论文,只做了一点翻译工作。论文中给出了更全面的理论证明,和朴素贝叶斯产生最优贝叶斯分类的充分必要条件。本打算看完把理论证明也尝试复述一遍,但能力有限,一方面没有理解很透彻,另一方面证明过程有点长感觉表达能力有点不大够用...


7.3 试编程实现拉普拉斯修正的朴素贝叶斯分类器,并以西瓜数据集 3.0 为训练集,对 p.151 "测1" 样本进行判别.


答:

han1057578619/MachineLearning_Zhouzhihua_ProblemSets

import numpy as np
import pandas as pd
from sklearn.utils.multiclass import type_of_target
from collections import namedtuple
def train_nb(X, y):
    m, n = X.shape
    p1 = (len(y[y == '是']) + 1) / (m + 2)  # 拉普拉斯平滑
    p1_list = []  # 用于保存正例下各属性的条件概率
    p0_list = []
    X1 = X[y == '是']
    X0 = X[y == '否']
    m1, _ = X1.shape
    m0, _ = X0.shape
    for i in range(n):
        xi = X.iloc[:, i]
        p_xi = namedtuple(X.columns[i], ['is_continuous', 'conditional_pro'])  # 用于储存每个变量的情况
        is_continuous = type_of_target(xi) == 'continuous'
        xi1 = X1.iloc[:, i]
        xi0 = X0.iloc[:, i]
        if is_continuous:  # 连续值时,conditional_pro 储存的就是 [mean, var] 即均值和方差
            xi1_mean = np.mean(xi1)
            xi1_var = np.var(xi1)
            xi0_mean = np.mean(xi0)
            xi0_var = np.var(xi0)
            p1_list.append(p_xi(is_continuous, [xi1_mean, xi1_var]))
            p0_list.append(p_xi(is_continuous, [xi0_mean, xi0_var]))
        else:  # 离散值时直接计算各类别的条件概率
            unique_value = xi.unique()  # 取值情况
            nvalue = len(unique_value)  # 取值个数
            xi1_value_count = pd.value_counts(xi1)[unique_value].fillna(0) + 1  # 计算正样本中,该属性每个取值的数量,并且加1,即拉普拉斯平滑
            xi0_value_count = pd.value_counts(xi0)[unique_value].fillna(0) + 1
            p1_list.append(p_xi(is_continuous, np.log(xi1_value_count / (m1 + nvalue))))
            p0_list.append(p_xi(is_continuous, np.log(xi0_value_count / (m0 + nvalue))))
    return p1, p1_list, p0_list
def predict_nb(x, p1, p1_list, p0_list):
    n = len(x)
    x_p1 = np.log(p1)
    x_p0 = np.log(1 - p1)
    for i in range(n):
        p1_xi = p1_list[i]
        p0_xi = p0_list[i]
        if p1_xi.is_continuous:
            mean1, var1 = p1_xi.conditional_pro
            mean0, var0 = p0_xi.conditional_pro
            x_p1 += np.log(1 / (np.sqrt(2 * np.pi) * var1) * np.exp(- (x[i] - mean1) ** 2 / (2 * var1 ** 2)))
            x_p0 += np.log(1 / (np.sqrt(2 * np.pi) * var0) * np.exp(- (x[i] - mean0) ** 2 / (2 * var0 ** 2)))
        else:
            x_p1 += p1_xi.conditional_pro[x[i]]
            x_p0 += p0_xi.conditional_pro[x[i]]
    if x_p1 > x_p0:
        return '是'
    else:
        return '否'
if __name__ == '__main__':
    data_path = r'C:\Users\hanmi\Documents\xiguabook\watermelon3_0_Ch.csv'
    data = pd.read_csv(data_path, index_col=0)
    X = data.iloc[:, :-1]
    y = data.iloc[:, -1]
    p1, p1_list, p0_list = train_nb(X, y)
    x_test = X.iloc[0, :]   # 书中测1 其实就是第一个数据
    print(predict_nb(x_test, p1, p1_list, p0_list))


这里代码很简单。不怎么规范。


7.4 实践中使用式 (7.15)决定分类类别时,若数据的维数非常高,则概率连乘image.png的结果通常会非常接近于 0 从试述防止下溢的可能方案.而导致下溢.


答:

这在p153中已经给出答案了。即取对数将连乘转化为“连加”防止下溢。


image.png


7.5 试证明:二分类任务中两类数据满足高斯分布且方差相同时,线性判别分析产生贝叶斯最优分类器.


答:

首先看一下贝叶斯最优分类器:在书中p148中解释了对于最小化分类错误率的贝叶斯最优分类器可表示为:

image.png

image.png

image.png


7.6 试编程实现 AODE 分类器,并以西瓜数据集 3.0 为训练集,对 p.151的"测1" 样本进行判别.


答:

代码在:

han1057578619/MachineLearning_Zhouzhihua_ProblemSets

'''
目前仅拿西瓜数据集测试过,运行正常,其他数据未测试
'''
import numpy as np
import pandas as pd
from sklearn.utils.multiclass import type_of_target
class AODE(object):
    def __init__(self, m):
        self.m_hat = m
        self.m = None
        self.n = None
        self.unique_y = None
        self.n_class = None
        self.is_continuous = None
        self.unique_values = None
        self.total_p = None
    def predict(self, X):
        X = np.array(X)
        if self.total_p == None:
            raise Exception('you have to fit first before predict.')
        result = pd.DataFrame(np.zeros((X.shape[0], self.unique_y.shape[0])), columns=self.unique_y)
        for i in self.total_p.keys():
            result += self._spode_predict(X, self.total_p[i], i)
        return self.unique_y[np.argmax(result.values, axis=1)]
    def fit(self, X, y):
        X = np.array(X)
        self.m, self.n = X.shape
        self.unique_y = np.unique(y)
        self.n_class = self.unique_y.size
        # 这里转为list, 是因为貌似type_of_target 有bug, 只有在pd.Series类型的时候才能解析为'continuous',
        # 在这里转为array类型后解析为 'unknown'了
        is_continuous = pd.DataFrame(X).apply(lambda x: (type_of_target(x.tolist()) == 'continuous'))
        self.is_continuous = is_continuous
        unique_values = {}  # 离散型字段的各取值
        for i in is_continuous[~is_continuous].index:
            unique_values[i] = np.unique(X[:, i])
        self.unique_values = unique_values
        # 获取可以作为父节点的属性索引,这里在论文中取值为30; 但在西瓜书中由于样本很少, 所有直接取0就好
        parent_attribute_index = self._get_parent_attribute(X)
        total_p = {}
        for i in parent_attribute_index:
            p = self._spode_fit(X, y, i)
            total_p[i] = p
        self.total_p = total_p
        return self
    def _spode_fit(self, X, y, xi_index):
        p = pd.DataFrame(columns=self.unique_y, index=self.unique_values[xi_index])  # 储存各取值下的条件概率
        nunique_xi = self.unique_values[xi_index].size  # 当前属性的取值数量
        pc_xi_denominator = self.m + self.n_class * nunique_xi  # 计算 p(c, xi) 的分母 |D| + N * N_i
        for c in self.unique_y:
            for xi in self.unique_values[xi_index]:
                p_list = []  # 储存y取值为c, Xi取值为xi下各个条件概率p(xj|c, xi)和先验概率p(c, xi)
                c_xi = (X[:, xi_index] == xi) & (y == c)
                X_c_xi = X[c_xi, :]  # y 取值 为c, Xi 取值为xi 的所有数据
                pc_xi = (X_c_xi.shape[0] + 1) / pc_xi_denominator  # p(c, xi)
                # 实际上这里在j = i时, 个人理解应该是可以跳过不计算的,因为p(xi|c, xi) = 1, 在计算中都是一样的但这里为了方便实现,就没有跳过了。
                for j in range(self.n):
                    if self.is_continuous[j]:  # 字段为连续值, 假设服从高斯分布, 保存均值和方差
                        # 这里因为样本太少。有时候会出现X_c_xi为空或者只有一个数据的情况, 如何是离散值,依然可以计算;
                        # 但是连续值的情况下,np.mean会报warning, 只有一个数据时,方差为0
                        # 所有这时, 均值和方差以类别样本来替代。
                        if X_c_xi.shape[0] <= 1:
                            p_list.append([np.mean(X[y == c, j]), np.var(X[y == c, j])])
                        else:
                            p_list.append([np.mean(X_c_xi[:, j]), np.var(X_c_xi[:, j])])
                    else:
                        # 计算 p(xj|c, xi)
                        condi_proba_of_xj = (pd.value_counts(X_c_xi[:, j])[self.unique_values[j]].fillna(0) + 1) / (
                                X_c_xi.shape[0] + self.unique_values[j].size)
                        p_list.append(np.log(condi_proba_of_xj))
                p_list.append(np.log(pc_xi))  # p(c, xi)在最后的位置
                p.loc[xi, c] = p_list
        return p
    def _spode_predict(self, X, p, xi_index):
        assert X.shape[1] == self.n
        xi = X[:, xi_index]
        result = pd.DataFrame(np.zeros((X.shape[0], p.shape[1])), columns=self.unique_y)  # 储存每个样本为不同类别的对数概率值
        for value in p.index:  # 为了可以使用pandas的索引功能, 对于要预测的X值, 每一次循环求同一取值下样本的条件概率和
            xi_value = xi == value
            X_split = X[xi_value, :]
            for c in p.columns:
                p_list = p.loc[value, c]  # 储存p(xj|c, xi) 和 p(c, xi)的列表
                for j in range(self.n):  # 遍历所有的条件概率, 将对应的条件概率相加
                    if self.is_continuous[j]:
                        mean_, var_ = p_list[j]
                        result.loc[xi_value, c] += (
                                -np.log(np.sqrt(2 * np.pi) * var_) - (X_split[:, j] - mean_) ** 2 / (2 * var_ ** 2))
                    else:
                        result.loc[xi_value, c] += p_list[j][X_split[:, j]].values
                result.loc[xi_value, c] += p_list[-1]  # 最后加上p(c, xi)
        return result
    def _get_parent_attribute(self, X):
        '''
        基于属性下各取值的样本数量,决定哪些属性可以作为父属性。
        关于连续值的处理,在《机器学习》书中也没有提及,AODE的原论文也没有提及如何处理连续值,
        考虑到若将连续值x_j作为父属性时,如何求解p(x_i|c, x_j)条件概率会比较麻烦(可以通过贝叶斯公式求解),
        此外AODE本身就是要将取值样本数量低于m的属性去除的,从这个角度来说,连续值就不能作为父属性了。
        所以这里连续值不作为父属性
        :param X:
        :return:
        '''
        enough_quantity = pd.DataFrame(X).apply(
            lambda x: (type_of_target(x.tolist()) != 'continuous') & (pd.value_counts(x) > self.m_hat).all())
        return enough_quantity[enough_quantity].index.tolist()
if __name__ == '__main__':
    data_path = r'C:\Users\hanmi\Documents\xiguabook\watermelon3_0_Ch.csv'
    data = pd.read_csv(data_path, index_col=0)
    X = data.iloc[:, :-1]
    y = data.iloc[:, -1]
    aode = AODE(0)
    print(aode.fit(X, y).predict(X.iloc[[0], :]))


提一下关于连续值处理的问题。这个书中和原论文(好像)都没有提交,所以按照自己的理解来处理了。考虑到以下,实现过程中不将连续值作为父属性。

此外AODE本身就是要将取值样本数量低于一定阈值(论文中给出的是30)的属性去除的,从这个角度来说,连续值就不能作为父属性了,当前其实可以按照区间划分将连续值离散化。

另外,虽然在样本这么小的情况下,看预测结果实际意义不大,但相比于朴素贝叶斯,AODE对于西瓜数据集的拟合更好(错误率更低)。

ps.书中给出的式(7.24)有错误的,分母的image.png改正为 image.png ,在第十次印刷的时候纠正了,看旧版书的同学要注意了。


7.7 给定 d 个二值属性的二分类任务,假设对于任何先验概率项的估算至少需 30 个样例,则在朴素贝叶斯分类器式 (7.15) 中估算先验概率项 image.png需 30 x 2 = 60 个样例.试估计在 AODE 式 (7.23) 中估算先验概率项 image.png所需的样例数(分别考虑最好和最坏情形) .


答:

这里“假设对于任何先验概率项的估算至少需 30 个样例”意味着在所有样本中, 任意image.png的组合至少出现30次。

image.png

这个问题主要基于书中式7.26,就很容易理解了

首先考虑同父结构,根据式7.26,其联合分布可以表示为:


image.pngimage.png


好久没更新...罪过,堕落了...前面八题一个月之前就写好了,一直在看7.7(主要还是懒.)阅读材料给出的贝叶斯网相关论文(主要是《A Tutorial on Learning With Bayesian Networks》),下面两题应该还是需要写代码实现的,回头有时间再补把。

7.9 以西瓜数据集 2.0 为训练集,试基于 BIC 准则构建一个贝叶斯网.


答:

关于贝叶斯网结构的构建,书中p160只稍微提了一下,不过还是挺好理解的,《A Tutorial on Learning With Bayesian Networks》11节给出了更详细的描述。比较简单是方法就是贪心法:

  • 1) 初始化一个网络结构;
  • 2) 使用E表示当前合法的改变一条边的操作集合,比如若两个节点已经有连接,那么合法操作可以删除或者逆转,如没有连接则可以增加一条边,当前必须是在保证不会形成回路的情况;
  • 3) 从中选择一个使得BIC下降最大的一个作为实际操作;
  • 4) 循环2,3直到BIC不再下降。

论文中也给出了其他算法。

有时间再补代码吧。


相关文章
|
2月前
|
机器学习/深度学习
如何用贝叶斯方法来解决机器学习中的分类问题?
【10月更文挑战第5天】如何用贝叶斯方法来解决机器学习中的分类问题?
|
2月前
|
机器学习/深度学习 存储 自然语言处理
【机器学习】基于逻辑回归的分类预测
【机器学习】基于逻辑回归的分类预测
|
2月前
|
机器学习/深度学习 传感器 算法
机器学习入门(一):机器学习分类 | 监督学习 强化学习概念
机器学习入门(一):机器学习分类 | 监督学习 强化学习概念
|
2月前
|
机器学习/深度学习 算法 数据可视化
机器学习的核心功能:分类、回归、聚类与降维
机器学习领域的基本功能类型通常按照学习模式、预测目标和算法适用性来分类。这些类型包括监督学习、无监督学习、半监督学习和强化学习。
47 0
|
4月前
|
机器学习/深度学习 人工智能 算法
【人工智能】机器学习、分类问题和逻辑回归的基本概念、步骤、特点以及多分类问题的处理方法
机器学习是人工智能的一个核心分支,它专注于开发算法,使计算机系统能够自动地从数据中学习并改进其性能,而无需进行明确的编程。这些算法能够识别数据中的模式,并利用这些模式来做出预测或决策。机器学习的主要应用领域包括自然语言处理、计算机视觉、推荐系统、金融预测、医疗诊断等。
77 1
|
4月前
|
机器学习/深度学习 算法
【机器学习】简单解释贝叶斯公式和朴素贝叶斯分类?(面试回答)
简要解释了贝叶斯公式及其在朴素贝叶斯分类算法中的应用,包括算法的基本原理和步骤。
82 1
|
4月前
|
机器学习/深度学习
如何用贝叶斯方法来解决机器学习中的分类问题?
如何用贝叶斯方法来解决机器学习中的分类问题?
|
4月前
|
机器学习/深度学习 数据采集 自然语言处理
【NLP】讯飞英文学术论文分类挑战赛Top10开源多方案–4 机器学习LGB 方案
在讯飞英文学术论文分类挑战赛中使用LightGBM模型进行文本分类的方案,包括数据预处理、特征提取、模型训练及多折交叉验证等步骤,并提供了相关的代码实现。
53 0
|
6月前
|
机器学习/深度学习 分布式计算 算法
在机器学习项目中,选择算法涉及问题类型识别(如回归、分类、聚类、强化学习)
【6月更文挑战第28天】在机器学习项目中,选择算法涉及问题类型识别(如回归、分类、聚类、强化学习)、数据规模与特性(大数据可能适合分布式算法或深度学习)、性能需求(准确性、速度、可解释性)、资源限制(计算与内存)、领域知识应用以及实验验证(交叉验证、模型比较)。迭代过程包括数据探索、模型构建、评估和优化,结合业务需求进行决策。
60 0
|
7月前
|
机器学习/深度学习 存储 搜索推荐
利用机器学习算法改善电商推荐系统的效率
电商行业日益竞争激烈,提升用户体验成为关键。本文将探讨如何利用机器学习算法优化电商推荐系统,通过分析用户行为数据和商品信息,实现个性化推荐,从而提高推荐效率和准确性。
249 14