成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Go與神經網絡:線性回歸

開發 前端
在這篇文章中,我們在解決線性回歸問題時并未引入神經網絡的概念,其實基于神經網絡也可以解決線性回歸問題,并且一個線性回歸模型可以看成是一個單層的全連接神經網絡。

離發表上一篇與機器學習相關的文章《Go與神經網絡:張量運算》[1]已經過去整整一年了,AI領域,特別是大模型領域的熱度不僅未有減弱,反而愈演愈烈。整個行業變得更卷,競爭更加激烈,大模型你方唱罷我登場,層出不窮,各自能力也都在不斷提升,并在自然語言處理、問答、生成等方面展現出強大的能力。同時基于RAG(Retrieval-Augmented Generation)[2]等技術,大模型還可以實時檢索相關知識并融合到生成結果中,進一步提升了大模型在專業領域的應用價值。

很多人說用好大模型不必非要了解大模型的底層原理,也許這句話是對的。但對于后端程序員的我來說,對底層原理的不理解,始終讓我有一種“不安全感”。我認為即使大模型的使用變得日益簡單和廣泛,但如果我們無法深入理解其工作機制,恐怕還是難以充分發揮它們的潛力,甚至無法準確評估它們的局限性和風險。

但對大模型原理的學習是一個循序漸進的學習過程,我們不能一蹴而就地達到對大模型原理的深入理解。我決定從最基礎的機器學習入手,從傳統機器學習解決問題的一般步驟開始,以線性回歸這個傳統傳統機器學習的"Hello, World"示例為切入點,逐步探討機器學習的基本概念和實現流程,這也是本篇文章的初衷與主要內容。

1. 機器學習的那些事兒

1.1 人工智能的誕生

相對于機器學習(Machine Learning,ML),普通大眾更熟悉“人工智能(Artificial Intelligence,AI)[3]”這個字眼兒。

就像一千個人眼中有一千個哈姆雷特,每個人對人工智能的理解都不盡相同:有些人將其看成一個學術領域,有些人視之為人類文明下一個要實現的目標,懵懂無知的少年會將其想象為那種高大威猛的機器人(其實是隸屬于具身智能[4],人工智能和機器人學[5]的一個跨學科分支),而還有些人認為它是空中樓閣,永遠無法實現。

作為程序員,我們更聚焦工程領域。而工程領域主要是消化學術領域的研究,將其實現落地并應用于人類生活的方方面面。那么學術領域是如何定義人工智能的呢?人工智能專家Stuart Russell[6]和Peter Norvig[7]在他們的聯合著作《人工智能:現代方法(第4版)》中將人工智能定義為對從環境中接收感知并執行動作的智能體(Agent)的研究,并強調每個這樣的智能體都要實現一個將感知序列映射為動作的函數。

對人工智能的定義雖然內容不長,但這里面卻蘊含著計算機科學家對人工智能幾十年的探索歷程與嘗試。

人工智能的概念始于20世紀50年代。1950年,阿蘭·圖靈(Alan Turing)[8]發表了《計算機器與智能[9]》一文,提出了著名的圖靈測試[10]。但人工智能一詞的正式提出還要等到6年后的1956年達特茅斯會議。對于人工智能領域而言,這是堪比理論物理學界1927年比利時第五屆索爾維會議(如下圖)[11]的一次會議。約翰·麥卡錫(John McCarthy)、馬文·閔斯基(Marvin Minsky,人工智能與認知學專家)、克勞德·香農(Claude Shannon,信息論的創始人)、艾倫·紐厄爾(Allen Newell,計算機科學家)、赫伯特·西蒙(Herbert Simon,諾貝爾經濟學獎得主)等科學家正聚在一起,討論著一個完全不食人間煙火的主題:用機器來模仿人類學習以及其他方面的智能。會議足足開了兩個月的時間,雖然大家沒有達成普遍的共識,但是卻為會議討論的內容起了一個名字:人工智能。因此,1956年也就成為了人工智能元年。

1927年比利時第五屆索爾維會議合影1927年比利時第五屆索爾維會議合影


1.2 符號主義:早期的人工智能實現路徑

在人工智能的早期探索階段,研究主要集中于符號主義(Symbolism)和邏輯推理。這種方法使用符號來表示知識和問題,并通過邏輯推理來解決問題。這種方法依賴于明確的規則和符號系統來進行推理和決策。

邏輯推理是符號主義的核心,它使用邏輯規則來進行推理和決策。邏輯推理包括演繹推理、歸納推理和溯因推理:

  • 演繹推理:從一般規則推導出特定結論(例如,從“所有人都會死”推導出“蘇格拉底會死”)。
  • 歸納推理:從特定實例推導出一般規則(例如,從“蘇格拉底會死”推導出“所有人都會死”)。
  • 溯因推理:從結果推斷出可能的原因(例如,從“蘇格拉底死了”推斷出“他是人”)。

LISP語言[12]在符號主義和邏輯推理盛行的階段發揮了重要作用,其強大的符號處理能力、遞歸和動態特性、交互式開發環境以及宏和元編程功能使其成為AI研究和開發的主要工具。其他語言如Prolog和Scheme也在特定領域中提供了重要支持。

雖然在機器學習大行其道的今天,符號主義系統不受待見了,但不可否認符號主義系統也有很多優點:

  • 可解釋性:符號主義系統的推理過程透明、易于理解。
  • 明確規則:使用明確的規則和邏輯,使得系統行為可預測。
  • 可靠性:在明確定義的領域內,符號主義系統可以提供可靠的推理結果。

不過,它的缺點也很明顯:

  • 局限性: 依賴于預定義的規則和知識庫,難以處理復雜和動態的環境。
  • 知識獲取: 知識工程需要大量人工干預,獲取和維護知識庫成本高。

正是由于這些這些不足,以及當時計算能力和數據存儲的限制,AI研究在1970年代遇到了障礙,進入了第一次“AI寒冬”。許多早期的承諾未能實現,資金和興趣減少。

1980年代,符號主義和邏輯推理迎來了第二次巔峰:知識工程和專家系統。專家系統是計算機程序,旨在模仿人類專家的決策能力。它們在特定領域內使用顯式編碼的知識庫和推理引擎來解決復雜問題或提供建議。第一個成功的商用專家系統R1在數字設備公司(Digital Equipment Corporation,DEC)投入使用(McDermott, 1982),該程序幫助公司配置新計算機系統的訂單。截至1986年,它每年為公司節省約4000萬美元。到1988年,DEC的人工智能小組已經部署了40個專家系統,而且還有更多的專家系統在開發中。但事實證明,為復雜領域構建和維護專家系統是困難的,一部分原因是系統使用的推理方法在面臨不確定性時會崩潰,另一部分原因是系統無法從經驗中學習。專家系統的局限性和開發維護成本直接也導致了第二次“AI寒冬”的到來。

在兩次AI的興起和“寒冬”中,先行研究者們開發了許多基礎性的算法和系統。這些早期研究不僅解決了特定問題,還為AI的理論和實踐發展奠定了重要的基礎。隨著計算能力和數據資源的增加,AI研究逐漸從符號主義轉向數據驅動的方法,但這些早期成果仍然具有重要的歷史和學術意義。

1.3 數據驅動與機器學習

數據驅動方法依賴于大量數據,通過從數據中學習模式和關系來進行預測和決策。這種方法不依賴于明確的布爾邏輯和規則,而是通過統計和算法來從數據中提取知識,即基于機器學習而不是手工編碼。

1990年代至2000年代,隨著計算能力的提升和數據量的增加,機器學習(ML)逐漸成為AI的主要方法。基于統計學和概率論的算法(如支持向量機和決策樹)獲得了成功。大數據的可用性和向機器學習的轉變幫助人工智能恢復了商業吸引力。大數據是2011年IBM的Watson系統在《危險邊緣》(Jeopardy!)問答游戲中戰勝人類冠軍的關鍵因素,這一事件深深影響了公眾對人工智能的看法。

《人工智能:現代方法(第4版)》是這樣定義機器學習的:如果一個智能體通過對世界進行觀測來提高它的性能,我們稱其為智能體學習(learning)。學習可以是簡單的,例如記錄一個購物清單,也可以是復雜的,例如愛因斯坦推斷關于宇宙的新理論。當智能體是一臺計算機時,我們稱之為機器學習(machine learning):一臺計算機觀測到一些數據,基于這些數據構建一個模型(model),并將這個模型作為關于世界的一個假設(hypothesis)以及用于求解問題的軟件的一部分。不通過合適的方式編程來解決,而是希望一臺機器自主進行學習并解決問題,其原因主要有兩個:

  • 程序的設計者無法預見未來所有可能發生的情形。比如一個被設計用來導航迷宮的機器人無法掌握每一個它可能遇到的新迷宮的布局。
  • 有時候設計者并不知道如何設計一個程序來求解目標問題。比如識別人臉。

可以說,機器學習是另外一種實現人工智能的路徑(前一種是符號主義和邏輯推理),它是一類強大的可以從經驗中學習的技術。通常采用觀測數據或與環境交互的形式,機器學習算法會積累更多的經驗,其性能也會逐步提高。

機器學習的興起同樣離不開早期研究者的成果:

  • 感知機(1957): Frank Rosenblatt 設計的感知機是第一個用于分類任務的人工神經網絡模型,能夠學習二分類任務。感知機也被視為一種最簡單形式的前饋式人工神經網絡。
  • K均值聚類(1967): James MacQueen 提出的K均值聚類算法,是最早的聚類分析方法之一。
  • 決策樹(ID3, 1986): Ross Quinlan 提出的 ID3 算法,是決策樹學習的基礎。
  • 支持向量機(1992): Vladimir Vapnik 和 Alexey Chervonenkis 提出了支持向量機,為高維數據分類問題提供了強有力的解決方案。
  • 多層感知器(1986): 由 Geoffrey Hinton 等人推廣的反向傳播算法(Backpropagation),使得訓練多層神經網絡成為可能。
  • 梯度提升樹(2000): Jerome Friedman 提出的梯度提升樹(Gradient Boosting Machines),在分類和回歸任務中表現出色。
  • 隨機森林(2001): Leo Breiman 提出的隨機森林算法,通過集成多個決策樹提高了模型的準確性和魯棒性。

進入2010年后,在大規模數據集以及GPU硬件加速的賦能下,深度神經網絡逐漸成為主流且表現卓越的機器學習方案,深度學習走向前臺:

  • AlexNet(2012): Alex Krizhevsky 等人在 ImageNet 大賽上使用卷積神經網絡(CNN)贏得了第一名,推動了深度學習在計算機視覺中的應用。
  • 生成對抗網絡(GAN, 2014): Ian Goodfellow 等人提出的生成對抗網絡,開啟了生成模型的新方向。
  • BERT(2018): Google 提出的雙向編碼器表示(BERT)模型,在自然語言處理任務中取得了突破性進展。
  • Transformers(2022):Transformers模型及其變種在自然語言處理、圖像處理等多個領域取得了顯著進展,典型代表是ChatGPT的推出。

1.4 人工智能關系圖

基于上面的說明,我們下面用一張圖說明一下人工智能、機器學習、神經網絡以及深度學習的關系:

圖片圖片

而神經網絡是支撐機器學習的重要技術,是深度學習的核心技術。關于神經網絡,我們會在后面的系列文章中重點說明。

網絡上也有一個圖,可以更詳細地展示各個范圍內的具體技術,大家也可以參考一下:

圖片圖片

1.5 機器學習的本質

機器學習就是從數據中發現規律,發現的這個規律就是“模型”, 更具體來說就是一個或一組復合在一起的函數。而發現規律的這個過程就叫“學習”或叫“訓練”。這個過程與人類學習的有些相似:

圖片圖片

人類和機器都需要輸入信息來開始學習。人類通過感官感知信息,機器通過傳感器或數據集獲取數據。人類通過理解和記憶進行學習,機器通過訓練數據調整模型參數進行學習。人類在大腦中存儲知識和經驗,機器在模型參數和結構中存儲學到的模式和規則。人類根據實踐中的反饋調整和改進知識,機器根據評估和實際應用中的反饋調整模型參數和結構。兩者盡管實現手段不同,但核心思想都是從輸入數據中學習知識和模式,通過反饋進行調整和改進,并不斷適應新的環境和問題。

上圖中使用神經網絡的形式呈現了學習/訓練后的模型,其實在一些傳統機器學習的簡單場景下,訓練后的模型可能就是一個簡單的一元線性函數,比如:f(x) = wx + h。

訓練后的模型便可以應用于真實環境中的數據,進行推理和預測(serve/predict)。比如說,一個經過大量真實病歷數據訓練后得到醫療診斷模型,就可以用來預測和診斷新的病患情況了。

到這里,你可能依然對機器學習一知半解。別著急,之前我也是這樣,就想親手訓練一個模型來直觀體會一下什么是機器學習。接下來,我們就來訓練一個Hello,World級別的模型,不過在真正動手之前,我們還是要先來了解一下機器學習中的術語(“黑話”)與訓練的一般步驟。

2. 機器學習的術語與一般步驟

機器學習本身就有不低的門檻,因此我們將由淺入深的來學習機器學習的術語,并簡要說明一下機器學習項目的一般步驟。

2.1 特征、標簽與模型

在下圖中,我們以一個簡單的多元線性回歸模型(即一個多元一次函數)來說明一下一些機器學習中常見的術語:

圖片圖片

我們先介紹與數據有關的幾個重要術語,其他在后面說明機器學習的一般步驟時,結合具體的場景再行講解。機器學習離不開數據,如上圖中左上角的表格就是“喂給”機器學習訓練的**訓練數據集(training dataset )**。

上圖中的數據集是一個常見的房價相關數據,該數據集有三條數據,它們組成了該數據集的數據樣本。表中每條數據有三個影響房價的“因子”:居住面積、離市中心距離和建成時間(也就是房齡),這些因子共同決定了房子的價格。在機器學習中,我們稱這些“因子”為**特征(feature)。而房價則被稱為標簽(label)**。從數據來看,這三個特征表現出明顯的與房價(y)的相關性,如下圖:

圖片圖片

機器學習的目的就是找到通過特征預測標簽的函數(即模型),然后將得到的函數應用于生產中進行標簽預測。特征是機器學習模型的輸入,標簽是機器學習模型的輸出。無論是在訓練階段,還是在預測階段。特征的個數稱為特征的維度,維度越高,數據集越復雜。

