Python机器学习入门与实战_笔记

Python机器学习入门与实战 - 系统笔记


第1章 机器学习概述

核心思想:机器学习是让计算机通过数据自动学习规律,而无需显式编程的技术。

1.1 什么是机器学习?

是什么? 机器学习是人工智能的一个分支,通过算法让计算机从数据中学习模式并做出预测或决策。

为什么需要? 传统编程难以处理复杂、多变的问题(如图像识别、自然语言处理),需要让机器自动学习规律。

作用?

  • 自动发现数据中的隐藏模式
  • 进行预测和分类
  • 处理大规模复杂数据

局限?

  • 需要大量标注数据
  • 模型可解释性差(黑盒问题)
  • 对数据质量敏感

[由此引出:机器学习的分类方法]

1.2 机器学习的分类

类型 定义 特点 典型算法
监督学习 使用带标签的数据训练 有输入-输出对应关系 KNN、决策树、SVM、逻辑回归
无监督学习 使用无标签数据 发现数据内在结构 K-means、PCA
半监督学习 少量标签+大量无标签数据 降低标注成本 自训练、协同训练
强化学习 通过与环境交互学习 基于奖励/惩罚机制 Q-learning、DQN

第2章 Python机器学习基础

核心思想:Python凭借丰富的科学计算库成为机器学习的主流语言。

2.1 NumPy基础

是什么? Python科学计算的基础库,提供高效的多维数组操作。

为什么需要? Python原生列表效率低,NumPy提供C语言级别的运算速度。

核心功能代码:

python 复制代码
# NumPy数组创建与基本操作
import numpy as np

# 创建数组
arr = np.array([[1, 2, 3], [4, 5, 6]])

# 数组属性
print(arr.shape)    # (2, 3) - 形状
print(arr.ndim)     # 2 - 维度
print(arr.size)     # 6 - 元素总数

# 特殊数组创建
zeros = np.zeros((3, 3))      # 3x3零矩阵
ones = np.ones((2, 2))        # 2x2全1矩阵
identity = np.eye(3)          # 3x3单位矩阵
arange = np.arange(0, 10, 2)  # [0, 2, 4, 6, 8]

2.2 Pandas基础

是什么? 数据分析和处理的强大工具,提供DataFrame数据结构。

核心功能代码:

python 复制代码
import pandas as pd

# 创建DataFrame
df = pd.DataFrame({
    'name': ['Alice', 'Bob', 'Charlie'],
    'age': [25, 30, 35],
    'score': [85, 90, 88]
})

# 数据读取
df = pd.read_csv('data.csv')
df = pd.read_excel('data.xlsx')

# 基本操作
print(df.head())      # 前5行
print(df.describe())  # 统计描述
print(df['age'].mean())  # 均值

2.3 Matplotlib基础

是什么? Python最流行的数据可视化库。

核心功能代码:

python 复制代码
import matplotlib.pyplot as plt

# 折线图
plt.plot([1, 2, 3, 4], [1, 4, 9, 16])
plt.xlabel('X轴')
plt.ylabel('Y轴')
plt.title('折线图示例')
plt.show()

# 散点图
plt.scatter([1, 2, 3, 4], [1, 4, 9, 16])
plt.show()

第3章 K-近邻算法(KNN)

核心思想:通过计算样本与邻居的距离来进行分类,"物以类聚,人以群分"。

3.1 KNN算法原理

是什么? 一种基于实例的监督学习算法,通过测量不同特征值之间的距离进行分类。

为什么需要? 简单直观,无需训练过程,适合小数据集和基础分类任务。

作用?

  • 分类问题(如手写数字识别)
  • 回归问题(预测连续值)
  • 推荐系统

局限?

  • 计算复杂度高(需要计算与所有训练样本的距离)
  • 对异常值敏感
  • 需要大量内存存储训练数据
  • 维度灾难(高维数据效果差)

[由此引出:距离度量方法的选择]

3.2 距离度量方法

