使用TensorFlow的LinearClassifier和Panda的数据框构建SVM

6

我知道这个问题,但它是针对一个过时的函数。

假设我正在尝试预测一个人是否会访问国家“X”,已知他们已经访问的国家和他们的收入。

我有一个在pandas DataFrame中的训练数据集,格式如下:

  1. 每行代表不同的人员,与矩阵中的其他人员无关。
  2. 前10列都是国家名称,列中的值为二进制(如果他们访问了该国则为1,否则为0)。
  3. 第11列是他们的收入。它是一个连续的十进制变量。
  4. 最后,列12是另一个二进制表,说明他们是否访问了'X'。

因此,如果我的数据集中有100,000个人,则我有一个维度为100,000 x 12的数据框。我想正确地将其传递到使用tensorflow的线性分类器中。但是甚至不知道如何开始。

我正在尝试将数据传递到此函数

estimator = LinearClassifier(
    n_classes=n_classes, feature_columns=[sparse_column_a, 
 sparse_feature_a_x_sparse_feature_b], label_keys=label_keys)

如果有更好的建议可以使用哪种估算器,我会考虑尝试。

我正在传递数据:

df = pd.DataFrame(np.random.randint(0,2,size=(100, 12)), columns=list('ABCDEFGHIJKL'))
tf_val = tf.estimator.inputs.pandas_input_fn(X.iloc[:, 0:9], X.iloc[:, 11], shuffle=True)

然而,我不确定如何将这个输出适当地传递给分类器。我是否正确设置了这个问题?我并非来自数据科学背景,因此任何指导都将非常有帮助!

问题

  1. 第11列是协变量。因此,我认为它不能仅作为特征传递,对吗?
  2. 我如何将第11列纳入分类器中,因为第11列与第1到10列完全不同类型的特征。
  3. 至少,即使我忽略第11列,我该如何至少拟合第1到10列,并使用标签=第12列将其传递到分类器中?

(需要赏金的工作代码)


首先,你正在使用的TensorFlow LinearClassifier已经过时了,你应该改用这个。https://www.tensorflow.org/api_docs/python/tf/estimator/LinearClassifier - Laleh
你提到了“假设数据中没有人已经访问过‘X’”,然后有一列是他们是否访问了‘X’。这是否意味着这一列总是为零?还是为空的? - Laleh
抱歉,我的意思是这是我们想要预测的内容。 - Jonathan
2个回答

12

线性支持向量机

支持向量机是一种最大间隔分类器,即它最大化正类和负类之间分离的宽度或间隔。在二元分类的情况下,线性支持向量机的损失函数如下所示。

enter image description here

这可以从更一般的多类线性SVM损失(也称为铰链损失)中推导出来,如下所示(其中Δ=1)。

enter image description here enter image description here

注意:在上述所有方程中,权重向量w包括偏置b这个损失函数是怎么想出来的?让我们深入探讨一下。

enter image description here

上图显示了正类数据点和负类数据点之间的分离超平面(实线)。然而,可以有许多这样的分离超平面。SVM找到分离超平面,使得超平面到最近的正数据点和最近的负数据点的距离最大(点线表示)。
数学上,SVM找到权重向量w(包括偏差),使得

enter image description here

如果正类和负类的标签(y)分别为+1-1,那么支持向量机将找到w,使得

enter image description here

• 如果数据点在超平面的正确侧(被正确分类),那么{{执行某些操作}}。

enter image description here

• 如果数据点在错误的一侧(被错分),那么

enter image description here

因此,数据点的损失(一种衡量错误分类的指标)可以写成

enter image description here

正则化

如果一个权重向量w正确地分类数据(X),那么这个权重向量的任何倍数λw(其中λ>1)也将正确分类数据(零损失)。这是因为变换λW会拉伸所有得分幅度,因此也会拉伸它们的绝对差异。L2正则化通过将正则化损失添加到铰链损失中来惩罚大的权重。

enter image description here

