模式识别与机器学习learning notes(1-2章)

目录

第一章 概论 

第二章 统计判别

1、啥叫“统计判别”

2、作为“统计判别问题”的“模式分类”&贝叶斯判别

3、朴素贝叶斯(贝叶斯判别在多维特征数据上的推广)

4、贝叶斯最小风险判别

5、正态分布模式的贝叶斯分类器

6、用贝叶斯分类器实现手写数字识别

其他


第一章 概论 

  • 介绍模式识别与机器学习的起源、发展、现状和未来。
  • 总体介绍了课程安排,本篇blog记载的是前两章(准确来说是第二章)的知识重点。  

第二章 统计判别

1、啥叫“统计判别”

先放个百度百科对“统计”的定义:

很明显,我们这里的“统计”取的是第一种意思。那“统计判别”就是你先收集整理计算分析了一堆数据,然后得出了一个什么规律,这个规律可以帮助你去处理(在这里就是“判别”)新的数据。

对,这不就是机器学习那一套嘛:喂给模型一堆数据让它学习得出规律,然后让模型用这个规律判别新的数据。

只不过,统计判别不是让机器模型来做,是我们人工处理的。

2、作为“统计判别问题”的“模式分类”&贝叶斯判别

模式分类顾名思义了,它的数学定义如下:

它的贝叶斯判别规则如下: 

比如说p(w1|x)=0.9,也就是说 “你认为x属于w2”这个观点 错误的概率高达0.9,所以正确答案就是x属于w1(有点绕,细品)。

现在的问题是p\left ( w_i{|x}\right )怎么算出来?且看推导:

现在需要解释一下这几个概率的含义,直接上例子:

 问:若某日观察到明显的生物异常反应现象,一周内发生地震的概率为多少,即求P(ω1 | x=异常)=?

这样问的话那肯定得用上述公式算出概率值。but,假如问题是“若某日观察到明显的生物异常反应现象,一周内更可能发生地震还是不发生地震?”,这样一问其实就是二选一二分类问题了,你就不用哼哧哼哧计算p\left ( w_i{|x}\right )了,有个新方法:

不过这里x代表的是”异常与否“,严格意义上来说这个\mathit{x} \epsilon w不太准确,因为x和w完全就是两个范畴。这里判别的就是x的异常导致地震可能性更大。

噢对了,再补充一个概念:p(wi)、p(x|wi)我们都叫“先验概率”,p(wi|x)我们叫“后验概率” 。

 贝叶斯判别原则-经典三扇门问题

  • 问题描述:参赛者会看见三扇关闭了的门,其中一扇的后面有一辆汽车,选中后面有车的那扇门可赢得该汽车,另外两扇门后面则各藏有一只山羊。当参赛者选定了一扇门,但未去开启它的时候,节目主持人开启剩下两扇门的其中一扇,露出其中一只山羊。主持人其后会问参赛者要不要换另一扇仍然关上的门。问题是:换另一扇门是否会增加参赛者赢得汽车的机率。
  • 结果揭示:如果严格按照上述的条件,那么答案是会。不换门的话,赢得汽车的几率是1/3。换门的话,赢得汽车的几率是2/3。
  • 背后的贝叶斯原理支撑:这篇blog写得很清楚!三门问题(Monty Hall problem)背后的贝叶斯理论_zjuPeco的博客-CSDN博客_三门问题贝叶斯解法

3、朴素贝叶斯(贝叶斯判别在多维特征数据上的推广)

真实世界的问题要比二分类问题纷繁复杂得多,数据x往往有多个特征,所以往往是多维的。所以单纯的贝叶斯判别原则肯定是不够用的。于是作为贝叶斯判别的推广——朴素贝叶斯应运而生。

朴素贝叶斯算法是假设各个特征之间相互独立,此时的概率表达式为:

 举个栗子:社区账号的真实性检测。  

  • C=0表示真实账号,C=1表示不真实账号。
  • 选择三个特征属性:①x1:日志数量/注册天数,共有三个范围:{x1 <=0.05, 0.05< x1 <0.2, x1 >=0.2};②x2 :好友数量/注册天数,共有三个范围:{x2 <=0.1, 0.1< x2 <0.8, x2 >=0.8};③x3 :是否使用真实头像,共有两种情况(范围):{x3 =0(不是), x3 =1(是)}。
  • 先验(条件)概率如下:

  • 问题:某个账号使用非真实头像,日志数量与注册天数的比率为0.1,好友数与注册天数的比率为0.2。请问这个账号是否为真实账号?
  • 计算过程:

4、贝叶斯最小风险判别

贝叶斯判别并不是放之四海而皆准,针对特定情况的判别需要对贝叶斯判别进行调整,这就是贝叶斯最小风险判别,请看(一)贝叶斯判别简要原理及其实例 - 知乎 中的“贝叶斯最小风险判别”,写得很清楚~

5、正态分布模式的贝叶斯分类器

上述介绍的两种贝叶斯决策理论都需要用到类条件概率密度函数p\left ( x|w_{i} \right ),实际上这个先验条件概率的获取是比较困难的。因此,常常假设类条件概率密度函数(也就是样本x)服从多元正态分布。这节有点费脑细胞,我慢慢说。

5.1多元正态分布

正态分布(Normal distribution),也称“常态分布”,又名高斯分布(Gaussian distribution)。若随机变量X服从一个数学期望为μ、方差为σ2的正态分布,记为N(μ,σ2)。其概率密度函数为正态分布的期望值μ决定了其位置,其标准差σ决定了分布的幅度。当μ = 0,σ = 1时的正态分布是标准正态分布。一维正态分布表达式及其概率密度函数如下:

 现在我们把变量X从一维扩展到N维(也叫“N元”),大家直接看百度百科给出的解释:多元正态分布_百度百科

 这里我们看N维变量概率密度函数的一般形式:

上面百度百科给出的这个定义和“有几个模式类别”没啥关系,下面这个就是先验条件概率的表达式,这个考虑到了模式的类别数。 

我们老师给出的形式(我直接贴PDF了):

我们看一下上面的“final表达式”(更正一处错误,\frac{1}{2}(x-m_{i})右上角应当有一个转置T),可以看出判别函数d(x)是一个二次曲面(因为x次数为2)。顺便带大家回忆一下高数里面几个常见的二次曲面:

5.2两类问题

现在我们考虑类模式都是正态分布的二分类问题。

当x是二维模式时,判别界面为二次曲线,如椭圆、圆、抛物线或双曲线等。

 判别界面为x的线性函数,为一超平面。 当x是二维时,判别界面为一直线。

做一道题巩固一哈。

首先根据正态分布模式下的贝叶斯判别函数知道,我们首先需要计算w1和w2的均值向量和协方差矩阵。

  • 均值向量求法:就是每个分量的均值再拼成向量。题目中w1类有4个样本,每个样本都是二维特征,那么w1的均值向量就是两个维度分别的均值,得\begin{bmatrix} 1\\ 1\end{bmatrix},同理w2的均值向量为\begin{bmatrix} 5\\ 5\end{bmatrix}
  • 协方差矩阵求法:题目中每一列是一个样本,每一行是一个特征维度,所以计算过程如下:
    • 1、将w1模式矩阵减去它的均值向量得C1=\begin{bmatrix} -1 & 1& 1& -1\\ -1& -1& 1& 1 \end{bmatrix}
    • 2、最终的协方差矩阵C=\frac{1}{m}{C_{1}}{C_{1}^{T}}=\begin{bmatrix} 1 &0 \\ 0& 1 \end{bmatrix},其中,m是样本个数。 

        同理得w2的协方差矩阵为 \begin{bmatrix} 1 &0 \\ 0& 1 \end{bmatrix}

这里的模式矩阵结构为:每一列为一个样本,每一行为一个特征维度。如果交换两者,协方差矩阵的计算存在些许不同,看这里!如何求矩阵的协方差矩阵 - 知乎

还有个东西要解释一下:我这里m表示样本个数,而且为了【便于计算】,我这里也没考虑什么无偏估计有偏估计了,按道理求样本协方差应该除以(m-1)的,但是数字很不好算。

  •  显然C1=C2。现在根据贝叶斯判别界面公式进行一通很简单的矩阵相乘计算,可得界面方程为x_{1}+x_{2}-6=0(约简之后)。

手写实现两类正太分布模式的贝叶斯分类程序。但是我感觉还是差点儿意思 ,今天没时间了,后续再补充叭~

