Softmax迴歸簡介

燈火繾綣發表於2020-12-28

softmax迴歸簡介

Softmax迴歸也稱多項或多類的Logistic迴歸,是Logistic迴歸在多分類問題上的推廣。

在這裡插入圖片描述
對於這樣的一個資料分佈,我們希望有一種方法能夠將這組資料分成四類。

很顯然,我們用四根直線就可以將這組資料劃分開。

softmax迴歸與Logistic迴歸的不同之處就是softmax迴歸要面對的是兩類以上的分類問題,一般的認為,要進行的分類問題有幾種型別,就需要繪製幾條直線,每條直線的作用是“將某一類與其他所有類區分開①”。

①所描述的正是一種一對多餘的思想。

如何設計一個多分類的線性判別函式?

  • 一對其餘
  • 一對一
  • argmax

一對其餘

把多分類問題轉換為C個“一對其餘”的二分類問題。這樣的方式需要C個判別函式,C指需要區分的類別的個數。其中第c個判別函式fc是將類別c的資料樣本和其他所有不屬於c的資料樣本分開。

一對一

把多分類問題轉換為C(C-1)/2個“一對一”的二分類問題。這種方式需要C(C-1)/2個判別函式,其中第(i,j)個判別函式是把類別i和類別j的資料樣本分開。

argmax

argmax是一種改進的“一對其餘”方式,同樣需要C個判別函式。

