线性模型——Softmax回归
前面讲到了逻辑回归,将逻辑回归理解了,softmax回归便好理解。softmax实际上是逻辑回归的拓展,它将逻辑回归的二分类推广到了多分类,用逻辑回归的方法解决多分类问题。
在学习softmax回归之前,需要先了解两个知识点,一个是softmax函数,另一个是One-Hat编码。softmax函数和逻辑回归中的sigmoid函数的作用一样,都是为了将线性回归的连续值转换为离散值,从而完成分类任务,不同之处在于,sigmoid将连续值转换为0和1,而对于一个
C
C
C分类问题来说,softmax函数则是转换为
{
1
,
2
,
3...
,
C
}
\{1,2,3...,C\}
{1,2,3...,C}。softmax函数表达式为:
p
(
y
=
c
∣
x
)
=
softmax
(
w
c
T
x
)
=
e
w
c
T
x
∑
c
′
=
1
C
e
w
c
T
x
p(y=c|x) = \text{softmax}(w_c^Tx) = \frac{e^{w_c^Tx}}{\sum_{c'=1}^Ce^{w_c^Tx}}
p(y=c∣x)=softmax(wcTx)=∑c′=1CewcTxewcTx
在了解了softmax函数之后,再来看一看One-Hot编码。One-Hot编码又称为一位有效编码,急用 N N N个状态为来进行编码。以分类中的应用为例,假设某一个数据集共有三种标签,分别为1,2,3。我们可以使用One-Hot编码来对他们表示,将三种状态按顺序组成一个列表{1,2,3}。One-Hot编码的编码规则为,码元由0,1两种组成,再同一个码组中,有且仅有一位为1,该1位表示对应元素被选中。例如{0,1,0}表示该样本的标签为2。
在softmax回归中,考虑一个数据集 D D D,其中样本数量为 N N N,每个样本的特征数量为 M M M,类别为 C C C。为了实现这样一个多分类问题:
1、首先需要准备数据,这里以矩阵为例:1)增广特征矩阵 X M + 1 , N = { x 1 , x 2 , . . . x N } X_{M+1,N} = \{\bf x_1,x_2, ... x_N\} XM+1,N={x1,x2,...xN},其中${\bf x_i} = [x_{i,1},…,x_{i,M},1]^\text T 表示第 表示第 表示第i 个样本的增广特征向量。 2 )标签 个样本的增广特征向量。2)标签 个样本的增广特征向量。2)标签Y_{N,1} = [y_1,y_2,…y_N]^\text T 。 3 ) O n e − H o t 矩阵 。3)One-Hot矩阵 。3)One−Hot矩阵O_{C,N} 。 4 )增广权重矩阵 。4)增广权重矩阵 。4)增广权重矩阵W_{M+1,C} = {\bf w_1,w_2,…w_C} ,其中 ,其中 ,其中{\bf w_i} = [w_{i,1},…w_{i,M}]^\text T 表示第 表示第 表示第i$个类别的增广权重向量。
2、然后,需要计算线性回归输出
Y
C
,
N
L
I
N
=
W
T
X
Y^{LIN}_ {C,N} = W^\text T X
YC,NLIN=WTX。然后,将线性回归的输出作为softmax函数的输入,得到预测值
Y
^
C
,
N
=
softmax
(
W
T
X
)
\hat Y_{C,N} = \text{softmax}(W^\text T X)
Y^C,N=softmax(WTX)
3、随后,写函数函数(矩阵点乘)
L
1
,
1
=
−
1
N
∑
1
N
O
∗
log
Y
^
C
,
N
\mathcal{L}_{1,1} = \frac{-1}{N}\sum_{1}^NO*\log\hat Y_{C,N}
L1,1=N−11∑NO∗logY^C,N
4、最后,根据如下公式更新参数
W
M
+
1
,
C
t
+
1
=
W
M
+
1
,
C
t
+
α
X
M
+
1
,
N
(
O
C
,
N
−
Y
^
C
,
N
)
T
W_{M+1,C}^{t+1} = W_{M+1,C}^{t} + \alpha X_{M+1,N}(O_{C,N} - \hat Y_{C,N})^\text T
WM+1,Ct+1=WM+1,Ct+αXM+1,N(OC,N−Y^C,N)T
实现代码
"""
@File : SoftmaxRegression.py
@Author : CheckOneA
@Contact : 932261247@qq.com
@License : (C)Copyright 2018-2021
@Version : V1.0
@Date : 2022/9/11
@Encoding : UTF-8
@Des : None
"""
import numpy as np
class SoftmaxRegression:
def __init__(self, data, interation_max, n_class, learning_rate, bath_size, gradient_descent_model):
self.data = data
self.n_data = data.shape[0]
self.n_feature = data.shape[1]
self.n_class = n_class
self.interation_max = interation_max
self.lr = learning_rate
self.bath_size = bath_size
self.gradient_descent_model = gradient_descent_model
self.data_train = self.get_data_train()
self.data_train_n_data = self.data_train.shape[0]
self.data_train_n_feature = self.data_train.shape[1] - 1
def train(self):
w = np.random.random((self.data_train_n_feature + 1, self.n_class))
loss_dis = []
for interation_index in range(self.interation_max):
data_train = self.get_data_train()
feature, label = self.get_feature_and_label(data_train)
one_hot = self.one_hot(label)
line_out = w.T @ feature
label_pro = self.soft_max_function(line_out)
loss = self.loss_function(one_hot, label_pro)
loss_dis.append(loss)
parameter_update = (feature @ (one_hot - label_pro).T)
w = w + self.lr * parameter_update
return loss_dis, w
def get_data_train(self):
data_train = None
if self.gradient_descent_model == 1:
data_train = self.data
elif self.gradient_descent_model == 2:
data_train = self.data[np.random.randint(0, self.n_data), :]
elif self.gradient_descent_model == 3:
data_index = np.random.randint(low=0, high=self.n_data, size=self.bath_size)
data_train = self.data[data_index, :]
return data_train
@staticmethod
def get_feature_and_label(data_train):
feature = data_train[:, 0:2]
n_data_train, n_feature_train = data_train.shape
x = np.insert(feature, n_feature_train - 1, np.ones(n_data_train), axis=1).T
label = data_train[:, 2].astype(int).reshape(-1, 1)
return x, label
@staticmethod
def soft_max_function(input_data):
sum_each_col = (np.sum(input_data, axis=1)).reshape(-1, 1)
return np.exp(input_data) / sum_each_col
def loss_function(self, one_hot, label_pr):
loss = -np.sum(one_hot * np.log10(label_pr)) / self.data_train_n_data / self.n_class
return loss
def one_hot(self, label):
one_hot = np.zeros((self.n_class, self.data_train_n_data))
temp = np.linspace(0, self.n_class - 1, self.n_class, dtype=int)
for class_index in range(self.n_class):
for data_index in range(self.data_train_n_data):
if label[data_index] == class_index:
one_hot[class_index][data_index] = 1
return one_hot
测试代码
import numpy as np
import pandas as pd
from matplotlib import pyplot as plt
from SoftmaxRegression import SoftmaxRegression
if __name__ == '__main__':
data_df = pd.read_csv("train_dataset.txt")
data_np = data_df.to_numpy()
# 获取特征标签 大小为 (M+1) N
# print(data_np)
n_data, n_feature = data_np.shape
softmax_regression = SoftmaxRegression(data=data_np,
interation_max=20000,
n_class=4,
learning_rate=0.0000000001,
bath_size=128,
gradient_descent_model=1)
loss, w = softmax_regression.train()
print(loss)
print(w)
plt.figure()
x = np.linspace(1, len(loss), len(loss))
plt.plot(x, loss)
plt.show()