【机器学习】线性模型-softmax回归(三种方法实现 softmax 回归模型:自定义、sklearn、tensorflow)...

三种方法实现 softmax 回归模型
自定义、sklearn、tensorflow

f5c46032f94c39b4e2871f7d3c6f0c73.png

1c08a9898b85a140e60aec80492dc76b.png

一、原理

softmax回归

dbcc6bf10752567392bc41a767863bad.png

逻辑回归和softmax回归的区别

28373da1317ec717db4521561c10540c.png

二、示例代码1-Softmax_Sklearn

sklearn的LogisticRegression模块对鸢尾花数据集进行Softmax 回归多分类

Iris_Data脚本:对数据标准化、标签独热编码、打乱样本排序

# 导入 pandas 库,用于数据分析和处理
import pandas as pd


# 读取 iris.csv 文件,其中包含了鸢尾花数据集,返回一个 DataFrame 对象
data = pd.read_csv('Softmax Regression/iris.csv')


# 从 DataFrame 对象中获取 Species 列的值,返回一个一维数组,表示鸢尾花的类别
ydata = data['Species'].values
# 从 DataFrame 对象中获取第 1 到第 4 列的值,返回一个二维数组,表示鸢尾花的特征
xdata = data.iloc[:, 1:5].values


# 数据处理
# 导入 numpy 库,用于科学计算和数组操作
import numpy as np


# 对 xdata 数组进行标准化,即减去每一列的均值,再除以每一列的标准差,返回一个新的数组,表示标准化后的特征
handle_x_data = (xdata - np.mean(xdata, axis=0)) / np.std(xdata, axis=0)


# 对 ydata 数组进行独热化,即将每个类别用一个向量表示,其中只有一个元素为 1,其余为 0,返回一个新的数组,表示独热化后的类别
ydata = pd.get_dummies(data['Species']).values


# 因为数据中类别比较集中,不易于训练,因此打乱数据


# 首先将 handle_x_data 数组和 ydata 数组在水平方向(即列方向)拼接在一起,返回一个新的数组,表示特征和类别的组合
xydata = np.hstack((handle_x_data, ydata))
# 对 xydata 数组进行随机打乱,使得每一行的顺序变化,但是每一行的内容不变,返回一个新的数组,表示打乱后的数据
np.random.shuffle(xydata)


# 分离数据
# 从 xydata 数组中获取前 4 列的值,返回一个新的数组,表示打乱后的特征
X_DATA = xydata[:, :4]


# 从 xydata 数组中获取后 3 列的值,返回一个新的数组,表示打乱后的类别
Y_DATA = xydata[:, 4:]


# 将 X_DATA 数组和 Y_DATA 数组组成一个列表,赋值给 Data 变量,表示最终的数据集
Data = [X_DATA, Y_DATA]

Softmax_Sklearn.py

#-*- coding:utf-8 -*-
import sys 
import io 
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
# 导入 sklearn 库,用于机器学习相关的功能
import sklearn as sk
# 导入 Iris_Data 模块,其中包含了鸢尾花数据集
from Iris_Data import Data as smdata
# 导入 numpy 库,用于科学计算和数组操作
import numpy as np


# 从 sklearn.linear_model 模块导入 LogisticRegression 类,用于实现逻辑回归或 Softmax 回归
from sklearn.linear_model import LogisticRegression


# 创建一个 LogisticRegression 的实例,指定参数为多分类模式,使用 sag 算法,正则化系数为 200,最大迭代次数为 10000
sklr = LogisticRegression(multi_class='multinomial', solver='sag', C=200, max_iter=10000)


