🌞欢迎莅临我的个人主页👈🏻这里是我专注于深度学习领域、用心分享知识精粹与智慧火花的独特角落!🍉

🌈如果大家喜欢文章,欢迎:关注🍷+点赞👍🏻+评论✍🏻+收藏🌟,如有错误敬请指正!🪐

🍓“请不要相信胜利就像山坡上的蒲公英一样唾手可得,但是请相信生活中总有美好值得我们全力以赴,哪怕粉身碎骨!”🌹

前言

🌟模式识别导论课程编程实践作业🌟

目录

任务说明

应用单个特征

应用两个特征

数据文件

程序实现思路

读取数据

设置参数

类条件概率密度的最大似然估计

判别函数

结果评价与可视化

完整代码

效果展示

结论分析

一、准确率分析

二、假阳性率分析

三、假阴性率分析

结论

决策表


任务说明

应用单个特征

身高或体重数据作为特征,在正态分布假设下利用最大似然估计分布密度参数,建立最小错误率Bayes分类器,写出得到的决策规则,将该分类器应用到测试样本,考察测试错误情况。在分类器设计时可以考虑采用不同的先验概率(如0.5 vs 0.5、0.75 vs 0.25、0.9 vs 0.1等)进行实验,考察对决策规则和错误率的影响。

应用两个特征

同时采用身高和体重数据作为特征,在正态分布假设下估计概率密度,建立最小错误率Bayes分类器,写出得到的决策规则,将该分类器应用到训练/测试样本,考察训练/测试错误情况,比较相关假设和不相关假设下结果的差异。在分类器设计时可以考察采用不同先验概率(如0.5 vs 0.5、0.75 vs 0.25、0.9 vs 0.1等)进行实验,考察对决策和错误率的影响。

数据文件

🍎下载地址:身高/体重数据文件

🍉文件内容

🫐训练样本集

1、FEMALE.TXT:包含50个女生的身高、体重数据

2、MALE.TXT:包含50个男生的身高、体重数据

🍇测试样本集
1、test1.txt:包含35个同学的身高、体重、性别数据(15个女生、20个男生)

2、test2.txt:包含300个同学的身高、体重、性别数据(50个女生、250个男生)

程序实现思路

注:本系列编程实践均使用Python完成

读取数据

使用numpy库的np.loadtxt读取文本数据

# ----------------------------读取数据---------------------------#
def load_data(filename):
    data = np.loadtxt(filename)
    return data

设置参数

