1.1.4 反向傳遞與優化器
1.1.4反向傳遞與優化器
深度學習訓練過程如圖1-7所示。
在深度學習模型裡面,經常需要使用梯度演算法,針對損失函數的反饋不斷調整各層的參數,使得損失函數最小化。在訓練階段,真實值就是樣本對應的真實標籤,預測值就是機器學習模型預測的標籤值,這些都是明確的,所以損失函數是可以定義和計算的。機器學習模型訓練的過程就是不斷調整參數追求損失函數最小的過程。梯度可以理解為多元函數的指定點上升的坡度,假設多元函數可以表示為f(x,y),那麼對應的梯度的定義為:
可見梯度可以用偏導數來定義,通常損失函數就是這個多元函數,特徵向量就可以看成這個多元函數的某個點。在訓練過程中,針對參數的調整可以使用梯度和學習率來定義,其中學習率也叫作學習步長,物理含義就是變數在梯度方向上移動的長度,學習率是一個非常重要的參數,學習率過大會導致損失函數的震蕩難以收斂,過小會導致計算緩慢,目前還沒有很成熟的理論來推倒最合適的學習率,經驗值是0.001~0.1。以表示學習率,那麼迭代更新參數x的方法為:
圖1-7深度學習訓練過程
在求函數的最大值時,我們會向梯度向上的方向移動,使用加號,也稱為梯度向上演算法。如果我們想求函數的最小值,則需要向梯度向下的方向移動,使用減號,也稱為梯度下降演算法,比如求損失函數最小值時,對應迭代求解的方法為:
我們通過一個非常簡單的例子演示這個過程,假設我們只有一個變數x,對應的損失函數定義為:
根據梯度的定義,可以獲得對應的梯度為:
我們隨機初始化x,將學習率設置為0.1,整個過程如下:
defdemo():
importrandom
a=0.1
x=random.randint(1,10)
y=x*x+2
index=1
whileindex0.01:
y=x*x+2
print"batch={}x={}y={}".format(index,x,y)
x=x-2*x*a
index+=1
整個迭代過程最多100步,由於我們預先知道函數的最小值為2,所以如果當計算獲得的函數值非常接近2,我們也可以提前退出迭代過程,比如絕對值相差不超過0.01。最後果然沒讓我們失望,在迭代20次后就找到了接近理論上的最小點:
batch=14x=0.329853488333y=2.10880332377
batch=15x=0.263882790666y=2.06963412721
batch=16x=0.211106232533y=2.04456584141
batch=17x=0.168884986026y=2.02852213851
batch=18x=0.135107988821y=2.01825416864
batch=19x=0.108086391057y=2.01168266793
batch=20x=0.0864691128455y=2.00747690748
Keras裡面提供相應的工具返回loss函數關於variables的梯度,variables為張量變數的列表,這裡的loss函數即損失函數:
fromkerasimportbackendasK
k.gradients(loss,variables)
Keras也提供了function用於實例化一個Keras函數,inputs是輸入張量的列表,其元素為佔位符或張量變數,outputs為輸出張量的列表:
k.function(inputs,outputs,updates=[])
常用的優化器包括SGD、RMSprop和Adam。
1.SGD
SGD即隨機梯度下降法,是最基礎的優化方法。普通的訓練方法需要重複不斷地把整套數據放入神經網路中訓練,這會消耗大量計算資源。SGD則會把數據拆分后再分批不斷地放入神經網路中來計算。每次使用批數據,雖然不能反映整體數據的情況,不過卻在很大程度上加速了神經網路的訓練過程,而且也不會丟失太多準確率。
SGD支持動量參數,支持學習衰減率,函數的定義如下:
keras.optimizers.SGD(lr=0.01,momentum=0.0,decay=0.0,nesterov=False)
其中比較重要的參數如下:
lr:學習率。
momentum:動量參數。
decay:每次更新后的學習率衰減值。
2.RMSprop
RMSprop是面對遞歸神經網路時的一個良好選擇,函數的定義如下:
keras.optimizers.RMSprop(lr=0.001,rho=0.9,epsilon=1e-06)
其中比較重要的參數如下:
lr:學習率。
epsilon:大於或等於0的小浮點數,防止除0錯誤。
3.Adam
Adam是一種可以替代SGD的一階優化演算法,它能基於訓練數據迭代地更新神經網路權重,是目前最受歡迎的優化演算法之一,定義如下:
keras.optimizers.Adam(lr=0.001,beta_1=0.9,beta_2=0.999,epsilon=1e-08)
其中比較重要的參數如下:
lr:學習率。
epsilon:大於或等於0的小浮點數,防止除0錯誤。
下面我們以迭代生成對抗樣本的例子來感性認識一下不同優化器的計算收斂速度,代碼路徑為:
https://github.com/duoergun0729/adversarial_examples/code/1-case1-pytorch.ipynb
首先,定義全局變數,其中adam_original_loss、sdg_original_loss和RMSprop_original_loss分別代表迭代過程中不同優化演算法對應的損失函數的值:
sdg_original_loss=[]
RMSprop_original_loss=[]
epoch_range=[]
載入測試圖片,並縮放到長和寬均為224:
#獲取計算設備,默認是CPU
device=torch.device("cuda"iftorch.cuda.is_available()else"cpu")
#圖像載入以及預處理
image_path="../picture/cropped_panda.jpg"
orig=cv2.imread(image_path)[...,::-1]
orig=cv2.resize(orig,(224,224))
img=orig.copy().astype(np.float32)
對圖像數據進行標準化處理,由於攻擊的圖像分類模型是基於ImageNet2012數據集進行預訓練的,因此需要使用ImageNet2012數據集特有的均值mean和標準差std進行標準化:
mean=[0.485,0.456,0.406]
std=[0.229,0.224,0.225]
img/=255.0
img=(img-mean)/std
img=img.transpose(2,0,1)
img=np.expand_dims(img,axis=0)
img=Variable(torch.from_numpy(img).to(device).float())
實例化alexnet模型並載入預訓練的參數。在使用迭代優化的過程中,整個模型的參數不變化,反向傳遞僅調整原始圖像的內容:
#使用預測模式主要影響dropout和BN層的行為
model=models.alexnet(pretrained=True).to(device).eval()
#獲取分類標籤
label=np.argmax(model(img).data.cpu().numpy())
print("label={}".format(label))
#圖像數據梯度可以獲取
img.requires_grad=True
#設置為不保存梯度值,自然也無法修改
forparaminmodel.parameters():
param.requires_grad=False
使用定向攻擊,攻擊目標的標籤值為288,最大迭代次數為100:
loss_func=torch.nn.CrossEntropyLoss()
epochs=100
target=288
target=Variable(torch.Tensor([float(target)]).to(device).long())
迭代優化的計算過程中,根據預測結果與定向攻擊目標計算損失值,並通過手工調用反向傳遞過程,更新原始圖像:
forepochinrange(epochs):
#梯度清零
optimizer.zero_grad()
#forward+backward
output=model(img)
loss=loss_func(output,target)
label=np.argmax(output.data.cpu().numpy())
adam_original_loss+=[loss]
epoch_range+=[epoch]
#手工調用反向傳遞計算,更新原始圖像
loss.backward()
optimizer.step()
分別實例化不同的優化器,記錄100次迭代優化過程中損失值的變化,如圖1-8所示,當使用相同的學習速率對同一圖片進行迭代優化生成定向攻擊樣本時,RMSprop和Adam明顯快於SGD:
fig,ax=plt.subplots()
ax.plot(np.array(epoch_range),np.array(adam_original_loss),'b--',
label='Adam')
ax.plot(np.array(epoch_range),np.array(RMSprop_original_loss),'b-',
label='RMSprop')
ax.plot(np.array(epoch_range),np.array(sdg_original_loss),'b:',
label='SGD')
legend=ax.legend(loc='best',shadow=True,fontsize='large')
legend.get_frame().set_facecolor('#FFFFFF')
plt.xlabel('IterationStep')
plt.ylabel('Loss')
plt.show()
圖1-8相同條件下不同優化演算法的收斂速度