# 从 prettytable 模块导入 PrettyTable 类,用于格式化输出混淆矩阵
from prettytable import PrettyTable
# 定义一个函数,用于计算并输出真实标签和预测标签之间的混淆矩阵
def confusion(realy, outy, method='Sklearn'):
    # 创建一个 PrettyTable 的实例,用于存储混淆矩阵的数据
    mix = PrettyTable()
    # 获取真实标签中的所有类别,并按照降序排序
    type = sorted(list(set(realy.T[0])), reverse=True)
    # 设置表格的列名,第一列为方法名,后面的列为预测的各个类别
    mix.field_names = [method] + ['预测:%d类'%si for si in type]
    # 用一个字典来存储混淆矩阵的数据,键为真实的类别,值为一个列表,表示预测为各个类别的样本数量
    cmdict = {}
    # 遍历真实的类别
    for jkj in type:
        # 初始化一个空列表
        cmdict[jkj] = []
        # 遍历预测的类别
        for hh in type:
            # 计算真实为 jkj 类,预测为 hh 类的样本数量,并添加到列表中
            hu = len(['0' for jj in range(len(realy)) if realy[jj][0] == jkj and outy[jj][0] == hh])
            cmdict[jkj].append(hu)
    # 遍历真实的类别
    for fu in type:
        # 将每一行的数据添加到表格中,第一列为真实的类别,后面的列为预测的各个类别的样本数量
        mix.add_row([u'真实:%d类'%fu] + cmdict[fu])
    # 返回表格对象
    return mix




# 定义一个函数,用于将独热编码的类别变为标识为 1,2,3 的类别
def transign(eydata):
    # 初始化一个空列表
    ysign = []
    # 遍历独热编码的类别
    for hh in eydata:
        # 找到 1 的位置,加 1 后作为类别标识,并添加到列表中
        ysign.append([list(hh).index(1) + 1])
    # 将列表转化为 numpy 数组并返回
    return np.array(ysign)


# 主函数
if __name__ == '__main__':
    # 调用sklearn的LogisticRegression实例的 fit 方法,用鸢尾花数据集的特征和类别来训练 Softmax 回归模型
    regre = sklr.fit(smdata[0], transign(smdata[1]).T[0])


    # 调用 predict 方法,用训练好的模型来预测鸢尾花数据集的类别
    predata = np.array([sklr.predict(smdata[0])]).T


    # 打印模型的系数,包括权重向量和截距项
    print('系数为:\n', np.hstack((sklr.coef_, np.array([sklr.intercept_]).T)).T)


    # 调用 confusion 函数,输出真实标签和预测标签之间的混淆矩阵
    print('混淆矩阵:\n', confusion(transign(smdata[1]), predata))

输出结果:

8526cfbe3c0d2ccb91bc9d1ff4d13be8.png

输出结果是用 Softmax 回归对鸢尾花数据集进行多分类的结果,其中:

  • 系数为:是一个 5×3 的二维数组,它表示 Softmax 回归模型的参数矩阵,每一列表示一个类别的权重向量和截距项,每一行表示一个特征的系数。例如,第一列表示第一类(山鸢尾)的权重向量和截距项,第一行表示第一个特征(萼片长度)的系数。

  • 混淆矩阵:是一个 3×3 的二维表格,它表示模型的预测结果和真实标签之间的对应关系,每一行表示真实的类别,每一列表示预测的类别,每个单元格表示真实为某一类,预测为某一类的样本数量。例如,第一行第一列表示真实为第一类(山鸢尾),预测为第一类(山鸢尾)的样本数量,为 49。

三、示例代码2-Softmax-TensorFlow

使用 TensorFlow 2.x 实现 softmax 回归模型,对鸢尾花数据集进行分类。代码的主要步骤如下:

  • 导入所需的模块,如 tensorflownumpyprettytable 等。

  • 定义一个 confusion 函数,用于计算混淆矩阵,评估模型的分类效果。函数的参数包括:

    • realy:真实的类别标签,是一个一维数组。

    • outy:预测的类别标签,是一个一维数组。

    • method:模型的名称,用于在表格的第一列显示,默认为 TensorFlow

  • 定义一个 transign 函数,用于将独热编码的类别变为标识为 1,2,3 的类别。函数的参数是:

    • eydata:独热编码的类别标签,是一个二维数组,每一行是一个样本,每一列是一个类别的独热编码。

  • 定义一个 trans_tf 函数,用于构建和训练 softmax 回归模型。函数的参数包括:

    • datax:输入数据的特征矩阵,是一个二维数组,每一行是一个样本,每一列是一个特征。

    • datay:输入数据的标签矩阵,是一个二维数组,每一行是一个样本,每一列是一个类别的独热编码。

    • prea:预测数据的特征矩阵,是一个二维数组,用于测试模型的泛化能力。

    • learn_rate:学习率,控制梯度下降的速度,默认为 0.8。

    • iter_tiems:迭代次数,控制训练的轮数,默认为 40000。

    • error:误差阈值,控制训练的提前结束条件,默认为 1e-9。

  • 在 trans_tf 函数中,首先将输入数据和标签转换为张量,然后定义变量 Weight 和 Bias,用于存储 softmax 回归的参数。接着定义模型的输出 model_output,使用 tf.nn.softmax 函数将线性组合转换为概率分布。然后定义损失函数 costfunc,使用交叉熵作为分类的代价,并加入 L2 正则化项,防止过拟合。接下来定义优化器 optimizer,使用梯度下降法更新参数。最后,在循环中执行训练步骤,记录损失函数的值,直到达到迭代次数或误差阈值。最后,返回损失函数的值,预测数据的类别,以及模型的参数。

  • 在主函数中,首先从 Iris_Data 模块中导入鸢尾花数据集,然后调用 trans_tf 函数,传入训练数据和预测数据,得到训练结果。然后打印模型的参数和混淆矩阵,评估模型的分类效果。最后,使用 matplotlib 模块绘制损失函数的图像,展示模型的收敛过程。

