机器学习基础知识:
包括线性回归,逻辑回归,交叉熵,softmax,KNN,神经网络中梯度的传递思想。关于线性回归和逻辑回归部分的知识,可以参考这个博客的内容,就不再累述:http://blog.csdn.net/viewcode/article/details/8794401。
关于softmax的理解:
softmax是另一种误差函数的表现形式,可以叫做softmax-loss function.
loss的计算公式:
softmax梯度的求导(使用链式求导法,在two-layer-network中会使用到):
神经网络中的梯度的传递思想与上面softmax的链式求导法类似,逐级计算梯度然后一级一级传导。可以大大降低求梯度的难度。
part 2
cs231n/assignment1:
KNN:
原理:使用 k_nearest_neighbor分类器,实现对小型图像的识别,流程是:
1,载入数据:从cifar10数据库中随机抽取5000个样本作为训练集,500个样本作为测试集。
2,数据前处理:每个样本是一个32*32的彩色图片,一个像素由3个颜色组成,所以一张图片包含32×32×3=3072个数据,使用reshape函数,把每个样本的数据变成一个行向量(3072维),然后训练集就变成一个5000*3072的矩阵,测试集变成一个500*3072的矩阵。
3,计算距离矩阵:分别用三种方法(两层嵌套循环,一层循环,不使用循环)计算测试集中的样本与训练集中的样本在像素上的L2距离,存储在距离矩阵中(500*5000)。
两层嵌套循环:
vector1=np.mat(X[i])
vector2=np.mat(self.X_train[j])
dists[i,j]=math.sqrt((vector1-vector2)*((vector1-vector2).T))
每次计算两个行向量的L2距离,存放在dists[i,j]中。
一层循环:
p=np.mat(X[i])
cT=self.X_train.T
dists[i]=np.sqrt(np.square(p)*np.ones(cT.shape)+np.mat(np.ones(p.shape))*np.square(cT)-2*p*cT)
每次计算测试集一个样本(行向量)与训练集所有样本(矩阵)的L2距离,存放在dists[i]的行中。
不使用循环:
a=np.mat(X)
aT=np.mat(self.X_train.T)
dists=np.sqrt(np.square(a)*np.ones(aT.shape)+np.ones(a.shape)*np.square(aT)-2*a*aT)
直接对测试集和训练集两个矩阵进行运算,不使用循环,dists[i,j]中存放的是第i个测试样本与第j个训练样本的L2距离。
4,类别预测:在这一步中,针对每个测试集中的样本,对其所对应的dists距离矩阵中的对应行向量中的距离排序(从小到大),然后选取前K(K是一个超参数)个最小的距离,然后返回对应的标签存放在closest_y中,然后对closest_y中元素进行统计分析,从中挑选出出现次数最多的标签,使用“多数赞成投票”法决定测试样本的标签的预测,最后返回到数组中:y_pred,其中存放所有测试样本的预测标签。
代码段1:
order=np.argsort(dists[i]) #order is an array contains the index of array distance rank from mintomax
for j in range(1,k+1):
min=order[0,j-1] #order_k is the index of K-th value label
closest_y.append(self.y_train[min]) #min is the min index of distance
实现功能:把dists[i]按从小到大排序,然后取前k个对应的标签存放到closest_y中。
代码段2:
from collections import Counter
c=Counter(closest_y).most_common(1)
d=list(c[0])
y_pred[i]=d[0]
实现功能:统计closest_y中出现最多次数的标签给到y_pred[i]。
5,测试了K=1和等于K=5下识别的准确度:分别是
k=1:Got 137 / 500 correct => accuracy: 0.274000
k=5:Got 142 / 500 correct => accuracy: 0.284000
可看出k=5的情况下准确度稍微高一点。
6,测试了计算dists矩阵的三种不同方式下耗费的计算时间:
Two loop version took 57.856100 seconds
One loop version took 25.614912 seconds
No loop version took 0.358330 seconds
可明显看出,向量和矩阵运算的使用明显加快了计算速度,特别是无循环下,直接用矩阵进行运算的速度超乎想象,可见矩阵的并行计算有很大的优势,的确是趋势。
7,交叉验证,最后一步,knn算法使用了交叉验证的方法来实现对超参数K的调谐,分别针对k_choices = [1, 3, 5, 8, 10, 12, 15, 20, 50, 100]这几个
超参数选项,分别使用交叉验证的方法反复计算多次,验证其所得到的预测准则度,然后找出效果最好的那个超参数k.
代码段1:
X_train_folds=np.array_split(X_train,num_folds)
y_train_folds=np.array_split(y_train,num_folds)
实现功能:把训练集均分为num_folds份。
代码段2:
for k in k_choices:
for i in range(num_folds):
X_train_cross = np.array(X_train_folds[:i] + X_train_folds[i + 1:])
y_train_cross = np.array(y_train_folds[:i] + y_train_folds[i + 1:])
X_train_cross = X_train_cross.reshape(-1, X_train_cross.shape[2])
y_train_cross = y_train_cross.reshape(-1)
X_test_cross = np.array(X_train_folds[i])
y_test_cross = np.array(y_train_folds[i])
classifier = KNearestNeighbor()
classifier.train(X_train_cross, y_train_cross)
dists= classifier.compute_distances_no_loops(X_test_cross)
y_test_pred = classifier.predict_labels(dists,k)
num_correct = np.sum(y_test_pred == y_test_cross)
accuracy = float(num_correct) / y_test_cross.shape[0]
if (k in k_to_accuracies.keys()):
k_to_accuracies[k].append(accuracy)
else:
k_to_accuracies[k] = []
k_to_accuracies[k].append(accuracy)
实现功能:循环数次,每次使用不同的K值,然后每个k值验证num_folds次,每次验证时,训练集的第i部分作为测试集,其余部分作为训练集,最后把结果存起来
然后对结果(准确度)进行统计分析,可以绘制出一下的曲线:
SVM:
这个算法的难点,主要是loss函数的计算和偏导数的计算,首先公式为:
L= (1/N)∑iLi + λR(W)
Li= ∑j≠yi max(0, (xiW)j−(xyiW)j+Δ)
其中:
λR(W):正则化惩罚,为什么要正则化,因为w不唯一,如果w正好能使得loss=0(最优权重),那么kw也能做到,为了得到唯一的W进行分类工作,我们可以添加一个正则化惩罚项。其最主要作用是防止过拟合,提高模型的泛化能力,因为如果产生了大数值的权重容易导致产生过大的loss,进而过拟合,所以正则化惩罚能有效地抑制大数组权重的产生。
(xiW)j:错误分类的得分,(xyiW)j:正确分类的得分,意味着对于每一张图像样本,正确分类的得分应该比错误分类的得分至少高Δ(Δ的取值在实际中一般为1)。
梯度的公式:
∇WyiLi = - xiT(∑j≠yi1(xiWj- xiWyi +1>0)) + 2λWyi
∇WjLi = xiT 1(xiWj- xiWyi +1>0) + 2λWj , (j≠yi)
其中1()为逻辑函数,表达式为真为1,假为0.
代码段1:
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[:, y[i]] += -X[i, :]
dW[:, j] += X[i, :]
loss /= num_train
dW /= num_train
loss += 0.5 * reg * np.sum(W * W)
dW += reg * W
实现功能:用循环的方式,求出loss和dW
代码段2:
scores = X.dot(W)
num_train = X.shape[0]
num_classes = W.shape[1]
scores_correct = scores[np.arange(num_train), y] # 1 by N
scores_correct = np.reshape(scores_correct, (num_train, 1)) # N by 1
margins = scores - scores_correct + 1.0 # N by C
margins[np.arange(num_train), y] = 0.0
margins[margins <= 0] = 0.0
loss += np.sum(margins) / num_train
loss += 0.5 * reg * np.sum(W * W)
实现功能:用对向量的操作,不适用循环语句,直接求出loss
代码段3:
margins[margins > 0] = 1.0
row_sum = np.sum(margins, axis=1)
margins[np.arange(num_train), y] = -row_sum
dW += np.dot(X.T, margins)/num_train + reg * W
实现功能:用对向量的操作,不适用循环语句,直接求出dW
代码段4:
params = [(x,y) for x in learning_rates for y in regularization_strengths]
for lrate, regular in params:
svm = LinearSVM()
loss_hist = svm.train(X_train, y_train, learning_rate=lrate, reg=regular,num_iters=700, verbose=False)
y_train_pred = svm.predict(X_train)
accuracy_train = np.mean(y_train == y_train_pred)
y_val_pred = svm.predict(X_val)
accuracy_val = np.mean(y_val == y_val_pred)
results[(lrate, regular)]=(accuracy_train, accuracy_val)
if (best_val < accuracy_val):
best_val = accuracy_val
best_svm = svm
实现功能:svm算法中超参数的调谐,两个超参数:学习率和正则化惩罚系数:
learning_rates= [1.4e-7, 1.45e-7, 1.5e-7, 1.55e-7, 1.6e-7, 1.65e-7, 1.7e-7]
regularization_strengths= [3e4, 3.1e4, 3.2e4, 3.3e4, 3.4e4]
经过双重循环尝试遍所有可能的超参数组合,求出其对应的准确率,进行比较,可以找出准确率最高的一组超参数组合:
lr 1.400000e-07 reg 3.100000e+04 train accuracy: 0.375755 val accuracy: 0.401000
可见最好的超参数是学习率为 1.400000e-07,正则化惩罚系数为3.100000e+04,对应的准确率为40.1%.
Softmax:
softmax和svm方法极其类似,唯一区别就是loss函数和梯度的计算方法不一样,svm中是Hingeloss,在softmax中替换成了交叉熵cross-entropyloss:
Loss:
L= -(1/N)∑i∑j1(k=yi)log(exp(fk-fmax)/∑jexp(fj-fmax))+ λR(W)
其中:-fmax是为了保证数值稳定性的问题,因为在计算过程中,exp(fyi)和 ∑jexp(fj)的值可能会变得非常大,大值数相除容易导致数值不稳定,所以要把这个大数值给剔除掉。λR(W)则是正则化惩罚,保证w的唯一性,抑制产生大数值的权重导致过拟合。
偏导:
∇WkL = -(1/N)∑i xiT(pi,m-Pm)+ 2λWk
其中:Pk= exp(fk-fmax)/∑jexp(fj-fmax)
因为和svm类似,所以softmax和svm的代码段也很类似:
代码段1:
dW_each = np.zeros_like(W)
num_train, dim = X.shape
num_class = W.shape[1]
f = X.dot(W)
f_max = np.reshape(np.max(f, axis=1), (num_train, 1)) # N by 1
prob = np.exp(f - f_max) / np.sum(np.exp(f - f_max), axis=1, keepdims=True) # N by C
y_trueClass = np.zeros_like(prob)
y_trueClass[np.arange(num_train), y] = 1.0
for i in xrange(num_train):
for j in xrange(num_class):
loss += -(y_trueClass[i, j] * np.log(prob[i, j]))
dW_each[:, j] = -(y_trueClass[i, j] - prob[i, j]) * X[i, :]
dW += dW_each
loss /= num_train
loss += 0.5 * reg * np.sum(W * W)
dW /= num_train
dW += reg * W
实现功能:使用两层显示循环,计算loss和dW。
代码段2:
num_train, dim = X.shape
f = X.dot(W)
f_max = np.reshape(np.max(f, axis=1), (num_train, 1))
prob = np.exp(f - f_max) / np.sum(np.exp(f - f_max), axis=1, keepdims=True)
y_trueClass = np.zeros_like(prob)
y_trueClass[range(num_train), y] = 1.0
loss += -np.sum(y_trueClass * np.log(prob)) / num_train + 0.5 * reg * np.sum(W * W)
dW += -np.dot(X.T, y_trueClass - prob) / num_train + reg * W
实现功能:直接对向量进行计算,直接算出loss和dW。
代码段3:
params = [(x,y) for x in learning_rates for y in regularization_strengths ]
for lrate, regular in params:
softmax = Softmax()
loss_hist = softmax.train(X_train, y_train, learning_rate=lrate, reg=regular,num_iters=700, verbose=True)
y_train_pred = softmax.predict(X_train)
accuracy_train = np.mean( y_train == y_train_pred)
y_val_pred = softmax.predict(X_val)
accuracy_val = np.mean(y_val == y_val_pred)
results[(lrate, regular)] = (accuracy_train, accuracy_val)
if(best_val < accuracy_val):
best_val = accuracy_val
best_softmax = softmax
实现功能:softmax算法中超参数的调谐,两个超参数:学习率和正则化惩罚系数:
learning_rates= [1.4e-7, 1.45e-7, 1.5e-7, 1.55e-7, 1.6e-7, 1.65e-7, 1.7e-7]
regularization_strengths= [3e4, 3.1e4, 3.2e4, 3.3e4, 3.4e4]
经过双重循环尝试遍所有可能的超参数组合,求出其对应的准确率,进行比较,可以找出准确率最高的一组超参数组合:
lr 1.400000e-07 reg 3.400000e+04 train accuracy: 0.340592 val accuracy: 0.367000
可见最好的超参数是学习率为 1.400000e-07,正则化惩罚系数为3.400000e+04,对应的准确率为36.7%.
二层神经网络:
神经网络基本结构示意:
这个应用旨在学会搭建一个最小系统的神经网络:只包含一个输入层通过一个隐含层(一层神经元细胞)的神经网络,然后是输出层。
神经网络的具体训练过程如下:
1,参数设置:迭代次数N-iteration,Cifar-10中取出的样本库50000个,每次迭代抽样容量:batch-size。
2,样本库预处理:把样本图片(32*32*3
)正则化,即所有图片减去平均图像,可以控制图片矩阵的大小在一个范围内,矩阵reshape为行向量3072维。
3,初始化训练参数设置。
4,开始迭代过程:从样本中随机抽取batch-size个图片,输入神经网络,前向传导(hiddenlayer中神经元使用Relu作为激活函数),得到loss,然后反向传导,
反向逐级计算梯度,然后得到loss(softmaxloss)对W1和W2的偏导数,并用其更新W1和W2(随机梯度下降法)。
5,进行下一次迭代:从样本中随机抽取另外batch-size个图片,重复上面步骤,更新W1和W2以使得loss趋于极小值点。
6,在迭代过程中,每间隔一个纪元epoch(epoch=50000/batch-size)次迭代,会进行一次训练准确度的检查,会计算并记录此时神经网络训练出的训练误差和验证误差,并且为了防止迭代到后期,学习率过大的问题,还会对学习率进行衰减。
对于迭代过程中的前向传导和反向传导:
上图中给出了迭代过程中数据在神经网络中前向传导和反向传导的公式。
neural-net.py中代码实现:
代码段1:
z1=X.dot(W1)+b1#(H)
a1=np.zeros_like(z1)
a1=np.maximum(z1,0)
scores=a1.dot(W2)+b2
实现功能:计算出前向传导过程的中间值和最终输出。
代码段2:
scores = scores - np.reshape(np.max(scores,axis=1),(N,-1))
p = np.exp(scores)/np.reshape(np.sum(np.exp(scores),axis=1),(N,-1))
loss = -sum(np.log(p[np.arange(N),y]))/N
loss += 0.5*reg*np.sum(W1*W1)+0.5*reg*np.sum(W2*W2)
实现功能:前向传导过程中的softmax-loss计算,并且对loss进行w1和w2正则化。
代码段3:
dldf=p
dldf[range(N),y]-=1.0
dldf/=N
dfdw2=a1
dW2=np.dot(dfdw2.T,dldf)
dh2 = np.sum(dldf,axis=0,keepdims=False)
dfda1=W2
dlda1=np.dot(dldf,dfda1.T)
dldz1=dlda1
dldz1[a1<=0]=0
dh1 = np.sum(dldz1,axis=0,keepdims=False)
dz1dw1=X
dW1=np.dot(dz1dw1.T,dldz1)
dW2 += reg*W2
dW1 += reg*W1
grads['W1']=dW1
grads['b1']=dh1
grads['W2']=dW2
grads['b2']=dh2
实现功能:反向传导,逐级计算梯度,计算出loss对w1,w2梯度(正则化)。
代码段4;
sample_index = np.random.choice(num_train, batch_size)
X_batch = X[sample_index, :] # select the batch sample
y_batch = y[sample_index] # select the batch label
实现功能:从50000个样本库中随机抽取batch-size个样本,作为训练集。
代码段5:
W1 = grads['W1']
b1 = grads['b1']
W2 = grads['W2']
b2 = grads['b2']
self.params['W1'] -= learning_rate*W1
self.params['b1'] -= learning_rate*b1
self.params['W2'] -= learning_rate*W2
self.params['b2'] -= learning_rate*b2
实现功能:用梯度更新W1和W2,随机梯度下降法.
代码段6:
z1=X.dot(self.params['W1'])+self.params['b1']#(H)
a1=np.zeros_like(z1)
a1=np.maximum(z1,0)
scores=a1.dot(self.params['W2'])+self.params['b2']
y_pred = np.argmax(scores, axis=1)
实现功能:预测样本的类型。
two-layer-net.ipyn文件:
代码段1:
best_valacc=-1.0
input_size = 32 * 32 * 3
num_classes = 10
#hidden_size = 50
hidden_size = 32 * 32 * 3
learn_rate =[7.2e-4]
#learning_rate_decay=[0.94,0.95,0.93]
reg=[1e-3]
results = {}
params = [x1 for x1 in learn_rate ]
# for x3 in learning_rate_decay for x4 in reg]
for learn_rate in params:
net = TwoLayerNet(input_size, hidden_size, num_classes)
# Train the network
stats = net.train(X_train, y_train, X_val, y_val,
num_iters=6400, batch_size=128,
learning_rate =7.2e-4, learning_rate_decay=0.95,
reg=1e-3, verbose=True)
# Predict on the validation set
val_acc = np.mean(net.predict(X_val) == y_val)
results[learn_rate] =val_acc
if val_acc>best_valacc:
best_valacc = val_acc
best_net = net
for learn_rate in sorted(results):
val_accuracy = results[(learn_rate)]
print 'learn_rate %e val accuracy: %f' % (
learn_rate, val_accuracy)
print 'best validation accuracy achieved during cross-validation: %f' % best_valacc
实现功能:反复修改神经网络的初始化参数和超参数,调试出具有最高分类精度的超参数。
结果 :
按照上面代码中的参数设置最终实现的精度为54.7%.