import numpy as np
from matplotlib import pyplot as plt

n1 = int(input("请输入您希望输入的w1类样本点个数(不小于3):")) # 样本数太小可能会导致协方差矩阵不可逆
x1s = []
x2s = []
for i in range(n1):
    x1s.append(int(input("请输入样本点的x1特征:")))
    x2s.append(int(input("请输入样本点的x2特征:")))
n2 = int(input("请输入您希望输入的w2类样本点个数(不小于3):"))
x11s = []
x22s = []
for i in range(n2):
    x11s.append(int(input("请输入样本点的x1特征:")))
    x22s.append(int(input("请输入样本点的x2特征:")))

w1 = np.array([x1s,x2s])
w2 = np.array([x11s,x22s])

# 直接用题目的样本作为分类模式
# w1 = np.array([[0,2,2,0],[0,0,2,2]])
# w2 = np.array([[4,6,6,4],[4,4,6,6]])

# 求均值向量
m1 = np.mean(w1,axis=1).reshape(2,1) # 不同维度的array计算,必须保证至少一个维度相同
m2 = np.mean(w2,axis=1).reshape(2,1)

# 求协方差矩阵
C1 = np.cov(w1)
C2 = np.cov(w2)

#求贝叶斯界面方程(lnp(w1)=lnp(w2))
x_weight = np.dot((m1-m2).T,np.linalg.inv(C1)) # 前提是保证C1可逆
b_weight = -1/2*(np.dot(np.dot(m1.T,np.linalg.inv(C1)),m1)-np.dot(np.dot(m2.T,np.linalg.inv(C1)),m2))
x1_weight = int(x_weight[0][0])
x2_weight = int(x_weight[0][1])
b_weight = int(b_weight[0][0])
print("贝叶斯界面方程为:"+str(x1_weight)+'x1'+str(x2_weight)+'x2'+'+'+str(b_weight)+'=0')

# 画图
plt.scatter(w1[0], w1[1])
plt.scatter(w2[0], w2[1])
# 为什么matplotlib不能传入斜率和截距作图?!
x = np.arange(0, b_weight, 0.5)
y = np.arange(0, b_weight, 0.5) # 之所以这样写是为了程序的可扩展性,但是有的分界线/面仍然画不出来☹
x1 = []
x2 = []
for item1 in x:
    for item2 in y:
        sum = x1_weight*item1 + x2_weight*item2 + b_weight
        if sum == 0:
            x1.append(item1)
            x2.append(item2)
            break
plt.plot(x1,x2)
plt.show()

6、用贝叶斯分类器实现手写数字识别

sklearn已经可以直接调用贝叶斯分类算法了,真,“站在巨人的肩膀上”,不过为了对贝叶斯分类算法有一个更深入的了解,手写实现一遍还是大有裨益的。 

6.1mnist手写数字数据集

数据集网址:MNIST handwritten digit database, Yann LeCun, Corinna Cortes and Chris Burges 

6.2代码实现:

思路分析:根据贝叶斯分类模型公式 

你需要计算p(wi)、p(x|wi)以及所有类别p(x|wi)p(wi)的和。

代码参考基于贝叶斯分类器(朴素贝叶斯)的手写数字识别_一个编程的菜鸡的博客-CSDN博客_贝叶斯实现手写数字识别 加入了一些个人理解~

import math
import struct
from typing import Counter
import numpy as np
import matplotlib.pyplot as plt

# 配置文件
config = {
    # 训练集文件
    'train_images_idx3_ubyte_file_path': 'data/train-images.idx3-ubyte',
    # 训练集标签文件
    'train_labels_idx1_ubyte_file_path': 'data/train-labels.idx1-ubyte',
 
    # 测试集文件
    'test_images_idx3_ubyte_file_path': 'data/t10k-images.idx3-ubyte',
    # 测试集标签文件
    'test_labels_idx1_ubyte_file_path': 'data/t10k-labels.idx1-ubyte',
 
    # 特征提取阙值
    'binarization_limit_value': 0.14,
 
    # 特征提取后的边长
    'side_length': 14
}

def decode_idx3_ubyte(path):
    '''
    加载图像数据
    '''
    print('loading %s' % path)  #提醒的标志性语句
    with open(path, 'rb') as f:
        # 前16位为附加数据,每4位为一个整数,分别为幻数,图片数量,每张图片像素行数,列数。
        magic, num, rows, cols = struct.unpack('>4I', f.read(16))
        print('magic:%d 数量:%d 行数:%d 列数:%d' % (magic, num, rows, cols))
        mnistImage = np.fromfile(f, dtype=np.uint8).reshape(num, rows, cols)
    print('图像数据下载完成')
    return mnistImage

def decode_idx1_ubyte(path):
    '''
    加载标签数据
    '''
    print('loading %s' % path)  #提醒的标志性语句
    with open(path, 'rb') as f:
        # 前8位为附加数据,每4位为一个整数,分别为幻数,标签数量。
        magic, num = struct.unpack('>2I', f.read(8))
        print('magic:%d 数量:%d' % (magic, num))
        mnistLabel = np.fromfile(f, dtype=np.uint8)
    print('标签数据下载完成')
    return mnistLabel

def normalizeImage(image):
    '''将像素值归一化为0.0-1.0,简化计算,加快收敛'''
    new_image = image.astype(np.float32)/255.0
    return new_image

def load_train_images(path=config['train_images_idx3_ubyte_file_path']): # 返回的是60000*28*28三维矩阵
    return normalizeImage(decode_idx3_ubyte(path))
 
 
def load_train_labels(path=config['train_labels_idx1_ubyte_file_path']):
    return decode_idx1_ubyte(path)
 
 
def load_test_images(path=config['test_images_idx3_ubyte_file_path']):
    return normalizeImage(decode_idx3_ubyte(path))
 
 
def load_test_labels(path=config['test_labels_idx1_ubyte_file_path']):
    return decode_idx1_ubyte(path)

def oneImagesFeatureExtraction(image): # 返回的是14*14单张图片
    '''
    对单张图片进行均值特征提取(降维)
    均值特征提取采用的是CNN mean-pooling的思想
    将28*28的图片降维为14*14的图片
    '''
    res = np.empty((config['side_length'], config['side_length']))
    num = 28//config['side_length']     # //先做除法向下取整,池化步长num=2
    for i in range(0, config['side_length']):
        for j in range(0, config['side_length']):
            # tempimage = image[num*i:num*(i+1), num*j:num*(j+1)]
            tempMean = image[num*i:num*(i+1), num*j:num*(j+1)].mean()  #采用均值进行降维  步长为2
            if tempMean > config['binarization_limit_value']:          #对图像进行二维化处理换成0/1相对大小便于矩阵运算,关于这个0.14怎么定的,我猜应该是黑白像素的界限换算成[0.0, 1.0]区间的数字就是0.14
                res[i, j] = 1
            else:
                res[i, j] = 0
    return res

def featureExtraction(images):
    '''将所有的图片信息转化为一个矩阵'''
    res = np.empty((images.shape[0], config['side_length'],config['side_length']), dtype=np.float32)  # '''将所有的图片信息转化为一个矩阵'''
    """empty函数的用法:numpy.empty(shape, dtype=float, order=‘C’)  shape表示空间中的维数,dty表示返回的类型"""
    for i in range(images.shape[0]):
        res[i] = oneImagesFeatureExtraction(images[i])  #单张图片的降维处理
    return res

def bayesModelTrain(train_x, train_y):
    '''
    贝叶斯分类器模型训练
    '''
    # 计算先验概率p(wi)
    totalNum = train_x.shape[0]  #矩阵行数:表示训练样本的个数(60000)
    # print(totalNum)
    classNumDic = Counter(train_y)  #计算返回每一类出现的次数
    #print(classNumDic)
    priori_prob = np.array([classNumDic[i]/totalNum for i in range(10)]) 
    #print(prioriP)

    # 计算类条件先验概率p(x|wi)
    '''
    原先我的想法是:样本x的个数/x所属类别w中所有样本的个数=p(x|w)
    但是在60000条数据里面进行两层循环计算代价实在太大了(我就浅跑了一次循环我电脑风扇呼呼的><)
    而且,我们要比较的是向量是否相等,很明显这也增加了计算开销
    so,我决定利用特征提取进行降维,也就是借鉴了网上的写法。
    '''
    oldShape = train_x.shape #返回的内容是:(60000, 14, 14)
    # print(oldShape)
    train_x.resize((oldShape[0], oldShape[1]*oldShape[2]))     #将三维图像转换为二维
    # print(train_x.shape)
    posterior_num = np.empty((10, train_x.shape[1])) #返回一个二维10*196大小的数组,里面存储的是
    posterior_prob = np.empty((10, train_x.shape[1]))  #返回一个二维大小的数组
    print(posterior_prob)
    for i in range(10):
        # train_x的每一行长度是196,所以posterior_num[i]的长度也是196
        posterior_num[i] = train_x[np.where(train_y == i)].sum(axis=0)  #axis=1表示按行相加 , axis=0表示按列相加
        # 拉普拉斯平滑
        posterior_prob[i] = (posterior_num[i] + 1) / (classNumDic[i] + 2)
    train_x.resize(oldShape)
    return priori_prob, posterior_prob

