NLP面試題總結(包含8種優化器簡介).03

目錄

part 1.

1. 介紹一下幾種優化器

1.1 SGD(Stochastic Gradient Descent) 

1.2 BGD(Batch Gradient Descent)

1.3 MBGD(Mini-Batch Gradient Descent)

1.4 Momentum

1.5 Adagrad(Adaptive gradient algorithm)

1.6 Adadelta

1.7 RMSprop

1.8 Adam(Adaptive Moment Estimation)

2. LSTM裏面有哪些門,爲什麼用這些門?

3.LSTM裏面爲什麼有些激活函數用sigmoid,有些用tanh?

4. Bert中的位置向量作用是什麼?有哪些生成方式?

part 2.

1. 淺copy和深copy的概念

2. Python中的self關鍵字

3. Python中類的繼承

4. 完全二叉樹的概念

5. 單鏈表與順序表的區別

6. 給出二叉樹的前序遍歷(preorder)和中序遍歷(inorder),重建該二叉樹:

7.反轉一個鏈表,並返回頭結點


part 1.

1. 介紹一下幾種優化器

在機器學習與深度學習中,主要應用於梯度下降。如,傳統的優化器主要結合數據集,通過變化單次循環所採用的數據量的大小對梯度下降進行控制;非傳統的優化器則進一步集合數據集的特點和模型的訓練時間,以不同的形式變化梯度下降的學習率。

常見的優化器有SGD、BGD、MBGD、Momentum、Adagrad、RMSprop、Adam。

梯度下降的原理:

                                                                 \large \theta ^{n+1}=\theta^n +\eta \nabla\theta J(\theta)

其中,\eta 爲學習率,\theta ^n 爲更新前的參數,\theta ^{n+1} 爲更新後的參數,\nabla\theta J(\theta) 爲當前參數的導數。

1.1 SGD(Stochastic Gradient Descent) 

SGD隨機梯度下降參數更新原則:單條數據就可對參數進行一次更新。

優點:參數更新速度快。

缺點:由於每次參數更新時採用的數據量小,造成梯度更新時震盪幅度大,但大多數情況是向着梯度減小的方向。

for n in n_epochs:
    for data in train_dataset:
        #對參數進行一次更新

1.2 BGD(Batch Gradient Descent)

BGD批量梯度下降參數更新原則:所有數據都參與梯度的每一次更新(一個batch中每個參數需要更新的梯度取均值作爲更新值)。

優點:由於每次參數更新時採用的數據量大,所以梯度更新時比較平滑。

缺點:由於參數更新時需要的數據量大,造成參數更新速度慢。

for n in n_epochs:
    for data in train_dataset:
        #計算每個參數所有梯度的均值作爲一次更新的梯度,對參數進行一次更新

1.3 MBGD(Mini-Batch Gradient Descent)

MBGD小批量梯度參數更新原則:只有所有數據的一部分進行參數的更新。

優點:相比於SGD,由於參與梯度更新的數據量大,所以梯度更新時相對平滑;相比於BGD,參與梯度更新的數據量小,參數更新速度更快一些。

缺點:沒有考慮到數據集的稀疏度和模型的訓練時間對參數更新的影響。

n=0
while n <= n_epochs:
    for minibatch_traindataset in train_dataset:
        if n <= n_epochs:
            n+=1
            for i in minibatch_traindataset:
                #計算每個參數更新的梯度的均值作爲一次更新的梯度進行參數更新
          else:break
    

1.4 Momentum

Momentum解決的問題是:SGD梯度下降時的震盪問題。

Momentum參數更新原則:通過引入 \gamma v_n ,加速SGD,並且抑制震盪。(MBGD是通過小批量數據來平滑梯度更新,方法不同而已)

更新公式:

\large v_{n+1}=\gamma v_n +\eta \theta J(\theta) 

\large \theta^{n+1}=\theta^n -v_{n+1}

