tensorflow2學(xué)習(xí)筆記

第二章神經(jīng)網(wǎng)絡(luò)優(yōu)化

2.1預(yù)備知識(shí)

1.tf.where():條件語(yǔ)句真返回A,條件語(yǔ)句假返回B,tf.where(條件語(yǔ)句,真返回A,假返回B),舉例如下:

a=tf.constant([1,2,3,1,1])
b=tf.constant([0,1,3,4,5])
c=tf.where(tf.greater(a,b), a, b) #若a>b,返回a對(duì)應(yīng)位置的元素,否則返回b對(duì)應(yīng)位置的元素
print("c:",c)

結(jié)果如下:

c: tf.Tensor([1 2 3 4 5], shape=(5,), dtype=int32)
  1. np.random.RandomState.rand():返回一個(gè)[0,1)之間的隨機(jī),np.random.RandomState.rand(維度)#維度為空,返回標(biāo)量,舉例如下:
import numpy as np
rdm=np.random.RandomState(seed=1) #seed=常數(shù)每次生成隨機(jī)數(shù)相同 
a=rdm.rand() # 返回一個(gè)隨機(jī)標(biāo)量
b=rdm.rand(2,3) #返回維度為2行3列隨機(jī)數(shù)矩陣
print("a:",a)
print("b:",b)

結(jié)果如下:

a: 0.417022004702574
b: [[7.20324493e-01 1.14374817e-04 3.02332573e-01]
[1.46755891e-01 9.23385948e-02 1.86260211e-01]]
  1. np.vstack():將兩個(gè)數(shù)組按垂直方向疊加,np.vstack(數(shù)組1,數(shù)組2),舉例如下:
import numpy as np
a = np.array([1,2,3]) 
b = np.array([4,5,6]) 
c = np.vstack((a,b)) 
print("c:\n",c)

結(jié)果如下:

c:
[[1 2 3] 
[4 5 6]]

4.np.mgrid[ ] .ravel( ) np.c_[ ]
np.mgrid[起始值:結(jié)束值:步長(zhǎng),起始值:結(jié)束值:步長(zhǎng) , ... ]
x.ravel() 將x變?yōu)橐痪S數(shù)組,“把.前變量拉直”
np.c_[ ] 使返回的間隔數(shù)值點(diǎn)配對(duì): np.c_[ 數(shù)組1,數(shù)組2, ... ]
舉例如下:

import numpy as np
x, y = np.mgrid [1:3:1, 2:4:0.5] 
grid = np.c_[x.ravel(), y.ravel()] 
print("x:",x)
print("y:",y)
print('grid:\n', grid)

結(jié)果如下:

x = [[1. 1. 1. 1.] [2. 2. 2. 2.]]
y = [[2. 2.5 3. 3.5] [2. 2.5 3. 3.5]]
grid:
[[1. 2. ] [1. 2.5] [1. 3. ] [1. 3.5] [2. 2. ] [2. 2.5] [2. 3. ] [2. 3.5]]

2.2復(fù)雜度和學(xué)習(xí)率

時(shí)間復(fù)雜度:模型的運(yùn)算次數(shù)
空間復(fù)雜度:總參數(shù)量 + 各層輸出特征圖。
指數(shù)衰減學(xué)習(xí)率
可以先用較大的學(xué)習(xí)率,快速得到較優(yōu)解,然后逐步減小學(xué)習(xí)率,使 模型在訓(xùn)練后期穩(wěn)定。
指數(shù)衰減學(xué)習(xí)率 = 初始學(xué)習(xí)率 * 學(xué)習(xí)率衰減率( 當(dāng)前輪數(shù) / 多少輪衰減一次 ),實(shí)現(xiàn)代碼如下:

import tensorflow as tf
w = tf.Variable(tf.constant(5, dtype=tf.float32))
epoch = 40
LR_BASE = 0.2  # 最初學(xué)習(xí)率
LR_DECAY = 0.99  # 學(xué)習(xí)率衰減率
LR_STEP = 1  # 喂入多少輪BATCH_SIZE后,更新一次學(xué)習(xí)率