了解完特征、標簽和模型后,我們來看看機器學習項目的一般步驟,更具體來說就是機器學習訓練的步驟,一旦訓練ok,得到模型,模型應用就比較簡單了。

2.2 機器學習訓練的一般步驟

圖片圖片

上圖展示了機器學習訓練的一般步驟,我們逐個說明一下。

2.2.1 數據收集與預處理

就像人類要從各種資料(書籍、媒體等)中學習一樣,機器也要從數據中學習。沒有數據,機器學習就無從談起。數據也是通過機器學習解決生活中實際問題的前提。

數據收集渠道有多種,有爬取互聯網的數據,有開源數據集(Image Net、Kaggle、Google Public Data Explorer),有購買的,還有客戶積攢的海量歷史數據等。這些數據拿到手后,還不能直接喂給模型進行訓練,因為業界有句名言“輸入的是垃圾,輸出的也是垃圾”(Garbage in, garbage out),我們需要對數據進行分析和預處理,了解數據內在關系并使其滿足機器學習訓練的規格和質量要求,最后還需要做特征的提取,即使用數據的領域知識來創建/識別出那些使機器學習算法起作用的特征的過程。

數據的預處理是十分重要的工作,預處理的好壞直接決定了訓練出來的機器學習模型的有效性。在《零基礎學習機器學習》一書中提到了數據預處理工作包含的幾項內容:

  • 可視化:用Excel表和各種數據分析工具(如Matplotlib等)從各種角度(如列表、直方圖、散點圖等)探索一下數據。對數據有了基本的了解后,才方便進一步分析判斷,即為后續的模型選擇奠定基礎。
  • 數據向量化[13]:把原始數據格式化,使其變得機器可以讀取。例如,將原始圖片轉換為機器可以讀取的數字矩陣,將文字轉換為one-hot編碼,將文本類別(如男、女)轉換成0、1這樣的數值。
  • 處理壞數據和空數據:一條數據可不是全部都能用,要利用數據處理工具來把“搗亂”的“壞數據”(冗余數據、離群數據、錯誤數據)處理掉,把缺失值補充上。
  • 特征縮放:可以顯著提升模型的性能和訓練效率。許多機器學習算法,例如梯度下降法,依賴于特征之間的距離計算。如果特征的尺度差異很大,會導致算法在不同特征方向上以不同的速度進行更新,從而降低收斂速度。特征縮放可以將所有特征縮放到相同的尺度,使算法能夠更快地收斂到最優解。特征尺度差異過大可能導致數值計算不穩定,例如出現梯度爆炸或梯度消失現象,影響模型訓練效果。特征縮放還可以使模型的權重更加可解釋。當特征尺度差異很大時,模型的權重可能無法反映特征的實際重要性。特征縮放可以使權重更加反映特征的真實貢獻。

特征縮放適用于大多數機器學習算法,包括線性回歸、邏輯回歸、支持向量機、神經網絡等。常見的特征縮放方法包括如下幾種:

  • 標準化 (Standardization):對數據特征分布的轉換,目標是使其符合正態分布(均值為0,標準差為1)。在實踐中,會去除特征的均值來轉換數據, 使其居中,然后除以特征的標準差來對其進行縮放。
  • 歸一化/規范化 (Normalization):將特征數據縮放到特定范圍,通常是0到1之間。歸一化不會改變數據的分布形態。

數據預處理還包括特征工程和特征提取,即確定數據中究竟哪個特征對問題的解決會起到關鍵作用,并提取出來作為后續訓練和預測的輸入特征。許多現代機器學習算法,如深度學習模型,可以從原始數據中學習復雜的表示形式,而不需要明確的特征工程,但是特征工程仍然在機器學習工作流程中扮演著重要角色,尤其是在領域知識、可解釋性和數據質量方面起到重要作用。不過特征提取是一個細分領域,內容很多(對之我也不甚了解),這里就不展開說了。

2.2.2 選擇機器學習模型

AI科學家期望能有一個通用的機器學習模型可以學習一切類型的數據,并處理所有領域的任務,這樣世界將變得簡單了。但就目前AI發展的水平來看,還沒有一個通用的機器學習模型可以適合于所有類型的數據和任務,即便是當今大熱的預訓練的大語言模型也可能不勝任某一領域的工作。在前期的傳統機器學習階段,不同的數據和問題需要采用不同的機器學習方法和模型。

影響機器學習模型選擇的一些關鍵的因素包括:

  • 數據類型和特征:比如圖像數據和文本數據一般需要不同的模型。數據的維度、稀疏程度等也會影響選擇的模型。
  • 任務類型:分類、回歸、聚類等任務適合不同的模型。有監督學習和無監督學習也需要不同的方法。
  • 數據規模:對于大規模數據,可擴展性強的模型如深度學習效果更好。小樣本數據可能更適合傳統的機器學習算法。
  • 領域知識:某些領域問題需要結合專業領域知識,不能單純依賴通用的機器學習模型。

