Go與神經(jīng)網(wǎng)絡(luò):線性回歸
離發(fā)表上一篇與機器學習相關(guān)的文章《Go與神經(jīng)網(wǎng)絡(luò):張量運算》[1]已經(jīng)過去整整一年了,AI領(lǐng)域,特別是大模型領(lǐng)域的熱度不僅未有減弱,反而愈演愈烈。整個行業(yè)變得更卷,競爭更加激烈,大模型你方唱罷我登場,層出不窮,各自能力也都在不斷提升,并在自然語言處理、問答、生成等方面展現(xiàn)出強大的能力。同時基于RAG(Retrieval-Augmented Generation)[2]等技術(shù),大模型還可以實時檢索相關(guān)知識并融合到生成結(jié)果中,進一步提升了大模型在專業(yè)領(lǐng)域的應(yīng)用價值。
很多人說用好大模型不必非要了解大模型的底層原理,也許這句話是對的。但對于后端程序員的我來說,對底層原理的不理解,始終讓我有一種“不安全感”。我認為即使大模型的使用變得日益簡單和廣泛,但如果我們無法深入理解其工作機制,恐怕還是難以充分發(fā)揮它們的潛力,甚至無法準確評估它們的局限性和風險。
但對大模型原理的學習是一個循序漸進的學習過程,我們不能一蹴而就地達到對大模型原理的深入理解。我決定從最基礎(chǔ)的機器學習入手,從傳統(tǒng)機器學習解決問題的一般步驟開始,以線性回歸這個傳統(tǒng)傳統(tǒng)機器學習的"Hello, World"示例為切入點,逐步探討機器學習的基本概念和實現(xiàn)流程,這也是本篇文章的初衷與主要內(nèi)容。
1. 機器學習的那些事兒
1.1 人工智能的誕生
相對于機器學習(Machine Learning,ML),普通大眾更熟悉“人工智能(Artificial Intelligence,AI)[3]”這個字眼兒。
就像一千個人眼中有一千個哈姆雷特,每個人對人工智能的理解都不盡相同:有些人將其看成一個學術(shù)領(lǐng)域,有些人視之為人類文明下一個要實現(xiàn)的目標,懵懂無知的少年會將其想象為那種高大威猛的機器人(其實是隸屬于具身智能[4],人工智能和機器人學[5]的一個跨學科分支),而還有些人認為它是空中樓閣,永遠無法實現(xiàn)。
作為程序員,我們更聚焦工程領(lǐng)域。而工程領(lǐng)域主要是消化學術(shù)領(lǐng)域的研究,將其實現(xiàn)落地并應(yīng)用于人類生活的方方面面。那么學術(shù)領(lǐng)域是如何定義人工智能的呢?人工智能專家Stuart Russell[6]和Peter Norvig[7]在他們的聯(lián)合著作《人工智能:現(xiàn)代方法(第4版)》中將人工智能定義為對從環(huán)境中接收感知并執(zhí)行動作的智能體(Agent)的研究,并強調(diào)每個這樣的智能體都要實現(xiàn)一個將感知序列映射為動作的函數(shù)。
對人工智能的定義雖然內(nèi)容不長,但這里面卻蘊含著計算機科學家對人工智能幾十年的探索歷程與嘗試。
人工智能的概念始于20世紀50年代。1950年,阿蘭·圖靈(Alan Turing)[8]發(fā)表了《計算機器與智能[9]》一文,提出了著名的圖靈測試[10]。但人工智能一詞的正式提出還要等到6年后的1956年達特茅斯會議。對于人工智能領(lǐng)域而言,這是堪比理論物理學界1927年比利時第五屆索爾維會議(如下圖)[11]的一次會議。約翰·麥卡錫(John McCarthy)、馬文·閔斯基(Marvin Minsky,人工智能與認知學專家)、克勞德·香農(nóng)(Claude Shannon,信息論的創(chuàng)始人)、艾倫·紐厄爾(Allen Newell,計算機科學家)、赫伯特·西蒙(Herbert Simon,諾貝爾經(jīng)濟學獎得主)等科學家正聚在一起,討論著一個完全不食人間煙火的主題:用機器來模仿人類學習以及其他方面的智能。會議足足開了兩個月的時間,雖然大家沒有達成普遍的共識,但是卻為會議討論的內(nèi)容起了一個名字:人工智能。因此,1956年也就成為了人工智能元年。
1927年比利時第五屆索爾維會議合影
1.2 符號主義:早期的人工智能實現(xiàn)路徑
在人工智能的早期探索階段,研究主要集中于符號主義(Symbolism)和邏輯推理。這種方法使用符號來表示知識和問題,并通過邏輯推理來解決問題。這種方法依賴于明確的規(guī)則和符號系統(tǒng)來進行推理和決策。
邏輯推理是符號主義的核心,它使用邏輯規(guī)則來進行推理和決策。邏輯推理包括演繹推理、歸納推理和溯因推理:
- 演繹推理:從一般規(guī)則推導(dǎo)出特定結(jié)論(例如,從“所有人都會死”推導(dǎo)出“蘇格拉底會死”)。
- 歸納推理:從特定實例推導(dǎo)出一般規(guī)則(例如,從“蘇格拉底會死”推導(dǎo)出“所有人都會死”)。
- 溯因推理:從結(jié)果推斷出可能的原因(例如,從“蘇格拉底死了”推斷出“他是人”)。
LISP語言[12]在符號主義和邏輯推理盛行的階段發(fā)揮了重要作用,其強大的符號處理能力、遞歸和動態(tài)特性、交互式開發(fā)環(huán)境以及宏和元編程功能使其成為AI研究和開發(fā)的主要工具。其他語言如Prolog和Scheme也在特定領(lǐng)域中提供了重要支持。
雖然在機器學習大行其道的今天,符號主義系統(tǒng)不受待見了,但不可否認符號主義系統(tǒng)也有很多優(yōu)點:
- 可解釋性:符號主義系統(tǒng)的推理過程透明、易于理解。
- 明確規(guī)則:使用明確的規(guī)則和邏輯,使得系統(tǒng)行為可預(yù)測。
- 可靠性:在明確定義的領(lǐng)域內(nèi),符號主義系統(tǒng)可以提供可靠的推理結(jié)果。
不過,它的缺點也很明顯:
- 局限性: 依賴于預(yù)定義的規(guī)則和知識庫,難以處理復(fù)雜和動態(tài)的環(huán)境。
- 知識獲?。?知識工程需要大量人工干預(yù),獲取和維護知識庫成本高。
正是由于這些這些不足,以及當時計算能力和數(shù)據(jù)存儲的限制,AI研究在1970年代遇到了障礙,進入了第一次“AI寒冬”。許多早期的承諾未能實現(xiàn),資金和興趣減少。
1980年代,符號主義和邏輯推理迎來了第二次巔峰:知識工程和專家系統(tǒng)。專家系統(tǒng)是計算機程序,旨在模仿人類專家的決策能力。它們在特定領(lǐng)域內(nèi)使用顯式編碼的知識庫和推理引擎來解決復(fù)雜問題或提供建議。第一個成功的商用專家系統(tǒng)R1在數(shù)字設(shè)備公司(Digital Equipment Corporation,DEC)投入使用(McDermott, 1982),該程序幫助公司配置新計算機系統(tǒng)的訂單。截至1986年,它每年為公司節(jié)省約4000萬美元。到1988年,DEC的人工智能小組已經(jīng)部署了40個專家系統(tǒng),而且還有更多的專家系統(tǒng)在開發(fā)中。但事實證明,為復(fù)雜領(lǐng)域構(gòu)建和維護專家系統(tǒng)是困難的,一部分原因是系統(tǒng)使用的推理方法在面臨不確定性時會崩潰,另一部分原因是系統(tǒng)無法從經(jīng)驗中學習。專家系統(tǒng)的局限性和開發(fā)維護成本直接也導(dǎo)致了第二次“AI寒冬”的到來。
在兩次AI的興起和“寒冬”中,先行研究者們開發(fā)了許多基礎(chǔ)性的算法和系統(tǒng)。這些早期研究不僅解決了特定問題,還為AI的理論和實踐發(fā)展奠定了重要的基礎(chǔ)。隨著計算能力和數(shù)據(jù)資源的增加,AI研究逐漸從符號主義轉(zhuǎn)向數(shù)據(jù)驅(qū)動的方法,但這些早期成果仍然具有重要的歷史和學術(shù)意義。
1.3 數(shù)據(jù)驅(qū)動與機器學習
數(shù)據(jù)驅(qū)動方法依賴于大量數(shù)據(jù),通過從數(shù)據(jù)中學習模式和關(guān)系來進行預(yù)測和決策。這種方法不依賴于明確的布爾邏輯和規(guī)則,而是通過統(tǒng)計和算法來從數(shù)據(jù)中提取知識,即基于機器學習而不是手工編碼。
1990年代至2000年代,隨著計算能力的提升和數(shù)據(jù)量的增加,機器學習(ML)逐漸成為AI的主要方法。基于統(tǒng)計學和概率論的算法(如支持向量機和決策樹)獲得了成功。大數(shù)據(jù)的可用性和向機器學習的轉(zhuǎn)變幫助人工智能恢復(fù)了商業(yè)吸引力。大數(shù)據(jù)是2011年IBM的Watson系統(tǒng)在《危險邊緣》(Jeopardy!)問答游戲中戰(zhàn)勝人類冠軍的關(guān)鍵因素,這一事件深深影響了公眾對人工智能的看法。
《人工智能:現(xiàn)代方法(第4版)》是這樣定義機器學習的:如果一個智能體通過對世界進行觀測來提高它的性能,我們稱其為智能體學習(learning)。學習可以是簡單的,例如記錄一個購物清單,也可以是復(fù)雜的,例如愛因斯坦推斷關(guān)于宇宙的新理論。當智能體是一臺計算機時,我們稱之為機器學習(machine learning):一臺計算機觀測到一些數(shù)據(jù),基于這些數(shù)據(jù)構(gòu)建一個模型(model),并將這個模型作為關(guān)于世界的一個假設(shè)(hypothesis)以及用于求解問題的軟件的一部分。不通過合適的方式編程來解決,而是希望一臺機器自主進行學習并解決問題,其原因主要有兩個:
- 程序的設(shè)計者無法預(yù)見未來所有可能發(fā)生的情形。比如一個被設(shè)計用來導(dǎo)航迷宮的機器人無法掌握每一個它可能遇到的新迷宮的布局。
- 有時候設(shè)計者并不知道如何設(shè)計一個程序來求解目標問題。比如識別人臉。
可以說,機器學習是另外一種實現(xiàn)人工智能的路徑(前一種是符號主義和邏輯推理),它是一類強大的可以從經(jīng)驗中學習的技術(shù)。通常采用觀測數(shù)據(jù)或與環(huán)境交互的形式,機器學習算法會積累更多的經(jīng)驗,其性能也會逐步提高。
機器學習的興起同樣離不開早期研究者的成果:
- 感知機(1957): Frank Rosenblatt 設(shè)計的感知機是第一個用于分類任務(wù)的人工神經(jīng)網(wǎng)絡(luò)模型,能夠?qū)W習二分類任務(wù)。感知機也被視為一種最簡單形式的前饋式人工神經(jīng)網(wǎng)絡(luò)。
- K均值聚類(1967): James MacQueen 提出的K均值聚類算法,是最早的聚類分析方法之一。
- 決策樹(ID3, 1986): Ross Quinlan 提出的 ID3 算法,是決策樹學習的基礎(chǔ)。
- 支持向量機(1992): Vladimir Vapnik 和 Alexey Chervonenkis 提出了支持向量機,為高維數(shù)據(jù)分類問題提供了強有力的解決方案。
- 多層感知器(1986): 由 Geoffrey Hinton 等人推廣的反向傳播算法(Backpropagation),使得訓練多層神經(jīng)網(wǎng)絡(luò)成為可能。
- 梯度提升樹(2000): Jerome Friedman 提出的梯度提升樹(Gradient Boosting Machines),在分類和回歸任務(wù)中表現(xiàn)出色。
- 隨機森林(2001): Leo Breiman 提出的隨機森林算法,通過集成多個決策樹提高了模型的準確性和魯棒性。
進入2010年后,在大規(guī)模數(shù)據(jù)集以及GPU硬件加速的賦能下,深度神經(jīng)網(wǎng)絡(luò)逐漸成為主流且表現(xiàn)卓越的機器學習方案,深度學習走向前臺:
- AlexNet(2012): Alex Krizhevsky 等人在 ImageNet 大賽上使用卷積神經(jīng)網(wǎng)絡(luò)(CNN)贏得了第一名,推動了深度學習在計算機視覺中的應(yīng)用。
- 生成對抗網(wǎng)絡(luò)(GAN, 2014): Ian Goodfellow 等人提出的生成對抗網(wǎng)絡(luò),開啟了生成模型的新方向。
- BERT(2018): Google 提出的雙向編碼器表示(BERT)模型,在自然語言處理任務(wù)中取得了突破性進展。
- Transformers(2022):Transformers模型及其變種在自然語言處理、圖像處理等多個領(lǐng)域取得了顯著進展,典型代表是ChatGPT的推出。
1.4 人工智能關(guān)系圖
基于上面的說明,我們下面用一張圖說明一下人工智能、機器學習、神經(jīng)網(wǎng)絡(luò)以及深度學習的關(guān)系:
圖片
而神經(jīng)網(wǎng)絡(luò)是支撐機器學習的重要技術(shù),是深度學習的核心技術(shù)。關(guān)于神經(jīng)網(wǎng)絡(luò),我們會在后面的系列文章中重點說明。
網(wǎng)絡(luò)上也有一個圖,可以更詳細地展示各個范圍內(nèi)的具體技術(shù),大家也可以參考一下:
圖片
1.5 機器學習的本質(zhì)
機器學習就是從數(shù)據(jù)中發(fā)現(xiàn)規(guī)律,發(fā)現(xiàn)的這個規(guī)律就是“模型”, 更具體來說就是一個或一組復(fù)合在一起的函數(shù)。而發(fā)現(xiàn)規(guī)律的這個過程就叫“學習”或叫“訓練”。這個過程與人類學習的有些相似:
圖片
人類和機器都需要輸入信息來開始學習。人類通過感官感知信息,機器通過傳感器或數(shù)據(jù)集獲取數(shù)據(jù)。人類通過理解和記憶進行學習,機器通過訓練數(shù)據(jù)調(diào)整模型參數(shù)進行學習。人類在大腦中存儲知識和經(jīng)驗,機器在模型參數(shù)和結(jié)構(gòu)中存儲學到的模式和規(guī)則。人類根據(jù)實踐中的反饋調(diào)整和改進知識,機器根據(jù)評估和實際應(yīng)用中的反饋調(diào)整模型參數(shù)和結(jié)構(gòu)。兩者盡管實現(xiàn)手段不同,但核心思想都是從輸入數(shù)據(jù)中學習知識和模式,通過反饋進行調(diào)整和改進,并不斷適應(yīng)新的環(huán)境和問題。
上圖中使用神經(jīng)網(wǎng)絡(luò)的形式呈現(xiàn)了學習/訓練后的模型,其實在一些傳統(tǒng)機器學習的簡單場景下,訓練后的模型可能就是一個簡單的一元線性函數(shù),比如:f(x) = wx + h。
訓練后的模型便可以應(yīng)用于真實環(huán)境中的數(shù)據(jù),進行推理和預(yù)測(serve/predict)。比如說,一個經(jīng)過大量真實病歷數(shù)據(jù)訓練后得到醫(yī)療診斷模型,就可以用來預(yù)測和診斷新的病患情況了。
到這里,你可能依然對機器學習一知半解。別著急,之前我也是這樣,就想親手訓練一個模型來直觀體會一下什么是機器學習。接下來,我們就來訓練一個Hello,World級別的模型,不過在真正動手之前,我們還是要先來了解一下機器學習中的術(shù)語(“黑話”)與訓練的一般步驟。
2. 機器學習的術(shù)語與一般步驟
機器學習本身就有不低的門檻,因此我們將由淺入深的來學習機器學習的術(shù)語,并簡要說明一下機器學習項目的一般步驟。
2.1 特征、標簽與模型
在下圖中,我們以一個簡單的多元線性回歸模型(即一個多元一次函數(shù))來說明一下一些機器學習中常見的術(shù)語:
圖片
我們先介紹與數(shù)據(jù)有關(guān)的幾個重要術(shù)語,其他在后面說明機器學習的一般步驟時,結(jié)合具體的場景再行講解。機器學習離不開數(shù)據(jù),如上圖中左上角的表格就是“喂給”機器學習訓練的**訓練數(shù)據(jù)集(training dataset )**。
上圖中的數(shù)據(jù)集是一個常見的房價相關(guān)數(shù)據(jù),該數(shù)據(jù)集有三條數(shù)據(jù),它們組成了該數(shù)據(jù)集的數(shù)據(jù)樣本。表中每條數(shù)據(jù)有三個影響房價的“因子”:居住面積、離市中心距離和建成時間(也就是房齡),這些因子共同決定了房子的價格。在機器學習中,我們稱這些“因子”為**特征(feature)。而房價則被稱為標簽(label)**。從數(shù)據(jù)來看,這三個特征表現(xiàn)出明顯的與房價(y)的相關(guān)性,如下圖:
圖片
機器學習的目的就是找到通過特征預(yù)測標簽的函數(shù)(即模型),然后將得到的函數(shù)應(yīng)用于生產(chǎn)中進行標簽預(yù)測。特征是機器學習模型的輸入,標簽是機器學習模型的輸出。無論是在訓練階段,還是在預(yù)測階段。特征的個數(shù)稱為特征的維度,維度越高,數(shù)據(jù)集越復(fù)雜。
了解完特征、標簽和模型后,我們來看看機器學習項目的一般步驟,更具體來說就是機器學習訓練的步驟,一旦訓練ok,得到模型,模型應(yīng)用就比較簡單了。
2.2 機器學習訓練的一般步驟
圖片
上圖展示了機器學習訓練的一般步驟,我們逐個說明一下。
2.2.1 數(shù)據(jù)收集與預(yù)處理
就像人類要從各種資料(書籍、媒體等)中學習一樣,機器也要從數(shù)據(jù)中學習。沒有數(shù)據(jù),機器學習就無從談起。數(shù)據(jù)也是通過機器學習解決生活中實際問題的前提。
數(shù)據(jù)收集渠道有多種,有爬取互聯(lián)網(wǎng)的數(shù)據(jù),有開源數(shù)據(jù)集(Image Net、Kaggle、Google Public Data Explorer),有購買的,還有客戶積攢的海量歷史數(shù)據(jù)等。這些數(shù)據(jù)拿到手后,還不能直接喂給模型進行訓練,因為業(yè)界有句名言“輸入的是垃圾,輸出的也是垃圾”(Garbage in, garbage out),我們需要對數(shù)據(jù)進行分析和預(yù)處理,了解數(shù)據(jù)內(nèi)在關(guān)系并使其滿足機器學習訓練的規(guī)格和質(zhì)量要求,最后還需要做特征的提取,即使用數(shù)據(jù)的領(lǐng)域知識來創(chuàng)建/識別出那些使機器學習算法起作用的特征的過程。
數(shù)據(jù)的預(yù)處理是十分重要的工作,預(yù)處理的好壞直接決定了訓練出來的機器學習模型的有效性。在《零基礎(chǔ)學習機器學習》一書中提到了數(shù)據(jù)預(yù)處理工作包含的幾項內(nèi)容:
- 可視化:用Excel表和各種數(shù)據(jù)分析工具(如Matplotlib等)從各種角度(如列表、直方圖、散點圖等)探索一下數(shù)據(jù)。對數(shù)據(jù)有了基本的了解后,才方便進一步分析判斷,即為后續(xù)的模型選擇奠定基礎(chǔ)。
- 數(shù)據(jù)向量化[13]:把原始數(shù)據(jù)格式化,使其變得機器可以讀取。例如,將原始圖片轉(zhuǎn)換為機器可以讀取的數(shù)字矩陣,將文字轉(zhuǎn)換為one-hot編碼,將文本類別(如男、女)轉(zhuǎn)換成0、1這樣的數(shù)值。
- 處理壞數(shù)據(jù)和空數(shù)據(jù):一條數(shù)據(jù)可不是全部都能用,要利用數(shù)據(jù)處理工具來把“搗亂”的“壞數(shù)據(jù)”(冗余數(shù)據(jù)、離群數(shù)據(jù)、錯誤數(shù)據(jù))處理掉,把缺失值補充上。
- 特征縮放:可以顯著提升模型的性能和訓練效率。許多機器學習算法,例如梯度下降法,依賴于特征之間的距離計算。如果特征的尺度差異很大,會導(dǎo)致算法在不同特征方向上以不同的速度進行更新,從而降低收斂速度。特征縮放可以將所有特征縮放到相同的尺度,使算法能夠更快地收斂到最優(yōu)解。特征尺度差異過大可能導(dǎo)致數(shù)值計算不穩(wěn)定,例如出現(xiàn)梯度爆炸或梯度消失現(xiàn)象,影響模型訓練效果。特征縮放還可以使模型的權(quán)重更加可解釋。當特征尺度差異很大時,模型的權(quán)重可能無法反映特征的實際重要性。特征縮放可以使權(quán)重更加反映特征的真實貢獻。
特征縮放適用于大多數(shù)機器學習算法,包括線性回歸、邏輯回歸、支持向量機、神經(jīng)網(wǎng)絡(luò)等。常見的特征縮放方法包括如下幾種:
- 標準化 (Standardization):對數(shù)據(jù)特征分布的轉(zhuǎn)換,目標是使其符合正態(tài)分布(均值為0,標準差為1)。在實踐中,會去除特征的均值來轉(zhuǎn)換數(shù)據(jù), 使其居中,然后除以特征的標準差來對其進行縮放。
- 歸一化/規(guī)范化 (Normalization):將特征數(shù)據(jù)縮放到特定范圍,通常是0到1之間。歸一化不會改變數(shù)據(jù)的分布形態(tài)。
數(shù)據(jù)預(yù)處理還包括特征工程和特征提取,即確定數(shù)據(jù)中究竟哪個特征對問題的解決會起到關(guān)鍵作用,并提取出來作為后續(xù)訓練和預(yù)測的輸入特征。許多現(xiàn)代機器學習算法,如深度學習模型,可以從原始數(shù)據(jù)中學習復(fù)雜的表示形式,而不需要明確的特征工程,但是特征工程仍然在機器學習工作流程中扮演著重要角色,尤其是在領(lǐng)域知識、可解釋性和數(shù)據(jù)質(zhì)量方面起到重要作用。不過特征提取是一個細分領(lǐng)域,內(nèi)容很多(對之我也不甚了解),這里就不展開說了。
2.2.2 選擇機器學習模型
AI科學家期望能有一個通用的機器學習模型可以學習一切類型的數(shù)據(jù),并處理所有領(lǐng)域的任務(wù),這樣世界將變得簡單了。但就目前AI發(fā)展的水平來看,還沒有一個通用的機器學習模型可以適合于所有類型的數(shù)據(jù)和任務(wù),即便是當今大熱的預(yù)訓練的大語言模型也可能不勝任某一領(lǐng)域的工作。在前期的傳統(tǒng)機器學習階段,不同的數(shù)據(jù)和問題需要采用不同的機器學習方法和模型。
影響機器學習模型選擇的一些關(guān)鍵的因素包括:
- 數(shù)據(jù)類型和特征:比如圖像數(shù)據(jù)和文本數(shù)據(jù)一般需要不同的模型。數(shù)據(jù)的維度、稀疏程度等也會影響選擇的模型。
- 任務(wù)類型:分類、回歸、聚類等任務(wù)適合不同的模型。有監(jiān)督學習和無監(jiān)督學習也需要不同的方法。
- 數(shù)據(jù)規(guī)模:對于大規(guī)模數(shù)據(jù),可擴展性強的模型如深度學習效果更好。小樣本數(shù)據(jù)可能更適合傳統(tǒng)的機器學習算法。
- 領(lǐng)域知識:某些領(lǐng)域問題需要結(jié)合專業(yè)領(lǐng)域知識,不能單純依賴通用的機器學習模型。
這里提到了有監(jiān)督學習和無監(jiān)督學習,提到了分類、回歸、聚類等任務(wù)類型,我們需要簡單科普一下這些概念。
機器學習中,有監(jiān)督學習和無監(jiān)督學習是兩種主要的學習方法,它們有各自擅長的任務(wù)類型。
有監(jiān)督學習是一種通過使用已標注的數(shù)據(jù)(即如前面圖中的訓練數(shù)據(jù)集那樣,樣本數(shù)據(jù)包含特征與對應(yīng)的標簽)來訓練模型的方法。在這種方法中,每個訓練樣本都是一個輸入-輸出對,模型通過學習這些對的關(guān)系來預(yù)測新的輸入數(shù)據(jù)的輸出。有監(jiān)督學習擅長的任務(wù)類型包括下面這幾個:
- 分類任務(wù):將輸入數(shù)據(jù)分類到預(yù)定義的類別中,例如垃圾郵件檢測、圖像分類。
- 回歸任務(wù):預(yù)測連續(xù)的數(shù)值輸出,例如房價預(yù)測(前面圖中的示例)、股票價格預(yù)測。
- 標注任務(wù):為輸入數(shù)據(jù)中的每個元素分配一個標簽。例如:命名實體識別(NER):在文本中識別出人名、地名、組織名等。
- 排序任務(wù):根據(jù)某種標準對項目進行排序。例如:信息檢索、推薦系統(tǒng)。
- 序列預(yù)測任務(wù):根據(jù)時間序列數(shù)據(jù)進行預(yù)測。例如,銷售額預(yù)測、天氣預(yù)報等。
使用有監(jiān)督學習,我們需要向模型提供巨大數(shù)據(jù)集,且每個數(shù)據(jù)樣本都需要包含特征和相應(yīng)標簽值,這很可能是一個既耗時又費錢的過程。
而無監(jiān)督學習則是一種通過使用未標注的數(shù)據(jù)來訓練模型的方法。在這種方法中,模型試圖從數(shù)據(jù)中發(fā)現(xiàn)結(jié)構(gòu)或模式,而無需使用明確的輸入-輸出對。無監(jiān)督學習擅長的任務(wù)類型包括下面幾個:
- 聚類任務(wù):將相似的樣本歸為一類,比如給定一組照片,模型能把它們分成風景照片、狗、嬰兒、貓和山峰。同樣,給定一組用戶的網(wǎng)頁瀏覽記錄,模型能將具有相似行為的用戶聚類。
- 降維任務(wù):減少數(shù)據(jù)的維度,同時保持其重要特征,例如主成分分析(PCA)問題,模型能否找到少量的參數(shù)來準確地捕捉數(shù)據(jù)的線性相關(guān)屬性?比如,一個球的運動軌跡可以用球的速度、直徑和質(zhì)量來描述。
- 異常檢測:識別數(shù)據(jù)中的異?;虍惓DJ剑缙墼p檢測、設(shè)備故障檢測。
這兩種方法在不同的應(yīng)用場景中各有所長,選擇哪種方法通常取決于數(shù)據(jù)的特性和具體的任務(wù)需求。
我們以前面圖中的房價預(yù)測問題為例,根據(jù)前面關(guān)于有監(jiān)督和無監(jiān)督的任務(wù)類型以及帶有標簽的數(shù)據(jù)對的訓練數(shù)據(jù)集,我們初步判斷應(yīng)該選擇線性回歸模型。當然,你也可以自己探索數(shù)據(jù)集中一些特征與標簽的關(guān)系,比如我們利用gonum.org/v1/plot相關(guān)包分別畫出房屋面積、離市中心距離兩個特征與標簽房價的散點圖(當然這是自己生成的一組訓練數(shù)據(jù)集,具體描畫代碼參見https://github.com/bigwhite/experiments/blob/master/go-and-nn/linear-regression/plotter.go):
圖片
從數(shù)據(jù)的特征散點圖,可以看出一些特征與標簽之間的線性關(guān)系,這符合使用線性回歸模型的要求。線性回歸基于幾個簡單的假設(shè):首先,假設(shè)自變量(x1, x2, x3, ..., xn)和因變量y之間的關(guān)系是線性的,即y可以表示為自變量集合中元素的加權(quán)和。以前面的房價預(yù)測問題為例,線性模型對應(yīng)的假設(shè)函數(shù)可以表示為居住面積、與市中心距離以及房齡的加權(quán)和,就像下面這樣:
圖片
這個函數(shù)叫做假設(shè)函數(shù)(也叫預(yù)測函數(shù)),其中的w1、w2和w3稱為權(quán)重,權(quán)重決定了每個特征對我們預(yù)測值的影響。b稱為偏置(bias)、 偏移量(offset)或截距(intercept)。偏置是指當所有特征都取值為0時,預(yù)測值應(yīng)該為多少。
現(xiàn)在權(quán)重w1、w2、w3和偏置b的值都是未知的,它們也被稱為模型內(nèi)的參數(shù),直接影響模型的預(yù)測結(jié)果。
接下來的訓練就是為了得到這些參數(shù)的合理值,使得假設(shè)函數(shù)得到的結(jié)果與真實房價越接近越好。
2.2.3 訓練
到這里,我們擁有了一份訓練數(shù)據(jù)集(帶標簽)以及一個權(quán)重和偏置參數(shù)未知的多元線性假設(shè)函數(shù)(y')。而我們接下來要做的就是找到假設(shè)函數(shù)中各個未知參數(shù)的合理值。
圖片
機器學習的“學習訓練”過程非常樸素,就是將訓練數(shù)據(jù)集中的特征逐條喂給y',并將得到的結(jié)果與訓練數(shù)據(jù)集中的標簽比對,如果差距過大,則調(diào)整y'的權(quán)重參數(shù)和偏置,然后再重復(fù)一輪學習,這樣循環(huán)往復(fù)直到通過y'計算得到的結(jié)果與標簽的差距在預(yù)期范圍以內(nèi)。
不過,這個過程看似容易,但真正實施起來,還有很多“阻塞點”要突破。以y'這個多元線性函數(shù)模型為例,首先就是權(quán)重和偏置參數(shù)的初始值。在我們這篇入門文章中,針對y'這個簡單的線性函數(shù),我們可采用隨機初始化的方式,即將參數(shù)隨機地設(shè)置在一個合理的范圍內(nèi)。這種方法簡單快捷,但對于復(fù)雜的模型,可能會導(dǎo)致收斂速度慢或陷入局部最優(yōu)。關(guān)于初始參數(shù)的選擇也是一個細分方向,這里就不展開說明了。
其次,我們要確定一個y'計算結(jié)果與訓練數(shù)據(jù)集中標簽值的差距計算方法。機器學習領(lǐng)域稱這個計算方法為損失函數(shù)(Loss function)。損失也就是 誤差,也稱為成本(cost)或代價,用于體現(xiàn)當前預(yù)測值和真實值之間的差距。它是一個數(shù)值,表示對于單個樣本而言模型預(yù)測的準確程度。如果模型的預(yù)測完全準確,則損失為0;如果不準確,就有損失。在機器學習中,我們追求的當然是比較小的損失。不過,模型好不好還不能僅看單個樣本,而是要針對所有數(shù)據(jù)樣本,找到一組平均損失“較小”的函數(shù)模型。計算平均損失是每一個機器學習項目的必要環(huán)節(jié)。損失函數(shù)實質(zhì)上就是用來計算平均損失的,它是模型參數(shù)的函數(shù):L(w1, w2, w3, b)。機器學習的訓練過程就是找一組模型參數(shù)的解,比如本示例中的(w1, w2, w3, b),使得損失函數(shù)的計算結(jié)果最小。
機器學習中的損失函數(shù)有很多,針對不同任務(wù)類別,選擇一個合適的即可。
比如,用于回歸的損失函數(shù)就有:均方誤差(Mean Square Error,MSE)函數(shù)、平均絕對誤差(Mean Absolute Error,MAE)函數(shù)和平均偏差誤差(mean bias error)函數(shù)。用于分類的損失函數(shù)有交叉熵損失(cross-entropy loss)函數(shù)和多分類SVM損失(hinge loss)函數(shù)等。
對于我們的回歸問題來說,下面的均方差函數(shù)L就可以滿足評估參數(shù)的目的了。
圖片
在這個函數(shù)中,yi'基于樣本數(shù)據(jù)的特征經(jīng)由假設(shè)函數(shù)計算出來的值,yi則是樣本數(shù)據(jù)的標簽值。假設(shè)只有一個樣本數(shù)據(jù)如下:
x1 = 55, x2 = 11, x3 = 5, y = 210
我們的假設(shè)函數(shù)為:y' = 0.1x1 + 0.1x2+0.1x3 + 0.1 ,即初始參數(shù)w1 = w2 = w3 = b = 0.1。那么我們可以計算一下針對這個樣本的損失:
y' = 0.1 * 55 + 0.1 * 11 + 0.1 * 5 + 0.1 = 7.2
L = 1/2 * (7.2 - 210)^2 = 20563.920000000002
這個損失函數(shù)值看起來就不大行:),我們需要調(diào)整模型參數(shù)再戰(zhàn)!但如何調(diào)整呢?w1調(diào)大?w2調(diào)?。縲3不動?盡管現(xiàn)在算力已經(jīng)很強大了,但我們也不能拍腦袋亂猜!我們需要一種科學的方法為機器學習后續(xù)的參數(shù)調(diào)整指明方向,這樣才能大幅縮短訓練過程,并得到滿足需求的模型參數(shù)組合。
大多流行的優(yōu)化算法通?;谝环N基本方法–梯度下降(gradient descent)。簡而言之,在每個步驟中,梯度下降法都會檢查每個參數(shù),看看如果僅對該參數(shù)進行少量變動,訓練集損失會朝哪個方向移動。然后,它在可以減少損失的方向上優(yōu)化參數(shù)。
梯度下降的過程就是在程序中一點點變化參數(shù)w1、w2、w3和b,使L ,也就是損失值逐漸趨近最低點(也稱為機器學習中的最優(yōu)解)。而要實現(xiàn)這一點,我們需要借助導(dǎo)數(shù)。導(dǎo)數(shù)描述了函數(shù)在某點附近的變化率(比如:L正在隨著w1增大而增大還是減小),而這正是進一步猜測更好的權(quán)重時所需要的全部內(nèi)容。即梯度下降法通過求導(dǎo)來計算損失曲線在起點處的梯度。此時,梯度就是損失曲線導(dǎo)數(shù)的矢量,它可以讓我們了解哪個方向距離目標“更近”或“更遠”。如果求導(dǎo)后梯度為正值,則說明L正在隨著w增大而增大,應(yīng)該減小w,以得到更小的損失。如果求導(dǎo)后梯度為負值,則說明L正在隨著w增大而減小,應(yīng)該增大w,以得到更小的損失。
在單個權(quán)重參數(shù)的情況下,損失相對于權(quán)重的梯度就稱為導(dǎo)數(shù);若考慮偏置,或存在多個權(quán)重參數(shù)時(就像我們上面的房價預(yù)測示例),損失相對于單個權(quán)重的梯度就稱為偏導(dǎo)數(shù)。
在上面示例中,損失函數(shù)L是權(quán)重參數(shù)和偏置的函數(shù),表示為L(w1, w2, w3, b)。我們需要分別求出L相對于w1、w2、w3和b的偏導(dǎo)數(shù)來決定后續(xù)各個權(quán)重參數(shù)和偏置參數(shù)的調(diào)整方向(增大還是減?。N覀円訪對w1的偏導(dǎo)數(shù)為例,給出偏導(dǎo)數(shù)公式的推導(dǎo)過程:
圖片
我們看到:針對每個樣本,我們計算其損失值(y'-y)與該樣本特征(x1)的乘積。取這些乘積的平均值就得到了L對w1的偏導(dǎo)值。
依次類推,我們可以得到L對w1、w2、w3和b的偏導(dǎo)數(shù)公式:
圖片
上面的偏導(dǎo)數(shù)為我們指定了參數(shù)調(diào)整方向,下面是w1、w2、w3和b的更新公式:
圖片
這種計算梯度并反向更新模型參數(shù)的過程就稱為“反向傳播”。
上面參數(shù)更新公式中有一個新的變量α,該變量代表的是學習率(learning rate)。是一個超參數(shù),它控制著模型參數(shù)更新的步伐大小。在梯度下降過程中,學習率決定了每次更新參數(shù)時移動的步長。學習率的引入是為了控制模型訓練的速度。如果學習率太大,參數(shù)更新步伐過大,可能導(dǎo)致模型無法收斂甚至發(fā)散;如果學習率太小,參數(shù)更新步伐過小,訓練時間會過長且可能陷入局部最小值。
w1的更新公式是w1減去損失函數(shù)相對于w1的偏導(dǎo)數(shù)乘以學習率,這個公式表示,我們沿著損失函數(shù)梯度的負方向更新參數(shù),因為梯度的方向是損失函數(shù)增大的方向,所以負方向是使損失函數(shù)減小的方向。
到這里,我們已經(jīng)可以實現(xiàn)訓練的閉環(huán)了!訓練后的模型可以使用另外一套測試數(shù)據(jù)集來評估模型的效果。但訓練出來的模型是否真的是滿足要求的呢?還不一定,很多情況下,我們還需要對超參進行調(diào)試以繼續(xù)優(yōu)化模型。
2.2.4 超參調(diào)試與性能優(yōu)化
在上面的講解中,我們知道w1、w2、w3和b是模型內(nèi)的參數(shù),這些參數(shù)通過y'正向傳播和基于梯度下降的反向傳播在多輪訓練中得以更新優(yōu)化,并得到一個合理的值。這些值是機器從數(shù)據(jù)中學習到的,不需要我們手工調(diào)整。但還有一些參數(shù),比如上面提到的學習率(learning rate)、訓練輪數(shù)(Epochs)等,是模型外部的可以通過人工調(diào)節(jié)的參數(shù),這樣的參數(shù)稱為**超參數(shù)(Hyperparameters)**。大多數(shù)機器學習從業(yè)者真正花費相當多的時間來調(diào)試的正是這類超參數(shù)。
在實際應(yīng)用中,選擇合適的學習率和訓練輪數(shù)等超參數(shù)通常需要結(jié)合以下方法:
- 經(jīng)驗法則:基于先前經(jīng)驗和領(lǐng)域知識設(shè)定初始值。
- 交叉驗證:通過交叉驗證選擇一組最優(yōu)的超參數(shù)。
- 網(wǎng)格搜索:在多個可能的超參數(shù)組合上進行搜索,找到效果最好的參數(shù)組合。
- 學習率調(diào)度:動態(tài)調(diào)整學習率,比如在訓練過程中逐漸減小學習率。
超參數(shù)對模型效果和優(yōu)化的影響非常重要,選擇合適的超參數(shù)可以顯著提高模型性能。本文是入門文章,關(guān)于超參的調(diào)優(yōu)就不展開說明了。
基于通過上面的對機器學習的術(shù)語、概念和對訓練一般步驟的了解,接下來,我們通過一個實例來訓練一個最簡單的機器學習模型:線性回歸模型。這也被稱為機器學習領(lǐng)域的“Hello, World”。
3. 線性回歸:機器學習的Hello, World
我們按照前面關(guān)于機器學習的一般步驟,逐步展開該示例的說明。
3.1 準備數(shù)據(jù)和預(yù)處理
我們這個示例依舊是預(yù)測房價,但是為了簡單,我們不使用那些公共數(shù)據(jù)集(比如kaggle平臺[14]上的數(shù)據(jù)),而是讓大模型幫我生成兩個小規(guī)模的數(shù)據(jù)集,一個是用于訓練的train.csv,一個是用于測試的test.csv:
$cat train.csv
面積,距離,房價
50,10,200
60,12,220
70,15,250
80,20,300
90,25,330
100,30,360
110,35,390
120,40,420
130,45,450
140,50,480
$cat test.csv
面積,距離,房價
55,11,210
65,13,230
75,17,260
85,22,310
95,27,340
105,32,370
115,37,400
125,42,430
135,47,460
145,52,490
還是為了簡單,我們在這兩份數(shù)據(jù)集中僅使用兩個特征:面積和離市中心距離。
接下來,我們就通過編碼來實現(xiàn)對csv文件的讀取:
// go-and-nn/linear-regression/main.go
func readCSV(filePath string) ([][]float64, error) {
file, err := os.Open(filePath)
if err != nil {
return nil, err
}
defer file.Close()
reader := csv.NewReader(file)
records, err := reader.ReadAll()
if err != nil {
return nil, err
}
data := make([][]float64, len(records)-1)
for i := 1; i < len(records); i++ {
data[i-1] = make([]float64, len(records[i]))
for j := range records[i] {
data[i-1][j], err = strconv.ParseFloat(records[i][j], 64)
if err != nil {
return nil, err
}
}
}
return data, nil
}
readCSV用于從CSV文件中讀取所有樣本數(shù)據(jù)(已去掉了header),所有樣本數(shù)據(jù)(包括特征與標簽)都存儲在一個[][]float64類型的變量中。
拿到數(shù)據(jù)后,我們便可以對其進行標準化,前面說過通常情況下,標準化后的數(shù)據(jù)會使模型訓練更加穩(wěn)定和快速,從而可能提高模型的預(yù)測性能。下面是我們實現(xiàn)用于對訓練數(shù)據(jù)集進行標準化的函數(shù):
// go-and-nn/linear-regression/main.go
func standardize(data [][]float64) ([][]float64, []float64, []float64) {
mean := make([]float64, len(data[0])-1)
std := make([]float64, len(data[0])-1)
for i := 0; i < len(data[0])-1; i++ {
for j := 0; j < len(data); j++ {
mean[i] += data[j][i]
}
mean[i] /= float64(len(data))
}
for i := 0; i < len(data[0])-1; i++ {
for j := 0; j < len(data); j++ {
std[i] += math.Pow(data[j][i]-mean[i], 2)
}
std[i] = math.Sqrt(std[i] / float64(len(data)))
}
standardizedData := make([][]float64, len(data))
for i := 0; i < len(data); i++ {
standardizedData[i] = make([]float64, len(data[i]))
for j := 0; j < len(data[i])-1; j++ {
standardizedData[i][j] = (data[i][j] - mean[j]) / std[j]
}
standardizedData[i][len(data[i])-1] = data[i][len(data[i])-1]
}
return standardizedData, mean, std
}
standardize中的mean和std分別用于存儲每個特征的均值和標準差(標準差是反應(yīng)一組數(shù)據(jù)離散程度最常用的一種量化形式,累加每個樣本的特征值與均值的平方差,然后除以樣本數(shù)量,再開平方,便可得到該特征的標準差)。有了均值和標準差后,我們用原始特征值減去均值,然后除以標準差,得到標準化后的特征值。標簽無需標準化。
3.2 選擇機器學習模型
基于前面的鋪墊,我們早就明確了適合房屋價格預(yù)測的機器學習模型,那就是一個多元線性函數(shù),確定假設(shè)函數(shù)為:
圖片
損失函數(shù)我們也用均方誤差(Mean Square Error,MSE)函數(shù),這樣損失函數(shù)就是w1、w2和b的函數(shù):L(w1, w2, b)。依據(jù)前面的介紹,我們可以推導(dǎo)出損失函數(shù)L對w1、w2和b的偏導(dǎo)數(shù)以及權(quán)重更新公式如下:
圖片
確定了模型相關(guān)的公式后,我們就可以來實現(xiàn)該模型的訓練了!
3.3 訓練
下面是訓練函數(shù)的實現(xiàn)代碼:
// go-and-nn/linear-regression/main.go
func trainModel(data [][]float64, learningRate float64, epochs int) ([]float64, float64) {
features := len(data[0]) - 1
weights := make([]float64, features)
bias := 0.0
for epoch := 0; epoch < epochs; epoch++ {
gradW := make([]float64, features)
gradB := 0.0
mse := 0.0
for i := 0; i < len(data); i++ {
prediction := bias
for j := 0; j < features; j++ {
prediction += weights[j] * data[i][j]
}
error := prediction - data[i][features]
mse += error * error
for j := 0; j < features; j++ {
gradW[j] += error * data[i][j]
}
gradB += error
}
mse /= float64(len(data))
// 更新權(quán)重
for j := 0; j < features; j++ {
gradW[j] /= float64(len(data))
weights[j] -= learningRate * gradW[j]
}
gradB /= float64(len(data))
// 更新偏置
bias -= learningRate * gradB
// Output the current weights, bias and loss
fmt.Printf("Epoch %d: Weights: %v, Bias: %f, MSE: %f\n", epoch+1, weights, bias, mse)
}
return weights, bias
}
在這個代碼實現(xiàn)中,我們將權(quán)重和偏置的初始值都設(shè)置為了0,然后進入訓練循環(huán),循環(huán)的次數(shù)由外部傳入的epochs來決定,前面提到過epochs也是一個超參。每次循環(huán)代表一次完整的訓練過程。gradW用于存儲每個特征的梯度,gradB則用于存儲偏置的梯度值。梯度計算以及后面的更新權(quán)重的算法也都是按照上面圖片中的公式進行的。注意代碼中的error變量并非代表錯誤,而是表示預(yù)測誤差(即預(yù)測值減去真實標簽值)。
下面是驅(qū)動訓練函數(shù)的代碼:
// go-and-nn/linear-regression/main.go
func main() {
// Read training data
trainData, err := readCSV("train.csv")
if err != nil {
log.Fatalf("failed to read training data: %v", err)
}
// Read testing data
testData, err := readCSV("test.csv")
if err != nil {
log.Fatalf("failed to read testing data: %v", err)
}
// Standardize training data
standardizedTrainData, mean, std := standardize(trainData)
// Train model
learningRate := 0.0001
epochs := 1000
weights, bias := trainModel(standardizedTrainData, learningRate, epochs)
fmt.Printf("Trained Weights: %v\n", weights)
fmt.Printf("Trained Bias: %f\n", bias)
// Evaluate model on test data
predictAndEvaluate2(testData, weights, bias, mean, std)
}
這里我們設(shè)置超參學習率為0.0001,設(shè)置epochs為1000,即進行1000輪完整的訓練。trainModel訓練完成后返回最優(yōu)的權(quán)重值和偏置值。
之后,我們基于訓練后的模型以及測試數(shù)據(jù)集進行模型效果評估,
// go-and-nn/linear-regression/main.go
func predictAndEvaluate(data [][]float64, weights []float64, bias float64, mean []float64, std []float64) {
features := len(data[0]) - 1
mse := 0.0
for i := 0; i < len(data); i++ {
// Standardize the input features using the training mean and std
standardizedFeatures := make([]float64, features)
for j := 0; j < features; j++ {
standardizedFeatures[j] = (data[i][j] - mean[j]) / std[j]
}
// Calculate the prediction
prediction := bias
for j := 0; j < features; j++ {
prediction += weights[j] * standardizedFeatures[j]
}
// Calculate the error and accumulate the MSE
error := prediction - data[i][features]
mse += error * error
// Print the prediction and the actual value
fmt.Printf("Sample %d: Predicted Value: %f, Actual Value: %f\n", i+1, prediction, data[i][features])
}
// Calculate the final MSE
mse /= float64(len(data))
fmt.Printf("Mean Squared Error: %f\n", mse)
}
該評估函數(shù)會輸出測試集中每一組數(shù)據(jù)的預(yù)測值與標簽值的對比。
我們運行一下該代碼:
$go build
$./demo
Epoch 1: Weights: [0.009191300234460844 0.009159461537409297], Bias: 0.034000, MSE: 124080.000000
Epoch 2: Weights: [0.018380768863390594 0.01831709148135162], Bias: 0.067997, MSE: 124053.513977
Epoch 3: Weights: [0.02756840625241513 0.027472890197452842], Bias: 0.101990, MSE: 124027.033923
Epoch 4: Weights: [0.03675421276708735 0.036626858051265865], Bias: 0.135980, MSE: 124000.559834
Epoch 5: Weights: [0.04593818877288719 0.0457789954082706], Bias: 0.169966, MSE: 123974.091710
... ...
Epoch 997: Weights: [8.311660331200889 8.279923139396109], Bias: 32.264505, MSE: 100432.407457
Epoch 998: Weights: [8.319195610465172 8.287426591989], Bias: 32.295278, MSE: 100411.202067
Epoch 999: Weights: [8.326729388699432 8.294928543563927], Bias: 32.326049, MSE: 100390.001368
Epoch 1000: Weights: [8.334261666203304 8.302428994420524], Bias: 32.356816, MSE: 100368.805359
Trained Weights: [8.334261666203304 8.302428994420524]
Trained Bias: 32.356816
Sample 1: Predicted Value: 10.081607, Actual Value: 210.000000
Sample 2: Predicted Value: 14.223776, Actual Value: 230.000000
Sample 3: Predicted Value: 19.606495, Actual Value: 260.000000
Sample 4: Predicted Value: 25.609490, Actual Value: 310.000000
Sample 5: Predicted Value: 31.612486, Actual Value: 340.000000
Sample 6: Predicted Value: 37.615481, Actual Value: 370.000000
Sample 7: Predicted Value: 43.618476, Actual Value: 400.000000
Sample 8: Predicted Value: 49.621471, Actual Value: 430.000000
Sample 9: Predicted Value: 55.624466, Actual Value: 460.000000
Sample 10: Predicted Value: 61.627461, Actual Value: 490.000000
Mean Squared Error: 104949.429046
從最終的預(yù)測結(jié)果輸出來看,這個模型的效果那是相當?shù)牟睿☆A(yù)測值與測試集中的真實標簽值相距“十萬八千里”!問題出在哪里了呢?我們接下來來看看超參對模型訓練的作用。
3.4 超參調(diào)試和優(yōu)化
我們在上面例子中使用的學習率(learningRate)為0.0001,這個數(shù)值似乎有些小。
如果學習率太小,模型的更新幅度會很小,導(dǎo)致訓練過程非常緩慢,可能需要大量的訓練輪次才能收斂。我們這里設(shè)置的訓練輪次(epochs)為1000,在0.0001如此小的學習率下面,模型可能尚未收斂,訓練就結(jié)束了!所以,我們嘗試先將學習率由0.0001改為0.01,再來訓練和評估一次,這回的輸出結(jié)果如下:
$go build
$./demo
Epoch 1: Weights: [0.009191300234460844 0.009159461537409297], Bias: 0.034000, MSE: 124080.000000
Epoch 2: Weights: [0.018380768863390594 0.01831709148135162], Bias: 0.067997, MSE: 124053.513977
Epoch 3: Weights: [0.02756840625241513 0.027472890197452842], Bias: 0.101990, MSE: 124027.033923
Epoch 4: Weights: [0.03675421276708735 0.036626858051265865], Bias: 0.135980, MSE: 124000.559834
Epoch 5: Weights: [0.04593818877288719 0.0457789954082706], Bias: 0.169966, MSE: 123974.091710
Epoch 6: Weights: [0.055120334635221604 0.05492930263387402], Bias: 0.203949, MSE: 123947.629550
... ...
Epoch 996: Weights: [47.520035679041236 44.407936879025506], Bias: 339.984720, MSE: 44.287037
Epoch 997: Weights: [47.521568654779436 44.406403906767075], Bias: 339.984872, MSE: 44.286092
Epoch 998: Weights: [47.523101572396406 44.404870992560404], Bias: 339.985024, MSE: 44.285147
Epoch 999: Weights: [47.524634431895045 44.40333813640399], Bias: 339.985174, MSE: 44.284203
Epoch 1000: Weights: [47.52616723327823 44.401805338296306], Bias: 339.985322, MSE: 44.283259
Trained Weights: [47.52616723327823 44.401805338296306]
Trained Bias: 339.985322
Sample 1: Predicted Value: 216.742422, Actual Value: 210.000000
Sample 2: Predicted Value: 239.923439, Actual Value: 230.000000
Sample 3: Predicted Value: 269.738984, Actual Value: 260.000000
Sample 4: Predicted Value: 302.871794, Actual Value: 310.000000
Sample 5: Predicted Value: 336.004604, Actual Value: 340.000000
Sample 6: Predicted Value: 369.137414, Actual Value: 370.000000
Sample 7: Predicted Value: 402.270225, Actual Value: 400.000000
Sample 8: Predicted Value: 435.403035, Actual Value: 430.000000
Sample 9: Predicted Value: 468.535845, Actual Value: 460.000000
Sample 10: Predicted Value: 501.668655, Actual Value: 490.000000
Mean Squared Error: 54.966611
這回我們看懂,訓練后的模型在測試集上的預(yù)測結(jié)果與實際標簽值非常接近,可以看到對超參learningRate的調(diào)整見效了!
當然如果不調(diào)整learningRate,通過調(diào)節(jié)epochs到一個更大的值可能也能達到這個效果,但卻要耗費更多的算力和等待時間。
4. 小結(jié)
在這篇文章中,我們在解決線性回歸問題時并未引入神經(jīng)網(wǎng)絡(luò)的概念,其實基于神經(jīng)網(wǎng)絡(luò)也可以解決線性回歸問題,并且一個線性回歸模型可以看成是一個單層的全連接神經(jīng)網(wǎng)絡(luò)。
本文涉及的源碼可以在這里[15]下載 - https://github.com/bigwhite/experiments/blob/master/go-and-nn/linear-regression
本文的數(shù)學公式均由https://www.latexlive.com/基于latex語法在線生成。
本文中的部分源碼由OpenAI的GPT-4o生成。
5. 參考資料
- 《人工智能:現(xiàn)代方法(第4版)》[16] - https://book.douban.com/subject/36152133/
- 《零基礎(chǔ)學機器學習》[17] - https://book.douban.com/subject/35264202/
- 《動手學深度學習2nd-pytorch版》[18] - https://zh-v2.d2l.ai
- 《深度學習進階:自然語言處理》[19] - https://book.douban.com/subject/35225413/
- 《機器學習:Go語言實現(xiàn)》[20] - https://book.douban.com/subject/30457083/
- 《深度學習入門2:自制框架》[21] - https://book.douban.com/subject/36303408/
- 《GO語言機器學習實戰(zhàn)》[22] - https://book.douban.com/subject/35037170/