CS231N Assignment3 SoftMax
Begin
本文主要介绍CS231N系列课程的第三项作业,写一个SoftMax损失的线形训练模型。
课程主页:网易云课堂CS231N系列课程
语言:Python3.6
系统:Windows10
1 SoftMax分类器
简单介绍SoftMax,它和SVM都是比较常用的线形分类器,SoftMax最终得到的损失是
反应每个分类的概率大小。如下所示:
最左边蓝色框为每个分类的分数,我们取exp可以得到红色框,再归一化得到最右边绿色
的框,标准化的值就反应它的概率情况,我们再取它的-log得到我们要的损失值。
softmax到底什么含义呢?我查了一下,一般直接max取值使直接取最大值,softmax将
其转变为概率问题,这样就会考察概率情况,更加合理~~~emmm机器学习知识还不太好,等
我再看看书多补充点解释。
2 softmax梯度
前文解释了前向传播,计算损失,训练一个模型,还需要找到梯度,如何计算梯度呢?
下图是我写的一个计算,emmm懒得敲公式
然后在网易云课堂上,写了说要考虑一个稳定性问题~~~让每一个分数值都减去最大值,
那么每个分数都变为了复数,也就到了左半轴,这样指数函数变化率就会减小。emmm
大概使考虑这样就稳定了
3 代码部分
由于都是线形分类器,所以代码的训练、预测部分都是一样的,不同的在于损失函数
的求解以及梯度的计算。如下为损失函数的计算部分
def loss(self,W,X,Y,reg): loss = 0.0 num_train = X.shape[0] dW = np.zeros(W.shape) #计算分数 scores = X.dot(W) scores = scores - np.max(scores,axis=1,keepdims=True) #取score的exp exp_scores = np.exp(scores) #求行和,得到Pk的底 sum_row = np.sum(exp_scores,axis = 1,keepdims=True) #相除得到Pk P = exp_scores / sum_row #计算Loss loss = -1.0 / num_train * np.log(P[np.arange(num_train),Y]).sum() loss += 0.5 * reg * np.sum(W*W) #计算Dw梯度 grad = np.zeros_like(P)#生成一个和P一样的0矩阵 grad[np.arange(num_train),Y] = 1#对矩阵中Y所对应的部分加一个1,因为一会要剪 dW = X.T.dot(P - grad) #上文刚说要减,现在就减去,只有Y对应的地方有变化 dW = dW / num_train#加正则 dW += 0.5 * reg * np.sum(W*W) return loss,dW
测试:损失为2.5589,梯度如下
其他代码如下
class Softmax(): def __init__(self): self.W = None pass def loss(self,W,X,Y,reg): loss = 0.0 num_train = X.shape[0] dW = np.zeros(W.shape) #计算分数 scores = X.dot(W) scores = scores - np.max(scores,axis=1,keepdims=True) #取score的exp exp_scores = np.exp(scores) #求行和,得到Pk的底 sum_row = np.sum(exp_scores,axis = 1,keepdims=True) #相除得到Pk P = exp_scores / sum_row #计算Loss loss = -1.0 / num_train * np.log(P[np.arange(num_train),Y]).sum() loss += 0.5 * reg * np.sum(W*W) #计算Dw梯度 grad = np.zeros_like(P)#生成一个和P一样的0矩阵 grad[np.arange(num_train),Y] = 1#对矩阵中Y所对应的部分加一个1,因为一会要剪 dW = X.T.dot(P - grad) #上文刚说要减,现在就减去,只有Y对应的地方有变化 dW = dW / num_train#加正则 dW += 0.5 * reg * np.sum(W*W) return loss,dW def train(self,X,Y,learning_rate=1e-3,reg=1e-5,num_iters=100,batch_size=200,verbose=False): ''' 随机梯度下降法训练分类器 输入参数: -learning_rate学习率 -reg正则化强度 -num_iters步长值 -batch_size每一步使用的样本数量 -verbose若为真则打印过程 输出参数: list损失值 ''' num_train,dim = X.shape num_classes = np.max(Y) + 1 #if self.W is None: #初始化W矩阵 self.W = 0.001 * np.random.randn(dim,num_classes) loss_history = [] #开始训练num_iters步 for it in range(num_iters): X_batch = None Y_batch = None ######################## # 选取部分训练样本 # 随机生成一个序列 batch_inx = np.random.choice(num_train,batch_size) X_batch = X[batch_inx,:] Y_batch = Y[batch_inx] ######################### # 计算损失与梯度 loss,grade = self.loss(self.W,X_batch,Y_batch,reg) loss_history.append(loss) ######################## # 参数更新 # 梯度为正表示损失增大,应该减少,成负相关 self.W = self.W - learning_rate * grade #打印结果 if verbose and it % 100 == 0: print('iteration %d / %d : loss %f'%(it ,num_iters,loss)) return loss_history def predict(self,X_train): y_predict = np.zeros(X_train.shape[1]) #根据训练后的W矩阵计算分数 scores = X_train.dot(self.W) #找到得分中最大的值作为类别 y_predict = np.argmax(scores,axis = 1)#计算每一行最大值 return y_predict
接下来我们做一步测试,训练我们用到的数据。
############################################################## # step4 调参 # 两个参数,学习率;正则化强度 learning_rate = [1e-7,2e-7,5e-7] regularization_strengths = [3e4,3.25e4,3.5e4] results = {} best_val = 0 best_svm = None ###################################### # 循环执行代码 # 对不同的学习率以及正则化强度进行测试 # for rate in learning_rate: for regular in regularization_strengths: Softmax2 = Softmax() #训练 Softmax2.train(X_train,Y_train,learning_rate=rate,reg=regular,num_iters=1000) #预测 Y1 = Softmax2.predict(X_train) Y2 = Softmax2.predict(X_val) accuracy_train = np.mean(Y1==Y_train) accuracy_val = np.mean(Y2==Y_val) #判断优略 if best_val < accuracy_val: best_val = accuracy_val best_svm = Softmax2#保存当前模型 #存储数据 results[rate,regular] = (accuracy_train,accuracy_val) #打印数据 for lr,reg in sorted(results): accuracy_train,accuracy_val = results[(lr,reg)] print('lr:%e reg %e train accuracy: %f val val accuracy : %f'%(lr,reg,accuracy_train,accuracy_val))
结果如下,emmm有点低,等我以后调调,先学会写轮子把