马槟 發表於 2025-4-22 09:17:00

软间隔:让支持向量机更“宽容”

<p>在<code>SVM</code>中,<strong>软间隔</strong>是一个重要的概念,它允许模型在一定程度上容忍误分类,从而提高模型的泛化能力。</p>
<p>本文将详细介绍<strong>软间隔</strong>的定义、与<strong>硬间隔</strong>的区别、损失函数的作用,最后使用 <code>scikit-learn</code> 进行实际演示。</p>
<h1 id="1-软间隔-vs-硬间隔">1. 软间隔 vs 硬间隔</h1>
<p>在支持向量机中,<strong>软间隔</strong>是指允许某些数据点违反分类边界(即<strong>误分类</strong>)的间隔。</p>
<p>与<strong>硬间隔</strong>(严格要求所有数据点都正确分类)不同,<strong>软间隔</strong>通过引入<strong>松弛变量</strong>(<code>slack variables</code>),允许部分数据点位于分类边界内或错误分类,从而优化分类边界。</p>
<p>与<strong>硬间隔</strong>相比,<strong>软间隔</strong>的优势非常明显,因为在现实世界中,数据往往存在<strong>噪声</strong>和<strong>异常值</strong>,很难找到一个完美的分类边界将所有数据点正确分类。</p>
<p><strong>硬间隔</strong> <code>SVM</code> 在这种情况下可能会导致<strong>过拟合</strong>,即模型在训练数据上表现很好,但在新的测试数据上表现不佳。</p>
<p><strong>软间隔</strong>通过允许一定的误分类,能够更好地处理噪声和异常值,从而提高模型的泛化能力。</p>
<p>不过,<strong>软间隔</strong>也有自己的劣势,它需要选择合适的<strong>松弛变量</strong>参数(如惩罚参数$ C $),否则可能导致欠拟合或过拟合。</p>
<p>参数选择的不好,效果可能还不如<strong>硬间隔</strong>。</p>
<h1 id="2-软间隔中的损失函数">2. 软间隔中的损失函数</h1>
<p>在<strong>软间隔</strong><code>SVM</code>中,需要通过<strong>损失函数</strong>来实现容错机制,并通过优化<strong>损失函数</strong>来训练模型。</p>
<p>常见的<strong>损失函数</strong>主要有<code>4</code>种:</p>
<h2 id="21-hinge-loss">2.1. Hinge Loss</h2>
<p>它的公式是:$ L(y,f(x))=max(0,1-y\cdot f(x)) $</p>
<p>其中, $ y <span class="math inline">\(是真实标签(`+1`或`-1`),\)</span> f(x) $是模型的预测值。</p>
<p><code>Hinge Loss</code> 是 <code>SVM</code> 的经典损失函数,它惩罚那些被错误分类或接近分类边界的点。</p>
<p>如果数据点被正确分类且距离分类边界较远,则损失为零;否则,损失值会随着距离的减小而增加。</p>
<p><code>Hinge Loss</code> 适合<strong>线性可分</strong>的数据,但在处理噪声和异常值时可能不够灵活。</p>
<h2 id="22-squared-hinge-loss">2.2. Squared Hinge Loss</h2>
<p>它的公式是:$ L(y,f(x))=max(0,1-y\cdot f(x))^2 $</p>
<p>与 <code>Hinge Loss</code> 类似,但对误分类的惩罚更严厉,因为损失值是平方的。</p>
<p><code>Squared Hinge Loss</code> 对噪声和异常值的惩罚更重,可能导致模型对这些点更敏感。</p>
<p>在某些情况下,这可以提高模型的鲁棒性,但也可能增加过拟合的风险。</p>
<h2 id="23-logistic-loss">2.3. Logistic Loss</h2>
<p>它的公式是:$ L(y,f(x))=log(1+exp(-y\cdot f(x))) $</p>
<p><code>Logistic Loss</code> 是逻辑回归中常用的损失函数,它对所有数据点都有惩罚,但惩罚程度随着距离分类边界的增加而逐渐减小。</p>
<p><code>Logistic Loss</code> 适合处理非线性可分的数据,因为它对所有数据点都有惩罚,但对误分类的惩罚相对较轻。这使得模型在处理噪声和异常值时更加灵活。</p>
<h2 id="24-smooth-hinge-loss">2.4. Smooth Hinge Loss</h2>
<p>它的公式是:<span class="math inline">\(L(y, f(x)) =
\begin{cases}
0 &amp; \text{if } y \cdot f(x) \geq 1 \\
1 - y \cdot f(x) &amp; \text{if } 0 &lt; y \cdot f(x) &lt; 1 \\
\frac{(1 - y \cdot f(x))^2}{2} &amp; \text{if } y \cdot f(x) \leq 0
\end{cases}\)</span></p>
<p><code>Smooth Hinge Loss</code> 是 <code>Hinge Loss</code> 的平滑版本,它对误分类的惩罚更加平滑,避免了 <code>Hinge Loss</code> 的不连续性。</p>
<p><code>Smooth Hinge Loss</code> 在优化过程中更加稳定,适合使用梯度下降等优化算法,它对噪声和异常值的处理也更加灵活。</p>
<h1 id="3-软间隔实践">3. 软间隔实践</h1>
<p>为了直观地展示软间隔和硬间隔的区别,我们使用<code>scikit-learn</code>构造一个简单的线性可分数据集,并分别训练<strong>硬间隔</strong>和<strong>软间隔</strong><code>SVM</code>。</p>
<h2 id="31-软间隔和硬间隔比较">3.1. 软间隔和硬间隔比较</h2>
<p>为了比较<strong>软间隔</strong>和<strong>硬间隔</strong>的效果,我们构造一个比较难于线性划分的数据。</p>
<pre><code class="language-python">import matplotlib.pyplot as plt
from sklearn.datasets import make_blobs

# 生成线性可分的数据集
X, y = make_blobs(n_samples=100, centers=2, random_state=42, cluster_std=4)

# 绘制数据集
plt.scatter(X[:, 0], X[:, 1], c=y, cmap='viridis')
plt.title("非线性可分数据集")
plt.show()
</code></pre>
<p><img src="https://cdn.nlark.com/yuque/0/2025/png/2235414/1745140843847-fe408a55-d11c-4abe-9c0b-2025f3735a7d.png" alt="" loading="lazy"></p>
<p>两类数据有交织的部分,无法用一条直线来划分。</p>
<p>然后分别用硬间隔和软间隔的支持向量机来训练。</p>
<pre><code class="language-python">import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score
from sklearn.svm import SVC
import time

# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(
    X, y, test_size=0.3, random_state=42
)

# 硬间隔 SVM
svm_hard = SVC(kernel="linear", C=1e10)# 大的 C 值近似硬间隔
svm_hard.fit(X, y)
y_pred_hard = svm_hard.predict(X_test)
print("硬间隔的准确率:", accuracy_score(y_test, y_pred_hard))

# 软间隔 SVM
svm_soft = SVC(kernel="linear", C=1.0)# 较小的 C 值允许软间隔
svm_soft.fit(X, y)
y_pred_soft = svm_hard.predict(X_test)
print("软间隔的准确率:", accuracy_score(y_test, y_pred_soft))


# 绘制决策边界
def plot_decision_boundary(model, X, y, title):
    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_)
    Z = Z.reshape(xx.shape)

    plt.contourf(xx, yy, Z, alpha=0.8, cmap="viridis")
    plt.scatter(X[:, 0], X[:, 1], c=y, edgecolors="k", cmap="viridis")
    plt.title(title)
    plt.xlabel("Feature 1")
    plt.ylabel("Feature 2")


# 绘制硬间隔和软间隔的决策边界
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plot_decision_boundary(svm_hard, X, y, "硬间隔 SVM")

plt.subplot(1, 2, 2)
plot_decision_boundary(svm_soft, X, y, "软间隔 SVM")