利用贝叶斯分类,需要不同类别的先验概率与条件概率密度,在此我们设先验概率为一组可以调整的参数,如0.5 vs 0.5、0.75 vs 0.25、0.9 vs 0.1等(第一个是女生的概率,第二个是男生的概率

# -----------------------先验概率(女,男)-----------------------#
prior_probs = [(0.5, 0.5), (0.75, 0.25), (0.9, 0.1)]

类条件概率密度的最大似然估计

计算类条件概率密度需要根据特定的概率分布模型以及估计得到的参数来计算。

1️⃣首先是计算用于估计概率分布的参数,这个可以用最大似然估计得到。对于单特征,需要计算样本均值和标准差,对于多特征,需要计算样本均值和协方差矩阵

# ----------------------------最大似然估计-----------------------#
def max_estimate(data, multi_feature):
    if multi_feature:
        return np.mean(data, axis=0), np.cov(data, rowvar=False)
    else:
        return np.mean(data), np.std(data)

2️⃣概率分布模型根据要求使用的是正态分布

3️⃣计算类条件概率密度

单特征情况下,使用norm.pdf()(norm是scipy.stats.norm模块)来计算单变量正态分布的概率密度函数值,也就是单特征下的类条件概率密度。

多特征情况下,使用multivariate_normal.pdf(multivariate_normal是scipy.stats.multivariate_normal模块)来计算多变量正态分布的概率密度函数值,即多特征下的类条件概率密度。

#--------------------类条件概率密度---------------------#
# -------------------单特征-------------------#
p_female = norm.pdf(FEATURE, female_data[0], female_data[1])
p_male = norm.pdf(FEATURE, male_data[0], male_data[1])
# -------------------多特征-------------------#
p_female = multivariate_normal.pdf(FEATURE, female_data[0], female_data[1])
p_male = multivariate_normal.pdf(FEATURE, male_data[0], male_data[1])

判别函数

利用贝叶斯判别需要2个参数:先验概率和类条件概率密度

现在先验概率已经通过假设得到,类条件概率密度通过最大似然估计和正态分布得到,那么两个相乘就是后验概率,我们最终只需要比较后验概率就可以判定数据所属类别

# -------------------单特征--------------------#
# ---------------------------后验概率-------------------------------#
p_female = norm.pdf(FEATURE, female_data[0], female_data[1]) * prior_female
p_male = norm.pdf(FEATURE, male_data[0], male_data[1]) * prior_male
# -------------------多特征-------------------#
p_female = multivariate_normal.pdf(FEATURE, female_data[0], female_data[1]) * prior_female
p_male = multivariate_normal.pdf(FEATURE, male_data[0], male_data[1]) * prior_male
# ---------------------------判断---------------------------#
if p_female > p_male:
     predictions.append(2)  # 女性
else:
     predictions.append(1)  # 男性

补充:根据给定的文本数据来看,1代表男生的标签,2表示女生的标签

结果评价与可视化

⭐️性能评估部分我使用了准确率、错误率和混淆矩阵

🍑准确率和错误率

准确率:预测正确的样本占整个样本的比例

错误率分为假阳性率和假阴性率,这里我假设的男生是正样本、女生是负样本

# -------------------------计算错误率------------------------------#
def error_rate(predictions, true_labels):
    # -------------------阳性(男)--------------------------------#
    positive = np.sum(true_labels == 1)
    # --------------------阴性(女)-------------------------------#
    negative = np.sum(true_labels == 2)
    TP = np.sum((predictions == 1) & (true_labels == 1))
    TN = np.sum((predictions == 2) & (true_labels == 2))
    FP = np.sum((predictions == 1) & (true_labels == 2))
    FN = np.sum((predictions == 2) & (true_labels == 1))
    # -------------------准确率-------------------------#
    accuracy = (TP + TN) / (positive + negative)
    # -------------------假阳性率-------------------------#
    FPR = FP / negative
    # -------------------假阴性率-------------------------#
    FNR = FN / positive
    return accuracy, FPR, FNR

🥥混淆矩阵

TN:预测为阳性实际为阳性

TP:预测为阴性实际为阴性

FN:预测为阳性实际为阴性

FP:预测为阴性实际为阳性

利用内置函数confusion_matrix对预测结果和实际标签进行计算,结果需要注意对角线的值

from sklearn.metrics import confusion_matrix
# -------------------------计算混淆矩阵----------------------------#
cm = confusion_matrix(true_labels, prediction, labels=[1, 2])
confusion_matrices.append((prior_female, prior_male, cm))

⭐️可视化部分仅对混淆矩阵实现

传入混淆矩阵参数,使用seaborn库实现热力图可视化,同时我把界面美化了一下

# --------------------------可视化混淆矩阵-------------------------#
def draw_matrix(confusion_matrices):
    for prior_female, prior_male, cm in confusion_matrices:
        cmap = sns.diverging_palette(0, 230, 90, 60, as_cmap=True)
        sns.heatmap(cm, annot=True, fmt='d', cmap=cmap, linewidths=5, xticklabels=['Female', 'Male'],
                    yticklabels=['Female', 'Male'], square=True)
        plt.title(f'Prior Female: {prior_female}, Prior Male: {prior_male}')
        # plt.savefig(f'confusion_matrix_{prior_female}_{prior_male}.png')
        plt.show()

完整代码

🥕项目目录

        本部分我写了比较详细的注释(主要是便于后期随时修改),只需要改变几个参数就可以实现单特征(身高/体重)或多特征的性别分类任务

import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import matplotlib

matplotlib.use('TkAgg')
from scipy.stats import norm, multivariate_normal
from sklearn.metrics import confusion_matrix

# ----------------------训练数据集----------------------#
FEMALE = r'test1\data\FEMALE.TXT'
MALE = r'test1\data\MALE.TXT'
# ----------------------测试数据集----------------------#
#   测试数据集选择:1: test1.txt     2: test2.txt
# -----------------------------------------------------#
test_num = 1
TEST1 = r'test1\data\test1.txt'
TEST2 = r'test1\data\test2.txt'
# -----------------------先验概率(女,男)-----------------------#
prior_probs = [(0.5, 0.5), (0.4, 0.6), (0.15, 0.85)]
# -----------------------选择单特征/多特征-----------------------#
#       True:多特征
#       False:单特征(需要设置特征选项)
# -------------------------------------------------------------#
multi_feature = True
# -------------------------特征--------------------------------#
# 身高: 0 体重: 1
# -------------------------------------------------------------#
feature = 1

# 新添加:风险矩阵,假设将女性错分为男性的损失为 5,将男性错分为女性的损失为 3
loss_matrix = [[0, 3], [5, 0]]


# ----------------------------读取数据---------------------------#
def load_data(filename):
    data = np.loadtxt(filename)
    return data


# ----------------------------最大似然估计-----------------------#
def max_estimate(data, multi_feature):
    if multi_feature:
        return np.mean(data, axis=0), np.cov(data, rowvar=False)
    else:
        return np.mean(data), np.std(data)


# --------------------------贝叶斯判别函数-------------------------#
def bayes_classifier(test_data, multi_feature, feature, prior_male, prior_female, male_data, female_data,
                     use_minimum_error_rate=True):
    predictions = []
    for row in test_data:
        # -------------------单特征-------------------#
        if not multi_feature:
            FEATURE = row[feature]
            # ---------------------------后验概率-------------------------------#
            p_female = norm.pdf(FEATURE, female_data[0], female_data[1]) * prior_female
            p_male = norm.pdf(FEATURE, male_data[0], male_data[1]) * prior_male
        # -------------------多特征-------------------#
        else:
            FEATURE = row[:2]
            p_female = multivariate_normal.pdf(FEATURE, female_data[0], female_data[1]) * prior_female
            p_male = multivariate_normal.pdf(FEATURE, male_data[0], male_data[1]) * prior_male
        # ---------------------------判断---------------------------#
        if use_minimum_error_rate:
            if p_female > p_male:
                predictions.append(2)  # 女性
            else:
                predictions.append(1)  # 男性
        else:
            risk_female = loss_matrix[1][0] * p_male
            risk_male = loss_matrix[0][1] * p_female
            if risk_female < risk_male:
                predictions.append(2)  # 女性
            else:
                predictions.append(1)  # 男性
    return np.array(predictions)


# -------------------------计算错误率------------------------------#
def error_rate(predictions, true_labels):
    # -------------------阳性(男)--------------------------------#
    positive = np.sum(true_labels == 1)
    # --------------------阴性(女)-------------------------------#
    negative = np.sum(true_labels == 2)
    TP = np.sum((predictions == 1) & (true_labels == 1))
    TN = np.sum((predictions == 2) & (true_labels == 2))
    FP = np.sum((predictions == 1) & (true_labels == 2))
    FN = np.sum((predictions == 2) & (true_labels == 1))
    # -------------------准确率-------------------------#
    accuracy = (TP + TN) / (positive + negative)
    # -------------------假阳性率-------------------------#
    FPR = FP / negative
    # -------------------假阴性率-------------------------#
    FNR = FN / positive
    return accuracy, FPR, FNR


# --------------------------可视化混淆矩阵-------------------------#
def draw_matrix(confusion_matrices):
    for prior_female, prior_male, cm in confusion_matrices:
        cmap = sns.diverging_palette(0, 230, 90, 60, as_cmap=True)
        sns.heatmap(cm, annot=True, fmt='d', cmap=cmap, linewidths=5, xticklabels=['Female', 'Male'],
                    yticklabels=['Female', 'Male'], square=True)
        plt.title(f'Prior Female: {prior_female}, Prior Male: {prior_male}')
        # plt.savefig(f'confusion_matrix_{prior_female}_{prior_male}.png')
        plt.show()


if __name__ == '__main__':
    # 新增:让用户选择使用最小错误率还是最小风险贝叶斯决策
    decision_type = input("请选择决策类型(1. 最小错误率,2. 最小风险):")
    use_minimum_error_rate = True if decision_type == "1" else False

    # -------------------测试数据集-------------------#
    if test_num == 1:
        test_data = load_data(TEST1)
    elif test_num == 2:
        test_data = load_data(TEST2)
    # -------------------类别标签---------------------#
    true_labels = test_data[:, 2]
    # -------------------混淆矩阵---------------------#
    confusion_matrices = []
    # -------------------------------------------------------------#
    #                       单特征
    # -------------------------------------------------------------#
    if not multi_feature:
        females = load_data(FEMALE)[:, feature]
        males = load_data(MALE)[:, feature]
    # -------------------------------------------------------------#
    #                       多特征
    # -------------------------------------------------------------#
    else:
        females = load_data(FEMALE)
        males = load_data(MALE)
    # --------------------最大似然估计---------------------#
    female_data = max_estimate(females, multi_feature)
    male_data = max_estimate(males, multi_feature)
    # -------------------不同先验概率测试-------------------#
    for prior_female, prior_male in prior_probs:
        prediction = bayes_classifier(test_data, multi_feature, feature, prior_male, prior_female, male_data,
                                      female_data, use_minimum_error_rate)
        # -------------------------计算混淆矩阵----------------------------#
        cm = confusion_matrix(true_labels, prediction, labels=[1, 2])
        confusion_matrices.append((prior_female, prior_male, cm))
        # ----------------------------计算错误率---------------------------#
        accuracy, FPR, FNR = error_rate(prediction, true_labels)
        if not multi_feature:
            if feature:
                print(
                    f"Feature: Weight | Female: {prior_female:.2f} | Male: {prior_male:.2f} | 准确率: {accuracy * 100:.2f}% | 假阳性率: {FPR * 100:.2f}% | 假阴性率: {FNR * 100:.2f}%")
            else:
                print(
                    f"Feature: Height | Female: {prior_female:.2f} | Male: {prior_male:.2f} | 准确率: {accuracy * 100:.2f}% | 假阳性率: {FPR * 100:.2f}% | 假阴性率: {FNR * 100:.2f}%")
        else:
            print(
                f"Feature: Weight and Height | Female: {prior_female:.2f} | Male: {prior_male:.2f} | 准确率: {accuracy * 100:.2f}% | 假阳性率: {FPR * 100:.2f}% | 假阴性率:{FNR * 100:.2f}%")
    # ----------------------------绘制混淆矩阵---------------------------#
    draw_matrix(confusion_matrices)

效果展示

仅展示test2.txt测试集的测试结果

🥪终端

🍟混淆矩阵

结论分析

一、准确率分析

        随着赋予女性的先验概率从 0.50 增加到 0.90准确率呈现下降趋势。这表明当越来越倾向于预测为女性时,整体的正确分类比例在降低。可能的原因是,过度偏向某一类别会导致对另一类别的误判增多,从而降低了整体的准确率。

二、假阳性率分析

        在三种不同的先验概率设置下,假阳性率始终保持在 2.00%。这说明无论如何调整女性和男性的先验概率,将实际为男性而错误判断为女性的情况相对稳定且比例较低

三、假阴性率分析

        随着女性先验概率的增加,假阴性率不断升高。这意味着当更倾向于预测为女性时,将实际为男性错误判断为女性的情况增多,导致男性被误判的比例上升。

结论

  •   当阈值更加倾向于预测女性(即Female阈值较高)时,假阴性率增加,这表示更多的女性被错误地排除。
  • 假阳性率在整个过程中保持相对稳定,说明模型在区分非女性方面较为一致
  • 随着Female阈值的增加,整体的准确率下降,这可能表明模型在该数据集上更倾向于预测男性。  

决策表

  

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