這里提到了有監督學習和無監督學習,提到了分類、回歸、聚類等任務類型,我們需要簡單科普一下這些概念。

機器學習中,有監督學習和無監督學習是兩種主要的學習方法,它們有各自擅長的任務類型。

有監督學習是一種通過使用已標注的數據(即如前面圖中的訓練數據集那樣,樣本數據包含特征與對應的標簽)來訓練模型的方法。在這種方法中,每個訓練樣本都是一個輸入-輸出對,模型通過學習這些對的關系來預測新的輸入數據的輸出。有監督學習擅長的任務類型包括下面這幾個:

  • 分類任務:將輸入數據分類到預定義的類別中,例如垃圾郵件檢測、圖像分類。
  • 回歸任務:預測連續的數值輸出,例如房價預測(前面圖中的示例)、股票價格預測。
  • 標注任務:為輸入數據中的每個元素分配一個標簽。例如:命名實體識別(NER):在文本中識別出人名、地名、組織名等。
  • 排序任務:根據某種標準對項目進行排序。例如:信息檢索、推薦系統。
  • 序列預測任務:根據時間序列數據進行預測。例如,銷售額預測、天氣預報等。

使用有監督學習,我們需要向模型提供巨大數據集,且每個數據樣本都需要包含特征和相應標簽值,這很可能是一個既耗時又費錢的過程。

而無監督學習則是一種通過使用未標注的數據來訓練模型的方法。在這種方法中,模型試圖從數據中發現結構或模式,而無需使用明確的輸入-輸出對。無監督學習擅長的任務類型包括下面幾個:

  • 聚類任務:將相似的樣本歸為一類,比如給定一組照片,模型能把它們分成風景照片、狗、嬰兒、貓和山峰。同樣,給定一組用戶的網頁瀏覽記錄,模型能將具有相似行為的用戶聚類。
  • 降維任務:減少數據的維度,同時保持其重要特征,例如主成分分析(PCA)問題,模型能否找到少量的參數來準確地捕捉數據的線性相關屬性?比如,一個球的運動軌跡可以用球的速度、直徑和質量來描述。
  • 異常檢測:識別數據中的異常或異常模式,例如欺詐檢測、設備故障檢測。

這兩種方法在不同的應用場景中各有所長,選擇哪種方法通常取決于數據的特性和具體的任務需求。

