機器學習 10大演算法

redufa發表於2024-10-10

目錄
  • 一、演算法特點
    • 1. KNN 分類演算法
    • 2. 線性迴歸
    • 3. 邏輯迴歸
    • 4. 支援向量機(SVM)
    • 5. 決策樹
    • 6. 隨機森林
    • 7. 樸素貝葉斯
    • 8. 梯度提升(Gradient Boosting)
    • 9. 整合學習
    • 10. 神經網路
  • 二、應用程式碼
    • 1. KNN 分類演算法
    • 2. 線性迴歸
    • 3. 邏輯迴歸
    • 4. 支援向量機(SVM)
    • 5. 決策樹
    • 6. 隨機森林
    • 7. 樸素貝葉斯
    • 8. K-均值聚類
    • 9. 主成分分析(PCA)
    • 10. 梯度提升(Gradient Boosting)
  • 三、原理程式碼
    • 1. KNN 分類
    • 2.線性迴歸
    • 3.邏輯迴歸
    • 4.支援向量機(SVM)
    • 5. 決策樹
    • 6. 隨機森林
    • 7. 樸素貝葉斯
    • 8. K-均值聚類
    • 9.主成分分析(PCA)
    • 10. 梯度提升(Gradient Boosting)

一、演算法特點

機器學習中10大經典演算法的適用場景和優缺點:

1. KNN 分類演算法

  • 適用場景:適用於分類和迴歸問題,特別適合於多分類問題,適合對稀有事件進行分類。
  • 優點:簡單,易於理解,易於實現,無需估計引數。可以處理分類問題,同時天然可以處理多分類問題,適合對異常點不敏感。
  • 缺點:計算量太大,尤其是特徵數非常多的時候。可理解性差,無法給出像決策樹那樣的規則。是慵懶散學習方法,基本上不學習,導致預測時速度比起邏輯迴歸之類的演算法慢。樣本不平衡的時候,對稀有類別的預測準確率低。對訓練資料依賴度特別大,對訓練資料的容錯性太差。

2. 線性迴歸

  • 適用場景:適用於預測數值型資料的監督學習演算法,適用於線性可分和特徵空間不太大的情況。
  • 優點:模型簡單,易於理解和實現,計算效率高。
  • 缺點:對異常值敏感,對特徵相關性敏感,對特徵空間的規模有限制,對非線性問題表現不佳。

3. 邏輯迴歸

  • 適用場景:最常用於解決二分類問題,但也可以擴充套件到多分類問題。可以用於預測某一事件發生的機率。
  • 優點:模型解釋性強,適用於線性可分資料,計算效率高,可用於多分類問題。
  • 缺點:對異常值敏感,對特徵相關性敏感,對特徵空間的規模有限制,對非線性問題表現不佳。

4. 支援向量機(SVM)

  • 適用場景:適用於分類和迴歸分析,特別適用於非線性問題和高維資料。
  • 優點:可以解決高維問題,解決小樣本下機器學習問題,能夠處理非線性特徵的相互作用,無區域性極小值問題,泛化能力比較強。
  • 缺點:當觀測樣本很多時,效率並不是很高;對非線性問題沒有通用解決方案,有時候很難找到一個合適的核函式;對核函式的高維對映解釋力不強,尤其是徑向基函式;常規SVM只支援二分類;對缺失資料敏感。

5. 決策樹

  • 適用場景:適用於分類和迴歸問題,可以處理連續和種類欄位。
  • 優點:可以生成可以理解的規則,計算量相對不是很大,可以處理連續和種類欄位,可以清晰的顯示哪些欄位比較重要。
  • 缺點:對連續型欄位比較難預測,對於有時間順序資料,需要許多預處理工作,當類別較多時,錯誤可能增加的比較快,對處理特徵關聯性比較強的資料時,表現的不是太好。

6. 隨機森林

  • 適用場景:適用於分類和迴歸問題,可以處理高維資料,不需要進行特徵選擇,可以處理缺失值和異常值。
  • 優點:隨機選擇特徵和樣本,減少了過擬合的風險,可以處理高維資料,不需要進行特徵選擇,可以處理缺失值和異常值,可以評估每個特徵的重要性,用於特徵選擇和解釋模型。
  • 缺點:隨機森林分類器的訓練時間比單棵決策樹長,需要構建多棵決策樹,隨機森林分類器的模型比較複雜,不易解釋。

7. 樸素貝葉斯

  • 適用場景:適用於文字分類、情感分析、疾病診斷輔助等。
  • 優點:演算法簡單易懂,容易實現,對小規模資料表現良好,對缺失資料不太敏感。
  • 缺點:假設特徵之間相互獨立,這在很多實際情況中並不成立,對輸入資料的準備方式(如離散化、特徵選擇等)比較敏感。

8. 梯度提升(Gradient Boosting)

  • 適用場景:適用於迴歸問題(線性和非線性);也可用於二分類問題(設定閾值,大於為正,否則為負)和多分類問題。
  • 優點:可以靈活處理各種型別的資料,包括連續值和離散值,在相對少的調參時間情況下,預測的準備率也可以比較高,使用一些健壯的損失函式,對異常值的魯棒性非常強,很好的利用了弱分類器進行級聯,充分考慮的每個分類器的權重。
  • 缺點:由於弱學習器之間存在依賴關係,難以並行訓練資料。