超參數設定值:\gamma 一般取 0.9 左右。

優點:通過加入 \gamma v_n ,使得梯度方向不變的維度上速度變快,梯度方向改變的維度上更新速度變慢,這樣就可以加快收斂並減小震盪。

缺點:梯度方向不變時,參數更新速度會越來越快,但是在梯度方向改變時,梯度更新速度不能及時減小導致適應性差。

1.5 Adagrad(Adaptive gradient algorithm)

Adagrad解決的問題:解決不能根據參數重要性而對不同參數進行不同程度更新問題。

Adagrad參數更新原則:對低頻的參數做較大的更新,對高頻的參數做較小的更新。

更新公式:

\large \theta_{t+1,i}=\theta_{t,i}- {{\eta}\over{\sqrt{G_{t,ii}}} }g_{t,i}

其中,g爲t時刻 \large \theta_i 的梯度; \large g_{t,i}=\nabla \theta J(\theta_i) ,\large G_{t,ii} 是個對角矩陣,\large (i,i) 元素就是t時刻參數 \large \theta_i 的梯度平方和

如果是普通的SGD,那麼 \large \theta_i 在每一時刻的梯度更新公式爲:

\large \theta_{t+1,i}=\theta_{t,i} - \eta g_{t,i} ,超參數 \large \eta 選取0.01 。

優點:減少了學習率的手動調節。

缺點:分母會不斷積累,導致學習率會收縮並最終變得很小。

1.6 Adadelta

Adadelta解決的問題:解決Adagrad分母不斷積累,導致學習率收縮變得非常小的問題。

Adadelta參數更新原則:和Adagrad相比,就是分母的\large G_{t,ii} 換成了過去的梯度平方的衰減平均值,指數衰減平均值。

\large \Delta \theta_t=- {\eta \over{ \sqrt{E[g^2]_t} +\epsilon }}g_{t.}

這個分母相當於梯度的均方根(root  mean squared,RMS),在數據統計分析中,將所有值平方求和,求其均值,再開平方,就得到均方根值,所以可以用RMS簡寫:

\large \Delta \theta_t=- {\eta \over {RMS[g]_{t.}}}g_{t.}

其中,E的計算公式如下, t時刻的依賴於前一時刻的平均和當前的梯度:

\large E[g^2]_t=\gamma E[g^2]_{t-1} +(1+\gamma )g^2_{t.}

此外,還將學習率 \large \eta 換成了\large RMSE(\Delta \theta) ,這樣甚至都不需要提前設定學習率,更新公式爲:

\large \Delta \theta_t=- {{RMS[\Delta \theta]} \over {RMS[g]_t}} g_{t.}

\large \theta_{t+1}=\theta_t + \Delta \theta_{t.}   ,超參數 \large \gamma 一般設定爲0.9 。

優點:減少了學習率的手動調節。

1.7 RMSprop

RMSprop解決的問題:RMSProp和Adadelta 都是爲了解決Adagrad學習率急劇下降問題。

參數更新原則:RMSprop與Adadelta第一種形式相同:使用的是指數加權平均,旨在消除梯度下降中的擺動,與Momentum的效果一樣,某一維度的導數比較大,則指數加權平均就大,某一維度的導數比較小,則其指數加權平均就小,這樣就保證了各維度導數都在一個量級,進而減少了擺動,允許使用一個較大的學習率 \large \eta 。

更新公式:

\large E[g^2]_t=0.9E[g^2]_{t-1} +0.1g^2_t

\large \theta_{t+1} =\theta_t -{\eta \over {\sqrt{E[g^2_t] + \epsilon } }}g_{t.}

1.8 Adam(Adaptive Moment Estimation)

Adam解決的問題:這個算法是另一種計算每個參數的自適應學習率的方法。

Adam參數更新原則:相當於RMSprop + Momentum。除了像Adadelt 和RMSprop一樣存儲了過去梯度的平方 \large v_t 的指數衰減平均值,也像Momentum一樣保持了過去梯度 \large m_t 的指數衰減平均值:

\large m_t=\beta _1m_{t-1} +(1-\beta_1)g_{t.}

\large v_t=\beta_2v_{t-1} + (1-\beta_2)g^2_{t.}

\large \widehat{m}_t ={m_t \over {1-\beta^t_1}}

\large \widehat{v}_t={v_t \over {1-\beta_2^t}}

梯度更新公式:

\large \theta_{t+1}=\theta_t - {\eta \over{\sqrt {\widehat{v}_t}} +\varepsilon }\widehat{m}_{t.}

2. LSTM裏面有哪些門,爲什麼用這些門?

  • 遺忘門:將細胞狀態中的信息選擇性遺忘。
  • 輸入門:將新的信息選擇性的記錄到細胞狀態中。
  • 輸出門:當前細胞的信息保存到隱層中。

3.LSTM裏面爲什麼有些激活函數用sigmoid,有些用tanh?

  • sigmoid用在LSTM的三個門裏,作用在前一個狀態和輸出上,主要功能是讓神經元對過去輸入和前一個狀態的信息選擇性輸入,它的輸出範圍是(0,1),0是丟棄,1是保留;
  • tanh是用在後一個狀態和輸出上,是對數據的處理。它的輸出範圍是(-1,1),功能是對哪些輸出信息放大,對哪些輸出信息縮小。

4. Bert中的位置向量作用是什麼?有哪些生成方式?

1.位置向量的作用是表示每個單詞token 距離目標單詞的遠近,每個token 的位置向量表示形式可以是one-hot形式,將它們合起來就是一個隨機矩陣;也可以說隨機id形式。

2.論文中Bert的位置向量是通過餘弦函數生成,位置向量的維度和word embedding維度相同都是512維,其中位置向量的前一半通過正弦生成,後一半(後面256的長度)是通過餘弦生成的。

 

part 2.

1. 淺copy和深copy的概念

在Python中對象的賦值其實就是對象的引用。當創建一個對象,並把它賦值給另外一個對象時,Python並沒有拷貝這個對象,只是拷貝了這個對象的引用。

淺copy:拷貝了最外圍的對象本身,內部的元素只是拷貝了一個引用;也就是把對象複製一遍,但是對象中引用的其他對象並不複製。比如,對於嵌套數組的淺拷貝,僅僅是拷貝外圍數組元素對象,內部數組對象並不拷貝,僅拷貝引用。

深copy:外圍和內部元素都進行拷貝對象本身,而不是引用。

# ---拷貝---
alist=[1, 2, 3, ['a', 'b']]
b=alist
print( b)
[1, 2, 3, ['a', 'b']]

alist.append(5)
print (alist)
print(b)
[1, 2, 3, ['a', 'b'], 5]
[1, 2, 3, ['a', 'b'], 5]

#---淺拷貝---沒有拷貝子對象,所以原始數據改變,子對象會改變
# 總的來說就是,淺拷貝之後,僅僅子對象與原始數據有關係
import copy
alist=[1, 2, 3, ['a', 'b'], 5]
c=copy.copy(alist)
alist.append(5)
print(alist,c)
#輸出
[1, 2, 3, ['a', 'b'], 5]
[1, 2, 3, ['a', 'b']]
alist[3].append('ccc')
print(alist,c)
#輸出
[1, 2, 3, ['a', 'b', 'cccc'], 5]
[1, 2, 3, ['a', 'b', 'cccc']] 裏面的子對象被改變了

# ---深拷貝---包含對象裏面自對象的拷貝,所以原始對象的改變並不會造成深拷貝里面任何子元素的改變
# 總的來說就是,深拷貝之後,與原始數據已經沒有關係了
[1, 2, 3, ['a', 'b'], 5]
d=copy.deepcopy(alist)
alist.append(5)
print(alist,d)
#輸出
[1, 2, 3, ['a', 'b'], 5]
[1, 2, 3, ['a', 'b']]始終沒有改變
alist[3].append('ccc')
print(alist,d)
#輸出:
[1, 2, 3, ['a', 'b', 'ccccc'], 5]
[1, 2, 3, ['a', 'b']]  始終沒有改變


