使用 tensorflow 的 LinearClassifier 和 Panda 的 Dataframes 构建 SVM

Posted

技术标签:

【中文标题】使用 tensorflow 的 LinearClassifier 和 Panda 的 Dataframes 构建 SVM【英文标题】:Building SVM with tensorflow's LinearClassifier and Panda's Dataframes 【发布时间】:2019-08-20 20:03:48 【问题描述】:

我知道这个question,但它是一个过时的功能。

假设我想根据一个人已经访问过的国家和收入来预测他们是否会访问国家“X”。

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

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

所以本质上,如果我的数据集中有 100,000 人,那么我就有一个维度为 100,000 x 12 的数据框。我希望能够使用 tensorflow 正确地将其传递给线性分类器。但甚至不知道如何解决这个问题。

我正在尝试将数据传递到这个function

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)

但是,我不确定如何获取此输出并正确传递给分类器。我是否正确设置了问题?我不是来自数据科学背景,所以任何指导都会非常有帮助!

疑虑

    第 11 列是协变量。因此,我不认为它可以作为一个特性传入,不是吗? 如何将第 11 列也合并到分类器中,因为第 11 列是与第 1 到 10 列完全不同的特征类型。 至少,即使我忽略第 11 列,我如何至少适合第 1 到第 10 列,标签 = 第 12 列并将其传递给分类器?

(赏金所需的工作代码)

【问题讨论】:

第一点,您使用的张量流 LinearClassifier 已弃用,您应该改用这个。tensorflow.org/api_docs/python/tf/estimator/LinearClassifier 您提到“假设数据中没有人已经访问过'X'”,然后如果他们访问过'X',则会有一列。这是否意味着该列始终为零?空吗? 抱歉,我的意思是这就是我们想要预测的结果。 【参考方案1】:

线性支持向量机

SVM 是一个最大边距分类器,即它最大化将正类与负类分开的宽度或边距。线性SVM在二分类情况下的损失函数如下。

它可以从下面所示的更广义的多类线性 SVM 损失(也称为铰链损失)推导出来(Δ = 1)。

注意:在上述所有等式中,权重向量w 包括偏差b

到底是怎么有人想出这个损失的?让我们深入挖掘。

上图显示了属于正类的数据点与属于负类的数据点通过分离超平面(显示为实线)分开。然而,可以有许多这样的分离超平面。 SVM 找到分离超平面,使得超平面到最近的正数据点和最近的负数据点的距离最大(显示为虚线)。

在数学上,SVM 找到权重向量 w(包括偏差)使得

如果+ve类和-ve类的标签(y)分别是+1-1,那么SVM找到w这样

• 如果数据点位于超平面的正确一侧(正确分类),则

• 如果数据点位于错误的一侧(未分类),则

所以一个数据点的损失,它是一个未分类的度量,可以写成

正则化

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

例如,如果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 表示为优化问题,则约束二次优化问题的广义拉格朗日形式如下

现在我们知道了线性 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()

SVM 也可以表示为一个有约束的二次优化问题。这个公式的优点是我们可以使用核技巧来分类非线性可分数据(使用不同的核)。 LIBSVM 为核化支持向量机 (SVM) 实现了序列最小优化 (SMO) 算法。

代码

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() 

终于

可用于问题陈述的使用 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)

参考文献

    CS231n

更新 1:

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

【讨论】:

非常感谢您的回答,非常有用。两个快速澄清。我查看了验证参数,它没有交叉验证选项。当我训练(关于 TF)时这可能吗?我想合并 AUC 指标,但为此我需要一个适当的验证方法。 另外,为了使用 AUC,预测值必须在 [0 1] 之间。但这些预测是线性的。有什么方法可以映射它们吗? 另外一行关于如何预测我的情况会有所帮助。 @Jonathan,要进行预测,您可以使用 sigmod/softmax 将其转换为概率。用附加信息更新了答案。 @amarnathchatterjee 请在​​答案中查看update 1【参考方案2】:

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

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)

预测输出的概率最有可能是您感兴趣的。您有两种概率,一种用于目标为 Flase,另一种用于 True。

如果您想了解更多详细信息,请查看这篇关于 TensorFlow 二进制分类的精彩 blog-post。

【讨论】:

对于列'K',我不需要将它作为某种形式的协变量引入吗?或者根据 ML 理论,最好将其作为一个特征包含在内? 您能否解释一下如何解释输出?这真的会让这个答案更广泛地有用。

以上是关于使用 tensorflow 的 LinearClassifier 和 Panda 的 Dataframes 构建 SVM的主要内容,如果未能解决你的问题,请参考以下文章

干货使用TensorFlow官方Java API调用TensorFlow模型(附代码)

tensorflow 怎么设置成cpu运行

如何理解TensorFlow中的tensor

tensorflow 为啥用训练好的数据出来的概率不变

如何让 Tensorflow Profiler 在 Tensorflow 2.5 中使用“tensorflow-macos”和“tensorflow-metal”工作

tensorflow怎么gpu加速