前言
提醒:
文章内容为方便作者自己后日复习与查阅而进行的书写与发布,其中引用内容都会使用链接表明出处(如有侵权问题,请及时联系)。
其中内容多为一次书写,缺少检查与订正,如有问题或其他拓展及意见建议,欢迎评论区讨论交流。
分类与回归算法
机器学习中的分类与回归算法是两种主要的监督学习任务,它们分别用于解决不同类型的问题。以下是这两种算法的总结:
分类算法:
分类算法用于将数据分成不同的类别,适用于输出为离散标签的问题。常见的分类算法包括:
- 逻辑回归:使用逻辑函数来估计概率,用于二分类问题,也可以扩展到多分类问题。
- 支持向量机(SVM):通过找到最优的决策边界来最大化样本的分类准确率,适用于高维数据。
- 决策树:通过树结构来进行决策,每个节点代表一个特征的选择,叶子节点代表分类结果。
- 随机森林:由多个决策树组成的集成学习方法,通过投票来决定最终分类结果。
- 梯度提升决策树(GBDT):通过构建和结合多个弱学习器来形成强学习器,适用于分类和回归问题。
- 朴素贝叶斯:基于贝叶斯定理,假设特征之间相互独立,适用于文本分类等场景。
- K近邻(KNN):根据样本之间的距离进行分类,适用于小规模数据集。
- 神经网络:通过多层感知机学习数据的复杂模式,适用于图像、语音等复杂分类问题。
回归算法:
回归算法用于预测连续数值输出,适用于输出为连续变量的问题。常见的回归算法包括:
- 线性回归:通过拟合一条直线来预测目标变量的值,是最简单的回归方法。
- 岭回归:线性回归的扩展,通过引入L2正则化项来防止过拟合。
- Lasso回归:线性回归的另一种扩展,通过引入L1正则化项来进行特征选择。
- 弹性网回归:结合了岭回归和Lasso回归,同时引入L1和L2正则化项。
- 决策树回归:使用决策树结构来进行回归预测,适用于非线性关系。
- 随机森林回归:由多个决策树组成的集成学习方法,通过平均来决定最终回归结果。
- 梯度提升决策树回归(GBDT回归):通过构建和结合多个弱学习器来形成强学习器,适用于回归问题。
- 支持向量回归(SVR):支持向量机在回归问题上的应用,通过找到最优的决策边界来最大化样本的回归准确率。
- 神经网络回归:通过多层感知机学习数据的复杂模式,适用于复杂的回归问题。
分类与回归算法的比较:
- 输出类型:分类算法输出离散标签,回归算法输出连续数值。
- 评估指标:分类算法常用准确率、召回率、F1分数等指标,回归算法常用均方误差(MSE)、均方根误差(RMSE)等指标。
- 问题类型:分类算法适用于类别预测问题,如垃圾邮件检测;回归算法适用于数值预测问题,如房价预测。 在实际应用中,选择分类还是回归算法取决于问题的性质和需求。有时,可以将回归问题转化为分类问题,或者将分类问题转化为回归问题,具体取决于问题的特点和目标。
决策树回归算法与分类算法
决策树分类算法可参见: python_决策树(DecisionTree)分类算法
决策树回归算法与分类算法在核心机制和应用场景上有以下关键区别:
- 任务类型与输出形式
- 分类算法:用于离散类别预测(如判断邮件是否为垃圾邮件),输出为类别标签或概率分布。
- 回归算法:用于连续数值预测(如预测房价或温度),输出为具体的数值。
- 节点分裂标准
-
分类树:
通过最小化不纯度来分裂节点,常用指标包括:- 基尼不纯度(Gini Impurity):衡量数据集的混乱程度。
- 信息增益(Information Gain):基于熵的减少,选择信息增益最大的特征。
-
回归树:
通过最小化预测误差来分裂节点,常用指标包括:- 均方误差(MSE):使分裂后的子节点数据方差最小化。
- 平均绝对误差(MAE):鲁棒性更强,对异常值不敏感。
- 叶子节点的输出
- 分类树:
叶子节点代表类别标签或各类别的概率分布(如多数投票法确定最终类别)。 - 回归树:
叶子节点输出该区域样本的目标变量均值或中位数(如预测值为该叶子节点所有样本的平均值)。
- 预测方式
- 分类树:从根节点到叶子节点的路径由特征判断决定,最终输出离散类别。
- 回归树:同样通过特征判断路径,最终输出连续值(如叶子节点样本的均值)。
- 算法实现细节
- 特征处理:
- 分类树可能对连续特征分箱(离散化),而回归树直接处理连续值。
- 剪枝策略:
- 分类树可能基于验证集的分类准确率剪枝,回归树则基于均方误差等回归指标。
- 评估指标:
- 分类常用准确率、F1分数、AUC等;回归常用均方误差(MSE)、R²等。
示例对比
特征 | 分类树 | 回归树 |
---|---|---|
输出 | 类别标签(如“是/否”) | 连续值(如价格、温度) |
分裂标准 | 基尼不纯度、信息增益 | 均方误差、平均绝对误差 |
叶子节点内容 | 多数类标签或概率分布 | 目标变量的均值或中位数 |
评估指标 | 准确率、召回率、AUC | MSE、RMSE、R² |
- 总结
决策树分类与回归的核心差异源于任务目标的不同:分类关注离散类别的区分,而回归关注连续值的拟合。这种差异直接影响了分裂策略、叶子节点输出及评估方法,但两者的树形结构和递归分裂机制是相似的。
决策树回归算法
决策树回归算法是一种利用决策树模型进行数值预测的方法。其核心思想是通过递归地划分特征空间,将数据分割成更小的区域,最终在每个区域内进行数值预测。
基本概念:
决策树回归的目标是找到特征与目标变量之间的关系,通过树结构中的节点测试特征,并根据测试结果将数据分割成不同的分支,直至到达叶节点。在叶节点处,通常使用该区域内训练数据的目标值的均值作为预测值。
数学表示:
假设数据集
D
=
{
(
x
1
,
y
1
)
,
(
x
2
,
y
2
)
,
…
,
(
x
n
,
y
n
)
}
D = \{(x_1, y_1), (x_2, y_2), \dots, (x_n, y_n)\}
D={(x1,y1),(x2,y2),…,(xn,yn)},其中
x
i
x_i
xi 是特征向量,
y
i
y_i
yi 是目标变量。
分裂标准:
在每个节点
t
t
t 处,对于特征
f
f
f 和阈值
θ
\theta
θ,将数据分为两个子集:
- 左子集 D L = { ( x i , y i ) ∣ x i , f ≤ θ } D_L = \{(x_i, y_i) \mid x_{i,f} \leq \theta\} DL={(xi,yi)∣xi,f≤θ}
- 右子集 D R = { ( x i , y i ) ∣ x i , f > θ } D_R = \{(x_i, y_i) \mid x_{i,f} > \theta\} DR={(xi,yi)∣xi,f>θ}
目标是找到
f
f
f 和
θ
\theta
θ,以最小化以下损失:
Loss
=
MSE
(
D
L
)
+
MSE
(
D
R
)
\text{Loss} = \text{MSE}(D_L) + \text{MSE}(D_R)
Loss=MSE(DL)+MSE(DR)
其中,
MSE
(
D
)
=
1
∣
D
∣
∑
(
x
,
y
)
∈
D
(
y
−
y
ˉ
)
2
\text{MSE}(D) = \frac{1}{|D|} \sum_{(x,y) \in D} (y - \bar{y})^2
MSE(D)=∣D∣1(x,y)∈D∑(y−yˉ)2
而
y
ˉ
\bar{y}
yˉ 是集合
D
D
D 中目标值的均值。
构建树:
树构建过程是递归的:
- 初始化: 从根节点开始,包含所有训练数据。
- 分裂: 对于每个节点,找到使损失最小的特征和阈值。
- 停止条件: 如果节点达到某种标准(如最小样本数、最大深度或损失阈值),则停止分裂。
- 叶节点预测: 在叶节点处,预测目标值的均值 y ˉ \bar{y} yˉ。
数学细节:
在节点
t
t
t 处,假设节点有
N
N
N 个样本。对于每个可能的分裂(特征
f
f
f 和阈值
θ
\theta
θ),计算:
Q
(
t
,
f
,
θ
)
=
N
L
N
⋅
MSE
(
D
L
)
+
N
R
N
⋅
MSE
(
D
R
)
Q(t, f, \theta) = \frac{N_L}{N} \cdot \text{MSE}(D_L) + \frac{N_R}{N} \cdot \text{MSE}(D_R)
Q(t,f,θ)=NNL⋅MSE(DL)+NNR⋅MSE(DR)
其中
N
L
N_L
NL 和
N
R
N_R
NR 分别是左子集和右子集的样本数。
目标是使
Q
Q
Q 最小:
f
∗
,
θ
∗
=
arg
min
f
,
θ
Q
(
t
,
f
,
θ
)
f^*, \theta^* = \arg\min_{f, \theta} Q(t, f, \theta)
f∗,θ∗=argf,θminQ(t,f,θ)
一旦找到
f
∗
f^*
f∗ 和
θ
∗
\theta^*
θ∗,节点
t
t
t 将分裂成两个子节点
t
L
t_L
tL 和
t
R
t_R
tR。
正则化和剪枝:
为了防止过拟合,通常使用剪枝技术。这可以通过设置树的最大深度、节点分裂所需的最小样本数或直接在成本函数中添加正则化项来实现。
一种常用的方法是成本复杂度剪枝,它在损失函数中添加了一个惩罚项:
Cost
=
∑
t
叶节点
N
t
⋅
MSE
(
D
t
)
+
α
⋅
叶子节点数
\text{Cost} = \sum_{t \text{ 叶节点}} N_t \cdot \text{MSE}(D_t) + \alpha \cdot \text{叶子节点数}
Cost=t 叶节点∑Nt⋅MSE(Dt)+α⋅叶子节点数
其中
α
\alpha
α 是控制树复杂度的正则化参数。
决策树回归的优势:
- 非线性关系: 决策树可以捕捉数据中的非线性关系。
- 特征选择: 它们可以处理高维数据,并且在构建过程中选择重要的特征。
- 数据预处理: 相对于其他算法,决策树需要的数据预处理较少。
总结:
决策树回归通过递归地根据特征分裂数据来工作,目标是使每个分裂后的子集的均方误差最小。通过正则化和剪枝技术,可以控制树的复杂度,从而提高模型的泛化能力。
算法实现
手动实现1
import numpy as np
def mse(y):
"""计算给定数据集的目标值的均方误差(MSE)。"""
return np.mean((y - np.mean(y)) ** 2)
def split_dataset(X, y, feature_idx, split_value):
"""根据选定的特征索引和分裂值分割数据集。"""
left_mask = X[:, feature_idx] <= split_value
right_mask = ~left_mask
return X[left_mask], y[left_mask], X[right_mask], y[right_mask]
def find_best_split(X, y):
"""寻找最佳分裂点,返回最佳分裂特征及其值。"""
best_mse = np.inf
best_feature_idx, best_split_value = None, None
for feature_idx in range(X.shape[1]):
unique_values = np.unique(X[:, feature_idx])
for split_value in unique_values:
X_left, y_left, X_right, y_right = split_dataset(X, y, feature_idx, split_value)
if len(y_left) == 0 or len(y_right) == 0:
continue
mse_left, mse_right = mse(y_left), mse(y_right)
weighted_mse = (len(y_left) * mse_left + len(y_right) * mse_right) / len(y)
if weighted_mse < best_mse:
best_mse = weighted_mse
best_feature_idx, best_split_value = feature_idx, split_value
return best_feature_idx, best_split_value
class DecisionTreeRegressor:
def __init__(self, max_depth=None):
self.max_depth = max_depth
def fit(self, X, y, depth=0):
if self.max_depth is None or depth < self.max_depth:
feature_idx, split_value = find_best_split(X, y)
if feature_idx is not None: # 如果找到了有效的分裂
X_left, y_left, X_right, y_right = split_dataset(X, y, feature_idx, split_value)
self.split = {'feature_idx': feature_idx, 'split_value': split_value,
'left': DecisionTreeRegressor(self.max_depth).fit(X_left, y_left, depth+1),
'right': DecisionTreeRegressor(self.max_depth).fit(X_right, y_right, depth+1)}
else:
self.split = {'prediction': np.mean(y)}
else:
self.split = {'prediction': np.mean(y)}
return self
def predict(self, X):
predictions = np.zeros(len(X))
for i, x in enumerate(X):
node = self.split
while 'feature_idx' in node:
if x[node['feature_idx']] <= node['split_value']:
node = node['left'].split
else:
node = node['right'].split
predictions[i] = node['prediction']
return predictions
# 示例使用
if __name__ == "__main__":
import matplotlib.pyplot as plt
# 创建示例数据集
np.random.seed(42)
X = np.sort(5 * np.random.rand(80, 1), axis=0)
y = np.sin(X).ravel() + np.random.normal(0, 0.1, X.shape[0])
# 训练决策树回归器
regressor = DecisionTreeRegressor(max_depth=3)
regressor.fit(X, y)
# 预测
X_test = np.arange(0.0, 5.0, 0.01)[:, np.newaxis]
y_pred = regressor.predict(X_test)
# 绘制结果
plt.figure()
plt.scatter(X, y, s=20, edgecolor="black", c="darkorange", label="data")
plt.plot(X_test, y_pred, color="cornflowerblue", linewidth=2)
plt.xlabel("data")
plt.ylabel("target")
plt.title("Decision Tree Regression")
plt.legend()
plt.show()
运行结果:
手动实现2
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_squared_error
class DecisionTreeRegressorManual:
def __init__(self, max_depth=None, min_samples_split=2):
"""
初始化决策树回归模型
:param max_depth: 树的最大深度
:param min_samples_split: 节点分裂所需的最小样本数
"""
self.max_depth = max_depth
self.min_samples_split = min_samples_split
self.tree = None
def fit(self, X, y):
"""
训练决策树回归模型
:param X: 特征矩阵
:param y: 目标变量
"""
self.tree = self._build_tree(X, y)
def _build_tree(self, X, y, depth=0):
"""
递归构建决策树
:param X: 特征矩阵
:param y: 目标变量
:param depth: 当前树的深度
:return: 决策树节点
"""
n_samples, n_features = X.shape
# 停止条件
if depth == self.max_depth or n_samples < self.min_samples_split:
return {"value": np.mean(y)}
# 找到最佳分裂特征和阈值
best_feature, best_threshold = self._find_best_split(X, y)
# 分裂数据集
X_left, y_left, X_right, y_right = self._split_data(X, y, best_feature, best_threshold)
# 递归构建子树
node = {
"feature": best_feature,
"threshold": best_threshold,
"left": self._build_tree(X_left, y_left, depth + 1),
"right": self._build_tree(X_right, y_right, depth + 1)
}
return node
def _find_best_split(self, X, y):
"""
找到最佳分裂特征和阈值
:param X: 特征矩阵
:param y: 目标变量
:return: 最佳特征索引和阈值
"""
best_mse = float("inf")
best_feature = None
best_threshold = None
for feature_idx in range(X.shape[1]):
thresholds = np.unique(X[:, feature_idx])
for threshold in thresholds:
X_left, y_left, X_right, y_right = self._split_data(X, y, feature_idx, threshold)
if len(y_left) < self.min_samples_split or len(y_right) < self.min_samples_split:
continue
mse = self._calculate_mse(y_left, y_right)
if mse < best_mse:
best_mse = mse
best_feature = feature_idx
best_threshold = threshold
return best_feature, best_threshold
def _split_data(self, X, y, feature_idx, threshold):
"""
根据特征和阈值分裂数据集
:param X: 特征矩阵
:param y: 目标变量
:param feature_idx: 特征索引
:param threshold: 阈值
:return: 左子集和右子集
"""
X_left = X[X[:, feature_idx] <= threshold]
y_left = y[X[:, feature_idx] <= threshold]
X_right = X[X[:, feature_idx] > threshold]
y_right = y[X[:, feature_idx] > threshold]
return X_left, y_left, X_right, y_right
def _calculate_mse(self, y_left, y_right):
"""
计算均方误差
:param y_left: 左子集目标值
:param y_right: 右子集目标值
:return: 均方误差
"""
mse_left = np.mean((y_left - np.mean(y_left)) ** 2)
mse_right = np.mean((y_right - np.mean(y_right)) ** 2)
return mse_left + mse_right
def predict(self, X):
"""
预测目标变量
:param X: 特征矩阵
:return: 预测值
"""
return np.array([self._predict_single(x) for x in X])
def _predict_single(self, x):
"""
预测单个样本
:param x: 单个样本特征
:return: 预测值
"""
node = self.tree
while "feature" in node:
if x[node["feature"]] <= node["threshold"]:
node = node["left"]
else:
node = node["right"]
return node["value"]
# 示例
if __name__ == "__main__":
# 生成示例数据集
np.random.seed(0)
X = np.random.rand(100, 1) * 10 # 生成100个0到10之间的随机数作为特征
y = 2 * X.squeeze() + np.random.randn(100) # 生成对应的目标值,y = 2 * x + 噪声
# 数据集拆分
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 训练决策树回归模型
regressor = DecisionTreeRegressorManual(max_depth=3)
regressor.fit(X_train, y_train)
# 使用训练好的模型进行预测
y_pred_train = regressor.predict(X_train)
y_pred_test = regressor.predict(X_test)
# 计算训练集和测试集的均方误差
mse_train = mean_squared_error(y_train, y_pred_train)
mse_test = mean_squared_error(y_test, y_pred_test)
print("训练集上的均方误差:", mse_train)
print("测试集上的均方误差:", mse_test)
# 可视化结果
plt.figure(figsize=(10, 6))
plt.scatter(X_train, y_train, color='blue', label='Training data')
plt.scatter(X_test, y_test, color='green', label='Testing data')
plt.plot(np.sort(X_train, axis=0), regressor.predict(np.sort(X_train, axis=0)), color='red', linewidth=2, label='Decision Tree Regression')
plt.title('Decision Tree Regression')
plt.xlabel('X')
plt.ylabel('y')
plt.legend()
plt.show()
运行结果:
python函数库实现
import numpy as np
import matplotlib.pyplot as plt
from sklearn.tree import DecisionTreeRegressor
from sklearn.model_selection import train_test_split
# 生成示例数据
np.random.seed(0)
X = np.sort(np.random.rand(100, 1) * 10, axis=0) # 100个样本,1个特征
y = np.sin(X).ravel() + np.random.randn(100) * 0.1 # 添加一些噪声
# 拆分数据集为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)
# 初始化决策树回归模型
tree_regressor = DecisionTreeRegressor(max_depth=5) # 设置最大深度
# 拟合模型
tree_regressor.fit(X_train, y_train)
# 预测
y_pred = tree_regressor.predict(X_test)
# 生成用于绘制回归曲线的细分数据
X_grid = np.arange(0, 10, 0.01).reshape(-1, 1) # 创建细分的特征值
y_grid = tree_regressor.predict(X_grid) # 预测对应的目标值
# 可视化结果
plt.figure(figsize=(10, 6)) # 设置图形尺寸
plt.scatter(X_test, y_test, color='blue', label='Actual Data', s=50, alpha=0.6) # 实际数据点
plt.plot(X_grid, y_grid, color='red', label='Decision Tree Regression Prediction', linewidth=2) # 决策树预测曲线
plt.title('Decision Tree Regression Fit', fontsize=16) # 图形标题
plt.xlabel('Feature X', fontsize=14) # X轴标签
plt.ylabel('Target y', fontsize=14) # Y轴标签
plt.legend(fontsize=12) # 图例
plt.grid() # 添加网格
plt.show()
# 打印模型的评分
print("模型的 R^2 分数:", tree_regressor.score(X_test, y_test))
运行结果: