支持向量机分类器预测收入等级
我们将构建一个支持向量机(SVM)分类器,以预测一个人基于14个属性的收入等级。我们的目标是判断收入是否高于或低于每年$50,000。因此,这是一个二元分类问题。我们将使用在此处可用的人口普查收入数据集。需要注意的是,此数据集中的每个数据点都是单词和数字的混合。我们不能使用原始格式的数据,因为算法不知道如何处理单词。我们不能使用标签编码器转换所有数据,因为数值数据是有价值的。因此,我们需要结合使用标签编码器和原始数值数据来构建一个有效的分类器。
引言
支持向量机(SVM)是一种监督学习模型,用于解决分类和回归问题。对于分类任务,SVM 通过找到一个最优超平面来将不同类别的样本分开。SVM 的核心思想是通过最大化不同类别之间的间隔(margin)来找到一个最优的分割超平面。这个最优超平面由权重向量
w
w
w 和偏差
b
b
b 决定。
数学原理
SVM 的目标是找到一个超平面,使得所有正类样本和负类样本尽可能远离这个超平面。对于线性可分的情况,假设数据集中的样本为
(
x
i
,
y
i
)
(x_i, y_i)
(xi,yi),其中
x
i
x_i
xi 是输入向量,
y
i
∈
{
+
1
,
−
1
}
y_i \in \{+1, -1\}
yi∈{+1,−1} 是标签。我们希望找到一个超平面使得:
w
⋅
x
i
+
b
≥
1
if
y
i
=
+
1
w \cdot x_i + b \geq 1 \quad \text{if} \quad y_i = +1
w⋅xi+b≥1ifyi=+1
w
⋅
x
i
+
b
≤
−
1
if
y
i
=
−
1
w \cdot x_i + b \leq -1 \quad \text{if} \quad y_i = -1
w⋅xi+b≤−1ifyi=−1
这可以合并为一个约束条件:
y
i
(
w
⋅
x
i
+
b
)
≥
1
∀
i
y_i (w \cdot x_i + b) \geq 1 \quad \forall i
yi(w⋅xi+b)≥1∀i
SVM 的优化目标是最大化间隔
2
/
∥
w
∥
2 / \|w\|
2/∥w∥,这等价于最小化
∥
w
∥
2
/
2
\|w\|^2 / 2
∥w∥2/2。因此,SVM 的优化问题可以表示为:
min
1
2
∥
w
∥
2
\min \frac{1}{2} \|w\|^2
min21∥w∥2
subject to
y
i
(
w
⋅
x
i
+
b
)
≥
1
∀
i
\text{subject to} \quad y_i (w \cdot x_i + b) \geq 1 \quad \forall i
subject toyi(w⋅xi+b)≥1∀i
对偶问题
为了求解上述优化问题,通常会将其转换为对偶问题。引入拉格朗日乘子
α
i
\alpha_i
αi,对偶问题可以表示为:
max
∑
i
=
1
n
α
i
−
1
2
∑
i
=
1
n
∑
j
=
1
n
α
i
α
j
y
i
y
j
(
x
i
⋅
x
j
)
\max \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 \cdot x_j)
maxi=1∑nαi−21i=1∑nj=1∑nαiαjyiyj(xi⋅xj)
subject to
∑
i
=
1
n
α
i
y
i
=
0
\text{subject to} \quad \sum_{i=1}^n \alpha_i y_i = 0
subject toi=1∑nαiyi=0
0
≤
α
i
≤
C
∀
i
\quad \quad \quad \quad \quad 0 \leq \alpha_i \leq C \quad \forall i
0≤αi≤C∀i
其中,
C
C
C 是惩罚参数,控制样本误差和间隔的权衡。
核函数
对于非线性可分的情况,可以将输入空间映射到高维特征空间,在高维特征空间中寻找线性可分的超平面。这通过核函数 K ( x i , x j ) = ϕ ( x i ) ⋅ ϕ ( x j ) K(x_i, x_j) = \phi(x_i) \cdot \phi(x_j) K(xi,xj)=ϕ(xi)⋅ϕ(xj) 来实现。常用的核函数包括多项式核、高斯核(RBF核)等。
案例代码
预测一个人基于14个属性的收入等级。我们的目标是判断收入是否高于或低于每年$50,000。数据文件可以从这里下载.
# 导入所需的包
import numpy as np
import matplotlib.pyplot as plt
from sklearn import preprocessing
from sklearn.svm import LinearSVC
from sklearn.multiclass import OneVsOneClassifier
from sklearn import cross_validation
# 输入包含数据的文件
input_file = 'income_data.txt'
# 读取数据
X = []
y = []
count_class1 = 0
count_class2 = 0
max_datapoints = 25000
# 打开文件并开始读取每行数据
with open(input_file, 'r') as f:
for line in f.readlines():
if count_class1 >= max_datapoints and count_class2 >= max_datapoints:
break
if '?' in line:
continue
# 每行数据是以逗号分隔的,需要相应地进行拆分
data = line[:-1].split(', ')
# 最后一个元素是标签,根据标签将其分配到不同的类
if data[-1] == '<=50K' and count_class1 < max_datapoints:
X.append(data)
count_class1 += 1
if data[-1] == '>50K' and count_class2 < max_datapoints:
X.append(data)
count_class2 += 1
# 将列表转换为numpy数组,以便用作sklearn函数的输入
X = np.array(X)
# 将字符串数据转换为数值数据
label_encoder = []
X_encoded = np.empty(X.shape)
for i, item in enumerate(X[0]):
if item.isdigit():
X_encoded[:, i] = X[:, i]
else:
label_encoder.append(preprocessing.LabelEncoder())
X_encoded[:, i] = label_encoder[-1].fit_transform(X[:, i])
X = X_encoded[:, :-1].astype(int)
y = X_encoded[:, -1].astype(int)
# 创建SVM分类器并使用线性核
classifier = OneVsOneClassifier(LinearSVC(random_state=0))
# 训练分类器
classifier.fit(X, y)
# 使用80/20拆分进行交叉验证以进行训练和测试,然后预测训练数据的输出
X_train, X_test, y_train, y_test = cross_validation.train_test_split(X, y, test_size=0.2, random_state=5)
classifier = OneVsOneClassifier(LinearSVC(random_state=0))
classifier.fit(X_train, y_train)
y_test_pred = classifier.predict(X_test)
# 计算分类器的F1得分
f1 = cross_validation.cross_val_score(classifier, X, y, scoring='f1_weighted', cv=3)
print("F1得分: " + str(round(100*f1.mean(), 2)) + "%")
# 对测试数据点进行预测
input_data = ['37', 'Private', '215646', 'HS-grad', '9', 'Never-married', 'Handlers-cleaners', 'Not-in-family', 'White', 'Male', '0', '0', '40', 'United-States']
# 使用之前创建的标签编码器对测试数据点进行编码
input_data_encoded = [-1] * len(input_data)
count = 0
for i, item in enumerate(input_data):
if item.isdigit():
input_data_encoded[i] = int(input_data[i])
else:
input_data_encoded[i] = int(label_encoder[count].transform([input_data[i]])[0])
count += 1
input_data_encoded = np.array(input_data_encoded)
# 使用分类器对编码的数据点进行预测并打印输出
predicted_class = classifier.predict([input_data_encoded])
print(label_encoder[-1].inverse_transform(predicted_class)[0])
运行结果:
F1 score: 76.01%
[[ 37 2 215646 11 9 4 5 1 4 1
0 0 40 38]]
<=50K
本专栏将系统学习机器学习核心技术,同时注重代码实践,欢迎关注!
作者 :计算小屋
个人主页 : 计算小屋的主页