机器学习之十二:softmax回归
发布日期:2021-05-07 13:29:30 浏览次数:14 分类:原创文章

本文共 8370 字,大约阅读时间需要 27 分钟。

1、Softmax模型
    Softmax回归和逻辑回归的回归模型很类似,为如下条件概率:

P(Y=k|xi;wk)=exp(wkxi)Kk=1exp(wkxi) P ( Y = k | x i ; w k ) = exp ⁡ ( w k ⋅ x i ) ∑ k = 1 K exp ⁡ ( w k ⋅ x i )

其中 xRn+1 x ∈ R n + 1 wkRn+1 w k ∈ R n + 1 k=1,2,3...,K k = 1 , 2 , 3... , K ,表示有K个类别。
    对于给定的测试输入 xi x i ,我们想用条件概率针对每一个类别 k k 估算出概率值 P ( Y = k | x i ) 。也就是说,我们想估计 xi x i 的每一种分类结果出现的概率。因此,我们的条件概率函数将要输出一个 K K 维的向量(向量元素的和为1)来表示这 K K 个估计的概率值。 具体地说,我们的条件概率函数 P w ( x i ) 如下:
Pw(xi)=P(Y=1|xi;w1)P(Y=2|xi;w2)...P(Y=K|xi;wK)=1Kk=1exp(wkxi)exp(w1xi)exp(w2xi)...exp(wKxi) P w ( x i ) = [ P ( Y = 1 | x i ; w 1 ) P ( Y = 2 | x i ; w 2 ) . . . P ( Y = K | x i ; w K ) ] = 1 ∑ k = 1 K exp ⁡ ( w k ⋅ x i ) [ exp ⁡ ( w 1 ⋅ x i ) exp ⁡ ( w 2 ⋅ x i ) . . . exp ⁡ ( w K ⋅ x i ) ]

其中 w1,w2,...,wKRn+1 w 1 , w 2 , . . . , w K ∈ R n + 1
    为了方便起见,我们同样使用符号 w w 来表示全部的模型参数。在实现Softmax回归时,将 w 用一个 k(n+1) k ∗ ( n + 1 ) 的矩阵来表示会很方便,该矩阵是将 w1,w2,...,wK w 1 , w 2 , . . . , w K 按行罗列起来得到的,如下所示:
w=w1w2...wK w = [ w 1 w 2 . . . w K ]

2、模型参数估计
在下面的公式中, 1{ } 1 { } 是示性函数,其取值规则为: 1{ }=0,1{ }=1 1 { 值 为 假 的 表 达 式 } = 0 , 1 { 值 为 真 的 表 达 式 } = 1 。举例来说,表达式 1{ 2+2=4} 1 { 2 + 2 = 4 } 的值为1 , 1{ 1+1=5} 1 { 1 + 1 = 5 } 的值为 0。我们对数似然函数为:

L(w)=i=1Nk=1K1{ yi=k}logexp(wkxi)Kl=1exp(wlxi) L ( w ) = ∑ i = 1 N ∑ k = 1 K 1 { y i = k } log ⁡ exp ⁡ ( w k ⋅ x i ) ∑ l = 1 K exp ⁡ ( w l ⋅ x i )

值得注意的是,上述公式是logistic回归代价函数的推广。logistic回归代价函数可以改为:
L(w)=i=1N[yilogπ(xi)+(1yi)log(1π(xi))]=i=1Nk=1K1{ yi=k}logP(yi=k|xi;wk) L ( w ) = ∑ i = 1 N [ y i log ⁡ π ( x i ) + ( 1 − y i ) log ⁡ ( 1 − π ( x i ) ) ] = ∑ i = 1 N ∑ k = 1 K 1 { y i = k } log ⁡ P ( y i = k | x i ; w k )

    对于 L(w) L ( w ) 的最小化问题,目前还没有闭式解法。因此,我们使用迭代的优化算法(例如梯度下降法)。经过求导,我们得到梯度公式如下:
L(w)wk=i=1N[xi(1{ yi=k}P(yi=k|xi;wk))] ∂ L ( w ) ∂ w k = ∑ i = 1 N [ x i ( 1 { y i = k } − P ( y i = k | x i ; w k ) ) ]