9. 整合學習

  • 適用場景:適用於需要提高模型泛化能力和效能的場景,可以減少過擬合,提高模型的魯棒性和可解釋性。
  • 優點:減少過擬合,提高模型的泛化能力和效能,提高模型的魯棒性和可解釋性。
  • 缺點:需要較高的計算資源和時間成本,可能導致模型的複雜性增加。

10. 神經網路

  • 適用場景:適用於影像識別、語音識別、自然語言處理等複雜任務。
  • 優點:能夠自動學習資料的複雜特徵表示,具有很強的表達能力。
  • 缺點:訓練過程複雜且計算資源消耗大,容易過擬合。

二、應用程式碼

以下是機器學習中10大經典演算法的原理簡介,以及相應的Python程式碼實現。

1. KNN 分類演算法

原理:KNN(K-Nearest Neighbors)演算法是一種基於例項的學習,或者說是懶惰學習。它的核心思想是在預測新資料的類別時,不是透過訓練學習輸入資料到輸出資料的對映關係,而是直接在分類時,將該資料與訓練資料進行對比,找出與之最為相似的K個訓練例項,然後根據這些例項的標籤決定新資料的標籤。

程式碼

from sklearn.neighbors import KNeighborsClassifier
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split

# 載入資料集
iris = load_iris()
X, y = iris.data, iris.target

# 劃分訓練集和測試集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 建立KNN分類器例項
knn = KNeighborsClassifier(n_neighbors=3)

# 訓練模型
knn.fit(X_train, y_train)

# 預測測試集
y_pred = knn.predict(X_test)

2. 線性迴歸

原理:線性迴歸是一種預測數值型資料的監督學習演算法。它透過擬合最佳直線來建立自變數和因變數的關係。這條最佳直線叫做迴歸線,並且用 Y = a * X + b 這條線性等式來表示。

程式碼

from sklearn.linear_model import LinearRegression
import numpy as np

# 建立資料集
X = np.array([[1], [2], [3], [4], [5]])
y = np.array([1, 2, 1.3, 3.75, 2.25])

# 建立線性迴歸模型例項
lin_reg = LinearRegression()

# 訓練模型
lin_reg.fit(X, y)

# 預測
y_pred = lin_reg.predict(X)

3. 邏輯迴歸

原理:邏輯迴歸是一種用於二分類問題的監督學習演算法,它將資料對映到logit函式來預測事件發生的機率。因此,它也被稱為logit迴歸。

程式碼

from sklearn.linear_model import LogisticRegression
from sklearn.datasets import make_classification

# 建立資料集
X, y = make_classification(n_samples=100, n_features=2, n_classes=2, random_state=42)

# 建立邏輯迴歸模型例項
log_reg = LogisticRegression()

# 訓練模型
log_reg.fit(X, y)

# 預測
y_pred = log_reg.predict(X)

4. 支援向量機(SVM)

原理:支援向量機是一種強大的分類器,它的基本思想是在特徵空間中尋找一個最優的超平面,以此來區分不同的類別。支援向量機將向量對映到一個更高維的空間裡,在這個空間裡建立有一個最大間隔超平面。

程式碼

from sklearn.svm import SVC

# 建立SVM分類器例項
svm_clf = SVC(kernel='linear')

# 訓練模型(使用上面的X_train, y_train)
svm_clf.fit(X_train, y_train)

# 預測(使用上面的X_test)
y_pred = svm_clf.predict(X_test)

5. 決策樹

原理:決策樹是一種樹形結構,用於分類和迴歸的決策規則。它透過學習簡單的決策規則來預測目標變數的值。

程式碼

from sklearn.tree import DecisionTreeClassifier

# 建立決策樹分類器例項
dec_tree = DecisionTreeClassifier()

# 訓練模型(使用上面的X_train, y_train)
dec_tree.fit(X_train, y_train)

# 預測(使用上面的X_test)
y_pred = dec_tree.predict(X_test)

6. 隨機森林

原理:隨機森林是一種整合學習方法,它透過構建多個決策樹並輸出平均結果來提高預測準確性。

程式碼

from sklearn.ensemble import RandomForestClassifier

# 建立隨機森林分類器例項
rand_forest = RandomForestClassifier(n_estimators=100)

# 訓練模型(使用上面的X_train, y_train)
rand_forest.fit(X_train, y_train)

# 預測(使用上面的X_test)
y_pred = rand_forest.predict(X_test)

7. 樸素貝葉斯

原理:樸素貝葉斯是一種基於機率理論的簡單分類器,它假設預測變數之間相互獨立。

程式碼

from sklearn.naive_bayes import GaussianNB

# 建立樸素貝葉斯分類器例項
nb = GaussianNB()

# 訓練模型(使用上面的X_train, y_train)
nb.fit(X_train, y_train)

# 預測(使用上面的X_test)
y_pred = nb.predict(X_test)

8. K-均值聚類

原理:K-均值是一種無監督學習演算法,用於將資料點分組成K個簇。它透過迭代地移動簇中心(質心)來最小化簇內距離的總和。

程式碼

from sklearn.cluster import KMeans

# 建立K-均值聚類例項
kmeans = KMeans(n_clusters=3)

# 訓練模型(使用上面的X_train)
kmeans.fit(X_train)

# 預測
y_pred = kmeans.predict(X_train)

9. 主成分分析(PCA)

原理:PCA是一種用於資料降維的演算法,它透過正交變換將資料轉換到新的座標系統中,使得資料的任何投影的第一大方差在第一個座標(稱為第一主成分)上,第二大方差在第二個座標上,依此類推。

程式碼

from sklearn.decomposition import PCA

