1. 程式人生 > ># [cs231n (八)神經網路總結:最小網路案例研究 ][1]

# [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()

1

此時資料是非線性的, 對資料進行標準差標準化已經做過了。

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相關的交叉熵損失,損失函式應該是:

Li=log(efyijefj)\displaystyle Li=-log(\frac{e^{f_{y_i}}}{\sum_je^{f_j}})

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. 反向傳播法計算梯度

這裡引入 pk=efkjefjLi=log(pyi)p_k = \frac{e^{f_k}}{ \sum_j e^{f_j} } \hspace{1in} L_i =-\log\left(p_{y_i}\right),

可以使用鏈式法則:

Lifk=pk1(yi=k)\frac{\partial L_i }{ \partial f_k } = p_k - \mathbb{1}(y_i = k)

好簡單哈 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

最後,我們得到的分數 scores=np.dotXW+bscores = np.dot(X,W)+ b ,所以分數梯度儲存在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%, 看一下學習到邊界

2

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的非線性,很簡答啊
r=max(0,x),drdx=1(x>0)r = max(0, x), \frac{dr}{dx} = 1(x > 0)
可以知道梯度通過如果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))

3

5. 總結

其實從線性到網路我們變化的程式碼很少,Loss只變了一行,反向傳播只不過是加了“中間變數”。