其中 L(w)wk ∂ L ( w ) ∂ w k 本身是一个向量,它的第 l l 个元素 L ( w ) w k l L(w) L ( w ) wk w k 的第 l l 个分量的偏导数。
    有了上面的偏导数公式以后,我们就可以将它代入到梯度下降法等算法中,来最小化 L ( w ) 。 例如,在梯度下降法的标准实现中,每一次迭代需要进行如下更新:
wk=wkαL(w)wk(k=1,,K w k = w k − α ∂ L ( w ) ∂ w k ( k = 1 , … , K )

3、Softmax回归 vs 二元回归
    当类别数 k=2 k = 2 时,softmax 回归退化为 logistic 回归。这表明 softmax 回归是 logistic 回归的一般形式。如果你在开发一个音乐分类的应用,需要对k种类型的音乐进行识别,那么是选择使用 softmax 分类器呢,还是使用 logistic 回归算法建立 k 个独立的二元分类器呢?
    这一选择取决于你的类别之间是否互斥,例如,如果你有四个类别的音乐,分别为:古典音乐、乡村音乐、摇滚乐和爵士乐,那么你可以假设每个训练样本只会被打上一个标签(即:一首歌只能属于这四种音乐类型的其中一种),此时你应该使用类别数 k = 4 的softmax回归。(如果在你的数据集中,有的歌曲不属于以上四类的其中任何一类,那么你可以添加一个“其他类”,并将类别数 k 设为5。)
    如果你的四个类别如下:人声音乐、舞曲、影视原声、流行歌曲,那么这些类别之间并不是互斥的。例如:一首歌曲可以来源于影视原声,同时也包含人声 。这种情况下,使用4个二分类的 logistic 回归分类器更为合适。这样,对于每个新的音乐作品 ,我们的算法可以分别判断它是否属于各个类别。
    现在我们来看一个计算视觉领域的例子,你的任务是将图像分到三个不同类别中。(i) 假设这三个类别分别是:室内场景、户外城区场景、户外荒野场景。你会使用sofmax回归还是 3个logistic 回归分类器呢? (ii) 现在假设这三个类别分别是室内场景、黑白图片、包含人物的图片,你又会选择 softmax 回归还是多个 logistic 回归分类器呢?
    在第一个例子中,三个类别是互斥的,因此更适于选择softmax回归分类器 。而在第二个例子中,建立三个独立的 logistic回归分类器更加合适。

4、Softmax回归的实现:
    这里用的数据集仍然为上一篇逻辑回归中的数据集:

#coding=utf-8from numpy import *import matplotlib.pyplot as pltclass SoftmaxRegression:    def __init__(self):        self.dataMat = []   # 数据集        self.labelMat = []  # 类标签        self.weights = []   # 权重        self.M = 0          # 样本点个数        self.N = 0          # 样本特征个数+1        self.K = 0          # 保存累别的总数        self.alpha = 0.001  # 学习率    #加载数据集    def loadDataSet(self):        for line in open('softmax.txt','r'):            items = line.strip().split('    ')            self.dataMat.append([1.0, float(items[0]), float(items[1])])            self.labelMat.append(int(items[2]))        self.K = len(set(self.labelMat))                self.dataMat = mat(self.dataMat)        self.labelMat = mat(self.labelMat).transpose()        self.M,self.N = shape(self.dataMat)        self.weights = mat(ones((self.N,self.K)))  # 权重初始化为全1的 N*K的矩阵,每一列为一个权重向量        # self.weights = [[-1.19792777,6.05913226,-4.44164147,3.58043698],        #                 [ 1.78758743,0.47379819,0.63335518,1.1052592 ],        #                 [ 1.48741185,-0.18748907,1.79339685,0.90668037]]    def likelihoodfunc(self):        likelihood = 0.0        for i in range(self.M):                    # 对所有样本点迭代            t = exp(self.dataMat[i]*self.weights)            likelihood += log(t[0,self.labelMat[i,0]]/sum(t))        print likelihood    # 梯度下降法,每次更新权重时选择所有样本点    def gradientAscent(self):        for l in range(10):                        # 进行迭代10次            error = exp(self.dataMat*self.weights) # 得到一个矩阵,每个元素代表一个样本点和一个权值的内积            rowsum = -error.sum(axis=1)            # 矩阵每一行的所有分量相加,得到一列。每个元素对应条件概率公式中的分母            rowsum = rowsum.repeat(self.K, axis=1) # 对rowsum进行 列方向上的扩展,结果为 N行K列的矩阵          #求出每个样本点属于每个类别的条件概率,即条件概率模型的公式,矩阵第几行代表第几个样本点,第几列代表第几类            error = error/rowsum                              for m in range(self.M):        # 对所有样本点进行迭代                error[m,self.labelMat[m,0]] += 1   # 如果某个样本点属于某个类,对应元素+1,这对应公式中1{ }示性函数            self.weights = self.weights + self.alpha * self.dataMat.transpose()* error  # 跟新权重向量            self.likelihoodfunc()        print self.weights   # 每一列为一个权重向量    # 随机梯度上升法version0,每次按顺序选一个样本点更新权重    def stochasticGradientAscent_V0(self):        for l in range(500):               # 进行迭代500次            for i in range(self.M):        # 对每个样本进行遍历                error = exp(self.dataMat[i]*self.weights)                rowsum = -error.sum(axis=1)                rowsum = rowsum.repeat(self.K, axis=1)                error = error/rowsum                error[0,self.labelMat[i,0]] += 1                self.weights = self.weights + self.alpha * self.dataMat[i].transpose()* error                  # self.likelihoodfunc()        print self.weights    # 随机梯度下降法version1,每次随机选一个样本点更新权重    def stochasticGradientAscent_V1(self):        for l in range(500):            idxs = range(self.M)            for i in range(self.M):                alpha = 4.0/(1.0+l+i)+0.01                rdmidx = int(random.uniform(0,len(idxs)))                error = exp(self.dataMat[rdmidx]*self.weights)                rowsum = -error.sum(axis=1)                rowsum = rowsum.repeat(self.K, axis=1)                error = error/rowsum                error[0,self.labelMat[rdmidx,0]] += 1                self.weights = self.weights + alpha * self.dataMat[rdmidx].transpose()* error                del(idxs[rdmidx])                # self.likelihoodfunc()        print self.weights    def classify(self,X):        p = X * self.weights        return p.argmax(1)[0,0]    def test(self):        xcord0 = []        ycord0 = []        xcord1 = []        ycord1 = []        xcord2 = []        ycord2 = []        xcord3 = []        ycord3 = []        for i in range(50):            for i in arange(80):                x = random.uniform(-3.0, 3.0)                y = random.uniform(0.0, 15.0)                c = self.classify(mat([[1.0,x,y]]))                if c==0:                    xcord0.append(x)                    ycord0.append(y)                if c==1:                    xcord1.append(x)                    ycord1.append(y)                if c==2:                    xcord2.append(x)                    ycord2.append(y)                if c==3:                    xcord3.append(x)                    ycord3.append(y)        # for i in range(self.M):            # if self.labelMat[i]==0:                # xcord0.append(self.dataMat[i,1]); ycord0.append(self.dataMat[i,2])            # elif self.labelMat[i]==1:                # xcord1.append(self.dataMat[i,1]); ycord1.append(self.dataMat[i,2])            # elif self.labelMat[i]==2:                # xcord2.append(self.dataMat[i,1]); ycord2.append(self.dataMat[i,2])            # else:                # xcord3.append(self.dataMat[i,1]); ycord3.append(self.dataMat[i,2])        fig = plt.figure()        ax = fig.add_subplot(111)        ax.scatter(xcord0, ycord0, s=20, c='yellow', marker='s')        ax.scatter(xcord1, ycord1, s=20, c='blue')        ax.scatter(xcord2, ycord2, s=20, c='red')        ax.scatter(xcord3, ycord3, s=20, c='black')        plt.title('inference')        # plt.title('train data')        plt.xlabel('X1')        plt.ylabel('X2');        plt.show()if __name__=='__main__':        myclassification = SoftmaxRegression()    myclassification.loadDataSet()       # myclassification.gradientAscent()    myclassification.stochasticGradientAscent_V0()       # myclassification.stochasticGradientAscent_V1()    myclassification.test()
上一篇:机器学习之十三:SVM(支持向量机)
下一篇:机器学习之十一:逻辑回归

发表评论

最新留言

关注你微信了!
[***.104.42.241]2025年04月09日 05时35分25秒