距离度量 公式 适用场景
欧氏距离 d=∑i=1n(xi−yi)2d = \sqrt{\sum_{i=1}^{n}(x_i - y_i)^2}d=∑i=1n(xi−yi)2 连续特征,空间距离重要
曼哈顿距离 $d = \sum_{i=1}^{n} x_i - y_i
闵可夫斯基距离 $d = (\sum_{i=1}^{n} x_i - y_i

欧氏距离公式
d(x,y)=∑i=1n(xi−yi)2d(x, y) = \sqrt{\sum_{i=1}^{n}(x_i - y_i)^2}d(x,y)=i=1∑n(xi−yi)2

3.3 KNN算法实现代码

python 复制代码
import numpy as np
from collections import Counter

def kNN_classify(k, X_train, y_train, x):
    """
    KNN分类器
    :param k: 邻居数量
    :param X_train: 训练数据
    :param y_train: 训练标签
    :param x: 待预测样本
    :return: 预测类别
    """
    # 计算距离
    distances = [np.sqrt(np.sum((x_train - x) ** 2)) for x_train in X_train]
    
    # 获取最近的k个邻居的索引
    nearest = np.argsort(distances)[:k]
    
    # 获取k个邻居的标签
    topK_y = [y_train[i] for i in nearest]
    
    # 投票表决
    votes = Counter(topK_y)
    return votes.most_common(1)[0][0]

3.4 K值选择的影响

K值 特点 风险
K较小 对噪声敏感,模型复杂 过拟合
K较大 决策边界平滑 欠拟合
K=N 所有样本投票,退化为基准 完全失效

[由此引出:特征归一化的必要性]

3.5 特征归一化

是什么? 将不同量纲的特征缩放到相同范围(通常是[0,1]或均值为0,方差为1)。

为什么需要? 避免量纲大的特征主导距离计算。

Min-Max归一化公式
xnorm=x−xminxmax−xminx_{norm} = \frac{x - x_{min}}{x_{max} - x_{min}}xnorm=xmax−xminx−xmin
Z-Score标准化公式
xstd=x−μσx_{std} = \frac{x - \mu}{\sigma}xstd=σx−μ

python 复制代码
# 特征归一化实现
def normalize(X):
    """Min-Max归一化"""
    min_val = np.min(X, axis=0)
    max_val = np.max(X, axis=0)
    return (X - min_val) / (max_val - min_val)

def standardize(X):
    """Z-Score标准化"""
    mean = np.mean(X, axis=0)
    std = np.std(X, axis=0)
    return (X - mean) / std

第4章 决策树

核心思想:通过递归地选择最优特征对数据进行划分,构建树形结构的分类模型。

4.1 信息熵

是什么? 度量样本集合纯度的指标,熵越小纯度越高。

为什么需要? 需要量化数据的不确定性,以选择最优划分特征。

信息熵公式
Ent(D)=−∑k=1∣y∣pklog⁡2pkEnt(D) = -\sum_{k=1}^{|y|}p_k \log_2 p_kEnt(D)=−k=1∑∣y∣pklog2pk

其中 pkp_kpk 是第k类样本所占比例

作用?

  • 评估数据集的纯度
  • 为特征选择提供量化依据

局限?

  • 对数计算量大
  • 偏向选择取值较多的特征

[由此引出:信息增益与特征选择]

4.2 信息增益

是什么? 使用某特征划分数据集前后信息熵的减少量。

信息增益公式
Gain(D,a)=Ent(D)−∑v=1V∣Dv∣∣D∣Ent(Dv)Gain(D, a) = Ent(D) - \sum_{v=1}^{V}\frac{|D^v|}{|D|}Ent(D^v)Gain(D,a)=Ent(D)−v=1∑V∣D∣∣Dv∣Ent(Dv)

决策树构建代码:

python 复制代码
import numpy as np
import operator

def calcEntroy(datas):
    """计算信息熵"""
    labelCounts = {}
    for feat in datas:
        currentLabel = feat[-1]
        if currentLabel not in labelCounts.keys():
            labelCounts[currentLabel] = 0
        labelCounts[currentLabel] += 1
    
    entroy = 0.0
    num = len(datas)
    for key in labelCounts:
        prob = float(labelCounts[key]) / num
        entroy -= prob * np.log2(prob)
    return entroy

def splitDatas(datas, axis, value):
    """按指定特征切分数据集"""
    returnDatas = []
    for feat in datas:
        if feat[axis] == value:
            featcopy = feat[:axis]
            featcopy.extend(feat[axis+1:])
            returnDatas.append(featcopy)
    return returnDatas

def chooseBest(datas):
    """提取信息增益最大的维度"""
    numFlag = len(datas[0]) - 1
    base = calcEntroy(datas)
    bestGain = 0.0
    bestFlag = -1
    
    for i in range(numFlag):
        lists = [feat[i] for feat in datas]
        simpleVals = set(lists)
        entroy_new = 0.0
        
        for value in simpleVals:
            subDatas = splitDatas(datas, i, value)
            prob = len(subDatas) / float(len(datas))
            entroy_new += prob * calcEntroy(subDatas)
        
        gain = base - entroy_new
        if gain > bestGain:
            bestGain = gain
            bestFlag = i
    return bestFlag

def createTree(datas, labels):
    """递归创建决策树"""
    classes = [example[-1] for example in datas]
    
    # 所有类别相同,返回该类别
    if classes.count(classes[0]) == len(classes):
        return classes[0]
    
    # 没有更多特征,返回多数类
    if len(datas[0]) == 1:
        return majorityNode(classes)
    
    bestFlag = chooseBest(datas)
    bestFlagLabel = labels[bestFlag]
    result_tree = {bestFlagLabel: {}}
    del labels[bestFlag]
    
    flag_value = [example[bestFlag] for example in datas]
    unique_value = set(flag_value)
    
    for mValue in unique_value:
        sub_labels = labels[:]
        result_tree[bestFlagLabel][mValue] = createTree(
            splitDatas(datas, bestFlag, mValue), sub_labels)
    return result_tree

def majorityNode(classes):
    """多数表决"""
    class_num = {}
    for cls in classes:
        if cls not in class_num.keys():
            class_num[cls] = 0
        class_num[cls] += 1
    sorted_class_num = sorted(class_num.items(), 
                              key=operator.itemgetter(1), reverse=True)
    return sorted_class_num[0][0]

4.3 决策树剪枝

是什么? 通过剪除决策树的分支来防止过拟合的技术。

为什么需要? 决策树容易过拟合训练数据,导致泛化能力差。

剪枝类型 时机 特点
预剪枝 构建过程中 提前停止分裂,速度快,可能欠拟合
后剪枝 构建完成后 自底向上剪枝,效果好,计算量大

[由此引出:集成学习方法]


第5章 朴素贝叶斯

核心思想:基于贝叶斯定理,假设特征之间相互独立,通过概率计算进行分类。

5.1 贝叶斯定理

是什么? 描述条件概率之间关系的定理。

贝叶斯公式
P(c∣x)=P(x∣c)P(c)P(x)P(c|x) = \frac{P(x|c)P(c)}{P(x)}P(c∣x)=P(x)P(x∣c)P(c)

其中:

  • P(c∣x)P(c|x)P(c∣x) 是后验概率
  • P(x∣c)P(x|c)P(x∣c) 是似然
  • P(c)P(c)P(c) 是先验概率
  • P(x)P(x)P(x) 是证据因子

朴素贝叶斯假设:特征之间相互独立

P(x∣c)=P(x1∣c)×P(x2∣c)×...×P(xn∣c)P(x|c) = P(x_1|c) \times P(x_2|c) \times ... \times P(x_n|c)P(x∣c)=P(x1∣c)×P(x2∣c)×...×P(xn∣c)

5.2 朴素贝叶斯分类器代码

python 复制代码
import numpy as np

def train_naive_bayes(train_mat, train_category):
    """
    朴素贝叶斯训练
    :param train_mat: 训练词向量矩阵
    :param train_category: 训练标签
    :return: 条件概率和先验概率
    """
    train_doc_num = len(train_mat)
    words_num = len(train_mat[0])
    
    # 计算先验概率(差评概率)
    pos_abusive = np.sum(train_category) / train_doc_num
    
    # 初始化计数器(使用拉普拉斯平滑)
    p0num = np.ones(words_num)  # 好评词频
    p1num = np.ones(words_num)  # 差评词频
    p0num_all = 2.0
    p1num_all = 2.0
    
    for i in range(train_doc_num):
        if train_category[i] == 1:
            p1num += train_mat[i]
            p1num_all += np.sum(train_mat[i])
        else:
            p0num += train_mat[i]
            p0num_all += np.sum(train_mat[i])
    
    # 取对数防止下溢出
    p1vec = np.log(p1num / p1num_all)
    p0vec = np.log(p0num / p0num_all)
    
    return p0vec, p1vec, pos_abusive

def classify_naive_bayes(vec2classify, p0vec, p1vec, p_class1):
    """
    朴素贝叶斯分类
    """
    p1 = np.sum(vec2classify * p1vec) + np.log(p_class1)
    p0 = np.sum(vec2classify * p0vec) + np.log(1 - p_class1)
    
    if p1 > p0:
        return 1
    else:
        return 0

5.3 朴素贝叶斯的优缺点

优点 缺点
训练速度快,适合大规模数据 "朴素"假设现实中很少成立
对缺失数据不敏感 对输入数据表达形式敏感
适合多分类任务 需要计算先验概率
可增量式训练 分类决策存在错误率

第6章 逻辑回归

核心思想:使用Sigmoid函数将线性回归结果映射到[0,1]区间,实现二分类。

6.1 Sigmoid函数

是什么? 一种S型函数,将任意实数映射到(0,1)区间。

Sigmoid函数公式
σ(z)=11+e−z\sigma(z) = \frac{1}{1 + e^{-z}}σ(z)=1+e−z1

为什么需要? 线性回归输出范围无限,分类需要概率输出。

作用? 将线性组合结果转化为概率值。

局限?

  • 两端饱和,梯度消失
  • 输出非零中心化

[由此引出:梯度下降优化]

6.2 梯度上升/下降算法

是什么? 通过迭代调整参数,使损失函数最大化/最小化的优化算法。

梯度上升更新公式
w:=w+α∇wJ(w)w := w + \alpha \nabla_w J(w)w:=w+α∇wJ(w)

其中 α\alphaα 是学习率(步长)

python 复制代码
import numpy as np

def sigmoid(x):
    """Sigmoid函数"""
    return 1.0 / (1 + np.exp(-x))

def grad_ascent(data_arr, class_labels):
    """
    梯度上升算法
    """
    data_mat = np.mat(data_arr)
    label_mat = np.mat(class_labels).transpose()
    m, n = np.shape(data_mat)
    
    alpha = 0.001      # 学习率
    max_cycles = 500   # 迭代次数
    weights = np.ones((n, 1))
    
    for k in range(max_cycles):
        h = sigmoid(data_mat * weights)
        error = label_mat - h
        weights = weights + alpha * data_mat.transpose() * error
    
    return weights

def stoc_grad_ascent1(data_mat, class_labels, num_iter=200):
    """
    随机梯度上升(改进版)
    """
    m, n = np.shape(data_mat)
    weights = np.ones(n)
    
    for j in range(num_iter):
        data_index = list(range(m))
        for i in range(m):
            # 动态调整学习率
            alpha = 4 / (1.0 + j + i) + 0.01
            rand_index = int(np.random.uniform(0, len(data_index)))
            h = sigmoid(np.sum(data_mat[data_index[rand_index]] * weights))
            error = class_labels[data_index[rand_index]] - h
            weights = weights + alpha * error * data_mat[data_index[rand_index]]
            del(data_index[rand_index])
    
    return weights

6.3 逻辑回归分类实现

python 复制代码
def classify_vector(in_x, weights):
    """
    逻辑回归分类
    """
    prob = sigmoid(np.sum(in_x * weights))
    if prob > 0.5:
        return 1.0
    return 0.0

第7章 支持向量机(SVM)

核心思想:寻找最大间隔超平面,使分类边界具有最强的泛化能力。

7.1 线性可分与超平面

是什么?

  • 线性可分:存在超平面能将两类样本完全分开
  • 超平面:n维空间中的(n-1)维子空间

超平面方程
wTx+b=0w^Tx + b = 0wTx+b=0

为什么需要最大间隔?

  • 间隔越大,分类越稳健
  • 泛化能力越强

[由此引出:间隔最大化问题]

7.2 间隔最大化

几何间隔公式
γ=∣wTx+b∣∣∣w∣∣\gamma = \frac{|w^Tx + b|}{||w||}γ=∣∣w∣∣∣wTx+b∣
SVM优化目标
max⁡w,b1∣∣w∣∣\max_{w,b} \frac{1}{||w||}w,bmax∣∣w∣∣1

s.t.yi(wTxi+b)≥1,i=1,2,...,ms.t. \quad y_i(w^Tx_i + b) \geq 1, \quad i=1,2,...,ms.t.yi(wTxi+b)≥1,i=1,2,...,m

7.3 SMO算法代码

python 复制代码
import numpy as np

def selectJrand(i, m):
    """随机选择第二个alpha"""
    j = i
    while j == i:
        j = int(np.random.uniform(0, m))
    return j

def clipAlpha(aj, H, L):
    """限制alpha在边界范围内"""
    if aj > H:
        aj = H
    if L > aj:
        aj = L
    return aj

def smoSimple(dataMatIn, classLabels, C, toler, maxIter):
    """
    简化版SMO算法
    """
    dataMatrix = np.mat(dataMatIn)
    labelMat = np.mat(classLabels).transpose()
    m, n = np.shape(dataMatrix)
    b = 0
    alphas = np.mat(np.zeros((m, 1)))
    iter = 0
    
    while iter < maxIter:
        alphaPairsChanged = 0
        for i in range(m):
            # 计算预测值和误差
            fXi = float(np.multiply(alphas, labelMat).T * 
                       (dataMatrix * dataMatrix[i, :].T)) + b
            Ei = fXi - float(labelMat[i])
            
            # 检查是否违反KKT条件
            if ((labelMat[i]*Ei < -toler) and (alphas[i] < C)) or \
               ((labelMat[i]*Ei > toler) and (alphas[i] > 0)):
                j = selectJrand(i, m)
                fXj = float(np.multiply(alphas, labelMat).T * 
                           (dataMatrix * dataMatrix[j, :].T)) + b
                Ej = fXj - float(labelMat[j])
                
                alphaIold = alphas[i].copy()
                alphaJold = alphas[j].copy()
                
                # 计算边界
                if labelMat[i] != labelMat[j]:
                    L = max(0, alphas[j] - alphas[i])
                    H = min(C, C + alphas[j] - alphas[i])
                else:
                    L = max(0, alphas[j] + alphas[i] - C)
                    H = min(C, alphas[j] + alphas[i])
                
                if L == H:
                    continue
                
                # 计算eta
                eta = 2.0 * dataMatrix[i, :] * dataMatrix[j, :].T - \
                      dataMatrix[i, :] * dataMatrix[i, :].T - \
                      dataMatrix[j, :] * dataMatrix[j, :].T
                
                if eta >= 0:
                    continue
                
                # 更新alpha
                alphas[j] -= labelMat[j] * (Ei - Ej) / eta
                alphas[j] = clipAlpha(alphas[j], H, L)
                
                if abs(alphas[j] - alphaJold) < 0.00001:
                    continue
                
                alphas[i] += labelMat[j] * labelMat[i] * (alphaJold - alphas[j])
                
                # 更新b
                b1 = b - Ei - labelMat[i] * (alphas[i] - alphaIold) * \
                     dataMatrix[i, :] * dataMatrix[i, :].T - \
                     labelMat[j] * (alphas[j] - alphaJold) * \
                     dataMatrix[i, :] * dataMatrix[j, :].T
                
                b2 = b - Ej - labelMat[i] * (alphas[i] - alphaIold) * \
                     dataMatrix[i, :] * dataMatrix[j, :].T - \
                     labelMat[j] * (alphas[j] - alphaJold) * \
                     dataMatrix[j, :] * dataMatrix[j, :].T
                
                if (0 < alphas[i]) and (C > alphas[i]):
                    b = b1
                elif (0 < alphas[j]) and (C > alphas[j]):
                    b = b2
                else:
                    b = (b1 + b2) / 2.0
                
                alphaPairsChanged += 1
        
        if alphaPairsChanged == 0:
            iter += 1
        else:
            iter = 0
    
    return b, alphas

def calcWs(alphas, dataArr, classLabels):
    """计算权重w"""
    X = np.mat(dataArr)
    labelMat = np.mat(classLabels).transpose()
    m, n = np.shape(X)
    w = np.zeros((n, 1))
    for i in range(m):
        w += np.multiply(alphas[i] * labelMat[i], X[i, :].T)
    return w

7.4 SVM核函数

是什么? 将低维数据映射到高维空间,解决非线性分类问题。

核函数 公式 适用场景
线性核 K(x,y)=xTyK(x,y) = x^TyK(x,y)=xTy 线性可分,特征多
多项式核 K(x,y)=(γxTy+r)dK(x,y) = (\gamma x^Ty + r)^dK(x,y)=(γxTy+r)d 多项式特征关系
RBF(高斯)核 $K(x,y) = \exp(-\gamma
Sigmoid核 K(x,y)=tanh⁡(γxTy+r)K(x,y) = \tanh(\gamma x^Ty + r)K(x,y)=tanh(γxTy+r) 神经网络类似

第8章 AdaBoost

核心思想:通过组合多个弱分类器,构建一个强分类器,"三个臭皮匠顶个诸葛亮"。

8.1 AdaBoost原理

是什么? 一种迭代式的集成学习算法,每次训练关注前一轮分类错误的样本。

为什么需要? 单一弱分类器效果差,需要组合多个分类器提升性能。

核心思想:

  1. 初始化样本权重(均匀分布)
  2. 训练弱分类器,计算错误率
  3. 计算分类器权重(发言权)
  4. 更新样本权重(错分样本权重增加)
  5. 重复直到收敛

分类器权重公式
αt=12ln⁡1−ϵtϵt\alpha_t = \frac{1}{2}\ln\frac{1-\epsilon_t}{\epsilon_t}αt=21lnϵt1−ϵt

其中 ϵt\epsilon_tϵt 是第t轮的错误率

8.2 AdaBoost算法代码

python 复制代码
import numpy as np

def stump_classify(data_mat, dimen, thresh_val, thresh_ineq):
    """
    单层决策树分类函数
    """
    ret_array = np.ones((np.shape(data_mat)[0], 1))
    if thresh_ineq == 'lt':
        ret_array[data_mat[:, dimen] <= thresh_val] = -1.0
    else:
        ret_array[data_mat[:, dimen] > thresh_val] = -1.0
    return ret_array

def build_stump(data_arr, class_labels, D):
    """
    构建单层决策树
    """
    data_mat = np.mat(data_arr)
    label_mat = np.mat(class_labels).T
    m, n = np.shape(data_mat)
    num_steps = 10.0
    best_stump = {}
    best_class_est = np.mat(np.zeros((m, 1)))
    min_err = np.inf
    
    for i in range(n):
        range_min = data_mat[:, i].min()
        range_max = data_mat[:, i].max()
        step_size = (range_max - range_min) / num_steps
        
        for j in range(-1, int(num_steps) + 1):
            for inequal in ['lt', 'gt']:
                thresh_val = range_min + float(j) * step_size
                predicted_vals = stump_classify(data_mat, i, thresh_val, inequal)
                err_arr = np.mat(np.ones((m, 1)))
                err_arr[predicted_vals == label_mat] = 0
                weighted_err = D.T * err_arr
                
                if weighted_err < min_err:
                    min_err = weighted_err
                    best_class_est = predicted_vals.copy()
                    best_stump['dim'] = i
                    best_stump['thresh'] = thresh_val
                    best_stump['ineq'] = inequal
    
    return best_stump, min_err, best_class_est

def ada_boost_train_ds(data_arr, class_labels, num_it=40):
    """
    AdaBoost训练
    """
    weak_class_arr = []
    m = np.shape(data_arr)[0]
    D = np.mat(np.ones((m, 1)) / m)  # 初始化权重
    agg_class_est = np.mat(np.zeros((m, 1)))
    
    for i in range(num_it):
        best_stump, error, class_est = build_stump(data_arr, class_labels, D)
        
        # 计算分类器权重
        alpha = float(0.5 * np.log((1.0 - error) / max(error, 1e-16)))
        best_stump['alpha'] = alpha
        weak_class_arr.append(best_stump)
        
        # 更新样本权重
        expon = np.multiply(-1 * alpha * np.mat(class_labels).T, class_est)
        D = np.multiply(D, np.exp(expon))
        D = D / D.sum()
        
        # 累计分类结果
        agg_class_est += alpha * class_est
        
        # 计算错误率
        agg_errors = np.multiply(np.sign(agg_class_est) != 
                                 np.mat(class_labels).T, np.ones((m, 1)))
        error_rate = agg_errors.sum() / m
        
        if error_rate == 0.0:
            break
    
    return weak_class_arr, agg_class_est

def ada_classify(data_to_class, classifier_arr):
    """
    AdaBoost分类
    """
    data_mat = np.mat(data_to_class)
    m = np.shape(data_mat)[0]
    agg_class_est = np.mat(np.zeros((m, 1)))
    
    for i in range(len(classifier_arr)):
        class_est = stump_classify(
            data_mat, classifier_arr[i]['dim'],
            classifier_arr[i]['thresh'],
            classifier_arr[i]['ineq']
        )
        agg_class_est += classifier_arr[i]['alpha'] * class_est
    
    return np.sign(agg_class_est)

第9章 线性回归

核心思想:通过拟合最优直线(或超平面),建立自变量与因变量之间的线性关系。

9.1 最小二乘法

是什么? 通过最小化预测值与真实值之间误差平方和来求解最优参数的方法。

最小二乘目标函数
J(β)=∑i=1m(yi−xiTβ)2J(\beta) = \sum_{i=1}^{m}(y_i - x_i^T\beta)^2J(β)=i=1∑m(yi−xiTβ)2
最优解公式
β=(XTX)−1XTy\beta = (X^TX)^{-1}X^Tyβ=(XTX)−1XTy

python 复制代码
import numpy as np

def standRegres(xArr, yArr):
    """
    标准线性回归(最小二乘法)
    """
    xMat = np.mat(xArr)
    yMat = np.mat(yArr).T
    xTx = xMat.T * xMat
    
    if np.linalg.det(xTx) == 0.0:
        print("矩阵奇异,无法求逆")
        return
    
    ws = xTx.I * (xMat.T * yMat)
    return ws

9.2 局部加权线性回归(LWLR)

是什么? 给预测点附近的样本赋予更高权重,实现局部拟合。

高斯核权重公式
w(i,i)=exp⁡((x(i)−x)2−2k2)w(i, i) = \exp\left(\frac{(x^{(i)} - x)^2}{-2k^2}\right)w(i,i)=exp(−2k2(x(i)−x)2)

python 复制代码
def lwlr(testPoint, xArr, yArr, k=1.0):
    """
    局部加权线性回归
    """
    xMat = np.mat(xArr)
    yMat = np.mat(yArr).T
    m = np.shape(xMat)[0]
    weights = np.mat(np.eye(m))
    
    for j in range(m):
        diffMat = testPoint - xMat[j, :]
        weights[j, j] = np.exp(diffMat * diffMat.T / (-2.0 * k ** 2))
    
    xTx = xMat.T * (weights * xMat)
    if np.linalg.det(xTx) == 0.0:
        return
    
    ws = xTx.I * (xMat.T * (weights * yMat))
    return testPoint * ws

def lwlrTest(testArr, xArr, yArr, k=1.0):
    """测试LWLR"""
    m = np.shape(testArr)[0]
    yHat = np.zeros(m)
    for i in range(m):
        yHat[i] = lwlr(testArr[i], xArr, yArr, k)
    return yHat

9.3 岭回归与套索回归

是什么? 在标准线性回归基础上加入正则化项,防止过拟合和解决多重共线性问题。

方法 正则化项 特点
岭回归(Ridge) L2:λ∑βj2L2: \lambda\sum\beta_j^2L2:λ∑βj2 系数趋近于0但不等于0
套索回归(Lasso) $L1: \lambda\sum \beta_j

岭回归目标函数
J(β)=∑i=1m(yi−xiTβ)2+λ∑j=1nβj2J(\beta) = \sum_{i=1}^{m}(y_i - x_i^T\beta)^2 + \lambda\sum_{j=1}^{n}\beta_j^2J(β)=i=1∑m(yi−xiTβ)2+λj=1∑nβj2

python 复制代码
def ridgeRegres(xMat, yMat, lam=0.2):
    """
    岭回归
    """
    xTx = xMat.T * xMat
    denom = xTx + np.eye(np.shape(xMat)[1]) * lam
    
    if np.linalg.det(denom) == 0.0:
        print("矩阵奇异")
        return
    
    ws = denom.I * (xMat.T * yMat)
    return ws

第10章 K-Means聚类

核心思想:通过迭代将数据划分为K个簇,使簇内相似度高,簇间相似度低。

10.1 K-Means算法原理

是什么? 一种基于距离的无监督聚类算法。

算法步骤:

  1. 随机选择K个初始质心
  2. 将每个样本分配到最近的质心,形成K个簇
  3. 重新计算每个簇的质心
  4. 重复2-3直到质心不再变化或达到最大迭代次数

为什么需要? 无标签数据需要自动发现内在结构。

局限?

  • K值需要预先指定
  • 对初始质心敏感
  • 对异常值敏感
  • 只能发现球形簇

10.2 K-Means代码实现

python 复制代码
import numpy as np

def distEclud(vecA, vecB):
    """欧氏距离"""
    return np.sqrt(np.sum(np.power(vecA - vecB, 2)))

def randCent(dataMat, k):
    """随机生成k个质心"""
    m, n = np.shape(dataMat)
    centroids = np.mat(np.zeros((k, n)))
    
    for j in range(n):
        minJ = np.min(dataMat[:, j])
        rangeJ = float(np.max(dataMat[:, j]) - minJ)
        centroids[:, j] = minJ + rangeJ * np.random.rand(k, 1)
    
    return centroids

def kMeans(dataMat, k, distMeas=distEclud, createCent=randCent):
    """
    K-Means聚类算法
    """
    m, n = np.shape(dataMat)
    clusterAssment = np.mat(np.zeros((m, 2)))  # 存储分配结果和误差
    centroids = createCent(dataMat, k)
    clusterChanged = True
    
    while clusterChanged:
        clusterChanged = False
        
        # 分配样本到最近的质心
        for i in range(m):
            minDist = np.inf
            minIndex = -1
            
            for j in range(k):
                distJI = distMeas(centroids[j, :], dataMat[i, :])
                if distJI < minDist:
                    minDist = distJI
                    minIndex = j
            
            if clusterAssment[i, 0] != minIndex:
                clusterChanged = True
            clusterAssment[i, :] = minIndex, minDist ** 2
        
        # 更新质心
        for cent in range(k):
            ptsInClust = dataMat[np.nonzero(clusterAssment[:, 0].A == cent)[0]]
            centroids[cent, :] = np.mean(ptsInClust, axis=0)
    
    return centroids, clusterAssment

第11章 PCA降维

核心思想:通过线性变换将高维数据投影到低维空间,保留最大方差的信息。

11.1 PCA原理

是什么? 主成分分析,一种无监督的降维技术。

为什么需要?

  • 减少数据维度,降低计算复杂度
  • 去除噪声和冗余特征
  • 便于数据可视化

核心思想:

  • 找到数据方差最大的方向(主成分)
  • 将数据投影到这些方向上

PCA步骤

  1. 数据中心化(减去均值)
  2. 计算协方差矩阵
  3. 对协方差矩阵进行特征值分解
  4. 选择前k个最大特征值对应的特征向量
  5. 将数据投影到选定的特征向量上

11.2 PCA代码实现

python 复制代码
import numpy as np

def pca(dataMat, topNfeat=9999999):
    """
    PCA降维
    :param dataMat: 数据矩阵
    :param topNfeat: 保留的特征数
    :return: 降维后的数据和重构数据
    """
    # 数据中心化
    meanVals = np.mean(dataMat, axis=0)
    meanRemoved = dataMat - meanVals
    
    # 计算协方差矩阵
    covMat = np.cov(meanRemoved, rowvar=0)
    
    # 特征值分解
    eigVals, eigVects = np.linalg.eig(np.mat(covMat))
    
    # 排序并选择前topNfeat个特征向量
    eigValInd = np.argsort(eigVals)
    eigValInd = eigValInd[:-(topNfeat+1):-1]
    redEigVects = eigVects[:, eigValInd]
    
    # 降维
    lowDDataMat = meanRemoved * redEigVects
    
    # 重构(用于调试)
    reconMat = (lowDDataMat * redEigVects.T) + meanVals
    
    return lowDDataMat, reconMat

11.3 PCA图像压缩代码

python 复制代码
from PIL import Image
import numpy as np

def loadImage(path):
    """加载图像"""
    img = Image.open(path)
    img = img.convert("L")
    width, height = img.size
    data = np.array(img.getdata()).reshape(height, width) / 100
    return data

def pca_image(data, k):
    """
    PCA图像压缩
    """
    n_samples, n_features = data.shape
    
    # 数据中心化
    mean = np.array([np.mean(data[:, i]) for i in range(n_features)])
    normal_data = data - mean
    
    # 计算协方差矩阵
    matrix_ = np.dot(np.transpose(normal_data), normal_data)
    
    # 特征值分解
    eig_val, eig_vec = np.linalg.eig(matrix_)
    
    # 选择前k个特征向量
    eigIndex = np.argsort(eig_val)
    eigVecIndex = eigIndex[:-(k+1):-1]
    feature = eig_vec[:, eigVecIndex]
    
    # 降维和重构
    new_data = np.dot(normal_data, feature)
    rec_data = np.dot(new_data, np.transpose(feature)) + mean
    
    return rec_data

def error(data, recdata):
    """计算重构误差"""
    sum1 = 0
    sum2 = 0
    D_value = data - recdata
    
    for i in range(data.shape[0]):
        sum1 += np.dot(data[i], data[i])
        sum2 += np.dot(D_value[i], D_value[i])
    
    error_rate = sum2 / sum1
    return error_rate

第12章 卷积神经网络(CNN)

核心思想:通过卷积操作提取图像局部特征,利用层次化结构学习从低级到高级的特征表示。

12.1 CNN核心组件

组件 作用 特点
卷积层 提取局部特征 权重共享,局部连接
激活层(ReLU) 引入非线性 计算简单,缓解梯度消失
池化层 降维,提取主要特征 最大池化/平均池化
全连接层 综合特征进行分类 传统神经网络结构

12.2 卷积操作类型

类型 输出大小 特点
Valid卷积 较小 不使用填充,只计算完全重叠部分
Same卷积 与输入相同 使用填充保持尺寸
Full卷积 较大 允许部分重叠,输出最大

12.3 Keras实现CNN代码

python 复制代码
from keras.datasets import cifar10
from keras.utils import np_utils
from keras.models import Sequential
from keras.layers.core import Dense, Dropout, Activation, Flatten
from keras.layers.convolutional import Conv2D, MaxPooling2D
from keras.optimizers import SGD, Adam, RMSprop

# 加载数据
(X_train, y_train), (X_test, y_test) = cifar10.load_data()

# 数据预处理
Y_train = np_utils.to_categorical(y_train, 10)
Y_test = np_utils.to_categorical(y_test, 10)
X_train = X_train.astype('float32') / 255
X_test = X_test.astype('float32') / 255

# 构建CNN模型
model = Sequential()

# 第一层卷积
model.add(Conv2D(32, (3, 3), padding='same', input_shape=(32, 32, 3)))
model.add(Activation('relu'))
model.add(Conv2D(32, (3, 3), padding='same'))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

# 第二层卷积
model.add(Conv2D(64, (3, 3), padding='same'))
model.add(Activation('relu'))
model.add(Conv2D(64, (3, 3)))
model.add(Activation('relu'))
model.add(MaxPooling2D(pool_size=(2, 2)))
model.add(Dropout(0.25))

# 全连接层
model.add(Flatten())
model.add(Dense(512))
model.add(Activation('relu'))
model.add(Dropout(0.5))
model.add(Dense(10))
model.add(Activation('softmax'))

# 编译模型
model.compile(loss='categorical_crossentropy',
              optimizer=RMSprop(),
              metrics=['accuracy'])

# 训练模型
history = model.fit(X_train, Y_train,
                    batch_size=128,
                    epochs=20,
                    validation_split=0.2,
                    verbose=1)

# 评估模型
score = model.evaluate(X_test, Y_test, batch_size=128, verbose=1)
print("测试准确率:", score[1])

第13章 验证码识别实战

核心思想:通过图像处理(灰度化、二值化、分割)和机器学习算法实现验证码自动识别。

13.1 图像处理基础代码

python 复制代码
from PIL import Image
import numpy as np

# 图像灰度化
def grayscale(image_path):
    img = Image.open(image_path)
    gray_img = img.convert('L')
    return gray_img

# 图像二值化
def binarize(image_path, threshold=128):
    img = Image.open(image_path).convert('L')
    pix = np.array(img)
    binary = (pix > threshold) * 255
    return Image.fromarray(binary.astype(np.uint8))

# 图像分割(按区域)
def split_image(image_path):
    im = Image.open(image_path)
    pix = np.array(im)
    
    # 假设验证码有4个字符,等宽分割
    width = pix.shape[1]
    char_width = width // 4
    
    chars = []
    for i in range(4):
        char = pix[:, i*char_width:(i+1)*char_width]
        chars.append(Image.fromarray(char))
    
    return chars

13.2 KNN验证码识别代码

python 复制代码
from sklearn import neighbors
from PIL import Image
import numpy as np
import os

# 加载训练数据
def load_training_data(train_dir):
    X = []
    y = []
    
    for label in os.listdir(train_dir):
        label_path = os.path.join(train_dir, label)
        for file in os.listdir(label_path):
            img_path = os.path.join(label_path, file)
            img = Image.open(img_path).convert('L')
            pix = np.array(img)
            pix = (pix > 150) * 1  # 二值化
            pix = pix.ravel()  # 拉平
            X.append(list(pix))
            y.append(int(label))
    
    return np.array(X), np.array(y)

# 训练KNN分类器
def train_knn(X_train, y_train, k=32):
    model = neighbors.KNeighborsClassifier(n_neighbors=k)
    model.fit(X_train, y_train)
    return model

# 预测验证码
def predict_captcha(model, image_path):
    img = Image.open(image_path).convert('L')
    pix = np.array(img)
    pix = (pix > 150) * 1
    pix = pix.ravel()
    prediction = model.predict([pix])
    return prediction[0]

第14章 答题卡识别实战

核心思想:利用OpenCV进行图像处理(边缘检测、直线检测、形态学操作),定位答题区域并识别填涂答案。

14.1 OpenCV基础操作代码

python 复制代码
import cv2
import numpy as np

# 读取图像
img = cv2.imread('card.png')

# 灰度化
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 二值化
_, binary = cv2.threshold(gray, 100, 255, cv2.THRESH_BINARY_INV)

# 边缘检测
edges = cv2.Canny(gray, 50, 200)

# 膨胀操作
kernel = np.ones((5, 5), np.uint8)
dilated = cv2.dilate(binary, kernel, iterations=1)

# 腐蚀操作
eroded = cv2.erode(binary, kernel, iterations=1)

# 查找轮廓
contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, 
                                cv2.CHAIN_APPROX_SIMPLE)

# 绘制轮廓
cv2.drawContours(img, contours, -1, (0, 255, 0), 2)

# 霍夫直线检测
lines = cv2.HoughLinesP(edges, 1, np.pi/180, 100)
if lines is not None:
    for line in lines:
        x1, y1, x2, y2 = line[0]
        cv2.line(img, (x1, y1), (x2, y2), (255, 0, 0), 2)

14.2 答题卡识别完整代码

python 复制代码
import cv2
import numpy as np
from imutils import contours

def recognize_answer_card(image_path):
    """答题卡识别主函数"""
    # 读取并调整图像大小
    card = cv2.imread(image_path)
    dst = cv2.resize(card, (420, 600))
    
    # 截取答题区域(根据实际调整坐标)
    dst1 = dst[203:555, 0:390]
    
    # 灰度化和二值化
    gray = cv2.cvtColor(dst1, cv2.COLOR_BGR2GRAY)
    _, thresh = cv2.threshold(gray, 140, 255, cv2.THRESH_BINARY_INV)
    
    # 形态学操作(开运算)
    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (4, 4))
    erode = cv2.erode(thresh, kernel)
    dilated = cv2.dilate(erode, kernel)
    
    # 查找轮廓
    cts, _ = cv2.findContours(dilated, cv2.RETR_EXTERNAL, 
                              cv2.CHAIN_APPROX_SIMPLE)
    
    # 排序轮廓(从上到下)
    cts_sorted = contours.sort_contours(cts, method="top-to-bottom")[0]
    
    # 识别答案逻辑(根据实际答题卡布局调整)
    answers = []
    for ct in cts_sorted:
        x, y, w, h = cv2.boundingRect(ct)
        # 根据位置判断题号和选项
        # ... 具体逻辑根据答题卡布局实现
        answers.append((x, y, w, h))
    
    return answers