# 建立PCA例項
pca = PCA(n_components=2)

# 訓練模型(使用上面的X_train)
pca.fit(X_train)

# 降維
X_train_pca = pca.transform(X_train)

10. 梯度提升(Gradient Boosting)

原理:梯度提升是一種整合學習演算法,它透過迭代地訓練決策樹來最小化損失函式。每一棵樹都嘗試糾正前一棵樹的錯誤。

程式碼

from sklearn.ensemble import GradientBoostingClassifier

# 建立梯度提升分類器例項
gb_clf = GradientBoostingClassifier(n_estimators=100)

# 訓練模型(使用上面的X_train, y_train)
gb_clf.fit(X_train, y_train)

# 預測(使用上面的X_test)
y_pred = gb_clf.predict(X_test)

這些程式碼示例提供了使用scikit-learn庫實現這些演算法的基本框架。在實際應用中,你可能需要進行資料預處理、特徵選擇、模型調優等步驟。

KNN(K-Nearest Neighbors)演算法的核心思想是,對於一個待分類的樣本,演算法會在訓練集中找到與其最近的K個樣本(即K個鄰居),然後根據這些鄰居的標籤來確定待分類樣本的標籤。在Python中,我們可以使用scikit-learn庫來輕鬆實現KNN演算法,但為了更好地理解KNN演算法,下面我將提供一個手寫實現的版本,不依賴於scikit-learn

三、原理程式碼

1. KNN 分類

首先,我們需要定義一個函式來計算兩個樣本之間的距離,這裡我們使用歐氏距離:

import numpy as np

def euclidean_distance(x1, x2):
    return np.sqrt(np.sum((x1 - x2) ** 2))

接下來,我們定義KNN分類器類:

class KNN:
    def __init__(self, k=3):
        self.k = k

    def fit(self, X, y):
        """X is the feature matrix and y is the label vector"""
        self.X_train = X
        self.y_train = y

    def predict(self, X):
        """X is the feature matrix of the test data"""
        predicted_labels = [self._predict(x) for x in X]
        return np.array(predicted_labels)

    def _predict(self, x):
        # 計算待測樣本與訓練集中所有樣本的距離
        distances = [euclidean_distance(x, x_train) for x_train in self.X_train]
        
        # 獲取與之最近的k個鄰居的索引
        k_indices = np.argsort(distances)[:self.k]
        
        # 獲取這些鄰居的標籤
        k_nearest_labels = [self.y_train[i] for i in k_indices]
        
        # 採用投票機制,多數類標籤作為預測標籤
        most_common = np.argmax(np.bincount(k_nearest_labels))
        return most_common

現在,我們可以使用這個KNN類來訓練模型並進行預測。以下是如何使用這個手寫KNN分類器的示例:

# 假設我們有一些訓練資料和標籤
X_train = np.array([[1, 2], [2, 3], [3, 1], [6, 5], [7, 7], [8, 6]])
y_train = np.array([0, 0, 0, 1, 1, 1])

# 假設我們有一些測試資料
X_test = np.array([[1, 1], [5, 5]])

# 建立KNN分類器例項,設定k=3
knn = KNN(k=3)

# 訓練模型
knn.fit(X_train, y_train)

# 進行預測
predictions = knn.predict(X_test)

print(predictions)  # 輸出預測結果

請注意,這個手寫的KNN實現是非常基礎的版本,沒有進行最佳化,因此在處理大型資料集時可能效率較低。在實際應用中,推薦使用scikit-learn等庫提供的最佳化過的實現。

2.線性迴歸

線性迴歸是一種預測數值型資料的監督學習演算法。它的基本形式是:[ y = wx + b ],其中 ( w ) 是權重,( b ) 是偏置項,而 ( x ) 是特徵,( y ) 是目標值。

下面是一個簡單的線性迴歸模型的Python手寫程式碼實現,我們將從頭開始編寫程式碼,不使用任何專門的機器學習庫。

import numpy as np

# 定義線性迴歸類
class LinearRegression:
    def __init__(self):
        self.w = None
        self.b = None

    # 訓練模型的方法
    def fit(self, X, y, learning_rate=0.01, n_iterations=1000):
        # 初始化權重和偏置
        self.w = np.zeros(X.shape[1])
        self.b = 0
        
        # 梯度下降
        for _ in range(n_iterations):
            # 預測值
            y_pred = np.dot(X, self.w) + self.b
            
            # 計算梯度
            dw = (-2/X.shape[0]) * np.dot(X.T, (y - y_pred))
            db = (-2/X.shape[0]) * np.sum(y - y_pred)
            
            # 更新權重和偏置
            self.w -= learning_rate * dw
            self.b -= learning_rate * db

    # 預測新資料的方法
    def predict(self, X):
        return np.dot(X, self.w) + self.b

# 為簡單起見,我們建立一些合成資料
# 真實權重為1.5,真實偏置為0.5
X = 2 * np.random.rand(100, 1)
y = 3 + 2.5 * X.squeeze() + np.random.randn(100)

# 建立線性迴歸模型例項
model = LinearRegression()

# 訓練模型
model.fit(X, y)

# 進行預測
predictions = model.predict(X)

# 列印預測值和真實值
print("Predictions:", predictions)
print("Real values:", y)

在這段程式碼中,我們首先定義了一個LinearRegression類,它有一個fit方法來訓練模型和一個predict方法來進行預測。在fit方法中,我們使用梯度下降演算法來更新權重和偏置。我們初始化權重為零,然後不斷迭代,計算預測值和實際值之間的差異,然後根據這個差異來更新權重和偏置。