這C個判別函式的通式是:
f c ( x : w c ) = w c T x + b c , c ∈ { 1 , ⋯   , C } f_c(x:w_c) = w^T_cx+b_c , c∈\lbrace1,\cdots,C\rbrace fc(x:wc)=wcTx+bc,c{1,,C}
對於一個樣本x,如果存在一個類別c,相對於其他所有類別 c ~ \tilde{c} c~( c ~ \tilde{c} c~≠c)有 f c ( x ; w c ) f_c(x;w_c) fc(x;wc)>KaTeX parse error: Got function '\tilde' with no arguments as subscript at position 3: f_\̲t̲i̲l̲d̲e̲{c}(x;w\tilde{c… ,那麼x就是屬於類別c的。

對於函式 f c ( x ; w c ) f_c(x;w_c) fc(x;wc),我們可以理解為它表示樣本點 x i x_i xi關於這C條直線(C個判別函式)的距離判別。

我們先看一下三種判別方式能得到的區分效果。

在這裡插入圖片描述

可以看到,無論是“一對其餘”還是“一對一”方式,都會有盲區。而且“argmax”方式對前面兩種方式做了改進,使得這些盲區內的資料也可以被區分。

我們可以將argmax方式的判別函式看成是歐式距離的計算,顯然,即使是“盲區”內的資料點,這些資料點關於直線的歐式距離是一定存在的,所以它一定是可以被劃分的,所以在一定程度上解決了這個問題。

one-hot編碼

Logistic迴歸輸出的是一個範圍為[0,1]的概率值,是因為Logistic迴歸面對的二分類問題,一個範圍在[0,1]內的概率值可以明確的區分輸出的結果是0類還是1類。

然而在softmax迴歸中,多類問題使分類結果的表示變得困難起來。

所以會對輸出結果y進行one-hot編碼,進行編碼後使得這個結果y_hot可以清晰的表示結果所屬的類別。

那麼什麼是one-hot編碼呢?

我們直觀的拿一個例子:

假設四分類問題中分類為1,2,3,4四類,分類y=1經過one-hot編碼後,得到的結果是[1,0,0,0]。

是的,它是一個陣列,或者說是一個向量。

那麼同樣的,分類y=2經過one-hot編碼後得到的結果是[0,1,0,0],以此類推。

當然,在實際進行引數訓練後得到的結果不可能是這麼精確的值,我們知道分類問題得到的都是一些概率值。

所以我們得到的值可能是y1 = [0.1,0.2,0.5,0.2],y2 = [0.7,0.1,0.1,0.1]…

不過結果依然很顯然了,y1表示這個資料更趨向於3類,y2表示的資料更趨向於1類。

softmax用到的函式

  • softmax啟用函式
  • one-hot編碼
  • 交叉熵損失函式
  • 梯度下降法

與Logistic迴歸不同的是啟用函式,softmax迴歸用到的啟用函式是softmax。

在這裡插入圖片描述

softmax和sigmoid函式的目的都是將 w 1 ∗ x 1 + w 2 ∗ x 2 + b w_1*x_1+w_2*x_2+b w1x1+w2x2+b得到的值進行一個歸一化,將值壓縮到區間(0,1)內,這樣便於所有的值進行比較。

程式碼實現

  1. 引入所需庫

    import numpy as np
    import matplotlib.pyplot as plt
    
  2. 隨機生成資料集

    np.random.seed(0)
    Num=100
    #0類別
    x1 = np.random.normal(-3,1,size=(Num))
    x2 = np.random.normal(-3,1,size=(Num))
    y= np.zeros(Num)
    data0 = np.array([x_1,x_2,y])
    #1類別
    x1 = np.random.normal(3,1,size=(Num))
    x2 = np.random.normal(-3,1,size=(Num))
    y= np.ones(Num)
    data1 = np.array([x_1,x_2,y])
    #2類別
    x1 = np.random.normal(-3,1,size=(Num))
    x2 = np.random.normal(3,1,size=(Num))
    y= np.ones(Num)*2
    data2 = np.array([x_1,x_2,y])
    #3類別
    x1 = np.random.normal(3,1,size=(Num))
    x2 = np.random.normal(3,1,size=(Num))
    y= np.ones(Num)*3
    data3 = np.array([x_1,x_2,y])
    data0 = data0.T
    data1 = data1.T
    data2 = data2.T
    data3 = data3.T
    
  3. 檢視資料分佈

    plt.scatter(data0[:,0],data0[:,1],marker="o")
    plt.scatter(data1[:,0],data1[:,1],marker="+")
    plt.scatter(data2[:,0],data2[:,1],marker="v")
    plt.scatter(data3[:,0],data3[:,1],marker="s")
    

    在這裡插入圖片描述

  4. 隨機初始化權值與偏置

    W = np.random.rand(4,2)
    b = np.random.rand(4,1)
    
  5. 打亂資料集

    All_data=np.concatenate((data0,data1,data2,data3))
    np.random.shuffle(All_data)
    
  6. 檢視初始化後判別函式的情況

    x=np.arange(-5,5)
    y1=(-W[0,0]*x-b[0])/W[0,1]
    y2=(-W[1,0]*x-b[1])/W[1,1]
    y3=(-W[2,0]*x-b[2])/W[2,1]
    y4=(-W[3,0]*x-b[3])/W[3,1]
    plt.scatter(data0[:,0],data0[:,1],marker="o")
    plt.scatter(data1[:,0],data1[:,1],marker="+")
    plt.scatter(data2[:,0],data2[:,1],marker="v")
    plt.scatter(data0[:,0],data3[:,1],marker="s")
    plt.plot(x,y1)
    plt.plot(x,y2)
    plt.plot(x,y3)
    plt.plot(x,y4)
    

    在這裡插入圖片描述

  7. 定義函式

    #softmax(x)=e^x/sum(e^x)
    def softmax_matrix(z):#當z不是一維時
        exp = np.exp(z)
        sum_exp = np.sum(np.exp(z),axis=1,keepdims=True)
        return exp/sum_exp
    def softmax_vector(z):#當z是一維時
        return np.exp(z)/np.sum(np.exp(z))
    #對Y進行one-hot編碼
    #temp = {0,1,2,3}
    def one_hot(temp):
        one_hot = np.zeros((len(temp),len(np.unique(temp))))
        one_hot[np.arange(len(temp)),temp.astype(np.int).T]=1 
        return one_hot
    # 計算 y_hat
    def compute_y_hat(W,X,b):
        return np.dot(X,W.T)+b.T
    #計算交叉熵
    def cross_entropy(y,y_hat):
        loss = -(1/len(y))*np.sum(y*np.log(y_hat))
        return loss
    
  8. 進行訓練

    #w = w + lr*grad
    lr = 0.01
    loss_list=[]
    for i in range(1000):
        #計算loss
        y_hat = softmax_matrix(compute_y_hat(W,train_data_X,b))
        y = one_hot(train_data_Y)
        loss = cross_entropy(y,y_hat)
        loss_list.append(loss)
        #計算梯度
        grad_w = (1/len(train_data_X))*(np.dot(train_data_X.T,(y-y_hat)).T)
        grad_b = (1/len(train_data_X))*np.sum(y-y_hat)
        #更新引數
        W = W + lr*grad_w
        b = b + lr*grad_b
        # 輸出
        if i%100==1 :
            print("i:%d , loss:%f"%(i,loss))
    

    在這裡插入圖片描述

  9. 繪製訓練後的分類情況

    x=np.arange(-5,5)
    y1=(-W[0,0]*x-b[0])/W[0,1]
    y2=(-W[1,0]*x-b[1])/W[1,1]
    y3=(-W[2,0]*x-b[2])/W[2,1]
    y4=(-W[3,0]*x-b[3])/W[3,1]
    plt.scatter(data0[:,0],data0[:,1],marker="o")
    plt.scatter(data1[:,0],data1[:,1],marker="+")
    plt.scatter(data2[:,0],data2[:,1],marker="v")
    plt.scatter(data3[:,0],data3[:,1],marker="s")
    plt.plot(x,y1)
    plt.plot(x,y2)
    plt.plot(x,y3)
    plt.plot(x,y4)
    

    在這裡插入圖片描述

  10. 檢視loss值下降曲線

    plt.plot(loss_list)
    

    在這裡插入圖片描述

相關文章