SHU模式识别

SHU模式识别课程笔记

ycfang@staff.shu.edu.cn

东区917

《模式识别与机器学习》

前言

img

概率论

是时候开始重学概率论了。

b站宋浩概率论

《概率论》学习笔记 - 是傅里叶啦的文章 - 知乎 https://zhuanlan.zhihu.com/p/418319247

随机变量

是值为一个随机事件的结果的变量。

量化随机事件的函数,将随机事件每一个出现的结果赋予了一个数字。

概率分布

概率分布是随机变量所有可能结果及其相应概率的列表。

离散概率分布

连续概率分布

卡方分布、学生t分布和F分布

泰勒公式

简单来讲就是用一个多项式函数去逼近一个给定的函数(即尽量使多项式函数图像拟合给定的函数图像),注意,逼近的时候一定是从函数图像上的某个点展开。如果一个非常复杂函数,想求其某点的值,直接求无法实现,这时候可以使用泰勒公式去近似的求该值,这是泰勒公式的应用之一。泰勒公式在机器学习中主要应用于梯度迭代。
————————————————
版权声明:本文为CSDN博主「豆沙糕」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_38646027/article/details/88014692


Chap1概论

Chap2 贝叶斯决策理论

2.1 贝叶斯决策的基本概念

先验概率

类条件概率密度函数

后验概率

贝叶斯公式

2.2 几种常用的决策规则

最小错误率准则


最小风险准则


限定错误率的两类决策

错误率的两类,不是两类决策

Neyman-Pearson决策


最大最小决策(Maximum and minimum decision making method)

先验概率变化,如何使最大可能的风险最小,也就是在最差的条件下争取最好的结果。

贝叶斯决策理论(三):最小最大决策、序贯分类


最小最大决策准则?

2.3 正态分布时的统计决策

单变量正态分布

多变量正态分布


正态分布模型下的最小错误率贝叶斯决策

特例123


2.4 概率密度函数估计

实际问题bayes分类器求解

输入类数,特征数,待分样本数

输入训练样本数和训练样本集

计算先验概率

用训练数据中各类出现的频率来估计,或者直接依靠经验

计算类条件概率密度

参数估计(最大似然估计,Bayes估计)

非参数估计

计算各类的后验概率

若按最小错误率原则分类,则根据后验概率判定

若按最小风险原则分类,则计算各样本属于各类时的风险并判定


极大似然估计

贝叶斯估计与贝叶斯学习

正态分布Bayes决策应用


非参数估计

实验可以做假设检验

非参数估计:密度函数的形式未知,也不作假设,利用训练数据直接对概率密度进行估计(模型无关方法)

基本思想

估计的目的:从样本集K= {x*1, x2,…, xN}估计样本空间中任何一点的概率密度p(*x)

基本方法:用某种核函数表示某一样本对待估计的密度函数的贡献,所有样本所作贡献的线性组合视作对某点概率密度p(x)的估计

Parzen窗法(基本思想)

归一化条件

常用核函数:均匀核,高斯核,指数窗

窗宽,太大:平均化,分辨率低。太小,统计变动大。

很古老但一直在被用,虽然不再江湖,但江湖上却一直流传着他的名字

某一个分布决定了哪一个样本最多,怎么去取样?

性能:应用普遍,样本足够多才有较好估计

image-20211215194055472

2.5 分类器错误率问题

对于已设计好的分类器,利用样本来估计错误率。

对于未设计好的分类器,需将样本分成训练集和测试集。


不知道为什么突然开始讲分布了

学生分布多峰不受影响,与高斯分布区别

直接暴力上了个直方图

来自PRML,不相信机器学习,所有模型都用图表示,跟句法有什么关系?应用上没有什么成就。

概率统计上有什么可以参考这本书。


Chap3 线性分类器

3.0 线性判别函数基本概念

image-20211222191610062

实际问题中间环节庞杂,获取分布很难,DL直接确定判别函数


判别函数种类

线性判别函数:

  • 线性判别函数
  • 广义线性判别函数
  • 分段线性判别函数

非线性判别函数


判别函数一般定义

image-20211222191930928

两类二维

两类多维

多类线性判别函数,两种情况,单个面和$\frac{C(C-1)}{2}$个线性判别函数,每两类一个。

决策树也可以用。。实现


3.1 Fisher线性判别准则

画图,分界面,贝叶斯决策比较一下?

image-20211222192712561


让我们从二类开始

Fisher准则函数(问题描述

image-20211222192934108
$$
m_i=\frac{1}{n_1}\sum_{y_k \in Y_i}y_k=\frac{1}{n_1}\sum_{x_k \in X_i}w^Tx_k=w^TM_i
$$


Fisher准则函数(离散度)

投影样本类间离散度
$$
|m_1-m_2|^2=|w^T(M_1-M_2)|^2 \
=w^T(M_1-M_2)(M_1-M_2)^Tw
$$

$S_b=(M_1-M_2)(M_1-M_2)^T$就是类间散度矩阵

投影样本类内离散度
$$
\begin{align}
S_i^2 &=\sum_{y_k \in Y_i}(y_k-m_i)^2 \
& =\sum_{x_k \in X_i}(w^Tx_k-w^TM_i)^2\
& =w^T\tilde{S_i}w\\

\end{align}
$$
$\tilde{S_i}=\sum_{x_k \in X_i}(x_k-M_i)(x_k-M_i)^T$

这个类别的协方差估计


Fisher准则函数(定义)
$$
J_F=\frac{|m_1-m_2|^2}{S_1^2+S_2^2}
$$
$|m_1-m_2|^2=|w^T(M_1-M_2)|^2=w^T(M_1-M_2)(M_1-M_2)^Tw$

$S_1^2+S_2^2=w^T\tilde{S_1}w+w^T\tilde{S_2}w=w^T(\tilde{S_1}+\tilde{S_2})w$

$S_b=(M_1-M_2)(M_1-M_2)^T$

$\tilde{S_w}=\tilde{S_1}+\tilde{S_2}$
$$
\begin{align}
&J_F=\frac{w^T \tilde{S_b} w^T}{w^T\tilde{S_w}w}\
&L(w,\lambda)=w^T\tilde{S_b}w-\lambda(w^T\tilde{S_w}w-c)\
&\frac{\partial L(w,\lambda)}{\partial w}=2(\tilde{S_b}w-\lambda \tilde{S_w}w)=0\
\end{align}\
$$
$\Rightarrow \tilde{S_b}w^=\lambda \tilde{S_w}w^$

拉格朗日闭着眼睛写,说不定哪天就考到了。

$$
\begin{align}
\Rightarrow \lambda w^&=\tilde{S_w}^{-1}\tilde{S_b}w^\
&=\tilde{S_w}^{-1}(M_1-M_2)(M_1-M_2)^T w^*\
&= \tilde{S_w}^{-1}(M_1-M_2)\gamma \\

\end{align}\
$$

$\Rightarrow w^=\frac{\gamma}{\lambda} \tilde{S_w}^{-1}(M_1-M_2)\Leftrightarrow w^=\tilde{S_w}^{-1}(M_1-M_2)$

最后一步常数怎么没了?w的大小对结果没有影响,只要求出w的方向即可。就是投影算子w可以忽略比例,也可以理解为找到决策方程的系数参数,或者说决策面的参数。

image-20211222230732929


close-form

数学里面很厉害的结论,很漂亮

假设分母是小常数c,作为约束条件,让分子大

类内散度作为正则项

拉格朗日

求导,就是矩阵代数里的矩阵微分

按理两个变量求导,fisher牛,只对w求导就可以了。

类间离散度越小越好,类内离散度越大越好


image-20211222200302619

后面就是考虑了天然的先验分布,考虑有偏无偏的区别

Fisher准则函数(步骤)

image-20211222200630493

3.2 感知准则函数

Frank Rosenblatt出生在纽约,父亲是医生,其1956年在Cornell大学拿到博士学位后,留校任教,研究方向为心理学和认知心理学。1957年,Frank提出了Perceptron的理论。1960年,在计算机运算能力还不强的时候,其使用基于硬件结构搭建了一个神经网络,大概长下面这样(跪)。

cool

但是和所有先驱一样,Frank开创性的工作并没有在当时得到认可。当时两位科学家 Marvin Minksy 和 Seymour Papert(这两位之后都成了AI界泰斗级的人物)对Frank的工作表示质疑,认为他只不过想博取世人眼球,并且还特地写了一本书来批判Perceptron,书名就叫《Perceptron》。也就是这本书,导致Perceptron沉寂了将近20年,直到80年代另一位dalao — Hinton发明BP算法,让其成为当今AI最热门的领域。

  • 随意确定的判别函数初始权值

  • 通过训练样本逐步修正权值

规范化,增广

梯度下降法

感知器准则函数梯度法示例12

3.3 最小平方误差准则

image-20211229201604557

克拉姆法则求解,不定有多解,随便设了个b

矛盾方程组,方程数大于未知数,无精确解

MSE准则函数,其实矩阵代数学过

求b

权值迭代公式

image-20211229202144084

牛

MSE解等价于Fisher解,迭代解误差足够小或者次数足够结束。


Chap4 knn近邻法

4.1 最近邻法和近邻法则

最重要的非参数方法就是近邻法

4.2 k近邻法, 模糊k近邻分类器

4.3 改进的近邻法

研讨

评估指标

对不起我补笔记忘了记了。从中间开始好了。

自举法,基本思想是有放回的抽样,

.632估计,加权


分类器性能度量指标及非均衡问题的解决

混淆矩阵

FRR

TRR

ERR等错误率

ROC和AUC


非均衡问题

随机过采样,随机前采样

基于代价敏感的算法


线性回归

机器学习实验做的图

局部加权线性回归

多重共线性,模型中变量高度相关

逐步回归的三种主要:前向逐步回归,后向逐步回归,逐步回归


岭回归:解决这类问题可以使用岭回归和LASSO回归,主要针对自变量之间存在多重共线性或者自变量个数多于样本量的情况。

机器学习十大经典算法之岭回归和LASSO回归(学习笔记整理)

岭回归模型在目标函数上加了一个L2范数的惩罚项


逻辑回归

董镇源

机器学习任务:分类回归聚类

sigmoid函数,二分类

推导一下,公式一堆,其实就是最大似然估计和

梯度上升与梯度下降其实是一回事

softmax函数,多分类

transformer


多人姿态检测

YOLO

Pose


YuNet

CNN

DW和PW

EIOU

解决难易样本不平衡。


山世光

于仕琪(NTU)

上机实验

实验一 可视化分析+opencv

数据分析基础

处理收集到的身高体重等数据,并画出直方图可视化

pycharm的快捷键CTRL+Y被redo占用了,那我就把删除整行设置成ALT+Y好了

资料

matplotlib

python中plt.hist参数详解

 matplotlib.pyplot.hist(  
 x, bins=10, range=None, normed=False,   
 weights=None, cumulative=False, bottom=None,   
 histtype=u'bar', align=u'mid', orientation=u'vertical',   
 rwidth=None, log=False, color=None, label=None, stacked=False,   
 hold=None, **kwargs)  

CSDN一次性掌握所有 Python 画图基础操作

CSDNpandas库scatter_matrix绘图可视化参数详解

scatter_matrix(frame, alpha=0.5, c,figsize=None, ax=None, diagonal='hist', marker='.', density_kwds=None,hist_kwds=None, range_padding=0.05, **kwds)

1。frame,pandas dataframe对象
2。alpha, 图像透明度,一般取(0,1]
3。figsize,以英寸为单位的图像大小,一般以元组 (width, height) 形式设置
4。ax,可选一般为none
5。diagonal,必须且只能在{‘hist’, ‘kde’}中选择1个,’hist’表示直方图(Histogram plot),’kde’表示核密度估计(Kernel Density Estimation);该参数是scatter_matrix函数的关键参数
6。marker。Matplotlib可用的标记类型,如’.’,’,’,’o’等
7。density_kwds。(other plotting keyword arguments,可选),与kde相关的字典参数
8。hist_kwds。与hist相关的字典参数
9。range_padding。(float, 可选),图像在x轴、y轴原点附近的留白(padding),该值越大,留白距离越大,图像远离坐标原点
10。kwds。与scatter_matrix函数本身相关的字典参数
11。c。颜色

画直方图,查看分布对比图

画子图,查看分布对比图
data_temp = data
# 分布对比图
def fea_diff(label,fea,fea_name,bins,ran,ran_limit,log):
    data = pd.DataFrame({'fea':fea,'label':label})
    #nrows, ncols : int, optional, default: 1  Number of rows/columns of the subplot grid.
    #True or 'all': x- or y-axis will be shared among all subplots.

    fig,(ax0,ax1) = plt.subplots( nrows=2,sharex=True,figsize = (12,8))
    #调整子图位置
    plt.subplots_adjust(hspace=0)
    data = data.fillna(0)
    if ran_limit==False:
        ran = [min(data.fea),max(data.fea)] 
    #画直方图    
     #x : (n,) array or sequence of (n,) arrays    
     # bins : int or sequence or str, optional    
    ax0.hist(data.loc[(data.label==1),'fea'],bins=bins,alpha=0.5,range=ran,normed = False,log=True)
    ax0.set_ylabel('count',fontsize=20)
    ax0.legend(['label = 1'],loc = 9,fontsize=20)
    ax0.tick_params(labelsize = 20)
    ax0.grid()

    ax1.hist(data.loc[(data.label==0),'fea'],color='r',bins=bins,alpha=0.5,range=ran,normed = False,log=True)
    ax1.set_xlabel(fea_name,fontsize=20)
    ax1.set_ylabel('count',fontsize=20)
    ax1.legend(['label = 0'],loc = 9,fontsize=20)
    ax1.tick_params(labelsize = 20)
    ax1.grid()



fea_diff(label = (data_temp.loan_cur_balance<1000000),#标签列
         fea = data_temp.loan_cur_balance, #特征列
         fea_name = 'loan_cur', #特征名
         bins = 50, #分桶数
         ran_limit=False, #是否启用范围限制
         ran = [0,0], #数据范围
         log = False #是否对纵轴取对数
         )

python双重直方图_Python – 使用散点图堆叠两个直方图

OpenCV

opencv官网下载

太慢了直接github下载源码编译或者exe

VS2019配置opencv工程文件

验证一:人脸检测

搞了半天环境纯属浪费时间然后照着他们做好了的做一遍很快。

验证实验二:人脸检测分类器

没看明白文件在哪,问了才知道是在opencv文件夹里v14下的exe示例

opencv_createsamples.exe -info positives/trainpos.txt -vec data/positives.vec -num 100 -w 20 -h 20

opencv_traincascade.exe -data data/cascade -vec data/positives.vec -bg trainneg.txt -numPos 100 -numNeg 60 -nstages 10 -mem 600 -mode ALL -w 20 -h 20

级联分类器训练:OpenCV 3.4.8

CSDN一篇搞定所有报错

基本上把所有报错踩了一遍,又是改参数名又是改路径的。

如果出现 Parameters can not be written, because file xml/params.xml can not be opened 错误,则自己需要手动创建一个文件夹xml;

opencv_visualisation --image=test/lena.jpg --model=data/cascade/cascade.xml --data=data/result/

测试完成,由于图片数据太少结果毫无用处。

Snipaste_2021-12-11_15-30-03

可以看到左上角有一个小框,不仔细看还真看不到。

验证实验三

1.增加实验样本进行测试。

2.自己选检测目标,仿照上述步骤训练。把垃圾识别的小程序做起来。


实验二 贝叶斯决策

课前小问题:如果有一种病,发病率是十万分之一,有一种试剂可以检测是否患病,正确率可达99%,那么如果开展全民筛查,查到某个人检测结果阳性的时候,这个人真的得病的概率多大?

#欢迎大家把答案写在回复中[偷笑]

虽然贝叶斯算出来是0.0989%,直觉告诉你很反常对吧,但确实就是这样。是不是感觉“正确率”不是很严谨,我也是,所以就不填正确答案哈哈哈。看到知乎有相关的问题,找到了下面第一个专业的关于检验学的回答,解决了我的疑问。即正确率在没有特别定义的情况下说的显然是这么一个意思——我测了很多个,这些人中我测对的占95%。所以0.0989%就是正确答案。

“你发现问题了吗?发现问题了吗?

另外你的问题中,有一个很关键的点叫做正确率。我不得不说,我没见过哪个检测疾病的正规说法里会提“正确率”的。我听到的都是什么假阳率,漏诊率,误诊率诸如此类的。

先跟你说一下为了衡量一个检测手段的效用,我们提出了多少标准,不是一个“正确率”可以泛泛谈之的。比较常用的大概有:灵敏度和特异度,漏诊率和误诊率。”


一道关于疾病检验的概率的问题? - 大汤圆的回答 - 知乎 https://www.zhihu.com/question/60562939/answer/178476420

一道关于疾病检验的概率的问题? - 彭铭燃的回答 - 知乎 https://www.zhihu.com/question/60562939/answer/177546247


(1)应用单个特征进行实验

以(a)身高、(b)体重或(c)鞋码尺寸数据作为特征,在正态分布假设下利用最大似然法或者贝叶斯估计法估计分布密度参数(只利用训练数据估计密度),建立最小错误率Bayes 分类器,写出得到的决策规则,将该分类器应用到测试样本,考察测试错误情况。在分类器设计时可以考察采用不同先验概率(如0.50.5, 0.750.25, 0.90.1 等)进行实验,考察对决策规则和错误率的影响。

《机器学习实战》是将数据离散化做的贝叶斯分类器,用频率替代了概率,这里使用概率估计方法做一下。

这里出大问题,在画ROC曲线的时候才反应过来,先验概率首先应该是数据

正态分布

from scipy.stats import norm
prob = norm.pdf((x-mu)/sigma)/sigma
cdf = norm.cdf((x-mu)/sigma)

总觉得哪里不对,果然,参数是标准差

np.var是方差,np.std才是标准差,好在查了一手,感觉自己变谨慎了。

全体男生女生身高正态分布概率密度函数

测试样本与训练样本的划分

from sklearn.model_selection import train_test_split
#data:需要进行分割的数据集
#random_state:设置随机种子,保证每次运行生成相同的随机数
#test_size:将数据分割成训练集的比例
train_set, test_set = train_test_split(data, test_size=0.2, random_state=42)

然而我们知道计算机学院以前都是阳盛阴衰,因此样本类别标签不均匀,如果我们通过随机采样的方式,极端情况下可能将正例样本(男生)都划分到训练集上,而反例样本(女生)恰好都分到测试集,这样训练出来的模型,效果一定不会太好,所以我们需要采用分层采样的方式进行划分数据集,也就是说保证训练集中既包含一定比例的正例样本又要包含一定比例的负例样本。

幸运的是sklearn提供了我们分层抽样的函数,在这之前先看看官方提供的例子:

from sklearn.model_selection import StratifiedShuffleSplit
X = np.array([[1, 2], [3, 4], [1, 2], [3, 4]])
y = np.array([0, 0, 1, 1])
split = StratifiedShuffleSplit(n_splits=3, test_size=0.5, random_state=0)
print(split )       # doctest: +ELLIPSIS

# StratifiedShuffleSplit(n_splits=3, random_state=0, ...)
for train_index, test_index in split.split(X, y):
    print("TRAIN:", train_index, "TEST:", test_index)
    X_train, X_test = X[train_index], X[test_index]
    y_train, y_test = y[train_index], y[test_index]

"""
StratifiedShuffleSplit(n_splits=3, random_state=0, test_size=0.5,train_size=None)
TRAIN: [1 2] TEST: [3 0]
TRAIN: [0 2] TEST: [1 3]
TRAIN: [0 2] TEST: [3 1]
"""
------------------------------------------------------
from sklearn.model_selection import StratifiedShuffleSplit

split = StratifiedShuffleSplit(n_splits = 1,test_size = 0.2,random_state = 42)

#根据mnist["target"]来进行分层采样
for train_index,test_index in split.split(data[:,:-1],data[:,-1]):
    train_set = data[train_index,:]
    test_set = data[test_index,:]

print(len(train_set),len(test_set))

"""
56000 14000
"""

通过上面的例子我们可以了解使用分层进行划分数据集的大概流程,以及各个参数的含义:

  • n_splits:分割迭代的次数,如果我们要划分训练集和测试集的话,将其设置为1即可;
  • test_size:分割测试集的比例;
  • random_state:设置随机种子;

然而不如直接在train_test_split加stratify=y方便

sklearn的train_test_split()各函数参数含义解释(非常全)


最大似然估计

这边做的都是用最大似然估计


贝叶斯估计

最后又研究了半天,然后zxy那边实现了一下发现错误率堪比我那个离谱的最小风险贝叶斯分类器,他说是数据样本太少的原因,对参数的估计不够准确,我觉得可能还有别的原因,或许是对假设的理解出了问题。


模型测试结果

错误率
def modelTests():
    # 测试模型
    featureList = ['height', 'weight', 'footsize']
    pBoyList = [0.5, 0.75, 0.9]
    error_result = []
    for i in range(len(featureList)):
        error_list = []
        for j in range(len(pBoyList)):
            singleFeature = featureList[i]
            pBoy = pBoyList[j]
            girlConProFunc, boyConProFunc = trainNB1(X_train, singleFeature)
            # 测试单一特征模型性能
            error = testModel1(X_test, girlConProFunc, boyConProFunc, pBoy, singleFeature)
            error_list.append(error)
            # print("The Model error rate is ",error)
        error_result.append(error_list)
    result_df = pd.DataFrame(error_result, index=featureList, columns=pBoyList)
    result_df.to_csv("result1.csv")

实验二模型测试结果

决策面

singleF_Dserfaces

可以看到,先验概率对决策面的影响,当男生先验概率与训练数据先验概率越接近时(即接近真实分布,模型拟合效果越来越好)

用sympy解方程求解决策面

def randomcolor():
    """
    python随机生成颜色
    :return:
    """
    colorArr = ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']
    color = ""
    for i in range(6):
        color += colorArr[random.randint(0, 14)]
    return "#" + color

def getDecisionSurface1(pBoy_choice,feature_choice,ax):
    """
    找决策面
    TODO:决策面可视化
    :return:
    """
    featureList = ['height', 'weight', 'footsize']
    pBoyList = [0.5, 0.75, 0.9]
    pBoy = pBoyList[pBoy_choice]
    singleFeature = featureList[feature_choice]
    # plot single feature decision facet

    mu0, std0 = getNormArg(dataDf[singleFeature].loc[dataDf['gender'] == 0])
    mu1, std1 = getNormArg(dataDf[singleFeature].loc[dataDf['gender'] == 1])
    sigma, mu, x, y = sy.symbols("sigma,mu, x,y")

    p = 1 / (sigma * sy.sqrt(2 * sy.pi)) * sy.exp(-1 / 2 * ((x - mu) / sigma) ** 2)
    f1 = p.subs([(mu, mu0), (sigma, std0)])
    f2 = p.subs([(mu, mu1), (sigma, std1)])
    solve_x = sy.solveset(sy.Eq((1 - pBoy) * f1, pBoy * f2), x)
    x_list = list(solve_x)
    x = [d for d in x_list if ((d >= (min(mu1-3*std1, mu0-3*std0))) and (d <= max(mu0+3*std0, mu1+3*std1)))][0]
    # 求解方程多解需给定范围

    plotPdf(mu0, std0, ax, (1 - pBoy))
    plotPdf(mu1, std1, ax, pBoy)
    ax.axvline(x, ls="-", c=randomcolor())  # 添加垂直直线决策面
    ax.grid(True)
    # ax.legend(["girl", "boy", "decision surface"])
    # ax.title(singleFeature + "pBoy")

def allSingleFeatureDSurface():
    # 直接多图全部画出来
    # fig = plt.figure()
    # ax = fig.add_subplot(111)
    fig, ax = plt.subplots(3, 3)
    for i in range(3):
        for j in range(3):
            getDecisionSurface1(i, j, ax[i][j])
    # plt.title("All single feature decision serfaces",loc="center")
    plt.suptitle("All single feature decision serfaces")
    plt.savefig('./singleF_Dserfaces.jpg')
    plt.show()
混淆矩阵
def testModel2(X_test, girlConProFunc, boyConProFunc, pBoy, singleFeature):
    """
    测试模型给出混淆矩阵
    Parameters:
        X_test - 测试集
        girlConProFunc - 女生类条件概率
        boyConProFunc - 男生类条件概率
        pBoy - 男生先验概率
        singleFeature - 单一特征名
    :return: TP,FP,FN,TN
    TP = True Postive = 真阳性; FP = False Positive = 假阳性
    FN = False Negative = 假阴性; TN = True Negative = 真阴性
    """
    TP = 0;
    FP = 0;
    FN = 0;
    TN = 0;
    lenOfX_test = len(X_test)
    for test_id in range(lenOfX_test):
        testsample = X_test.iloc[test_id]
        result_gender = classifyNB1(testsample, girlConProFunc, boyConProFunc, pBoy, singleFeature)
        if (testsample["gender"] == 1):
            if (result_gender == 1):
                TP += 1
            if (result_gender == 0):
                FP += 1
        else:
            if (result_gender == 1):
                FN += 1
            if (result_gender == 0):
                TN += 1
    precision = TP / (TP + FP)
    recall = TP / (TP + FN)
    specificity = TN / (TN + FP)
    F1 = 2 * TP / (2 * TP + FP + FN)
    print("==========The Model confusion matrix==========")
    print("total num:", lenOfX_test)
    print(" ", "\t", "1", "\t\t", "0")
    print("1", "\t", TP, "\t", FP)
    print("0", "\t", FN, "\t", TN)
    print("==============================================")
    print("precision:",precision)
    print("recall:",recall)
    print("specificity:",specificity)
    print("F1:",F1)
    return TP, FP, FN, TN

def confusionMatrix1():
    featureList = ['height', 'weight', 'footsize']
    pBoyList = [0.5, 0.75, 0.9]
    singleFeature=featureList[0]
    pBoy=pBoyList[0]
    # 训练单一特征贝叶斯分类器模型
    girlConProFunc, boyConProFunc = trainNB1(X_train, singleFeature)
    testModel2(X_test, girlConProFunc, boyConProFunc, pBoy, singleFeature)

精度(precision, 或者PPV, positive predictive value) = TP / (TP + FP)

召回(recall, 或者敏感度,sensitivity,真阳性率,TPR,True Positive Rate) = TP / (TP + FN)

特异度(specificity,或者真阴性率,TNR,True Negative Rate) = TN / (TN + FP)

F1-值(F1-score) = 2*TP / (2*TP+FP+FN)

==========The Model confusion matrix==========
total num: 252
       1          0
1      154      19
0      10      69
==============================================
precision: 0.8901734104046243
recall: 0.9390243902439024
specificity: 0.7840909090909091
F1: 0.913946587537092

ROC曲线

roc曲线怎么画? - 争取一遍过的大喵的回答 - 知乎 https://www.zhihu.com/question/22844912/answer/246037337

身高最小错误率贝叶斯分类器ROC_Curve1

def ROC1(pBoy_choice=0, feature1_choice=0):
    """
    画ROC曲线,参数同决策面选择不同模型
    :return:
    """
    featureList = ['height', 'weight', 'footsize']
    pBoyList = [0.5, 0.75, 0.9]
    singleFeature = featureList[feature1_choice]
    pBoy = pBoyList[pBoy_choice]
    # 获取测试集真实分类
    gender_list = X_test["gender"]
    girl_num = gender_list[gender_list == 0].count()
    boy_num = gender_list[gender_list == 1].count()

    # 训练单一特征贝叶斯分类器模型
    girlConProFunc, boyConProFunc = trainNB1(X_train, singleFeature)

    def getScore(featureData2score):
        score = boyConProFunc(featureData2score) * pBoy
        return score

    # X_test_copy=X_test
    lenOfX_test = len(X_test)
    score_list = []
    for test_id in range(lenOfX_test):
        testsample = X_test.iloc[test_id]
        testsample_score = getScore(testsample[singleFeature])
        score_list.append(testsample_score)

    arg_sort_index = np.argsort(score_list)
    arg_sort_index_list = arg_sort_index.tolist()  # 从大到小的index
    arg_sort_index_list = list(reversed(arg_sort_index_list))
    # X_test_copy=X_test_copy[arg_sort_index]

    FPR_x_list = []
    TPR_y_list = []

    for roc_i in range(0, lenOfX_test, 10):  # 第roc_i+1大的score,判断为阳性的数目=roc_i+1,阴性数目为=lenOfX_test-(roc_i+1)
        # roc_i=10
        threshold = score_list[roc_i]  # 设置阈值

        max_score_index_list = arg_sort_index_list[:roc_i + 1]  # 取前roc_i+1个score值的index,即判断为阳性的样本
        hypo_boy_gender_list = gender_list.iloc[max_score_index_list]  # 忘记reverse了,结果全是女生。这是判成男生的样本index列表
        hypo_boy_num = hypo_boy_gender_list[hypo_boy_gender_list == 0].count()  # 错判成男生的女生数目
        # 判断FP
        FP = hypo_boy_num / girl_num
        # 判断TP
        TP = ((roc_i + 1) - hypo_boy_num) / boy_num
        FPR_x_list.append(FP)
        TPR_y_list.append(TP)
    # FPR_x_list.append(1.0)
    # TPR_y_list.append(1.0)
    AUC = 0.0
    for i in range(1, len(FPR_x_list)):
        AUC = AUC + (FPR_x_list[i] - FPR_x_list[i - 1]) * (TPR_y_list[i] + TPR_y_list[i - 1]) / 2
    plt.title('ROC Curve')
    plt.plot(FPR_x_list, TPR_y_list, color='blue', label='ROC(AUC={:.6f})'.format(AUC))
    plt.plot([0, 1], [0, 1], color='red', label='Random Guess')
    plt.xlabel('False Positive Rate')
    plt.ylabel('True Positive Rate')
    plt.legend()
    plt.savefig('./ROC_Curve1.jpg')
    plt.show()

All_ROC_Curve1

一文深度解读模型评估方法

先验概率对ROC曲线无影响,也不需要估计正态分布来得到类条件概率密度,ROC是为了找到最佳阈值来确定

AUC不依赖分类阈值,摆脱了固定分类阈值看分类效果的局限性。

简单来说:AUC对样本的正负比例情况是不敏感,即使正例与负例的比例发生了很大变化,ROC曲线面积也不会产生大的变化

ROC曲线有个很好的特性:当测试集中的正负样本的分布变化的时候,ROC曲线能够保持不变。在实际的数据集中经常会出现类不平衡(class imbalance)现象,即负样本比正样本多很多(或者相反),而且测试数据中的正负样本的分布也可能随着时间变化。


PR曲线

直接使用plot_precision_recall_curve函数绘制PR曲线,但是自己也可以实现一下,应该也很快。

from sklearn.metrics import plot_precision_recall_curve
disp = plot_precision_recall_curve(classifier, X_test, y_test)
disp.ax_.set_title(‘2-class Precision-Recall curve’)

终于搞懂了PR曲线 - yfor的文章 - 知乎 https://zhuanlan.zhihu.com/p/404798546

P-R曲线就是精确率precision vs 召回率recall 曲线,以recall作为横坐标轴,precision作为纵坐标轴。首先解释一下精确率和召回率。不过在解释精确率和召回率之前,先来看下混淆矩阵,

预测\真实
TP FP
FN TN

把正例正确分类为正例,表示为TP(true positive),把正例错误分类为负例,表示为FN(false negative),

把负例正确分类为负例,表示为TN(true negative), 把负例错误分类为正例,表示为FP(false positive)

精确率和召回率可以从混淆矩阵中计算而来,precision = TP/(TP + FP), recall = TP/(TP +FN)。那么P-R曲线是怎么来的呢?

算法对样本进行分类时,一般都会有置信度,即表示该样本是正样本的概率,比如99%的概率认为样本A是正例,1%的概率认为样本B是正例。通过选择合适的阈值,比如50%,对样本进行划分,概率大于50%的就认为是正例,小于50%的就是负例。

通过置信度就可以对所有样本进行排序,再逐个样本的选择阈值,在该样本之前的都属于正例,该样本之后的都属于负例。每一个样本作为划分阈值时,都可以计算对应的precision和recall,那么就可以以此绘制曲线。

————————————————
版权声明:本文为CSDN博主「keep_forward」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/b876144622/article/details/80009867

仔细琢磨会发现这些图的规律和问题,比如西瓜书上的PR曲线

西瓜书上的PR曲线

下面是我自己画的PR曲线

All_PR_Curve1

女生人数太少,准确率降不到0。

曲线最终不会到(1,0)点。很多P-R曲线的终点看着都是(1,0)点,这可能是因为负例远远多于正例。

最后一个点表示所有的样本都被判为正例,因此FN=0,所以recall = TP/(TP + FN) = 1, 而FP = 所有的负例样本数,因此precision = TP/(TP+FP) = 正例的占所有样本的比例,故除非负例数很多,否则precision不会为0.

另外,如果有个划分点可以把正负样本完全区分开,那么P-R曲线就是整个1*1的面积。

总之,P-R曲线应该是从(0,0)开始画的一条曲线,切割1*1的正方形,得到一块区域。

还有一些补充,与ROC曲线对比,PR曲线对样本不均衡敏感。

在众多学习器对数据进行学习后,如果其中一个学习器的PR曲线A完全包住另一个学习器B的PR曲线,则可断言A的性能优于B。但是A和B发生交叉,那性能该如何判断呢?我们可以根据曲线下方的面积大小来进行比较,但更常用的是平衡点F1。平衡点(BEP)是P=R时的取值(斜率为1),F1值越大,我们可以认为该学习器的性能较好。F1的计算如下所示:
F1 = 2 P R /( P + R )
————————————————
版权声明:本文为CSDN博主「gz7seven」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/guzhao9901/article/details/107961184


代价曲线

没画了,不知道怎么算风险的那块面积,怎么取直线交点?


(2)应用两个特征进行实验

同时采用身高和体重数据作为特征,假设二者不相关,在正态分布假设下估计概率密度,建立最小错误率Bayes分类器,写出得到的决策规则,将该分类器应用到训练/测试样本,考察训练/测试错误情况。

def trainNB2(trainMatrix, feature_name1, feature_name2):
    """
    函数说明:朴素贝叶斯分类器训练函数

    Parameters:
        trainMatrix - 训练数据,单一特征
        fearture_name1 - 特征名
        fearture_name2 - 特征名
    Returns:
        p0Func1 - 女生的某特征条件概率函数
        p1Func1 - 男生的某特征条件概率函数
        p0Func2 - 女生的某特征条件概率函数
        p1Func2 - 男生的某特征条件概率函数
    """
    # Bayes Classifier
    p0Func1, p1Func1 = trainNB1(trainMatrix, feature_name1)
    p0Func2, p1Func2 = trainNB1(trainMatrix, feature_name2)
    return p0Func1, p1Func1, p0Func2, p1Func2

def classifyNB2(data2Classify, p0Func1, p1Func1, p0Func2, p1Func2, pBoy, feature_name1, feature_name2):
    """
    函数说明:朴素贝叶斯分类器分类函数2

    Parameters:
        data2Classify - 待分类的新数据
        p0Func1 - 女生的身高条件概率函数
        p1Func1 - 男生的身高条件概率函数
        p0Func2 - 女生的体重条件概率函数
        p1Func2 - 男生的体重条件概率函数
        pBoy - 属于男生的先验概率
    Returns:
        0 - 属于女生
        1 - 属于男生
    """
    height = data2Classify[feature_name1]
    weight = data2Classify[feature_name2]
    ln_p1 = np.log(p1Func1(height + 1e-5)) + np.log(p1Func2(weight + 1e-5)) + np.log(pBoy + 1e-5)
    ln_p0 = np.log(p0Func1(height + 1e-5)) + np.log(p0Func2(weight + 1e-5)) + np.log(1.0 - pBoy + 1e-5)
    if ln_p1 > ln_p0:
        return 1
    else:
        return 0

在分类器设计时可以考察采用不同先验概率(如0.5 vs. 0.5, 0.75 vs. 0.25, 0.9 vs. 0.1 等)进行实验,考察对决策规则和错误率的影响。


二维决策面

Figure_1

pBoy=0.75

好像可以看出男女之间身高体重相关程度不同。也可能是比例尺度的关系。

p=0.5

画错了,还是得推导一下公式。不记得还有考虑先验概率了,上面画的默认男女概率1比1.

重来。

参考资料:[贝叶斯七]之正态分布贝叶斯决策

多元正态分布

  • 多变量pdf表达

$$
p(x)=\frac{1}{(2 \pi)^{1 / 2}\left|\sum\right|^{1 / 2}} \exp \left\{-\frac{1}{2}(x-\mu)^{T} {\sum}^{-1}(x-\mu)\right\}, \quad x \in R^{l}
$$

  • Mean Vector (均值)

$$
\mu=E[x]=E\left[x_{1}, x_{2}, \ldots, x_{l}\right]
$$

  • Convariance matrix (协方差矩阵)

$$
\sum=E\left[(x-\mu)(x-\mu)^{T}\right]
$$

$$
=\left[\begin{array}{cccc}
\sigma_{11}^{2} & \sigma_{12}^{2} & \cdots & \sigma_{1 l}^{2} \
\sigma_{21}^{2} & \sigma_{22}^{2} & \cdots & \sigma_{2 l}^{2} \
\vdots & \vdots & \ddots & \vdots \
\sigma_{l 1}^{2} & \sigma_{l 2}^{2} & \cdots & \sigma_{l l}^{2}
\end{array}\right]
$$

  • 数学表达

$$
p(x) N(\mu, \Sigma)
$$

特点

  • K个参数(均值和方差)决定L−dim的正态分布

$$
K=l+\frac{l(l+1)}{2}
$$

  • 协方差矩阵的特征向量决定主轴,而且主轴的长度和协方差矩阵的特征向量是成比例的。
  • 超椭球面(super-ellipsoid)上点概率值相等
  • 对于正态分布来说,不相关和独立是相等的
  • 如果x是独立的,那么协方差矩阵是对角矩阵

贝叶斯分类器理论推导

我们考虑每个类别数据都是服从正态分布的。同样的,我们判决函数用ln函数,那么我们能得到如下的决策函数。

image-20211227154312003

然后,因为x是服从正态分布的,由此,我们将x的pdf函数带入到上式。得到如下式子。

公式(1)

根据决策面方程(第i类和第j类的分界面):
$$
g_{ij}(x)=g_i(x)-g_j(x)=0
$$
由此我们可以推导出决策面

决策面公式,显然是个二次曲面

在实际代码实现画决策面的投影时出现了问题,仔细琢磨一下公式发现如果是二元正态分布,可能出现多解和复数解,但大概很接近正确答案了。

我们发现这个方程其实还是很难的,而且虽然每个类别样本都是服从正态分布,但是正态分布也会因为协方差矩阵不同而千差万别(就像单变量正态分布,如果方差不一样,那么他们之间的胖瘦就不一样)。不仅如此,多变量正态分布还存在属性之间的关系等等。所以,这里为了进一步分析上面的方程,就像朴素贝叶斯一样,我们也可以做一些强的假设。假设条件如下:

  • 假设协方差矩阵元素相等,而且是一个对角阵。即特征向量之间是相互独立的,方差相等。

然而实验条件假设身高体重不相关,但是男生类和女生类的身高或者体重方差会相同吗?那显然。。。。。。我们不知道。因为假设没说,虽然我们可以通过标准化把身高体重变成标准正态分布。但那只是改变了坐标轴的比例而已,好像没什么意思。

但为了美观,可以把方差标准化好,身高体重都除以自己的std标准差就行了。那样画出来的投影图就一定是书上标准的两个圆,两个圆心的垂直平分线就是决策面。

或者我们还可以乖乖照着公式,画出一般情况的多远正态分布。我是个很喜欢偷懒的人,所以喜欢一次性把事情做好。

下面是假设成立时的推导,把它推导完好了。

  • 因为每个类别的协方差矩阵相等,我们可以忽略决策函数(公式(1))中的第1项和第2项,那么可以得到一个简化的判定函数如下。

image-20211227155317939

  • 上式中的第1个二次项与类别无关,因此完全可以忽略。

经过上述简化过程之后,简化后的决策函数(公式2)可以接着改写为如下的式子。

image-20211227155354525

简化后的决策函数(公式3)过于冗余,我们可以用一些变量来进行替换,做如下几个定义。
$$
\left\{\begin{array}{c}
w_{i}=\Sigma^{-1} \mu_{i} \
w_{i 0}=\frac{1}{2} \mu_{i}^{T} \Sigma^{-1} \mu_{i}+\ln p\left(w_{i}\right)
\end{array}\right.
$$
由此,我们可以将公式3改写成如下的式子。
$$
g_i(x)=w_i^Tx+w_{i0}
$$

哇哦可以说是非常的爽了。数学讲究的简单就是美!!!各位客官不着急。。。还没完呢,现在我们假设的是类别样本都是标准的、除了μ不同的正态分布。最终是想知道和μ之间有什么关系。所以。。。。接着来,我们要把μ给牵出来。

yyt:推着推着忘了,先跑路去写代码了。有空继续。

公式改好之后再来画决策面,遇到求出复数解的问题,而且因为决策面是二次曲线,这里比较特殊是两条相交直线,所以应该会有两个解,手动判断画出合理的一条(这里其实有误,应该画出两条)。

另一个问题是求出复数解,我人都傻了,但方程推导确实表示的可能是一个复平面,计算机解的时候是有可能因为边缘高斯概率太小溢出,所以我直接找两个实数点延长直线画出来。

multipleF_Dserfaces4

multipleF_Dserfaces5

改变先验概率之后画出的决策面就很怪,怀疑是取错了另外一条直线的点。

全错了,重新来!先入为主误以为决策面是直线,但是其实应该按照方程来求解画出来。

算出错误率看看,发现应该是决策面画错了。

height+weight height+footsize weight+footsize
0.5 0.130952381 0.031746032 0.031746032
0.75 0.111111111 0.035714286 0.03968254
0.9 0.095238095 0.03968254 0.043650794
def getDecisionSurface2(pBoy_choice=0, feature1_choice=0, feature2_choice=1):
    """
    :param pBoy_choice: 先验概率选择默认0.5
    :param feature1_choice: 第一个特征选择默认身高
    :param feature2_choice: 第一个特征选择默认体重
    :return:
    """
    # 测试两个特征
    featureList = ['height', 'weight', 'footsize']
    pBoyList = [0.5, 0.75, 0.9]
    pBoy = pBoyList[pBoy_choice]
    singleFeature1 = featureList[feature1_choice]
    singleFeature2 = featureList[feature2_choice]

    # p0Func1, p1Func1, p0Func2, p1Func2=trainNB2(X_train,singleFeature1,singleFeature2)
    # test_id = 1
    # print(X_test.iloc[test_id])
    # result_gender = classifyNB2(X_test.iloc[test_id], p0Func1, p1Func1, p0Func2, p1Func2, pBoy,singleFeature1,singleFeature2)
    # print(result_gender)
    f1 = X_train[singleFeature1]
    f2 = X_train[singleFeature2]

    f1_min = np.min(f1, axis=0)
    f2_min = np.min(f2, axis=0)
    f1_max = np.max(f1, axis=0)
    f2_max = np.max(f2, axis=0)
    # 构建坐标范围这里要适合
    # x_index = np.linspace(f1_min - 10, f1_max + 10, (int(f1_max - f1_min + 20) * 50))
    # y_index = np.linspace(f2_min - 10, f2_max + 10, (int(f2_max - f2_min + 20) * 50))
    x_index = np.linspace(f1_min - 10, f1_max + 10, 100)
    y_index = np.linspace(f2_min - 10, f2_max + 10, 100)

    height_mu0, height_std0 = getNormArg(dataDf[singleFeature1].loc[dataDf['gender'] == 0])
    height_mu1, height_std1 = getNormArg(dataDf[singleFeature1].loc[dataDf['gender'] == 1])
    weight_mu0, weight_std0 = getNormArg(dataDf[singleFeature2].loc[dataDf['gender'] == 0])
    weight_mu1, weight_std1 = getNormArg(dataDf[singleFeature2].loc[dataDf['gender'] == 1])
    sigma, mu, x, y = sy.symbols("sigma,mu, x,y")

    p_height = 1 / (sigma * sy.sqrt(2 * sy.pi)) * sy.exp(-1 / 2 * ((x - mu) / sigma) ** 2)
    p_weight = 1 / (sigma * sy.sqrt(2 * sy.pi)) * sy.exp(-1 / 2 * ((y - mu) / sigma) ** 2)
    height_f1 = p_height.subs([(mu, height_mu0), (sigma, height_std0)])
    height_f2 = p_height.subs([(mu, height_mu1), (sigma, height_std1)])
    weight_f1 = p_weight.subs([(mu, weight_mu0), (sigma, weight_std0)])
    weight_f2 = p_weight.subs([(mu, weight_mu1), (sigma, weight_std1)])

    X, Y = np.meshgrid(x_index, y_index)  # x-y 平面的网格
    z0 = np.ones(X.shape)
    z1 = np.ones(X.shape)

    for i in range(X.shape[0]):
        for j in range(X.shape[1]):
            z0[i][j] = height_f1.evalf(subs={x: x_index[i]}) * weight_f1.evalf(subs={y: y_index[j]}) * (1 - pBoy)
            z1[i][j] = height_f2.evalf(subs={x: x_index[i]}) * weight_f2.evalf(subs={y: y_index[j]}) * pBoy

    ax3 = plt.axes(projection='3d')
    plt.title('Decision Surface')
    ax3.plot_surface(X, Y, z0, cmap='rainbow')
    ax3.contour(X, Y, z0, zdir='z', offset=-0.001, cmap='rainbow')
    ax3.plot_surface(X, Y, z1, cmap='rainbow')
    ax3.contour(X, Y, z1, zdir='z', offset=-0.001, cmap='rainbow')
    # zdir : 'z' | 'x' | 'y' 表示把等高线图投射到哪个面
    # offset : 表示等高线图投射到指定页面的某个刻度
    # plt.legend(loc='upper right')
    # 设置图像z轴的显示范围,x、y轴设置方式相同
    ax3.set_zlim(-0.001, 0.003)
    plt.show()

真~二维特征贝叶斯分类器决策面

multipleF_Dserfaces12

为了画这个图可谓是几经波折,改一个很细节的取值问题都从晚上八点做到了十一点。深切体会到了肉眼debug的重要性。

没有实现自动取范围的功能,所以这个图算是半手动画出来的。

把所有图都跑了一遍,都是椭圆,因为没有调好范围参数,没有完全画出来。就不放了。


模型测试

错误率

模型测试错误率

对比一维特征错误率下降了很多,但主要是鞋码本来就是最好的区分特征,加入新的特征反而误判

混淆矩阵
==========The Model confusion matrix==========
total num: 252
       1          0
1      158      15
0      13      66
==============================================
precision: 0.9132947976878613
recall: 0.9239766081871345
specificity: 0.8148148148148148
F1: 0.9186046511627907
def testModel2_confusionMatrix(X_test, pBoy, feature_name1, feature_name2):
    """
    测试模型给出混淆矩阵
    Parameters:
        X_test - 测试集
        girlConProFunc - 女生类条件概率
        boyConProFunc - 男生类条件概率
        pBoy - 男生先验概率
        singleFeature - 单一特征名
    :return: TP,FP,FN,TN
    TP = True Postive = 真阳性; FP = False Positive = 假阳性
    FN = False Negative = 假阴性; TN = True Negative = 真阴性
    """
    TP = 0;
    FP = 0;
    FN = 0;
    TN = 0;

    p0Func1, p1Func1, p0Func2, p1Func2 = trainNB2(X_train, feature_name1, feature_name2)
    lenOfX_test = len(X_test)
    for test_id in range(lenOfX_test):
        testsample = X_test.iloc[test_id]
        result_gender = classifyNB2(testsample, p0Func1, p1Func1, p0Func2, p1Func2, pBoy, feature_name1, feature_name2)
        if (testsample["gender"] == 1):
            if (result_gender == 1):
                TP += 1
            if (result_gender == 0):
                FP += 1
        else:
            if (result_gender == 1):
                FN += 1
            if (result_gender == 0):
                TN += 1
    precision = TP / (TP + FP)
    recall = TP / (TP + FN)
    specificity = TN / (TN + FP)
    F1 = 2 * TP / (2 * TP + FP + FN)
    print("==========The Model confusion matrix==========")
    print("total num:", lenOfX_test)
    print(" ", "\t", "1", "\t\t", "0")
    print("1", "\t", TP, "\t", FP)
    print("0", "\t", FN, "\t", TN)
    print("==============================================")
    print("precision:", precision)
    print("recall:", recall)
    print("specificity:", specificity)
    print("F1:", F1)
    return TP, FP, FN, TN

改一下分类器函数就行了,上面是身高体重pBoy=0.75


其他评估指标

ROC曲线

All_ROC_Curve1

画出ROC曲线后发现了问题,改变先验概率模型的AUC不变,表示模型性能都相同,很奇怪。

问了机器学习的老师,贝叶斯的先验概率需要根据实际数据来计算,不能随便改变。

我们是人为设定了不同的先验概率来设计不同的分类器,然而这样是否可行呢?

def ROC1(pBoy_choice, feature1_choice,ax):
    """
    画ROC曲线,参数同决策面选择不同模型
    :return:
    """
    featureList = ['height', 'weight', 'footsize']
    pBoyList = [0.5, 0.75, 0.9]
    singleFeature = featureList[feature1_choice]
    pBoy = pBoyList[pBoy_choice]
    # 获取测试集真实分类
    gender_list = X_test["gender"]
    girl_num = gender_list[gender_list == 0].count()
    boy_num = gender_list[gender_list == 1].count()

    # 训练单一特征贝叶斯分类器模型
    girlConProFunc, boyConProFunc = trainNB1(X_train, singleFeature)

    def getScore(featureData2score):
        score = boyConProFunc(featureData2score) * pBoy
        return score

    # X_test_copy=X_test
    lenOfX_test = len(X_test)
    score_list = []
    for test_id in range(lenOfX_test):
        testsample = X_test.iloc[test_id]
        testsample_score = getScore(testsample[singleFeature])
        score_list.append(testsample_score)

    arg_sort_index = np.argsort(score_list)
    arg_sort_index_list = arg_sort_index.tolist()  # 从大到小的index
    arg_sort_index_list = list(reversed(arg_sort_index_list))
    # X_test_copy=X_test_copy[arg_sort_index]

    FPR_x_list = []
    TPR_y_list = []

    for roc_i in range(0, lenOfX_test, 10):  # 第roc_i+1大的score,判断为阳性的数目=roc_i+1,阴性数目为=lenOfX_test-(roc_i+1)
        # roc_i=10
        threshold = score_list[roc_i]  # 设置阈值

        max_score_index_list = arg_sort_index_list[:roc_i + 1]  # 取前roc_i+1个score值的index,即判断为阳性的样本
        hypo_boy_gender_list = gender_list.iloc[max_score_index_list]  # 忘记reverse了,结果全是女生。这是判成男生的样本index列表
        hypo_boy_num = hypo_boy_gender_list[hypo_boy_gender_list == 0].count()  # 错判成男生的女生数目
        # 判断FP
        FP = hypo_boy_num / girl_num
        # 判断TP
        TP = ((roc_i + 1) - hypo_boy_num) / boy_num
        FPR_x_list.append(FP)
        TPR_y_list.append(TP)
    # FPR_x_list.append(1.0)
    # TPR_y_list.append(1.0)
    AUC = 0.0
    for i in range(1, len(FPR_x_list)):
        AUC = AUC + (FPR_x_list[i] - FPR_x_list[i - 1]) * (TPR_y_list[i] + TPR_y_list[i - 1]) / 2
    # ax.title('ROC Curve of '+singleFeature)
    ax.set_title('ROC Curve of '+singleFeature+' and pBoy={:.2f}'.format(pBoy))
    ax.plot(FPR_x_list, TPR_y_list, color='blue', label='ROC(AUC={:.6f})'.format(AUC))
    ax.plot([0, 1], [0, 1], color='red', label='Random Guess')
    ax.set_xlabel('False Positive Rate')
    ax.set_ylabel('True Positive Rate')
    ax.legend()
    # plt.savefig('./ROC_Curve1.jpg')
    # plt.show()

def allROC1():
    """
    TODO: 画出所有单一特征正态分布最小错误率Bayes分类的ROC curve
    :return:
    """
    plt.figure(figsize=(10, 10), dpi=80)
    plt.figure(1)
    plot_pos = 331

    for singleFeature_choice in range(3):
        for pBoy_choice in range(3):
            ax1 = plt.subplot(plot_pos)
            ROC1(pBoy_choice,singleFeature_choice,ax1)
            plot_pos+=1
            print(plot_pos)


    plt.tight_layout()
    plt.savefig('./All_ROC_Curve1.jpg')
    plt.show()

(3)Parzen 窗法估计

采用Parzen 窗法估计概率密度,重复步骤(1),并且进行比较。

作图直观理解Parzen窗估计

Parzen窗估计属于非参数估计。所谓非参数估计是指,已知样本所属的类别,但未知总体概率密度函数的形式,要求我们直接推断概率密度函数本身。

假设一种情况,你正在屋里看《PRML》,结果天降正义掉下来一盆乒乓球,掉的哪里都是,你觉得这是天意,如果很多乒乓球都掉在了一个位置,那么那个位置下一次必掉屠龙宝刀,你想通过估计屋子里乒乓球密度,找出这个位置,那么如何估计呢?

假设你的屋里正好铺了地砖,每块地砖的大小都相同。你此时灵机一动,我只需要统计每块地砖上的乒乓球个数,有最多乒乓球的地砖就是屠龙宝刀的位置。

这似乎听起来很简单,的确,就是这么简单。

  • 这里所写的窗函数表示超立方体,而不是超球体,判断条件也不是点到中心的距离小于2/h,而是点坐标的每个元素都小于2/h。
错误率 0.5 0.75 0.9
height 0.166666667 0.091269841 0.178571429
weight 0.138888889 0.166666667 0.214285714
footsize 0.027777778 0.027777778 0.035714286

使用均匀核(方窗),h=2,一维特征V=h**1,对比的图就不画了

image-20211226171222858

(4)最小风险的Bayes 决策

自行给出一个决策表,采用最小风险的Bayes 决策重复步骤(1)

就是在决策的后验概率分别乘上风险系数,一般是根据经验设置,很简单就不做了

yyt,你不是一个会因为事情简单就放弃去尝试机会的人。

image-20211226194149262

随便设了个风险系数,结果发现错误率高的离谱,0.6865079365079365。

def classifyNB4(data2Classify, p0Func, p1Func, pBoy, feature_name, riskMat):
    """
    函数说明:最小风险贝叶斯分类器分类函数

    Parameters:
        data2Classify - 待分类的新数据
        p0Func - 女生的某特征条件概率函数
        p1Func - 男生的某特征条件概率函数
        pBoy - 属于男生的先验概率
        riskMat - 风险系数矩阵
    Returns:
        0 - 属于女生
        1 - 属于男生
    """
    height = data2Classify[feature_name]
    ln_p1 = np.log(p1Func(height + 1e-5)) + np.log(pBoy + 1e-5)
    ln_p0 = np.log(p0Func(height + 1e-5)) + np.log(1.0 - pBoy + 1e-5)
    risk0 = ln_p0 * riskMat[0][0] + ln_p1 * riskMat[0][1]
    risk1 = ln_p0 * riskMat[1][0] + ln_p1 * riskMat[1][1]

    # 选风险小的
    if risk1 < risk0:
        return 1
    else:
        return 0

def testModel4(X_test, girlConProFunc, boyConProFunc, pBoy, singleFeature, riskMat):
    """
    测试最小风险贝叶斯模型错误率
    Parameters:
        X_test - 测试集
        girlConProFunc - 女生类条件概率
        boyConProFunc - 男生类条件概率
        pBoy - 男生先验概率
        singleFeature - 单一特征名
    :return: error
    """
    count = 0.0
    lenOfX_test = len(X_test)
    for test_id in range(lenOfX_test):
        testsample = X_test.iloc[test_id]
        result_gender = classifyNB4(testsample, girlConProFunc, boyConProFunc, pBoy, singleFeature, riskMat)
        if result_gender != testsample["gender"]:
            count += 1
    error = count / lenOfX_test
    # print("The Model error rate is ", error)
    return error

if __name__ == "__main__":
    # 数据可视化准备
    # year_list=[2009,2010,2011,2017,2018,2021]
    dirname = "genderdata"
    savename = "all_data.txt"
    savepath = os.path.join(dirname, savename)
    # 读取文件
    datapath = savepath
    dataDf = pd.read_csv(datapath, index_col=0)
    dataDf.index = range(len(dataDf))

    # 划分训练集与测试集,均匀抽样
    X_all = dataDf
    y_all = np.array(dataDf.loc[:, "gender"])
    X_train, X_test, y_train, y_test = train_test_split(X_all, y_all, test_size=0.3, random_state=1, stratify=y_all)
    # data:需要进行分割的数据集
    # random_state:设置随机种子,保证每次运行生成相同的随机数
    # test_size:将数据分割成训练集的比例

    # print("begin debug......")

    # allSingleFeatureDSurface() # 画单特征决策面
    # getDecisionSurface2(0,0,1) # 画多特征决策面,身高,体重,pBoy=0.5
    # getDecisionSurface2_counter(2, 0, 1)  # 决策面分开画

    # ROC1(0,0) # 画身高的最小错误率贝叶斯分类ROC曲线并保存
    # TODO:身高的最小错误率贝叶斯分类PR曲线并保存
    # PR()  #身高的最小错误率贝叶斯分类PR曲线并保存
    # TODO: 贝叶斯估计
    # confusionMatrix1() # 单一特征贝叶斯分类器的混淆矩阵和评估指标
    # modelTests() # 测试所有单一特征贝叶斯分类器模型错误率并保存结果
    # modelTests2(X_train, X_test) # 多元正态分布Bayes模型测试错误率
    # modelTests3() # Parzen窗法估计,测试单一特征贝叶斯分类器模型错误率并保存结果

    # 选择参数
    pBoy_choice = 0
    feature_choice1 = 0
    feature_choice2 = 1
    featureList = ['height', 'weight', 'footsize']
    pBoyList = [0.5, 0.75, 0.9]
    pBoy = pBoyList[pBoy_choice]
    feature_name1 = featureList[feature_choice1]
    # feature_name2 = featureList[feature_choice2]

    riskMat = [[0, 6], [1, 0]]
    girlConProFunc, boyConProFunc = trainNB1(X_train, feature_name1)
    error = testModel4(X_test, girlConProFunc, boyConProFunc, pBoy, feature_name1, riskMat)
    print(error)

资料

Python基础之闭包函数

python划分训练集和测试集_训练集、验证集和测试集


grad_f1 = np.array([[float(fx.evalf(subs={x:x_tmp,y:y_tmp}))],

[float(fy.evalf(subs={x:x_tmp,y:y_tmp}))]])     

————————————————
版权声明:本文为CSDN博主「一小闷棍666」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/crusierLiu/article/details/105811211


实验三 线性分类器设计

以下用身高/体重/鞋尺码数据为例说明实验过程

1、基本要求:

试验直接设计线性分类器的方法,与基于概率密度估计的贝叶斯分类器进行

比较。体会留一法估计错误率的方法和结果。

2、具体步骤:

(1)同时采用身高和体重数据作为特征,用Fisher 线性判别方法求分类器,将该分类器应用到训练和测试样本,考察训练和测试错误情况。将训练样本和求得的决策边界画到图上,同时把以往用Bayes 方法求得的分类器也画到图上,比较结果的异同。

(2)选择上述实验的Fisher线性判别方法,用留一法在训练集上估计错误率,与在测试集上得到的错误率进行比较。

3、验收内容:

(1)上述具体步骤2(1)(2)为必做内容;

(2)简述分类器原理和基本算法;

(3)说明程序类型;

(3)说明实验结果并进行分析。


实验四 PCA

实验五 k近邻分类器

实验六 SVM分类器

关于Python

pycharm的快捷键CTRL+Y被redo占用了,那我就把删除整行设置成ALT+Y好了


np.linspace主要用来创建等差数列。
np.linspace参数:

numpy.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None, axis=0)
Return evenly spaced numbers over a specified interval.
(在start和stop之间返回均匀间隔的数据)
Returns num evenly spaced samples, calculated over the interval [start, stop].
(返回的是 [start, stop]之间的均匀分布)
The endpoint of the interval can optionally be excluded.
Changed in version 1.16.0: Non-scalar start and stop are now supported.
(可以选择是否排除间隔的终点)

参数含义:

start:返回样本数据开始点
stop:返回样本数据结束点
num:生成的样本数据量,默认为50
endpoint:True则包含stop;False则不包含stop
retstep:If True, return (samples, step), where step is the spacing between samples.(即如果为True则结果会给出数据间隔)
dtype:输出数组类型
axis:0(默认)或-1

使用例子:

np.linspace(2.0, 3.0, num=5)
array([ 2. , 2.25, 2.5 , 2.75, 3. ])
np.linspace(2.0, 3.0, num=5, endpoint=False)
array([ 2. , 2.2, 2.4, 2.6, 2.8])
np.linspace(2.0, 3.0, num=5, retstep=True)
(array([ 2. , 2.25, 2.5 , 2.75, 3. ]), 0.25)
————————————————
版权声明:本文为CSDN博主「Asher117」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Asher117/article/details/87855493


word[:2]# 取前两个 
word[2:] # 除了前两个,其他全部选取

自学报告

小组研讨,yolo

课程报告


   转载规则


《SHU模式识别》 Henry-Avery 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录