三种方法实现 softmax 回归模型
自定义、sklearn、tensorflow
一、原理
softmax回归
逻辑回归和softmax回归的区别
二、示例代码1-Softmax_Sklearn
sklearn的LogisticRegression模块对鸢尾花数据集进行Softmax 回归多分类
Iris_Data脚本:对数据标准化、标签独热编码、打乱样本排序
makefile
# 导入 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
python
#-*- 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))
输出结果:
输出结果是用 Softmax 回归对鸢尾花数据集进行多分类的结果,其中:
-
系数为:是一个 5×3 的二维数组,它表示 Softmax 回归模型的参数矩阵,每一列表示一个类别的权重向量和截距项,每一行表示一个特征的系数。例如,第一列表示第一类(山鸢尾)的权重向量和截距项,第一行表示第一个特征(萼片长度)的系数。
-
混淆矩阵:是一个 3×3 的二维表格,它表示模型的预测结果和真实标签之间的对应关系,每一行表示真实的类别,每一列表示预测的类别,每个单元格表示真实为某一类,预测为某一类的样本数量。例如,第一行第一列表示真实为第一类(山鸢尾),预测为第一类(山鸢尾)的样本数量,为 49。
三、示例代码2-Softmax-TensorFlow
使用 TensorFlow 2.x 实现 softmax 回归模型,对鸢尾花数据集进行分类。代码的主要步骤如下:
-
导入所需的模块,如
tensorflow
,numpy
,prettytable
等。 -
定义一个
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
模块绘制损失函数的图像,展示模型的收敛过程。
python
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()
输出结果:
四、示例代码3-Softmax 自定义实现
使用逻辑回归模型对鸢尾花数据集进行分类,评估模型的效果。代码使用了梯度下降法和正则化技术来优化模型的参数,使用了混淆矩阵和成本函数来评估模型的性能。代码使用了 numpy
库来进行矩阵运算,使用了 prettytable
库来生成表格,使用了 matplotlib
库来绘制图像。
ruby
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()
输出结果:
代码解读:
五、以上三种softmax回归对比(自定义、sklearn、tensorflow2.x)
使用三种不同的方法实现 softmax 回归模型,对一个人工生成的数据集进行多分类,并绘制不同方法的分割结果和混淆矩阵。
python
# 引入三种方法
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() # 显示图像
输出结果:
参考网址:
(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