import tensorflow as tf


# 导入 Iris 数据集,将其分为特征数据 smdata[0] 和标签数据 smdata[1]
from Iris_Data import Data as smdata
import numpy as np


# 定义一个函数,用于计算混淆矩阵
from prettytable import PrettyTable
def confusion(realy, outy, method='TensorFlow'):
    mix = PrettyTable()
    type = sorted(list(set(realy.T[0])), reverse=True)
    mix.field_names = [method] + ['预测:%d类'%si for si in type]
    cmdict = {}
    for jkj in type:
        cmdict[jkj] = []
        for hh in type:
            hu = len(['0' for jj in range(len(realy)) if realy[jj][0] == jkj and outy[jj][0] == hh])
            cmdict[jkj].append(hu)
    for fu in type:
        mix.add_row(['真实:%d类'%fu] + cmdict[fu])
    # 返回表格对象
    return mix
  
# 定义一个函数,用于将独热编码的类别变为标识为 1,2,3 的类别
def transign(eydata):
    ysign = []
    for hh in eydata:
        ysign.append([list(hh).index(1) + 1])
    return np.array(ysign)




# 定义一个函数,用于构建 softmax 回归模型  使用tensorflow2.x
def trans_tf(datax, datay, prea, learn_rate=0.8, iter_tiems=40000, error=1e-9):
    x_data = tf.convert_to_tensor(datax, dtype=tf.float32)
    y_target = tf.convert_to_tensor(datay, dtype=tf.float32)
    
    Weight = tf.Variable(tf.random.normal(shape=[len(datax[0]), len(datay[0])]))
    Bias = tf.Variable(tf.random.normal(shape=[1, len(datay[0])]))


    def model_output(x_data):
        x_data= tf.cast(x_data, tf.float32)
        return tf.nn.softmax(tf.add(tf.matmul(x_data, Weight), Bias))


    def cross_entropy(y_target, y_pred):
        return tf.reduce_sum(y_target * tf.math.log(y_pred))


    def regularizer():
        return tf.nn.l2_loss(Weight) * 2 / 20000


    def costfunc(y_target, y_pred):
        return -cross_entropy(y_target, y_pred) / len(datax) + regularizer()
    
    optimizer = tf.optimizers.SGD(learn_rate) # 梯度
    #optimizer = tf.optimizers.Momentum(learning_rate=learn_rate, momentum=0.9) # 动量法
    #optimizer = tf.optimizers.Adadelta(learning_rate=learn_rate, rho=0.55, epsilon=1e-08)
    #optimizer = tf.optimizers.Adam(learning_rate=learn_rate, beta1=0.9, beta2=0.99, epsilon=1e-08)


    
    loss_vec = []  
    # 开始训练
    for i in range(iter_tiems):
        with tf.GradientTape() as tape:
            y_pred = model_output(x_data)
            loss = costfunc(y_target, y_pred)
        gradients = tape.gradient(loss, [Weight, Bias])
        optimizer.apply_gradients(zip(gradients, [Weight, Bias]))
        loss_vec.append(loss.numpy())
        if len(loss_vec) > 2:
            if loss_vec[-2] - loss_vec[-1] >= 0 and (loss_vec[-2] - loss_vec[-1]) <= error:
                break


    predata = model_output(prea)
    maxnumber = tf.reduce_max(predata, axis=1)
    y_pre_type = []
    for jj in range(len(maxnumber)):
        fu = tf.where(tf.equal(predata[jj], maxnumber[jj]))[0][0] + 1
        y_pre_type.append([fu])


    y_pre_type = np.array(y_pre_type)
    # 返回成本函数的值的列表,预测的类别的数组,以及权重矩阵和偏置向量的值
    return loss_vec, y_pre_type, Weight.numpy(), Bias.numpy() 




