经典机器学习算法的极简实现(Python+NumPy)

大三的时候曾花两个星期学习了几个经典的机器学习算法,学习方法主要是白天参考《统计学习方法》推导公式,晚上利用公式编写实现。在参考GitHub上算法实现时,我发现其中大多数都比较繁杂冗长,很难体现出算法的核心思想。因此我特地找出了以前的机器学习算法实现,在修改整理后分享给大家(GitHub地址)。

所有算法的实现都没有使用其他机器学习库。希望可以帮助大家对机器学习算法及其本质原理有个基本的了解,但并不是提供最有效的实现。

目前已经实现的算法包括:

  • AdaBoost
  • 决策树
  • EM算法
  • 高斯混合模型
  • K-Means
  • K近邻
  • 线性判别分析
  • 逻辑斯蒂回归
  • 朴素贝叶斯
  • 主成因分析
  • 感知机
  • 支持向量机

尚未更新的有:

  • 梯度提升树
  • 随机森林
  • 条件随机场
  • 隐马尔可夫模型

算法实现尽可能精简,因此大多数都只有几行,例如:

  • AdaBoost:

    class AdaBoost:
        ...
    
        def fit(self, X: np.ndarray, Y: np.ndarray):
            weights = np.full([len(X)], 1 / len(X))  # 样本权重
            for _ in range(self.n_estimators):
                estimator = WeakEstimator(lr=self.lr)
                error = estimator.fit(X, Y, weights)  # 带权重训练弱分类器
                if error < self.eps:  # 误差达到下限,提前停止迭代
                    break
                alpha = np.log((1 - error) / error) / 2  # 更新弱分类器权重
                weights *= np.exp(-alpha * Y * estimator(X))  # 更新样本权重
                weights /= np.sum(weights)  # 除以规范化因子
                self.estimators += [(alpha, estimator)]  # 添加此弱分类器
    
        def __call__(self, X: np.ndarray):
            pred = sum((alpha * estimator(X) for alpha, estimator in self.estimators))
            return np.where(pred > 0, 1, -1)
    
  • 主成因分析:

    class PCA:
        ...
        
        def __call__(self, X: np.ndarray):
            X_norm = X - X.mean(axis=0)  # 去中心化
            L, U = np.linalg.eig(X_norm.T @ X_norm)  # 对协方差矩阵进行特征值分解
            topk = np.argsort(L)[::-1][:self.k]  # 找出前K大特征值对应的索引
            return X_norm @ U[:, topk]  # 将去中心化的X乘以前K大特征值对应的特征向量
    
  • K近邻:

    class KNN:
        ...
    
        def __call__(self, X: np.ndarray):
            Y = np.zeros([len(X)], dtype=int)  # X对应的类别
            for i, x in enumerate(X):
                dist = LA.norm(self.X - x, axis=1)  # 计算x与所有已知类别点的距离
                topk = np.argsort(dist)[:self.k]  # 取距离最小的k个点对应的索引
                Y[i] = np.bincount(self.Y[topk]).argmax()  # 取近邻点最多的类别作为x的类别
            return Y
    
  • 感知机:

    class Perceptron:
        ...
    
        def fit(self, X: np.ndarray, Y: np.ndarray):
            for x, y in zip(self._pad(X), Y):
                if y * (x @ self.weights) <= 0:  # 分类错误, y 与 wx + b 符号不同
                    neg_grad = x * y  # 计算weights的负梯度
                    self.weights += self.lr * neg_grad  # 沿负梯度方向更新weights
    
        def __call__(self, X: np.ndarray):
            pred = self._pad(X) @ self.weights
            return np.where(pred > 0, 1, -1)
    
  • 逻辑斯蒂回归:

    class LogisticRegression:
        ...
    
        def fit(self, X: np.ndarray, Y: np.ndarray):
            x_pad = self._pad(X)  # 为X填充1作为偏置
            pred = self._sigmoid(x_pad @ self.weights)  # 计算预测值
            grad = x_pad.T @ (pred - Y) / len(pred)  # 计算梯度
            self.weights -= self.lr * grad  # 沿负梯度更新参数
    
        def __call__(self, X: np.ndarray):
            x_pad = self._pad(X)  # 为X填充1作为偏置
            pred = self._sigmoid(x_pad @ self.weights)  # 计算预测值
            return np.where(pred > 0.5, 1, 0)  # 将(0, 1)之间分布的概率转化为{0, 1}标签
    

除了最精简的实现之外,还对一些算法进行了可视化:

  • AdaBoost:


    adaboost.png
  • 主成因分析:


    pca.png
  • 线性判别分析:


    LDA.png
  • 感知机:


    perceptron.gif
  • 高斯混合模型


    gmm.png
  • K-Means


    kmeans.gif
  • K近邻


    knn.png
  • 逻辑斯蒂回归


    logistic_regression.gif
  • 支持向量机


    svm.png

你可能感兴趣的:(经典机器学习算法的极简实现(Python+NumPy))