【CS231n】Spring 2020 Assignments - Assignment1 - Softmax

Mister_Yu發表於2020-11-19


前言

我的作業是在 Google Colaboratory 上完成的,還是挺方便的。


一、Implement softmax_loss_naive(W, X, y, reg)

邏輯迴歸分類器(Softmax Classifier):希望正確類別得到的概率分數接近 1

Softmax 損失函式(常用,也叫多項式邏輯迴歸):我們用 softmax 對分數進行轉化處理(即不直接使用),並得到正確類別的損失函式是 − l o g P -logP logP ;公式如下

L i = −   l o g ( e s y i ∑ j e s j ) L_i = -\ log(\frac{e^{s_{y_i}}}{\sum_{j}{e^{s_j}}}) Li= log(jesjesyi)

綜上,損失的計算還是比較簡單。先求出分數 Scores,將 Scores 正數化( e S e^S eS),再將 Scores 歸一化,即除以每個資料樣本在每個類的得分總和,這樣就可以得出概率 P 了。那對於每個資料樣本 X i X_i Xi L i = −   l o g ( e s y i ∑ j e s j ) L_i = -\ log(\frac{e^{s_{y_i}}}{\sum_{j}{e^{s_j}}}) Li= log(jesjesyi)

可是計算梯度 dW 就比較複雜了,因為這個損失函式比較複雜,可以參考下面這篇文章:

多分類器softmax——絕對簡單易懂的梯度推導 by 月下花弄影

裡面講得很詳細。

下面是程式碼:

    num_train = X.shape[0]
    num_classes = W.shape[1]
    scores = X.dot(W)
    positive_scores = np.e ** scores
    softmax = positive_scores / np.sum(positive_scores, axis=1).reshape((num_train, 1))

    for i in range(num_train):
      for j in range(num_classes):
        if j == y[i]:
          loss += -np.log(softmax[i][j])
          dW[:, j] += (softmax[i][j] - 1) * X[i]
        else:
          dW[:, j] += softmax[i][j] * X[i]

    loss /= num_train
    loss += reg * np.sum(W*W)

    dW /= num_train
    dW += 2 * reg * W

二、Implement softmax_loss_vectorized(W, X, y, reg)

這題和 SVM 的第二題差不多。損失是比較好求的,比較難的仍然是梯度矩陣。

總體思路是構造出一個矩陣,讓它和 X 點乘能夠得到 dW 。從上一題中我們可以看到 dW[:, j] 都需要加上 X[i] ,只不過當 j = y[i] 時的係數需要減 1 ,那麼很自然的我們先將 softmax 相應的項減去1,再與 X.T 點乘即可得到 dW。當然最後別忘了除以 num_train 以及 進行正則化懲罰。

    num_train = X.shape[0]
    num_classes = W.shape[1]
    dim = X.shape[1]
    scores = X.dot(W)
    positive_scores = np.e ** scores
    softmax = positive_scores / np.sum(positive_scores, axis=1).reshape((num_train, 1))

    loss = np.sum((-np.log(softmax))[range(num_train), y])
    softmax[range(num_train), y] -= 1
    dW = X.T.dot(softmax)

    loss /= num_train
    loss += reg * np.sum(W*W)

    dW /= num_train
    dW += 2 * reg * W

三、Write code that chooses the best hyperparameters

這一題的思路跟 SVM 裡面的完全一樣,想看思路的同學可以看我的上一篇部落格。

需要注意的是,我選用的引數都是經過對比、選擇最後得出來的,並不是一開始就將 learning_rate_step_size, regularization_strength_step_size, num_iters 這些引數設成這個值的。

其實研究了一下其他同學的程式碼之後,我嘗試了一下,發現不怕耗費時間的同學真的可以將 num_iters 固定成一個比較大的值,這樣訓練出來的結果會準確很多,對於區間的選擇也更精準。

learning_rate_step_size = (1.288000e-06 - 0.5e-7) / 15
regularization_strength_step_size = (4.200000e+04 - 1e4) / 15

lr = 1e-7 - learning_rate_step_size
regularization_strength = 1e4 - regularization_strength_step_size

best_lr = 0.
best_regularization_strength = 0.

for i in range(15):
  lr += learning_rate_step_size
  regularization_strength = 1e4
  for j in range(15):
    regularization_strength += regularization_strength_step_size
    new_softmax = Softmax()
    new_softmax.train(X_train, y_train, learning_rate=lr, reg=regularization_strength, num_iters=700, verbose=False)
    y_train_pred = new_softmax.predict(X_train)
    training_accuracy = np.mean(y_train == y_train_pred)
    y_val_pred = new_softmax.predict(X_val)
    validation_accuracy = np.mean(y_val == y_val_pred)
    if validation_accuracy > best_val:
      best_val = validation_accuracy
      best_softmax = new_softmax
      best_lr = lr
      best_regularization_strength = regularization_strength
    results[(lr, regularization_strength)] = (training_accuracy, validation_accuracy)

# Train best_softmax and update bast_value
best_softmax.train(X_train, y_train, best_lr, best_regularization_strength, num_iters=800, verbose=True)
y_val_pred = best_softmax.predict(X_val)
best_val = np.mean(y_val == y_val_pred)
print('best_lr:', best_lr, 'best_regularization_strength:', best_regularization_strength)

總結

學習CS231n對數學的要求真的蠻高的。

相關文章