# 主函数
if __name__ == '__main__':
    tf_result = trans_tf(smdata[0], smdata[1], smdata[0])


    print('系数:\n', np.vstack((tf_result[2], tf_result[3])))


    print('混淆矩阵:\n', confusion(transign(smdata[1]), tf_result[1]))


    # 绘制成本函数的图像
    import matplotlib.pyplot as plt
    from pylab import mpl  # 作图显示中文


    # 设置中文字体为仿宋
    mpl.rcParams['font.sans-serif'] = ['FangSong']
    # 设置不显示负号
    mpl.rcParams['axes.unicode_minus'] = False


    plt.plot(list(range(len(tf_result[0]))), tf_result[0], '-', linewidth=5)
    plt.title('成本函数图')
    plt.ylabel('Cost 值')
    plt.xlabel('迭代次数')
    plt.show()

输出结果:

6fa7ce0f446d4ea6b594117d5ffa825c.jpeg

2c500561c5566897c7bad3869a7ea24f.jpeg

四、示例代码3-Softmax  自定义实现

使用逻辑回归模型对鸢尾花数据集进行分类,评估模型的效果。代码使用了梯度下降法和正则化技术来优化模型的参数,使用了混淆矩阵和成本函数来评估模型的性能。代码使用了 numpy 库来进行矩阵运算,使用了 prettytable 库来生成表格,使用了 matplotlib 库来绘制图像。

from Iris_Data import Data as smdata
import numpy as np


class LRReg:
    def __init__(self, learn_rate=0.9, iter_times=40000, error=1e-17):
        self.learn_rate = learn_rate
        self.iter_times = iter_times
        self.error = error


    # w和b合为一个参数,也就是x最后加上一列全为1的数据。
    def trans(self, xdata):
        one1 = np.ones(len(xdata))
        xta = np.append(xdata, one1.reshape(-1, 1), axis=1)
        return xta


    # 梯度下降法
    def Gradient(self, xdata, ydata, func=trans):
        xdata = func(self, xdata)
        # 系数w,b的初始化
        self.weights = np.zeros((len(xdata[0]), len(ydata[0])))
        # 存储成本函数的值
        cost_function = []


        for i in range(self.iter_times):
            # 计算np.exp(X.W)的值
            exp_xw = np.exp(np.dot(xdata, self.weights))


            #计算y_predict每一行的和值
            sumrow = np.sum(exp_xw, axis=1).reshape(-1, 1)


            # 计算除去和值得值
            devi_sum = exp_xw / sumrow


            # 计算减法
            sub_y = ydata - devi_sum


            # 得到梯度
            grad_W = -1 / len(xdata) * np.dot(xdata.T, sub_y)




            # 正则化
            # 成本函数中添加系数的L2范数
            l2norm = np.sum(0.5 * np.dot(self.weights.T, self.weights) / len(xdata))


            last_grad_W = grad_W + 0.002 * self.weights / len(xdata)


            # 计算最大似然的对数的值
            likehood = np.sum(ydata * np.log(devi_sum))


            cost = - likehood / len(xdata) + l2norm


            cost_function.append(cost)


            # 训练提前结束
            if len(cost_function) > 2:
                if 0 <= cost_function[-2] - cost_function[-1] <= self.error:
                    break


            #更新
            self.weights = self.weights - self.learn_rate * last_grad_W


        return self.weights, cost_function


    # 预测
    def predict(self, xdata, func=trans):
        pnum = np.dot(func(self, xdata), self.weights)
        # 选择每一行中最大的数的index
        maxnumber = np.max(pnum, axis=1)
        # 预测的类别
        y_pre_type =[]
        for jj in range(len(maxnumber)):
            fu = list(pnum[jj]).index(maxnumber[jj]) + 1
            y_pre_type.append([fu])
        return np.array(y_pre_type)