附录:算法对比总结

监督学习算法对比

算法 类型 优点 缺点 适用场景
KNN 分类/回归 简单直观,无需训练 计算量大,内存需求高 小数据集,基础分类
决策树 分类/回归 可解释性强,处理非线性 易过拟合,不稳定 特征选择,规则提取
朴素贝叶斯 分类 训练快,适合大规模数据 特征独立假设 文本分类,情感分析
逻辑回归 分类 输出概率,可解释 只能处理线性可分 二分类,概率预测
SVM 分类/回归 泛化能力强,适合高维 大规模数据慢,调参难 文本分类,图像识别
AdaBoost 集成 准确率高,不易过拟合 对异常值敏感 复杂分类问题

无监督学习算法对比

算法 类型 优点 缺点 适用场景
K-Means 聚类 简单高效 需指定K,对初始值敏感 客户分群,图像分割
PCA 降维 无参数,计算高效 线性方法,可能丢失信息 特征压缩,可视化

重要公式汇总

欧氏距离
d(x,y)=∑i=1n(xi−yi)2d(x, y) = \sqrt{\sum_{i=1}^{n}(x_i - y_i)^2}d(x,y)=i=1∑n(xi−yi)2
信息熵
Ent(D)=−∑k=1∣y∣pklog⁡2pkEnt(D) = -\sum_{k=1}^{|y|}p_k \log_2 p_kEnt(D)=−k=1∑∣y∣pklog2pk
贝叶斯公式
P(c∣x)=P(x∣c)P(c)P(x)P(c|x) = \frac{P(x|c)P(c)}{P(x)}P(c∣x)=P(x)P(x∣c)P(c)
Sigmoid函数
σ(z)=11+e−z\sigma(z) = \frac{1}{1 + e^{-z}}σ(z)=1+e−z1
梯度下降更新
w:=w−α∇J(w)w := w - \alpha \nabla J(w)w:=w−α∇J(w)
最小二乘解
β=(XTX)−1XTy\beta = (X^TX)^{-1}X^Tyβ=(XTX)−1XTy