for epoch in range(epoch):  # for epoch 定義頂層循環(huán),表示對(duì)數(shù)據(jù)集循環(huán)epoch次,此例數(shù)據(jù)集數(shù)據(jù)僅有1個(gè)w,初始化時(shí)候constant賦值為5,循環(huán)100次迭代。
   lr = LR_BASE * LR_DECAY ** (epoch / LR_STEP)
   with tf.GradientTape() as tape:  # with結(jié)構(gòu)到grads框起了梯度的計(jì)算過(guò)程。
       loss = tf.square(w + 1)
   grads = tape.gradient(loss, w)  # .gradient函數(shù)告知誰(shuí)對(duì)誰(shuí)求導(dǎo)
   w.assign_sub(lr * grads)  # .assign_sub 對(duì)變量做自減 即:w -= lr*grads 即 w = w - lr*grads
   print("After %s epoch,w is %f,loss is %f,lr is %f" % (epoch, w.numpy(), loss, lr))

2.3激活函數(shù)

激活函數(shù)是用來(lái)加入非線性因素的,因?yàn)榫€性模型的表達(dá)能力不夠。引入非線性激活函數(shù),可使深層神經(jīng)網(wǎng)絡(luò)的表達(dá)能力更加強(qiáng)大。
優(yōu)秀的激活函數(shù)應(yīng)滿足:
非線性: 激活函數(shù)非線性時(shí),多層神經(jīng)網(wǎng)絡(luò)可逼近所有函數(shù)
可微性: 優(yōu)化器大多用梯度下降更新參數(shù)
單調(diào)性: 當(dāng)激活函數(shù)是單調(diào)的,能保證單層網(wǎng)絡(luò)的損失函數(shù)是凸函數(shù)
近似恒等性:當(dāng)參數(shù)初始化為隨機(jī)小值時(shí),神經(jīng)網(wǎng)絡(luò)更穩(wěn)定
激活函數(shù)輸出值的范圍: 激活函數(shù)輸出為有限值時(shí),基于梯度的優(yōu)化方法更穩(wěn)定 激活函數(shù)輸出為無(wú)限值時(shí),建議調(diào)小學(xué)習(xí)率
下面給出幾種常用的激活函數(shù)
一.sigmoid
f(x)=\frac{1}{1+e^{-x}}

函數(shù)圖像

導(dǎo)數(shù)圖像

使用:tf.nn.sigmoid(x)
優(yōu)點(diǎn):
1.輸出映射在(0,1)之間,單調(diào)連續(xù),輸出范圍有限,優(yōu)化穩(wěn)定,可用作輸出層;
2.求導(dǎo)容易。
缺點(diǎn):
1.易造成梯度消失;
2.輸出非0均值,收斂慢;
3.冪運(yùn)算復(fù)雜,訓(xùn)練時(shí)間長(zhǎng)。
igmoid函數(shù)可應(yīng)用在訓(xùn)練過(guò)程中。然而,當(dāng)處理分類問(wèn)題作出輸出時(shí),sigmoid卻無(wú)能為力。簡(jiǎn) 單地說(shuō),sigmoid函數(shù)只能處理兩個(gè)類,不適用于多分類問(wèn)題。而softmax可以有效解決這個(gè)問(wèn)題,并且softmax函數(shù)大都運(yùn)用在神經(jīng)網(wǎng)路中的最后一層網(wǎng)絡(luò)中,使得值區(qū)間在(0,1)之間,而不是二分類的。
二.tanh

函數(shù)圖像

導(dǎo)數(shù)圖像

使用:tf.math.tanh(x)
優(yōu)點(diǎn):

  1. 比sigmoid函數(shù)收斂速度更快。
    2.相比sigmoid函數(shù),其輸出以0為中心。
    缺點(diǎn)
    1.易造成梯度消失;
    2.冪運(yùn)算復(fù)雜,訓(xùn)練時(shí)間長(zhǎng)。
    三.ReLU
    f(x)=max(0,x)
    函數(shù)圖像

導(dǎo)數(shù)圖像

使用:tf.nn.relu
優(yōu)點(diǎn):
1.解決了梯度消失問(wèn)題(在正區(qū)間);
2.只需判斷輸入是否大于0,計(jì)算速度快;
3.收斂速度遠(yuǎn)快于sigmoid和tanh,因?yàn)閟igmoid和tanh涉及很多expensive的操作;
4.提供了神經(jīng)網(wǎng)絡(luò)的稀疏表達(dá)能力。
缺點(diǎn):
1.輸出非0均值,收斂慢;
2.Dead ReLU問(wèn)題:某些神經(jīng)元可能永遠(yuǎn)不會(huì)被激活,導(dǎo)致相應(yīng)的參數(shù)永遠(yuǎn)不能被更新。
四.Leaky ReLU

函數(shù)圖像

導(dǎo)數(shù)圖像

