做網(wǎng)站哪便宜網(wǎng)絡營銷seo優(yōu)化
神經(jīng)網(wǎng)絡學習
- 導語
- 數(shù)據(jù)驅動
- 驅動方法
- 訓練/測試數(shù)據(jù)
- 損失函數(shù)
- 均方誤差
- 交叉熵誤差
- mini-batch
- 數(shù)值微分
- 梯度
- 梯度法
- 神經(jīng)網(wǎng)絡梯度
- 學習算法的實現(xiàn)
- 隨機梯度下降
- 2層神經(jīng)網(wǎng)絡實現(xiàn)
- mini-batch實現(xiàn)
- 總結
- 參考文獻
導語
神經(jīng)網(wǎng)絡中的學習指從訓練數(shù)據(jù)中自動獲取最優(yōu)權重參數(shù)的過程,這個“最優(yōu)”的定義在不同的應用場景下各有不同。
數(shù)據(jù)驅動
機器學習最關鍵的部分即如何對待數(shù)據(jù),從數(shù)據(jù)當中找到模式,找到規(guī)律,找到答案。
驅動方法
機器學習的思路是,先從圖像中提取特征量(一般由人設計),再用機器學習學習特征量的模式,神經(jīng)網(wǎng)絡的思路是,直接學習數(shù)據(jù)本身,特征量也是自主學習,區(qū)別圖如下(來自書):
神經(jīng)網(wǎng)絡對所有的問題都用同樣的流程解決,有時也被稱為端到端機器學習(從輸入直接獲得輸出)。
訓練/測試數(shù)據(jù)
訓練數(shù)據(jù)用來學習和找到最優(yōu)的參數(shù),測試數(shù)據(jù)用來評價模型的泛化能力,判斷已訓練的模型效果如何。
泛化能力即處理未被觀察過的數(shù)據(jù)(簡單說就是訓練數(shù)據(jù)之外的數(shù)據(jù))的能力,機器學習的目標就是獲得這種能力。
為了提高泛化能力,對一個模型往往使用多個數(shù)據(jù)集進行考察,如果只使用一個數(shù)據(jù)集的話,可能會導致模型在這個數(shù)據(jù)集上表現(xiàn)很好,但是直接拿來檢測新的數(shù)據(jù)時表達很差,這種只對某個數(shù)據(jù)集過度擬合的狀態(tài)就是過擬合。
損失函數(shù)
為了評估每次訓練的結果,神經(jīng)網(wǎng)絡采用了損失函數(shù)這一指標,這個損失函數(shù)有多種取法,書上給了幾種函數(shù)及其實現(xiàn)方式。
均方誤差
均方誤差是常用的損失函數(shù),表達式為(僅兩個數(shù)據(jù)比對): E = 1 2 ∑ k ( y k ? t k ) 2 E=\frac{1}{2}\mathop{\sum}\limits_k(y_k-t_k)^2 E=21?k∑?(yk??tk?)2。
y k y_k yk?為神經(jīng)網(wǎng)絡輸出, t k t_k tk?為監(jiān)督數(shù)據(jù), k k k為數(shù)據(jù)的維數(shù),一般用one-hot表示(正解標簽為1,其他為0)。
python實現(xiàn):
def mean_squared_error(y,t):return 0.5*np/sum((y-t)**2)
交叉熵誤差
交叉熵誤差表達式為: E = ? ∑ k t k l n y k E=-\mathop{\sum}\limits_kt_klny_k E=?k∑?tk?lnyk?,也用one-hot標簽,python的實現(xiàn)很簡單,如下:
def cross_entropy_error(y,t):return -np.sum(t*np.log(y))
這個實現(xiàn)方法看起來沒什么問題,但是如果熟悉 l n x lnx lnx曲線的話就會知道,當 y y y存在0項時, l n 0 ln0 ln0的值是無窮大,是無法運算的,因此需要添加一個微小值防止負無窮大,更改之后的函數(shù)實現(xiàn)如下:
def cross_entropy_error(y,t):delta=1e-8return -np.sum(t*np.log(y+delta))
mini-batch
上述給出的實現(xiàn)和式子都是只針對只有一個值的情況,實際上,神經(jīng)網(wǎng)絡是一批一批地處理數(shù)據(jù)的,當要求處理大量數(shù)據(jù)時,書上以交叉熵誤差為例,式子表達為: E = ? 1 N ∑ n ∑ k t n k l n y n k E=-\frac{1}{N}\mathop{\sum}\limits_n\mathop{\sum}\limits_kt_{nk}lny_{nk} E=?N1?n∑?k∑?tnk?lnynk?。
其中, t n k t_{nk} tnk?表示第 n n n個數(shù)據(jù)的第 k k k個元素值, y n k y_{nk} ynk?是輸出, t n k t_{nk} tnk?是監(jiān)督數(shù)據(jù)。
一般來說,由于數(shù)據(jù)集的數(shù)據(jù)量很大,用全部數(shù)據(jù)來計算損失函數(shù)是不適合的,因此mini-batch應運而生(從全部數(shù)據(jù)隨機選出一部分,作為整體的近似),可以理解為現(xiàn)實中的抽樣調查。
mini-batch的交叉熵實現(xiàn):
def cross_entropy_error(y,t):if y.ndim==1:t=t.reshape(1,t.size)y=y.reshape(1,y.size)batch_size=y.shape[0]return -np.sum(t.np.log(y+1e-8))/batch_size#one-hot表示#return -np.sum(np.log(y[np.arange(batch_size),t]+1e-8))/batchsize#非one-hot表示
數(shù)值微分
書上在這里介紹了導數(shù)(用割線來代替)、求導、偏導等概念,如果有高等數(shù)學基礎,應該很容易能理解這些,因此跳過,直接從梯度開始。
梯度
梯度是建立在偏導數(shù)的基礎上的,假設有變量 x 0 , x 1 , . . . . . . x n x_0,x_1,......x_n x0?,x1?,......xn?,然后有偏導數(shù) ? f ? x 0 , ? f ? x 1 . . . . . . , ? f ? x n \frac{\partial f}{\partial x_0},\frac{\partial f}{\partial x_1}......,\frac{\partial f}{\partial x_n} ?x0??f?,?x1??f?......,?xn??f?,由全部變量的偏導數(shù)匯成的向量就是梯度,書上python的實現(xiàn)如下:
def numerical_gradient(f,x):h=1e-4grad=np.zeros_like(x)#先生成一個全0數(shù)組for idx in range(x.size):#遍歷輸入tmp_val=x[idx]x[idx]=tmp_val+hfxh1=f(x)#f(x+h)x[idx] = tmp_val - hfxh2 = f(x)#f(x-h)grad[idx]=(fxh1-fxh2)/(2*h)x[idx]=tmp_valreturn grad
梯度指示的方向是各點函數(shù)值減小最多的方向,具體證明屬于高數(shù)知識,略。
梯度法
對于機器學習中的損失函數(shù),我們總是想讓其達到最小,但是,損失函數(shù)一般很復雜,不能直接得到最小的取值,此時,梯度法就是很好的解決方案。
但梯度法不一定每次都能取到最值,根據(jù)高等數(shù)學的指示,梯度所指的方向更類似于極值,而非最值(根據(jù)尋求最大值和最小值分成梯度下降和梯度上升)。
梯度法的思路很簡單,計算當前位置的梯度,然后函數(shù)的取值沿著梯度前進一定距離,然后在新的地方求梯度(移動多少書在后面解釋了,和學習率有關),循環(huán)往復。
梯度法的數(shù)學表達式很簡單: x = x ? η ? f ? x x=x-η\frac{\partial f}{\partial x} x=x?η?x?f?, η \eta η是更新量,也就是學習率,決定了每次移動的步長。
像學習率這種參數(shù)被稱為超參數(shù),因為它并不是神經(jīng)網(wǎng)絡自動學習獲得的,在實際的訓練過程中,往往需要嘗試多個值,學習率過小,則迭代次數(shù)過多,訓練的時間被無意義浪費,學習率過大,可能步子邁大了扯著蛋,越過了極值或最值。
書上將梯度法式子用python實現(xiàn):
def gradient_descent(f,init_x,lr=0.01,step_num=100):x=init_x#初始化網(wǎng)絡參數(shù)for i in range(step_num):grad=numerical_gradient(f,x)#計算梯度x-=lr*grad#向梯度方向移動return x
神經(jīng)網(wǎng)絡梯度
在理解了梯度的概念和用法之后,在神經(jīng)網(wǎng)絡中運用梯度就變得很容易了,將結果矩陣中的每個值對權重秋偏導即可,以一個 2 × 2 2×2 2×2的矩陣為例,表達式如下:
W = ( w 11 w 12 w 21 w 22 ) W= \begin{pmatrix} w_{11}&w_{12}\\ w_{21}&w_{22} \end{pmatrix} W=(w11?w21??w12?w22??)
? L ? W = ( ? L ? w 11 ? L ? w 12 ? L ? w 21 ? L ? w 22 ) \frac{\partial L}{\partial W}= \begin{pmatrix} \frac{\partial L}{\partial w_{11}}&\frac{\partial L}{\partial w_{12}}\\ & &\\ \frac{\partial L}{\partial w_{21}}&\frac{\partial L}{\partial w_{22}}\\ \end{pmatrix} ?W?L?= ??w11??L??w21??L???w12??L??w22??L??? ?
python的實現(xiàn)如下:
def numerical_gradient(f, x):h = 1e-4 # 0.0001grad = np.zeros_like(x)#初始化一個全0數(shù)組it = np.nditer(x, flags=['multi_index'], op_flags=['readwrite'])#設置迭代器#操作對象為多維,操作為讀寫while not it.finished:idx = it.multi_indextmp_val = x[idx]x[idx] = float(tmp_val) + hfxh1 = f(x) # f(x+h)x[idx] = tmp_val - h fxh2 = f(x) # f(x-h)grad[idx] = (fxh1 - fxh2) / (2*h)x[idx] = tmp_val # 還原值it.iternext() return grad
學習算法的實現(xiàn)
神經(jīng)網(wǎng)絡的學習過程大致包括這幾個部分:選出mini-batch,計算梯度,更新參數(shù),循環(huán)往復,理解起來很簡單,最關鍵的部分就是前面提到的梯度及其更新的部分。
隨機梯度下降
隨機梯度下降的概述很簡單,在原數(shù)據(jù)中隨機選擇mini batch的數(shù)據(jù),然后再計算梯度,再根據(jù)梯度下降的方向移動,進行下一次運算,循環(huán)往復,一般將該函數(shù)命名為SGD。
2層神經(jīng)網(wǎng)絡實現(xiàn)
書上實現(xiàn)了一個兩層神經(jīng)網(wǎng)絡的類,一些函數(shù)在前面已經(jīng)寫過,在此不再贅述,附帶注釋的代碼如下:
import sys, os
sys.path.append(os.pardir) # 為了導入父目錄的文件而進行的設定
from common.functions import *
from common.gradient import numerical_gradientclass TwoLayerNet:def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01):#輸入層神經(jīng)元數(shù),隱藏層神經(jīng)元數(shù),輸出層神經(jīng)元數(shù)# 初始化權重self.params = {}self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)#隨機初始化權重,使用高斯分布self.params['b1'] = np.zeros(hidden_size)#偏置初始化為0self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size)#隨機初始化權重self.params['b2'] = np.zeros(output_size)def predict(self, x):#推理過程W1, W2 = self.params['W1'], self.params['W2']b1, b2 = self.params['b1'], self.params['b2']a1 = np.dot(x, W1) + b1z1 = sigmoid(a1)a2 = np.dot(z1, W2) + b2y = softmax(a2)return y# x:輸入數(shù)據(jù), t:監(jiān)督數(shù)據(jù)def loss(self, x, t):y = self.predict(x)return cross_entropy_error(y, t)def accuracy(self, x, t):#計算精準度y = self.predict(x)y = np.argmax(y, axis=1)#重新排列成一維數(shù)組t = np.argmax(t, axis=1)accuracy = np.sum(y == t) / float(x.shape[0])return accuracy# x:輸入數(shù)據(jù), t:監(jiān)督數(shù)據(jù)def numerical_gradient(self, x, t):#進行梯度下降loss_W = lambda W: self.loss(x, t)#獲得損失值grads = {}grads['W1'] = numerical_gradient(loss_W, self.params['W1'])grads['b1'] = numerical_gradient(loss_W, self.params['b1'])grads['W2'] = numerical_gradient(loss_W, self.params['W2'])grads['b2'] = numerical_gradient(loss_W, self.params['b2'])#向梯度減小的方向移動return grads
mini-batch實現(xiàn)
書上以MNIST數(shù)據(jù)集為基礎,用兩層神經(jīng)網(wǎng)絡進行了學習,修改和加上注釋后,代碼和運行結果如下:
# coding: utf-8
import sys, os
sys.path.append(os.pardir) # 為了導入父目錄的文件而進行的設定
import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from two_layer_net import TwoLayerNet# 讀入數(shù)據(jù)
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)#加載數(shù)據(jù)network = TwoLayerNet(input_size=784, hidden_size=100, output_size=10)#創(chuàng)造一個神經(jīng)網(wǎng)絡類,隱藏層數(shù)據(jù)可以設置其他合理值iters_num = 10000 # 適當設定循環(huán)的次數(shù)
train_size = x_train.shape[0]
batch_size = 100#抽100個訓練
learning_rate = 0.1train_loss_list = []
train_acc_list = []
test_acc_list = []iter_per_epoch = max(train_size / batch_size, 1)for i in range(iters_num):batch_mask = np.random.choice(train_size, batch_size)#選100個隨機數(shù)x_batch = x_train[batch_mask]#獲得下標t_batch = t_train[batch_mask]#獲得下標# 計算梯度grad = network.numerical_gradient(x_batch, t_batch)#很慢,不如反向傳播# 更新參數(shù)for key in ('W1', 'b1', 'W2', 'b2'):network.params[key] -= learning_rate * grad[key]loss = network.loss(x_batch, t_batch)#計算損失train_loss_list.append(loss)if i % iter_per_epoch == 0:#每次算完一小批就輸出一次結果train_acc = network.accuracy(x_train, t_train)test_acc = network.accuracy(x_test, t_test)train_acc_list.append(train_acc)test_acc_list.append(test_acc)print("train acc, test acc | " + str(train_acc) + ", " + str(test_acc))# 繪制圖形
markers = {'train': 'o', 'test': 's'}
x = np.arange(len(train_acc_list))
plt.plot(x, train_acc_list, label='train acc')
plt.plot(x, test_acc_list, label='test acc', linestyle='--')
plt.xlabel("epochs")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
plt.legend(loc='lower right')
plt.show()
代碼循環(huán)次數(shù)為10000,每一次都隨機抽100個進行學習,計算損失函數(shù)值,輸出一次學習結果,記錄準確率,可以明顯的看到準確率在逐漸增加,一般來說,準確率的增加也有可能意味著過擬合的出現(xiàn),但是可以從圖中看到,隨著學習進行,訓練數(shù)據(jù)和測試數(shù)據(jù)的精度幾乎同步上升,且基本重合,因此可以說沒有出現(xiàn)過擬合。
總結
神經(jīng)網(wǎng)絡的學習中有許多重要的概念,如梯度、損失函數(shù)、梯度下降等,在弄清楚了這些之后就能更好的理解神經(jīng)網(wǎng)絡的學習過程。
參考文獻
- 《深度學習——基于Python的理論實現(xiàn)》