# 将独热编码的类别变为标识为1,2,3的类别
def transign(eydata):
    ysign = []
    for hh in eydata:
        ysign.append([list(hh).index(1) + 1])
    return np.array(ysign) #


#计算混淆矩阵
from prettytable import PrettyTable
def confusion(realy, outy, method='AnFany'):
    mix = PrettyTable()
    type = sorted(list(set(realy.T[0])), reverse=True)
    mix.field_names = [method] + ['预测:%d类'%si for si in type]
    # 字典形式存储混淆矩阵数据
    cmdict = {}
    for jkj in type:
        cmdict[jkj] = []
        for hh in type:
            hu = len(['0' for jj in range(len(realy)) if realy[jj][0] == jkj and outy[jj][0] == hh])
            cmdict[jkj].append(hu)
    # 输出表格
    for fu in type:
        mix.add_row(['真实:%d类'%fu] + cmdict[fu])
    return mix


# 主函数
if __name__ == '__main__':
    lr_re = LRReg() #
    lf = lr_re.Gradient(smdata[0], smdata[1])


    y_calss_pre = lr_re.predict(smdata[0])
    print('系数:\n', lr_re.weights)


    print('混淆矩阵:\n', confusion(transign(smdata[1]), y_calss_pre))


    # 绘制成本函数图
    import matplotlib.pyplot as plt
    from pylab import mpl  # 作图显示中文


    mpl.rcParams['font.sans-serif'] = ['FangSong']  # 设置中文字体新宋体
    mpl.rcParams['axes.unicode_minus'] = False


    plt.plot(list(range(len(lf[1]))), lf[1], '-', linewidth=5)
    plt.title('成本函数图')
    plt.ylabel('Cost 值')
    plt.xlabel('迭代次数')
    plt.show()

输出结果:

5027bdebfddaac1cc07b9917b3cc0f58.jpeg

b134fef2ff3b1b650fbf6fc640564215.jpeg

代码解读:

90efe365e2453a8187bc75f326e4c5e8.png

五、以上三种softmax回归对比(自定义、sklearn、tensorflow2.x)

使用三种不同的方法实现 softmax 回归模型,对一个人工生成的数据集进行多分类,并绘制不同方法的分割结果和混淆矩阵。

# 引入三种方法
import Softmax_AnFany as SM_A  # 需要注释105行以后的内容
import Softmax_Sklearn as SM_S # 需要注释37行以后的内容
import Softmax_TensorFlow as SM_T # 需要注释86行以后的内容
import matplotlib.pyplot as plt # 引入绘图模块
from pylab import mpl  # 作图显示中文
mpl.rcParams['font.sans-serif'] = ['FangSong']  # 设置中文字体新宋体
mpl.rcParams['axes.unicode_minus'] = False # 设置正常显示负号
import numpy as np # 引入数值计算模块


x_data = np.random.random((900, 2)) # 生成900个二维特征的随机样本
y_data = [] # 初始化标签列表




for dat in x_data: # 遍历每个样本
    if dat[1] - 3 * dat[0] + 0.5 >= 0: # 根据一个线性函数划分为第一类
        y_data.append([1, 0, 0]) # 用独热编码表示类别
    elif dat[1] - 3 * dat[0] + 0.5 < 0 and dat[1] - 3 * dat[0] + 1.5 > 0 : # 根据另一个线性函数划分为第二类
        y_data.append([0, 1, 0]) # 用独热编码表示类别
    elif dat[1] - 3 * dat[0] + 1.5 <= 0: # 其他情况划分为第三类
        y_data.append([0, 0, 1]) # 用独热编码表示类别


y_data = np.array(y_data) # 将标签列表转换为数组


def divided(xdata, ydata, percent=0.2): # 定义一个函数,用于将数据集划分为训练集和测试集
    sign_list = list(range(len(xdata))) # 生成样本的序号列表
    #用于测试的序号
    select_sign = sorted(np.random.choice(sign_list, int(len(xdata)*percent), replace=False)) # 随机选择一定比例的序号作为测试集的序号


    no_select_sign = [isign for isign in sign_list if isign not in select_sign] # 剩余的序号作为训练集的序号


    x_predict_data = xdata[select_sign] # 根据测试集的序号筛选特征矩阵
    y_predict_data = ydata[select_sign] # 根据测试集的序号筛选标签矩阵


    x_train_data = xdata[no_select_sign] # 根据训练集的序号筛选特征矩阵
    y_train_data = ydata[no_select_sign] # 根据训练集的序号筛选标签矩阵


    return x_train_data, y_train_data, x_predict_data, y_predict_data #返回训练集和测试集的特征矩阵和标签矩阵


