支持向量机(Support Vector Machine,简称SVM)是一种监督学习算法,主要用于分类和回归问题,但在实际应用中更常用于分类问题。SVM的基本原理是找到一个超平面,将数据集中的不同类别尽可能地分开,同时保持超平面与最近的数据点(即支持向量)之间的间隔最大化。
以下是SVM的关键概念和步骤:
-
线性可分性:SVM最初是为线性可分数据设计的,即数据可以通过一个线性超平面完全分开。
-
超平面:在N维空间中,超平面是一个N-1维的线性子空间,可以由一个权重向量
w
和一个偏置项b
定义,形式为w^T * x + b = 0
,其中x
是数据点,w^T
表示权重向量的转置。 -
间隔:间隔是数据点到超平面的最短距离。在SVM中,我们希望找到这样一个超平面,使得不同类别的数据点到这个超平面的距离(即间隔)最大化。
-
支持向量:支持向量是最靠近超平面的数据点,它们决定了超平面的位置和方向。
-
优化问题:SVM的训练过程可以转化为一个凸优化问题。目标是最大化间隔,同时确保所有数据点都在正确的一侧,并且违反间隔的数据点尽可能少。
-
软间隔和正则化:在现实世界的数据中,数据往往是非线性可分的。软间隔SVM允许一些数据点违反间隔,以获得更好的泛化能力。正则化项用于控制模型的复杂度,防止过拟合。
-
核函数:为了处理非线性问题,SVM引入了核函数,它可以将原始数据映射到更高维的空间中,在这个新的空间中寻找线性可分的超平面。常用的核函数包括线性核、多项式核、径向基函数(RBF)核等。
-
SVM的实现:SVM的实现通常包括选择合适的核函数、设置正则化参数和误差项的惩罚系数,然后通过求解优化问题来训练模型。
-
多类分类:对于多类分类问题,SVM可以采用一对一或一对其余等策略,将多类问题转化为多个二类问题来解决。
-
SVM的优缺点:SVM在许多基准数据集上表现出色,特别是在高维数据上。它的优点包括内存效率高、分类精度高、对非线性问题有很好的处理能力。但缺点是对于大规模数据集的训练可能比较慢,对核函数和参数的选择敏感,且在某些噪声较大的数据集上可能表现不佳。
SVM是一种强大的分类算法,广泛应用于生物信息学、图像识别、文本分类等领域。
代码解释:
这段代码是一个使用支持向量机(SVM)进行图像分类的示例,主要分为以下几个部分:
-
导入必要的库:代码开始处导入了
numpy
、sklearn
中的一些模块、time
、operator
、os
和PIL
等库。 -
图像数据加载:
imagedata
函数用于加载图像文件夹中的所有图像,将它们转换为灰度,并调整为28x28像素的尺寸,然后归一化并返回图像数据数组。 -
标签数据加载:
labeldata
函数用于从MNIST数据集的标签文件中读取标签数据。注意这里有一个逻辑错误,for
循环的范围应该是基于文件的实际大小,而不是固定的21。 -
数据归一化:将图像数据
X
的像素值从[0, 255]缩放到[0, 1]。 -
数据类型转换:将标签数据
y
转换为整数类型。 -
添加偏置项:
X_train_bias
通过在每个特征向量的末尾添加一个值为1的列来添加偏置项,这对于线性模型是必要的。 -
损失函数:
L_i_vectorized
和L
函数分别计算了单个样本和整个训练集的损失。 -
SVM训练函数:
train_svm
函数实现了SVM的训练过程。它使用随机梯度下降优化权重矩阵W
,并通过正则化防止过拟合。 -
模型训练:使用
train_svm
函数和添加了偏置项的训练数据X_train_bias
以及标签y_train
来训练SVM模型。 -
预测:使用训练好的权重矩阵
W
对第一个训练样本进行预测,计算得分,并找到得分最高的类别作为预测结果。 -
计算准确率:这部分代码被注释掉了,如果取消注释,它将计算模型在整个测试集上的准确率。
代码中有几个需要注意的地方:
labeldata
函数的实现可能不正确,因为它假设标签文件的大小是固定的,而实际上应该根据文件的实际大小来读取。train_svm
函数中的y_batch
应该从标签到索引的映射,但在代码中没有看到相应的转换。- 代码中的注释和打印语句有助于理解每个步骤的作用,但在实际使用中可能需要根据具体情况进行调整。
整体来看,这段代码展示了如何使用SVM对图像数据进行分类,包括数据预处理、模型训练和预测等步骤。
import numpy as np
from sklearn.datasets import fetch_openml
from sklearn.model_selection import train_test_split
import numpy as np
import time
import operator
import os
from PIL import Image
from sklearn.datasets import load_digits
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
# 加载MNIST数据集
def imagedata(image_folder):
image_files = os.listdir(image_folder)
image_list = []
for image_file in image_files:
file_path = os.path.join(image_folder, image_file)
with Image.open(file_path) as img:
img_gray = img.convert('L')
img_resized = img_gray.resize((28, 28))
image_arry = np.array(img_resized)
image_list.append(image_arry)
mnist_images = np.stack(image_list)
train_image = mnist_images.reshape(mnist_images.shape[0], -1)
return train_image
def labeldata(MNIST_labels_path):
with open(MNIST_labels_path, 'rb') as f:
file_labels = f.read()
train_label = []
for i in range(1, 21):
label = int.from_bytes(file_labels[i + 8 - 1:8 + i], 'big')
train_label.append(label)
return train_label
image_folder = "D:\\MNIST_data\\train1"
MNIST_labels_path = 'D:\\MNIST_data\\train-labels-idx1-ubyte\\train-labels.idx1-ubyte'
X = imagedata(image_folder)
y = labeldata(MNIST_labels_path)
test_image_folder = "D:\\MNIST_data\\test"
test_image = imagedata(test_image_folder)
X = X / 255.0 # 将像素值缩放到0到1之间
y = np.array(y)
y = y.astype(int)
X_train=X
y_train=y
# 将数据分割为训练集和测试集
# X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 添加偏置项到特征向量中
X_train_bias = np.c_[X_train, np.ones((X_train.shape[0], 1))]
# X_test_bias = np.c_[X_test, np.ones((X_test.shape[0], 1))]
def L_i_vectorized(x, y, W):
delta = 1.0
scores = W.dot(x)
margins = np.maximum(0, scores - scores[y] + delta)
margins[y] = 0
loss_i = np.sum(margins)
return loss_i
def L(X, y, W):
num_train = X.shape[1]
scores = W.dot(X)
margins = np.maximum(0, scores - scores[y] + 1.0)
margins[y, np.arange(num_train)] = 0
loss = np.sum(margins) / num_train
return loss
def train_svm(X, y, learning_rate=1e-7, reg=5e4, num_iters=1000, batch_size=20):
num_train = X.shape[0]
dim = X.shape[1]
num_classes = len(np.unique(y))
W = np.zeros((num_classes, dim+1)) # 初始化权重矩阵,包含偏置项
for it in range(num_iters):
batch_size = min(batch_size, num_train)
idx = np.random.choice(num_train, batch_size, replace=False)
X_batch = X[idx]
y_batch = y[idx] # 将类别标签转换为从0开始的索引
loss = 0
dW = np.zeros_like(W)
for i in range(batch_size):
xi = X_batch[i]
yi = y_batch[i]
scores = W[:, :-1].dot(xi) + W[:, -1] # 计算得分,包括偏置项
correct_class_score = scores[yi]
margins = np.maximum(0, scores - correct_class_score + 1)
margins[yi] = 0 # 忽略正确类别的边界违反
loss_i = np.sum(margins)
loss += loss_i
# 计算梯度
for j in range(num_classes):
if j == yi:
# 仅对正确类别计算梯度
dW[j, :-1] -= learning_rate * (xi * (margins[j] > 0) - reg * W[j, :-1])
dW[j, -1] -= learning_rate * (1 * (margins[j] > 0) - reg * W[j, -1])
else:
# 对其他类别,如果违反了边界,则更新梯度
if margins[j] > 0:
dW[j, :-1] -= learning_rate * (xi * 1 - reg * W[j, :-1])
dW[j, -1] -= learning_rate * (1 * 1 - reg * W[j, -1])
W += dW
loss /= batch_size
if it % 10 == 0:
print(f"Iteration {it}: loss {loss}")
return W
# 训练模型
W = train_svm(X_train_bias, y_train , learning_rate=1e-8, reg=5e4, num_iters=1000, batch_size=5)
# # 预测
scores = W.dot(X_train_bias[0].T)
y_pred = np.argmax(scores, axis=0)
#
# # 计算准确率
# accuracy = np.mean(y_pred == y_test)
# print(f"Accuracy: {accuracy}")