[0:00:01] 歡迎來到國立清華大學自然語言處理課程,本次助教課給大家講的主要是PyTorch的使用方法、模型的訓練方法等實際操作的內容,那這裡是高雄宇老師的助教。在講實際的code該怎麼寫之前,我們先大概介紹一下Python環境的架設。假如大家要在自己的電腦上面跑Python,或者跑深度學習的話,這邊推薦Anaconda或者MiniConda來構建Python的環境。這邊以Windows系統為例,大家可以直接去他們的官網下載安裝包,然後也可以通過指令來下載。下載完了以後,在Windows系統上這邊會有一個Anaconda prompt的檔,打開檔以後大家可以看到一個像Shell一樣的黑框,那這個黑框就是我們可以操作環境的窗口。左上角的括號base就是現在我們所處的環境的名稱,大家如果創建了自己的環境的話,在這邊它的base就會換成你自己環境的名稱。那這邊大家可以通過一些指令來操作環境,這裡有簡單的列出幾個,比方說可以通過create,conda create,來創建環境,或者是conda activate來激活環境。 [0:01:32] 然後我們創建好了基本的Python環境以後,接下來就是安裝PyTorch。這裡的PyTorch不推薦大家直接用PIP install來安裝,也不推薦什麼。這裡的PyTorch不推薦大家直接用PIP install來安裝,也不推薦什麼。這裡的PyTorch不推薦大家直接用PIP install來安裝,也不推薦什麼。這裡的PyTorch不推薦大家直接用PIP install來安裝,也不推薦什麼。這裡的PyTorch不推薦大家直接用PIP install來安裝,也不推薦什麼。因為直接用PIP或者conda你很難指定安裝什麼版本的PyTorch,而我們這裡可以看到PyTorch有支援Linux、Mac、Windows有不同的作業系統版本,然後也有支持不同ientes版本的PyTorch。而我們這裡可以看到PyTorch有支援Linux、Mac、Windows有不同的作業系統版本,然後也有支持不同 analytical sci-fi版本的PyTorch。而我們這裡可以看到PyTorch有支援Linux、Mac、Windows有不同的作業系統版本,然後也有支持不同 evangelical sci-fi版本的PyTorch。所以大家最好是上官網去選擇一個符合自己電腦需求的這樣一個PyTorch版本選好自己的需求以後下面就會有對應的安裝指令那裝好那大家直接把這個安裝指令輸入到Anaconda Prompt就是可以操作環境的窗口當中就可以安裝PyTorch了那這邊要提醒大家的是安裝PyTorch不需要額外安裝CUDA因為PyTorch裡面有整合CUDA的內容這是PyTorch跟TensorFlow不同的地方因為TensorFlow它裡面並沒有去整合這些內容所以當我們裝TensorFlow的時候我們要去查TensorFlow跟CUDA有哪些版本的對應關係並安裝對應版本的CUDA但是PyTorch就不需要這麼做 [0:03:05] 但相比之下PyTorch的包其實會有點難受可能會更大一點然後安裝那麼接下來如果大家想要安裝早些版本的PyTorch或者是安裝一些其他CUDA版本之類的那麼就可以點擊這裡這邊有個鏈接點進去以後就會看到過去各個版本的PyTorch的安裝指令那安裝好了基本的環境架設以後接下來呢就是有關於Deep Learning我們先從Deep Learning開始介紹那Deep Learning我們大概可以有這樣一個印象就是我們會有一個模型那我們也會有一堆資料模型呢他會不斷的去吃資料然後讓自己的效能不斷的提升就是我們相當於我們不斷的給模型範例input是什麼樣output應該要是什麼樣不斷的給他這種範例以後模型會自我調整裡面的參數然後讓自己的效能越來越好那這裡wide就等於ax加b假如說這個模型他的input跟output是像這樣的一個多項式關係他有兩個參數一個是A一個是B那那這樣的話這個模型他我們要訓練的就是這兩個參數A跟B這兩個參數那用什麼來訓練他我們這裡面給了三個資料其實是在空間當中的三個點 [0:04:36] 當input是-1的時候output需要是1當input是-1的時候output是2當input是-2的時候output是3給出這三個點以後我們就可以發現當A跟B我們取一個固定的A跟B以後把x把橫座標輸入進去我們就可以獲得他重座標的值如果A跟B都是-1A跟B都是1的話那output出來的重座標就是225那明顯這個225他是不符合我們的期待的因為我們希望他是123那我們要如何衡量這個225他跟123中間有多大的距離呢我們需要去衡量出這個量化的距離並且最小化這個距離所以這邊的距離我們把它表示成標準答案減去預測結果的平方再取平均值也就是這三個點會取平均值獲得我們最終跟結果的誤差也就是L那這個方式其實我們之後會介紹到叫做mean square error那這邊我們把它表示成一個L然後我們就會發現當A跟B各取一個值的時候A跟B取任何不同的實數我們都可以獲得一組225就是一組這個output prediction一組預測結果那有了這種預測結果我們就會有一個對應的誤差 [0:06:06] 就是一個對應的L那麼問題就來了就是我們是不是可以把A跟B和L的對應關係畫成一張圖就像這樣A跟B在取不同的座標點的時候都會有一個對應的L那這是我通過3D的繪圖軟體來畫出來的一張圖畫出來的一個區面這個區面我們要找的就是它A跟B在某個值的位置中在某個值的時候L的算出的結果會最低也就是最底下這個點我們如何獲得最底下這個點就是通過梯度下降的方式那梯度下降大家肯定在微積分裡面有學過通過偏微分來算出梯度然後再通過梯度來更新來尋找出最低的那個點所以這邊Y等於AX加B就是我們描述XY等於B我們把這個點定為一個等式我們把它叫做模型我們定義模型的預測結果和標準答案之間距離的這樣一個等式叫做loss function損失函數另外我們用來做梯度下降的工具叫做optimizer就是優化器中文翻譯叫優化器 [0:07:36] 所以我們可以用一張圖來表示這個訓練的過程首先data會餵給模型data會餵給模型然後模型會產出預測結果預測結果跟ground truth跟標準答案之間會算出一個loss值這個loss值我們為了最小化這個loss值會用梯度下降的方法所以loss值會獲得一個梯度那梯度再通過這個optimizer來更新模型的參數像這樣一個模型獲得預測的參數預測在獲得梯度梯度在更新模型這樣循環往復的這個過程就是我們深度學習當中的主循環也就是main loop為了完成這個main loop我們就可以想說我們需要一個特殊的資料結構那這個資料結構需要做什麼呢就是首先它需要能夠計算我data餵給模型以後我們通過一個基本的資料結構去算出一個預測結果另外這個資料結果還這個資料結構它可以保存我們的計算過程並且通過這個計算過程來算出梯度這個梯度還能繼續回來更新這個資料結構裡面的數值那當我們這個模型越來越大就是參數量越來越大的時候我們會希望這個模型能夠加速運算所以也就是說在GPU上運算 [0:09:08] 所以這裡就誕生了一個資料結構叫做Tensor就是張量所謂的張量我們大名鼎鼎的TensorFlow大家應該都有聽過叫做張中文直譯叫張量流那意思就是說在它這個資料在它這個包裡面數就是data都是通過張量的方式就通過Tensor的方式來進行處理的所以大家理解Tensor可以把它大概的理解成矩陣只不過它是維度特別自由就是它可以是很高維的矩陣它跟我們平常會在現代裡面接觸過那種二維矩大多數都是二維矩陣的還不太一樣它可以非常高維它的基本運算方式其實也就是矩陣的運算方式我們之後會介紹到那它確實完成了可以計算T度並且在GPU上運行的這樣一個需求然後我們就我們介紹了大概Tensor的由來那接下來就是我們如何先創建一個TensorTensor的創建可以通過這個Python的基本型別就是list來創建但是要注意這邊因為Tensor它我們大概理解成的是一個矩陣所以它不能是參差不齊的list什麼意思呢就是現在這裡是一個二乘二的list那我不能說 [0:10:39] 第一個第一個維度它只有一個數第二個維度它有兩個三個數它必須是要對齊就是二乘二那在這個方方正正的矩陣當中每一個它都有一個就每一個它都不是空的它至少都是有一個數的然後並另外呢它也可以通過nonpy來初始化然後也可以隨機初始化就是torch.ran這個randn後面多了一個n的它是以normal distribution的機率分佈來初始化的然後後面沒有多一個n的它是均勻初始化的然後我們也可以創建全都是1的Tensor也可以也有全都是0的還有就是對角矩陣這個在我們往往深度學習的一些中間變量的處理當中是經常會遇到的然後呢創建好了一個Tensor以後我們就可以通過一些Tensor當中的一些成員變量去獲得Tensor的一些屬性比方說data就是相當於是return一個Tensor的copy就return它自己那這個這邊還有一個如果說你是單一的變量的話你不是一個一串一個vector或者一個矩陣 [0:12:09] 那麼你可以用item來回傳出它裡面的數值那另外呢你也可以通過這個.shape來看它是幾乘幾像這個就是二乘二然後也有這個裡面是什麼數值像假如說它是一點二點就是它是float的話這邊也會return出這個torch.float那然後再往後也可以看說這個Tensor它在CPU上還是GPU上畢竟因為Tensor它需要能夠滿足我們在GPU上運算的這樣一個需求然後我們也可以看說這個Tensor它是不是一個需要回傳T度的Tensor然後這個我們也可以把它的T度給回傳出來然後這邊有一個比較有趣的insight有關於Tensor.data那Tensor.data我們之前有講過它是把這個Tensor裡面的相當於它裡面的一些內容給回傳出來的這樣一個動作我剛剛解釋說是把Tensor給複製一份那在它實作的過程當中也確實有複製的這個動作Tensor的儲存方式是底層有一個最底層有一個Tensor內部data的儲存它往往是一個連續的空間 [0:13:40] 是一字排開的空間那在這個空間之上會有一個view所謂的view就是說這一串的data它要通過怎樣的組合來構成我們這個Tensor然後在我們實際寫code的時候我們會有一個變量比方說它這個變量叫AA來指向這個view這邊有個簡單的範例假如說我們命出一個AA是一個二乘二的矩陣就是0123這樣一個二乘二的矩陣那B把A.data的這個A.data的這個屬性給複製給B然後再把A跟B他們中間的在memory當中的位置給print出來然後再把A的01這個位置也就是因為0123把1的那個位置改成100再把A跟B給print出來那從這個結果當中我們可以發現A跟B它雖然A.data把它複製給了B但是A跟B中間他們用的並不是同一塊記憶體但是我們修改A的時候B的值也會一起改意思是什麼呢意思就是說在我們當我們把A.data給B的時候它實際上是把A的這個view給複製了一份 [0:15:12] 複製了一份新的view給了B但它的底層0123這個最底層的資料存儲並沒有重新複製一份所以A跟B它的在memory當中它這個view在memory當中的位置是不一樣的但是當我們改A裡面的值的時候B也會跟著一起改因為它的底層的資料存儲並沒有改變然後接下來就是有關於就是Tensor的運算Tensor加上一個常量就是Tensor加上一個具體的數或者減具體的數就是在Tensor當中的每一個數值做同樣的加減乘除的操作這邊呢其實我們會經常會假如說Tensor是一個矩陣運算的話這邊會多一個符號叫做這個小老鼠那矩陣運算比方說我舉個例子叫A矩陣是1234那B矩陣是0123如果說它是小老鼠的話它OP出來是他們矩陣乘法的結果但如果是單單是這個星號星星那麼他們return出來就是一個元素相加的結果就是元素相乘的結果就是0乘1 2乘13乘24乘3的這樣一個結果 [0:16:43] 只有這個除了這個例外以外其他像加減啊還有除啊這些還有乘次方啊都是矩陣兩個矩陣當中對應元素的加減加減乘除次方然後在Tensor除了它基本的矩陣運算以外還有一些自己的一些操作比方說CATCAT就是把兩個Tensor給拼起來Concatenate那麼A跟B都是2乘2的矩陣那他們在shape就是2乘2shape在0這個位置是的大小是21的這個大小也是2我們把它在維度為0的這個地方拼接它就會變成4乘2的矩陣也就是它在shape等於0那個第一個2的這個位置拼接起來那它在它就會變成4乘2如果說它在第二個2就是維度為1的這個地方拼接起來它就會變成2乘4另外它還可以split把一個Tensor給切成很多塊像這邊初始化一個Tensor是5675乘6乘7的Tensor它在維度為0的時候去切分並且我們希望它切出來的Tensor在這個維度的大小是2這個值所以它切的是維度是5 [0:18:14] 並且我們希望切出來的大小是2那我會我們會不難想象就是5它其實是沒有辦法被2整除的所以我們切就切出一個267在第二個還是267在最後一個只剩1了它實際上並不會報錯而是把最後那個1給也給放在後面最後一個會是它的就是它是會是一個餘數那這邊還有squeeze跟unsqueeze這個是加加維度跟減維度A是一個2乘2的矩陣那A在最後一個維度unsqueeze那麼它就會最後一個維度加一個加一個1就變成2乘2乘1的矩陣然後A再squeeze它就又會變成1234就又會變成2乘2的矩陣然後這邊還有transpose那transpose就是我們常說矩陣矩陣運算當中的轉制把1234變成1324就是按對角線翻轉一下然後我們可以然後以上是一些有關tensor的基本操作那接下來這樣這個stride函數它主要介紹的是tensor它在底tensor它是否連續或者說它在底層是怎樣存儲的這樣一個function [0:19:44] stride它返回的是什麼呢假如說我們有一個3乘3的tensor3乘3是就是012345678對吧就像這樣的一個tensor這邊它底層它在它在存儲的時候它會它會有先是第一排在前面然後是第二排然後是第三排它是一排二排三排這樣子連成一條連成一串的存儲方式那stride就是說在stride它會回傳一個tupletuple在第零個維度的時候它在第一個維度就表示在這個維度在零維度的時候index加1它在底層存儲的時候會跳幾格在這邊第零個維度就是零跳到3這一步它在底層會跳三格0到1到2到3會跳三格它在它在第一維度的時候也就是說從零到一那麼它在底層會跳一格那這樣如果我們可以通過檢查stride這個函數來判斷這個tensor它是否是所謂的連續的tensor也就是說是否是像這樣子存儲方式第一排第二排第三排的 [0:21:15] 存儲方式的這樣一個tensor然後我們就可以再看就是transpose這個tensor這邊還有另外一個有趣的inside就是transpose這個function它實際上並不會創建新的tensor而是創建了一個新的view其實tensor的操作有很多都不是創建新的tensor而是創建新的view像是本來是012345678它的底層是這樣存的它通過transpose以後把上面的翻到旁邊來變成這樣但是它們底層的其實並沒有改變它只是創建了一個新的view出來那這時候它的stride就變成了13就是在第0維度從0跳到1的時候它在底層只會跳一格從0跳到3它會跳三格也就是說它從連續變成了不連續的矩陣那這邊呼叫contiguouscontiguous就是把一個不連續的矩陣變成連續的矩陣如果a本身就是連續的話那麼它其實不會做任何處理就是把a給return出來所以這邊這邊就是說它呼叫了contiguous那麼它會在底層的存儲又創建一個新的存儲空間 [0:22:46] 並且把它存成像這樣子排列方式的也就是把這邊的第1排變成變到開頭然後再是第2排再是第3排的這樣一個連續的存儲然後這樣的話它的stride就變回了3e也就是說它變回了連續的連續空間存儲的這樣一個存儲方式然後接下來是有關於reshape一個2x2的矩陣它可以被reshape成1x4或者4x1的形狀那PyTorch在reshape這個功能之下是做了兩個函數一個叫做viewed還有一個叫reshape之前我們有介紹過PyTorch它是底層的資料在上面再蓋一個view那這個view我們就很好理解它實際上也是一個創建view的一個動作然後reshape呢它的功能會比view要更強一些在這邊有一個介紹就是view它無法去操作不連續的矩陣它只能去操作連續的並且它是在一個連續矩陣的基礎之上再創建一個新的view然後它不會去改變底層的存儲但是reshape不一樣reshape是contiguous加view假如說你的這個input data它並不是連續的 [0:24:16] 那麼它就會創建一個新的那麼它就會創建一個新的內部存儲空間相當於它先contiguous再跑view所以reshape就加了這樣一個判斷它的功能也會比view要強大一些然後接下來我們介紹了一些有關於tensor的基本的操作以後這邊會建議說大家在做tensor的運算的時候儘量用矩陣運算的方式來運算而不要用循序的方式來運算因為tensor它內部它對於這個矩陣運算有自己的並行化的一些加速過程假如我們這邊舉的一個範例是我們要normalize所謂的normalize就是減平均除以標準差這個過程我們可以寫兩個套兩個復迴圈來寫也可以就是用矩陣矩陣運算的方式來寫那這邊我們會發現它的效能會差很多因為這就是我們就因為矩陣運算它實際上相當於利用了PyTorch內部的一些加速功能但是如果你是循序的話它實際上就是你就是強制它一個一個來算這其實是相當耗時間的也是一個相當沒有效率的做法然後 [0:25:47] 接下來我們介紹了建構模型的或者和資料的一個基本單位就是Tensor那接下來我們就是要通過這個Tensor來建構一個模型那Tensor在模型當中的主要的一個角色就是就是它的參數在一個模型當中它底層的參數就是通過Tensor來存儲的並且在Tensor的這個參數之上有一個大的資料結構叫做torch.nn.module也就是模組所謂的模組我們所有的模型它都是它都需要是一個模組的物件那假如說我們要創建一個新的我們自己的模型我們就繼承著這個模組torch.nn.module然後再去複寫它的我們自己特有的這樣一個初始化的函數跟forward函數初始化的函數它主要是定義模型當中有哪些層比方說我們可以有線性的層還有什麼卷積層RNN層各種各樣的層然後這個forward函數就是當我們有一筆資料投進來的時候模型要吃那筆資料的時候那筆資料它需要通過怎樣的處理方式來獲得模型的output [0:27:19] 那這邊也就是我們有這樣一個物件以後新創建出來一個這樣一個物件然後物件在吃資料這個input就是資料把資料丟給這個模型以後模型就會自己處理去return出這個output就是我們所謂的logits那這樣一個這樣一個function這樣一個把資料丟進來的這樣一個過程它會呼叫這個torch.nn.module裡面的這個call這個function然後這個call function會直接去呼叫它的forward然後這邊有一個大概的就是在我們initialize模型的時候我們可以定義的一些層像是linear就是常用的線性層所謂的線性層就是乘以一個矩陣那當然我們如果形象的表示它其實就是一個fully connected全連接的層那這邊convolution這邊是卷積層第二個是卷積層卷積層大家在圖像處理的時候肯定是經常會遇到是一個非常有名的層它可以是2D 1D 3D在早期的NLP的使作過程當中也會時不時的出現這個卷積層然後RNN跟LSTM這個在我們NLP的課上應該經常有 [0:28:49] 接觸到那這兩個層其實就是序列資料生成的序列資料生成的一個模組然後max pooling這個也是常用於圖像處理的內容然後embeddingembedding往往是把資料往往是把一些電腦實際上看沒辦法處理的資料轉化成能看得懂的也就是說把一個某個東西轉化成一個向量那這個東西它又會有個對應的ID通過這個embedding層把ID映射到向量它內部其實也就是一個矩陣乘法的一個形式然後再是Batch Normalize這個是我們在運算的時候它會有它會需要時不時的進行一些減平均除以標準差的動作來防止它的資料過於發散過於極端然後Sequential跟ModuleList、ModuleDict是通過大家可以就ModuleList跟ModuleDict就是模組版本的Python的List跟Dictionary那這個Sequential其實就是把很多個層包成一個上下串成一個大層然後我們在這個模型背面建構出來以後模型也會有一些自己的Member Function比方說Forward剛剛我們有講過就是資料進來以後要怎麼處理 [0:30:20] 然後StateDictionary它會回傳一個OrderDictionary那這個Dictionary它的key是參數的名稱然後它的value就是參數的值參數的內容也就是參數對應的向量那這邊這邊StateDictionary它其實我們可以連它其實跟NameParameter有一點比較像一點但是NameParameter它回傳的是一個回傳的是一個迭代器就是我們可以用復迴圈來展現它裡面把裡面的內容給Print出來它裡面Print也是參數的名稱跟Tensor那這邊沒有Name的Parameter它實際上只會它這個迭代器只會OP出來一些只會吐一些就是參數的內容然後model.train和EVAL它就是把模型的模型的一個模式轉換就是把它變成Train或者是Evaluation訓練模式跟驗證模式之間的轉換首先我們來看就是ModuleList該怎麼大概的做法左邊是我們創建出來的一個模型那這個模型它裡面有三層一層三層都是Linear而且差不多那在一個Input進來以後這個Input會分別位給這三層並且獲得他們各自的Output [0:31:50] 這個各自的Output串成一串然後Return出來那這一個方式其實就可以改寫成就是因為它這三個這三個Linear它實際上是從我們可以把它直接寫成一個List的形式那這個List它其實它也就是跟Python的List有點相似的地方就是它可以通過這個中括號一個整數的方式來取它我們要的是其中的某一層當這個層越來越多就當這邊的Linear越來越多的時候很多很多的Linear那實際上用這種方式其實會大大減少我們的就是Coding的難度吧就Coding的複雜程度然後第二個是有關於SequentialSequential它底層的做法Sequential它有一個自帶的Forward就是它的Input喂進來它會依序的餵給它自己的每一層每一個模組每一個麾下的模組然後這個前一個output就會餵給下一個的喂到下一個作為下一個的Input這樣一層一層喂下去一直到獲得這個最終的output就像在這裡我們這邊的左邊的模型它有三層第一層是LinearLinear的output再過一層Renew [0:33:21] 就是過一層激活函數然後再過一層Linear那麼這邊就可以寫成把這三層包成一個大的Sequential這個Sequential就是第一層是Linear第二層是Renew第三層還是Linear在一筆資料進來的時候就可以直接丟給這個大的組合起來這個層它就會直接在底底層幫我們第一層的output會給第二層第二層的output會給第三層第三層的output再output出來再return出來作為Forward return value然後這個是這個是Sequential這個這個函數這個功能是這個模組它能夠完成的一些功能然後另外為什麼我們就接下來我們就會可能會產生一個問題就是為什麼我們不用Python它自己的List而我們一定要用Module List因為Module List其實我們剛剛也看到它實際上完成的功能也就是很大程度上也就是Python List的這樣一個功能就是存儲各種各樣的模型然後串成一個List的功能那為什麼我不直接用Python的List但實際上我們就會發現這個我們仔細去看它每一個每一個Member Function的底層Code我們就會發現它很多的Code的實作都是用一個遞迴的方式實作 [0:34:52] 也就是當我們比方說像下面這個例子當我們去呼叫StateDictionary的時候它實際上我們回顧一下StateDictionary它需要return回來的是這個模型的所有參數名稱跟參數的內容參數的Tensor它組成了一個OrderDictionary那也就是說我們呼叫這個函數的時候我們的Python需要往下找找出所有的所有的參數和它們對應的名稱並且return回來這個尋找的過程是一個遞迴的過程在呼叫StateDictionary的時候在下面它會呼叫自己每一個模組每一個子模組的呼叫StateDictionary並把它給加進來加到自己的加到這個副模組它的StateDictionary裡面去所以這邊是通過NameCommon通過這個復迴圈來便利Self.下滑線Module的這個成員變量這個成員變量其實存的就是模組類型的東西那假如所以假如說我們把我們把我們不用模組類別的東西我們就用Python的基本類別它如果只是一個List那麼實際上它就並不是一個模組類別的東西 [0:36:23] 所以它就不會被存進來就像這個範例左邊是一個用ModuleList來實作的一個實作的一個模型右邊我們只是用Python的基本型也就是增刮號的那個模組然後我們再把我們創建出這樣一個物件以後在下面就創建出一個物件以後再把這個物件的下滑線模組這個東西也就是我們StateDictionary要遞迴要去尋找的這樣就是用來遞迴的這個成員變量把它印出來以後發現用ModuleList它實際上能夠順利的存到這個下滑線模組這個成員變量裡面去但是如果我們用List它實際上這個模組是空的也就是當我們需要去回回傳它所有參數的名稱的時候我們去呼叫StateDictionary的時候如果我們用Python的基本型實際上我們回傳出來是個空的東西因為它這個模組它裡面沒有存任何東西所以這就是告訴我們就是告訴我們一個結論就是在寫模型的時候只要我們的模組它裡面是有參數只要裡面有東這個成員變量它裡面有參數它是一個層的定義模組的定義那我們就要用ModuleTorch.nn.Module這一個類別的東西來實現對於來實現對於這些 [0:37:54] 模組的存儲而不要用Python的基本型別然後接下來就是有關於訓練模式跟驗證模式因為剛剛我們在介紹Model介紹模型的成員函數的時候我們有介紹過就是在訓練模式跟驗證模式之間的轉換在訓練模式當中因為我們要對模型做訓練那模型當中可能會有一些像Dropout就在訓練時候為了防止overfitting所做的一些所做的一個拋棄部分拋棄部分元素的一個處理一個Dropout和一個BatchNormalize這個也是在訓練過程當中我們會需要用到的那這兩個它會在Train訓練模式的時候被激活它只會在訓練模式的時候發揮作用就Dropout才會真的丟東西而在驗證模式的時候Dropout就會不起作用它這個Dropout跟BatchNormalize就會不起作用那這是訓練模式跟驗證模式的區別一般來說訓練模式就是我們在訓練模型之前我們會Model.Train把它把這個模型以及下面所有的子模組都變成訓練模式然後在驗證的時候就在我們有Evaluate就是驗證集驗證Dataset進來的時候會 [0:39:24] 呼叫這個Model.EVAL的這個函數把它變成驗證的模式然後這兩個模式它都是用遞迴的方式也就是我們呼叫一個模型的Train的時候它會尋找下面的每一個尋找下面的各個模組也子模組就是每一層每一個torch.n.module然後就類別的東西它都會去呼叫它的Train它也是這樣遞迴下去的所以這也是印證了我們之前說的要在模組裡面寫模組不要在模組裡面寫Python基本性別然後我們介紹完了模型大概的建構接下來我們就是要訓練這個模型那訓練我們會有兩步吧就首先資料會未進來獲得預測結果預測結果會傳出loss我們最小化loss就會回傳T度那這個T度通過一個叫反向傳播的方式來計算也就是這邊的loss.backward這個backward它完成的具體功能就是逐層的往回去計算偏微分去計算T度通過這個偏微分來計算T度以後再把T度存在各個參數的.grade這個變量裡面注意是各個參數的grade這個變量而不是中間計算結果的變量的grade [0:40:54] 成員變量這裡面它在計算的時候呢會通過一個有象無環圖就是DAG會構建這個DAG然後用一種類似於BFS的方式來逐層的去一層一層往回去回傳T度去計算那個偏微分那接下來這邊有一個簡單的範例A跟B就是我們的參數我們傳進來兩個變量A跟B然後呢這個A跟B會經過這樣的運算就是B先經過relude然後A跟C再矩陣乘法然後C跟D再加一加就會獲得我們的E這個E就是相當於我們模型的output假設這個E就是我們模型的output那這邊他在forward這個變在跑這個forward過程當中他會去記錄說這個E他是如何從AB給算出來的他會經歷這個C這個中間函數跟D這個中間函數C這個中間的tensor跟D這個中間的tensor然後然後說B是通過relude來算出這個C的他這邊就會記錄C如果要返回去就要算這個backward function那麼他會經過這個relude backward [0:42:25] 來回來回到這個B這個地方然後這邊呢就是他這邊是矩陣這邊是矩陣乘法來算出通過矩陣乘法來算出這個D這個變量然後所以這邊也這邊D也儲存了一個矩陣乘法的backward function那每一個中間的變量都會有一個乘一個他是如何算出來的那麼這個計算方式對應的backward function並且還有一個dependency就是他們的依賴這個依賴表示的意思就是說假如說這個D這個我們要跑D往回算A跟C的這個偏微分的時候我們需要先算出就是E跟D從E到D的這個偏微分為什麼因為我們實際上是我們實際上在backward往前傳遞的時候我們是從這個E開始算各個T度的然後我們要優化的其實也是就是把這個E的這個值可能是最小化或者怎樣所以我們其實要算假如說我們要算這個A的T度我們是算E對A的偏微分那E對A的偏微分要怎麼算呢就是先算E對D的偏微分再D對A的偏微分它是用一個這樣 [0:43:56] 就是它是這樣子通過中間的這樣一些變量給傳遞回去的所以我們要算我們這個D要算往前的偏微分的時候我們要先算E跟D的偏微分E對D的偏微分再把D再傳回去所以D就是dependD就是依賴E回傳的這一步所以它會依賴一個就是E的這個偏微分而C呢C它會依賴兩個就是它有兩條路是E先走D再到C還有就是E就是直接到C所以它的依賴就會是2它有兩條路來算這個偏微分OK那我們直接去跑它的就是反向傳播當我們呼叫backward的時候它會就是PyTorch會構建這樣一個反向的DAGE通過E來跑backward的時候E的dependency是0它這邊會有一個function task queue這個function task就是我們現在可以執行的backward function什麼叫現在可以執行呢就是dependency它沒有依賴於其他沒有依賴於其他backward function要先完成它自己才能做那它就不能馬上執行嘛如果說別人要先做它自己才能做的話那它不能馬上執行它就不會被加到這個function task queue [0:45:26] 你看如果D是0它可以直接做了那麼它就會被加到這個function task queue裡面這個E它的D是0它就會被加進來並且它會算著E到D的偏微分E到C的偏微分這兩個偏微分的計算其實就寫在這個add backward裡面然後這個add backward跑完以後它這兩個變量的D就會減1然後也就是在這兩個變量的位置它會有算到這個E對D的偏微分跟E對C的偏微分但注意這個算出來的偏微分並不會被存在這些中間變量的點gradient成員變量裡面因為這個C跟D它是一個中間tensor它並不是它並不是一個參數它並不是像A跟B這樣的leaf node它只是一個中間的中間node而已所以中間node它並不會去存t度這個東西然後E算出往回算出這個對D跟C的偏微分以後它們的dependency會各減1因為畢竟E已經好了嘛這時候我們會發現D的dependency變成了0那這樣的話我們就可以算D了 [0:46:56] 因為只有0接下來我們就會把D的這個MN backward就是它是通過矩陣乘法來算出來的把這個MN backward給丟給這個function task queue之後E的那個backward會從這裡pop出來然後再把MN backward給丟進去在這個D在做反向傳播然後D在算了以後這邊我們就會算算出在C這個點因為D它一條線是指向A一條線是指向C這個A其實也會被算出來然後接下來就是它會算出C這個點它的它的這個點它的偏微分是多少那這個偏微分就是首先就是一條路E到C這條路前面這個是在第一步就算好的第二步算出來是E通過D來算C就是先對D做偏微分再D再做對C做偏微分是這樣一個動作然後這個算好以後那C的偏微分也完成了那麼這樣的話我們就可以跑C的backward就是relude backward然後C再往前再傳給B然後這下我們就可以算出來A的偏微分就是E傳到D再傳到A是這樣是這個運算的結果 [0:48:26] 那B的偏微分就是E通過DEDCB跟ECB這兩條路的偏微分算出來的偏微分相加然後也會存在B的gradient的這個變量裡面然後這下來C的算完以後把它的relude backward丟出去這樣的話A跟B的梯度就被算完了那這就大概就是一個backward function它的底層實作的方法那我們在具體在呼叫這個backward的時候其實我們就只要寫一行就是loss就是我們算出來那個loss的tensor點backward這個過程其實就被完成了所以大家其實這個過程它是其實是Pytorch自己有實作好的那另外呢另外這邊還有三種方法可以規避梯度的計算首先第一種是tensor.requiresgrade就是需不需要傳梯度呢不需要把它設定成false把它設定成false以後它底層的代碼就會看說它這個地方是不需要傳梯度的那麼它這個梯度就就會相當於是把這個相當於是把這個node看作一個非leave node就看作相當於一箇中間結果的樣子然後它這個它這個算出在這個這一步算出來 [0:49:56] 算出梯度就不會被保存下來然後還有一還有一種是torch.nodegrade就是它這邊有一個context manager就是上下文管理器吧我們可以叫做在這個width.backward這個管理器下的code也不會被算梯度就是也會當成中間的東西不會被存下來梯度第三個就是detachdetach跟前面兩個不一樣它指的是在tensor比方說我們中間中間我們比方說算出了某一個中間產物detach就是說讓這個中間產物跟原來的這個因為我們算梯度的時候會有一個有像有像的dag嘛就有一個graph那detach就是說這個tensor這一個變量它從graph當中被摘出來所以它會創建一個新的tensor這個tensor它不會改變這個底層的存儲它會創建一個新的view然後它的連續性也不會改變但是它是已經從這個forward backward的這個系統當中脫離的這樣一個tensor然後我們介紹完了這個backwardforward backward就介紹完了接下來就是我們要train一個模型了模型它會它會吃data [0:51:27] data會算出預測結果然後預測結果再算出lossloss再回傳梯度那這樣一個那模型的訓練參數更新大概就是這樣的一個過程那另外呢在這個主循在主循環的之外我們還需要有比方說我們需要準備好dataset對吧我們需要有datadata在之前我們的regression有那些像是什麼線性迴歸啦或者是之前那個二之前那個多項式迴歸當中就是一個一些空間當中的點那第二個就是我們需要構建出我們的模型模型就是input跟output之間它是什麼樣的關係它可能是線性關係也可能是卷積的關係它也可能是rn的關係等等等等另外呢我們會需要構建出我們的optimizer就是我們需要定義我們要用什麼樣的optimizer什麼樣的梯度下降工具然後我們需要對於這個特定的任務我們要有什麼樣的loss function來衡量模型的預測結果跟我們希望它就跟標準答案之間的距離然後我們還需要在模型訓練完以後去驗證這個模型那實際上就是把測試級我們會有一個專門獨立於訓練級以外的測試級這個測試級的資料一個個丟給模型看看模型能不能準確的預測 [0:52:58] 這些這些資料注意它是模型訓練的時候沒有接觸過不要用訓練的資料來測試然後loss function那我們之前有講loss function的作用是什麼呢它的作用就是評價模型的output跟標準答案之間的距離它是一個量化的指標就算就算可能比方說是分類任務它照樣也會有一個像這樣的量化指標那這邊有很多我們在在實際PyTorch訓練DL訓練深度學習模型當中經常會遇到的經常會用到的一些loss function比方說cross entropy loss交叉上就是我們所謂的分類任務當中會需要的需要的loss function那MSE就是我們在這一節助教課開頭所講的就是標準答案減去預測結果在平方在平均它是專門它一般會用於迴歸任務也就是我們會有一個具體的數值我們希望這往往是一個像打分之類的工作我會希望模型的output是一個具體的數值而標準答案也是一個具體的數值然後它的作用就是把兩個具體的數值評價兩個具體數值之間的距離第三個是KLDivergence loss那這個往往被用於 [0:54:29] 資料的知識蒸餾的這個方法所謂知識蒸餾就是我們有一個Teacher模型就是我們有一個模型當作老師這個模型往往是更大更復雜的模型然後我們會有個student模型就是會有一個小模型來做大模型的student我們希望在小模型當中去用一個比較小的模型去編碼大模型的能力所以我們會希望小模型的分佈就它的output出來的分佈跟大模型的越接近越好所以KL divergence所評價的就是兩個機率分佈之間的距離然後BCEBCE就是所謂的Binary Cross Entropy那這個它對於cross entropy之間的不同點就是BCE它只能用於它只能用於二元分類兩個class的分類它並不能使用在很多class的分類那麼另外還有一些是有關於我們其實在未來的實作當中可能會經常遇到但是PyTorch裡面並沒有實際寫出來的一些loss function比方說contrastive loss就是它是適用於所謂的contrastive learning對比學習就是在就是我們output出來往往是一個一個vector representation是一個具體的vector在高維的空間當中我們希望vector去 [0:55:59] 靠近某一個正樣本的值並且儘可能去遠離那些負樣本那我們就會用到這個contrastive loss那下面這個entropyentropy它往往可能不一定會是一個完整的loss function它往往它有的時候在很多論文裡面它會作為一個loss function裡面的一個模組而存在為的就是提升模型預測結果的信心度就是當我們模型預測的分數大多數集中在某一個某一個類別而不是分散在平均分配的時候也就是模型它很自信我的預測結果就是某一類的時候它的entropy的值就會小也就是它的loss function它的這個loss值就會小加這個也就是說希望模型能夠儘可能的去提高它的信心度那這是我們常用的一些loss function接下來就是optimizer相對於loss function這種不同的任務我們會有不同的loss function那optimizer實際上就相對簡單我們一般來說我們一般來說會用到的基本上就是這個adamadamw就是加了weight decay的adam基本上沒有特別的情況我們都會默認就是adam來作為我們的optimizer優化器來使用 [0:57:29] 那我們介紹了前面的各個深度學習的組件以後接下來就是有關於在training的過程當中會有這樣一個最一些基本的操作就是首先把資料為給模型算出模型的預測結果模型的預測結果在跟ground truth一起這邊其實還有一個ground truth模型的預測結果跟ground truth一起算出loss值然後loss在反向傳播然後算出就是算出梯度梯度再給optimizer來更新模型參數那注意就是每一輪每一個step訓練每一輪梯度計算之後我們會需要把這個optimizer的前一輪算出來的梯度給清除掉因為不然的話我們在我們不斷的去算梯度的過程當中這個梯度他會不斷的累加累加累加實際上可能對於我們模型訓練的過程當中來說是我們不願意看到的OK那這一個主循環的實作呢我們會有一個範例這個範例是一個線性迴歸的範例大家可能在作業的說明當中也有看到過這線性迴歸就是在這個二維空間當中有這樣一些點這些點呢我們希望用一條用一個直線去表示這個點 [0:58:59] 那這個點呢就是他他們各自的分母他並不會是一個直線就像在實際生活當中現實中現實的任務往往是一個複雜非線性的非線性的任務那他往往每一筆data就每一個現實狀況他都會有自己的bias就像這些紅點一樣他並不會完全的一條直線來排列那我們往往對現實的data做建模的時候會用一個比方說這邊我們希望用一個直線來對這些現實的data來做建模用直線來表示這些紅點所以所以接下來就是有關於我們把這個程式寫出來以後首先就是有一些像這些input data跟output data的tensor那input data他會有x座標有y座標然後模型的定義模型的定義這邊因為是linear regression所以input size是1output size也是1然後在input size跟output size都是1的時候並且他默認是有biasnn.linear他的bias他裡面有個參數是biasbias的默認值是true大家也可以把它變成false這個他的關係就是這個他在他定義input跟output之間的關係就是y等於wx加b [1:00:29] output等於w乘以inputinput再加一個bias然後那那input就是我們的xtrain那output就是ytrain下面的criterion就是因為我們是linear regression我們是一個迴歸任務我們的output是一個數值我們的ground truth是數值模型的預測也是數值要讓數值更接近我們用的是mean square error loss這邊我們就定義我們的loss function然後再是optimizer,optimizer我們採用一個比較基本的stochastic gradient descent就是一個t度下降的optimizer然後optimized的對象就是模型的參數然後他這邊會定義一個learning ratelearning rate就是我們算出t度以後我們的步長我們所謂的步長就是我們一般來說learning rate都會定義一個在1的如果模型大的話可能是10的-5次方如果模型比較小的話可能是-3甚至有可能是-2次方但一般來說是這樣一個數量級然後接下來就是我們進入我們的模型訓練的主循環就像我們一開始的範例給大家介紹的把資料丟給模型算出這些資料的output就算出這些資料的預測結果這些預測結果再跟ground truth [1:02:01] 來算出loss值也就是在這個資料集上模型會模型的效能評估所謂的效能評估量化的評估就是這個loss值預測結果跟output之間的跟ground truth之間的距離然後最小化這個loss值就會需要從把前一輪的t度清空然後再算出通過loss的backward算出新的t度然後再根據這個t度來優化模型參數然後這樣一輪輪跑完以後就會我們就會獲得這樣一條大概就可以代表這個紅點這個趨勢的這樣一條直線在這個過程當中訓練過程當中我們把我們一步一步往前訓練我們就會發現這個loss值就模型算出來這個loss就越來越小越來越小然後呢我們再進一步的去觀察它裡面的行為那這個模型是y.wx加b它裡面就有兩個參數一個是w一個是b就是我們所謂的第一個是weight第二個是bias那weight因為它是因為它input跟output都是一維不是一維是只有一個數所以它的weight它的weight就是也是一個數它的bias也是一個數然後我們跑zero grade的時候 [1:03:32] 它的weight的t度就會是空的然後它的bias也是空的然後我們在loss點backward的時候我們再把它印出來會發現weight跟bias都有了一個新的值這個值就是我們算出來的t度然後我們在optimizer.step的時候我們就會通過這個weight在乘上learning rate來更新我們的模型的參數就是我們的w跟b那這時候我們會發現0.410.65被更新到了0.4065也就是這個weight在乘上learning rate這個learning rate它會是10的可能是10的-3次方之類的大小然後然後optimizer.step更新完模型的權重更新完模型的權重以後模型裡面的參數以後再optimizer.zero grade那這個gradient就是這個t度它又會被清空變成none回到none然後來準備下一次的更新那這就是我們這就是我們那個一個最簡單的linear regression的範例那麼在NLP接下來我們就會引入NLP自然語言處理的任務當中模型的訓練的行為它大概是什麼 [1:05:02] 我們之前所給大家的範例其實都是一些非常簡單的模型它裡面的參數其實就那麼一兩個就像我們之前說的第一個範例多項式它的參數就是a跟b第二個linear regression它的參數就是w跟b那對於我們的自然語言處理來說因為自然語言它實際上是一個更加複雜的非線性系統所以它裡面的特徵實際上會比這種最簡單的就在空間裡面的點這種特徵要更要複雜的多所以我們在就是我們在訓練這個自然語言處理的時候我們的模型不管是模型還是資料都會遠遠遠遠高於之前的那些linear regression它可能會到它可能會到達的量級就模型參數的量級可能是多少個million或者多少個billion就是有可能你這個很多就是你看那個GPU好像有好幾十個G的那個大小這VRAM大小實際上你可能模型塞進去都不一定塞得下然後資料量就是更加恐怖它有可能會上億就上千萬上億的這個級別所以這就產生了一個問題就是我們之前算loss的時候我們之前算loss的那個的值它的在空間當中的變化的那個區面的時候 [1:06:32] 實際上我們是把所有的data全部丟給模型來算變化但是但是在自然源處理的任務當中我們不可能把所有的資料丟給模型因為資料量實在是太大了所以我們的處理方法就是在資料量裡面取出一批一批的資料通過這一批資料來代表整一個資料dataset的分佈所以這也是為什麼我們如果經常就如果有接觸deep learning的話就大家經常會聽到說儘可能的把data的這個badge size設大會比較好為什麼呢就是因為大badge的badge size大了以後他這個badge就更能夠去表示這個dataset的分佈如果小badge如果是一筆的話他有可能會有各種自己的bias所以我們需要我們需要有的在在贈源處理當中我們需要有的其實是在像CV領域其實像還有訊號處理其實也是一樣我們需要給data來分批位給模型所以我們需要有這個dataset跟dataloader的物件那dataset就是我們用來存儲就是PyTorch裡面自帶的一個用來存儲存儲資料的這樣一個資料結構它裡面它裡面的會有三個成員變量就是我們一定要寫出來的 [1:08:03] 第一個就是初始化的這樣一個動作他是把他是把我們我們的data往往會在initialize作為一個參數給傳進來然後存在這邊是命為self.data存在這個self.data的那個成員變量裡面然後再通過這個getitem兩個下滑線getitem兩個下滑線就是通過這個index這個指標這個指標來中括號取出這個getitem也就是我們平常我們寫Python list的時候我們後面的那個中括號一個整數來取出幾筆的這樣一個動作然後接下來就是有關這個length這個length在我們之後獲得這個我們在之後在實作這個data loader的時候是也會需要寫出來的然後當我們去建立一個新的data loader的變量的時候data loader的物件的時候我們會第一個闡述我們可以會傳入我們的dataset的這樣一個大資料集然後第二個我們會定義它的定義它的best size是多少它會有什麼樣的correct function然後它是否要打亂然後再通過這個一個復迴圈我們就可以一筆一筆資料把它取出來那具體的資料處理過程就像這樣如圖所示在dataset的這個物件裡面 [1:09:33] data會一筆一筆的通過這個getitem的這個呃成員變量就成員函數把它取出來然後這個testdata第一筆取到這裡然後第二筆再取到這裡一筆一筆取出來那這個地方我們可以加一些單筆資料的預處理然後做取出這些data以後相當於取出這一批data以後然後這一批data會打包成一個tuple這個tuple再位給correct functioncorrect function它它是定義在我們的data loader裡面的data loader去的input就是它的data loaderinput是這個tuple以後然後它會在在batch的這個維度在資料分批的這個維度再對資料做一些處理比方說你可以在correct function裡面把資料打包成tensor之類的動作然後data loader復迴圈我們用復迴圈去去取出data loader這樣一批一批資料的時候實際上取出的就是correct function的return value這個return value大家我們可以在裡面把它打包成tensor取出來的就是tensor然後呢就是對於一個RNN模型來說 [1:11:03] 那接下來我們就要進入這個自然源處理的一些模型RNN模型來說它裡面的資料的處理過程就是比方說我們有一筆資料叫iloveai然後這一筆資料它會有每一個token它一共四個token每一個token都會有自己的ID比方說i它會有一個id它會相當於一個1號vector它對應的是這個i這個token在dictionary裡面它是第幾個字符然後它在embedding這個層的時候它會相當於通過一個矩陣乘法的方式來把這個id通過這個id取出這個token的embedding也就是這裡相當於是1號vector實際上是一個非常離散是一個sparse vector它取出來以後變成embedding它就會變成這個hidden size是768那它會取出一個768維的呃768維的dense factor然後再把這個dense factor位給下面的隱藏層然後隱藏層隱藏層的output就隱藏層的這個input就是我們embeddingembedding層output出來的這些dense factor那這些dense factor就會 [1:12:34] 經過這個rnn模型這邊有一個hidden statehidden state加上加上第1個token然後再傳過來再加上第2個token再傳過來再加上第3個token這個大家在課上應該有講老師應該有講到說rnn的模型的處理方式那對於torch.nn.rnn的這樣一個function就這樣一個物件來說它吃到的這個input就是它吃到的input就是這邊的這個dense factor它吃到的output它的return value就是模型它處理好的這個它這樣子按照rnn的這樣一個處理方式處理好的對每一個token對應的這個output變量然後另外呢它後面會有個hn這個hn它就是最後一個h最後一個hidden state那麼這個hn假如說這個hn的作用就是假如說我們這個rnn我們的output的這個h我們要運用到下面可能一些其他的模組裡面那麼我們就會用這個hn來傳給下面的模組比方說下面可能會有一個像類似於decoder之類的東西然後呢訓練rnn的時候我們我在作業的講解當中有講過就是generative learning跟teacher forcing之間的區別那麼我們這邊會建議大家用teacher forcing那大家如果用generative learning [1:14:06] 訓練出來也能訓練出來的話實際上也應該不會有大問題但是這邊為什麼推薦teacher forcing其實也不光是因為teacher forcing它的coding難度低還有一個原因就是teacher forcing它訓練起來是會更穩定的這邊是一個簡單的理由範例假如說我們給模型的ground truth是iloveai那麼我們給rnn模型的第一個token是i我們就規定它從i開始生成那假如說我們直接跑一遍模型的生成那它預測出來的結果可不一定是iloveai它可能是ieatapple一個句號我們讓這個模型完整的生成完了以後再去再去優化它的這個模型我們就會發現iloveai跟ieatapple它實際上從第一個token的預測開始就錯了在這種訓練的過程當中它第一個token的錯誤它就會被利用到接下來的訓練過程像是我們i會生成eat那但是我們ground truth是love所以我們要優化的點就是i的情況下生出love這個沒有什麼問題但是接下來一個token就是我們input是ieat但是我們的ground truth是ai [1:15:36] 所以我們要優化的就變成了i這個其實上非常的奇怪就是因為如果我們直接用generative learning我們直接用生成的方式訓練的話會產生這樣子不穩定會產生這樣子有問題的表達所以在通過生成的方式來訓練往往會產生像這樣子的問題也就是早期的預測結果的錯誤會被一步步去影響後面的預測結果後面的訓練過程所以generative learning它實際上不太穩定沒有teacher forcing穩定為什麼teacher forcing會更穩定呢是因為我們在input的時候就直接給他ground truth所以他的他要optimize他要去優化的這些內容他並不會有那種奇怪的像i eat ai這種奇怪的東西出現他就是i的情況下優化lovei love的情況下優化aii love ai優化驚歎號他實際上就是一個就沒有什麼沒有特別奇怪的東西在這裡面所以他會更加的穩定所以我們也會更加推薦大家用teacher forcing而不是生成然後再優化的這樣一個過程另外呢接下來就是有關我們接下來會大概的先帶到一點怎樣在怎樣去使 [1:17:06] 怎樣通過hugging face這個套件就這樣一個平臺去調用transformer模型那大家在上課應該也已經接觸也已經講到transformer模型他大概是怎樣一回事那transformer模型他相對於之前的循環神經網路來說那他其實會有更強的處理能力並且有更加好的平行化能力所以這邊以bert為例嘛就是bert他是在transformer架構在基礎上經過了一個pretraining的動作也就是Google在開發這個bert的時候他用大量的就是大量的文本大量的沒有標註的資料用完形填空的方式去訓練這個模型當然還有就是預測某個句子是不是哪個句子接下來某個句子是不是哪個句子在往後接下來寫他兩個句子接在一起通不通順這種各種各樣的就是這種多個stage的預訓練任務然後這個預訓練往往會用到非常多的硬體資源跟時間資源還有各種各樣的什麼電力成本啊這些所以如果我們要下載這個這個已經pretrain好的像bert模型的話我們一般會透過hugging face的這個平臺 [1:18:37] 來下載這個模型那這個hugging face的套件調用就是我們接下來會講的那我們下載了這個預訓練模型以後實際上這個通過預訓練的這個通過pretraining這個方法讓模型先學會這個語言的表達方式啊文法結構啊他會更加的能夠對下游任務有更好的理解能力相當於我們直接下游任務的任務下游任務的資料其實並不會資料量其實並一般來說不會很充足那我們直接訓練過去其實就太遙遠所以我們會通過pretraining先讓他學會這門語言然後再把他適應的任務再一點點調過去那這樣會顯著減少他的資料量的需求資料的需求量所以在transform就在hugging face這個平臺上面其實他有打包出這樣一個transformer的這個包透過t-transformer.autotokenizertokenizer我們就可以把tokenizer就是把birth模型給給load進來然後可以指定一個本地的存儲的地點來存儲這個模型然後什麼叫tokenizer,tokenizer所完成的目標就是把就像是這裡一樣就batch encode plus我經常會用這個我個人經常會用這個這個 [1:20:07] 就encodetokenizer的方式這個其實確實非常的方便他可以直接這樣子一行他所完成的結果就是第一步把我們的比方說像iloveai就是個自然語言的段落給變成每一個token並且每個token他自己的ID把它切好token然後把每個token再換成自己的ID然後並且他可以做到padding就是假如說我們有一筆比方說第一筆叫什麼iloveai第二筆叫做i eat apple today然後這兩筆資料他的他的長度是不一樣的那麼他可以做到padding就是把短的資料添零補位變成跟長的資料一樣的長度並且他也可以做translation就是我們可以定義一個最大的長度然後超過那個長度我們就把後面給截後面的都不要然後並且他也可以自動幫我們做到就打包成一個pytorch tensor這樣一個功能然後這個data這個data所回傳出來的是這樣一個bash encoding的這樣一個data資料結構那這個資料結構大家可以把它當成一個dictionary來使用那麼他默認回傳出來的東西是在cpu上面的大家如果要在gpu上跑的話 [1:21:38] 需要把這些資料再搬到gpu上去跑然後有關於模型權重的下載這邊就是transformer.automodel然後再frompretrained再通過這個指令來調用這個模型的來調用這個模型他的內部的權重啊模型架構這些然後這樣子調用出來以後他其實相當於就是一個torch.nn.module就是一個模組的就是一個模組的型別的東西大家就是按照之前所講的Pytorch的torch.nn.module的使用方式來使用大概就可以然後在transformer的auto encoder裡面也有一個自己的就是比方說我們要對他做分類任務如果我們要對他做分類任務那麼也會有一個自己的處理方式那auto encoder跟rnn之間的會有一些明顯的不一樣的點就是auto encoder他並不像rnn那樣他是先處理第1個然後第1個再encode到hidden state再處理第2個再一直往後往後一直處理到結尾auto encoder他是所有的token平行的往前處理所以這邊embedding層他照樣是通過這個embedding層把資料變成dense vector [1:23:08] 然後這個dense vector在這一批dense vector在位到這個hiddenlayers裡面去然後這個hidden layers就是birth model他會有自己的處理的過程他會有attention fee for network然後他會有好多好多層的這樣的層然後output出來以後也會是一個同樣大小的這樣一個dense vector那這個dense vector他跟input也是一個1對應的關係就是第1個token就是cls就是我們在birth處理的時候他表示的是一個能夠代表這整句話訊息的這樣一個token然後接下來就是每一個token他對應的向量然後最後是給sep表示這句話的結束如果我們要做sequence classification的話我們會採用第1個就是他能夠表示整一個句子訊息的這個變量來取出來然後對這個token做down projection做一個映射然後獲得比方說我們是二分類任務的話那麼會獲得這兩個類別他相應自己的分數如果我們要對每一個token來做對每一個token來做分類任務的話做分類的話比方說是一些比方說ner任務啊 [1:24:38] 那這邊也是就是假設是分兩類那麼我們就把每一個token所對應的dense factor給取出來然後再做再做一個linear層再過一個linear層然後會得到每一個token他對他的分類結果OK那這個就是一個transformer的基本的實作過程基本的資料的大概處理過程在之後的作業當中我們會要求大家能夠是構建一個比方說從hugging face上面下載模型然後並且並且寫出一個自己模型的這樣一個過程並且通過這個模型來完成各種各樣可能是分類可能是生成任務的結果這任務的這樣一個目標所以所以大家可以先從hugging face開始來瞭解這個transformer這個大類模型它的使用方法是什麼樣的那在未來的助教課當中助教會再進一步的去講解一些其他的API其中也包含就是transformer更細節的使用還有一些像是像是其他一些好用的API我們應該怎麼調用的這樣一個實作方法那本場就是本次助教課大概講的內容就是如上所示 [1:26:08] 謝謝大家