# 数据名称
Train_X, Train_Y, Predict_X, Predict_Y = divided(x_data, y_data) # 调用函数,划分数据集


# 绘制散点图
def fig_scatter(exdata, eydata, titl='训练数据散点图', co=['r', 'b', 'g'], marker=['o','*','^']): # 定义一个函数,用于绘制数据集的散点图
    for ii in range(len(eydata[0])): # 遍历每个类别
        datax = exdata[eydata[:, ii] == 1] # 根据标签矩阵筛选出属于该类别的样本
        plt.scatter(datax[:, 0], datax[:, -1], c=co[ii], s=50, marker=marker[ii]) # 绘制样本的散点图,用不同的颜色和标记表示不同的类别
    plt.title(titl) # 设置图的标题
    plt.legend(['1类', '2类', '3类']) # 设置图例
    plt.xlabel('X1 值') # 设置x轴标签
    plt.ylabel('X2 值') # 设置y轴标签




# 计算不同的方法得到的结果
# AnFany
lr_re = SM_A.LRReg() # 创建一个Softmax_AnFany模块中的LRReg类的实例
lf = lr_re.Gradient(Train_X, Train_Y) # 调用Gradient方法,传入训练数据,得到参数矩阵和成本函数的值的列表
Pre = lr_re.predict(Predict_X) # 调用predict方法,传入测试数据,得到预测类别的数组
print('AnFany混淆矩阵:\n', SM_A.confusion(SM_A.transign(Predict_Y), Pre)) # 调用confusion函数,传入真实类别和预测类别,得到混淆矩阵,打印混淆矩阵




# Sklearn
regre = SM_S.sklr.fit(Train_X, SM_S.transign(Train_Y).T[0]) # 调用Softmax_Sklearn模块中的sklr对象的fit方法,传入训练数据,得到参数矩阵
predata = np.array([SM_S.sklr.predict(Predict_X)]).T # 调用sklr对象的predict方法,传入测试数据,得到预测类别的数组
print('Sklearn混淆矩阵:\n',SM_S.confusion(SM_S.transign(Predict_Y), predata)) # 调用confusion函数,传入真实类别和预测类别,得到混淆矩阵,打印混淆矩阵




# TensorFlow
tf_result = SM_T.trans_tf(Train_X, Train_Y, Predict_X) # 调用Softmax_TensorFlow模块中的trans_tf函数,传入训练数据和测试数据,得到参数矩阵和预测类别的数组
print('TensorFlow混淆矩阵:\n', SM_T.confusion(SM_T.transign(Predict_Y), tf_result[1])) # 调用confusion函数,传入真实类别和预测类别,得到混淆矩阵,打印混淆矩阵


plt.subplot(2, 1, 1) # 创建一个2行1列的子图,选择第一个子图
fig_scatter(Train_X, Train_Y) # 调用fig_scatter函数,绘制训练数据的散点图




plt.subplot(2, 1, 2) # 选择第二个子图
plt.text(0.5, 0.9, '训练的结果展示', size='large', weight='extra bold') # 在图中添加文本,显示标题
plt.axis('off') # 关闭坐标轴
plt.text(0.00, 0.7, 'AnFany  系数\n%s'%lr_re.weights, size='large') # 在图中添加文本,显示AnFany方法的参数矩阵
plt.text(0.33, 0.5, 'Sklearn  系数\n%s'%np.hstack((SM_S.sklr.coef_, np.array([SM_S.sklr.intercept_]).T)).T, size='large') # 在图中添加文本,显示Sklearn方法的参数矩阵
plt.text(0.66, 0.3, 'TensorFlow  系数\n%s'%np.vstack((tf_result[2], tf_result[3])), size='large') # 在图中添加文本,显示TensorFlow方法的参数矩阵


plt.show() # 显示图像


# 输出预测的结果


