CS231n 最小的神经网络案例学习

最小的神经网络案例学习

1.生成数据

首先生成一个不是简单线性可分的分类数据集。我们希望是螺旋形的数据集,像这样生成:

N = 100 # 每种类型的数据数
D = 2 # 纬度,这里是二维坐标点
K = 3 # 类型数
X = np.zeros((N*K,D)) # 数据,每一行为一个样本
y = np.zeros(N*K, dtype='uint8') # 数据的类型标签
for j in xrange(K):
  ix = range(N*j,N*(j+1))
  r = np.linspace(0.0,1,N) # 极坐标半径
  t = np.linspace(j*4,(j+1)*4,N) + np.random.randn(N)*0.2 # 极坐标角度
  X[ix] = np.c_[r*np.sin(t), r*np.cos(t)]
  y[ix] = j
# 数据可视化:
plt.scatter(X[:, 0], X[:, 1], c=y, s=40, cmap=plt.cm.Spectral)
plt.show()
image

由上图可知,三类数据(红黄蓝)螺旋状的数据构成不能被轻易的线性划分。

通常我们需要将数据预处理成0均值单位标准差的分布,但是这里数据已经很好的分布在-1到1区间了,所以我们省去预处理。

2.训练Softmax线性分类器

1.1 初始化参数

像我们之前课程中看到的,Softmax拥有线性的评分函数(score function)然后使用交叉熵(cross-entropy)计算损失。我们首先用随机数初始化参数:

W = 0.01 * np.random.randn(D,K) #权值
b = np.zeros((1,K)) #偏差

回想一下D = 2是数据维度,K=3是分类的类型数。

1.2 计算类型得分

因为这是一个线性分类器,我们通过一个非常简单的并行的矩阵乘法来计算类型得分:

scores = np.dot(X, W) + b #得分(N,K)

在这个例子里我们有300个2维点坐标,到目前为止得分scores是一个[300x3]的矩阵,每一行都有3个得分(红黄蓝)。

1.3 计算损失值

这里的关键部分是损失函数,它是一个用来量化得分的可微函数,反映预测值(类型得分)和真实值(类型标签)间的差距。无疑我们需要正确类型的得分高于其它的类型的得分,而损失函数的值越低越好。回忆一下Softmax的损失函数如下:

L_i = -\log\left(\frac{e^{f_{y_i}}}{ \sum_j e^{f_j} }\right)

它其实是真实标签分布和预测得分分布的交叉熵,因为真实标签分布为[0,...,1,...,0]的形式,所以简化为只有一项。再回忆一下完整的带正则化项的损失函数:

L = \underbrace{ \frac{1}{N} \sum_i L_i }_\text{data loss} + \underbrace{ \frac{1}{2} \lambda \sum_k\sum_l W_{k,l}^2 }_\text{regularization loss}

下面我们来计算损失Loss:

num_examples = X.shape[0]
exp_scores = np.exp(scores)
probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
correct_logprobs = -np.log(probs[range(num_examples),y])
data_loss = np.sum(correct_logprobs)/num_examples
reg_loss = 0.5*reg*np.sum(W*W)
loss = data_loss + reg_loss

1.4 计算反向传播(Backpropagation)的解析梯度

通过求导可得:

\frac{\partial L_i }{ \partial f_{y_i} } = \left\{\begin{matrix} p_k (y_i \neq k) \\ p_k - 1 (y_i = k) \end{matrix}\right.

然后通过链式求导法则,计算出dW,db:

dscores = probs
dscores[range(num_examples),y] -= 1
dscores /= num_examples

dW = np.dot(X.T, dscores)
db = np.sum(dscores, axis=0, keepdims=True)
dW += reg*W # 别忘记正则化项

1.5 进行参数更新

W += -step_size * dW
b += -step_size * db

1.6 汇总:训练一个Softmax分类器

# 随机初始化参数
W = 0.01 * np.random.randn(D,K)
b = np.zeros((1,K))

# 一些超参数
step_size = 1e-0
reg = 1e-3 # regularization strength

# 梯度下降循环
num_examples = X.shape[0]
for i in xrange(200):

    # 计算得分scores, [N x K]
    scores = np.dot(X, W) + b 
    
    # 计算Softmax
    exp_scores = np.exp(scores)
    probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True) # [N x K]
    
    # 计算损失: 交叉熵损失 和 正则化
    correct_logprobs = -np.log(probs[range(num_examples),y])
    data_loss = np.sum(correct_logprobs)/num_examples
    reg_loss = 0.5*reg*np.sum(W*W)
    loss = data_loss + reg_loss
    if i % 10 == 0:
    print "iteration %d: loss %f" % (i, loss)
    
    # 计算scores的梯度
    dscores = probs
    dscores[range(num_examples),y] -= 1
    dscores /= num_examples
    
    # 计算出参数 (W,b) 的梯度
    dW = np.dot(X.T, dscores)
    db = np.sum(dscores, axis=0, keepdims=True)
    
    dW += reg*W # regularization gradient
    
    # 进行参数更新
    W += -step_size * dW
    b += -step_size * db