使用:tf.nn.leaky_relu
理論上來(lái)講,Leaky ReLU有ReLU的所有優(yōu)點(diǎn),外加不會(huì)有Dead ReLU問(wèn)題,但是在實(shí)際操作當(dāng)中,并沒(méi)有完全證明Leaky ReLU總是好于ReLU。
五.softmax
使用:tf.nn.softmax
對(duì)神經(jīng)網(wǎng)絡(luò)全連接層輸出進(jìn)行變換,使其服從概率分布,即每個(gè)值都位于[0,1]區(qū)間且和為1。

2.4損失函數(shù)

神經(jīng)網(wǎng)絡(luò)模型的效果及優(yōu)化的目標(biāo)是通過(guò)損失函數(shù)來(lái)定義的。
1.均方誤差損失函數(shù):

        loss_mse = tf.reduce_mean(tf.square(y_ - y))

2.自定義損失函數(shù):

        loss = tf.reduce_sum(tf.where(tf.greater(y, y_), (y - y_) * COST, (y_ - y) * PROFIT))

3.交叉熵?fù)p失函數(shù):
交叉熵(Cross Entropy)表征兩個(gè)概率分布之間的距離,交叉熵越小說(shuō)明二者分布越接近,是分類問(wèn)題中使用較廣泛的損失函數(shù)。

import tensorflow as tf

loss_ce1 = tf.losses.categorical_crossentropy([1, 0], [0.6, 0.4])
loss_ce2 = tf.losses.categorical_crossentropy([1, 0], [0.8, 0.2])
print("loss_ce1:", loss_ce1)
print("loss_ce2:", loss_ce2)

softmax與交叉熵相結(jié)合

import tensorflow as tf
import numpy as np

y_ = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1], [1, 0, 0], [0, 1, 0]])
y = np.array([[12, 3, 2], [3, 10, 1], [1, 2, 5], [4, 6.5, 1.2], [3, 6, 1]])
y_pro = tf.nn.softmax(y)
loss_ce1 = tf.losses.categorical_crossentropy(y_,y_pro)
loss_ce2 = tf.nn.softmax_cross_entropy_with_logits(y_, y)

print('分步計(jì)算的結(jié)果:\n', loss_ce1)
print('結(jié)合計(jì)算的結(jié)果:\n', loss_ce2)

2.5緩解過(guò)擬合

欠擬合的解決方法:
增加輸入特征項(xiàng)
增加網(wǎng)絡(luò)參數(shù)
減少正則化參數(shù)
過(guò)擬合的解決方法:
數(shù)據(jù)清洗
增大訓(xùn)練集
采用正則化
增大正則化參數(shù)

2.6優(yōu)化器

定義:待優(yōu)化參數(shù)w,損失函數(shù)lose,學(xué)習(xí)率lr,每次迭代一個(gè)batch,t表示當(dāng)前batch迭代的總次數(shù):

步驟

一。SGD(無(wú)momentum),常用的梯度下降法
SGD

代碼實(shí)現(xiàn):

# 利用鳶尾花數(shù)據(jù)集,實(shí)現(xiàn)前向傳播、反向傳播,可視化loss曲線

# 導(dǎo)入所需模塊
import tensorflow as tf
from sklearn import datasets
from matplotlib import pyplot as plt
import numpy as np
import time  ##1##

# 導(dǎo)入數(shù)據(jù),分別為輸入特征和標(biāo)簽
x_data = datasets.load_iris().data
y_data = datasets.load_iris().target

# 隨機(jī)打亂數(shù)據(jù)(因?yàn)樵紨?shù)據(jù)是順序的,順序不打亂會(huì)影響準(zhǔn)確率)
# seed: 隨機(jī)數(shù)種子,是一個(gè)整數(shù),當(dāng)設(shè)置之后,每次生成的隨機(jī)數(shù)都一樣(為方便教學(xué),以保每位同學(xué)結(jié)果一致)
np.random.seed(116)  # 使用相同的seed,保證輸入特征和標(biāo)簽一一對(duì)應(yīng)
np.random.shuffle(x_data)
np.random.seed(116)
np.random.shuffle(y_data)
tf.random.set_seed(116)

# 將打亂后的數(shù)據(jù)集分割為訓(xùn)練集和測(cè)試集,訓(xùn)練集為前120行,測(cè)試集為后30行
x_train = x_data[:-30]
y_train = y_data[:-30]
x_test = x_data[-30:]
y_test = y_data[-30:]