#根据三种方法生成的系数,绘制分割线
#绘制三种方法各自生成的直线需要的数据
def tnd_ydata(datdxx, weights): # 定义一个函数,用于计算不同方法的参数矩阵对应的分割面的值
    dmin = datdxx[:, 0] # 取特征矩阵的第一列
    x1da = np.linspace(datdxx[:, 0].min() - 0.2, datdxx[:, 0].max() + 0.2, 100) # 在第一列的最小值和最大值之间生成100个等间距的点
    x2da = np.linspace(datdxx[:, 1].min() - 0.2, datdxx[:, 1].max() + 0.2, 100) # 在第二列的最小值和最大值之间生成100个等间距的点
    X, Y = np.meshgrid(x1da, x2da) # 生成网格矩阵,用于表示特征空间中的所有点
    ydaset = [] # 初始化分割面的值的列表
    tw = weights.T # 转置参数矩阵 kx(n+1)  3x3
    for hh in range(len(tw)): # 遍历每个类别
        yda = tw[hh][0] * X + tw[hh][1] * Y + tw[hh][2] # 计算每个点属于该类别的得分,即特征矩阵乘以参数矩阵的结果
        ydaset.append(yda) # 将每个类别的得分矩阵存储在一个列表中  3x1   三个小矩阵构成一个大矩阵
    return X, Y, ydaset # 返回网格矩阵和得分矩阵的列表




from mpl_toolkits.mplot3d import Axes3D # 引入三维绘图模块
Af_data = tnd_ydata(Train_X, lr_re.weights)# AnFany # 调用tnd_ydata函数,传入训练数据和AnFany方法的参数矩阵,得到AnFany方法的分割面的值
Sk_data = tnd_ydata(Train_X, np.hstack((SM_S.sklr.coef_, np.array([SM_S.sklr.intercept_]).T)).T) # Sklearn # 调用tnd_ydata函数,传入训练数据和Sklearn方法的参数矩阵,得到Sklearn方法的分割面的值
Tf_data = tnd_ydata(Train_X, np.vstack((tf_result[2], tf_result[3]))) # TensorFlow # 调用tnd_ydata函数,传入训练数据和TensorFlow方法的参数矩阵,得到TensorFlow方法的分割面的值


fig = plt.figure() # 创建一个plt.figure对象,用于绘制四个子图
ax = fig.add_subplot(2, 2, 1, projection='3d') # 创建一个2行2列的子图,选择第一个子图,设置为三维投影
fig_scatter(Train_X, Train_Y) # 调用fig_scatter函数,绘制训练数据的散点图,用不同的颜色和标记表示不同的类别
ax.plot_wireframe(Af_data[0], Af_data[1], Af_data[2][0], color='r') # 调用ax.plot_wireframe函数,绘制AnFany方法的第一类的分割面,用红色表示
ax.plot_wireframe(Af_data[0], Af_data[1], Af_data[2][1], color='b') # 调用ax.plot_wireframe函数,绘制AnFany方法的第二类的分割面,用蓝色表示
ax.plot_wireframe(Af_data[0], Af_data[1], Af_data[2][2], color='g') # 调用ax.plot_wireframe函数,绘制AnFany方法的第三类的分割面,用绿色表示
ax.set_zlabel(r'$X_{i} \dot W$') # 设置z轴的标签,用LaTeX表示
plt.title('AnFany训练得出的分割结果') # 设置图的标题
ax.set_yticks(np.linspace(-0.2, 1.4, 3)) # 设置y轴的刻度
ax.set_xticks(np.linspace(-0.2, 1.4, 3)) # 设置x轴的刻度
plt.legend(['1类', '2类', '3类', '1类分割面', '2类分割面', '3类分割面'], bbox_to_anchor=(1.2, 0.9)) # 设置图例,用bbox_to_anchor参数调整位置