我們以前面圖中的房價預測問題為例,根據前面關于有監督和無監督的任務類型以及帶有標簽的數據對的訓練數據集,我們初步判斷應該選擇線性回歸模型。當然,你也可以自己探索數據集中一些特征與標簽的關系,比如我們利用gonum.org/v1/plot相關包分別畫出房屋面積、離市中心距離兩個特征與標簽房價的散點圖(當然這是自己生成的一組訓練數據集,具體描畫代碼參見https://github.com/bigwhite/experiments/blob/master/go-and-nn/linear-regression/plotter.go):

圖片圖片

從數據的特征散點圖,可以看出一些特征與標簽之間的線性關系,這符合使用線性回歸模型的要求。線性回歸基于幾個簡單的假設:首先,假設自變量(x1, x2, x3, ..., xn)和因變量y之間的關系是線性的,即y可以表示為自變量集合中元素的加權和。以前面的房價預測問題為例,線性模型對應的假設函數可以表示為居住面積、與市中心距離以及房齡的加權和,就像下面這樣:

圖片圖片

這個函數叫做假設函數(也叫預測函數),其中的w1、w2和w3稱為權重,權重決定了每個特征對我們預測值的影響。b稱為偏置(bias)、 偏移量(offset)或截距(intercept)。偏置是指當所有特征都取值為0時,預測值應該為多少。

現在權重w1、w2、w3和偏置b的值都是未知的,它們也被稱為模型內的參數,直接影響模型的預測結果。

接下來的訓練就是為了得到這些參數的合理值,使得假設函數得到的結果與真實房價越接近越好。

2.2.3 訓練

到這里,我們擁有了一份訓練數據集(帶標簽)以及一個權重和偏置參數未知的多元線性假設函數(y')。而我們接下來要做的就是找到假設函數中各個未知參數的合理值。

圖片圖片

機器學習的“學習訓練”過程非常樸素,就是將訓練數據集中的特征逐條喂給y',并將得到的結果與訓練數據集中的標簽比對,如果差距過大,則調整y'的權重參數和偏置,然后再重復一輪學習,這樣循環往復直到通過y'計算得到的結果與標簽的差距在預期范圍以內。

不過,這個過程看似容易,但真正實施起來,還有很多“阻塞點”要突破。以y'這個多元線性函數模型為例,首先就是權重和偏置參數的初始值。在我們這篇入門文章中,針對y'這個簡單的線性函數,我們可采用隨機初始化的方式,即將參數隨機地設置在一個合理的范圍內。這種方法簡單快捷,但對于復雜的模型,可能會導致收斂速度慢或陷入局部最優。關于初始參數的選擇也是一個細分方向,這里就不展開說明了。

其次,我們要確定一個y'計算結果與訓練數據集中標簽值的差距計算方法。機器學習領域稱這個計算方法為損失函數(Loss function)。損失也就是 誤差,也稱為成本(cost)或代價,用于體現當前預測值和真實值之間的差距。它是一個數值,表示對于單個樣本而言模型預測的準確程度。如果模型的預測完全準確,則損失為0;如果不準確,就有損失。在機器學習中,我們追求的當然是比較小的損失。不過,模型好不好還不能僅看單個樣本,而是要針對所有數據樣本,找到一組平均損失“較小”的函數模型。計算平均損失是每一個機器學習項目的必要環節。損失函數實質上就是用來計算平均損失的,它是模型參數的函數:L(w1, w2, w3, b)。機器學習的訓練過程就是找一組模型參數的解,比如本示例中的(w1, w2, w3, b),使得損失函數的計算結果最小。

機器學習中的損失函數有很多,針對不同任務類別,選擇一個合適的即可。

比如,用于回歸的損失函數就有:均方誤差(Mean Square Error,MSE)函數、平均絕對誤差(Mean Absolute Error,MAE)函數和平均偏差誤差(mean bias error)函數。用于分類的損失函數有交叉熵損失(cross-entropy loss)函數和多分類SVM損失(hinge loss)函數等。

對于我們的回歸問題來說,下面的均方差函數L就可以滿足評估參數的目的了。

圖片圖片

在這個函數中,yi'基于樣本數據的特征經由假設函數計算出來的值,yi則是樣本數據的標簽值。假設只有一個樣本數據如下:

x1 = 55, x2 = 11, x3 = 5, y = 210

我們的假設函數為:y' = 0.1x1 + 0.1x2+0.1x3 + 0.1 ,即初始參數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

這個損失函數值看起來就不大行:),我們需要調整模型參數再戰!但如何調整呢?w1調大?w2調小?w3不動?盡管現在算力已經很強大了,但我們也不能拍腦袋亂猜!我們需要一種科學的方法為機器學習后續的參數調整指明方向,這樣才能大幅縮短訓練過程,并得到滿足需求的模型參數組合。

大多流行的優化算法通常基于一種基本方法–梯度下降(gradient descent)。簡而言之,在每個步驟中,梯度下降法都會檢查每個參數,看看如果僅對該參數進行少量變動,訓練集損失會朝哪個方向移動。然后,它在可以減少損失的方向上優化參數。

梯度下降的過程就是在程序中一點點變化參數w1、w2、w3和b,使L ,也就是損失值逐漸趨近最低點(也稱為機器學習中的最優解)。而要實現這一點,我們需要借助導數。導數描述了函數在某點附近的變化率(比如:L正在隨著w1增大而增大還是減小),而這正是進一步猜測更好的權重時所需要的全部內容。即梯度下降法通過求導來計算損失曲線在起點處的梯度。此時,梯度就是損失曲線導數的矢量,它可以讓我們了解哪個方向距離目標“更近”或“更遠”。如果求導后梯度為正值,則說明L正在隨著w增大而增大,應該減小w,以得到更小的損失。如果求導后梯度為負值,則說明L正在隨著w增大而減小,應該增大w,以得到更小的損失。

在單個權重參數的情況下,損失相對于權重的梯度就稱為導數;若考慮偏置,或存在多個權重參數時(就像我們上面的房價預測示例),損失相對于單個權重的梯度就稱為偏導數。

在上面示例中,損失函數L是權重參數和偏置的函數,表示為L(w1, w2, w3, b)。我們需要分別求出L相對于w1、w2、w3和b的偏導數來決定后續各個權重參數和偏置參數的調整方向(增大還是減小)。我們以L對w1的偏導數為例,給出偏導數公式的推導過程:

圖片圖片

我們看到:針對每個樣本,我們計算其損失值(y'-y)與該樣本特征(x1)的乘積。取這些乘積的平均值就得到了L對w1的偏導值。

依次類推,我們可以得到L對w1、w2、w3和b的偏導數公式:

圖片圖片

上面的偏導數為我們指定了參數調整方向,下面是w1、w2、w3和b的更新公式:

圖片圖片

這種計算梯度并反向更新模型參數的過程就稱為“反向傳播”。

上面參數更新公式中有一個新的變量α,該變量代表的是學習率(learning rate)。是一個超參數,它控制著模型參數更新的步伐大小。在梯度下降過程中,學習率決定了每次更新參數時移動的步長。學習率的引入是為了控制模型訓練的速度。如果學習率太大,參數更新步伐過大,可能導致模型無法收斂甚至發散;如果學習率太小,參數更新步伐過小,訓練時間會過長且可能陷入局部最小值。

w1的更新公式是w1減去損失函數相對于w1的偏導數乘以學習率,這個公式表示,我們沿著損失函數梯度的負方向更新參數,因為梯度的方向是損失函數增大的方向,所以負方向是使損失函數減小的方向。

到這里,我們已經可以實現訓練的閉環了!訓練后的模型可以使用另外一套測試數據集來評估模型的效果。但訓練出來的模型是否真的是滿足要求的呢?還不一定,很多情況下,我們還需要對超參進行調試以繼續優化模型。

2.2.4 超參調試與性能優化

在上面的講解中,我們知道w1、w2、w3和b是模型內的參數,這些參數通過y'正向傳播和基于梯度下降的反向傳播在多輪訓練中得以更新優化,并得到一個合理的值。這些值是機器從數據中學習到的,不需要我們手工調整。但還有一些參數,比如上面提到的學習率(learning rate)、訓練輪數(Epochs)等,是模型外部的可以通過人工調節的參數,這樣的參數稱為**超參數(Hyperparameters)**。大多數機器學習從業者真正花費相當多的時間來調試的正是這類超參數。

在實際應用中,選擇合適的學習率和訓練輪數等超參數通常需要結合以下方法:

  • 經驗法則:基于先前經驗和領域知識設定初始值。
  • 交叉驗證:通過交叉驗證選擇一組最優的超參數。
  • 網格搜索:在多個可能的超參數組合上進行搜索,找到效果最好的參數組合。
  • 學習率調度:動態調整學習率,比如在訓練過程中逐漸減小學習率。

超參數對模型效果和優化的影響非常重要,選擇合適的超參數可以顯著提高模型性能。本文是入門文章,關于超參的調優就不展開說明了。

基于通過上面的對機器學習的術語、概念和對訓練一般步驟的了解,接下來,我們通過一個實例來訓練一個最簡單的機器學習模型:線性回歸模型。這也被稱為機器學習領域的“Hello, World”。

3. 線性回歸:機器學習的Hello, World

我們按照前面關于機器學習的一般步驟,逐步展開該示例的說明。

3.1 準備數據和預處理

我們這個示例依舊是預測房價,但是為了簡單,我們不使用那些公共數據集(比如kaggle平臺[14]上的數據),而是讓大模型幫我生成兩個小規模的數據集,一個是用于訓練的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

還是為了簡單,我們在這兩份數據集中僅使用兩個特征:面積和離市中心距離。

接下來,我們就通過編碼來實現對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文件中讀取所有樣本數據(已去掉了header),所有樣本數據(包括特征與標簽)都存儲在一個[][]float64類型的變量中。

拿到數據后,我們便可以對其進行標準化,前面說過通常情況下,標準化后的數據會使模型訓練更加穩定和快速,從而可能提高模型的預測性能。下面是我們實現用于對訓練數據集進行標準化的函數:

// 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分別用于存儲每個特征的均值和標準差(標準差是反應一組數據離散程度最常用的一種量化形式,累加每個樣本的特征值與均值的平方差,然后除以樣本數量,再開平方,便可得到該特征的標準差)。有了均值和標準差后,我們用原始特征值減去均值,然后除以標準差,得到標準化后的特征值。標簽無需標準化。

3.2 選擇機器學習模型

基于前面的鋪墊,我們早就明確了適合房屋價格預測的機器學習模型,那就是一個多元線性函數,確定假設函數為:

圖片圖片

損失函數我們也用均方誤差(Mean Square Error,MSE)函數,這樣損失函數就是w1、w2和b的函數:L(w1, w2, b)。依據前面的介紹,我們可以推導出損失函數L對w1、w2和b的偏導數以及權重更新公式如下:

圖片圖片

確定了模型相關的公式后,我們就可以來實現該模型的訓練了!

3.3 訓練

下面是訓練函數的實現代碼:

// 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))
        
  // 更新權重
        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
}

在這個代碼實現中,我們將權重和偏置的初始值都設置為了0,然后進入訓練循環,循環的次數由外部傳入的epochs來決定,前面提到過epochs也是一個超參。每次循環代表一次完整的訓練過程。gradW用于存儲每個特征的梯度,gradB則用于存儲偏置的梯度值。梯度計算以及后面的更新權重的算法也都是按照上面圖片中的公式進行的。注意代碼中的error變量并非代表錯誤,而是表示預測誤差(即預測值減去真實標簽值)。

下面是驅動訓練函數的代碼:

// 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)
}