# 轉(zhuǎn)換x的數(shù)據(jù)類型,否則后面矩陣相乘時(shí)會(huì)因數(shù)據(jù)類型不一致報(bào)錯(cuò)
x_train = tf.cast(x_train, tf.float32)
x_test = tf.cast(x_test, tf.float32)

# from_tensor_slices函數(shù)使輸入特征和標(biāo)簽值一一對(duì)應(yīng)。(把數(shù)據(jù)集分批次,每個(gè)批次batch組數(shù)據(jù))
train_db = tf.data.Dataset.from_tensor_slices((x_train, y_train)).batch(32)
test_db = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(32)

# 生成神經(jīng)網(wǎng)絡(luò)的參數(shù),4個(gè)輸入特征故,輸入層為4個(gè)輸入節(jié)點(diǎn);因?yàn)?分類,故輸出層為3個(gè)神經(jīng)元
# 用tf.Variable()標(biāo)記參數(shù)可訓(xùn)練
# 使用seed使每次生成的隨機(jī)數(shù)相同(方便教學(xué),使大家結(jié)果都一致,在現(xiàn)實(shí)使用時(shí)不寫(xiě)seed)
w1 = tf.Variable(tf.random.truncated_normal([4, 3], stddev=0.1, seed=1))
b1 = tf.Variable(tf.random.truncated_normal([3], stddev=0.1, seed=1))

lr = 0.1  # 學(xué)習(xí)率為0.1
train_loss_results = []  # 將每輪的loss記錄在此列表中,為后續(xù)畫(huà)loss曲線提供數(shù)據(jù)
test_acc = []  # 將每輪的acc記錄在此列表中,為后續(xù)畫(huà)acc曲線提供數(shù)據(jù)
epoch = 500  # 循環(huán)500輪
loss_all = 0  # 每輪分4個(gè)step,loss_all記錄四個(gè)step生成的4個(gè)loss的和

# 訓(xùn)練部分
now_time = time.time()  ##2##
for epoch in range(epoch):  # 數(shù)據(jù)集級(jí)別的循環(huán),每個(gè)epoch循環(huán)一次數(shù)據(jù)集
    for step, (x_train, y_train) in enumerate(train_db):  # batch級(jí)別的循環(huán) ,每個(gè)step循環(huán)一個(gè)batch
        with tf.GradientTape() as tape:  # with結(jié)構(gòu)記錄梯度信息
            y = tf.matmul(x_train, w1) + b1  # 神經(jīng)網(wǎng)絡(luò)乘加運(yùn)算
            y = tf.nn.softmax(y)  # 使輸出y符合概率分布(此操作后與獨(dú)熱碼同量級(jí),可相減求loss)
            y_ = tf.one_hot(y_train, depth=3)  # 將標(biāo)簽值轉(zhuǎn)換為獨(dú)熱碼格式,方便計(jì)算loss和accuracy
            loss = tf.reduce_mean(tf.square(y_ - y))  # 采用均方誤差損失函數(shù)mse = mean(sum(y-out)^2)
            loss_all += loss.numpy()  # 將每個(gè)step計(jì)算出的loss累加,為后續(xù)求loss平均值提供數(shù)據(jù),這樣計(jì)算的loss更準(zhǔn)確
        # 計(jì)算loss對(duì)各個(gè)參數(shù)的梯度
        grads = tape.gradient(loss, [w1, b1])

        # 實(shí)現(xiàn)梯度更新 w1 = w1 - lr * w1_grad    b = b - lr * b_grad
        w1.assign_sub(lr * grads[0])  # 參數(shù)w1自更新
        b1.assign_sub(lr * grads[1])  # 參數(shù)b自更新

    # 每個(gè)epoch,打印loss信息
    print("Epoch {}, loss: {}".format(epoch, loss_all / 4))
    train_loss_results.append(loss_all / 4)  # 將4個(gè)step的loss求平均記錄在此變量中
    loss_all = 0  # loss_all歸零,為記錄下一個(gè)epoch的loss做準(zhǔn)備

    # 測(cè)試部分
    # total_correct為預(yù)測(cè)對(duì)的樣本個(gè)數(shù), total_number為測(cè)試的總樣本數(shù),將這兩個(gè)變量都初始化為0
    total_correct, total_number = 0, 0
    for x_test, y_test in test_db:
        # 使用更新后的參數(shù)進(jìn)行預(yù)測(cè)
        y = tf.matmul(x_test, w1) + b1
        y = tf.nn.softmax(y)
        pred = tf.argmax(y, axis=1)  # 返回y中最大值的索引,即預(yù)測(cè)的分類
        # 將pred轉(zhuǎn)換為y_test的數(shù)據(jù)類型
        pred = tf.cast(pred, dtype=y_test.dtype)
        # 若分類正確,則correct=1,否則為0,將bool型的結(jié)果轉(zhuǎn)換為int型
        correct = tf.cast(tf.equal(pred, y_test), dtype=tf.int32)
        # 將每個(gè)batch的correct數(shù)加起來(lái)
        correct = tf.reduce_sum(correct)
        # 將所有batch中的correct數(shù)加起來(lái)
        total_correct += int(correct)
        # total_number為測(cè)試的總樣本數(shù),也就是x_test的行數(shù),shape[0]返回變量的行數(shù)
        total_number += x_test.shape[0]
    # 總的準(zhǔn)確率等于total_correct/total_number
    acc = total_correct / total_number
    test_acc.append(acc)
    print("Test_acc:", acc)
    print("--------------------------")