請注意,這個簡單的實現沒有包括正則化、批處理或任何高階最佳化技術。在實際應用中,你可能需要使用更高階的最佳化演算法(如Adam或RMSprop),並可能需要新增正則化項來防止過擬合。此外,對於大規模資料集,逐個樣本的梯度下降可能效率較低,你可能需要使用小批次梯度下降或隨機梯度下降。在實際應用中,通常會使用像scikit-learn這樣的庫,它們提供了這些高階特性和最佳化。

3.邏輯迴歸

邏輯迴歸是一種用於二分類問題的機器學習演算法。它使用sigmoid函式將線性迴歸模型的輸出對映到0和1之間,表示機率。

以下是邏輯迴歸模型的Python手寫程式碼實現,不依賴於任何專門的機器學習庫:

import numpy as np

# 定義sigmoid函式
def sigmoid(x):
    return 1 / (1 + np.exp(-x))

# 定義邏輯迴歸類
class LogisticRegression:
    def __init__(self, learning_rate=0.01, n_iterations=1000):
        self.learning_rate = learning_rate
        self.n_iterations = n_iterations
        self.weights = None
        self.bias = None

    # 訓練模型的方法
    def fit(self, X, y):
        # 初始化引數
        self.weights = np.zeros(X.shape[1])
        self.bias = 0
        
        # 梯度下降
        for _ in range(self.n_iterations):
            # 計算模型的線性組合
            linear_model = np.dot(X, self.weights) + self.bias
            # 應用sigmoid函式
            y_predicted = sigmoid(linear_model)
            
            # 計算梯度
            dw = (1 / X.shape[0]) * np.dot(X.T, (y_predicted - y))
            db = (1 / X.shape[0]) * np.sum(y_predicted - y)
            
            # 更新權重和偏置
            self.weights -= self.learning_rate * dw
            self.bias -= self.learning_rate * db

    # 預測新資料的方法
    def predict(self, X):
        linear_model = np.dot(X, self.weights) + self.bias
        y_predicted = sigmoid(linear_model)
        y_predicted_cls = [1 if i > 0.5 else 0 for i in y_predicted]
        return np.array(y_predicted_cls)

    # 預測機率的方法
    def predict_proba(self, X):
        linear_model = np.dot(X, self.weights) + self.bias
        y_predicted = sigmoid(linear_model)
        return y_predicted

# 建立一些合成資料
# 假設我們有一些二分類的資料
X = np.array([[0.5, 1.5], [1, 2], [2, 2.5], [3, 4], [5, 5]])
y = np.array([0, 0, 0, 1, 1])

# 建立邏輯迴歸模型例項
model = LogisticRegression(learning_rate=0.01, n_iterations=1000)

# 訓練模型
model.fit(X, y)

# 進行預測
predictions = model.predict(X)

# 列印預測值和真實值
print("Predictions:", predictions)
print("Real values:", y)

在這段程式碼中,我們首先定義了一個LogisticRegression類,它有一個fit方法來訓練模型,一個predict方法來進行預測,以及一個predict_proba方法來輸出屬於每個類別的機率。

fit方法中,我們使用梯度下降演算法來更新權重和偏置。我們初始化權重為零,然後不斷迭代,計算預測值和實際值之間的差異,然後根據這個差異來更新權重和偏置。

請注意,這個簡單的實現沒有包括正則化、批處理或任何高階最佳化技術。在實際應用中,你可能需要使用更高階的最佳化演算法(如Adam或RMSprop),並可能需要新增正則化項來防止過擬合。此外,對於大規模資料集,逐個樣本的梯度下降可能效率較低,你可能需要使用小批次梯度下降或隨機梯度下降。在實際應用中,通常會使用像scikit-learn這樣的庫,它們提供了這些高階特性和最佳化。

4.支援向量機(SVM)

支援向量機(SVM)是一種強大的分類演算法,它透過尋找最大間隔超平面來區分不同類別的資料點。在二分類問題中,SVM的目標是找到一個超平面,使得兩個類別之間的間隔最大化。

以下是支援向量機(SVM)的Python手寫程式碼實現,不依賴於任何專門的機器學習庫:

import numpy as np

class SVM:
    def __init__(self, learning_rate=0.001, lambda_param=0.01, n_iterations=1000):
        self.lr = learning_rate
        self.lambda_param = lambda_param
        self.n_iterations = n_iterations
        self.w = None
        self.b = None

    def fit(self, X, y):
        n_samples, n_features = X.shape
        self.w = np.zeros(n_features)
        self.b = 0

        for _ in range(self.n_iterations):
            for idx, x_i in enumerate(X):
                condition = y[idx] * (np.dot(x_i, self.w) - self.b) >= 1
                if condition:
                    self.w -= self.lr * (2 * self.lambda_param * self.w)
                else:
                    self.w -= self.lr * (2 * self.lambda_param * self.w - np.dot(x_i, y[idx]))
                    self.b -= self.lr * y[idx]

    def predict(self, X):
        linear_output = np.dot(X, self.w) - self.b
        return np.sign(linear_output)

# 生成一些合成資料
X = np.array([[5, 5], [3, 5], [4, 3], [2, 3], [5, 3], [5, 4], [3, 5], [4, 4], [3, 3], [4, 2], [3, 2], [2, 4]])
y = np.array([-1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, 1])

# 建立SVM模型例項
svm = SVM(learning_rate=0.001, lambda_param=0.01, n_iterations=1000)