ax = fig.add_subplot(2, 2, 2, projection='3d') # 选择第二个子图,设置为三维投影
fig_scatter(Train_X, Train_Y) # 调用fig_scatter函数,绘制训练数据的散点图,用不同的颜色和标记表示不同的类别
ax.plot_wireframe(Sk_data[0], Sk_data[1], Sk_data[2][0], color='r') # 调用ax.plot_wireframe函数,绘制Sklearn方法的第一类的分割面,用红色表示
ax.plot_wireframe(Sk_data[0], Sk_data[1], Sk_data[2][1], color='b') # 调用ax.plot_wireframe函数,绘制Sklearn方法的第二类的分割面,用蓝色表示
ax.plot_wireframe(Sk_data[0], Sk_data[1], Sk_data[2][2], color='g') # 调用ax.plot_wireframe函数,绘制Sklearn方法的第三类的分割面,用绿色表示
ax.set_zlabel(r'$X_{i} \dot W$') # 设置z轴的标签,用LaTeX表示
ax.set_yticks(np.linspace(-0.2, 1.4, 3)) # 设置y轴的刻度
ax.set_xticks(np.linspace(-0.2, 1.4, 3)) # 设置x轴的刻度
plt.title('Sklearn训练得出的分割结果') # 设置图的标题
plt.legend(['1类', '2类', '3类', '1类分割面', '2类分割面', '3类分割面'], bbox_to_anchor=(1.2, 0.9)) # 设置图例,用bbox_to_anchor参数调整位置






ax = fig.add_subplot(2, 2, 3, projection='3d') # 选择第三个子图,设置为三维投影
fig_scatter(Train_X, Train_Y) # 调用fig_scatter函数,绘制训练数据的散点图,用不同的颜色和标记表示不同的类别
ax.plot_wireframe(Tf_data[0], Tf_data[1], Tf_data[2][0], color='r') # 调用ax.plot_wireframe函数,绘制TensorFlow方法的第一类的分割面,用红色表示
ax.plot_wireframe(Tf_data[0], Tf_data[1], Tf_data[2][1], color='b') # 调用ax.plot_wireframe函数,绘制TensorFlow方法的第二类的分割面,用蓝色表示
ax.plot_wireframe(Tf_data[0], Tf_data[1], Tf_data[2][2], color='g') # 调用ax.plot_wireframe函数,绘制TensorFlow方法的第三类的分割面,用绿色表示
ax.set_zlabel(r'$X_{i} \dot W$') # 设置z轴的标签,用LaTeX表示
ax.set_yticks(np.linspace(-0.2, 1.4, 3)) # 设置y轴的刻度
ax.set_xticks(np.linspace(-0.2, 1.4, 3)) # 设置x轴的刻度
plt.title('TensorFlow训练得出的分割结果') # 设置图的标题
plt.legend(['1类', '2类', '3类', '1类分割面', '2类分割面', '3类分割面'], bbox_to_anchor=(1.2, 0.9)) # 设置图例,用bbox_to_anchor参数调整位置






ax = fig.add_subplot(2, 2, 4) # 选择第四个子图


plt.title('预测结果三种方法的混淆矩阵对比') # 设置图的标题


plt.text(0.2, 0.6, SM_A.confusion(SM_A.transign(Predict_Y), Pre)) # 在图中添加文本,显示AnFany方法的混淆矩阵
plt.text(0.2, 0.3, SM_S.confusion(SM_S.transign(Predict_Y), predata)) # 在图中添加文本,显示Sklearn方法的混淆矩阵
plt.text(0.2, 0.0, SM_T.confusion(SM_T.transign(Predict_Y), tf_result[1])) # 在图中添加文本,显示TensorFlow方法的混淆矩阵
plt.axis('off') # 关闭坐标轴


plt.show() # 显示图像

输出结果:

8e72f65d4a7af7918be67c58b74f26b4.png

1dbe7015cd80b4a992a9acfbd044f9ac.png

参考网址:

(1) What are the differences between softmax regression and logistic .... https://ai.stackexchange.com/questions/6368/what-are-the-differences-between-softmax-regression-and-logistic-regression-oth.
(2) Difference between logistic regression and softmax regression. https://stackoverflow.com/questions/36051506/difference-between-logistic-regression-and-softmax-regression.
(3) Do logistic regression and softmax regression do the same thing?. https://datascience.stackexchange.com/questions/14110/do-logistic-regression-and-softmax-regression-do-the-same-thing.
(4) Is multinomial logistic regression really the same as softmax .... https://stats.stackexchange.com/questions/466646/is-multinomial-logistic-regression-really-the-same-as-softmax-regression.
(5) undefined. http://ufldl.stanford.edu/tutorial/supervised/SoftmaxRegression/.

(6)https://zhuanlan.zhihu.com/p/98061179

(7)https://github.com/Anfany/Machine-Learning-for-Beginner-by-Python3


The End

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值