Softmax分类器基本实现

#-*- coding: utf-8 -*-
import numpy as np

def softmax_loss_naive(W, X, y, reg):
    """
    使用显式循环版本计算Softmax损失函数
    N表示:数据个数,D表示:数据维度,C:表示数据类别个数。
    Inputs:
    - W: 形状(D, C) numpy数组,表示分类器权重(参数).
    - X: 形状(N, D) numpy数组,表示训练数据.
    - y: 形状(N,) numpy数组,表示数据类标。
        其中 y[i] = c 意味着X[i]为第c类数据,c取值为[0,c)
    - reg: 正则化惩罚系数
    Returns  二元组(tuple):
    - loss,数据损失值
    - dW,权重W所对应的梯度,其形状和W相同
    """
    # 初始化损失值与梯度.
    loss = 0.0  
    dW = np.zeros_like(W)
    #############################################################################        
    #  任务:使用显式循环实现softmax损失值loss及相应的梯度dW 。                 #
    #  温馨提示: 如果不慎,将很容易造成数值上溢。别忘了正则化哟。               #
    #############################################################################
    # =============================================================================
    # 第一种
    # =============================================================================
    # num_train = X.shape[0]  # 获取训练样本数
    # num_class = W.shape[1]  # 获取分类总数
    # for i in range(num_train):  
    #     s = X[i].dot(W)  # (1, C) 每类的可能性
    #     scores = s - max(s)  # 关注最高分
    #     #使用指数函数,在不影响单调性的情况下,使相对得分更明显
    #     scores_E = np.exp(scores)  
    #     # 计算总得分
    #     Z = np.sum(scores_E)
    #     # 找到目标值
    #     score_target = scores_E[y[i]]
    #     # 计算出损失值
    #     loss += -np.log(score_target/Z)
    #     # 计算梯度值
    #     for j in range(num_class):
    #         if j == y[i]:
    #             dW[:, j] += -(1-scores_E[j]/Z)*X[i]
    #         else:
    #             dW[:, j] += X[i]*scores_E[j]/Z
    # # 使用平均损失,再引入正则化
    # loss = loss/num_train+0.5*reg*np.sum(W*W)
    # # 使用平均梯度,再引入正则化
    # dW = dW/num_train+reg*W

    # =============================================================================
    # 第二种
    # =============================================================================
    N = X.shape[0]  # 获取训练样本数
    C = W.shape[1]  # 获取分类总数

    result = X.dot(W)
    result -= np.max(result,axis=1,keepdims=True)#避免指数太大,导致计算太大,内存不够

    for i in range(N):
        # 计算函数值
        soft_max = np.exp(result[i][y[i]])/np.sum(np.exp(result[i]))
        # 计算出损失值
        loss += -np.log(soft_max)

        for j in range(C):
            
            if j==y[i]:
                dW[:,j] += -X[i].T
            dW[:,j] += (X[i].T * np.exp(result[i][j])) / (np.sum(np.exp(result[i])))

    loss/= N
    loss += reg*np.sum(W*W)
    dW /= N
    dW += reg*2*W
    #############################################################################
    #                           结束编码                                        #
    #############################################################################
    return loss, dW


def softmax_loss_vectorized(W, X, y, reg):
    """
    Softmax损失函数,使用矢量计算版本.
    输入,输出格式与softmax_loss_naive相同
    """
    # 初始化损失值与梯度
    loss = 0.0
    dW = np.zeros_like(W)
    #############################################################################
    # 任务: 不使用显式循环计算softmax的损失值loss及其梯度dW.                    #
    # 温馨提示: 如果不慎,将很容易造成数值上溢。别忘了正则化哟。                #
    #############################################################################
    # 训练样本数
    num_train = X.shape[0]
    # 计算函数值
    s = np.dot(X, W)
    # 关注最高分
    scores = s-np.max(s, axis=1, keepdims=True)
    # 使用指数函数来扩大相对比
    scores_E = np.exp(scores)
    # 求和得到总值
    Z = np.sum(scores_E, axis=1, keepdims=True)
    # 计算每一类的可能性
    prob = scores_E/Z
    # 真实标签标记
    y_trueClass = np.zeros_like(prob)
    y_trueClass[range(num_train), y] = 1.0
    # 计算损失值
    loss += -np.sum(y_trueClass*np.log(prob))/num_train+0.5*reg*np.sum(W*W)
    # 计算梯度
    dW += -np.dot(X.T, y_trueClass-prob)/num_train+reg*W
    #############################################################################
    #                          结束编码                                         #
    #############################################################################
    return loss, dW

