0%

机器学习|softmax模型详解与实现

softmax是非常简单的多分类模型,常见于神经网络中的输出层。那为什么这么简单的模型还要花时间写篇文章来讲呢?原因在于:LR模型大家都很熟悉了,而softmax模型大家都不是很关注,因为可能觉得softmax是LR的推广,所以就没有详细去推导过softmax模型以及它的反向传播。所以,这篇博客,将着重讲解一下关于softmax模型导数的推导与反向传播的推导,并采用python进行实现。

这篇博客侧重于softmax函数导数的推导,以及softmax的loss function的导数推导。

softmax函数介绍

softmax函数常见于神经网络的输出层,用来做归一化分布。它的形式如下:

其中,$T$表示输出的总类别数目。下面,我们记那个推导一下关于softmax函数的导数形式:

softmax的loss function的求导

在softmax中,损失函数通常使用交叉熵。其对于参数$w,b$的求导如下:

讲解完毕🎉

softmax的实现

把模型实现一遍才算是真正的吃透了这个模型呀。在这里,我采取了python与scikit-learn库来实现softmax模型。我的github里面可以下载到所有的代码,欢迎访问我的github,也欢迎大家star和fork。附上GitHub地址: 《统计学习方法》及常规机器学习模型实现。具体代码如下:

python实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# coding:utf-8
# Author:codewithzichao
# Date:2020-1-2
# E-mail:lizichao@pku.edu.cn

'''
数据集:Mnist
准确率:0.1532.
时间:16.96704387664795.
--------------
tips:实现的非常简单,直接将输入数据输入到一个线性层,然后输出10个0-1之间的数,中间没有用任何的隐藏层,
由于是线性变换,所以效果比较差吧,如果加几个隐藏层,效果会好得多!
'''

import numpy as np
import time


def loadData(fileName):
'''
加载数据
:param fileName:数据路径名
:return: 特征向量矩阵、还有标签矩阵
'''
data_list = [];
label_list = []

with open(fileName, "r") as f:
for line in f.readlines():
curline = line.strip().split(",")
label_list.append(int(curline[0]))
data_list.append([int(feature) / 255 for feature in curline[1:]])

data_matrix = np.array(data_list)
label_matrix = np.array(label_list).reshape(1, -1)
return data_matrix, label_matrix


class Softmax:
def __init__(self, train_data, train_label, iter, learning_rate):
'''
构造函数
:param train_data: 训练数据
:param train_label: 训练数据的标签类别
:param iter: epoch的数目
:param learning_rate: 学习率
'''
self.train_data = train_data
self.train_label = train_label
self.iter = iter
self.learning_rate = learning_rate
self.feature_num = self.train_data.shape[1]
self.input_num = self.train_data.shape[0]
self.w, self.b = self.initialize_params(self.feature_num)

def softmax(self, X):
'''
softmax函数
:param X: 输入数据
:return: 返回含有10个元素的np.array
'''
return np.exp(X) / np.sum(np.exp(X))

def initialize_params(self, feature_dim):
'''
初始化参数w,b
:param feature_dim:实例特征数目
:return: 参数w,b
'''
w = np.random.uniform(0, 1, (feature_dim, 10))
b = 0

return w, b

def train(self):
'''
训练
:return:返回参数w,b
'''
for iter in range(self.iter):
for i in range(self.input_num):
x = train_data[i].reshape(-1, 1)
y = np.zeros((10, 1))
y[train_label[0][i]] = 1
y_ = self.softmax(np.dot(self.w.T, x) + self.b)
self.w -= self.learning_rate * (np.dot((y_ - y), x.T))
self.b -= self.learning_rate * (y_ - y)
return self.w, self.b

def predict(self, digit):
'''
预测单个样本的值
:param digit: 严格样本的特征向量
:return: 返回预测的样本类别
'''

# 返回softmax中概率最大的值
return np.argmax(np.dot(self.w.T, digit) + self.b)

def test(self, test_data, test_label):
'''
测试
:param test_data: 测试集的特征向量
:param test_label: 测试集的标签类别
:return: 准确率
'''
error = 0
for i in range(test_data.shape[0]):
if (self.predict(test_data[i]) != test_label[0][i]):
error += 1
print(f"the prediction is {self.predict(test_data[i])},the true is {test_label[0][i]}.")

accuracy = (test_data.shape[0] - error) / test_data.shape[0]

return accuracy


if __name__ == "__main__":
start = time.time()

print("start load data.")
train_data, train_label = loadData("../MnistData/mnist_train.csv")
test_data, test_label = loadData("../MnistData/mnist_test.csv")
print("finished load data.")

a = Softmax(train_data, train_label, 50, 0.005)
accuracy = a.test(test_data, test_label)

end = time.time()
print(f"the accuracy is {accuracy}.")
print(f"the total time is {end - start}.")

Would you like to buy me a cup of coffee☕️~