例如,如果x=[1,1,1,1]并且有两个权重向量w1=[1,0,0,0]w2=[0.25,0.25,0.25,0.25]。那么dot(W1,x) =dot(w2,x) =1即两个权重向量导致相同的点积,因此相同的铰链损失。但是w1的L2惩罚为1.0,而w2的L2惩罚仅为0.25。因此,L2正则化更喜欢w2而不是w1。鼓励分类器将所有输入维度考虑在内,而不是少数输入维度非常强烈地考虑一些输入维度。这可以提高模型的泛化性能,并导致过度拟合降低。
L2惩罚导致SVM中的最大边距特性。如果将SVM表示为优化问题,则受限二次优化问题的广义拉格朗日形式如下:

enter image description here

现在我们知道线性SVM的损失函数,可以使用梯度下降(或其他优化器)来找到最小化损失的权重向量。

代码

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets

# Load Data
iris = datasets.load_iris()
X = iris.data[:, :2][iris.target != 2]
y = iris.target[iris.target != 2]

# Change labels to +1 and -1 
y = np.where(y==1, y, -1)

# Linear Model with L2 regularization
model = tf.keras.Sequential()
model.add(tf.keras.layers.Dense(1, activation='linear', kernel_regularizer=tf.keras.regularizers.l2()))

# Hinge loss
def hinge_loss(y_true, y_pred):    
    return tf.maximum(0., 1- y_true*y_pred)

# Train the model
model.compile(optimizer='adam', loss=hinge_loss)
model.fit(X, y,  epochs=50000, verbose=False)

# Plot the learned decision boundary 
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, 0.01),
                         np.arange(y_min, y_max, 0.01))
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
cs = plt.contourf(xx, yy, Z, cmap=plt.cm.coolwarm, alpha=0.8)
plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Set1)
plt.show()

enter image description here

SVM也可以表示为一个受限制的二次优化问题。这种表述的优点在于我们可以使用核技巧来对非线性可分数据进行分类(使用不同的核函数)。LIBSVM实现了序列最小优化(SMO)算法,用于核支持向量机(SVMs)。

代码

from sklearn.svm import SVC
# SVM with linear kernel
clf = SVC(kernel='linear')
clf.fit(X, y) 

# Plot the learned decision boundary 
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, 0.01),
                         np.arange(y_min, y_max, 0.01))
Z = model.predict(np.c_[xx.ravel(), yy.ravel()])
Z = Z.reshape(xx.shape)
cs = plt.contourf(xx, yy, Z, cmap=plt.cm.coolwarm, alpha=0.8)
plt.scatter(X[:, 0], X[:, 1], c=y, cmap=plt.cm.Set1)
plt.show() 

enter image description here

最终

您可以使用基于tf的线性SVM模型来解决问题陈述:

# Prepare Data 
# 10 Binary features
df = pd.DataFrame(np.random.randint(0,2,size=(1000, 10)))
# 1 floating value feature 
df[11] = np.random.uniform(0,100000, size=(1000))
# True Label 
df[12] = pd.DataFrame(np.random.randint(0, 2, size=(1000)))

# Convert data to zero mean unit variance 
scalar = StandardScaler().fit(df[df.columns.drop(12)])
X = scalar.transform(df[df.columns.drop(12)])
y = np.array(df[12])

# convert label to +1 and -1. Needed for hinge loss
y = np.where(y==1, +1, -1)

# Model 
model = tf.keras.Sequential()
model.add(tf.keras.layers.Dense(1, activation='linear', 
                                kernel_regularizer=tf.keras.regularizers.l2()))
# Hinge Loss
def my_loss(y_true, y_pred):    
    return tf.maximum(0., 1- y_true*y_pred)

# Train model 
model.compile(optimizer='adam', loss=my_loss)
model.fit(X, y,  epochs=100, verbose=True)

K-Fold交叉验证和预测

import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.model_selection import KFold
from sklearn.metrics import roc_curve, auc

# Load Data
iris = datasets.load_iris()
X = iris.data[:, :2][iris.target != 2]
y_ = iris.target[iris.target != 2]

# Change labels to +1 and -1 
y = np.where(y_==1, +1, -1)


# Hinge loss
def hinge_loss(y_true, y_pred):    
    return tf.maximum(0., 1- y_true*y_pred)

def get_model():
    # Linear Model with L2 regularization
    model = tf.keras.Sequential()
    model.add(tf.keras.layers.Dense(1, activation='linear', kernel_regularizer=tf.keras.regularizers.l2()))
    model.compile(optimizer='adam', loss=hinge_loss)
    return model