def bayesClassifier(test_x, priori_prob, posterior_prob):
    '''
    使用贝叶斯分类器进行分类(极大似然估计)
    请记住:按照贝叶斯判别准则,p(x|w)叫做“密度函数”,也叫“似然”,所以求p(x|w)就是求概率密度函数。
    那么,怎么求呢?
    我们用“极大似然估计法”:即把概率密度映射成一个高斯分布,该高斯分布就是概率密度函数。这样则对于任意点的概率密度可以通过求高斯分布上该点的概率而获得。
    '''
    # test_x是单张测试图片
    oldShape = test_x.shape
    test_x.resize(oldShape[0]*oldShape[1])
    classP = np.empty(10)
    for j in range(10):
        temp = sum([math.log(1-posterior_prob[j][x]) if test_x[x] == 0 else math.log(posterior_prob[j][x]) for x in range(test_x.shape[0])])
        # 很奇怪,在降维成7*7的时候,注释掉下面这一句正确率反而更高
        classP[j] = np.array(math.log(priori_prob[j]) + temp)
        classP[j] = np.array(temp)
    test_x.resize(oldShape)
    return np.argmax(classP)  #返回概率最大点的位置上的数

def modelEvaluation(test_x, test_y, prioriP, posteriorP):
    '''
    对贝叶斯分类器的模型进行评估
    '''
    bayesClassifierRes = np.empty(test_x.shape[0])
    for i in range(test_x.shape[0]):
        bayesClassifierRes[i] = bayesClassifier(test_x[i], prioriP, posteriorP)
        #print('预测值:%d 真实值:%d'%(bayesClassifierRes[i],test_y[i]))
    # return bayesClassifierRes, (bayesClassifierRes == test_y).sum() / test_y.shape[0]
    return (bayesClassifierRes == test_y).sum() / test_y.shape[0]

if __name__ == '__main__':
    print('加载数据')
    train_images = load_train_images()  #加载训练的图片
    train_labels = load_train_labels()  #加载训练的标签
    test_images = load_test_images()   #加载测试的图片
    test_labels = load_test_labels()    #加载测试的标签
    print('加载完毕')
    print('图片转换')
    train_images_feature = featureExtraction(train_images)
    # print("转换之后的所有图片形状:\n", train_images_feature.shape)
    # print("转换之后的图片:\n", train_images_feature)
    print('图片转换结束')
    print('模型训练开始')
    prioriP, posteriorP = bayesModelTrain(train_images_feature, train_labels)
    print('模型训练完毕')
    print('模型评价开始')
    test_images_feature = featureExtraction(test_images)
    val = modelEvaluation(test_images_feature, test_labels, prioriP, posteriorP)
    print('贝叶斯分类器的准确度为%.2f %%' % (val*100))
    print('模型评价结束')

代码bayesModelTrain、bayesClassifier、modelEvaluation里面的一些疑惑可以从下图找到解答,from【人工智能实验】运用贝叶斯决策理论实现手写数字识别_小果果学长的博客-CSDN博客_贝叶斯手写数字识别

其他

  • 早听说sklearn了,这次巧合又去看了一些相关的东西,先把链接放上,后续有需要过来拿~机器学习入门之sklearn介绍 - 知乎
  • 贝叶斯实现手写数字识别还是没有彻底懂(搞了一天了人已麻,后续再补充吧,给统计学大佬递膝盖呜呜)
  • 祝大家国庆快乐! 

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值