程序运行输出:

iteration 0: loss 1.096956
iteration 10: loss 0.917265
iteration 20: loss 0.851503
iteration 30: loss 0.822336
iteration 40: loss 0.807586
iteration 50: loss 0.799448
iteration 60: loss 0.794681
iteration 70: loss 0.791764
iteration 80: loss 0.789920
iteration 90: loss 0.788726
iteration 100: loss 0.787938
iteration 110: loss 0.787409
iteration 120: loss 0.787049
iteration 130: loss 0.786803
iteration 140: loss 0.786633
iteration 150: loss 0.786514
iteration 160: loss 0.786431
iteration 170: loss 0.786373
iteration 180: loss 0.786331
iteration 190: loss 0.786302

计算训练集准确率:

scores = np.dot(X, W) + b
predicted_class = np.argmax(scores, axis=1)
print 'training accuracy: %.2f' % (np.mean(predicted_class == y))

准确率大概是 49%,不是十分好,因为数据集的分布不是线性可分的。我们将学习出的选择边界画出来:

[图片上传失败...(image-8d7848-1533216591080)]

2.训练神经网络

很明显,线性分类器不适用于这种线性不可分的数据集,所以我们希望使用神经网络。一个两层的网络足够应付这个简单的例子。

2.1 初始化参数

h = 100 # 隐层规模
W = 0.01 * np.random.randn(D,h)
b = np.zeros((1,h))
W2 = 0.01 * np.random.randn(h,K)
b2 = np.zeros((1,K))

2.2 计算类型得分

hidden_layer = np.maximum(0, np.dot(X, W) + b) # 使用ReLU激活
scores = np.dot(hidden_layer, W2) + b2

2.3 计算损失

和之前的线性分类器一样。

2.4 反向传播计算梯度

dscores的计算同线性分类器。

dW2 = np.dot(hidden_layer.T, dscores)
db2 = np.sum(dscores, axis=0, keepdims=True)

dhidden = np.dot(dscores, W2.T)
dhidden[hidden_layer <= 0] = 0  # BP for ReLU

dW = np.dot(X.T, dhidden)
db = np.sum(dhidden, axis=0, keepdims=True)

2.5 进行参数更新

W += -step_size * dW
b += -step_size * db
W2 += -step_size * dW2
b2 += -step_size * db2

2.6 汇总: 训练一个神经网络

# 随机初始化参数
h = 100 # size of hidden layer
W = 0.01 * np.random.randn(D,h)
b = np.zeros((1,h))
W2 = 0.01 * np.random.randn(h,K)
b2 = np.zeros((1,K))

# 一些超参数
step_size = 1e-0
reg = 1e-3 # 正则化强度

# 梯度下降循环
num_examples = X.shape[0]
for i in xrange(10000):
  
    # 计算类型得分scores, [N x K]
    hidden_layer = np.maximum(0, np.dot(X, W) + b) # note, ReLU activation
    scores = np.dot(hidden_layer, W2) + b2
    
    # 计算Softmax
    exp_scores = np.exp(scores)
    probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True) # [N x K]
    
    # 计算损失: 交叉熵损失 和 正则化
    correct_logprobs = -np.log(probs[range(num_examples),y])
    data_loss = np.sum(correct_logprobs)/num_examples
    reg_loss = 0.5*reg*np.sum(W*W) + 0.5*reg*np.sum(W2*W2)  #所有层的正则化项的和
    loss = data_loss + reg_loss
    if i % 1000 == 0:
    print "iteration %d: loss %f" % (i, loss)
    
    # 计算scores的梯度
    dscores = probs
    dscores[range(num_examples),y] -= 1
    dscores /= num_examples
    
    # 反向传播计算参数梯度
    # W2和b2的梯度
    dW2 = np.dot(hidden_layer.T, dscores)
    db2 = np.sum(dscores, axis=0, keepdims=True)
    # 反向传播到隐层
    dhidden = np.dot(dscores, W2.T)
    # 计算ReLU的梯度
    dhidden[hidden_layer <= 0] = 0
    # 最后是W和b
    dW = np.dot(X.T, dhidden)
    db = np.sum(dhidden, axis=0, keepdims=True)
    
    # 加上正则化项的梯度贡献
    dW2 += reg * W2
    dW += reg * W
    
    # 进行参数更新
    W += -step_size * dW
    b += -step_size * db
    W2 += -step_size * dW2
    b2 += -step_size * db2

计算训练集的准确率:

hidden_layer = np.maximum(0, np.dot(X, W) + b)
scores = np.dot(hidden_layer, W2) + b2
predicted_class = np.argmax(scores, axis=1)
print 'training accuracy: %.2f' % (np.mean(predicted_class == y))

准确率是 98%!学习出的选择边界如图所示:

image

总结

我们看到了线性分类器和两层的神经网络在代码上区别不大,但是带来了巨大的提升。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,036评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,046评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,411评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,622评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,661评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,521评论 1 304
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,288评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,200评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,644评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,837评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,953评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,673评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,281评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,889评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,011评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,119评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,901评论 2 355

推荐阅读更多精彩内容