Softmax回歸


Reference:

http://ufldl.stanford.edu/wiki/index.php/Softmax_regression

http://deeplearning.net/tutorial/logreg.html

 

起源:Logistic的二類分類

Softmax回歸是Logistic回歸的泛化版本,用於解決線性多類(K類)的分類問題。

Logistic回歸可以看作是Softmax回歸在K=2時的特例。Softmax函數即是K分類版的Logistc函數。

裸Softmax回歸的效果很差,因為沒有隱層結構,歸根還是是線性回歸。所以在深度學習里,Softmax則通常作為MLP的輸出層。

即,將BP網絡和Softmax結合起來,取BP網絡的隱層映射機制、Softmax的多分類機制,加以組合形成新的MLP架構。

這么做的原因就是,傳統BP網絡的輸出層是個多神經元的自行設計接口層,比如常見的log2(K)方法,轉多分類需要麻煩的編碼。

但實際上,隱層(可看作是input)到輸出層的映射原理等效於Softmax,既然Softmax擁有概率取分類的方法,何必再用低效的編碼方法?

 

Part I  如何從2類轉化為K類?

解決方案是引入K組(W、b)參數,即有K個分隔超平面,選擇$max P(Y=j|x^{i},\theta,b)$作為最終分類即可。

由於存在K組參數,原來的$h(\theta)=sigmoid(Inner)$將從單個值,變成一個大小為K的向量。

 

 

 

Part II  變化的目標函數