def sigmoid(x):
    return 1 / (1 + np.exp(-x))

predict = lambda model, x : sigmoid(model.predict(x).reshape(-1))
predict_class = lambda model, x : np.where(predict(model, x)>0.5, 1, 0)


kf = KFold(n_splits=2, shuffle=True)

# K Fold cross validation
best = (None, -1)

for i, (train_index, test_index) in enumerate(kf.split(X)):
    X_train, X_test = X[train_index], X[test_index]
    y_train, y_test = y[train_index], y[test_index]

    model = get_model()
    model.fit(X_train, y_train, epochs=5000, verbose=False, batch_size=128)
    y_pred = model.predict_classes(X_test)
    val = roc_auc_score(y_test, y_pred)    
    print ("CV Fold {0}: AUC: {1}".format(i+1, auc))
    if best[1] < val:
        best = (model, val)

# ROC Curve using the best model
y_score = predict(best[0], X)
fpr, tpr, _ = roc_curve(y_, y_score)
roc_auc = auc(fpr, tpr)
print (roc_auc)

# Plot ROC
plt.figure()
lw = 2
plt.plot(fpr, tpr, color='darkorange',
         lw=lw, label='ROC curve (area = %0.2f)' % roc_auc)
plt.plot([0, 1], [0, 1], color='navy', lw=lw, linestyle='--')
plt.xlim([0.0, 1.0])
plt.ylim([0.0, 1.05])
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.legend(loc="lower right")
plt.show()

# Make predictions
y_score = predict_class(best[0], X)

进行预测

由于模型的输出是线性的,我们需要将其归一化为概率以进行预测。如果是二分类问题,我们可以使用sigmoid函数;如果是多分类问题,则可以使用softmax函数。以下代码适用于二分类问题。

predict = lambda model, x : sigmoid(model.predict(x).reshape(-1))
predict_class = lambda model, x : np.where(predict(model, x)>0.5, 1, 0)

参考文献

  1. CS231n

更新 1:

为了使代码与 tf 2.0 兼容,y 的数据类型应与 X 相同。为此,在y = np.where(..... 行后添加 y = y.astype(np.float64)


非常感谢您的答复,信息量很大。有两个简短的澄清问题。我查看了验证参数,但它没有跨验证选项。在使用TF训练时是否可能实现这一点?我想加入AUC指标,但为此需要一个正确的验证方法。 - Jonathan
此外,为了使用AUC,预测值必须在[0 1]之间。但这些预测是线性的。有没有一种方式可以映射它们? - Jonathan
同时,关于如何预测我的情况的建议也会很有帮助。 - Jonathan
获奖了!非常感谢你! - Jonathan
1
@amarnathchatterjee 请查看答案中的 update 1 - mujjiga
显示剩余5条评论

1

由于您所有的特征都已经是数字,您可以直接使用它们。

df = pd.DataFrame(np.random.randint(0,2,size=(100, 12)), columns=list('ABCDEFGHIJKL'))
df['K'] = np.random.random(100)
nuemric_features = [tf.feature_column.numeric_column(column) for column in df.columns[:11]]
model = tf.estimator.LinearClassifier(feature_columns=nuemric_features)
tf_val = tf.estimator.inputs.pandas_input_fn(df.iloc[:,:11], df.iloc[:,11], shuffle=True)
model.train(input_fn=tf_val, steps=1000)

print(list(model.predict(input_fn=tf_val))[0])
{'logits': array([-1.7512109], dtype=float32), 'logistic': array([0.14789453], dtype=float32), 'probabilities': array([0.8521055 , 0.14789453], dtype=float32), 'class_ids': array([0]), 'classes': array([b'0'], dtype=object)}

预测输出的概率可能是您感兴趣的。您有两个概率值,一个代表目标为False,另一个代表True。
如果您想了解更多细节,请查看这篇关于使用TensorFlow进行二元分类的博客文章

对于列'K',我是否不需要将其引入为某种协变量?或者根据机器学习理论,最好将其作为一个特征包含进去? - Jonathan
你能否提供一些关于如何解释输出的说明?这将使得这个答案更加广泛地有用。 - Jonathan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接