這里我們設置超參學習率為0.0001,設置epochs為1000,即進行1000輪完整的訓練。trainModel訓練完成后返回最優的權重值和偏置值。

之后,我們基于訓練后的模型以及測試數據集進行模型效果評估,

// 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)
}

該評估函數會輸出測試集中每一組數據的預測值與標簽值的對比。

我們運行一下該代碼:

$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

從最終的預測結果輸出來看,這個模型的效果那是相當的差!預測值與測試集中的真實標簽值相距“十萬八千里”!問題出在哪里了呢?我們接下來來看看超參對模型訓練的作用。

3.4 超參調試和優化

我們在上面例子中使用的學習率(learningRate)為0.0001,這個數值似乎有些小。

如果學習率太小,模型的更新幅度會很小,導致訓練過程非常緩慢,可能需要大量的訓練輪次才能收斂。我們這里設置的訓練輪次(epochs)為1000,在0.0001如此小的學習率下面,模型可能尚未收斂,訓練就結束了!所以,我們嘗試先將學習率由0.0001改為0.01,再來訓練和評估一次,這回的輸出結果如下:

$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

這回我們看懂,訓練后的模型在測試集上的預測結果與實際標簽值非常接近,可以看到對超參learningRate的調整見效了!

當然如果不調整learningRate,通過調節epochs到一個更大的值可能也能達到這個效果,但卻要耗費更多的算力和等待時間。

