大家好,歡迎回來。今天,我們的一些助教會進行演示。還不認識的同學注意了,這位是David,我課題組的學生,也是這門課的助教。另一位助教Chanaka稍后會出場。請大家記住他們的樣子,因為他們會負責答疑時間,并幫助大家完成項目,也會主持閱讀和討論環節。
在他們開始前,先說幾件瑣事。首先,請確保你已經提交了項目提案。有一個簡短的表格,需要你填寫隊友信息和項目偏好。我已經在Piazza上分享了大家的回復。如果你還沒找到隊友,想組建團隊,可以去看看那個表格,找找興趣相近的同學。
另外,第一個任務是在下周四進行項目提案的演示。到時候,每個小組需要確定一個想法,并用5分鐘時間快速介紹你們打算做什么、有什么想法、會用哪些數據集。課程會給出反饋,相關說明和評分細則都在Piazza上。之后一周,你們需要提交一份關于該提案的簡短報告,也就是文字版,方便我們閱讀和提供反饋。所以,記得填表、找隊友,并開始著手準備項目提案的演示和報告。
今天,我們會為大家提供一些實用技巧的教程,涵蓋如何使用PyTorch、構建模型、迭代以及調試機器學習模型。
那開始吧?David,交給你了。
謝謝介紹。我是David,這門課的助教。首先我會講如何使用PyTorch,然后基于一個非常簡單的項目,講講如何一步步進行。開始之前,請先打開這兩個鏈接,你會看到兩個代碼頁面,可以跟著我一起操作。今天的教程主要針對PyTorch新手。如果你已經熟悉PyTorch,這部分可能幫助不大,但我會盡量安排好結構,希望每個人都能有所收獲。我們開始講PyTorch。大家都能打開這個Colab筆記本嗎?
首先,非常簡要地介紹一下PyTorch。它是一個加速機器學習流程的庫。PyTorch的核心是張量(Tensor),可以理解為線性代數中多個矩陣的集合。
我們需要先導入PyTorch。然后看一些基本操作。初始化張量有幾種方式:如果你有一個二維數組,比如[1,2,3,4],可以直接調用`torch.tensor`來初始化。如果你用過NumPy,也可以把NumPy數組轉換成Torch張量。
我們先快速過一遍基礎內容。我們可以初始化特定形狀的張量,比如要一個2x3的矩陣,可以用`torch.rand`(隨機張量)、`torch.ones`或`torch.zeros`來初始化全1或全0的張量。
張量有不同類型,可以用`tensor.type()`查看。一個常見的錯誤來源就是張量類型不是你預期的。如果你初始化了一個整數類型的張量,就不能把它和浮點類型相乘后再存回整數類型,因為不兼容。所以加載數據時,記得用`tensor.type()`檢查類型,可以用print,或者用調試器。
另外,張量可以運行在不同設備上,比如CPU或GPU。用`tensor.device`可以查看張量所在的設備,這也是常見的錯誤點。比如兩個張量一個在CPU,一個在GPU,就不能直接相乘。需要用`tensor.to(device)`把張量移動到你想計算的設備上。
還有一些簡單的張量運算。比如兩個張量x和y,可以用`x+y`相加。注意,`x*y`是逐元素相乘,不是矩陣乘法。矩陣乘法要用`torch.mm`。這些就是PyTorch張量運算的簡要概覽。
接下來,我會用我們實驗室收集的一個小型數據集,帶大家完成一個小項目。這個數據集來自一些氣體傳感器,采集了特定氣味的數據。這個簡單教程很適合用來完整走一遍訓練、數據處理、評估等整個流程。
在深入模型之前,我們首先得看看數據。這一點我強烈建議:數據格式可能不是你期望的樣子。無論是自己收集還是從網上下載的數據,都很常見。文檔上說是一種格式,下載下來卻是另一種。所以在建模或訓練之前,實際查看數據非常重要。
我們運行代碼,下載實驗室采集的氣味數據。這個項目是一個三類分類任務:識別環境空氣、酒精和咖啡豆。加載數據后,打開數據文件夾,你會看到一堆CSV文件,對應各個類別。為了簡化,我們只用環境、酒精和咖啡豆這三類。然后用`os.listdir`列出所有文件,再打開一個文件看看內容。這一步非常重要。從數據中你會看到有很多列,對應不同的氣體傳感器。
可視化數據也很重要。處理時間序列數據時,正則化很關鍵,因為有些數據與分類相關,有些則無關。比如酒精檢測的圖中,紅色區域是傳感器的有效響應區間。我們先采集一些環境讀數,然后把待測物質靠近氣體傳感器,采集實際讀數。這樣能消除環境因素的影響,在這個項目中非常有用。
可以看到,有些氣體傳感器對物質非常敏感(比如NO2 BLC這類),有些則不相關(比如酒精傳感器可能不工作)。還有一些溫度、氣壓讀數,對分類任務幫助不大。
![]()
提問:這些數據是手動收集的嗎?具體怎么采集的?
答:我們有一個開關,可以自動在狀態0和1之間切換。狀態1代表有物體靠近,狀態0代表遠離。你可以看到,有些通道與目標物質相關,有些則無關。那應該刪掉這些列,還是保留它們參與訓練?我的建議是:大多數情況下,不需要刪除任何通道。因為如果某個通道對所有類別都不相關,神經網絡會自動學會忽略它。但如果模型完全無效,你可以嘗試去掉一些通道。不過大多數時候沒必要。
提問:如果你知道某個傳感器不工作,為什么還要保留它?
答:這個傳感器在這個樣本里不工作,但在其他樣本里是工作的。所以如果要刪除通道,通常應該查看所有數據,而不是只看一個文件。
我覺得神經網絡對單個通道的噪聲通常很魯棒。如果噪聲分散在很多通道,可能問題更大,那時可能需要用PCA等方法來降噪。但我們的數據相對干凈,不需要太多預處理。
不過我們確實對每個時間段取了平均值,并減去了環境讀數,因為我們發現每個傳感器的初始讀數在不同實驗之間波動很大。所以通過減去環境讀數得到相對變化,讓數據更穩定。這是預處理的一部分,不是數據采集過程。
另一個發現是,不同讀數的量級差異很大。比如有的傳感器范圍是0-2000,有的是0-5100。這說明我們需要做歸一化。這是最重要的預處理步驟之一,因為神經網絡對量級變化并不魯棒。如果一個通道的值高達2000,它很可能會主導整個訓練過程。
所以加載、合并數據,分離特征和標簽之后,需要進行歸一化。我們用scikit-learn的StandardScaler,把輸入x變換到(比如0-1)范圍,使其更魯棒。然后按8:2的比例劃分訓練集和測試集。
接著初始化數據集(Dataset)和數據加載器(DataLoader)。Dataset負責提供數據,DataLoader負責啟動多進程工作,通過并行處理加速數據加載。每次請求數據時,它會返回一個批次(batch)的樣本,而不是單個樣本。這樣做的優點是,模型計算出的梯度(即如何更新模型使其更好)會基于多個樣本的平均值,更加魯棒。另一個實用技巧是:在GPU內存允許的情況下,盡量使用最大的批次大小,這樣訓練過程最穩定。
最后設計模型。這是一個非常簡單的MLP(多層感知機)模型,堆疊了幾個線性層和隨機失活(Dropout)層。我們可以在這里調整層的大小。輸入是13維,輸出是3維(三類分類)。這兩個數字固定,但中間的隱藏層大小可以改。對于這種小型樣本數據,兩到三層通常就足夠了。
提問:如何決定層數和每層大小?
答:很好的問題。一個好的起點是“簡單開始”,先用兩三層看看效果。之后會有關于調試模型的詳細教程,可以再深入討論。
接著是訓練循環。大致流程是:從DataLoader獲取數據和標簽,把數據輸入模型,將模型輸出與理想輸出比較,計算損失(Loss)。得到損失后,計算梯度,它告訴我們應該如何更新參數來改進模型。最后調用優化器的`step`方法,真正更新模型。這就是完整的訓練生命周期。
訓練完成后,進行評估。過程類似,只是不再更新模型。我們得到了大約75%的準確率,還算可以。更詳細地看每個類別的精確率、召回率和F1分數,模型在環境和酒精上表現不錯,但對咖啡豆的識別效果較差。
之后你可以迭代改進模型。先找出哪些類別表現最好、哪些最差。針對最差的類別,分析是不是數據不足,或者其他原因。如果是數據問題,就去收集更多咖啡豆的樣本。這就是迭代改進模型的方法。
最后,別忘了保存模型,以便后續分析使用。以上就是關于如何用PyTorch訓練一個非常簡單模型的教程。
我知道跳過了很多細節,但如果有問題,現在可以問。
提問:窗口和樣本?
答:我們采樣了多個時間段,每個時間段大約10行,然后把整個讀數分割成不同的時間段。分類是在這些10行的片段上做的。這些設計決策在你的應用中可能非常重要,比如窗口選大選小、每個窗口對應的標簽是什么、如何劃分訓練/驗證/測試集。例如,如果訓練和測試數據來自同一天或同一個樣本,模型可能只是記住了樣本,而無法泛化到你真正應用傳感器的場景。
很多超參數需要你仔細設計和決定。這確實是個好問題。做訓練測試劃分時,要盡可能貼近真實場景。如果你打算在某一天訓練,另一天使用,那么劃分就應該符合這個設定。不應該用同一個實驗的前半段訓練、后半段測試,那樣模型表現可能很好,但無法反映真實性能。
另一個例子:我們在實驗室某個房間收集數據并訓練模型,但換到另一個房間或室外,模型就失效了,因為它沒學過不同形狀的房間以及溫度、濕度等環境因素。這意味著,如果你在一個場景下訓練,卻要在另一個場景下使用,就必須收集多樣化的數據,并相應地劃分。
接下來,我快速講一下用隨機森林(Random Forest)進行分類。隨機森林不是深度學習模型,它由許多決策樹組成,通過多數投票來綜合輸出。訓練非常快。我們劃分訓練測試集,初始化隨機森林分類器(來自scikit-learn),然后評估,得到了95.8%的準確率,遠高于之前的深度學習模型。這告訴我們:對于非常簡單且噪聲不大的數據,簡單的模型(如隨機森林)有時比花哨的深度學習模型表現好得多。
另一個實用建議:處理低維數據時(每個數據點只有幾百個數),表現最好的很可能是淺層模型,比如隨機森林或SVM。處理這類數據時,建議先從簡單的經典模型開始,再考慮深度學習模型。
提問:既然不同房間測試結果不同,那隨機森林對這種變化魯棒嗎?
答:我們還沒有另一個房間的數據。但一般來說,簡單模型對噪聲更魯棒,因為復雜模型的決策邊界非常緊湊,更容易過擬合訓練數據中的噪聲,從而對環境的改變不太適應。更直接地回答:如果模型在不同地點效果都不好,那可能需要新的數據格式。對于視覺任務,我們有強大的預訓練編碼器,可能在任何數據上都有效。但對于時間序列數據,跨領域遷移不容易。可能一個簡單模型在一個環境下工作良好,但在另一個環境下就不行。PyTorch教程就到這里。
在講如何調試模型之前,我們快速過一下Hugging Face的教程。Hugging Face不是單一的包,而是一套工具包,讓你用更少的代碼訓練更大的模型。它主要有兩個包:Transformers和Datasets。Transformers提供API來初始化帶預訓練權重的大模型,比如大語言模型。Datasets方便你從網上下載公開數據集。
怎么選模型或數據集?你可以在Hugging Face網站上搜索,看哪些模型在你的任務上受歡迎,選一個,把名字寫進代碼里就行。還有一些非官方的包也很流行,比如bitsandbytes,它提供量化功能,把大模型變小,更好適配GPU內存。另外Flash Attention包能讓訓練更快、更省顯存。
本教程我們將用LoRA(低秩適配)來微調一個大語言模型。LoRA本質上是一個適配器,通過減少可訓練參數來降低訓練所需內存。
現在打開Hugging Face教程。在使用模型前,需要登錄Hugging Face。運行登錄單元格,會彈出網頁要求輸入token。你可以在Hugging Face網站上創建新token,取個名字,然后復制粘貼過來登錄。登錄的原因是我們要用的模型需要授權訪問。如果你還沒獲得權限,需要同意協議并申請,通常會自動批準。
然后安裝幾個包,主要是Hugging Face相關的。在代碼中你會看到模型名、數據集名和列名。我們用的模型是StarCoderBase-1B(10億參數),量化方式是4-bit(來自Anvil的KIV4)。判斷一個模型能否裝進你的GPU,可以看這個參數量。經驗法則是:把參數量的“B”前面的數字乘以2,得到近似的最小推理所需顯存(GB)。對于訓練,需要的會更多一些,取決于具體方式。
LoRA有一批參數,決定了你對模型改動的大小。R和alpha越大,所需顯存越多,改動也越大。
然后一行代碼加載數據集,因為時間有限,我們只取4000個樣本。接著初始化分詞器(Tokenizer),它負責把英文單詞轉成模型能理解的token。處理這類大語言模型時,你通常不需要自己處理分詞器,很簡單,調用即可。
數據集部分定義了訓練過程如何從原始數據集中獲取數據。需要調用分詞器把文字轉換成token,這是傳統模型不需要、但大模型微調必須做的步驟。
然后加載模型。由于內存限制,我們加載4-bit模型(理想情況下16-bit訓練更穩定,但Colab上只能4-bit)。接著設置LoRA并開始訓練。Hugging Face有個很好的功能,與Weights & Biases(wandb,訓練監控工具)深度集成。運行訓練后,你會看到wandb的鏈接。打開wandb頁面,登錄并粘貼API key,就可以實時看到訓練過程。
在監控面板上,我通常關注兩個指標:GPU內存分配和GPU功耗。如果GPU內存分配過高(如95%),訓練后期很可能會內存溢出。如果過低,說明批次大小太小,訓練不理想。建議讓GPU內存分配保持在80%-90%之間。GPU功耗則反映GPU的忙碌程度。如果功耗只有10%,說明訓練或數據處理管線效率低下,GPU大部分時間空閑。理想情況下,功耗大于60%比較好。如果低于60%,需要檢查數據集實現、處理腳本和訓練管道,找出慢的原因。
除了GPU使用率,損失值也很重要,我們希望它下降。如果損失不降,說明有問題。不過頭幾步損失不降很正常,可能因為學習率問題或模型初期不理想。可能幾百步后還不降,那就不正常了,訓練不會收斂。
另外可以關注梯度范數(Grad Norm),即每一步梯度的平均大小。如果梯度范數很大,說明每次更新步長非常大,有時可能是有意為之,但隨著模型收斂到最優點,它應該逐漸減小。如果梯度范數在增加,說明訓練過程有問題。
評估部分就不做了,需要再花一小時。我們現在可以講調試模型的部分了。
調試模型更像一門藝術,而非純粹的科學。通常寫程序時,你能得到明確信號判斷程序是否正確。但機器學習模型很難判斷什么時候工作、什么時候不工作。這個調試清單就是為了讓你有一系列可以逐一檢查的項目,理解如何改進模型。如果你從未訓練過模型,這聽起來可能有點學究,但長遠來看很有用。
訓練神經網絡是門藝術:有些模型能訓練,有些不能,沒有絕對。我們想揭開這個過程的神秘面紗。你可能會遇到各種奇怪的bug:梯度奇高、梯度奇低、損失無窮大,怎么辦?這張幻燈片涵蓋了訓練模型前建議你先過一遍的要點。
首先是“成為數據的一部分”。作為機器學習從業者,我們總愛用最花哨的模型,但這往往適得其反。最重要的是數據。你應該花大量時間觀察數據,理解數據分布,看看自己能否找出模式。大多數情況下,尤其當你沒有大語言模型那樣的海量數據時,你需要設計能夠識別數據中特定模式的模型。所以,觀察數據,花大量時間理解并找出可建模的直觀模式。
有了直觀理解后,嘗試建立一個非常簡單的模型,得到一些初步信號。比如線性回歸或一個簡單的分類器。有時你用的數據集在GitHub上可能已經有人嘗試解決過,直接拿他們的模型試試看。在PyTorch中,初始權重是隨機矩陣。關鍵是要先固定隨機種子(fix the seed),讓過程確定化,避免額外的變異性。
盡量簡化你的模型,并且時刻關注損失函數的初始化。很多時候初始化損失是無窮大,這有很多原因,檢查這個能幫你判斷模型是否正常工作。
最后,如果你的模型完全不起作用,嘗試讓它過擬合(overfit)單個數據樣本。也就是讓模型記住那一個樣本,來判斷模型有沒有學習能力。如果連這個都做不到,你就需要回到設計更好的模型上。
所以,先做簡單模型,在小數據集上過擬合。如果達不到低錯誤率,就回到觀察數據的步驟,或者改進建模策略。過程中要可視化損失的變化,觀察損失如何下降、上升或出現異常。同時關注梯度,有時梯度會變成無窮大,可能是因為你量化得不夠好,或初始化不正確。這能讓你明白應該修復什么來讓模型工作。
最重要的建議是:一開始不要太擔心模型架構。很多時候我們總想搞個很酷的架構,以為需要研究論文里那些東西。別這樣做,這只會適得其反。
做完上述步驟后,要注意:機器學習關心的不是記憶(memorization),而是泛化(generalization)。為了泛化,需要正則化技術,比如Dropout、權重衰減等。只有當你確認模型能在訓練集上過擬合之后,再開始做這些。在那之前,把這些正則化項關掉。
最后我再強調一下:大多數情況下,你的模型可能完全正常,只是還沒找到正確的超參數。所以盡力去搜索多種超參數,尤其是小模型,找到合適的性能。我見過很多數據科學家直接跑默認參數,得到很差的準確率。但如果仔細調參,往往能得到很好的結果。嘗試這樣做。如果這些都做了,可以用不同的集成模型,或者讓模型訓練到收斂之后繼續訓練(過去收斂點),有時會有幫助。做完所有這些,再開始考慮更復雜的架構。
基本流程就是:先關注數據,建立簡單模型,在這些簡單模型上嘗試過擬合數據。一旦能過擬合,再進行正則化,然后針對你最終的下游任務進行微調。
這就是調試的基本思路和技巧。如果有問題,我很樂意回答。之后我們會分享兩篇博客,建議大家都看看,深入理解這個話題。
大家還有問題嗎?
特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.