最简单的线性分类器:
f
(
x
,
W
,
b
)
=
W
x
+
b
f(x, W,b)= Wx + b
f(x,W,b)=Wx+b
W和b是训练参数。x是输入数据。在公式中,x和b是向量,x是单个样本reshape之后的向量,b的长度和类别数目一致,W是矩阵。所以我们想将线性分类器应用到图像上,就必须先将图像reshape为一个向量。
W的含义
W的shape是[out_dims,in_dims]
- out_dims:一共有几个类别
- in_dims,把输入图像reshape成向量的长度。
对线性分类器可以是如下理解:
在深度学习尤其是识别分类任务中,常常会用到内积相似性,即两个向量相乘的积越大,这两个向量在特征空间的余弦( cos )距离越小。而W和x相乘,就是计算样本和每一个类别的表征向量的相似性。W如果按照行抽取出来,我们可以获得in_dims个向量,向量的长度是输入图像的reshape之后的长度。
当模型训练完毕,我们得到了较好的W参数之后,就按照行抽取出来in_dims个向量,把这些向量reshape为图像大小,然后imshow出来,就是课程上的这幅图。
每一类都有自己的一个表征。这个表征可以被视作代表了这个类别的大致图像,如果输入图像和某个类的表征的乘积很大,则说明这个图像属于这个类的概率很大。
这就是线性分类器的大致思想。
code
import numpy as np
class LinearClassifier(object):
def __init__(self):
self.W = None
def train(self,x,y,learning_rate=1e-3,reg=1e-5,num_iters=100,
batch_size=200):
# 训练样本数目, 一个样本的向量长度
num_train, dim = x.shape
num_classes = np.max(y) + 1 # 假设y在[0 - k-1]之间, 我们需要的是那个k,所以这里加1
if self.W == None:
self.W = 0.001 * np.random.randn(dim, num_classes)
loss_history = []
for it in range(num_iters):
# 为当前迭代 随机挑选出 训练集
random_index = np.random.choice(len(x),batch_size,replace=True)
x_batch = x[random_index]
y_batch = y[random_index]
loss, grad = self.loss(x_batch,y_batch,reg)
loss_history.append(loss)
# 更新参数W
self.W -= learning_rate*grad
def loss(self,x,y,reg):
dw = np.zeros_like(self.W)
num_classes = self.W.shaoe[1]
batch_size = x.shape[0]
scores = x.dot(self.W) # x * w
# the shape of scores is [ batch_size. num_classes ]
exp_scores = np.exp(scores)
softmax_scores = exp_scores / np.sum(exp_scores,axis=1)
exp_corrent_class_scores = softmax_scores[np.arange(batch_size), y]
loss = - np.log(exp_corrent_class_scores).mean()
loss += 0.5*reg*np.dot(self.W,self.W) # 正则化
# 求导
# softmax的求导是经过softmax得到的矩阵,在每个正例位置减一
# z = x*w +b
# a = softmax(z)
# loss = -y*log(a)
grad_z = softmax_scores
grad_z[np.arange(batch_size),y] -= 1
grad_w = x.T.dot(grad_z)
grad_w /= batch_size # 梯度求平均
grad_w += reg*self.W
return loss, grad_w
代码解释
使用的是随机梯度下降法,代码整体结构简单,思路清晰。读者们不懂的地方可能就是求导那里是怎么来的了。
这就涉及到了softmax的求导,以及矩阵求导的知识了,我将在下一篇博客专门介绍。