4. 小結

在這篇文章中,我們在解決線性回歸問題時并未引入神經網絡的概念,其實基于神經網絡也可以解決線性回歸問題,并且一個線性回歸模型可以看成是一個單層的全連接神經網絡。

本文涉及的源碼可以在這里[15]下載 - https://github.com/bigwhite/experiments/blob/master/go-and-nn/linear-regression

本文的數學公式均由https://www.latexlive.com/基于latex語法在線生成。

本文中的部分源碼由OpenAI的GPT-4o生成。

5. 參考資料

  • 《人工智能:現代方法(第4版)》[16] - https://book.douban.com/subject/36152133/
  • 《零基礎學機器學習》[17] - https://book.douban.com/subject/35264202/
  • 《動手學深度學習2nd-pytorch版》[18] - https://zh-v2.d2l.ai
  • 《深度學習進階:自然語言處理》[19] - https://book.douban.com/subject/35225413/
  • 《機器學習:Go語言實現》[20] - https://book.douban.com/subject/30457083/
  • 《深度學習入門2:自制框架》[21] - https://book.douban.com/subject/36303408/
  • 《GO語言機器學習實戰》[22] - https://book.douban.com/subject/35037170/
責任編輯:武曉燕 來源: TonyBai
相關推薦

2024-06-11 08:19:34

2024-06-28 08:15:02

2017-09-10 07:07:32

神經網絡數據集可視化

2018-07-03 16:10:04

神經網絡生物神經網絡人工神經網絡

2017-10-09 11:21:46

神經網絡OpenAI非線性

2017-03-10 12:16:46

機器學習

2022-06-06 12:53:17

吳恩達AI機器學習

2017-11-30 18:05:18

2020-08-20 07:00:00

深度學習人工智能技術

2023-11-14 16:29:14

深度學習

2021-06-21 10:43:25

Python神經網絡深度學習

2025-02-25 14:13:31

2022-04-07 09:01:52

神經網絡人工智能

2019-05-07 19:12:28

機器學習神經網絡Python

2017-05-04 18:30:34

大數據卷積神經網絡

2024-01-01 15:37:59

機器學習神經網絡

2020-08-06 10:11:13

神經網絡機器學習算法

2018-03-21 10:14:38

JavaScript交叉GPU

2019-11-19 08:00:00

神經網絡AI人工智能

2019-01-05 08:40:17

VGG神經網絡
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 日本久久一区二区三区 | 国产一区二区三区在线免费观看 | 黄色大片视频 | 日韩不卡一区二区 | 国产精品成人69xxx免费视频 | 成人av播放 | 欧美日韩一区在线 | 亚洲精品久久 | 亚洲iv一区二区三区 | 久久国产福利 | av网址在线播放 | 日韩在线视频免费观看 | 欧美日韩在线一区二区三区 | 人人玩人人干 | 九九热免费看 | 成人精品一区二区 | 99资源站 | 中文字幕在线三区 | 亚洲精品国产第一综合99久久 | 中文字幕日韩一区 | 一区二区三区视频在线免费观看 | 国产 欧美 日韩 一区 | 亚洲综合激情 | 午夜一区 | 99热成人在线| 中国一级特黄视频 | 一本色道精品久久一区二区三区 | 欧美视频1区 | 欧美一区二区另类 | 日韩视频在线播放 | 97在线观视频免费观看 | 午夜精品在线 | 欧美一级网站 | 国产精品日韩在线观看 | 99精品欧美一区二区蜜桃免费 | 国产成人免费观看 | 欧美日韩国产一区二区三区 | 97久久久久久久久 | 在线婷婷 | 美日韩免费视频 | 婷婷色在线 |