首先是雙迴圈的版本,X是輸入的 test data,大小為 (num_test, D),輸出dists為一個大小為 (num_test, num_train) 的 numpy array,元素dists[i,j]代表第 i 個 test data point 與第 j 個 train data point 的歐幾里得距離。
1 2 3 4 5 6 7 8 9
defcompute_distances_two_loops(self, X): num_test = X.shape[0] num_train = self.X_train.shape[0] dists = np.zeros((num_test, num_train)) for i in range(num_test): for j in range(num_train): dists[i][j] = np.sqrt(np.sum((X[i] - self.X_train[j]) ** 2))
return dists
predict_labels
接著實作函式predict_labels,先找出前 k 個與測試資料最接近的點,再透過 majority vote 的方式選出最有可能的類別。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
defpredict_labels(self, dists, k=1): num_test = dists.shape[0] y_pred = np.zeros(num_test) for i in range(num_test): # A list of length k storing the labels of the k nearest neighbors to # the ith test point. closest_y = []
indices = np.argsort(dists[i])[:k] closest_y = self.y_train[indices]
y_count = {} for y in closest_y: y_count[y] = y_count.get(y, 0) + 1 max_value = max(y_count.values()) candidates = [y for y, v in y_count.items() if v == max_value] y_pred[i] = min(candidates)
return y_pred
下半部其實可以用一行程式碼解決:
1
y_pred[i] = np.bincount(closest_y).argmax()
利用剛剛得到的dists,可以計算出 test data 的預測結果,再將預測結果與正確答案比較就可以算出準確率。
k = 1 時的準確率:
1
Got 137 / 500 correct => accuracy: 0.274000
k = 5 時的準確率:
1
Got 139 / 500 correct => accuracy: 0.278000
compute_distances_one_loop
接著實作單迴圈版本的 kNN:
1 2 3 4 5 6 7 8
defcompute_distances_one_loop(self, X): num_test = X.shape[0] num_train = self.X_train.shape[0] dists = np.zeros((num_test, num_train)) for i in range(num_test): dists[i] = np.sqrt(np.sum((X[i] - self.X_train) ** 2, axis=1))
for k in k_choices: k_to_accuracies[k] = [] for n in range(num_folds): classifier.train(np.concatenate(X_train_folds[:n] + X_train_folds[n+1:]), np.concatenate(y_train_folds[:n] + y_train_folds[n+1:])) pred = classifier.predict(X_train_folds[n], k=k) num_correct = np.sum(pred == y_train_folds[n]) k_to_accuracies[k].append(float(num_correct) / len(pred))
# Print out the computed accuracies for k in sorted(k_to_accuracies): for accuracy in k_to_accuracies[k]: print('k = %d, accuracy = %f' % (k, accuracy))
執行此段程式碼會輸出每個 k 值在每個 fold 的表現。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
k = 1, accuracy = 0.263000 k = 1, accuracy = 0.257000 k = 1, accuracy = 0.264000 k = 1, accuracy = 0.278000 k = 1, accuracy = 0.266000 k = 3, accuracy = 0.239000 k = 3, accuracy = 0.249000 k = 3, accuracy = 0.240000 ... k = 100, accuracy = 0.256000 k = 100, accuracy = 0.270000 k = 100, accuracy = 0.263000 k = 100, accuracy = 0.256000 k = 100, accuracy = 0.263000
defsvm_loss_naive(W, X, y, reg): dW = np.zeros(W.shape) # initialize the gradient as zero
# compute the loss and the gradient 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 dW[:, j] += X[i] dW[:, y[i]] -= X[i]
# Right now the loss is a sum over all training examples, but we want it # to be an average instead so we divide by num_train. loss /= num_train dW /= num_train
# Add regularization to the loss. loss += reg * np.sum(W * W) dW += reg * W
defsvm_loss_vectorized(W, X, y, reg): """ Structured SVM loss function, vectorized implementation. Inputs and outputs are the same as svm_loss_naive. """ loss = 0.0 dW = np.zeros(W.shape) # initialize the gradient as zero
deftrain(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 isNone: # lazily initialize W 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
results = {} best_val = -1# The highest validation accuracy that we have seen so far. best_svm = None# The LinearSVM object that achieved the highest validation rate.
for lr in learning_rates: for reg in regularization_strengths: print("hyperparameter tuning: lr={}, reg={}".format(lr, reg)) svm = LinearSVM() tic = time.time() loss_hist = svm.train(X_train, y_train, learning_rate=lr, reg=reg, num_iters=1500, verbose=True)