Softmax Regression --【python代码实现】

上次的Logistic Regression 只能处理二元分类问题,而在其推广的Softmax回归能处理多元分类问题->称为多元Logistic Regression。

有 K 个 类别就有 K 个 wj列矩阵。

  • 因为对于每个实例x,都要计算ta成为K个特征的概率

    • Z j = g j ( x ) = w j T x Z_j = g_j(x)=w^T_jx Zj=gj(x)=wjTx

    • X = \begin{pmatrix}
          1 & x_1 & x_1^2 & \cdots & x_1^n \\
          1 & x_2 & x_2^2 & \cdots & x_2^n \\
          \vdots & \vdots & \vdots & \ddots & \vdots \\
          1 & x_m & x_m^2 & \cdots & x_m^n \\
      \end{pmatrix}
      \\
      

      W = ( w 1 T w 2 T ⋮ w K T ) z = g ( x ) = W x = ( z 1 z 2 ⋮ z K ) W = \begin{pmatrix} w^T_1 \\ w^T_2 \\ \vdots \\ w^T_K \end{pmatrix} \\ z =g(x)=Wx=\begin{pmatrix} z_1 \\ z_2 \\ \vdots \\ z_K \end{pmatrix} \\ W=w1Tw2TwKTz=g(x)=Wx=z1z2zK