softmax.py

#-*- coding: utf-8 -*-

import numpy as np
from random import shuffle
from softmax_loss import *

class Softmax(object):
    
    def __init__(self):
        self.W = None
        
    def train(self, X, y, learning_rate=1e-3, reg=1e-5, num_iters=100,
            batch_size=200, verbose=False):
        '''
        使用最小批量梯度下降算法训练Softmax分类器

        Parameters
        ----------
        X : TYPE
            数据
        y : TYPE
            数据类标
        learning_rate : TYPE, optional
            学习率. The default is 1e-3.
        reg : TYPE, optional
            权重衰减因子. The default is 1e-5.
        num_iters : TYPE, optional
            迭代次数. The default is 100.
        batch_size : TYPE, optional
            批大小. The default is 200.
        verbose : TYPE, optional
            是否显示中间过程. The default is False.

        Returns
        -------
        loss_history : TYPE
            DESCRIPTION.

        '''
        # 获得训练数据及维度
        num_train, dim = X.shape
        # 我们的计数是从0开始,因此10分类任务其y的最大值为9
        num_classes = np.max(y) + 1
        # 如果没有权重
        if self.W is None:
            # 随机初始化 W
            self.W = 0.001 * np.random.randn(dim, num_classes)
        # 储存每一轮的损失结果 W
        loss_history = []
        for it in range(num_iters):
            X_batch = None
            y_batch = None
            #########################################################################
            #                             任务:                                     #
            #     从训练数据 X 中采样大小为batch_size的数据及其类标,               #
            #     并将采样数据及其类标分别存储在X_batch,y_batch中                  #
            #        X_batch的形状为  (dim,batch_size)                              #
            #        y_batch的形状为  (batch_size)                                  #
            #     提示: 可以使用np.random.choice函数生成indices.                    #
            #            重复采样要比非重复采样快许多                               #
            #########################################################################
            # False表示不可以取相同数字
            indices = np.random.choice(num_train, batch_size, False)
            X_batch = X[indices, :]
            y_batch = y[indices]
            #########################################################################
            #                       结束编码                                        #
            #########################################################################
            # 计算损失及梯度
            loss, grad = self.loss(X_batch, y_batch, reg)
            loss_history.append(loss)
            # 更新参数
            #########################################################################
            #                       任务:                                           #
            #               使用梯度及学习率更新权重                                #
            #########################################################################
            self.W = self.W - learning_rate*grad 
            #########################################################################
            #                      结束编码                                         #
            #########################################################################
            if verbose and it % 500 == 0:
                print('迭代次数 %d / %d: loss %f' % (it, num_iters, loss))
        return loss_history
    
    
    def predict(self, X):
        """
        使用已训练好的权重预测数据类标
        Inputs:
        - X:数据形状 (N,D) .表示N条数据,每条数据有D维
        Returns:
        - y_pred:形状为(N,) 数据X的预测类标,y_pred是一个长度维N的一维数组,
        每一个元素是预测的类标整数
        """
        y_pred = np.zeros(X.shape[0])
        ###########################################################################
        #                         任务:                                           #
        #              执行预测类标任务,将结果储存在y_pred                       #
        ###########################################################################
        # 找到可能性最高的类别作为预测分类
        y_pred = np.argmax(X.dot(self.W), axis=1)	
        ###########################################################################
        #                           结束编码                                      #
        ###########################################################################
        return y_pred
    
    
    def loss(self, X_batch, y_batch, reg):
        """
        Compute the loss function and its derivative. 
        Subclasses will override this.

        Inputs:
        - X_batch: A numpy array of shape (N, D) containing a minibatch of N
          data points; each point has dimension D.
        - y_batch: A numpy array of shape (N,) containing labels for the minibatch.
        - reg: (float) regularization strength.

        Returns: A tuple containing:
        - loss as a single float
        - gradient with respect to self.W; an array of the same shape as W
        """
        return softmax_loss_vectorized(self.W, X_batch, y_batch, reg)
    

你可能感兴趣的:(深度学习,python,机器学习,深度学习,1024程序员节)