机器学习:支持向量机(SVM)原理解析及垃圾邮件过滤实战

news2025/6/7 13:14:44

一、什么是支持向量机(SVM)

1. 基本概念

1.1 二分类问题的本质

在机器学习中,分类问题是最常见的任务之一。最简单的情况就是二分类:比如一封邮件是“垃圾邮件”还是“正常邮件”?一个病人是“患病”还是“健康”?一个图像是“猫”还是“狗”?
SVM专门擅长处理这种“把样本分成两类”的问题。它的核心目标是:找到一个最优的边界,把两类样本尽可能清晰地分开。

1.2 最大间隔划分思想

SVM的最大特点,就是它不像KNN那样靠近邻,也不像逻辑回归那样直接拟合概率,而是追求“最大间隔”:

它试图在两个类别之间,找到一条“超平面”(一个直线、一个面、或者一个高维空间中的超平面),这个超平面与两类样本中最近的点之间的距离最大,这个“最近的点”就是“支持向量”。

为什么要最大化这个间隔?
简单来说:更大的间隔 = 更强的容错率 = 更好的泛化能力。

1.3 支持向量的意义

在SVM中,真正决定这个“最佳分隔线”的,并不是所有数据点,而是最接近这条线的一小部分点,我们称之为“支持向量”。

这些支持向量对模型训练有决定性作用,一旦它们的位置改变,分界线就可能会改变。而其他点的影响几乎为零。


2. 超平面与间隔

  1. 超平面:在二维中是直线,在三维中是平面,在更高维中是“超平面”。SVM的目标就是找到这样一个能分开两类样本的超平面。

  2. 间隔:超平面到支持向量的最小距离。SVM通过最大化这个间隔来优化模型。

这是一种几何直觉非常强的优化方式,适合处理线性可分的数据。

2.1 硬间隔

**硬间隔(Hard Margin)**是支持向量机(SVM)中的一个概念,指的是在分类过程中,模型要求训练数据必须被完全正确分类,且每个样本点都不能落在分类间隔区域内
直白解释:

硬间隔就是:“我要找到一条完美的分界线,一个样本都不能错分,也不能碰到分界线附近的空白区域。”

2.1.1 硬间隔的定义

设训练数据集为:

{ ( x 1 , y 1 ) , ( x 2 , y 2 ) , … , ( x n , y n ) } , x i ∈ R d , y i ∈ { − 1 , + 1 } \{(x_1, y_1), (x_2, y_2), \dots, (x_n, y_n)\},\quad x_i \in \mathbb{R}^d,\quad y_i \in \{-1, +1\} {(x1,y1),(x2,y2),,(xn,yn)},xiRd,yi{1,+1}

SVM 要求找到一个线性可分的超平面:

w T x + b = 0 w^T x + b = 0 wTx+b=0

使得所有训练样本被完全正确分类,并且分类间隔最大


约束条件(分类正确 + 间隔大):

对所有样本 i = 1 , 2 , . . . , n i = 1, 2, ..., n i=1,2,...,n,要求:

y i ( w T x i + b ) ≥ 1 y_i (w^T x_i + b) \geq 1 yi(wTxi+b)1

这表示:

  • 如果 y i = + 1 y_i = +1 yi=+1,则 w T x i + b ≥ 1 w^T x_i + b \geq 1 wTxi+b1

  • 如果 y i = − 1 y_i = -1 yi=1,则 w T x i + b ≤ − 1 w^T x_i + b \leq -1 wTxi+b1

也就是说:每个样本都要落在自己那一侧的“边界外”,而且不能在间隔区域里、也不能分类错误


