EUAdvancer

机器学习-Softmax回归


Softmax回归本质上就是逻辑回归的推广,可用于多分类问题,它广泛用于多层神经网络作为输出层,前段时间我接触的CNN模型中输出层就是softmax,所以了解它的原理对以后的DL模型会很有帮助

Softmax回归原理

不同于二元分类问题,在多分类问题中,类标 y 可以取 k 个不同的值,所以我们需要得到所有分类的概率值,从而选择最大的概率作为我们的预测分类。这就是Softmax基本原理

假设函数

根据上述原理,我们的假设函数形式如下

也就是对于每种分类我们都有相应的theta参数,而矩阵左边的式子是对于某一样本在 K 种分类上的值相加,用于对数值进行归一化,使得 K 类上的概率总和为1

代价函数

初看起来还挺复杂,其实原理简单

分子就是我们在假设函数中说的K种分类概率,而l{=}函数则代表当y==j时该函数为1,否则为0,其实就是对于某一样本,代价函数只计算在其分类上的值,其他都会乘以0,这一点其实是和逻辑回归相同的

我们对比逻辑回归的代价函数可以看出其实它的形式和softmax非常相似,同时对于标签为1的则只计算右侧公式,标签为0的计算左侧公式,而softmax则是推广到了K类上,所以当K=2时,softmax和逻辑回归其实就是一样的

偏导数

无论是梯度下降还是L-BFGS都需要偏导数,公式如下

权重衰减

由于softmax回归的参数集存在“冗余”问题,即将theta参数减去任一向量都没有任何影响,所以我们在代价函数中加入一个权重衰减项,这样我们就得到一下的代价函数以及其偏导数

K个逻辑回归 VS Softmax回归

这两种方法都能用于多分类问题,我记得我以前实现的BP神经网络最后一层就是使用了K个逻辑回归的,那么我们该如何选择呢?一般来说如果对于一个样本只对应一个分类,那么使用softmax会更加适合,而对于一个样本可能出现多种分类(比如电影可能是浪漫的,但也可能是搞笑的),那么K个逻辑回归会更适合,因为它会判断有哪些分类是符合的。

练习

这次的练习是对MNIST手写数字数据库中手写数字的识别,这个数据集我的印象还是挺深刻的,前段时间的tensorflow的学习也是使用这个数据集的,该数据集包含6万张28*28的经过二值化的手写图片,那么我们完成这个练习的步骤只需要实现代价函数和偏导数的计算就可以了

代价函数和偏导数的实现

在实现代价函数过程中可能出现值溢出的问题,所以在Tip中我们得到以下提示,为了防止指数函数无法正确计算,我们将所有分类值减去分类值中最大的那个值,而且经过证明,这不会改变和影响结果

l(y = j) 函数的实现

在前面我们讲过,我们只计算属于当前样本的分类的值,那么在代码里怎么实现呢, 我们的思路是这样的,由于对于某一样本来说我们的计算会得到一个K x 1的向量,这时候我们同样创建一个K x 1的向量,它的值形如[0, 0, 1, 0, 0…],然后将这两个向量进行元素相乘从而排除了不属于的分类的值。

那么我们如何获得这样的矩阵呢?这就要用到sparse和full函数了

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
sparse(1:i, 1:j, k): 创建一个i * j的稀疏矩阵(ij的维度应该相同),其中(ij)上的值为1,其他位置的值为0

>> a = sparse([1;1;3;4;3;2;6;7;3;9;2;6;5;10;5;2], 1:16, 1)
a =

Compressed Column Sparse (rows = 10, cols = 16, nnz = 16 [10%])

(1, 1) -> 1
(1, 2) -> 1
(3, 3) -> 1
(4, 4) -> 1
(3, 5) -> 1
(2, 6) -> 1
(6, 7) -> 1
(7, 8) -> 1
(3, 9) -> 1
(9, 10) -> 1
(2, 11) -> 1
(6, 12) -> 1
(5, 13) -> 1
(10, 14) -> 1
(5, 15) -> 1
(2, 16) -> 1

full(a): 把稀疏矩阵转为全矩阵
>> full(a)
ans =

1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1
0 0 1 0 1 0 0 0 1 0 0 0 0 0 0 0
0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0
0 0 0 0 0 0 1 0 0 0 0 1 0 0 0 0
0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0

从上面的例子我们可以看到,我通过sparse和full函数创建了一个 10 * 16的矩阵,其中每一列都代表一个样本的标签,这样我们就得到了用于相乘的矩阵

代码

以下代码得到的结果为 Accuracy: 92.640% , 应该是正确的。

softmaxCost.m

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
function [cost, grad] = softmaxCost(theta, numClasses, inputSize, lambda, data, labels)

% numClasses - the number of classes
% inputSize - the size N of the input vector
% lambda - weight decay parameter
% data - the N x M input matrix, where each column data(:, i) corresponds to
% a single test set
% labels - an M x 1 matrix containing the labels corresponding for the input data
%

% Unroll the parameters from theta
theta = reshape(theta, numClasses, inputSize);

numCases = size(data, 2);

groundTruth = full(sparse(labels, 1:numCases, 1)); % debug模式下10 * 100

cost = 0;

thetagrad = zeros(numClasses, inputSize);

%% ---------- YOUR CODE HERE --------------------------------------
% Instructions: Compute the cost and gradient for softmax regression.
% You need to compute thetagrad and cost.
% The groundTruth matrix might come in handy.

% 减去每个特征中值最大的值保证有足够的内存执行指数函数
M = bsxfun(@minus,theta * data, max(theta * data, [], 1)); % debug模式下10 * 100
M = exp(M);
% 根据代价函数公式对每个样本除以其在10种分类上所有值总和从而得到h(x)
p = bsxfun(@rdivide, M, sum(M)); % 10 * 100
% 变成向量计算也是可以的
% cost = -1/numCases * groundTruth(:)' * log(p(:)) + lambda/2 * sum(theta(:) .^ 2);
% 直接对应的值相乘更加方便,因为我们只取groundTruth为1的部分的值
cost = -1 / numCases * sum(sum((log(p) .* groundTruth))) + lambda / 2 * sum(sum(theta .^ 2));
thetagrad = -1 / numCases * (groundTruth - p) * data' + lambda * theta;


% ------------------------------------------------------------------
% Unroll the gradient matrices into a vector for minFunc
grad = [thetagrad(:)];
end

softmaxPredict.m

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function [pred] = softmaxPredict(softmaxModel, data)

% softmaxModel - model trained using softmaxTrain
% data - the N x M input matrix, where each column data(:, i) corresponds to
% a single test set
%
% Your code should produce the prediction matrix
% pred, where pred(i) is argmax_c P(y(c) | x(i)).

% Unroll the parameters from theta
theta = softmaxModel.optTheta; % this provides a numClasses x inputSize matrix
pred = zeros(1, size(data, 2));

%% ---------- YOUR CODE HERE --------------------------------------
% Instructions: Compute pred using theta assuming that the labels start
% from 1.

% 取每个样本10种分类概率最大的(这不是h(x),因为大家都除以相同的分母,所以没必要除)
[nop, pred] = max(theta * data);

% ---------------------------------------------------------------------

end

结语

softmax回归虽然形式上和逻辑回归很像,但是实现过程中需要注意细节的地方更多一些,所以简短的几句代码都要慢慢推敲