笔记整理完成,涵盖文档所有核心知识点和代码实现。

相关推荐
IT19952 小时前
Java文档阅读笔记-AI LangChain4j - Single User Chat Memory with AI Services
笔记
唐璜Taro2 小时前
Function Calling介绍
python
xin_yao_xin2 小时前
PDF 转 图片(python)
python·pdf
每天都要加加油王得坤2 小时前
langchain学习笔记
笔记·学习·langchain
石牌桥网管2 小时前
正则表达式:匹配不包含指定字符串的文本
java·javascript·python·正则表达式·go·php
前端摸鱼匠2 小时前
YOLOv8使用 Ultralytics 内置功能简化格式转换:介绍如何使用 yolo mode=data 等相关功能或辅助工具来加速和简化数据格式的准备工作
人工智能·yolo·目标检测·机器学习·目标跟踪·视觉检测
AC赳赳老秦2 小时前
DeepSeek助力云原生AI降本:容器化部署资源优化与算力利用率提升技巧
网络·python·django·prompt·tornado·ai-native·deepseek
卓越软件开发2 小时前
毕设全栈开发一条龙:Java/SpringBoot/Vue/ 小程序 / Python / 安卓 / AI 图像识别 人脸检测 车牌识别 YOLO
开发语言·spring boot·python·yolo·小程序·毕业设计·课程设计
甲枫叶2 小时前
【claude+weelinking产品经理系列15】UI/UX 打磨——产品经理的审美终于能自己实现
java·人工智能·python·ui·产品经理·ai编程·ux