# 訓練模型
svm.fit(X, y)

# 進行預測
predictions = svm.predict(X)

# 列印預測值和真實值
print("Predictions:", predictions)
print("Real values:", y)

在這個簡單的實現中,我們使用了一種稱為序列最小最佳化(SMO)演算法的簡化版本來訓練SVM模型。這個實現沒有包括核函式,這意味著它只能處理線性可分的資料集。在實際應用中,SVM通常使用核技巧來處理非線性可分的資料集,例如使用徑向基函式(RBF)核或多項式核。

請注意,這個實現是為了教育目的而簡化的,它沒有包括許多重要的特性,如支援向量的識別、不同核函式的支援、類別不平衡的處理等。在實際應用中,通常會使用像scikit-learn這樣的庫,它們提供了這些高階特性和最佳化。

5. 決策樹

決策樹是一種模仿人類決策過程的分類演算法。它透過學習簡單的決策規則來預測目標變數的值。以下是使用Python手寫一個簡單的決策樹分類器的程式碼實現:

import numpy as np

# 定義決策樹類
class DecisionTreeClassifier:
    def __init__(self, max_depth=None):
        self.max_depth = max_depth
        self.tree = None

    # 計算資訊增益
    def information_gain(self, X, y, split_attribute_name):
        # 計算原始資料集的熵
        parent_entropy = self.calculate_entropy(y)
        
        # 計算分裂後的資料集的熵
        values = X[:, split_attribute_name]
        unique_values = np.unique(values)
        weighted_entropy = 0.0
        for value in unique_values:
            sub_y = y[values == value]
            weighted_entropy += (len(sub_y) / len(y)) * self.calculate_entropy(sub_y)
        
        # 計算資訊增益
        information_gain = parent_entropy - weighted_entropy
        return information_gain

    # 計算熵
    def calculate_entropy(self, y):
        hist = np.bincount(y)
        ps = hist / len(y)
        return -np.sum([p * np.log2(p) for p in ps if p > 0])

    # 找到最佳分裂屬性
    def best_split(self, X, y):
        best_gain = 0.0
        best_attribute = -1
        num_features = X.shape[1]
        
        # 遍歷每個屬性
        for feature in range(num_features):
            gain = self.information_gain(X, y, feature)
            if gain > best_gain:
                best_gain = gain
                best_attribute = feature
                
        return best_attribute

    # 建立樹的節點
    def to_terminal(self, X, y, depth=0):
        num_samples, num_features = X.shape
        # 如果所有樣本都屬於同一個類別,則停止劃分
        if len(np.unique(y)) <= 1:
            return np.unique(y)[0]
        # 如果達到最大深度,則停止劃分
        if self.max_depth is not None and depth >= self.max_depth:
            return np.bincount(y).argmax()
        
        # 如果無法進一步分裂,則返回最常見的類別
        if len(np.unique(X[:, 0])) <= 1:
            return np.bincount(y).argmax()
        
        best_feature = self.best_split(X, y)
        return {best_feature: self.graft(X, y, best_feature, depth + 1)}

    # 生長樹
    def graft(self, X, y, feature, depth=0):
        ret = {feature: {}}

        values = X[:, feature]
        unique_values = np.unique(values)
        for value in unique_values:
            sub_X = X[values == value]
            sub_y = y[values == value]
            ret[feature][value] = self.to_terminal(sub_X, sub_y, depth + 1)
        
        return ret

    # 訓練模型
    def fit(self, X, y):
        self.tree = self.to_terminal(X, y)

    # 預測新樣本
    def predict(self, sample):
        tree = self.tree
        for feature_index in sample:
            branch = tree[feature_index]
            if isinstance(branch, dict):
                tree = branch[sample[feature_index]]
            else:
                return branch
        return tree

# 示例資料
X = np.array([[1, 2], [2, 3], [3, 1], [6, 5], [7, 7], [8, 6]])
y = np.array([0, 0, 0, 1, 1, 1])

# 建立決策樹分類器例項
dt = DecisionTreeClassifier(max_depth=2)

# 訓練模型
dt.fit(X, y)

# 進行預測
predictions = [dt.predict(sample) for sample in X]

# 列印預測值和真實值
print("Predictions:", predictions)
print("Real values:", y)

在這個簡單的實現中,我們首先定義了一個DecisionTreeClassifier類,它有一個fit方法來訓練模型和一個predict方法來進行預測。我們使用資訊增益作為分裂標準,並遞迴地建立樹的節點,直到達到最大深度或無法進一步分裂。

請注意,這個實現是為了教育目的而簡化的,它沒有包括許多重要的特性,如剪枝、處理缺失值、連續屬性分裂等。在實際應用中,通常會使用像scikit-learn這樣的庫,它們提供了這些高階特性和最佳化。

6. 隨機森林

隨機森林(Random Forest)是一種整合學習方法,它透過構建多個決策樹並結合它們的預測結果來提高整體模型的效能和準確性。以下是使用Python手寫一個簡單的隨機森林分類器的程式碼實現:

import numpy as np