2. Python中的self關鍵字

在Python中規定,函數的第一個參數是實例對象本身,並且約定俗成,把其名字寫成self。作用類似於Java中的this關鍵字,表達當前類的對象,可以調用當前類的屬性和方法。

3. Python中類的繼承

面向對象編程的一個主要功能是繼承。繼承指的是,它可以使用現有類的所有功能,並在無需重寫編寫現有類的情況下對這些功能進行擴展。

通過繼承創建的類稱爲子類或派生類,被繼承的類稱爲基類或者父類,繼承的過程就是從一般到特殊的過程。在某些面嚮對象語言中,一個子類可以繼承多個基類,但一般情況下一個子類只能有一個基類。

繼承的實現方式有兩種:實現繼承和接口繼承:

  1. 實現繼承指的是使用基類的屬性和方法而無需額外編碼的能力。
  2. 接口繼承指的是僅使用屬性和方法的名稱,但是子類必須提供實現的能力(子類重構父類方法)。

4. 完全二叉樹的概念

二叉樹:樹中每個節點最多有兩個子節點

二叉搜索樹:對於樹中任何節點,如果其左子節點不爲空,那麼該節點的value值永遠>=其左子節點;如果其右子節點不爲空,那麼該節點值永遠<=其右子節點值。

滿二叉樹:樹中除了葉子節點外,每個節點有2個子節點。

完全二叉樹:在滿足滿二叉樹的性質後,最後一層的葉子節點均需在最左邊

完美二叉樹:滿足完全二叉樹的性質,樹的葉子節點均在最後一層。

5. 單鏈表與順序表的區別

順序表和鏈表是非常基本的數據結構,它們被統稱爲線性表,順序表和鏈表是線性表的不同存儲結構。

順序表的特點是:

  • 長度固定,必須在分配內存之前固定數組的長度;
  • 存儲空間連續,即允許元素的隨機訪問;
  • 存儲密度大,內存中存儲的全部是數據元素;、
  • 要訪問特定元素,可以使用索引訪問;
  • 要想在順序表插入或刪除一個元素,都涉及到之後所有元素的移動。

而單鏈表是隻包含指向下一個節點的指針,只能單向遍歷,它的特點是:

  • 長度不固定,可以任意增刪;
  • 存儲空間不連續,數據元素之間使用指針相連,每個數據元素只能訪問周圍的一個元素(區別於雙鏈表);
  • 存儲密度小,因爲每個數據元素,都需要額外存儲一個指向下一個元素的指針;
  • 要訪問特定元素,只能從鏈表頭開始,遍歷到該元素(順序表只用索引查找即可)
  • 在特定元素之後插入或刪除元素,不需要移動其他元素。

6. 給出二叉樹的前序遍歷(preorder)和中序遍歷(inorder),重建該二叉樹:

思路:使用遞歸,遞歸的出口就是inorder爲空;首先從preorder中找到根節點,然後在inorder中找到根節點的索引index;在 inorder中,index之前的左子樹的節點,後面就是右子樹的節點

class Solution:
    def buildTree(self,preorder,inorder):
        if not inorder:return None
        root=TreeNode(preorder.pop(0))
        index=inorder.index(root.val)
        
        root.left=self.buildTree(preorder,inorder[:index])
        root.right=self.buildTree(preorder,inorder[index+1:])
        return root

7.反轉一個鏈表,並返回頭結點

class Solution:
    def reverseList(self, head):
        if not head or not head.next:return None
        prev=None
        cur=head
        while cur:
            tmp=cur.next
            cur.next=prev
            prev=cur
            cur=tmp
         return prev
        

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章