total_time = time.time() - now_time  ##3##
print("total_time", total_time)  ##4##

# 繪制 loss 曲線
plt.title('Loss Function Curve')  # 圖片標(biāo)題
plt.xlabel('Epoch')  # x軸變量名稱
plt.ylabel('Loss')  # y軸變量名稱
plt.plot(train_loss_results, label="$Loss$")  # 逐點(diǎn)畫(huà)出trian_loss_results值并連線,連線圖標(biāo)是Loss
plt.legend()  # 畫(huà)出曲線圖標(biāo)
plt.show()  # 畫(huà)出圖像

# 繪制 Accuracy 曲線
plt.title('Acc Curve')  # 圖片標(biāo)題
plt.xlabel('Epoch')  # x軸變量名稱
plt.ylabel('Acc')  # y軸變量名稱
plt.plot(test_acc, label="$Accuracy$")  # 逐點(diǎn)畫(huà)出test_acc值并連線,連線圖標(biāo)是Accuracy
plt.legend()
plt.show()

SGD:

w1.assign_sub(learning_rate * grads[0])
b1.assign_sub(learning_rate * grads[1])

二:SGDM,在SGD基礎(chǔ)上增加一階動(dòng)量。


SGDM

SGDM:

m_w, m_b = 0, 0
beta = 0.9
m_w = beta * m_w + (1 - beta) * grads[0]
m_b = beta * m_b + (1 - beta) * grads[1]
w1.assign_sub(lr * m_w)
b1.assign_sub(lr * m_b)

三:Adagrad,在SGD基礎(chǔ)上增加二階動(dòng)量


Adagrad

Adagrad:

v_w += tf.square(grads[0])
v_b += tf.square(grads[1]) 
w1.assign_sub(learning_rate * grads[0] / tf.sqrt(v_w)) 
b1.assign_sub(learning_rate * grads[1] / tf.sqrt(v_b))

四:RMSProp,在SGD基礎(chǔ)上增加二階動(dòng)量


RMSProp

RMSProp:

beta = 0.9
v_w = beta * v_w + (1 - beta) * tf.square(grads[0]) 
v_b = beta * v_b + (1 - beta) * tf.square(grads[1]) 
w1.assign_sub(learning_rate * grads[0] / tf.sqrt(v_w)) 
b1.assign_sub(learning_rate * grads[1] / tf.sqrt(v_b))

五:Adam,同時(shí)結(jié)合SGDM一階動(dòng)量和RMSProp二階動(dòng)量


Adam

Adam:

m_w, m_b = 0, 0
v_w, v_b = 0, 0
beta1, beta2 = 0.9, 0.999
delta_w, delta_b = 0, 0
global_step = 0
m_w = beta1 * m_w + (1 - beta1) * grads[0]
m_b = beta1 * m_b + (1 - beta1) * grads[1]
v_w = beta2 * v_w + (1 - beta2) * tf.square(grads[0])
v_b = beta2 * v_b + (1 - beta2) * tf.square(grads[1])

m_w_correction = m_w / (1 - tf.pow(beta1, int(global_step)))
m_b_correction = m_b / (1 - tf.pow(beta1, int(global_step)))
v_w_correction = v_w / (1 - tf.pow(beta2, int(global_step)))
v_b_correction = v_b / (1 - tf.pow(beta2, int(global_step)))

w1.assign_sub(lr * m_w_correction / tf.sqrt(v_w_correction))
b1.assign_sub(lr * m_b_correction / tf.sqrt(v_b_correction))
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容