class DecisionTreeClassifier:
    def __init__(self, max_depth=None):
        self.max_depth = max_depth
        self.tree = None

    def fit(self, X, y):
        self.tree = self._grow_tree(X, y)

    def _grow_tree(self, X, y, depth=0):
        if len(np.unique(y)) == 1 or depth == self.max_depth:
            return np.bincount(y).argmax()

        num_samples, num_features = X.shape
        if num_samples <= 1 or num_features == 0:
            return np.bincount(y).argmax()

        best_feature, best_threshold = self._best_split(X, y)
        if best_feature is None:
            return np.bincount(y).argmax()

        left_indices = X[:, best_feature] < best_threshold
        right_indices = X[:, best_feature] >= best_threshold

        left_sub_tree = self._grow_tree(X[left_indices], y[left_indices], depth + 1)
        right_sub_tree = self._grow_tree(X[right_indices], y[right_indices], depth + 1)

        return {best_feature: (best_threshold, left_sub_tree, right_sub_tree)}

    def _best_split(self, X, y):
        best_info_gain = -1
        best_feature, best_threshold = None, None
        num_samples, num_features = X.shape

        for feature in range(num_features):
            thresholds = np.unique(X[:, feature])
            for threshold in thresholds:
                left_indices = X[:, feature] < threshold
                right_indices = X[:, feature] >= threshold

                if len(left_indices) == 0 or len(right_indices) == 0:
                    continue

                p_left = len(left_indices) / num_samples
                p_right = len(right_indices) / num_samples

                left_entropy = self._calculate_entropy(y[left_indices])
                right_entropy = self._calculate_entropy(y[right_indices])
                info_gain = self._calculate_info_gain(
                    self._calculate_entropy(y), p_left * left_entropy + p_right * right_entropy
                )

                if info_gain > best_info_gain:
                    best_info_gain = info_gain
                    best_feature, best_threshold = feature, threshold

        return best_feature, best_threshold

    def _calculate_info_gain(self, parent_entropy, child_entropy):
        return parent_entropy - child_entropy

    def _calculate_entropy(self, y):
        hist = np.bincount(y)
        ps = hist / len(y)
        return -np.sum([p * np.log2(p) for p in ps if p > 0])

    def predict(self, X):
        return [self._predict(sample, self.tree) for sample in X]

    def _predict(self, sample, tree):
        if isinstance(tree, dict):
            feature, (threshold, left, right) = next(iter(tree.items()))
            if sample[feature] < threshold:
                return self._predict(sample, left)
            else:
                return self._predict(sample, right)
        else:
            return tree


class RandomForestClassifier:
    def __init__(self, n_trees=10, max_depth=None):
        self.n_trees = n_trees
        self.max_depth = max_depth
        self.trees = []

    def fit(self, X, y):
        for _ in range(self.n_trees):
            tree = DecisionTreeClassifier(max_depth=self.max_depth)
            indices = np.random.choice(len(X), len(X), replace=True)
            tree.fit(X[indices], y[indices])
            self.trees.append(tree)

    def predict(self, X):
        predictions = [tree.predict(X) for tree in self.trees]
        return np.array([np.bincount(pred).argmax() for pred in np.array(predictions).T])


# 示例資料
X = np.array([[1, 2], [2, 3], [3, 1], [6, 5], [7, 7], [8, 6]])
y = np.array([0, 0, 0, 1, 1, 1])

# 建立隨機森林分類器例項
rf = RandomForestClassifier(n_trees=10, max_depth=2)

# 訓練模型
rf.fit(X, y)

# 進行預測
predictions = rf.predict(X)

# 列印預測值和真實值
print("Predictions:", predictions)
print("Real values:", y)

在這個實現中,我們首先定義了一個DecisionTreeClassifier類,它包含了構建單個決策樹所需的方法。然後,我們定義了RandomForestClassifier類,它使用DecisionTreeClassifier類來構建多個決策樹,並在預測時透過投票機制來確定最終的預測結果。

請注意,這個實現是為了教育目的而簡化的,它沒有包括許多重要的特性,如特徵抽樣、處理缺失值、連續屬性分裂等。在實際應用中,通常會使用像scikit-learn這樣的庫,它們提供了這些高階特性和最佳化。此外,這個實現沒有進行特徵抽樣,因此每棵樹都是在整個特徵集上進行訓練的。在實際的隨機森林實現中,通常會在構建每棵樹時隨機選擇一部分特徵。

7. 樸素貝葉斯

樸素貝葉斯分類器是一種基於貝葉斯定理的簡單機率分類器,它假設特徵之間相互獨立。以下是使用Python手寫一個簡單的樸素貝葉斯分類器的程式碼實現:

import numpy as np

class NaiveBayesClassifier:
    def __init__(self):
        self.class_prior_prob = None
        self.conditional_prob = None

    def fit(self, X, y):
        n_samples, n_features = X.shape
        self.classes = np.unique(y)
        n_classes = len(self.classes)

        # 初始化機率
        self.class_prior_prob = np.zeros(n_classes)
        self.conditional_prob = np.zeros((n_classes, n_features))

        # 計算每個類的先驗機率
        for idx, c in enumerate(self.classes):
            X_c = X[y == c]
            self.class_prior_prob[idx] = len(X_c) / n_samples

            # 計算每個特徵的條件機率
            for i in range(n_features):
                feature_values = X_c[:, i]
                unique_values, counts = np.unique(feature_values, return_counts=True)
                probabilities = counts / len(feature_values)
                self.conditional_prob[idx, i] = probabilities

    def predict(self, X):
        y_pred = [self._predict(sample) for sample in X]
        return np.array(y_pred)

    def _predict(self, sample):
        # 計算每個類的後驗機率
        posterior_prob = []
        for idx, c in enumerate(self.classes):
            prior = np.log(self.class_prior_prob[idx])
            likelihood = np.sum(np.log(self.conditional_prob[idx, :]))
            posterior = prior + likelihood
            posterior_prob.append(posterior)

        # 返回具有最高後驗機率的類
        return self.classes[np.argmax(posterior_prob)]

