# [cs231n (八)神經網路總結:最小網路案例研究 ][1]
標籤(空格分隔): 神經網路
0.回顧
cs231n (一)影象分類識別講了KNN
cs231n (二)講了線性分類器:SVM和SoftMax
cs231n (三)優化問題及方法
cs231n (四)反向傳播
cs231n (五)神經網路 part 1:構建架構
cs231n (六)神經網路 part 2:傳入資料和損失
cs231n (七)神經網路 part 3 : 學習和評估
1. 引言
經過前面近七節課的學習訓練,我們有了深厚理論基礎,和毛毛草草的向量化程式設計基礎、調優基礎。。。這牛逼吹的怕了都。
那麼現在我們應該開始自己動手解決一個實際問題吧:快來啊。
首先實現線性分類器,然後拓展到神經網路,只要線性網路構建好了,我們拓展到神經網路就會很簡單很簡單很jian。
2. 生成一些資料
先生成一個螺旋離散資料
N = 100 # number of points per class
D = 2 # dimensionality
K = 3 # number of classes
X = np.zeros((N*K,D)) # data matrix (each row = single example)
y = np.zeros(N*K, dtype='uint8') # class labels
for j in range(K):
ix = range(N*j,N*(j+1))
r = np.linspace(0.0,1,N) # radius
t = np.linspace(j*4,(j+1)*4,N) + np.random.randn(N)*0.2 # theta
X[ix] = np.c_[r*np.sin(t), r*np.cos(t)]
y[ix] = j
# lets visualize the data:
plt.scatter(X[:, 0], X[:, 1], c=y, s=40, cmap=plt.cm.Spectral)
plt.show()
此時資料是非線性的, 對資料進行標準差標準化已經做過了。
3. 訓練一個softmax線性分類器
1. 初始化引數
先在這個分類資料集上訓練一個Softmax分類器,Softmax分類器具有線性分數函式,並使用交叉熵損失。
線性分類器的引數由每個類別的權重矩陣W和偏差向量b組成,先將這些引數初始化為隨機數:
# initialize parameters randomly
W = 0.01 * np.random.randn(D,K)
b = np.zeros((1,K))
W = DxC = 2x3
2. 計算分數
得到分數很簡答啊:
# compute class scores for a linear classifier
scores = np.dot(X, W) + b
X = NxD = 300x2
scores = NxC
3. 計算loss
損失函式是得到區分目標的關鍵:其實就是正確的類得分是最高的,並且損失是最低的,如果分類正確。
這裡使用的是softmax相關的交叉熵損失,損失函式應該是:
softmax 函式把每一個數據得到三個分數,按照上述公式得到的是標準化概率,並且:
當正確類別概率很小,那麼loss會趨近於無窮的。
當正確類別概率接近於1,那麼loss會趨近於0的, 因為log(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} \\$$
我們計算得到了分數,那麼損失可由上述獅子計算
num_examples = X.shape[0]
# get unnormalized probabilities
exp_scores = np.exp(scores)
# normalize them for each example
probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
現在得到的概率是:[300 x 3], 每行有三個數值,理論上最大的那個就是對應的正確分類的分數。
corect_logprobs = -np.log(probs[range(num_examples),y])
這裡只分配概率給正確分類,損失就是這些對數概率和正則化損失的均值
# compute the loss: average cross-entropy loss and regularization
data_loss = np.sum(corect_logprobs)/num_examples
reg_loss = 0.5*reg*np.sum(W*W)
loss = data_loss + reg_loss
損失越低意味著正確分類的概率越高。
4. 反向傳播法計算梯度
這裡引入 ,
可以使用鏈式法則:
好簡單哈 p = [0.2, 0.3, 0.5], 0.3是正確的分類, 那麼 df = [0.2, -0.7, 0.5]
增加分數向量f的第一個或最後一個元素(不正確類別的分數)會增加損失(由於+0.2和+0.5的正號)增加損失是不好的分類。
然而,增加正確分數的分數對損失有負面影響。 -0.7的梯度告訴我們,增加正確的分數會導致損失 Li 的減少,這是合理的。
probs儲存每個例子的所有類(作為行)的概率,為了獲得分數上的梯度dscores。
dscores = probs
dscores[range(num_examples),y] -= 1
dscores /= num_examples
最後,我們得到的分數 ,所以分數梯度儲存在dscores中),我們現在可以反向傳播到W和b:
dW = np.dot(X.T, dscores)
db = np.sum(dscores, axis=0, keepdims=True)
dW += reg*W # don't forget the regularization gradient
###5. 如何引數更新?
現在指導了梯度,指導引數如何影響損失函式,那麼就開始更新梯度啦,就是稍微減少點梯度,其實就是迭代啦。
# perform a parameter update
W += -step_size * dW
b += -step_size * db
6. 現在就可以合併一下得到softmax分類器
#Train a Linear Classifier
# initialize parameters randomly
W = 0.01 * np.random.randn(D,K)
b = np.zeros((1,K))
# some hyperparameters
step_size = 1e-0
reg = 1e-3 # regularization strength
# gradient descent loop
num_examples = X.shape[0]
for i in xrange(200):
# evaluate class scores, [N x K]
scores = np.dot(X, W) + b
# compute the class probabilities
exp_scores = np.exp(scores)
probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True) # [N x K]
# compute the loss: average cross-entropy loss and regularization
corect_logprobs = -np.log(probs[range(num_examples),y])
data_loss = np.sum(corect_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)
# compute the gradient on scores
dscores = probs
dscores[range(num_examples),y] -= 1
dscores /= num_examples
# backpropate the gradient to the parameters (W,b)
dW = np.dot(X.T, dscores)
db = np.sum(dscores, axis=0, keepdims=True)
dW += reg*W # regularization gradient
# perform a parameter update
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
經過190次迭代,可以得到訓練精度:
# evaluate training set accuracy
scores = np.dot(X, W) + b
predicted_class = np.argmax(scores, axis=1)
print 'training accuracy: %.2f' % (np.mean(predicted_class == y))
準確率是 49%, 看一下學習到邊界
4. 訓練神經網路
其實對於非線性邊界用線性分類器確實有點難,現在我們構建一個簡單的二層神經網路。
第一層第二層就是這麼簡單:
# initialize parameters randomly
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))
前向傳播的分數可以這樣得到:
# evaluate class scores with a 2-layer Neural Network
hidden_layer = np.maximum(0, np.dot(X, W) + b) # note, ReLU activation
scores = np.dot(hidden_layer, W2) + b2
在隱含層新增非線性單元------ReLU。
然後計算loss,分數梯度dscores都和之前一樣。
計算梯度的時候先BP到第二層網路,這裡也和之前的softmax類似。
# backpropate the gradient to the parameters
# first backprop into parameters W2 and b2
dW2 = np.dot(hidden_layer.T, dscores)
db2 = np.sum(dscores, axis=0, keepdims=True)
由於中間加了一個隱含層,所以我們需要計算隱含層的梯度:
dhidden = np.dot(dscores, W2.T)
還需要回傳ReLUd的非線性,很簡答啊
可以知道梯度通過如果x > 0, 梯度為零如果x < 0
# backprop the ReLU non-linearity
dhidden[hidden_layer <= 0] = 0
那麼計算第一層的權重和梯度就是:
# finally into W,b
dW = np.dot(X.T, dhidden)
db = np.sum(dhidden, axis=0, keepdims=True)
好了我們已經完成整個過程了,總結一下。
# initialize parameters randomly
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))
# some hyperparameters
step_size = 1e-0
reg = 1e-3 # regularization strength
# gradient descent loop
num_examples = X.shape[0]
for i in xrange(10000):
# evaluate class scores, [N x K]
hidden_layer = np.maximum(0, np.dot(X, W) + b) # note, ReLU activation
scores = np.dot(hidden_layer, W2) + b2
# compute the class probabilities
exp_scores = np.exp(scores)
probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True) # [N x K]
# compute the loss: average cross-entropy loss and regularization
corect_logprobs = -np.log(probs[range(num_examples),y])
data_loss = np.sum(corect_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)
# compute the gradient on scores
dscores = probs
dscores[range(num_examples),y] -= 1
dscores /= num_examples
# backpropate the gradient to the parameters
# first backprop into parameters W2 and b2
dW2 = np.dot(hidden_layer.T, dscores)
db2 = np.sum(dscores, axis=0, keepdims=True)
# next backprop into hidden layer
dhidden = np.dot(dscores, W2.T)
# backprop the ReLU non-linearity
dhidden[hidden_layer <= 0] = 0
# finally into W,b
dW = np.dot(X.T, dhidden)
db = np.sum(dhidden, axis=0, keepdims=True)
# add regularization gradient contribution
dW2 += reg * W2
dW += reg * W
# perform a parameter update
W += -step_size * dW
b += -step_size * db
W2 += -step_size * dW2
b2 += -step_size * db2
## This prints:
iteration 0: loss 1.098744
iteration 1000: loss 0.294946
iteration 2000: loss 0.259301
iteration 3000: loss 0.248310
iteration 4000: loss 0.246170
iteration 5000: loss 0.245649
iteration 6000: loss 0.245491
iteration 7000: loss 0.245400
iteration 8000: loss 0.245335
iteration 9000: loss 0.245292
訓練精度是:98%! 厲害了,老鐵
# evaluate training set accuracy
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))
5. 總結
其實從線性到網路我們變化的程式碼很少,Loss只變了一行,反向傳播只不過是加了“中間變數”。