Logistic的目標函數: $J(\theta)=\sum_{i=1}^{m}(1-y^{(i)})log(1-h_{\theta}(x^{i})+y^{i}log(h_{\theta}(x^{(i)}))$

在Softmax里,由於$h_{\theta}(x^{(i)}$已經變成了向量,所以不能再使用。

實際上,在Logistic的推導里,$h_{\theta}(x^{(i)})$只是偶然而已,$P(y=0|x;\theta)=h(\theta)$。

即$P(y|x;\theta))$才是真正的概率分布函數,上述情況只是二項分布的特例。由於y的取值變成的K類,所以新的K項分布概率密度分布表示如下:

$P(y^{(i)}=j|x;\theta)=\frac{e^{W_{j}X^{i}}}{\sum_{l=1}^{k}e^{W_{l}X^{i}}}$

且定義$1\{y_{i}=j\}=(y_{i}==j)?1:0$

則  $J(\theta)=\sum_{i=1}^{m}\sum_{j=0}^{l}1\{y_{i}=j\}log\frac{e^{W_{j}X^{i}}}{\sum_{l=1}^{k}e^{W_{l}X^{i}}}$

仔細觀察,其實就是$h_{\theta}(x^{(i)})$這個向量根據$y^{(i)}$情況抽取的單個值而已,這就是Logistic函數的修改版本——Softmax函數

梯度變成:$\frac{\partial J(\theta_{j})}{\partial \theta_{j}}=\sum_{i=1}^{m}x^{(i)}(1\{y_{i}=j\}-P(y^{(i)}=j|x;\theta_{j})),j=1,2....k$

可以使用梯度上升算法了(下降算法也可,即取均值加上負號,變成負對數似然函數):

$\theta_{j}^{new}=\theta_{j}^{new}+\alpha\frac{\partial J(\theta_{j})}{\partial \theta_{j}},j=1,2....k$

 

Part III  C++代碼與實現

#include "cstdio"
#include "iostream"
#include "fstream"
#include "vector"
#include "sstream"
#include "string"
#include "math.h"
using namespace std;
#define N 500
#define delta 0.0001
#define alpha 0.1
#define cin fin
#define K 2
#define Dim dataSet[0].feature.size()
struct Data
{
    vector<double> feature;
    int y;
    Data(vector<double> feature,int y):feature(feature),y(y) {}
};
struct Parament
{
    vector<double> w;
    double b;
    Parament() {}
    Parament(vector<double> w,double b):w(w),b(b) {}
};
vector<Data> dataSet;
vector<Parament> parament;
void read()
{
    ifstream fin("fullTrain.txt");
    double fea;int cls;
    string line;
    while(getline(cin,line))
    {
        stringstream sin(line);
        vector<double> feature;
        while(sin>>fea) feature.push_back(fea);
        cls=feature.back();feature.pop_back();
        dataSet.push_back(Data(feature,cls));
    }
    for(int i=0;i<K;i++) parament.push_back(Parament(vector<double>(Dim,0.0),0.0));
}
double calcInner(Parament param,Data data)
{
    double ret=0.0;
    for(int i=0;i<data.feature.size();i++) ret+=(param.w[i]*data.feature[i]);
    return ret+param.b;
}
double calcProb(int j,Data data)
{
    double ret=0.0,spec=0.0;
    for(int l=1;l<=K;l++)
    {
        double tmp=exp(calcInner(parament[l-1],data));
        if(l==j) spec=tmp;
        ret+=tmp;
    }
    return spec/ret;
}
double calcLW()
{
    double ret=0.0;
    for(int i=0;i<dataSet.size();i++)
    {
        double prob=calcProb(dataSet[i].y,dataSet[i]);
        ret+=log(prob);
    }
    return ret;
}
void gradient(int iter)
{
    /*batch (logistic)
    for(int i=0;i<param.w.size();i++)
    {
        double ret=0.0;
        for(int j=0;j<dataSet.size();j++)
        {
            double ALPHA=(double)0.1/(iter+j+1)+0.1;
            ret+=ALPHA*(dataSet[j].y-sigmoid(param,dataSet[j]))*dataSet[j].feature[i];
        }
        param.w[i]+=ret;
    }
    for(int i=0;i<dataSet.size();i++) ret+=alpha*(dataSet[i].y-sigmoid(param,dataSet[i]));
    */
    //random
    for(int j=0;j<dataSet.size();j++)
    {
        double ret=0.0,prob=0.0;
        double ALPHA=(double)0.1/(iter+j+1)+0.1;
        for(int k=1;k<=K;k++)
        {
            prob=((dataSet[j].y==k?1:0)-calcProb(k,dataSet[j]));
            for(int i=0;i<Dim;i++) parament[k-1].w[i]+=ALPHA*prob*dataSet[j].feature[i];
            parament[k-1].b+=ALPHA*prob;
        }
    }
}
void classify()
{
    ifstream fin("fullTest.txt");
    double fea;int cls,no=0;
    string line;
    while(getline(cin,line))
    {
        stringstream sin(line);
        vector<double> feature;
        while(sin>>fea) feature.push_back(fea);
        cls=feature.back();feature.pop_back();
        int bestClass=-1;double bestP=-1;
        for(int i=1;i<=K;i++)
        {
            double p=calcProb(i,Data(feature,cls));
            if(p>bestP) {bestP=p;bestClass=i;}
        }
        cout<<"Test:"<<++no<<"  origin:"<<cls<<" classify:"<<bestClass<<endl;
    }
}
void mainProcess()
{
    double objLW=calcLW(),newLW;
    int iter=0;
    gradient(iter);
    newLW=calcLW();
    while(fabs(newLW-objLW)>delta)
    {
        objLW=newLW;
        gradient(iter);
        newLW=calcLW();
        iter++;
        //if(iter%5==0) cout<<"iter: "<<iter<<"  target value: "<<newLW<<endl;
    }
    cout<<endl<<endl;
}
int main()
{
    read();
    mainProcess();
    classify();
}
Softmax

Part IV  測試

使用Iris鳶尾花數據集:http://archive.ics.uci.edu/ml/datasets/Iris,是三類分類問題

該數據集的第三組數據是非線性的,若K=3訓練,則因為非線性數據擾亂,錯誤率很大。

若K=2,則代碼等效於Logistic回歸,錯誤率相近。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM