<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Data poisoning attack
In this notebook, we use a convex optimization layer to perform a data poisoning attack; i.e., we show how to perturb the data used to train a logistic regression classifier so as to maximally increase the test loss. This example is also presented in section 6.1 of the paper Differentiable convex optimization layers.
Step1:  We are given training data $(x_i, y_i){i=1}^{N}$,
where $x_i\in\mathbf{R}^n$ are feature vectors and $y_i\in{0,1}$ are the labels.
Suppose we fit a model for this classification problem by solving
\begin{equation}
\begin{array}{ll}
\mbox{minimize} & \frac{1}{N}\sum{i=1}^N \ell(\theta; x_i, y_i) + r(\theta),
\end{array}
\label{eq
Step2:  Assume that our training data is subject to a data poisoning attack,
before it is supplied to us. The adversary has full knowledge of our modeling
choice, meaning that they know the form of the optimization problem above, and seeks
to perturb the data to maximally increase our loss on the test
set, to which they also have access. The adversary is permitted to apply an
additive perturbation $\delta_i \in \mathbf{R}^n$ to each of the training points $x_i$,
with the perturbations satisfying $\|\delta_i\|_\infty \leq 0.01$.
Let $\theta^\star$ be optimal.
The gradient of
the test loss with respect to a training data point, $\nabla_{x_i}
\mathcal{L}^{\mathrm{test}}(\theta^\star)$, gives the direction
in which the point should be moved to achieve the greatest
increase in test loss. Hence, one reasonable adversarial policy is to set $x_i
Step3:  Below, we plot the gradient of the test loss with respect to the training data points. The blue and orange points are training data, belonging to different classes. The red line is the hyperplane learned by fitting the the model, while the blue line is the hyperplane that minimizes the test loss. The gradients are visualized as black lines, attached to the data points. Moving the points in the gradient directions torques the learned hyperplane away from the optimal hyperplane for the test set.
<ASSISTANT_TASK:>
Python Code:
import cvxpy as cp
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
from cvxpylayers.tensorflow.cvxpylayer import CvxpyLayer
Explanation: Data poisoning attack
In this notebook, we use a convex optimization layer to perform a data poisoning attack; i.e., we show how to perturb the data used to train a logistic regression classifier so as to maximally increase the test loss. This example is also presented in section 6.1 of the paper Differentiable convex optimization layers.
End of explanation
from sklearn.datasets import make_blobs
from sklearn.model_selection import train_test_split
tf.random.set_seed(0)
np.random.seed(0)
n = 2
N = 60
X, y = make_blobs(N, n, centers=np.array([[2, 2], [-2, -2]]), cluster_std=3)
Xtrain, Xtest, ytrain, ytest = train_test_split(X, y, test_size=.5)
Xtrain, Xtest, ytrain, ytest = map(
    tf.constant, [Xtrain, Xtest, ytrain, ytest])
m = Xtrain.shape[0]
lambda1_tf = tf.constant([[0.1]], dtype=tf.float64)
lambda2_tf = tf.constant([[0.1]], dtype=tf.float64)
a = cp.Variable((n, 1))
b = cp.Variable((1, 1))
lambda1 = cp.Parameter((1, 1), nonneg=True)
lambda2 = cp.Parameter((1, 1), nonneg=True)
X = cp.Parameter((m, n))
Y = ytrain.numpy()[:, np.newaxis]
log_likelihood = (1. / m) * cp.sum(
    cp.multiply(Y, X @ a + b) -
    cp.log_sum_exp(cp.hstack([np.zeros((m, 1)), X @ a + b]).T, axis=0,
                   keepdims=True).T
)
regularization = - lambda1 * cp.norm(a, 1) - lambda2 * cp.sum_squares(a)
prob = cp.Problem(cp.Maximize(log_likelihood + regularization))
fit_logreg = CvxpyLayer(prob, [X, lambda1, lambda2], [a, b])
Explanation: We are given training data $(x_i, y_i){i=1}^{N}$,
where $x_i\in\mathbf{R}^n$ are feature vectors and $y_i\in{0,1}$ are the labels.
Suppose we fit a model for this classification problem by solving
\begin{equation}
\begin{array}{ll}
\mbox{minimize} & \frac{1}{N}\sum{i=1}^N \ell(\theta; x_i, y_i) + r(\theta),
\end{array}
\label{eq:trainlinear}
\end{equation}
where the loss function $\ell(\theta; x_i, y_i)$ is convex in $\theta \in \mathbf{R}^n$ and $r(\theta)$ is a convex
regularizer. We hope that the test loss $\mathcal{L}^{\mathrm{test}}(\theta) =
\frac{1}{M}\sum_{i=1}^M \ell(\theta; \tilde x_i, \tilde y_i)$ is small, where
$(\tilde x_i, \tilde y_i)_{i=1}^{M}$ is our test set. In this  example, we use the logistic loss
\begin{equation}
\ell(\theta; x_i, y_i) = \log(1 + \exp(\beta^Tx_i + b)) - y_i(\beta^Tx_i + b)
\end{equation}
with elastic net regularizaiton
\begin{equation}
r(\theta) = 0.1\|\beta\|_1 + 0.1\|\beta\|_2^2.
\end{equation}
End of explanation
from sklearn.linear_model import LogisticRegression
loss = tf.keras.losses.BinaryCrossentropy()
with tf.GradientTape() as tape:
    tape.watch(Xtrain)
    # Apply the layer
    slope, intercept = fit_logreg(Xtrain, lambda1_tf, lambda2_tf)
    # 30 is scale factor so visualization is pretty
    test_loss = 30 * loss(ytest, Xtest @ slope + intercept)
# Compute the gradient of the test loss with respect to the training data
Xtrain_grad = tape.gradient(test_loss, Xtrain)
Explanation: Assume that our training data is subject to a data poisoning attack,
before it is supplied to us. The adversary has full knowledge of our modeling
choice, meaning that they know the form of the optimization problem above, and seeks
to perturb the data to maximally increase our loss on the test
set, to which they also have access. The adversary is permitted to apply an
additive perturbation $\delta_i \in \mathbf{R}^n$ to each of the training points $x_i$,
with the perturbations satisfying $\|\delta_i\|_\infty \leq 0.01$.
Let $\theta^\star$ be optimal.
The gradient of
the test loss with respect to a training data point, $\nabla_{x_i}
\mathcal{L}^{\mathrm{test}}(\theta^\star)$, gives the direction
in which the point should be moved to achieve the greatest
increase in test loss. Hence, one reasonable adversarial policy is to set $x_i
:= x_i +
.01\mathrm{sign}(\nabla_{x_i}\mathcal{L}^{\mathrm{test}}(\theta^\star))$. The
quantity $0.01\sum_{i=1}^N \|\nabla_{x_i}
\mathcal{L}^{\mathrm{test}}(\theta^\star)\|_1$ is the predicted increase in
our test loss due to the poisoning.
End of explanation
lr = LogisticRegression(solver='lbfgs')
lr.fit(Xtest.numpy(), ytest.numpy())
beta_train = slope.numpy().flatten()
beta_test = lr.coef_.flatten()
b_train = intercept[0, 0].numpy()
b_test = lr.intercept_[0]
hyperplane = lambda x, beta, b: - (b + beta[0] * x) / beta[1]
Xtrain_np = Xtrain.numpy()
Xtrain_grad_np = Xtrain_grad.numpy()
ytrain_np = ytrain.numpy().astype(np.bool)
plt.figure()
plt.scatter(Xtrain_np[ytrain_np, 0], Xtrain_np[ytrain_np, 1], s=25)
plt.scatter(Xtrain_np[~ytrain_np, 0], Xtrain_np[~ytrain_np, 1], s=25)
for i in range(m):
    plt.arrow(Xtrain_np[i, 0], Xtrain_np[i, 1],
              Xtrain_grad_np[i, 0], Xtrain_grad_np[i, 1], color='black')
plt.xlim(-8, 8)
plt.ylim(-8, 8)
plt.plot(np.linspace(-8, 8, 100),
         [hyperplane(x, beta_train, b_train)
          for x in np.linspace(-8, 8, 100)], color='red', label='train')
plt.plot(np.linspace(-8, 8, 100),
         [hyperplane(x, beta_test, b_test)
         for x in np.linspace(-8, 8, 100)], color='blue', label='test')
plt.legend()
plt.show()
Explanation: Below, we plot the gradient of the test loss with respect to the training data points. The blue and orange points are training data, belonging to different classes. The red line is the hyperplane learned by fitting the the model, while the blue line is the hyperplane that minimizes the test loss. The gradients are visualized as black lines, attached to the data points. Moving the points in the gradient directions torques the learned hyperplane away from the optimal hyperplane for the test set.
End of explanation
<END_TASK>