UFLDL深度學習筆記 (二)Softmax 回歸
本文為學習“UFLDL Softmax回歸”的筆記與代碼實現,文中略過了對代價函數求偏導的過程,本篇筆記主要補充求偏導步驟的詳細推導。
1. 詳細推導softmax代價函數的梯度
經典的logistics回歸是二分類問題,輸入向量$ x^{(i)}\in\Re^{n+1}$ 輸出0,1判斷$y^{(i)}\in{{0,1}}$,Softmax回歸模型是一種多分類算法模型,如圖所示,輸出包含k個類型,\(y^{(i)}\in{\{0,1,…,k\}}\)。

在經典的多分類問題MNIST數字識別任務中包含0-9十個手寫數字。softmax的思路是將輸入值直接判決為k個類別的概率,這里就需要一個判決函數,softmax采用指數形式。求和的倒數是為了歸一化概率。
\(h_\theta(x^{(i)})=\begin{bmatrix}p(y^{(i)}=1|x^{(i)};\theta)\\ p(y^{(i)}=2|x^{(i)};\theta)\\\vdots\\ p(y^{(i)}=k|x^{(i)};\theta)\\\end{bmatrix}=\frac{1}{\sum_{j=1}^k e^{\theta_j^T \cdot x^{(i)}}}\begin{bmatrix} e^{\theta_1^T \cdot x^{(i)}} \\ e^{\theta_2^T \cdot x^{(i)}}\\\vdots\\ e^{\theta_k^T \cdot x^{(i)}}\\\end{bmatrix}\)
為了矩陣運算方便,將權重參數記作矩陣形式 \(\theta = \begin{bmatrix} \theta_1^T \\ \theta_2^T\\\vdots\\ \theta_k^T\\\end{bmatrix}_{k\times(n+1)}\)
包含權重懲罰項的softmax的代價函數為
$$J(\theta)=-\frac 1 m \left [\sum_m\sum_k 1{y{(i)}=j}\cdot log(p(y{(i)}=j|x^{(i)};\theta)) \right] +\frac \lambda 2 \sum_^k\sum_^n\theta_^2 $$
原文Softmax回歸中略過了求偏導的過程,下文對其做分步推導。$\theta_j$是行向量,表示每個輸入x與第j個輸出分類連接的權重, 將對數內除法拆分為減法可得:
\(J(\theta)=-\frac 1 m \left [\sum_{i=1}^m\sum_{j=1}^k 1\{y^{(i)}=j\}\cdot ({\theta_j^T x^{(i)}}-log(\sum_{l=1}^ke^{\theta_l^T \cdot x^{(i)}})) \right] +\frac \lambda 2 \sum_{i=1}^k\sum_{j=0}^n\theta_{ij}^2\)
對$\theta_j$求偏導,可得:
\(\begin{align} \frac {\nabla J(\theta)} {\nabla \theta_j} &= -\frac 1 m\sum_{i=1}^m \left [ \frac {\nabla\sum_{j=1}^k 1\{y^{(i)}=j\}\theta_j^T x^{(i)}} {\nabla \theta_j} - \frac {\nabla \sum_{j=1}^k 1\{y^{(i)}=j\}log(\sum_{l=1}^ke^{\theta_l^T \cdot x^{(i)}}))} {\nabla \theta_j} \right] +\lambda\theta_j \\ &= -\frac 1 m\sum_{i=1}^m \left [ 1\{y^{(i)}=j\} x^{(i)} - \frac {\nabla\sum_{j=1}^k 1\{y^{(i)}=j\}\sum_{l=1}^ke^{\theta_l^T \cdot x^{(i)}}} {\sum_{l=1}^ke^{\theta_l^T \cdot x^{(i)}}\nabla \theta_j} \right] +\lambda\theta_j \\ &= -\frac 1 m\sum_{i=1}^m \left [ 1\{y^{(i)}=j\} x^{(i)} - \frac {x^{(i)}e^{\theta_j^T \cdot x^{(i)}}} {\sum_{l=1}^ke^{\theta_l^T \cdot x^{(i)}}} \right] +\lambda\theta_j \\ &= -\frac 1 m\sum_{i=1}^m x^{(i)}\left [ 1\{y^{(i)}=j\} - p(y^{(i)}=j|x^{(i)};\theta) \right] +\lambda\theta_j \end{align}\)
這樣我們得到了代價函數對參數權重的梯度,類似前篇稀疏自編碼的做法,需要做以下步驟:
- 結合梯度下降法,使用訓練數據求出參數權重$\theta$的最優解;
- 用訓練過的權重對測試數據做前向傳播,每個測試數據得到$k$個軟判決輸出值,分別表示判決為$1…k$分類的概率;
- 選取$k$個中的最大值即為對測試數據的分類結果;
- 與測試數據集的真實輸出對比統計獲得預測准確率。
2. 偏導的矩陣化表示
當真正編寫代碼時會發現上述梯度公式是對行向量$\theta$的,UFLDL沒有給出矩陣公式,矩陣表達又該是怎樣呢?請看下文推導。
基本符號表達式這樣的:
輸入數據:\(X_{(n+1) \times m}\)
概率矩陣:\(norm(exp(\theta_{k\times (n+1)} \times X_{(n+1) \times m}) )= P_{k\times m}\)
1函數表示第i個輸入的輸出值是否為分類j,遍歷所有輸入、輸出得到矩陣 $ G_{k \times m}$,稱為groundTruth.
偏導第j行的向量為輸入數據每一行(共n+1行)與$G_{k \times m} P_{k \times m}\(的每一行的點積,加上\)\lambda\theta_j$ 本身:
\(\begin{align} \frac {\nabla J(\theta)} {\nabla \theta_j} &=-\frac 1 m X_{(n+1) \times m} \bullet(g_{m\times1}-p_{m\times1}) +\lambda\theta_j \end{align}\)
再進一步寫成矩陣形式:
\(\begin{align} \frac {\nabla J(\theta)} {\nabla \theta} &=-\frac 1 m (G_{k \times m}-P_{k\times m}) *X_{(n+1) \times m}^T +\lambda\theta \end{align}\)
好了,矩陣化完成,可以痛快地寫代碼了!
3. matlab代碼實現
這里只給出實現過程中遇到問題的代碼片段,完整代碼見https://github.com/codgeek/deeplearning,編寫過前一節稀疏自編碼 的小伙伴應該對整體結構比較熟悉了,softmaxCost.m實現給定參數權重時的代價值與梯度的矩陣計算,softmaxExercise.m結合梯度下降調用代價、梯度計算,完整實現上述四個步驟。
對1函數的計算有一些語法技巧,示例代碼給出的full/sparse有些抽象,我用最基本的的==
返回矩陣邏輯結果這個特性來計算,
首先把校驗標簽復制k
份獲得$k\times m$的矩陣:labels = repmat(labels, numClasses, 1);
然后制造出每一行等於行號的矩陣:k = repmat((1:numClasses)',1,numCases);
所以1函數對應的矩陣$ G_{k \times m}$為groundTruth = double((k == labels));
上一節已經給出了完整的矩陣化公式,也是理論轉換為代碼實現的難點所在,softmaxCost.m詳細代碼如下,
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));
%
labels = repmat(labels, numClasses, 1);
k = repmat((1:numClasses)',1,numCases);% numClasses×numCases.
groundTruth = double((k == labels));% % groundTruth algrithum is the same as (k===label)
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.
cost = 0;
z = theta*data;
z = z - max(max(z)); % avoid overflow while keep p unchanged.
z = exp(z); % matrix product: numClasses×numCases
p = z./repmat(sum(z,1),numClasses,1); % normalize the probbility aganist numClasses. numClasses×numCases
cost = -mean(sum(groundTruth.*log(p), 1)) + sum(sum(theta.*theta)).*(lambda/2);
thetagrad = -(groundTruth - p)*(data')./numCases + theta.*lambda; % numClasses×inputSize
% Unroll the gradient matrices into a vector for minFunc
grad = thetagrad(:);
end
另外一部分需要稍動腦筋的是預測判斷。怎樣寫的簡捷高效呢?請看下文.
function [pred] = softmaxPredict(softmaxModel, data)
theta = softmaxModel.optTheta; % this provides a numClasses x inputSize matrix
pred = zeros(1, size(data, 2));
inputSize = softmaxModel.inputSize;
numClasses= softmaxModel.numClasses;
%% ---------- YOUR CODE HERE --------------------------------------
z=exp(theta*data);
[~, pred] = max(z);
end
關鍵在於使用matlab的max
函數第二個返回值,它就是每列最大值的行號。
4. 圖示與結果
數據集來自Yann Lecun的筆跡數據庫,我們先瞜一眼原始MMIST數據集的筆跡。
設定與練習說明相同的參數,運行完整代碼https://github.com/codgeek/deeplearning 可以看到預測准確率達到92.6%。達到了練習的標准結果。
小結一下,看到梯度、矩陣化推導過程不難發現,一般都是先從對矩陣單個元素的偏導開始,給出表達式,然后把每個元素列舉成行成列,根據行、列計算的關系,往矩陣乘法的“乘加”模式上套用,最終給出非常精簡的矩陣化公式,矩陣只是一個規范化工具,難以直接在矩陣的抽象層次上推導,也很容易把一些在矩陣上不成立的直覺公式用上去而出錯,所以現階段還是一個從抽象到具體再到抽象的過程。