CS231N assignment1

本文详细解析了CS231N课程中的KNN分类器实现,包括二重循环、预测函数、一重循环及无循环四种方法,并深入探讨了SVM损失函数的naive与vectorized实现,以及SGD训练过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CS231N的第一次作业,因为是第一次用PY来写作业,对NP的很多函数还不是很熟悉,所以比较吃力。

第一部分是写knn classifier的四个函数。第一个是二重循环,第二个是预测函数,第三个是一重循环,第四个是零循环。KNN基本原理本来比较简单,就是算出测试点和样本点的距离,然后选出距离最近的k个点,然后这k个点里面哪个点出现次数最多。但是实际上用PY的时候,会用到np的函数,包括向量化的一些技巧,所以对我这种初学者还是比较吃力的。

1.二重循环

        distaces=np.sqrt(np.sum(np.square(self.X_train[j]-X[i])))
        dists[i][j]=distaces

比较好解释,就是先让相减,平方,求和,开方

2.预测函数

    for i in range(num_test):
      closest_y = []
      distances=dists[i,:]
      indexes = np.argsort(distances)
      closest_y=self.y_train[indexes[:k]]
     count = np.bincount(closest_y)
      y_pred[i] = np.argmax(count)

这里用到了argsort和argmax函数
np.argsort(a,axis,kind=“quicksort”,order=“None”) 返回与a同样大小的一个array,其中返回array的值表示原数组的下标,array的下标表示其对应值在原数组的从小到大第几个。
np.argmax(a,axis,out) 返回这个数组里面最大值的下标

3.一重循环

      distances = np.sqrt(np.sum(np.square(self.X_train - X[i]),axis = 1))
      dists[i, :] = distances

这个和二重循环差不多一个思想,只不过这里用到了python的广播(broadcast)原则。大概就是,python在按位运算中,会自动复制其中一些矩阵长度为1的维度,使等式能够正常进行运算

4.无循环

    M=np.dot(X,self.X_train.T)
    xi=np.sum(np.square(X),axis=1).reshape([500,1])
    # print(xi.shape)
    yj=np.sum(np.square(self.X_train),axis=1).reshape([5000,1])
    dists=np.sqrt(xi+np.array(yj).T-2*M)

这个无循环的分类器和之前的都不一样。这里用了这样一个式子
公式
所以,可以直接用上述代码,两次broadcast和一次矩阵乘法,计算出dists矩阵。

第二部分是SVM
1.首先是svm_loss_naive函数,定义如下

def svm_loss_naive(W, X, y, reg):
	  dW = np.zeros(W.shape) # initialize the gradient as zero

  num_classes = W.shape[1]
  num_train = X.shape[0]
  loss = 0.0
  for i in range(num_train):
    scores = X[i].dot(W)
    correct_class_score = scores[y[i]]
    for j in range(num_classes):
      if j == y[i]:
        continue
      margin = scores[j] - correct_class_score + 1 # note delta = 1
      if margin > 0:
        loss += margin
      #矩阵乘法的求导还是要加强
      #margin>0,说明有误差,而从L的定义式可以看出,在margin>0的情况下,L就是一个简单和式,dL/dWj=X[0:49000]
      #j系数为正
        dW[:,j]+=X[i]
      #yi的系数为负
        dW[:,y[i]]+=(-1*X[i])

  loss /= num_train
  dW /= num_train
  loss += reg * np.sum(W * W)
  dW += 2*reg * W
  return loss, dW

返回一个这个W对应的总loss值,以及dW(L对W求导结果,W下降方向)。值得注意的是,一开始我在dW处写的是dW+=2regnp.sum(W),明显错误,这也反映了对矩阵乘法求导的不敏感,还需要多思考与实践。

2.编写svm_loss_vectorized函数
这个函数就是将loss矩阵直接与构造的“正确矩阵”相减,并进行相关操作。最后可以看到,事件缩短了成原来的近百分之一,这也反映了python对向量运算速度很快(也算是python这个语言的特性叭)
但是编写这个程序的时候出现了困难。。。我实在不知道这个方法怎么更新值,所以说看了一下网上的source code。

def svm_loss_vectorized(W, X, y, reg):

  loss = 0.0
  dW = np.zeros(W.shape) # initialize the gradient as zero
  num_train=X.shape[0]
  num_classes = W.shape[1]

  scores=X.dot(W)
  correct_scores=scores[np.arange(num_train),y]
  correct_scores=np.reshape(np.repeat(correct_scores,num_classes),(num_train,num_classes))
  #correct_scores的每一行都是一样的,是正确的分数

  margin=scores+1.0-correct_scores
  #对应相减的地方会是1,则置零

  margin[np.arange(num_train),y]=0
  #margin现在是loss的初级,要得到loss只需要将正的项加起来

  loss=(np.sum(margin[margin>0]))/num_train
  loss+=reg*np.sum(W*W)
  #下面求导实在看不懂了。。
  margin[margin>0]=1
  margin[margin<=0]=0
  row_sum = np.sum(margin, axis=1)                  # 1 by N
  margin[np.arange(num_train), y] = -1*row_sum
  dW += np.dot(X.T, margin)     # D by C#
  #这里我看不懂。。。。应该是矩阵乘法的求导


  #除以N再正则化
  dW/=num_train
  dW+=2*reg*W
    #一开始这里写的np.sum(W)报错了,不应该这么写

  return loss, dW

3.训练函数 svm.train()
这里是用的SGD,就是将随机抽取原训练集的一部分做梯度下降。

  def train(self, X, y, learning_rate=1e-3, reg=1e-5, num_iters=100,
            batch_size=200, verbose=False):
  
    num_train, dim = X.shape
    num_classes = np.max(y) + 1 # assume y takes values 0...K-1 where K is number of classes
    if self.W is None:
      self.W = 0.001 * np.random.randn(dim, num_classes)

    # Run stochastic gradient descent to optimize W
    loss_history = []
    for it in range(num_iters):
      X_batch = None
      y_batch = None


      randi = np.random.choice(len(X),batch_size,replace=True)
      X_batch = X[randi]
      y_batch = y[randi]



      # evaluate loss and gradient
      loss, grad = self.loss(X_batch, y_batch, reg)
      loss_history.append(loss)

      # perform parameter update

      self.W += (-grad * learning_rate)


      if verbose and it % 100 == 0:
        print('iteration %d / %d: loss %f' % (it, num_iters, loss))

    return loss_history
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值