SVM Loss以及梯度推导

本文深入解析SVMLoss的数学原理,包括其在CS231N课程中的应用,详细推导了损失函数及梯度计算公式,并提供了两种Python实现方式:原生和向量化,以提高计算效率。
GPT-oss:20b

GPT-oss:20b

图文对话
Gpt-oss

GPT OSS 是OpenAI 推出的重量级开放模型,面向强推理、智能体任务以及多样化开发场景

背景

在学习CS231N时,线性分类器用到了SVM Loss,所以打算这里推导一样,并解释一下CS231N对SVM Loss的native实现和向量化实现

推导

给出SVM Loss的公式Li=∑j≠yiclassnummax(0,sj−syi+δ)=∑j≠yiclassnummax(0,wj∗xiT−wyi∗xiT+δ)L_i = \sum_{j\neq y_i}^{classnum} max(0, s_j-s_{y_i}+\delta) = \sum_{j\neq y_i}^{classnum}max(0, w_j*x_i^T - w_{y_i}*x_i^T+\delta)Li=j=yiclassnummax(0,sjsyi+δ)=j=yiclassnummax(0,wjxiTwyixiT+δ),这个公式表达的意思就是svm算法是对每一个样本计算所有的不正确分类和正确分类之间的差距,如果差距大于delta就代表差距大,需要继续优化,所以对于每个样本把这些差距家取来,让差距和最小(损失函数),这就是svm的思想。对于Loss的计算直接按照上面的公式即可,对于梯度的话,由于这里是一个max门,如果差距小于delta,代表max门的输出为0,这个时候梯度也是为0的,所以主要是要看当wj∗xiT−wyi∗xiT+δ>0w_j*x_i^T-w_{y_i}*x_i^T+\delta>0wjxiTwyixiT+δ>0的时候,梯度是什么样子,这里只有wj和wyiw_j和w_{y_i}wjwyi两个参数,所以分别对这两个参数求导,可得梯度:
j≠yi,wj∗xiT−wyi∗xiT+δ>0j\neq y_i, w_j*x_i^T-w_{y_i}*x_i^T+\delta>0j=yi,wjxiTwyixiT+δ>0 => ∂Li∂wj=xiT\frac{\partial L_i}{\partial w_j}=x_i^TwjLi=xiT
j≠yi,wj∗xiT−wyi∗xiT+δ>0j\neq y_i, w_j*x_i^T-w_{y_i}*x_i^T+\delta>0j=yi,wjxiTwyixiT+δ>0 => ∂Li∂wyi=−xiT\frac{\partial L_i}{\partial w_{y_i}}=-x_i^TwyiLi=xiT
j≠yi,wj∗xiT−wyi∗xiT+δ<=0j\neq y_i, w_j*x_i^T-w_{y_i}*x_i^T+\delta<=0j=yi,wjxiTwyixiT+δ<=0 => ∂Li∂wyi=0\frac{\partial L_i}{\partial w_{y_i}}=0wyiLi=0
j≠yi,wj∗xiT−wyi∗xiT+δ<=0j\neq y_i, w_j*x_i^T-w_{y_i}*x_i^T+\delta<=0j=yi,wjxiTwyixiT+δ<=0 => ∂Li∂wj=0\frac{\partial L_i}{\partial w_j}=0wjLi=0

代码实现

第一种是按照上诉推导直接计算的:

def svm_loss_native(W, X, y, reg):
  '''
  svm_loss的朴素实现
  输入的维度是D,有C个分类类别,并且我们在有N个例子的batch上进行操作
    输入:
    - W: 一个numpy array,形状是(D, C),代表权重
    - X: 一个形状为(N, D)为numpy array,代表输入数据
    - y: 一个形状为(N,)的numpy array,代表类别标签
    - reg: (float)正则化参数
    f返回:
    - 一个浮点数代表Loss
    - 和W形状一样的梯度
  '''
  dW = np.zeros_like(W) #初始化权值矩阵
  num_classes = W.shape[1]
  num_train = X.shape[0]
  loss = 0.0
  for i in xrange(num_train):
    scores = X[i].dot(W)
    correct_class_score = scores[y[i]]
    for j in xrange(num_classes):
      if j == y[i]:
        continue
      margin = scores[j] - correct_class_score + 1 #note delta = 1
      if margin > 0:
        loss += margin
        dW[:, j] += X[i].T
        dW[:, y[i]] += -X[i].T
  loss /= num_train
  dW /= num_train
  loss += 0.5 * reg * np.sum(W * W)
  dW += reg * W
  return loss, dW

第二种是利用了python中numpy的向量化计算,提高了计算速度,这里需要解释一下,首先计算loss的时候先另外声明一个正确矩阵correct_class_scores,这个矩阵首先在每个样本对应的正确类别处对应的元素是1,然后把这个矩阵reshape成[N,1]的矩阵,然后做差的时候根据numpy的broadcast属性会自动延展成[样本数,类别数】的矩阵。在求梯度时,公式是xiTx_i^TxiT,加的次数为j,j就是没有正确分类的情况,这种情况有多少个呢?其实就是把每一行之前求取的loss有效的结果先置1,再加起来的个数,由于每次修正错误列的时候还会修正一次正确类别的列,所以直接把这个加和结果取相反数就是正确类别的导数,最后再乘以X的转置,加L2正则化梯度即可。具体看下面的代码:

def svm_loss_vectorized(W, X, y, reg):
  loss = 0.0
  dW = np.zeros_like(W)
  num_classes = W.shape[1]
  num_train = X.shape[0]
  scores = X.dot(W)
  correct_class_scores = scores[range(num_train), list(y)].reshape(-1, 1) #[N, 1]
  margins = np.maximum(0, scores - correct_class_scores + 1)
  margins[range(num_train), list(y)] = 0
  loss = np.sum(margins) / num_train + 0.5 * reg * np.sum(W * W)

  coffe_mat = np.zeros((num_train, num_classes))
  coffe_mat[margins > 0] = 1
  coffe_mat[range(num_train), list(y)] = 0
  coffe_mat[range(num_train), list(y)] = -np.sum(coffe_mat, axis=1)

  dW = (X.T).dot(coffe_mat)
  dW = dW/num_train + reg*W
  return loss, dW

您可能感兴趣的与本文相关的镜像

GPT-oss:20b

GPT-oss:20b

图文对话
Gpt-oss

GPT OSS 是OpenAI 推出的重量级开放模型,面向强推理、智能体任务以及多样化开发场景

评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值