- C# 中的二次迴歸與線性迴歸基於相同的基礎:設計矩陣、最小平方法和矩陣運算。
- Solve 方法將預測變數和因變數分離,應用轉置、乘法和矩陣求逆來獲得模型係數。
- R 平方指標可以評估模型適配度,將解釋的變異性與總變異性進行比較,是評估預測品質的關鍵。
- 透過控制台結構和用於合成資料和矩陣代數的靜態方法,實現了從資料到數值預測的清晰流程。
C# 中的迴歸通常用線性迴歸的例子來講解,但在許多實際問題中,變數之間的關係並非簡單的直線,而是一條曲線,可以用二次函數很好地建模。雖然網路上找到的參考資料通常圍繞著線性迴歸展開,但這種方法同樣可以輕鬆應用於二次多項式模型,並保持相同的數學基礎:處理矩陣、係數和最小平方法。
本文將運用所有關於 C# 線性迴歸的現有知識(矩陣設計、矩陣求逆、R 平方計算、程式碼結構等),並對其進行重寫和調整,以詳細了解如何在C# 中實現二次迴歸方案。您將看到,從相同的基礎(矩陣的轉置、乘法和求逆)出發,如何將直線轉換為拋物線,以及如何將所有這些整合到易於理解的 .NET 控制台程式中,即使其中涉及一些數學複雜性。
在 C# 語言中,什麼是迴歸(線性迴歸和二次迴歸)?
簡單來說,迴歸問題就是根據一個或多個解釋變數來預測一個變數的數值。經典文獻中提到因變數(我們想要估計的值)和自變數或預測變數(我們用作輸入的資料)。當只有一個自變量時,通常稱為簡單迴歸;如果有多個自變量,則稱為多元迴歸或多變量迴歸。
經典線性迴歸假設產出與投入之間的關係可以表示為預測變數的線性組合。例如,我們可能想根據受教育年限、工作經驗和性別變數來預測一個人的年收入。每個預測變數都乘以一個係數,此外還包含一個常數項,作為滯後項或截距項。
二次回歸的想法與之類似,但更進一步:它將變數的平方項(以及,如果需要的話,交叉項)相加,以捕捉資料中的曲率。這樣,我們不再尋找穿過所有點的最佳直線,而是尋找使觀測值和估計值之間平方誤差總和最小的最佳拋物線(或其他二次多項式)。
從 C# 程式碼的角度來看,線性和二次版本都採用同一種矩陣方法求解:根據資料建立設計矩陣,建立最小二乘方程,並透過計算基於矩陣乘積和逆的封閉表達式來獲得係數。
使用 C# 從原始資料建立設計矩陣
典型的 C# 迴歸程序首先需要一個資料集,該資料集可以是真實資料集,也可以是合成資料集。常見的做法是準備幾行數據,每行包含預測變數的值,最後是我們要預測的數值輸出。以收入為例,資料集中會包含諸如受教育年限、工作經驗、性別(編碼為 0 或 1)以及以千元為單位的收入等列。
為了應用基於矩陣代數的最小平方法,資料表需要轉換成一個設計矩陣。此矩陣的建構方法是在所有原始列之前新增一個初始列,初始列的值全部為1。此初始列代表模型的截距。在線性模型中,設計矩陣的每一列都對應一個預測變數;在二次模型中,我們也會加入與預測變數平方對應的列,如果需要更複雜的計算,也可以加入包含預測變數之間交叉乘積的列。
在 C# 中,表示矩陣的簡單方法是使用double 類型的陣列的數組,而無需建立專門的 Matrix 類別。例如,一個名為 Design 的典型方法接受原始資料矩陣,並傳回一個新矩陣,該矩陣已包含全 1 列以及模型中要使用的所有預測變數(線性和二次)。
對於只有一個變數 x 的二次迴歸,每一行的設計矩陣都包含類似這樣的元素:1, x, x², y;其中 1 為常數項,x 為線性預測變量,x² 為二次項,y 為因變數的實際值。從線性迴歸到二次迴歸的轉變僅僅在於增加了包含這些額外冪次的列數。
利用最小平方法和矩陣求解係數。
無論是線性迴歸或二次迴歸,其本質問題都歸結為尋找使誤差平方和最小的係數。這種解可以用矩陣形式簡潔地表示,通常稱為閉式解或標準最小平方法方程式。
在數學符號中,如果我們稱 X 為設計矩陣(不包含因變數的最後一列),Y 為包含實際輸出值的列向量,β 為待估計的係數向量,那麼我們使用的公式基於矩陣的轉置、乘法和求逆。雖然這在紙面上看起來可能很複雜,但在 C# 程式碼中,它實際上可以簡化為一系列定義明確的矩陣運算:首先轉置 X,然後將轉置後的矩陣乘以 X,求逆,再次乘以 X,最後將結果應用於 Y 以獲得向量 β。
要實現這一點,三個操作至關重要:矩陣轉置(交換行和列)、矩陣乘法(合併兩個行列匹配的矩陣)以及方陣求逆。轉置的實現非常簡單;一旦了解矩陣的維度(n×m 乘以 m×p 得到一個 n×p 矩陣),乘法也易於處理。困難在於矩陣求逆,這通常需要更複雜的數值演算法,例如 LU 分解,如著名的杜利特爾分解。
在二次迴歸中,待求逆矩陣的大小取決於包含的預測變數(線性和二次項)的數量。一般來說,如果我們有 n 個輸入變量,並加上它們的二次項,則 X 的列數會增加,待求逆矩陣的大小將為(m × m),其中 m 是參數的總數(包括常數項)。這使得直接求逆法不適用於變數數量非常大的情況,但對於中等規模的問題則非常有效。
C# 中迴歸程序的典型結構
在 C# 中,用於實驗迴歸分析的經典專案通常是在 Visual Studio 中建立一個控制台應用程式。只需新建一個項目,選擇「控制台」模式,為其指定一個描述性名稱(例如 QuadraticRegression),然後編輯 Program.cs 檔案(或者如果您願意,也可以將其重新命名為更清晰的名稱)。無需任何特殊的外部程式庫,因為所有功能都可以使用基本的 System 類別和一些靜態方法來實現。
程式結構通常相當線性:在主方法中,列印一些開始和結束訊息,生成或載入數據,建立設計矩陣,透過最小二乘法求解係數,計算一些擬合指標(如 R 平方),並進行測試預測以檢查一切是否正常運作。
通常的做法是將邏輯分組為幾個輔助靜態方法,例如一個用於生成無需依賴外部文件即可進行測試的合成數據的方法,另一個用於構建設計矩陣的方法,一個名為 Solve 的中心方法接收該矩陣並返回係數的方法,以及其他用於計算 R² 等指標並在控制台上以用戶友好的格式顯示矩陣和向量的方法。
此外,還定義了一組專門用於矩陣代數運算的函數:MatrixTranspose 用於矩陣轉置,MatrixProduct 用於計算兩個矩陣的乘積,MatrixInverse 用於計算矩陣的逆矩陣,以及一些用於創建特定大小矩陣、複製矩陣或求解內部線性方程組的輔助方法。大部分技術細節集中在MatrixInverse 和矩陣分解這兩個方面,因為它們是數值計算中最具挑戰性的部分。
為了提高程式碼的可讀性,通常使用最簡單的陣列表示形式 `double[][]`,而不是設計帶有重載運算子的複雜類別。這種選擇簡化了程式碼,使範例更容易移植或適配到其他專案中,而無需遷移過多的程式碼基礎架構。
Solve 方法:係數計算的核心
迴歸演算法(包括線性迴歸和二次迴歸)的核心都集中在一個名為 Solve 的方法。此方法接收完整的設計矩陣,其中每一行包含預測變數(包括常數項的初始值 1),通常最後一列包含因變數。然後,資訊被分離成兩個內部矩陣:一個包含預測變數的 X 矩陣和一個包含輸出值的 Y 矩陣。
為了創建這兩個矩陣,需要逐行遍歷設計矩陣。在每次迭代中,除最後一列外的所有列都被複製到 X 矩陣,最後一列則作為 Y 矩陣的值。在分離資料時,請務必仔細檢查索引,避免出錯,因為即使是簡單的索引錯誤也可能導致係數完全錯誤,而編譯器卻不會發出任何異常警告。
一旦已知 X 和 Y,Solve 方法便將矩陣運算串聯起來:首先,求矩陣 Xt 的轉置;然後,計算 XtX(即 Xt 與 X 的乘積);接著,計算 XtX 的逆矩陣,並將其再次與 Xt 相乘。結果是一個中間矩陣,將其與 Y 相乘,即可得到另一個列矩陣,該矩陣的值恰好就是模型的係數。
這些係數通常會使用簡單的輔助方法轉換為一維向量,以便在程式的其餘部分輕鬆處理。這樣就可以定義一個類似 Income 的函數,該函數給定預測變數值和係數向量,只需將各項相乘並求和即可傳回模型的預測結果。
在二次迴歸中,步驟完全相同:唯一的差異在於 X 的列數較多,因為也包含了 x² 項以及可選的交互項。轉置、乘積和逆運算使用相同的程式計算,因此從線性模型過渡到二次模型不需要重寫核心邏輯,而只需要擴展設計矩陣。
計算R平方值作為適合度的指標。
儘管迴歸模型無需進一步分析即可進行訓練,但始終建議量化其與資料的擬合程度。 R 平方值(也稱為決定係數)常用於此目的。此指標取值範圍為 0 到 1,表示模型預測變數能夠解釋因變數變異的百分比。
在數學上,R² 通常定義為 1 減去殘差平方和與總平方和的比值。殘差平方和 (SSres) 是所有觀測值與模型估計值之間平方差的總和;總平方和 (SStot) 的計算方法是將每個觀測值與所有觀測值的總體平均值之間的平方差相加。
在專門用於計算 R 平方的 C# 方法中,通常會遍歷資料集,使用係數對每一行進行預測,並累積兩個量:殘差平方和與平均值偏差平方和。利用這兩個值,可以直接應用R² 公式,得到一個易於理解和解釋的數值。
R² 值接近 1 通常被認為是模型擬合良好的標誌,但具體解釋很大程度上取決於資料類型和背景。在資料雜訊很大的領域,0,6 或 0,7 左右的 R² 值也完全可以接受。對於二次模型,R² 也有助於比較是否值得加入平方項,還是應該使用純線性模型,但必須注意避免過度擬合。
有一種稱為調整後R平方的變體,它考慮了預測變數和觀測值的數量,並且會對變數過多的模型進行懲罰。然而,在許多C#入門迴歸範例中,簡單的R平方就足以清晰地了解模型的解釋力。
利用模型產生測試和預測數據
在 C# 中示範迴歸演算法時,通常會使用程式碼產生的合成資料。這樣可以精確控制變數的規模,並避免對外部文件的依賴。一個典型的方法,例如 `DummyData`,接受諸如要產生的行數和隨機數產生器的種子之類的參數,並傳回一個包含所有元素的數組,這些元素可用於填充設計矩陣。
在這些假設資料中,可以為每個預測變數設定一個合理的取值範圍,例如,受教育年限設定在一個較小的範圍內,工作經驗設定在一個特定的範圍內,或使用二元指示變數來表示性別。基於這些預測變量,可以使用受控公式計算因變量,並添加少量隨機雜訊以防止資料過於理想化。
模型訓練完成後,即可獲得係數,然後編寫一個方法,根據特定的輸入值計算預測結果。此方法(例如名為「收入」)接收數值參數和係數向量,並傳回對應的線性(或二次)組合結果。在演示程序中,通常會使用特定實例(例如具有特定教育程度和經驗的人)來展示模型產生的預測結果。
在二次迴歸中,預測函數必須考慮平方項。因此,不再只使用 coef0 + coef1 * x,而是計算類似 coef0 + coef1 * x + coef2 * x * x 的函數。如果有多個變量,結構會稍微複雜一些,但方法完全相同:設計矩陣的每一列都有一個對應的係數,預測值是透過將每個係數相乘並將所有結果相加得到的。
透過這種方法,C# 程式就像一個小型迴歸「黑盒」:輸入資料行,計算設計矩陣,使用 Solve 求解係數,使用 R 平方值衡量擬合度,最後將模型應用於新案例以獲得可在系統其他部分使用的數值估計。
最後,所有這些程式碼結構和支持方法表明,儘管回歸的理論概念可能看起來很抽象,但它在 C# 中的實際實現是基於一系列定義明確、可重用且相對機械的步驟,尤其是在掌握了矩陣運算和樣本數據的生成之後。
C# 中關於線性迴歸和二次迴歸的一系列概念,將這類問題變成了應用於程式設計的經典統計學的「Hello World」:它既有助於更好地理解數學理論和程式碼之間的橋樑,也有助於為具有相同精神的更高級的機器學習技術奠定基礎:借助數值運算和程式碼中組織良好的結構,將模型擬合到真實資料。