跳到主要内容常见机器学习算法原理:线性回归、决策树、SVM 与聚类 | 极客日志PythonAI算法
常见机器学习算法原理:线性回归、决策树、SVM 与聚类
本文介绍了四种常见的机器学习算法:线性回归、决策树、支持向量机(SVM)和聚类算法。详细阐述了各算法的基本原理、核心公式、Python 代码实现及应用场景。线性回归通过梯度下降优化参数;决策树基于信息熵或基尼系数构建;SVM 寻找最大间隔超平面;K-Means 等聚类算法用于无监督学习。文末提供算法选择指南,帮助根据数据特征选择合适的模型。
机器学习算法是人工智能领域的核心工具,不同的算法适用于不同的问题场景。本文将深入讲解几种最常用的机器学习算法,包括线性回归、决策树、支持向量机和聚类算法,帮助读者理解它们的原理、应用场景以及实现方式。
线性回归:最简单却最实用的算法
线性回归是机器学习中最基础也是最重要的算法之一。虽然它看起来很简单,但在实际应用中却非常有效,很多复杂的算法都是在线性回归的基础上发展而来的。
线性回归的基本原理
线性回归的核心思想是找到一条直线(或者高维空间中的超平面),使得这条线能够最好地拟合数据点。用数学语言来说,就是找到一个函数关系,使得预测值和真实值之间的误差最小。
对于最简单的一元线性回归,我们要找的是这样一个函数:
其中,x 是输入特征,y 是预测值,w 是权重(斜率),b 是偏置(截距)。我们的目标是找到最优的 w 和 b,使得所有样本点到这条直线的距离之和最小。
MSE = (1/n) * Σ(y_i - ŷ_i)²
其中,y_i 是真实值,ŷ_i 是预测值,n 是样本数量。
梯度下降法求解
要找到最优的参数,最常用的方法就是梯度下降法。梯度下降的思想很直观:想象你站在山顶,想要下到山谷,最快的方法就是沿着最陡的方向往下走。在数学上,这个最陡的方向就是梯度的反方向。
w = w - α * ∂L/∂w
b = b - α * ∂L/∂b
其中,α是学习率,控制每次更新的步长。学习率太大可能导致震荡,太小则收敛速度慢。
import numpy as np
import matplotlib.pyplot as plt
class LinearRegression:
def __init__(self, learning_rate=0.01, iterations=1000):
self.lr = learning_rate
self.iterations = iterations
self.w = None
self.b = None
self.losses = []
def fit(self, X, y):
n_samples, n_features = X.shape
self.w = np.zeros(n_features)
self.b = 0
for i in range(self.iterations):
y_pred = np.dot(X, self.w) + self.b
loss = np.mean((y - y_pred)**2)
self.losses.append(loss)
dw = -(2/n_samples) * np.dot(X.T, (y - y_pred))
db = -(2/n_samples) * np.sum(y - y_pred)
self.w -= self.lr * dw
self.b -= self.lr * db
if i % 100 == 0:
print(f"Iteration {i}: Loss = {loss:.4f}")
def predict(self, X):
return np.dot(X, self.w) + self.b
np.random.seed(42)
X = 2 * np.random.rand(100, 1)
y = 4 + 3 * X + np.random.randn(100, 1)
model = LinearRegression(learning_rate=0.1, iterations=1000)
model.fit(X, y.ravel())
X_test = np.array([[0], [2]])
predictions = model.predict(X_test)
print(f"预测结果:{predictions}")
plt.scatter(X, y, color='blue', label='真实数据')
plt.plot(X_test, predictions, color='red', linewidth=2, label='拟合直线')
plt.xlabel('X')
plt.ylabel('y')
plt.legend()
plt.title('线性回归拟合结果')
plt.show()
多元线性回归
在实际应用中,我们通常需要处理多个特征的情况。多元线性回归的形式是:
y = w₁x₁ + w₂x₂ + … + wₙxₙ + b
求解方法和一元线性回归类似,只是梯度计算变成了向量形式。上面的代码实现实际上已经支持多元线性回归了。
线性回归的应用场景
- 房价预测:根据面积、地段、楼层等特征预测房价
- 销售预测:根据广告投入、季节等因素预测销售额
- 温度预测:根据历史数据预测未来温度
- 股票趋势分析:分析股票价格的线性趋势
需要注意的是,线性回归假设特征和目标之间存在线性关系,如果关系是非线性的,需要进行特征工程或使用其他算法。
决策树:像人一样做决策
决策树是一种非常直观的算法,它的决策过程就像人类的思维方式一样,通过一系列的判断来得出结论。
决策树的基本原理
决策树通过树形结构来表示决策过程。每个内部节点表示一个特征的判断,每个分支表示判断的结果,每个叶子节点表示最终的决策结果。
晴天
阴天
雨天
高
正常
天气如何?
湿度如何?
去打球
不去打球
不去打球
去打球
构建决策树的关键问题是:如何选择最优的特征进行分裂?这就需要用到信息增益或基尼系数等指标。
信息熵与信息增益
信息熵是衡量数据不确定性的指标。熵越大,数据越混乱;熵越小,数据越纯净。
H(D) = -Σ p_i * log₂(p_i)
信息增益表示使用某个特征进行分裂后,信息熵的减少量:
Gain(D, A) = H(D) - Σ (|D_v|/|D|) * H(D_v)
其中,D_v 是特征 A 取值为 v 的样本子集。
我们选择信息增益最大的特征进行分裂,这样可以使得分裂后的子节点尽可能纯净。
基尼系数
除了信息增益,还可以使用基尼系数来选择分裂特征。基尼系数表示从数据集中随机抽取两个样本,其类别不一致的概率:
决策树的实现
import numpy as np
from collections import Counter
class DecisionTreeNode:
def __init__(self, feature=None, threshold=None, left=None, right=None, value=None):
self.feature = feature
self.threshold = threshold
self.left = left
self.right = right
self.value = value
class DecisionTree:
def __init__(self, max_depth=10, min_samples_split=2):
self.max_depth = max_depth
self.min_samples_split = min_samples_split
self.root = None
def _entropy(self, y):
"""计算信息熵"""
counter = Counter(y)
probs = np.array(list(counter.values())) / len(y)
return -np.sum(probs * np.log2(probs + 1e-10))
def _information_gain(self, X, y, feature, threshold):
"""计算信息增益"""
parent_entropy = self._entropy(y)
left_mask = X[:, feature] <= threshold
right_mask = ~left_mask
if np.sum(left_mask) == 0 or np.sum(right_mask) == 0:
return 0
n = len(y)
n_left, n_right = np.sum(left_mask), np.sum(right_mask)
e_left = self._entropy(y[left_mask])
e_right = self._entropy(y[right_mask])
child_entropy = (n_left / n) * e_left + (n_right / n) * e_right
return parent_entropy - child_entropy
def _best_split(self, X, y):
"""找到最优分裂点"""
best_gain = -1
best_feature = None
best_threshold = None
n_features = X.shape[1]
for feature in range(n_features):
thresholds = np.unique(X[:, feature])
for threshold in thresholds:
gain = self._information_gain(X, y, feature, threshold)
if gain > best_gain:
best_gain = gain
best_feature = feature
best_threshold = threshold
return best_feature, best_threshold
def _build_tree(self, X, y, depth=0):
"""递归构建决策树"""
n_samples, n_features = X.shape
n_classes = len(np.unique(y))
if depth >= self.max_depth or n_samples < self.min_samples_split or n_classes == 1:
leaf_value = Counter(y).most_common(1)[0][0]
return DecisionTreeNode(value=leaf_value)
best_feature, best_threshold = self._best_split(X, y)
if best_feature is None:
leaf_value = Counter(y).most_common(1)[0][0]
return DecisionTreeNode(value=leaf_value)
left_mask = X[:, best_feature] <= best_threshold
right_mask = ~left_mask
left_subtree = self._build_tree(X[left_mask], y[left_mask], depth + 1)
right_subtree = self._build_tree(X[right_mask], y[right_mask], depth + 1)
return DecisionTreeNode(best_feature, best_threshold, left_subtree, right_subtree)
def fit(self, X, y):
"""训练决策树"""
self.root = self._build_tree(X, y)
def _predict_sample(self, x, node):
"""预测单个样本"""
if node.value is not None:
return node.value
if x[node.feature] <= node.threshold:
return self._predict_sample(x, node.left)
else:
return self._predict_sample(x, node.right)
def predict(self, X):
"""预测多个样本"""
return np.array([self._predict_sample(x, self.root) for x in X])
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)
tree = DecisionTree(max_depth=5)
tree.fit(X_train, y_train)
y_pred = tree.predict(X_test)
accuracy = np.mean(y_pred == y_test)
print(f"准确率:{accuracy:.4f}")
决策树的优缺点
- 易于理解和解释,可以可视化
- 不需要数据标准化
- 可以处理数值型和类别型数据
- 可以处理多输出问题
- 容易过拟合,需要剪枝
- 对数据的微小变化敏感
- 可能创建偏向某些类别的树
- 训练时间较长
决策树的改进:随机森林
为了克服决策树容易过拟合的问题,可以使用集成学习方法,其中最著名的就是随机森林。随机森林通过构建多棵决策树,然后对它们的预测结果进行投票或平均,从而提高模型的泛化能力。
训练数据
决策树 1
决策树 2
决策树 3
...
决策树 N
投票/平均
最终预测
支持向量机:寻找最优分界线
支持向量机是一种非常强大的分类算法,它的核心思想是找到一个最优的超平面,使得不同类别的样本被正确分开,并且分类间隔最大。
SVM 的基本原理
对于线性可分的数据,SVM 要找的是这样一个超平面:
使得所有样本点到这个超平面的距离之和最大。这个距离称为间隔(margin)。
SVM 核心思想
找到最优超平面
最大化分类间隔
支持向量
距离超平面最近的点
决定超平面位置
最小化:(1/2) ||w||²
约束条件:y_i(w^T · x_i + b) ≥ 1
其中,y_i 是样本的类别标签(+1 或 -1),x_i 是样本特征。
软间隔与惩罚参数
在实际应用中,数据往往不是完全线性可分的,可能存在一些噪声点。为了处理这种情况,引入了软间隔的概念,允许一些样本点违反间隔约束。
最小化:(1/2) ||w||² + C * Σξ_i
约束条件:y_i(w^T · x_i + b) ≥ 1 - ξ_i,ξ_i ≥ 0
其中,C 是惩罚参数,控制对误分类的惩罚程度。C 越大,对误分类的惩罚越大,模型越复杂;C 越小,模型越简单,但可能欠拟合。
核函数:处理非线性问题
当数据不是线性可分时,可以通过核函数将数据映射到高维空间,使其在高维空间中线性可分。
- 线性核:K(x, y) = x^T · y
- 多项式核:K(x, y) = (x^T · y + c)^d
- 高斯核(RBF):K(x, y) = exp(-γ||x - y||²)
- Sigmoid 核:K(x, y) = tanh(αx^T · y + c)
其中,RBF 核是最常用的核函数,它可以将数据映射到无限维空间。
SVM 的实现
import numpy as np
from sklearn import svm
from sklearn.datasets import make_classification, make_circles
from sklearn.model_selection import train_test_split
import matplotlib.pyplot as plt
X_linear, y_linear = make_classification(n_samples=100, n_features=2, n_redundant=0, n_informative=2, n_clusters_per_class=1, random_state=42)
X_nonlinear, y_nonlinear = make_circles(n_samples=100, noise=0.1, factor=0.5, random_state=42)
def plot_svm_decision_boundary(X, y, model, title):
"""绘制 SVM 决策边界"""
plt.figure(figsize=(10, 6))
h = 0.02
x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1
y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1
xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h))
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
plt.contourf(xx, yy, Z, alpha=0.3, cmap=plt.cm.RdYlBu)
plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.RdYlBu, edgecolors='black')
plt.scatter(model.support_vectors_[:, 0], model.support_vectors_[:, 1], s=200, linewidth=1, facecolors='none', edgecolors='k', label='支持向量')
plt.xlabel('特征 1')
plt.ylabel('特征 2')
plt.title(title)
plt.legend()
plt.show()
print("训练线性 SVM...")
svm_linear = svm.SVC(kernel='linear', C=1.0)
svm_linear.fit(X_linear, y_linear)
print(f"线性 SVM 准确率:{svm_linear.score(X_linear, y_linear):.4f}")
plot_svm_decision_boundary(X_linear, y_linear, svm_linear, "线性 SVM")
print("\n训练 RBF 核 SVM...")
svm_rbf = svm.SVC(kernel='rbf', C=1.0, gamma='auto')
svm_rbf.fit(X_nonlinear, y_nonlinear)
print(f"RBF 核 SVM 准确率:{svm_rbf.score(X_nonlinear, y_nonlinear):.4f}")
plot_svm_decision_boundary(X_nonlinear, y_nonlinear, svm_rbf, "RBF 核 SVM")
print("\n比较不同 C 值的影响...")
for C in [0.1, 1, 10, 100]:
model = svm.SVC(kernel='linear', C=C)
model.fit(X_linear, y_linear)
print(f"C={C}: 准确率={model.score(X_linear, y_linear):.4f}, "
f"支持向量数量={len(model.support_vectors_)}")
SVM 的应用场景
- 文本分类:垃圾邮件检测、情感分析
- 图像识别:人脸识别、手写数字识别
- 生物信息学:蛋白质分类、基因分类
- 金融预测:信用评分、股票预测
SVM 的优点是在高维空间中表现良好,对于小样本数据也能取得不错的效果。但缺点是训练时间较长,对大规模数据不太适用。
聚类算法:发现数据的内在结构
聚类是无监督学习的典型代表,它的目标是将相似的样本归为一类,不同的样本分到不同的类。聚类不需要标签数据,完全依靠数据本身的特征进行分组。
K-Means 聚类
K-Means 是最常用的聚类算法,它的思想很简单:将数据分成 K 个簇,使得每个簇内的样本尽可能相似,不同簇之间的样本尽可能不同。
是
否
开始
随机初始化 K 个中心点
分配样本到最近的中心点
更新中心点位置
中心点是否变化?
结束
- 随机选择 K 个样本作为初始聚类中心
- 计算每个样本到各个聚类中心的距离,将样本分配给最近的聚类中心
- 重新计算每个簇的中心点(簇内所有样本的均值)
- 重复步骤 2 和 3,直到聚类中心不再变化或达到最大迭代次数
K-Means 的实现
import numpy as np
import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs
class KMeans:
def __init__(self, n_clusters=3, max_iters=100):
self.n_clusters = n_clusters
self.max_iters = max_iters
self.centroids = None
self.labels = None
def fit(self, X):
n_samples = X.shape[0]
random_indices = np.random.choice(n_samples, self.n_clusters, replace=False)
self.centroids = X[random_indices]
for i in range(self.max_iters):
distances = np.sqrt(((X - self.centroids[:, np.newaxis])**2).sum(axis=2))
self.labels = np.argmin(distances, axis=0)
new_centroids = np.array([X[self.labels == k].mean(axis=0) for k in range(self.n_clusters)])
if np.allclose(self.centroids, new_centroids):
print(f"在第{i+1}次迭代后收敛")
break
self.centroids = new_centroids
return self
def predict(self, X):
distances = np.sqrt(((X - self.centroids[:, np.newaxis])**2).sum(axis=2))
return np.argmin(distances, axis=0)
X, y_true = make_blobs(n_samples=300, centers=4, cluster_std=0.60, random_state=42)
kmeans = KMeans(n_clusters=4, max_iters=100)
kmeans.fit(X)
plt.figure(figsize=(12, 5))
plt.subplot(1, 2, 1)
plt.scatter(X[:, 0], X[:, 1], c=y_true, cmap='viridis', alpha=0.6)
plt.title('真实分布')
plt.xlabel('特征 1')
plt.ylabel('特征 2')
plt.subplot(1, 2, 2)
plt.scatter(X[:, 0], X[:, 1], c=kmeans.labels, cmap='viridis', alpha=0.6)
plt.scatter(kmeans.centroids[:, 0], kmeans.centroids[:, 1], c='red', marker='X', s=200, edgecolors='black', label='聚类中心')
plt.title('K-Means 聚类结果')
plt.xlabel('特征 1')
plt.ylabel('特征 2')
plt.legend()
plt.tight_layout()
plt.show()
如何选择 K 值
选择合适的 K 值是 K-Means 算法的关键问题。常用的方法有:
- 肘部法则:绘制不同 K 值对应的簇内误差平方和(SSE),选择曲线出现拐点的 K 值
def elbow_method(X, max_k=10):
"""肘部法则选择 K 值"""
sse = []
for k in range(1, max_k + 1):
kmeans = KMeans(n_clusters=k)
kmeans.fit(X)
sse_k = sum([np.sum((X[kmeans.labels == i] - kmeans.centroids[i])**2) for i in range(k)])
sse.append(sse_k)
plt.figure(figsize=(8, 5))
plt.plot(range(1, max_k + 1), sse, marker='o')
plt.xlabel('聚类数量 K')
plt.ylabel('簇内误差平方和 SSE')
plt.title('肘部法则选择 K 值')
plt.grid(True)
plt.show()
elbow_method(X, max_k=10)
- 轮廓系数:衡量样本与其所在簇的相似度,取值范围 [-1, 1],值越大表示聚类效果越好
层次聚类
除了 K-Means,层次聚类也是一种常用的聚类方法。层次聚类不需要预先指定簇的数量,它通过构建一个树状结构来表示样本之间的层次关系。
- 自底向上(凝聚):开始时每个样本是一个簇,然后逐步合并最相似的簇
- 自顶向下(分裂):开始时所有样本在一个簇中,然后逐步分裂
from scipy.cluster.hierarchy import dendrogram, linkage
import matplotlib.pyplot as plt
X, _ = make_blobs(n_samples=50, centers=3, random_state=42)
linkage_matrix = linkage(X, method='ward')
plt.figure(figsize=(12, 6))
dendrogram(linkage_matrix)
plt.title('层次聚类树状图')
plt.xlabel('样本索引')
plt.ylabel('距离')
plt.show()
DBSCAN:基于密度的聚类
DBSCAN 是一种基于密度的聚类算法,它可以发现任意形状的簇,并且能够识别噪声点。
- 核心点:在半径ε内至少有 MinPts 个邻居的点
- 边界点:不是核心点,但在某个核心点的ε邻域内
- 噪声点:既不是核心点也不是边界点
from sklearn.cluster import DBSCAN
from sklearn.datasets import make_moons
X, _ = make_moons(n_samples=200, noise=0.05, random_state=42)
dbscan = DBSCAN(eps=0.3, min_samples=5)
labels = dbscan.fit_predict(X)
plt.figure(figsize=(10, 6))
plt.scatter(X[:, 0], X[:, 1], c=labels, cmap='viridis', alpha=0.6)
plt.title('DBSCAN 聚类结果')
plt.xlabel('特征 1')
plt.ylabel('特征 2')
plt.colorbar(label='簇标签')
plt.show()
print(f"发现{len(set(labels))-(1if-1in labels else0)}个簇")
print(f"噪声点数量:{list(labels).count(-1)}")
聚类算法的应用场景
- 客户细分:根据购买行为将客户分组
- 图像分割:将图像分成不同的区域
- 异常检测:识别与正常数据不同的异常点
- 文档聚类:将相似的文档归为一类
- 基因分析:根据基因表达模式对基因分组
算法选择指南
面对不同的问题,如何选择合适的算法呢?下面给出一些建议:
是
否
是
否
是
否
机器学习问题
有标签数据?
预测连续值?
聚类算法
回归算法
分类算法
线性回归
岭回归
Lasso 回归
数据线性可分?
逻辑回归
线性 SVM
决策树
随机森林
核 SVM
神经网络
K-Means
层次聚类
DBSCAN
- 数据规模:大数据集适合线性模型、小数据集可以用 SVM
- 特征维度:高维数据适合线性模型或降维后使用
- 数据分布:线性可分用线性模型,非线性用核方法或树模型
- 可解释性:需要解释性用线性模型或决策树
- 训练时间:时间紧张用简单模型,时间充足可以用复杂模型
总结
本文详细介绍了机器学习中最常用的几种算法:线性回归、决策树、支持向量机和聚类算法。每种算法都有其适用的场景和特点:
线性回归简单高效,适合处理线性关系的问题,是入门机器学习的最佳选择。通过梯度下降法,我们可以找到最优的参数,使得预测误差最小。
决策树直观易懂,可以处理非线性问题,通过信息增益或基尼系数选择最优分裂特征。虽然单棵决策树容易过拟合,但通过随机森林等集成方法可以大大提高性能。
支持向量机通过最大化分类间隔来找到最优分界线,对于高维数据表现优异。通过核函数,SVM 可以处理非线性问题,是一种非常强大的分类算法。
聚类算法不需要标签数据,可以自动发现数据的内在结构。K-Means 简单高效,层次聚类可以展示数据的层次关系,DBSCAN 可以发现任意形状的簇并识别噪声点。
在实际应用中,没有一种算法是万能的,需要根据具体问题选择合适的算法。通常的做法是尝试多种算法,通过交叉验证等方法比较它们的性能,最终选择效果最好的模型。同时,特征工程、参数调优等技巧也对模型性能有重要影响。
掌握这些基础算法的原理和实现,是深入学习机器学习的重要基础。在此基础上,可以进一步学习深度学习、强化学习等更高级的技术。
微信扫一扫,关注极客日志
微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- RSA密钥对生成器
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
- Mermaid 预览与可视化编辑
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
- curl 转代码
解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online