plt.tight_layout()
plt.show()
</code></pre>
<p><img src="https://cdn.nlark.com/yuque/0/2025/png/2235414/1745140955351-3ae02654-a3de-4ba9-ab8f-2de2ebd3d1ed.png" alt="" loading="lazy"></p>
<p>执行之后,<strong>软间隔</strong>和<strong>硬间隔</strong>的准确率都是 <code>0.9</code>,从图上来看似乎也只是略有区别。</p>
<p>其实<strong>软间隔</strong>相比<strong>硬间隔</strong>的优势不是准确率,可以说在很多情况下,硬间隔的准确率都是高于软间隔的。</p>
<p>那么这个示例中,软间隔的有时是什么呢?</p>
<p>如果你实际去运行上面的代码就会发现,上面两次训练(硬间隔模型和软间隔模型)的时间分别:</p>
<p><strong>30.82秒</strong>和<strong>0.001秒</strong>,也就是<strong>软间隔模型</strong>的训练时间远远小于<strong>硬间隔模型</strong>的训练时间。</p>
<p>这还是在数据不复杂的情况,如果修改下数据,也就是上面创建数据的代码中,将:</p>
<pre><code class="language-python">X, y = make_blobs(n_samples=100, centers=2, random_state=42, cluster_std=4)
</code></pre>
<p>其中的<code>cluster_std</code>的值调大一些的话,那么<strong>硬间隔模型</strong>的训练时间几乎无法忍受。</p>
<p>这才是<strong>软间隔模型</strong>的一大优势,准确率绝对不是<strong>软间隔模型</strong>的优势。</p>
<h2 id="32-不同损失函数比较">3.2. 不同损失函数比较</h2>
<p>接下来演示不同<strong>损失函数</strong>对<code>SVM</code>训练效果的影响,我们使用 <code>scikit-learn</code> 构造一个简单的数据集,</p>
<p>然后用使用不同的损失函数来训练,并将训练结果用图形展示出来。</p>
<pre><code class="language-python">from sklearn.linear_model import SGDClassifier

# 生成数据集
X, y = make_blobs(n_samples=100, centers=2, random_state=42, cluster_std=4)

# Hinge Loss (SVM)
svm_hinge = SGDClassifier(
    loss="hinge", alpha=0.01, max_iter=1000, tol=1e-3, random_state=42
)
svm_hinge.fit(X, y)

# Squared Hinge Loss (SVM)
svm_squared_hinge= SGDClassifier(
    loss="squared_hinge", alpha=0.01, max_iter=1000, tol=1e-3, random_state=42
)
svm_squared_hinge.fit(X, y)

# Logistic Loss (Logistic Regression)
svm_logistic = SGDClassifier(
    loss="log_loss", alpha=0.01, max_iter=1000, tol=1e-3, random_state=42
)
svm_logistic.fit(X, y)

# modified_huber
svm_mod_huber = SGDClassifier(
    loss="modified_huber", alpha=0.01, max_iter=1000, tol=1e-3, random_state=42
)
svm_mod_huber.fit(X, y)

# 绘制决策边界
plt.figure(figsize=(12, 12))

plt.subplot(2, 2, 1)
plot_decision_boundary(svm_hinge, X, y, "Hinge Loss")

plt.subplot(2, 2, 2)
plot_decision_boundary(svm_squared_hinge, X, y, "Squared Hinge Loss")

plt.subplot(2, 2, 3)
plot_decision_boundary(svm_logistic, X, y, "Logistic Loss")

plt.subplot(2, 2, 4)
plot_decision_boundary(svm_mod_huber, X, y, "Smooth Hinge Loss")

plt.tight_layout()
plt.show()
</code></pre>
<p><img src="https://cdn.nlark.com/yuque/0/2025/png/2235414/1745148051747-1169235e-56cf-41e5-befb-9f30c00cc3f1.png" alt="" loading="lazy"></p>
<p>注意,<code>scikit-learn</code>中没有直接提供<code>Smooth Hinge Loss</code>这个<strong>损失函数</strong>的选项,<code>modified_huber</code>是与它最接近的<strong>损失函数</strong>。</p>
<p><strong>损失函数</strong>的效果取决于数据的特征,没有最好的损失函数,只有最合适的损失函数。</p>
<p>实际场景中,需要根据数据的特点和需求选择合适的<strong>损失函数</strong>。</p>
<p>比如,数据线性可分且无噪声,可以选择 <code>Hinge Loss</code>;</p>
<p>如果数据存在噪声和异常值,可以选择 <code>Logistic Loss</code> 或 <code>Smooth Hinge Loss</code>。</p>
<h1 id="4-总结">4. 总结</h1>
<p><strong>软间隔</strong>通过<strong>松弛变量</strong>赋予SVM处理不完美数据的能力,配合不同<strong>损失函数</strong>可以灵活应对各种场景。</p>
<p>理解软间隔机制,掌握参数调优方法,是提升SVM实战性能的关键。</p>
<p>实际项目中,建议从<strong>Hinge Loss+RBF核</strong>开始实验,逐步探索参数空间,最终通过<strong>交叉验证</strong>确定最优配置。</p><br><br>
来源:https://www.cnblogs.com/wang_yb/p/18839919
頁: [1]
查看完整版本: 软间隔:让支持向量机更“宽容”