【科学计算与数学建模】基于概率生成模型的二分类


一、加载数据以及标准化

任务描述

本关任务:编写代码实现csv文件数据加载以及标准化。

数据加载

csv 文件格式的本质是一种以文本存储的表格数据,我们通常使用 Excel 工具即可读写 csv 文件。csv 文件的每行代表一行数据,每行数据中每个单元格内的数据以逗号隔开,如下图所示。

1

其中Python中提供了 csv 模块来读写 csv 文件。由于 csv 文件的格式本身比较简单(通常第一行是表头,用于说明每列数据的含义,接下来每行代表一行数据),因此使用 csv 模块读取 csv 文件也非常简单:

通常我们常使用pandas库来读取csv文件,python语句如下:

 
  1. import pandas as pd
  2. trainData = pd.read_csv("data/train.csv")
  3. testData = pd.read_csv("data/test.csv")
数据预处理

对于csv数据,我们通过pandas库加载完数据后,需要对该数据进行一定的预处理操作。对于Barry Becker人口普查数据集主要操作包括:特征量化标准化操作

特征量化

如何将该数据集中的特征量化是分析数据、特征提取的第一步,也是至关重要的一步,量化的方法有很多,我们本次项目只考虑简单实用的one-hot编码的方法。

独热编码即 One-Hot 编码,又称一位有效编码。其方法是使用N位状态寄存器来对 N个状态进行编码,每个状态都有它独立的寄存器位,并且在任意时候,其中只有一位有效。

举例如下: 假设我们有四个样本(行),每个样本有三个特征(列),如图:

,

上述feature_1有两种可能的取值,比如是男/女,这里男用1表示,女用2表示。feature_2 和 feature_3 各有4种取值(状态)。

one-hot 编码就是保证每个样本中的单个特征只有1位处于状态1,其他的都是0。

上述状态用 one-hot 编码如下图所示:

,

在具体实现中,我们常采用以下方法实现:

 
  1. import pandas as pd
  2. B = pd.get_dummies(A)

在完成one-hot 编码之前,有些地方我们需要注意,如对于输入特征,我们需要将其区分为离散型特征还是连续型特征。对于离散型特征我们可以不需要考虑特征量化,而对于连续型特征我们才常使用one-hot编码的方式实现特征表示。

数据标准化

数据的标准化(normalization) 是将数据按比例缩放,使之落入一个小的特定区间。在某些比较和评价的指标处理中经常会用到,去除数据的单位限制,将其转化为无量纲的纯数值,便于不同单位或量级的指标能够进行比较和加权。其中最典型的就是数据的归一化处理,即将数据统一映射到[0,1]区间上。

常用的标准化也有两种,一种是最大最小标准化,另一种是0-1标准化

其中最大最小标准化也称为离差标准化,是对原始数据的线性变换,使结果值映射到[0 - 1]之间。转换函数如下

,

其中max为样本数据的最大值,min为样本数据的最小值。这种方法有个缺陷就是当有新数据加入时,可能导致max和min的变化,需要重新定义。

另外一种方法是0-1标准化,这种方法给予原始数据的均值(mean)和标准差(standard deviation)进行数据的标准化。经过处理的数据符合标准正态分布,即均值为0,标准差为1。

通过将输入数据减去均值再除上标准差得到标准化后的结果。

代码实现如下:

 
  1. Data = (Data - Data.mean()) / Data.std() # 标准化

我们直接通过调用库函数中自带的方法来求解数据的均值和标准差。

编程要求

根据提示,在右侧编辑器补充代码。 1、完成数据集的加载 2、进行one-hot编码 3、进行数据标准化

测试说明

平台会对你编写的代码进行测试:

代码部分

import csv
import numpy as np
import pandas as pd


def dataProcess_X(data):
    # income和sex列的值可以直接使用1位二进制码表示,不需要进行one-hot编码

    if "income" in data.columns:
        Data = data.drop(["income", "sex"], axis=1)
    else:
        Data = data.drop(["sex"], axis=1)

    # 离散属性列
    listObjectData = [
        col for col in Data.columns if Data[col].dtypes == "object"]

    # 连续属性列
    listNonObjectData = [
        col for col in Data.columns if col not in listObjectData]
    ObjectData = Data[listObjectData]
    NonObjectData = Data[listNonObjectData]

    # 插入sex列,0代表male,1代表female
    NonObjectData.insert(0, "sex", (data["sex"] == " Female").astype(np.int))

    #2.one-hot编码
    ################# Begin #################
    ObjectData = pd.get_dummies(ObjectData)

    ################# End #################
    Data = pd.concat([NonObjectData, ObjectData], axis=1)  # 合并离散属性和连续属性

    Data = Data.astype("int64")

    #3.数据标准化
    ################# Begin #################

    Data = (Data - Data.mean()) / Data.std()  # 标准化
    ################# End #################
    return Data


if __name__ == "__main__":

    #1.加载数据集
    ################# Begin #################
    trainData = pd.read_csv("train.csv")
    testData = pd.read_csv("test.csv")


    ################# End #################
    #训练数据将107维降为106维,以适应测试数据
    X_train = dataProcess_X(trainData).drop(
        ['native_country_ Holand-Netherlands'], axis=1).values
    print(X_train.shape)

二、后验概率预测


任务描述

本关任务:编写使用概率生成模型实现二分类的小程序。

相关知识

为了完成本关任务,你需要掌握:1.后验概率的计算原理,2.均值及协方差计算,3.计算概率,4.预测分类。

后验概率的计算原理

给定N个样本的数据集,用X来表示,每个样本xn有两个属性,最终属于某个分类t

t={0,1} xn​=(xn1​xn2​​), 假设模型参数w=w=(w1​w2​​)

X=⎣⎢⎢⎢⎢⎢⎡​x1T​x2T​..xnT​​⎦⎥⎥⎥⎥⎥⎤​

根据贝叶斯公式有: p(w∣t,X)=p(t∣X)p(t∣X,w)p(w)​

p(w|t,X) 告诉我们:在已知训练样本集 X 以及这些样本的某个分类 t (这是一个监督学习,因为我们已经有了样本集X、以及样本集中每个样本所属的分类t),需要求解模型参数 w 。因此,w 是未知的,是需要根据样本通过贝叶斯概率公式来进行求解的。求得了p(w|t,X)的分布,也就知道了模型参数w。

当我们求得了最优的模型参数 w∗ 之后,给定一个待预测的样本 xnew​ 根据公式

P(Tnew​=1∣xnew​,w∗) 来计算新样本 xnew​ 归类为 1 的概率是多少,这就是模型的预测。

均值及协方差计算

在利用最大后验概率预测时,需要计算不同类别的均值以及方差和协方差等值,下面我们将重点介绍如何计算。

学过概率统计的同学都知道,统计里最基本的概念就是样本的均值,方差,或者再加个标准差。首先我们初始化一个含有n个样本的集合,下面依次给出这些概念的公式描述。

1.均值:描述的是样本集合的中间点。公式如下:

,

2.标准方差:描述的是样本集合的各种样本点到均值的距离之平均,一般用来描述一维数据。公式如下:

,

3.协方差:用来度量两个随机变量关系的统计量。

,

如上所示,很显然,均值描述的是样本集合的中间点,它告诉我们的信息是很有限的,而标准差给我们描述的则是样本集合的各个样本点到均值的距离之平均。

以这两个集合为例,[0,8,12,20]和[8,9,11,12],两个集合的均值都是10,但显然两个集合差别是很大的,计算两者的标准差,前者是8.3,后者是1.8,显然后者较为集中,故其标准差小一些,标准差描述的就是这种“散布度”。

之所以除以n-1而不是除以n,是因为这样能使我们以较小的样本集更好的逼近总体的标准差,即统计上所谓的“无偏估计”。而方差则仅仅是标准差的平方。

具体计算均值的代码段实现如下:

 
  1. mu = np.zeros((106,)) # 初始化类别1均值
  2. n = 0 # 初始化类别数量
  3. for i in range(train_data_size):
  4. mu += X_train[i]
  5. n += 1
  6. mu /= n #计算均值

计算方差与协方差的代码段实现如下,注意上下数据形式保持一致:

 
  1. sigma = np.zeros((106, 106)) # 类别方差
  2. for i in range(train_data_size):
  3. sigma += np.dot(np.transpose([X_train[i] - mu1]),
  4. [X_train[i] - mu1])
  5. ...
  6. sigma1 /= n1 #计算方差
  7. sigma2 /= n2 #计算方差
  8. shared_sigma = (n1 / train_data_size) * sigma1 +
  9. (n2 / train_data_size) * sigma2 #协方差计算
计算概率

通过上一节的内容,我们获得了计算概率需要的各项参数,下面我们需要根据这些参数直接计算测试数据的概率值。

2

进一步化简为:

z=(μ1−μ2)TΣ−1x−21​(μ1)TΣ−1μ1+21​(μ2)TΣ−1μ2+lnN2​N1​​

其中u1u2分别为两类均值,Σ−1为协方差,(μ1−μ2)TΣ−1x为W,−21​(μ1)TΣ−1μ1+21​(μ2)TΣ−1μ2+lnN2​N1​​为b。

我们可以通过python代码实现如上W和b,来实现测试值的标签预测。

 
  1. w = np.transpose(mu1 - mu2).dot(np.linalg.inv(shared_sigma))
  2. b = -0.5 * np.transpose(mu1).dot(np.linalg.inv(shared_sigma)).dot(mu1) +\
  3. 0.5 * np.transpose(mu2).dot(np.linalg.inv(shared_sigma)).dot(mu2) +\
  4. np.log(float(n1 / n2))
预测分类

