通過K近鄰算法探究numpy向量運算提速
茴香豆的“茴”字有... ...
使用三種計算圖片距離的方式實現K近鄰算法:
1.最為基礎的雙循環
2.利用numpy的broadca機制實現單循環
3.利用broadcast和矩陣的數學性質實現無循環
圖片被拉伸為一維數組
X_train:(train_num, 一維數組)
X:(test_num, 一維數組)
方法驗證
import numpy as np a = np.array([[1,1,1],[2,2,2],[3,3,3]]) b = np.array([[4,4,4],[5,5,5],[6,6,6],[7,7,7]])
雙循環:
dists = np.zeros((3,4))
for i in range(3):
for j in range(4):
dists[i][j] = np.sqrt(np.sum(np.square(a[i] - b[j])))
print(dists)
[[ 5.19615242 6.92820323 8.66025404 10.39230485]
[ 3.46410162 5.19615242 6.92820323 8.66025404]
[ 1.73205081 3.46410162 5.19615242 6.92820323]]
單循環:
dists=np.zeros((3,4))
for i in range(3):
dists[i] = np.sqrt(np.sum(np.square(a[i] - b),axis=1))
print(dists)
[[ 5.19615242 6.92820323 8.66025404 10.39230485]
[ 3.46410162 5.19615242 6.92820323 8.66025404]
[ 1.73205081 3.46410162 5.19615242 6.92820323]]
無循環:
r1=(np.sum(np.square(a),axis=1)*(np.ones((b.shape[0],1)))).T r2=np.sum(np.square(b),axis=1)*(np.ones((a.shape[0],1))) r3=-2*np.dot(a,b.T) print(np.sqrt(r1+r2+r3))
[[ 5.19615242 6.92820323 8.66025404 10.39230485]
[ 3.46410162 5.19615242 6.92820323 8.66025404]
[ 1.73205081 3.46410162 5.19615242 6.92820323]]
無循環算法原理:
(注意,原理圖-驗證代碼-實現程序 的變量並不嚴格一一對應,均有調整)

全代碼實現如下:
import numpy as np
class KNearsNeighbor():
def _init_(self):
pass
def train(self, x, y):
self.X_train = x
self.y_train = y
# 選擇使用幾個循環體的方式來計算距離
def predict(self, X, k=1, num_loops=0):
if num_loops == 0:
dist = self.compute_distances_no_loops(X)
elif num_loops == 1:
dist = self.compute_distances_one_loops(X)
elif num_loops == 2:
dist = self.compute_distances_two_loops(X)
else:
raise ValueError('Invalid value %d' % num_loops)
return dist
def compute_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(np.square(X[i] - self.X_train[j])))
return dists
def compute_distances_one_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):
dists[i] = np.sqrt(np.sum(np.square(X[i] - self.X_train), axis=1))
return dists
def compute_distances_no_loops(self, X):
# num_test = X.shape[0]
# num_train = self.X_train.shape[0]
# dists = np.zeros((num_test,num_train))
dists = np.sqrt(-2*np.dot(X, self.X_train.T) +
np.sum(np.square(self.X_train), axis=1)*(np.ones((X.shape[0],1))) +
np.sum(np.square(X), axis=1)*(np.ones(X_train.shape[0],1)).T)
return dists
# 預測標簽
def predict_labels(self, dists, k=1):
num_test = dists.shape[0]
y_pred = np.zeros(num_test)
for i in range(num_test):
closest_y = self.y_train[np.argsort(dists[i])[:k]] # 【【【按照距離給索引排序】取最近的k個索引】按照索引取訓練標簽】
y_pred[i] = np.argmax(np.bincount(closest_y)) # 投票,注意np.bincount()和np.argmax()在投票上的妙用
return y_pred
交叉驗證選擇超參數k的取值
We have implemented(實施) the k-Nearest Neighbor classifier(分類) but we set the value k = 5 arbitrarily(武斷地). We will now determine the best value of this hyperparameter with cross-validation(交叉驗證).
import numpy as np
num_folds = 5
k_choices = [1, 3, 5, 8, 10, 12, 15, 20, 50, 100]
X_train_folds = []
y_train_folds = []
################################################################################
# TODO: #
# Split up the training data into folds. After splitting, X_train_folds and #
# y_train_folds should each be lists of length num_folds, where #
# y_train_folds[i] is the label vector for the points in X_train_folds[i]. #
# Hint: Look up the numpy array_split function. #
################################################################################
X_train_folds = np.split(X_train, num_folds)
y_train_folds = np.split(y_train, num_folds)
################################################################################
# END OF YOUR CODE #
################################################################################
# A dictionary holding the accuracies for different values of k that we find
# when running cross-validation. After running cross-validation,
# k_to_accuracies[k] should be a list of length num_folds giving the different
# accuracy values that we found when using that value of k.
k_to_accuracies = {}
################################################################################
# TODO: #
# Perform k-fold cross validation to find the best value of k. For each #
# possible value of k, run the k-nearest-neighbor algorithm num_folds times, #
# where in each case you use all but one of the folds as training data and the #
# last fold as a validation set. Store the accuracies for all fold and all #
# values of k in the k_to_accuracies dictionary. #
################################################################################
for k in k_choices:
k_to_accuracies[k]=np.zeros(num_folds)
for i in range(num_folds):
Xtr = np.concatenate(
(np.array(X_train_folds)[:i],np.array(X_train_folds)[(i+1):]),axis=0)
ytr = np.concatenate(
(np.array(y_train_folds)[:i],np.array(y_train_folds)[(i+1):]),axis=0)
Xte = np.array(X_train_folds)[i]
yte = np.array(y_train_folds)[i]
# [num_of_folds, num_in_flods, feature_of_x] -> [num_of_pictures, feature_of_x]
Xtr = np.reshape(Xtr, (X_train.shape[0] * 4 / 5, -1))
ytr = np.reshape(ytr, (y_train.shape[0] * 4 / 5, -1))
Xte = np.reshape(Xte, (X_train.shape[0] / 5, -1))
yte = np.reshape(yte, (y_train.shape[0] / 5, -1))
classifier.train(Xtr, ytr)
yte_pred = classifier.predict(Xte, k)
yte_pred = np.reshape(yte_pred, (yte_pred.shape[0], -1))
accuracy = np.sum(yte_pred == yte, dtype=float)/len(yte) # bool to int,我們需要顯示指定為float
k_to_accuracies[k][i] = accuracy
################################################################################
# END OF YOUR CODE #
################################################################################
# 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)
SVM支持向量機
def svm_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 # 向前傳播 scores = X.dot(W) scores_correct = scores[np.arange(y.shape[0]),y].reshape(y.shape[0],1) margins = np.maximum(0., scores - scores_correct + 1) # 錯誤類加上閾值減去正確類得分,滿足的置零,不滿足的計算loss margins[np.arange(y.shape[0]),y] = 0 # 把上一步正確類參與計算的部分置零 loss = np.sum(margins)/y.shape[0] + 0.5*reg*np.sum(W*W) # 反向傳播 margins[margins>0] = 1 # 類relu反向傳播 row_grad = np.sum(margins, axis=1) margins[np.arange(y.shape[0]),y] - row_grad # 分類器反向都是這樣,錯誤類不動,正確類減去上層梯度 dW = np.dot(X.T,margins)/y.shape[0] + reg*W return loss, dW