# 示例資料
X = np.array([
    [1, 0, 0, 1],
    [0, 1, 1, 0],
    [1, 1, 0, 0],
    [0, 1, 0, 1]
])
y = np.array([0, 1, 0, 1])

# 建立樸素貝葉斯分類器例項
nb = NaiveBayesClassifier()

# 訓練模型
nb.fit(X, y)

# 進行預測
predictions = nb.predict(X)

# 列印預測值和真實值
print("Predictions:", predictions)
print("Real values:", y)

在這個實現中,我們首先定義了一個NaiveBayesClassifier類,它包含了fit方法來訓練模型和predict方法來進行預測。在fit方法中,我們計算每個類的先驗機率和每個特徵的條件機率。在predict方法中,我們使用貝葉斯定理來計算每個類的後驗機率,並選擇具有最高後驗機率的類作為預測結果。

請注意,這個實現是為了教育目的而簡化的,它假設所有特徵都是離散的。在處理連續特徵時,通常需要對特徵進行適當的離散化或使用高斯樸素貝葉斯,後者假設特徵遵循高斯分佈。此外,這個實現沒有包括拉普拉斯平滑,這是一種常見的技術,用於處理零機率問題。在實際應用中,通常會使用像scikit-learn這樣的庫,它們提供了這些高階特性和最佳化。

8. K-均值聚類

K均值聚類是一種無監督學習演算法,用於將資料點分組成K個簇。以下是使用Python手寫一個簡單的K均值聚類演算法的程式碼實現:

import numpy as np

class KMeans:
    def __init__(self, K=3, max_iters=100):
        self.K = K
        self.max_iters = max_iters
        self.centroids = None
        self.clusters = None

    def fit(self, X):
        # 初始化質心
        self.centroids = self._init_centroids(X, self.K)
        for _ in range(self.max_iters):
            # 將每個點分配到最近的質心
            self.clusters = self._assign_clusters(X, self.centroids)
            # 更新質心
            self.centroids = self._update_centroids(X, self.clusters)

    def _init_centroids(self, X, K):
        # 隨機選擇K個資料點作為初始質心
        indices = np.random.choice(X.shape[0], K, replace=False)
        return X[indices, :]

    def _assign_clusters(self, X, centroids):
        # 計算每個點到每個質心的距離,並分配到最近的質心
        clusters = {}
        for x in X:
            distances = np.linalg.norm(x - centroids, axis=1)
            cluster_idx = np.argmin(distances)
            if cluster_idx not in clusters:
                clusters[cluster_idx] = []
            clusters[cluster_idx].append(x)
        return clusters

    def _update_centroids(self, X, clusters):
        # 計算每個簇的新質心
        new_centroids = []
        for idx in clusters:
            new_centroid = np.mean(clusters[idx], axis=0)
            new_centroids.append(new_centroid)
        return np.array(new_centroids)

    def predict(self, X):
        # 對新資料點進行聚類
        return self._assign_clusters(X, self.centroids)

# 示例資料
X = np.array([
    [1, 2],
    [1, 4],
    [1, 0],
    [10, 2],
    [10, 4],
    [10, 0]
])

# 建立K均值聚類例項
kmeans = KMeans(K=2, max_iters=100)

# 訓練模型
kmeans.fit(X)

# 列印質心
print("Centroids:")
print(kmeans.centroids)

# 對資料點進行聚類
clusters = kmeans.predict(X)

# 列印聚類結果
print("Clusters:")
for idx, cluster in clusters.items():
    print(f"Cluster {idx}: {cluster}")

在這個實現中,我們首先定義了一個KMeans類,它包含了fit方法來訓練模型。在fit方法中,我們初始化質心,然後迭代地將每個點分配到最近的質心,並更新質心的位置。我們使用歐氏距離來計算點到質心的距離。

請注意,這個實現是為了教育目的而簡化的,它沒有包括一些重要的特性,如收斂條件的檢查、空簇的處理、初始化方法的選擇等。在實際應用中,通常會使用像scikit-learn這樣的庫,它們提供了這些高階特性和最佳化。此外,這個實現沒有包括對資料預處理的支援,如特徵縮放,這在實際應用中通常是必要的。

9.主成分分析(PCA)

主成分分析(PCA)是一種統計方法,它可以透過正交變換將一組可能相關的變數轉換成一組線性不相關的變數,這些變數稱為主成分。以下是使用Python手寫一個簡單的PCA實現的程式碼:

import numpy as np

class PCA:
    def __init__(self, n_components):
        self.n_components = n_components
        self.components_ = None
        self.explained_variance_ = None

    def fit(self, X):
        # 計算協方差矩陣
        cov_matrix = np.cov(X.T)
        # 計算協方差矩陣的特徵值和特徵向量
        eigen_values, eigen_vectors = np.linalg.eigh(cov_matrix)
        # 排序特徵向量和特徵值
        idx = eigen_values.argsort()[::-1]
        eigen_values = eigen_values[idx]
        eigen_vectors = eigen_vectors[:, idx]
        # 選擇前n個主成分
        self.components_ = eigen_vectors[:, :self.n_components]
        # 計算解釋的方差
        total_variance = np.sum(eigen_values)
        explained_variance_ratio = (eigen_values / total_variance)[self.n_components:]
        self.explained_variance_ = explained_variance_ratio

    def transform(self, X):
        # 投影資料到主成分
        return np.dot(X, self.components_)

    def fit_transform(self, X):
        self.fit(X)
        return self.transform(X)