对于获得的概率值,如果大于0.5,则预测其标签为1,反之,预测其为0。

实现代码段如下:

 
  1. for i in range(len):
  2. if x[i] > 0.5:
  3. ans = 1
  4. else:
  5. ans = 0

当然,我们也可以将预测结果直接写入excel表格中, 通过writer.writerow()的方式完成。

编程要求

根据提示,在右侧编辑器补充代码。 1、计算均值 2、计算协方差 3、根据计算的概率进行分类

测试说明


开始你的任务吧,祝你成功!

代码部分

import csv
import numpy as np
import pandas as pd


def dataProcess_X(data):
    # income和sex列的值可以直接使用1位二进制码表示,不需要进行one-hot编码
    #print(data.columns)
    if "income" in data.columns:
        Data = data.drop(["income", "sex"], axis=1)
    else:
        Data = data.drop(["sex"], axis=1)

    # 离散属性列
    listObjectData = [
        col for col in Data.columns if Data[col].dtypes == "object"]

    # 连续属性列
    listNonObjectData = [
        col for col in Data.columns if col not in listObjectData]
    ObjectData = Data[listObjectData]
    NonObjectData = Data[listNonObjectData]
    # 插入sex列,0代表male,1代表female
    NonObjectData.insert(0, "sex", (data["sex"] == " Female").astype(np.int))

    ObjectData = pd.get_dummies(ObjectData)  # one-hot编码

    Data = pd.concat([NonObjectData, ObjectData], axis=1)  # 合并离散属性和连续属性

    Data = Data.astype("int64")

    Data = (Data - Data.mean()) / Data.std()  # 标准化
    return Data


def dataProcess_Y(data):
    # income属性,0代表小于等于50K,1代表大于50K
    return (data["income"] == " >50K").astype(np.int)


def train(X_train, y_train):
    train_data_size = X_train.shape[0]

    mu1 = np.zeros((106,))  # 类别1均值
    mu2 = np.zeros((106,))  # 类别2均值
    n1 = 0  # 类别1数量
    n2 = 0  # 类别2数量

    for i in range(train_data_size):
        #1.计算均值
        ################# Begin #################
        if y_train[i] == 1:     # >50k
            mu1 += X_train[i]
            n1 += 1
        else:
            mu2 += X_train[i]
            n2 += 1
        ################# End #################

    mu1 /= n1
    mu2 /= n2

    sigma1 = np.zeros((106, 106))  # 类别1方差
    sigma2 = np.zeros((106, 106))  # 类别2方差
    for i in range(train_data_size):
        if y_train[i] == 1:
            sigma1 += np.dot(np.transpose([X_train[i] - mu1]),
                             [X_train[i] - mu1])
        else:
            sigma2 += np.dot(np.transpose([X_train[i] - mu2]),
                             [X_train[i] - mu2])

    sigma1 /= n1
    sigma2 /= n2

    #2.计算协方差
    ################# Begin #################
    shared_sigma = (n1 / train_data_size) * sigma1 + (n2 / train_data_size) * sigma2 #协方差计算
    ################# End #################

    return mu1, mu2, shared_sigma, n1, n2


def cal(X_test, mu1, mu2, shared_sigma, n1, n2):
    # 计算概率
    w = np.transpose(mu1 - mu2).dot(np.linalg.inv(shared_sigma))
    b = -0.5 * np.transpose(mu1).dot(np.linalg.inv(shared_sigma)).dot(mu1) + \
        0.5 * np.transpose(mu2).dot(np.linalg.inv(shared_sigma)).dot(mu2) + \
        np.log(float(n1 / n2))
    arr = np.empty([X_test.shape[0], 1], dtype=float)
    for i in range(X_test.shape[0]):
        z = X_test[i, :].dot(w) + b
        z *= -1
        arr[i][0] = 1 / (1 + np.exp(z))
    return np.clip(arr, 1e-8, 1-(1e-8))


def predict(x):

    ans = np.zeros([x.shape[0], 1], dtype=int)

    #3.根据计算的概率进行分类
    ################# Begin #################
    for i in range(x.shape[0]):
        # print(x[i])
        if x[i] > 0.5:
            ans[i] = 1
        else:
            ans[i] = 0

    ################# End #################

    return ans


if __name__ == "__main__":

    trainData = pd.read_csv("train.csv")
    testData = pd.read_csv("test.csv")

    #训练数据将107维降为106维,以适应测试数据
    X_train = dataProcess_X(trainData).drop(
        ['native_country_ Holand-Netherlands'], axis=1).values
    X_test = dataProcess_X(testData).values
    y_train = dataProcess_Y(trainData).values

    # 计算概率所需的参数
    mu1, mu2, shared_sigma, n1, n2 = train(X_train, y_train)
    result = cal(X_test, mu1, mu2, shared_sigma, n1, n2)
    answer = predict(result)
    print(answer[5:15])

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值