softmax 函数预测概率

  • softmax函数输出为一个向量(每一个分量 j 是x预测为 j 类别的概率
    σ ( z ) = ( σ ( z 1 ) σ ( z 2 ) ⋮ σ ( z K ) ) \sigma(z)=\begin{pmatrix} \sigma(z_1) \\ \sigma(z_2) \\ \vdots \\ \sigma(z_K) \end{pmatrix} \\ σ(z)=σ(z1)σ(z2)σ(zK)

    σ ( z j ) = e z j ∑ k = 1 K e z k \sigma(z_j) = \frac{e^{z_j}}{\sum_{k=1}^K e^{z_k}} σ(zj)=k=1Kezkezj

    当k=2,分子分母同时除以ezj就是logistic函数

Softmax回归模型(分子是列向量,展开)(概率)

  • 回归模型假设函数

h w ( x ) = σ ( g ( x ) ) = 1 ∑ k = 1 K e w k T x ( e w 1 T x e w 2 T x ⋮ e w k T x ) h_w(x)=\sigma(g(x))=\frac{1}{\sum^{K}_{k=1}e^{w^T_kx}}\begin{pmatrix} e^{w^T_1x} \\ e^{w^T_2x} \\ \vdots \\ e^{w^T_kx} \end{pmatrix} hw(x)=σ(g(x))=k=1KewkTx1ew1Txew2TxewkTx

  • 如果通过训练确定了参数模型W,可构建多选分类函数如下
    H ( h w ( x ) ) = a r g m a x k   h w ( x ) k = a r g m a x ( w k T x ) − > K H(h_w(x))=argmax_k\ h_w(x)_k=argmax(w_k^Tx) ->K H(hw(x))=argmaxk hw(x)k=argmax(wkTx)>K

损失函数,交叉熵:

J ( W ) = − 1 m ∑ i = 1 m ∑ j = 1 K I ( y i = j ) ln ⁡ h w ( x i ) j J(W)=-\frac{1}{m}\sum_{i=1}^{m}\sum_{j=1}^{K}I(y_i=j)\ln h_w(x_i)_j J(W)=m1i=1mj=1KI(yi=j)lnhw(xi)j

注: I 是 指示函数 yj== j 的时候为1

梯度下降

  • If w.shape in Logistic Regression is (n+1, 1), here it must be a matrix (n+1, K).

    要更新W就要更新每一个wj,需要计算J(W)对每个wj的梯度

    公式:
    ∇ w j J ( W ) = 1 m ∑ i = 1 m ( h w ( x i ) j − I ( y i = j ) ) x i \nabla_{w_j}J(W)=\frac{1}{m}\sum_{i=1}^{m}{(h_w(x_i)_j-I(y_i=j))x_i} wjJ(W)=m1i=1m(hw(xi)jI(yi=j))xi

    更新参数W的公式

    W : = W − η ( ∇ w 1 J ( W ) T ∇ w 2 J ( W ) T ⋮ ∇ w K J ( W ) T ) W:=W-\eta\begin{pmatrix} \nabla_{w_1}J(W)^T \\ \nabla_{w_2}J(W)^T \\ \vdots \\ \nabla_{w_K}J(W)^T \end{pmatrix} W=Wηw1J(W)Tw2J(W)TwKJ(W)T


    感觉脑子不够用了…

代码实现(代码来自书籍)

import numpy as np

class SoftmaxRegression:
    def __init__(self, n_iter=200, eta=1e-3, tol=None):
        # 训练迭代次数
        self.n_iter = n_iter
        # 学习率
        self.eta = eta
        # 误差变化阈值
        self.tol = tol
        # 模型参数W(训练时初始化)
        self.W = None

    def _z(self, X, W):
        '''g(x)函数: 计算x与w的内积.'''
        if X.ndim == 1:
            return np.dot(W, X)   # 如果维度是一维的,就同书中公式那样进行乘积
        return np.matmul(X, W.T)  # 如果不是,则 (m, n+1) (n+1, K)  -> (m, K)

    def _softmax(self, Z):  # (m, K)
        '''softmax函数'''
        E = np.exp(Z)  # 上边公式中的分子
        if Z.ndim == 1:  # 维度为1,则直接类似于归一化的操作
            return E / np.sum(E)  # 归一化
        return E / np.sum(E, axis=1, keepdims=True)  # 保持维度的,同行相加 ->对应所有种类的值求sum

    def _predict_proba(self, X, W):
        '''h(x)函数: 预测y为各个类别的概率.'''
        Z = self._z(X, W)
        return self._softmax(Z)

    def _loss(self, y, y_proba):
        '''计算损失'''
        m = y.size  # (m, k)
        p = y_proba[range(m), y]  # 选去所有行的和???无法理解。可能是进行指示函数的步骤,但是难看。
        print(">>y:", y)
        print(">>p:", p)
        return -np.sum(np.log(p)) / m  #

    def _gradient(self, xi, yi, yi_proba):
        '''计算梯度'''
        K = yi_proba.size  #
        y_bin = np.zeros(K)
        y_bin[yi] = 1

        return (yi_proba - y_bin)[:, None] * xi  # 不行了, 这个操作好像了升维的。

    def _stochastic_gradient_descent(self, W, X, y):
        '''随机梯度下降算法'''

        # 若用户指定tol, 则启用早期停止法.
        if self.tol is not None:
            loss_old = np.inf
            end_count = 0

        # 使用随机梯度下降至多迭代n_iter次, 更新w.
        m = y.size
        idx = np.arange(m)
        for step_i in range(self.n_iter):
            # 计算损失
            y_proba = self._predict_proba(X, W)
            loss = self._loss(y, y_proba)
            print('%4i Loss: %s' % (step_i, loss))

            # 早期停止法
            if self.tol is not None:
                # 随机梯度下降的loss曲线不像批量梯度下降那么平滑(上下起伏),
                # 因此连续多次(而非一次)下降不足阈值, 才终止迭代.
                if loss_old - loss < self.tol:
                    print('haha')
                    end_count += 1
                    if end_count == 5:
                        break
                else:
                    end_count = 0

                loss_old = loss

            # 每一轮迭代之前, 随机打乱训练集.
            np.random.shuffle(idx)
            for i in idx:
                # 预测xi为各类别概率
                yi_proba = self._predict_proba(X[i], W)
                # 计算梯度
                grad = self._gradient(X[i], y[i], yi_proba)
                # 更新参数w
                W -= self.eta * grad


    def _preprocess_data_X(self, X):
        '''数据预处理'''

        # 扩展X, 添加x0列并置1.
        m, n = X.shape  # (m, n+1)
        X_ = np.empty((m, n + 1))
        X_[:, 0] = 1
        X_[:, 1:] = X

        return X_

    def train(self, X_train, y_train):
        '''训练'''

        # 预处理X_train(添加x0=1)
        X_train = self._preprocess_data_X(X_train)

        # 初始化参数向量W
        k = np.unique(y_train).size  # 获得种类数量
        _, n = X_train.shape  # (m, n+1)
        self.W = np.random.random((k, n)) * 0.05  # 初始化参数W (k种类, n+1特征)

        # 执行随机梯度下降训练W
        self._stochastic_gradient_descent(self.W, X_train, y_train)

    def predict(self, X):
        '''预测'''

        # 预处理X_test(添加x0=1)
        X = self._preprocess_data_X(X)

        # 对每个实例计算向量z.
        Z = self._z(X, self.W)

        # 向量z中最大分量的索引即为预测的类别.
        return np.argmax(Z, axis=1)

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JamePrin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值