优化目标(最大间隔 = 最小化 ∥ w ∥ 2 \|w\|^2 w2

支持向量机通过最大化间隔来实现最优划分。因为几何间隔是 2 ∥ w ∥ \frac{2}{\|w\|} w2,最大化它等价于最小化 ∥ w ∥ 2 \|w\|^2 w2

所以硬间隔 SVM 的优化问题是:

min ⁡ w , b 1 2 ∥ w ∥ 2 subject to y i ( w T x i + b ) ≥ 1 , ∀ i = 1 , . . . , n \begin{aligned} \min_{w, b} \quad & \frac{1}{2} \|w\|^2 \\ \text{subject to} \quad & y_i (w^T x_i + b) \geq 1,\quad \forall i = 1, ..., n \end{aligned} w,bminsubject to21w2yi(wTxi+b)1,i=1,...,n


硬间隔只适用于:

  • 数据完全线性可分

  • 没有噪声和异常值

如果你的数据本身是完全线性可分的,比如下图中的两类点完全不重叠,那么硬间隔SVM就可以很完美地找到一个“最大间隔的超平面”来分开它们。

在这里插入图片描述

一旦数据中有噪声或重叠样本,硬间隔就无法找到合理解,甚至根本无法收敛。这时我们就要用软间隔,允许一些“点分类错了”,换取更强的容错能力。


2.1.2 硬间隔的对偶问题

构造拉格朗日函数(Lagrangian)

引入拉格朗日乘子 α i ≥ 0 \alpha_i \geq 0 αi0,对应约束 y i ( w T x i + b ) ≥ 1 y_i(w^T x_i + b) \geq 1 yi(wTxi+b)1

构造拉格朗日函数:

L ( w , b , α ) = 1 2 ∥ w ∥ 2 − ∑ i = 1 n α i [ y i ( w T x i + b ) − 1 ] \mathcal{L}(w, b, \alpha) = \frac{1}{2} \|w\|^2 - \sum_{i=1}^n \alpha_i [y_i(w^T x_i + b) - 1] L(w,b,α)=21w2i=1nαi[yi(wTxi+b)1]


对偶问题推导步骤

我们要最小化 L \mathcal{L} L 关于 w w w b b b,最大化关于 α \alpha α
先求偏导设置为 0:

1. 对 w w w 求导并令其为 0:

∂ L ∂ w = w − ∑ i = 1 n α i y i x i = 0 ⇒ w = ∑ i = 1 n α i y i x i \frac{\partial \mathcal{L}}{\partial w} = w - \sum_{i=1}^n \alpha_i y_i x_i = 0 \Rightarrow w = \sum_{i=1}^n \alpha_i y_i x_i wL=wi=1nαiyixi=0w=i=1nαiyixi

2. 对 b b b 求导并令其为 0:

∂ L ∂ b = − ∑ i = 1 n α i y i = 0 ⇒ ∑ i = 1 n α i y i = 0 \frac{\partial \mathcal{L}}{\partial b} = - \sum_{i=1}^n \alpha_i y_i = 0 \Rightarrow \sum_{i=1}^n \alpha_i y_i = 0 bL=i=1nαiyi=0i=1nαiyi=0


代入得对偶问题

w w w 表达式代入 L \mathcal{L} L,得到只关于 α \alpha α 的对偶问题:

max ⁡ α ∑ i = 1 n α i − 1 2 ∑ i = 1 n ∑ j = 1 n α i α j y i y j x i T x j s.t. ∑ i = 1 n α i y i = 0 α i ≥ 0 , ∀ i \begin{aligned} \max_{\alpha} \quad & \sum_{i=1}^n \alpha_i - \frac{1}{2} \sum_{i=1}^n \sum_{j=1}^n \alpha_i \alpha_j y_i y_j x_i^T x_j \\ \text{s.t.} \quad & \sum_{i=1}^n \alpha_i y_i = 0 \\ & \alpha_i \geq 0,\quad \forall i \end{aligned} αmaxs.t.i=1nαi21i=1nj=1nαiαjyiyjxiTxji=1nαiyi=0αi0,i

这就是 硬间隔 SVM 的对偶形式

它是一个凸二次规划问题,可以用标准优化算法求解。

2.2 软间隔

软间隔(Soft Margin)是支持向量机(SVM)在面对现实中不可完美划分数据时,引入的一种“宽容机制”。

通俗来说:

如果“硬间隔”是一个完美主义者,要求训练集所有点都正确分类;
那么“软间隔”就是个现实主义者,承认一些点可能分类错误,但整体效果更重要


现实中的数据常常:

  • 噪声(例如标注错误、输入异常);

  • 两类样本有重叠

  • 完全线性可分几乎不可能。

此时,如果继续使用硬间隔 SVM,会导致:

  • 模型无法收敛;

  • 过拟合噪声点,泛化能力差。

所以我们需要“放松限制”——允许一部分样本距离边界近,甚至被分错,但整体间隔仍尽可能大。


2.2.1 软间隔的形式定义

引入松弛变量 ξ i ≥ 0 \xi_i \geq 0 ξi0 来表示第 i i i 个样本允许“违背间隔要求”的程度。

优化问题变成:

min ⁡ w , b , ξ 1 2 ∥ w ∥ 2 + C ∑ i = 1 n ξ i subject to y i ( w T x i + b ) ≥ 1 − ξ i , ∀ i ξ i ≥ 0 , ∀ i \begin{aligned} \min_{w, b, \xi} \quad & \frac{1}{2} \|w\|^2 + C \sum_{i=1}^{n} \xi_i \\ \text{subject to} \quad & y_i (w^T x_i + b) \geq 1 - \xi_i,\quad \forall i \\ & \xi_i \geq 0,\quad \forall i \end{aligned} w,b,ξminsubject to21w2+Ci=1nξiyi(wTxi+b)1ξi,iξi0,i

解释各个部分含义:

  • 1 2 ∥ w ∥ 2 \frac{1}{2} \|w\|^2 21w2:仍然是“最大化间隔”的目标;
  • ξ i \xi_i ξi:表示第 i i i 个样本有多“违反”分类边界;
    * ξ i = 0 \xi_i = 0 ξi=0:正常分类、在间隔外;
    * 0 < ξ i < 1 0 < \xi_i < 1 0<ξi<1:在间隔边界内但没错分;
    * ξ i > 1 \xi_i > 1 ξi>1:被分错类;
  • C C C:是一个超参数,控制“间隔最大化”与“容忍错误”的权衡:
    * C 大:更偏向少错分(严格)→ 更可能过拟合;
    * C 小:更偏向宽容误差(松弛)→ 更强泛化能力。

2.2.2 软间隔的对偶问题

构造拉格朗日函数

引入拉格朗日乘子:

  • α i ≥ 0 \alpha_i \geq 0 αi0:对应约束 y i ( w T x i + b ) ≥ 1 − ξ i y_i(w^T x_i + b) \geq 1 - \xi_i yi(wTxi+b)1ξi

  • μ i ≥ 0 \mu_i \geq 0 μi0:对应约束 ξ i ≥ 0 \xi_i \geq 0 ξi0

拉格朗日函数为:
在这里插入图片描述


对偶问题推导(KKT 条件)

我们将对 w , b , ξ w, b, \xi w,b,ξ 求偏导并设为 0:

w w w 求导:

∂ L ∂ w = w − ∑ i = 1 n α i y i x i = 0 ⇒ w = ∑ i = 1 n α i y i x i \frac{\partial \mathcal{L}}{\partial w} = w - \sum_{i=1}^n \alpha_i y_i x_i = 0 \Rightarrow w = \sum_{i=1}^n \alpha_i y_i x_i wL=wi=1nαiyixi=0w=i=1nαiyixi

b b b 求导:

∂ L ∂ b = − ∑ i = 1 n α i y i = 0 ⇒ ∑ i = 1 n α i y i = 0 \frac{\partial \mathcal{L}}{\partial b} = -\sum_{i=1}^n \alpha_i y_i = 0 \Rightarrow \sum_{i=1}^n \alpha_i y_i = 0 bL=i=1nαiyi=0i=1nαiyi=0

ξ i \xi_i ξi 求导:

∂ L ∂ ξ i = C − α i − μ i = 0 ⇒ α i ≤ C ( 因为  μ i ≥ 0 ) \frac{\partial \mathcal{L}}{\partial \xi_i} = C - \alpha_i - \mu_i = 0 \Rightarrow \alpha_i \leq C \quad (\text{因为 } \mu_i \geq 0) ξiL=Cαiμi=0αiC(因为 μi0)


最终对偶问题形式

整理后,软间隔 SVM 的对偶问题为:

max ⁡ α ∑ i = 1 n α i − 1 2 ∑ i = 1 n ∑ j = 1 n α i α j y i y j x i T x j s.t. ∑ i = 1 n α i y i = 0 0 ≤ α i ≤ C , ∀ i \begin{aligned} \max_{\alpha} \quad & \sum_{i=1}^n \alpha_i - \frac{1}{2} \sum_{i=1}^n \sum_{j=1}^n \alpha_i \alpha_j y_i y_j x_i^T x_j \\ \text{s.t.} \quad & \sum_{i=1}^n \alpha_i y_i = 0 \\ & 0 \leq \alpha_i \leq C,\quad \forall i \end{aligned} αmaxs.t.i=1nαi21i=1nj=1nαiαjyiyjxiTxji=1nαiyi=00αiC,i

相比于硬间隔的对偶问题,这里:

  • 增加了上界 α i ≤ C \alpha_i \leq C αiC,体现对误差的容忍;

  • 因此更适合实际带噪音的数据;

  • 仍然是一个凸二次规划问题

3. 核函数

3.1 核函数的定义

支持向量机算法分类和回归方法的中都支持线性性和非线性类型的数据类型。非线性类型通常是二维平面不可分,为了使数据可分,需要通过一个函数将原始数据映射到高维空间,从而使得数据在高维空间很容易可分,需要通过一个函数将原始数据映射到高维空间,从而使得数据在高维空间很容易区分,这样就达到数据分类或回归的目的,而实现这一目标的函数称为核函数。

在这里插入图片描述在这里插入图片描述

3.2 常见的核函数

1. 线性核(Linear Kernel)
K ( x , x ′ ) = x T x ′ K(x, x') = x^T x' K(x,x)=xTx
适用场景:

  • 特征数非常大(如文本分类)

  • 数据本身已经线性可分或接近线性


2. 多项式核

K ( x , x ′ ) = ( x T x ′ + c ) d K(x, x') = (x^T x' + c)^d K(x,x)=(xTx+c)d

其中 d d d:多项式的次数, c c c:常数项(通常 c ≥ 0 c \geq 0 c0

适用场景:

  • 特征间有高阶交互关系的情况

  • 对非线性边界较敏感的问题


3. 高斯核

K ( x , x ′ ) = exp ⁡ ( − ∥ x − x ′ ∥ 2 2 σ 2 ) K(x, x') = \exp\left(-\frac{\|x - x'\|^2}{2 \sigma^2} \right) K(x,x)=exp(2σ2xx2)

其中 σ \sigma σ:控制“邻近范围”的宽度(或使用 γ = 1 2 σ 2 \gamma = \frac{1}{2\sigma^2} γ=2σ21

适用场景:

  • 不知道数据的具体分布情况

  • 默认首选的非线性核


4. Sigmoid 核(双曲正切核)

K ( x , x ′ ) = tanh ⁡ ( κ x T x ′ + c ) K(x, x') = \tanh(\kappa x^T x' + c) K(x,x)=tanh(κxTx+c)

类似于神经元中的激活函数

适用场景:

  • 模拟神经网络的效果(如早期神经 SVM 研究)

  • 情况较少使用


二、代码实战

1. 实验内容

实战要求:使用SVM建立自己的垃圾邮件过滤器。首先需要将每个邮件x变成一个n维的特征向量,并训练一个分类器来分类给定的电子邮件x是否属于垃圾邮件 ( y = 1 ) (y=1)(y=1) 或者非垃圾邮件 ( y = 0 ) (y=0)(y=0) 。

已有数据集:emailSample1.txt, vocab.txt, spamTrain.mat, spamTest.mat

2.代码实现

1. 加载并查看样例邮件内容

#查看样例邮件
f = open('emailSample1.txt', 'r').read()
print(f)

读取一封样例邮件的原始文本内容,便于后续处理。

2. 邮件预处理函数 processEmail

def processEmail(email):
    email = email.lower() #转化为小写
    email = re.sub('<[^<>]+>', ' ', email) #移除所有HTML标签
    email = re.sub(r'(http|https)://[^\s]*', 'httpaddr', email) #将所有的URL替换为'httpaddr'
    email = re.sub(r'[^\s]+@[^\s]+', 'emailaddr', email) #将所有的地址替换为'emailaddr'
    email = re.sub(r'\d+', 'number', email) #将所有数字替换为'number'
    email = re.sub('[$]+', 'dollar', email) #将所有美元符号($)替换为'dollar'
    
    #将所有单词还原为词根//移除所有非文字类型,空格调整
    stemmer = nltk.stem.PorterStemmer() #使用Porter算法
    tokens = re.split(r'[ @$/#.-:&*+=\[\]?!()\{\},\'\">_<;%]', email) #把邮件分割成单个的字符串,[]里面为各种分隔符
    tokenlist = []
    for token in tokens:
        token = re.sub('[^a-zA-Z0-9]', '', token) #去掉任何非字母数字字符
        try: #porterStemmer有时会出现问题,因此用try
            token = stemmer.stem(token) #词根
        except:
            token = ''
        if len(token) < 1: 
            continue #字符串长度小于1的不添加到tokenlist里
        tokenlist.append(token)
    
    return tokenlist

核心步骤解释:

  • 统一格式:转小写、去 HTML 标签、替换网址、邮箱、数字、货币符号等。

  • 分词:使用正则表达式切分文本成单词。

  • 词干提取(stemming):调用 NLTK 的 PorterStemmer,还原单词基本形式,如 running → run。

  • 清洗:剔除空词、特殊符号,返回标准词汇列表。

  1. 词汇表映射及向量化处理
vocab_list = np.loadtxt('vocab.txt', dtype='str', usecols=1)

词汇表 vocab.txt 中存有 1899 个关键词。

下面两个函数实现了“文本 → 向量”转换:

def word_indices(processed_f, vocab_list):
    indices = []
    for i in range(len(processed_f)):
        for j in range(len(vocab_list)):
            if processed_f[i]!=vocab_list[j]:
                continue
            indices.append(j+1)
    return indices
def emailFeatures(indices):
    features = np.zeros((1899))
    for each in indices:
        features[each-1] = 1 #若indices在对应单词表的位置上词语存在则记为1
    return features

效果:

每封邮件被表示成一个 1899维的二进制特征向量,其中:

  • i i i 个位置为 1,表示邮件中包含词汇表中第 i i i 个词。

  • 类似 One-hot 编码,但是多热(multi-hot)表示。

  1. 训练 SVM 模型
train = scio.loadmat('spamTrain.mat')
train_x = train['X']
train_y = train['y']
  • 载入 .mat 格式的训练数据。
  • X 为 4000 封邮件的特征矩阵,y 为对应的垃圾与非垃圾标签(1 或 0)。
clf = svm.SVC(C=0.1, kernel='linear')
clf.fit(train_x, train_y)
  • 使用线性核训练 SVM 模型。
  • 参数 C=0.1 控制软间隔的惩罚程度,越小越宽容。
  1. 模型评估
def accuracy(clf, x, y):
    predict_y = clf.predict(x)
    m = y.size
    count = 0
    for i in range(m):
        count = count + np.abs(int(predict_y[i])-int(y[i])) #避免溢出错误得到225
    return 1-float(count/m) 
  • 自定义精度计算函数,统计预测正确率。
train_accuracy = accuracy(clf, train_x, train_y) 
test_accuracy = accuracy(clf, test['Xtest'], test['ytest'])
  1. 解释模型——重要单词排序
print(vocab_list[np.argsort(clf.coef_).flatten()[i]], end=' ')
  • SVM 的线性模型可以通过系数权重判断哪些词对“判定为垃圾邮件”影响最大。
    打印前 15 个正权重最大词汇,说明:
    邮件中若含这些词,更可能是垃圾邮件。
  1. 预测新邮件是否为垃圾邮件
t = open('spamSample2.txt', 'r').read()
processed_f = processEmail(t) 
f_indices = word_indices(processed_f, vocab_list)
x = np.reshape(emailFeatures(f_indices), (1,1899))
clf.predict(x)
  • 流程和训练集一致,对新邮件样本做预处理 → 向量化 → 调用模型预测。
  • 输出为 1(垃圾)或 0(正常)。
  1. PCA 可视化 SVM 决策边界
# PCA降维到2维
pca = PCA(n_components=2)
train_x_pca = pca.fit_transform(train_x)

# 重新训练一个2维的SVM,用于绘制决策边界
clf_pca = svm.SVC(C=0.1, kernel='linear')
clf_pca.fit(train_x_pca, train_y)

# 画图
plt.figure(figsize=(10, 6))

# 绘制不同类别样本点
plt.scatter(train_x_pca[train_y==0, 0], train_x_pca[train_y==0, 1], c='blue', label='Non-spam', edgecolors='k')
plt.scatter(train_x_pca[train_y==1, 0], train_x_pca[train_y==1, 1], c='red', label='Spam', edgecolors='k')

# 绘制决策边界
# 创建一个网格
x_min, x_max = train_x_pca[:, 0].min() - 1, train_x_pca[:, 0].max() + 1
y_min, y_max = train_x_pca[:, 1].min() - 1, train_x_pca[:, 1].max() + 1
xx, yy = np.meshgrid(np.linspace(x_min, x_max, 500),
                     np.linspace(y_min, y_max, 500))

# 计算决策函数值
Z = clf_pca.decision_function(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)

# 绘制决策边界和间隔带
plt.contour(xx, yy, Z, levels=[0], linewidths=2, colors='green')
plt.contour(xx, yy, Z, levels=[-1, 1], linestyles='--', colors='gray')

plt.legend()
plt.title('SVM Decision Boundary (PCA Reduced 2D)')
plt.xlabel('Principal Component 1')
plt.ylabel('Principal Component 2')
plt.show()
  • 用 PCA 将高维特征压缩成 2 维空间。
  • 使用 SVM 在这个低维空间重新训练一个模型(仅用于可视化)。
  • 展示不同类别的点分布情况,以及 SVM 的分界线与间隔。
  1. 完整代码
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import scipy.io as scio
from sklearn import svm 
import re #处理正则表达式的模块
import nltk #自然语言处理工具包
from sklearn.decomposition import PCA

#查看样例邮件
f = open('emailSample1.txt', 'r').read()
print(f)
 
def processEmail(email):
    email = email.lower() #转化为小写
    email = re.sub('<[^<>]+>', ' ', email) #移除所有HTML标签
    email = re.sub(r'(http|https)://[^\s]*', 'httpaddr', email) #将所有的URL替换为'httpaddr'
    email = re.sub(r'[^\s]+@[^\s]+', 'emailaddr', email) #将所有的地址替换为'emailaddr'
    email = re.sub(r'\d+', 'number', email) #将所有数字替换为'number'
    email = re.sub('[$]+', 'dollar', email) #将所有美元符号($)替换为'dollar'
    
    #将所有单词还原为词根//移除所有非文字类型,空格调整
    stemmer = nltk.stem.PorterStemmer() #使用Porter算法
    tokens = re.split(r'[ @$/#.-:&*+=\[\]?!()\{\},\'\">_<;%]', email) #把邮件分割成单个的字符串,[]里面为各种分隔符
    tokenlist = []
    for token in tokens:
        token = re.sub('[^a-zA-Z0-9]', '', token) #去掉任何非字母数字字符
        try: #porterStemmer有时会出现问题,因此用try
            token = stemmer.stem(token) #词根
        except:
            token = ''
        if len(token) < 1: 
            continue #字符串长度小于1的不添加到tokenlist里
        tokenlist.append(token)
    
    return tokenlist
 
#查看处理后的样例
processed_f = processEmail(f)
for i in processed_f:
    print(i, end=' ')
 
#得到单词表,序号为索引号+1
vocab_list = np.loadtxt('vocab.txt', dtype='str', usecols=1)
#得到词汇表中的序号
def word_indices(processed_f, vocab_list):
    indices = []
    for i in range(len(processed_f)):
        for j in range(len(vocab_list)):
            if processed_f[i]!=vocab_list[j]:
                continue
            indices.append(j+1)
    return indices
 
#查看样例序号
f_indices = word_indices(processed_f, vocab_list)
for i in f_indices:
    print(i, end=' ')

def emailFeatures(indices):
    features = np.zeros((1899))
    for each in indices:
        features[each-1] = 1 #若indices在对应单词表的位置上词语存在则记为1
    return features
        
sum(emailFeatures(f_indices)) #45

def emailFeatures(indices):
    features = np.zeros((1899))
    for each in indices:
        features[each-1] = 1 #若indices在对应单词表的位置上词语存在则记为1
    return features
        
sum(emailFeatures(f_indices)) #45

#训练模型
train = scio.loadmat('spamTrain.mat')
train_x = train['X']
train_y = train['y']
 
clf = svm.SVC(C=0.1, kernel='linear')
clf.fit(train_x, train_y)
 
#精度
def accuracy(clf, x, y):
    predict_y = clf.predict(x)
    m = y.size
    count = 0
    for i in range(m):
        count = count + np.abs(int(predict_y[i])-int(y[i])) #避免溢出错误得到225
    return 1-float(count/m) 
 
train_accuracy = accuracy(clf, train_x, train_y) #0.99825
print("Train accuracy:", train_accuracy)
 
#测试模型
test = scio.loadmat('spamTest.mat')
 
test_accuracy = accuracy(clf, test['Xtest'], test['ytest']) #0.989
print("Test accuracy:", test_accuracy)

#打印权重最高的前15个词,邮件中出现这些词更容易是垃圾邮件
i = (clf.coef_).size-1
while i >1883:
    #返回从小到大排序的索引,然后再打印
    print(vocab_list[np.argsort(clf.coef_).flatten()[i]], end=' ')
    i = i-1

t = open('spamSample2.txt', 'r').read()
#预处理
processed_f = processEmail(t) 
f_indices = word_indices(processed_f, vocab_list)
#特征提取
x = np.reshape(emailFeatures(f_indices), (1,1899))
#预测
clf.predict(x)

# 载入训练数据
train = scio.loadmat('spamTrain.mat')
train_x = train['X']
train_y = train['y'].flatten()  # 拉平成1维数组方便使用

# 用线性核训练SVM
clf = svm.SVC(C=0.1, kernel='linear')
clf.fit(train_x, train_y)

# PCA降维到2维
pca = PCA(n_components=2)
train_x_pca = pca.fit_transform(train_x)

# 重新训练一个2维的SVM,用于绘制决策边界
clf_pca = svm.SVC(C=0.1, kernel='linear')
clf_pca.fit(train_x_pca, train_y)

# 画图
plt.figure(figsize=(10, 6))

# 绘制不同类别样本点
plt.scatter(train_x_pca[train_y==0, 0], train_x_pca[train_y==0, 1], c='blue', label='Non-spam', edgecolors='k')
plt.scatter(train_x_pca[train_y==1, 0], train_x_pca[train_y==1, 1], c='red', label='Spam', edgecolors='k')

# 绘制决策边界
# 创建一个网格
x_min, x_max = train_x_pca[:, 0].min() - 1, train_x_pca[:, 0].max() + 1
y_min, y_max = train_x_pca[:, 1].min() - 1, train_x_pca[:, 1].max() + 1
xx, yy = np.meshgrid(np.linspace(x_min, x_max, 500),
                     np.linspace(y_min, y_max, 500))

# 计算决策函数值
Z = clf_pca.decision_function(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)

# 绘制决策边界和间隔带
plt.contour(xx, yy, Z, levels=[0], linewidths=2, colors='green')
plt.contour(xx, yy, Z, levels=[-1, 1], linestyles='--', colors='gray')

plt.legend()
plt.title('SVM Decision Boundary (PCA Reduced 2D)')
plt.xlabel('Principal Component 1')
plt.ylabel('Principal Component 2')
plt.show()

3.运行结果在这里插入图片描述

在这里插入图片描述

  1. 决策边界与支持向量带
    绿色实线:SVM 的决策边界(分类超平面),表示模型判断垃圾/非垃圾邮件的分界线。
    灰色虚线(上下各一条):代表 SVM 的“间隔边界”(margin)——支持向量带。
    SVM 的目标就是最大化这两条边界之间的间隔(margin)。
    落在虚线之间的点是支持向量,最影响决策边界的位置。

  2. 分类结果分布
    红色点(Spam):预测为垃圾邮件
    蓝色点(Non-spam):预测为非垃圾邮件
    观察结果:
    大部分红点和蓝点被成功分开,说明模型效果不错。
    决策边界大致水平(沿 x 轴延展),说明分类器主要依据 PCA 的第 2 主成分 进行分类(也就是 y 轴方向)。
    有少量红蓝点混杂,尤其靠近边界或 margin 区域,是分类难度较大的样本,也可能是误分类点。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2402935.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LLM Agent 如何颠覆股价预测的传统范式

写在前面 股价预测,金融领域的“圣杯”之一,吸引了无数研究者和投资者。传统方法从技术指标到复杂的计量经济模型,再到机器学习,不断演进,但市场的高度复杂性、非线性和充斥噪声的特性,使得精准预测依然是巨大的挑战。大型语言模型(LLM)的崛起,特别是LLM Agent这一新…

【案例分享】如何借助JS UI组件库DHTMLX Suite构建高效物联网IIoT平台

随着工业领域数字化进程的持续加快&#xff0c;Web 技术在实现实时监控、数据可视化与控制系统等方面正扮演着越来越核心的角色。来自智利的科技企业 Wise Data Global 就是这一趋势中的积极践行者。他们借助慧都科技代理的 DHTMLX Suite JavaScript UI 控件库&#xff0c;为遥…

Skia如何绘制几何图形

应用程序&#xff08;网页、桌面应用或移动应用&#xff09;大多数都是由基本的几何图形构成的。那我们该如何使用 Skia 绘制基本的几何图形。 画矩形 void drawRect(SkCanvas* canvas) {SkPaint paint;paint.setColor(SK_ColorRED);paint.setStroke(true);paint.setStrokeWid…

spring:实例化类过程中方法执行顺序。

如题。在实例化Bean时&#xff0c;会根据配置依次调用方法。在此测试代码如下&#xff1a; 在测试类中继承接口InitializingBean&#xff0c;接口InterfaceUserService&#xff08;该接口为自定义&#xff0c;只是定义set方法&#xff09;。 InterfaceUserService&#xff0c…

设置应用程序图标

(1)找一张图片 (2)然后转ico图片 在线生成透明ICO图标——ICO图标制作 验证16x16就可以 降低exe大小 (3) 在xxx.pro修改 添加 &#xff08;4&#xff09; 删除 build 和 xxxpro_user文件 (5)编译project 和运行xx.exe (6)右键 设置快捷方式

「基于连续小波变换(CWT)和卷积神经网络(CNN)的心律失常分类算法——ECG信号处理-第十五课」2025年6月6日

一、引言 心律失常是心血管疾病的重要表现形式&#xff0c;其准确分类对临床诊断具有关键意义。传统的心律失常分类方法主要依赖于人工特征提取和经典机器学习算法&#xff0c;但这些方法往往受限于特征选择的主观性和模型的泛化能力。 随着深度学习技术的发展&#xff0c;基于…

用go从零构建写一个RPC(4)--gonet网络框架重构+聚集发包

在追求高性能的分布式系统中&#xff0c;RPC 框架的底层网络能力和数据传输效率起着决定性作用。经过几轮迭代优化&#xff0c;我完成了第四版本的 RPC 框架。相比以往版本&#xff0c;这一版本的最大亮点在于 重写了底层网络框架 和 实现了发送端的数据聚集机制&#xff0c;这…

OpenBayes 一周速览|TransPixeler 实现透明化文本到视频生成;统一图像定制框架 DreamO 上线,一键处理多种图像生成任务

公共资源速递 2 个公共数据集&#xff1a; * s1K-1.1 数学推理数据集 * HPA 人类蛋白质图谱数据集 3 个公共模型&#xff1a; * MedGemma-4B-IT * Devstral-Small-2505 * DeepSeek-Prover-V2-7B 12 个公共教程&#xff1a; 视频生成 * 2 语音交互 * 3 代码生成 * 3 …

4D毫米波雷达产品推荐

供应商链接 &#xff1a;https://mp.weixin.qq.com/s/GYarrc9VEZS0FafxRUeG9w 大陆 ARS548 采埃孚 博世 安波福 -------- Waymo MobileEye 华为&#xff08;未找到官网资料&#xff09; ------- 森思泰克 http://www.whst.com/contact.html 芜湖经济技术开发区东区…

yolo 训练 中间可视化

yolo训练前几个batch&#xff0c;会可视化target: if plots and ni < 33:f save_dir / ftrain_batch{ni}.jpg # filenameplot_images(imgs, targets, paths, f, kpt_labelkpt_label)

Rust 学习笔记:关于 Cargo 的练习题

Rust 学习笔记&#xff1a;关于 Cargo 的练习题 Rust 学习笔记&#xff1a;关于 Cargo 的练习题问题一问题二问题三问题四问题五问题六问题七 Rust 学习笔记&#xff1a;关于 Cargo 的练习题 参考视频&#xff1a; https://www.bilibili.com/video/BV1xjAaeAEUzhttps://www.b…

光伏功率预测 | BiLSTM多变量单步光伏功率预测(Matlab完整源码和数据)

光伏功率预测 | BiLSTM多变量单步光伏功率预测&#xff08;Matlab完整源码和数据&#xff09; 目录 光伏功率预测 | BiLSTM多变量单步光伏功率预测&#xff08;Matlab完整源码和数据&#xff09;效果一览基本介绍程序设计参考资料 效果一览 基本介绍 光伏功率预测 | BiLSTM多变…

20250606-C#知识:委托和事件

C#知识&#xff1a;委托和事件 使用委托可以很方便地调用多个方法&#xff0c;也方便将方法作为参数进行传递 1、委托 委托是方法的容器委托可以看作一种特殊的类先定义委托类&#xff0c;再用委托类声明委托变量&#xff0c;委托变量可以存储方法 delegate int Calculate(in…

AI数字人技术革新进行时:井云数字人如何重塑人机交互未来?

老板们注意了&#xff01;不用反复真人出镜拍摄&#xff0c;AI数字人来帮你做口播&#xff0c;只需3分钟克隆你的形象和声音&#xff0c;输入文案24小时随时都能生成视频&#xff01; 在元宇宙概念持续升温、虚拟与现实加速融合的当下&#xff0c;AI数字人正以惊人的速度从科幻…

ruoyi-plus-could 负载均衡 通过 Gateway模块配置负载均衡

这个很简单的&#xff0c;其实都不用配置。 在nacos中ruoyi-gateway.yml配置文件里面&#xff1a; 其实他已经给我们配置好了&#xff0c;只要uri&#xff1a;lb有【lb】就表示负载均衡配置 我们只需要在启动服务的时候改下端口就可以。 然后通过小工具测试下&#xff1a; 结…

Matlab回归预测大合集又更新啦!新增2种高斯过程回归预测模型,已更新41个模型!性价比拉满!

Matlab回归预测大合集又更新啦&#xff01;新增2种高斯过程回归预测模型&#xff0c;已更新41个模型&#xff01;性价比拉满&#xff01; 目录 Matlab回归预测大合集又更新啦&#xff01;新增2种高斯过程回归预测模型&#xff0c;已更新41个模型&#xff01;性价比拉满&#xf…

主流 AI IDE 之一的 Cursor 介绍

一、什么是 Cursor Cursor 是由 Anysphere 公司开发的 AI 驱动的代码编辑器&#xff08;IDE&#xff09;&#xff1b;Anysphere 成立于 2022 年&#xff0c;创始团队包括来自麻省理工学院&#xff08;MIT&#xff09;的毕业生&#xff0c;如联合创始人 Aman Sanger 和 Michael …

0x-1 记一次SGA PGA设置失败,重新开库

0、生产侧定时平台上传数据库11g hang&#xff0c;修改无法startup 厂商统一发放的虚拟机作为前置机导入平台后&#xff0c;直接开机使用。主机在虚拟化平台中&#xff0c;实例卡死后&#xff0c;按照虚拟机系统64G&#xff0c;原SGA2g,不知哪个大聪明给默认设置的。保守计划修…

【科研绘图系列】R语言绘制和弦图(Chord diagram plot)

禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包数据下载导入数据数据预处理相关性计算和弦图系统信息介绍 本文介绍了一个基于R语言的数据分析和可视化流程,主要用于生成和弦图(Chord Diagram)。和弦图是一种用于展示…

PPT转图片拼贴工具 v3.0

软件介绍 这个软件就是将PPT文件转换为图片并且拼接起来。 这个代码支持导入单个文件也支持导入文件夹 但是目前还没有解决可视化界面问题。 效果展示 软件源码 import os import re import win32com.client from PIL import Image from typing import List, Uniondef con…