做網(wǎng)站哪便宜網(wǎng)絡(luò)營銷seo優(yōu)化
神經(jīng)網(wǎng)絡(luò)學(xué)習(xí)
- 導(dǎo)語
- 數(shù)據(jù)驅(qū)動
- 驅(qū)動方法
- 訓(xùn)練/測試數(shù)據(jù)
- 損失函數(shù)
- 均方誤差
- 交叉熵誤差
- mini-batch
- 數(shù)值微分
- 梯度
- 梯度法
- 神經(jīng)網(wǎng)絡(luò)梯度
- 學(xué)習(xí)算法的實(shí)現(xiàn)
- 隨機(jī)梯度下降
- 2層神經(jīng)網(wǎng)絡(luò)實(shí)現(xiàn)
- mini-batch實(shí)現(xiàn)
- 總結(jié)
- 參考文獻(xiàn)
導(dǎo)語
神經(jīng)網(wǎng)絡(luò)中的學(xué)習(xí)指從訓(xùn)練數(shù)據(jù)中自動獲取最優(yōu)權(quán)重參數(shù)的過程,這個“最優(yōu)”的定義在不同的應(yīng)用場景下各有不同。
數(shù)據(jù)驅(qū)動
機(jī)器學(xué)習(xí)最關(guān)鍵的部分即如何對待數(shù)據(jù),從數(shù)據(jù)當(dāng)中找到模式,找到規(guī)律,找到答案。
驅(qū)動方法
機(jī)器學(xué)習(xí)的思路是,先從圖像中提取特征量(一般由人設(shè)計(jì)),再用機(jī)器學(xué)習(xí)學(xué)習(xí)特征量的模式,神經(jīng)網(wǎng)絡(luò)的思路是,直接學(xué)習(xí)數(shù)據(jù)本身,特征量也是自主學(xué)習(xí),區(qū)別圖如下(來自書):
神經(jīng)網(wǎng)絡(luò)對所有的問題都用同樣的流程解決,有時也被稱為端到端機(jī)器學(xué)習(xí)(從輸入直接獲得輸出)。
訓(xùn)練/測試數(shù)據(jù)
訓(xùn)練數(shù)據(jù)用來學(xué)習(xí)和找到最優(yōu)的參數(shù),測試數(shù)據(jù)用來評價模型的泛化能力,判斷已訓(xùn)練的模型效果如何。
泛化能力即處理未被觀察過的數(shù)據(jù)(簡單說就是訓(xùn)練數(shù)據(jù)之外的數(shù)據(jù))的能力,機(jī)器學(xué)習(xí)的目標(biāo)就是獲得這種能力。
為了提高泛化能力,對一個模型往往使用多個數(shù)據(jù)集進(jìn)行考察,如果只使用一個數(shù)據(jù)集的話,可能會導(dǎo)致模型在這個數(shù)據(jù)集上表現(xiàn)很好,但是直接拿來檢測新的數(shù)據(jù)時表達(dá)很差,這種只對某個數(shù)據(jù)集過度擬合的狀態(tài)就是過擬合。
損失函數(shù)
為了評估每次訓(xùn)練的結(jié)果,神經(jīng)網(wǎng)絡(luò)采用了損失函數(shù)這一指標(biāo),這個損失函數(shù)有多種取法,書上給了幾種函數(shù)及其實(shí)現(xiàn)方式。
均方誤差
均方誤差是常用的損失函數(shù),表達(dá)式為(僅兩個數(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)絡(luò)輸出, t k t_k tk?為監(jiān)督數(shù)據(jù), k k k為數(shù)據(jù)的維數(shù),一般用one-hot表示(正解標(biāo)簽為1,其他為0)。
python實(shí)現(xiàn):
def mean_squared_error(y,t):return 0.5*np/sum((y-t)**2)
交叉熵誤差
交叉熵誤差表達(dá)式為: E = ? ∑ k t k l n y k E=-\mathop{\sum}\limits_kt_klny_k E=?k∑?tk?lnyk?,也用one-hot標(biāo)簽,python的實(shí)現(xiàn)很簡單,如下:
def cross_entropy_error(y,t):return -np.sum(t*np.log(y))
這個實(shí)現(xiàn)方法看起來沒什么問題,但是如果熟悉 l n x lnx lnx曲線的話就會知道,當(dāng) y y y存在0項(xiàng)時, l n 0 ln0 ln0的值是無窮大,是無法運(yùn)算的,因此需要添加一個微小值防止負(fù)無窮大,更改之后的函數(shù)實(shí)現(xiàn)如下:
def cross_entropy_error(y,t):delta=1e-8return -np.sum(t*np.log(y+delta))
mini-batch
上述給出的實(shí)現(xiàn)和式子都是只針對只有一個值的情況,實(shí)際上,神經(jīng)網(wǎng)絡(luò)是一批一批地處理數(shù)據(jù)的,當(dāng)要求處理大量數(shù)據(jù)時,書上以交叉熵誤差為例,式子表達(dá)為: 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ù)來計(jì)算損失函數(shù)是不適合的,因此mini-batch應(yīng)運(yùn)而生(從全部數(shù)據(jù)隨機(jī)選出一部分,作為整體的近似),可以理解為現(xiàn)實(shí)中的抽樣調(diào)查。
mini-batch的交叉熵實(shí)現(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ù)值微分
書上在這里介紹了導(dǎo)數(shù)(用割線來代替)、求導(dǎo)、偏導(dǎo)等概念,如果有高等數(shù)學(xué)基礎(chǔ),應(yīng)該很容易能理解這些,因此跳過,直接從梯度開始。
梯度
梯度是建立在偏導(dǎo)數(shù)的基礎(chǔ)上的,假設(shè)有變量 x 0 , x 1 , . . . . . . x n x_0,x_1,......x_n x0?,x1?,......xn?,然后有偏導(dǎo)數(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?,由全部變量的偏導(dǎo)數(shù)匯成的向量就是梯度,書上python的實(shí)現(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
梯度指示的方向是各點(diǎn)函數(shù)值減小最多的方向,具體證明屬于高數(shù)知識,略。
梯度法
對于機(jī)器學(xué)習(xí)中的損失函數(shù),我們總是想讓其達(dá)到最小,但是,損失函數(shù)一般很復(fù)雜,不能直接得到最小的取值,此時,梯度法就是很好的解決方案。
但梯度法不一定每次都能取到最值,根據(jù)高等數(shù)學(xué)的指示,梯度所指的方向更類似于極值,而非最值(根據(jù)尋求最大值和最小值分成梯度下降和梯度上升)。
梯度法的思路很簡單,計(jì)算當(dāng)前位置的梯度,然后函數(shù)的取值沿著梯度前進(jìn)一定距離,然后在新的地方求梯度(移動多少書在后面解釋了,和學(xué)習(xí)率有關(guān)),循環(huán)往復(fù)。
梯度法的數(shù)學(xué)表達(dá)式很簡單: x = x ? η ? f ? x x=x-η\frac{\partial f}{\partial x} x=x?η?x?f?, η \eta η是更新量,也就是學(xué)習(xí)率,決定了每次移動的步長。
像學(xué)習(xí)率這種參數(shù)被稱為超參數(shù),因?yàn)樗⒉皇巧窠?jīng)網(wǎng)絡(luò)自動學(xué)習(xí)獲得的,在實(shí)際的訓(xùn)練過程中,往往需要嘗試多個值,學(xué)習(xí)率過小,則迭代次數(shù)過多,訓(xùn)練的時間被無意義浪費(fèi),學(xué)習(xí)率過大,可能步子邁大了扯著蛋,越過了極值或最值。
書上將梯度法式子用python實(shí)現(xiàn):
def gradient_descent(f,init_x,lr=0.01,step_num=100):x=init_x#初始化網(wǎng)絡(luò)參數(shù)for i in range(step_num):grad=numerical_gradient(f,x)#計(jì)算梯度x-=lr*grad#向梯度方向移動return x
神經(jīng)網(wǎng)絡(luò)梯度
在理解了梯度的概念和用法之后,在神經(jīng)網(wǎng)絡(luò)中運(yùn)用梯度就變得很容易了,將結(jié)果矩陣中的每個值對權(quán)重秋偏導(dǎo)即可,以一個 2 × 2 2×2 2×2的矩陣為例,表達(dá)式如下:
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的實(shí)現(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'])#設(shè)置迭代器#操作對象為多維,操作為讀寫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
學(xué)習(xí)算法的實(shí)現(xiàn)
神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)過程大致包括這幾個部分:選出mini-batch,計(jì)算梯度,更新參數(shù),循環(huán)往復(fù),理解起來很簡單,最關(guān)鍵的部分就是前面提到的梯度及其更新的部分。
隨機(jī)梯度下降
隨機(jī)梯度下降的概述很簡單,在原數(shù)據(jù)中隨機(jī)選擇mini batch的數(shù)據(jù),然后再計(jì)算梯度,再根據(jù)梯度下降的方向移動,進(jìn)行下一次運(yùn)算,循環(huán)往復(fù),一般將該函數(shù)命名為SGD。
2層神經(jīng)網(wǎng)絡(luò)實(shí)現(xiàn)
書上實(shí)現(xiàn)了一個兩層神經(jīng)網(wǎng)絡(luò)的類,一些函數(shù)在前面已經(jīng)寫過,在此不再贅述,附帶注釋的代碼如下:
import sys, os
sys.path.append(os.pardir) # 為了導(dǎo)入父目錄的文件而進(jìn)行的設(shè)定
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ù)# 初始化權(quán)重self.params = {}self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)#隨機(jī)初始化權(quán)重,使用高斯分布self.params['b1'] = np.zeros(hidden_size)#偏置初始化為0self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size)#隨機(jī)初始化權(quán)重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):#計(jì)算精準(zhǔn)度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):#進(jìn)行梯度下降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實(shí)現(xiàn)
書上以MNIST數(shù)據(jù)集為基礎(chǔ),用兩層神經(jīng)網(wǎng)絡(luò)進(jìn)行了學(xué)習(xí),修改和加上注釋后,代碼和運(yùn)行結(jié)果如下:
# coding: utf-8
import sys, os
sys.path.append(os.pardir) # 為了導(dǎo)入父目錄的文件而進(jìn)行的設(shè)定
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)絡(luò)類,隱藏層數(shù)據(jù)可以設(shè)置其他合理值iters_num = 10000 # 適當(dāng)設(shè)定循環(huán)的次數(shù)
train_size = x_train.shape[0]
batch_size = 100#抽100個訓(xùn)練
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個隨機(jī)數(shù)x_batch = x_train[batch_mask]#獲得下標(biāo)t_batch = t_train[batch_mask]#獲得下標(biāo)# 計(jì)算梯度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)#計(jì)算損失train_loss_list.append(loss)if i % iter_per_epoch == 0:#每次算完一小批就輸出一次結(jié)果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,每一次都隨機(jī)抽100個進(jìn)行學(xué)習(xí),計(jì)算損失函數(shù)值,輸出一次學(xué)習(xí)結(jié)果,記錄準(zhǔn)確率,可以明顯的看到準(zhǔn)確率在逐漸增加,一般來說,準(zhǔn)確率的增加也有可能意味著過擬合的出現(xiàn),但是可以從圖中看到,隨著學(xué)習(xí)進(jìn)行,訓(xùn)練數(shù)據(jù)和測試數(shù)據(jù)的精度幾乎同步上升,且基本重合,因此可以說沒有出現(xiàn)過擬合。
總結(jié)
神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)中有許多重要的概念,如梯度、損失函數(shù)、梯度下降等,在弄清楚了這些之后就能更好的理解神經(jīng)網(wǎng)絡(luò)的學(xué)習(xí)過程。
參考文獻(xiàn)
- 《深度學(xué)習(xí)——基于Python的理論實(shí)現(xiàn)》