# 示例資料
X = np.array([
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9],
    [10, 11, 12]
])

# 建立PCA例項
pca = PCA(n_components=2)

# 訓練模型並轉換資料
X_pca = pca.fit_transform(X)

# 列印轉換後的資料
print("Transformed data:")
print(X_pca)

# 列印解釋的方差
print("Explained variance:")
print(pca.explained_variance_)

在這個實現中,我們首先定義了一個PCA類,它包含了fit方法來計算協方差矩陣、特徵值和特徵向量,並選擇前n_components個主成分。transform方法用於將資料投影到這些主成分上。fit_transform方法結合了fittransform步驟。

請注意,這個實現是為了教育目的而簡化的,它沒有包括一些重要的特性,如資料中心化、特徵縮放、奇異值分解(SVD)等。在實際應用中,通常會使用像scikit-learn這樣的庫,它們提供了這些高階特性和最佳化。此外,這個實現沒有包括對資料預處理的支援,如特徵縮放,這在實際應用中通常是必要的。在實際應用中,通常使用SVD方法來提高PCA的數值穩定性和效率。

10. 梯度提升(Gradient Boosting)

梯度提升(Gradient Boosting)是一種強大的整合學習演算法,它透過迭代地訓練決策樹來最小化損失函式。以下是使用Python手寫一個簡單的梯度提升分類器的程式碼實現:

import numpy as np

# 決策樹樁(單層決策樹)
class DecisionStump:
    def __init__(self):
        self.threshold = None
        self.feature_idx = None
        self.value_left = None
        self.value_right = None

    def fit(self, X, y, loss_fn):
        n_samples, n_features = X.shape
        best_loss = np.inf
        best_threshold, best_feature_idx, best_value_left, best_value_right = None, None, None, None

        for feature_idx in range(n_features):
            thresholds = np.unique(X[:, feature_idx])
            for threshold in thresholds:
                values_left = y[X[:, feature_idx] <= threshold]
                values_right = y[X[:, feature_idx] > threshold]

                if len(values_left) == 0 or len(values_right) == 0:
                    continue

                loss_left = loss_fn(values_left, np.ones(len(values_left)) / 2)
                loss_right = loss_fn(values_right, np.ones(len(values_right)) / 2)

                loss = (len(values_left) * loss_left + len(values_right) * loss_right) / n_samples

                if loss < best_loss:
                    best_loss = loss
                    best_threshold = threshold
                    best_feature_idx = feature_idx
                    best_value_left = loss_left
                    best_value_right = loss_right

        self.threshold = best_threshold
        self.feature_idx = best_feature_idx
        self.value_left = best_value_left
        self.value_right = best_value_right

    def predict(self, X):
        predictions = np.ones(X.shape[0]) / 2
        predictions[X[:, self.feature_idx] <= self.threshold] = 0
        predictions[X[:, self.feature_idx] > self.threshold] = 1
        return predictions

# 梯度提升分類器
class GradientBoostingClassifier:
    def __init__(self, n_estimators=100, learning_rate=1.0):
        self.n_estimators = n_estimators
        self.learning_rate = learning_rate
        self.estimators = []

    def fit(self, X, y):
        n_samples, _ = X.shape
        self.y_mean_ = np.mean(y)

        self.predictions_ = np.zeros(n_samples)
        self.estimators = []

        for _ in range(self.n_estimators):
            stump = DecisionStump()
            residuals = y - self.predictions_
            stump.fit(X, residuals, self._loss_fn)
            self.estimators.append(stump)

            predictions = stump.predict(X)
            self.predictions_ += self.learning_rate * predictions

    def predict(self, X):
        predictions = np.zeros(X.shape[0])
        for estimator in self.estimators:
            predictions += self.learning_rate * estimator.predict(X)

        return np.where(predictions > 0.5, 1, 0)

    def _loss_fn(self, y_true, y_pred):
        # 使用二元交叉熵損失函式
        return -np.mean(y_true * np.log(y_pred) + (1 - y_true) * np.log(1 - y_pred))

# 示例資料
X = np.array([
    [1, 2],
    [2, 3],
    [3, 1],
    [6, 5],
    [7, 7],
    [8, 6]
])
y = np.array([0, 0, 0, 1, 1, 1])

# 建立梯度提升分類器例項
gbc = GradientBoostingClassifier(n_estimators=10, learning_rate=0.1)

# 訓練模型
gbc.fit(X, y)

# 進行預測
predictions = gbc.predict(X)

# 列印預測值和真實值
print("Predictions:", predictions)
print("Real values:", y)

在這個實現中,我們首先定義了一個DecisionStump類,它是一個單層決策樹,用於在每個迭代中擬合殘差。然後我們定義了GradientBoostingClassifier類,它包含了fit方法來訓練模型和predict方法來進行預測。在fit方法中,我們迭代地訓練決策樹樁,並更新預測值。

請注意,這個實現是為了教育目的而簡化的,它沒有包括一些重要的特性,如正則化、早停、處理缺失值等。在實際應用中,通常會使用像scikit-learn這樣的庫,它們提供了這些高階特性和最佳化。此外,這個實現沒有包括對資料預處理的支援,如特徵縮放,這在實際應用中通常是必要的。在實際應用中,通常會使用更復雜的樹結構,而不是簡單的決策樹樁。

相關文章