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∣pklog2pkEnt(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优化目标 :
maxw,b1∣∣w∣∣\max_{w,b} \frac{1}{||w||}w,bmax∣∣w∣∣1s.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原理
是什么? 一种迭代式的集成学习算法,每次训练关注前一轮分类错误的样本。
为什么需要? 单一弱分类器效果差,需要组合多个分类器提升性能。
核心思想:
- 初始化样本权重(均匀分布)
- 训练弱分类器,计算错误率
- 计算分类器权重(发言权)
- 更新样本权重(错分样本权重增加)
- 重复直到收敛
分类器权重公式 :
αt=12ln1−ϵ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算法原理
是什么? 一种基于距离的无监督聚类算法。
算法步骤:
- 随机选择K个初始质心
- 将每个样本分配到最近的质心,形成K个簇
- 重新计算每个簇的质心
- 重复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步骤:
- 数据中心化(减去均值)
- 计算协方差矩阵
- 对协方差矩阵进行特征值分解
- 选择前k个最大特征值对应的特征向量
- 将数据投影到选定的特征向量上
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∣pklog2pkEnt(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
笔记整理完成,涵盖文档所有核心知识点和代码实现。