{"action":"created","comment":{"url":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/comments/42491038","id":42491038,"diff_hunk":"@@ -0,0 +1,1248 @@\n+\"\"\"Multi-layer Perceptron\n+\"\"\"\n+\n+# Authors: Issam H. Laradji <issam.laradji@gmail.com>\n+#          Andreas Mueller\n+#          Jiyuan Qian\n+# Licence: BSD 3 clause\n+\n+import numpy as np\n+\n+from abc import ABCMeta, abstractmethod\n+from scipy.optimize import fmin_l_bfgs_b\n+import warnings\n+\n+from ..base import BaseEstimator, ClassifierMixin, RegressorMixin\n+from ._base import logistic, softmax\n+from ._base import ACTIVATIONS, DERIVATIVES, LOSS_FUNCTIONS\n+from ._stochastic_optimizers import stochastic_optimizers\n+from ..cross_validation import train_test_split\n+from ..externals import six\n+from ..preprocessing import LabelBinarizer\n+from ..utils import gen_batches, check_random_state\n+from ..utils import shuffle\n+from ..utils import check_array, check_X_y, column_or_1d\n+from ..utils import ConvergenceWarning\n+from ..utils.extmath import safe_sparse_dot\n+from ..utils.validation import check_is_fitted\n+from ..utils.multiclass import _check_partial_fit_first_call\n+\n+\n+_STOCHASTIC_ALGOS = ['sgd', 'adam']\n+\n+\n+def _pack(coefs_, intercepts_):\n+    \"\"\"Pack the parameters into a single vector.\"\"\"\n+    return np.hstack([l.ravel() for l in coefs_ + intercepts_])\n+\n+\n+class BaseMultilayerPerceptron(six.with_metaclass(ABCMeta, BaseEstimator)):\n+    \"\"\"Base class for MLP classification and regression.\n+\n+    Warning: This class should not be used directly.\n+    Use derived classes instead.\n+    \"\"\"\n+\n+    @abstractmethod\n+    def __init__(self, hidden_layer_sizes, activation, algorithm,\n+                 alpha, batch_size, learning_rate, learning_rate_init, power_t,\n+                 max_iter, loss, shuffle, random_state, tol, verbose,\n+                 warm_start, momentum, nesterovs_momentum, early_stopping,\n+                 validation_fraction, beta_1, beta_2, epsilon):\n+        self.activation = activation\n+        self.algorithm = algorithm\n+        self.alpha = alpha\n+        self.batch_size = batch_size\n+        self.learning_rate = learning_rate\n+        self.learning_rate_init = learning_rate_init\n+        self.power_t = power_t\n+        self.max_iter = max_iter\n+        self.loss = loss\n+        self.hidden_layer_sizes = hidden_layer_sizes\n+        self.shuffle = shuffle\n+        self.random_state = random_state\n+        self.tol = tol\n+        self.verbose = verbose\n+        self.warm_start = warm_start\n+        self.momentum = momentum\n+        self.nesterovs_momentum = nesterovs_momentum\n+        self.early_stopping = early_stopping\n+        self.validation_fraction = validation_fraction\n+        self.beta_1 = beta_1\n+        self.beta_2 = beta_2\n+        self.epsilon = epsilon\n+\n+    def _unpack(self, packed_parameters):\n+        \"\"\"Extract the coefficients and intercepts from packed_parameters.\"\"\"\n+        for i in range(self.n_layers_ - 1):\n+            start, end, shape = self._coef_indptr[i]\n+            self.coefs_[i] = np.reshape(packed_parameters[start:end], shape)\n+\n+            start, end = self._intercept_indptr[i]\n+            self.intercepts_[i] = packed_parameters[start:end]\n+\n+    def _forward_pass(self, activations, with_output_activation=True):\n+        \"\"\"Perform a forward pass on the network by computing the values\n+        of the neurons in the hidden layers and the output layer.\n+\n+        Parameters\n+        ----------\n+        activations: list, length = n_layers - 1\n+            The ith index of the list holds the values of the ith layer.\n+\n+        with_output_activation : bool, default True\n+            If True, the output passes through the output activation\n+            function, which is either the softmax function or the\n+            logistic function\n+        \"\"\"\n+        # Iterate over the hidden layers\n+        for i in range(self.n_layers_ - 1):\n+            activations[i + 1] = safe_sparse_dot(activations[i],\n+                                                 self.coefs_[i])\n+            activations[i + 1] += self.intercepts_[i]\n+\n+            # For the hidden layers\n+            if i + 1 != self.n_layers_ - 1:\n+                hidden_activation = ACTIVATIONS[self.activation]\n+                activations[i + 1] = hidden_activation(activations[i + 1])\n+\n+        # For the last layer\n+        if with_output_activation:\n+            output_activation = ACTIVATIONS[self.out_activation_]\n+            activations[i + 1] = output_activation(activations[i + 1])\n+\n+        return activations\n+\n+    def _compute_loss_grad(self, layer, n_samples, activations, deltas,\n+                           coef_grads, intercept_grads):\n+        \"\"\"Compute the loss gradient for the layer.\"\"\"\n+        coef_grads[layer] = safe_sparse_dot(activations[layer].T,\n+                                            deltas[layer])\n+        coef_grads[layer] += (self.alpha * self.coefs_[layer])\n+        coef_grads[layer] /= n_samples\n+\n+        intercept_grads[layer] = np.mean(deltas[layer], 0)\n+\n+        return coef_grads, intercept_grads\n+\n+    def _loss_grad_lbfgs(self, packed_coef_inter, X, y, activations, deltas,\n+                         coef_grads, intercept_grads):\n+        \"\"\"Compute the MLP loss function and its corresponding derivatives\n+        with respect to the different parameters given in the initialization.\n+\n+        Parameters\n+        ----------\n+        packed_parameters : array-like\n+            A vector comprising the  flattened coefficients and intercepts.\n+\n+        X : {array-like, sparse matrix}, shape (n_samples, n_features)\n+            The input data.\n+\n+        y : array-like, shape (n_samples,)\n+            The target values.\n+\n+        activations: list, length = n_layers - 1\n+            The ith index of the list holds the values of the ith layer.\n+\n+        deltas : list, length = n_layers - 1\n+            The ith index of the list holds the difference between the\n+            activations of the i + 1 layer and the backpropagated error.\n+\n+        coef_grad : list, length = n_layers - 1\n+            The ith index contains the amount of change used to update the\n+            coefficient parameters of the ith layer in an iteration.\n+\n+        intercept_grads : list, length = n_layers - 1\n+            The ith index contains the amount of change used to update the\n+            intercept parameters of the ith layer in an iteration.\n+\n+        Returns\n+        -------\n+        loss : float\n+        grad : array-like, shape (number of nodes of all layers,)\n+\n+        \"\"\"\n+        self._unpack(packed_coef_inter)\n+        loss, coef_grads, intercept_grads = self._backprop(X, y, activations,\n+                                                           deltas, coef_grads,\n+                                                           intercept_grads)\n+        self.n_iter_ += 1\n+        grad = _pack(coef_grads, intercept_grads)\n+        return loss, grad\n+\n+    def _backprop(self, X, y, activations, deltas, coef_grads,\n+                  intercept_grads):\n+        \"\"\"Compute the MLP loss function and its corresponding derivatives\n+        with respect to each parameter: weights and bias vectors.\n+\n+        Parameters\n+        ----------\n+        X : {array-like, sparse matrix}, shape (n_samples, n_features)\n+            The input data.\n+\n+        y : array-like, shape (n_samples,)\n+            The target values.\n+\n+        activations: list, length = n_layers - 1\n+             The ith index of the list holds the values of the ith layer.\n+\n+        deltas : list, length = n_layers - 1\n+            The ith index of the list holds the difference between the\n+            activations of the i + 1 layer and the backpropagated error.\n+            More specifically, deltas are gradients of loss with respect to z\n+            in each layer, where z = wx + b is the value before passed through\n+            activation function\n+\n+        coef_grad : list, length = n_layers - 1\n+            The ith index contains the amount of change used to update the\n+            coefficient parameters of the ith layer in an iteration.\n+\n+        intercept_grads : list, length = n_layers - 1\n+            The ith index contains the amount of change used to update the\n+            intercept parameters of the ith layer in an iteration.\n+\n+        Returns\n+        -------\n+        loss : float\n+        \"\"\"\n+        n_samples = X.shape[0]\n+\n+        # Forward propagate\n+        activations = self._forward_pass(activations)\n+\n+        # Get loss\n+        loss = LOSS_FUNCTIONS[self.loss](y, activations[-1])\n+        # Add L2 regularization term to loss\n+        values = np.sum(np.array([np.sum(s ** 2) for s in self.coefs_]))\n+        loss += (0.5 * self.alpha) * values / n_samples\n+\n+        # Backward propagate\n+        last = self.n_layers_ - 2\n+\n+        # The calculation of delta[last] here works with following\n+        # combinations of output activation and loss function:\n+        # sigmoid and binary cross entropy, softmax and categorical cross\n+        # entropy, and identity with squared loss\n+        diff = y - activations[-1]\n+        deltas[last] = -diff\n+\n+        # Compute gradient for the last layer\n+        coef_grads, intercept_grads = self._compute_loss_grad(\n+            last, n_samples, activations, deltas, coef_grads, intercept_grads)\n+\n+        # Iterate over the hidden layers\n+        for i in range(self.n_layers_ - 2, 0, -1):\n+            deltas[i - 1] = safe_sparse_dot(deltas[i],\n+                                            self.coefs_[i].T)\n+            derivative = DERIVATIVES[self.activation]\n+            deltas[i - 1] *= derivative(activations[i])\n+\n+            coef_grads, \\\n+                intercept_grads = self._compute_loss_grad(i - 1,\n+                                                          n_samples,\n+                                                          activations,\n+                                                          deltas,\n+                                                          coef_grads,\n+                                                          intercept_grads)\n+\n+        return loss, coef_grads, intercept_grads\n+\n+    def _initialize(self, y, layer_units):\n+        # set all attributes, allocate weights etc for first call\n+        # Initialize parameters\n+        self.n_iter_ = 0\n+        self.t_ = 0\n+        self.n_outputs_ = y.shape[1]\n+\n+        # Compute the number of layers\n+        self.n_layers_ = len(layer_units)\n+\n+        # Output for regression\n+        if not isinstance(self, ClassifierMixin):\n+            self.out_activation_ = 'identity'\n+        # Output for multi class\n+        elif self.label_binarizer_.y_type_ == 'multiclass':\n+            self.out_activation_ = 'softmax'\n+        # Output for binary class and multi-label\n+        else:\n+            self.out_activation_ = 'logistic'\n+            if self.loss == 'log_loss':\n+                self.loss = 'binary_log_loss'\n+\n+        # Initialize coefficient and intercept layers\n+        self.coefs_ = []\n+        self.intercepts_ = []\n+\n+        for i in range(self.n_layers_ - 1):\n+            rng = check_random_state(self.random_state)\n+            coef_init, intercept_init = self._init_coef(layer_units[i],\n+                                                        layer_units[i + 1],\n+                                                        rng)\n+            self.coefs_.append(coef_init)\n+            self.intercepts_.append(intercept_init)\n+\n+        if self.algorithm in _STOCHASTIC_ALGOS:\n+            self.loss_curve_ = []\n+            self._no_improvement_count = 0\n+            if self.early_stopping:\n+                self.validation_scores_ = []\n+                self.best_validation_score_ = -np.inf\n+            else:\n+                self.best_loss_ = np.inf\n+\n+    def _init_coef(self, fan_in, fan_out, rng):\n+        if self.activation == 'logistic':\n+            # Use the initialization method recommended by\n+            # Glorot et al.\n+            init_bound = np.sqrt(2. / (fan_in + fan_out))\n+        elif self.activation == 'tanh':\n+            init_bound = np.sqrt(6. / (fan_in + fan_out))\n+        elif self.activation == 'relu':\n+            # He et al instead maybe?\n+            init_bound = np.sqrt(6. / (fan_in + fan_out))\n+        else:\n+            # this was caught earlier, just to make sure\n+            raise ValueError(\"Unknown activation function %s\" %\n+                             self.activation)\n+\n+        coef_init = rng.uniform(-init_bound, init_bound, (fan_in, fan_out))\n+        intercept_init = rng.uniform(-init_bound, init_bound, fan_out)\n+        return coef_init, intercept_init\n+\n+    def _fit(self, X, y, incremental=False):\n+        # Make sure self.hidden_layer_sizes is a list\n+        hidden_layer_sizes = self.hidden_layer_sizes\n+        if not hasattr(hidden_layer_sizes, \"__iter__\"):\n+            hidden_layer_sizes = [hidden_layer_sizes]\n+        hidden_layer_sizes = list(hidden_layer_sizes)\n+\n+        # Validate input parameters.\n+        self._validate_hyperparameters()\n+        if np.any(np.array(hidden_layer_sizes) <= 0):\n+            raise ValueError(\"hidden_layer_sizes must be > 0, got %s.\" %\n+                             hidden_layer_sizes)\n+\n+        X, y = self._validate_input(X, y, incremental)\n+        n_samples, n_features = X.shape\n+\n+        # Ensure y is 2D\n+        if y.ndim == 1:\n+            y = y.reshape((-1, 1))\n+\n+        self.n_outputs_ = y.shape[1]\n+\n+        layer_units = ([n_features] + hidden_layer_sizes +\n+                       [self.n_outputs_])\n+\n+        if not hasattr(self, 'coefs_') or (not self.warm_start and not\n+                                           incremental):\n+            # First time training the model\n+            self._initialize(y, layer_units)\n+\n+        # l-bfgs does not support mini-batches\n+        if self.algorithm == 'l-bfgs':\n+            batch_size = n_samples\n+        else:\n+            batch_size = np.clip(self.batch_size, 1, n_samples)","path":"sklearn/neural_network/multilayer_perceptron.py","position":346,"original_position":346,"commit_id":"7264be70a4cfcdbcee7ff37e48846e2f8fa6cda3","original_commit_id":"7264be70a4cfcdbcee7ff37e48846e2f8fa6cda3","user":{"login":"kastnerkyle","id":1563421,"avatar_url":"https://avatars.githubusercontent.com/u/1563421?v=3","gravatar_id":"","url":"https://api.github.com/users/kastnerkyle","html_url":"https://github.com/kastnerkyle","followers_url":"https://api.github.com/users/kastnerkyle/followers","following_url":"https://api.github.com/users/kastnerkyle/following{/other_user}","gists_url":"https://api.github.com/users/kastnerkyle/gists{/gist_id}","starred_url":"https://api.github.com/users/kastnerkyle/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kastnerkyle/subscriptions","organizations_url":"https://api.github.com/users/kastnerkyle/orgs","repos_url":"https://api.github.com/users/kastnerkyle/repos","events_url":"https://api.github.com/users/kastnerkyle/events{/privacy}","received_events_url":"https://api.github.com/users/kastnerkyle/received_events","type":"User","site_admin":false},"body":"might want to warn if we change the batch_size","created_at":"2015-10-20T12:57:20Z","updated_at":"2015-10-20T12:57:20Z","html_url":"https://github.com/scikit-learn/scikit-learn/pull/5214#discussion_r42491038","pull_request_url":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/5214","_links":{"self":{"href":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/comments/42491038"},"html":{"href":"https://github.com/scikit-learn/scikit-learn/pull/5214#discussion_r42491038"},"pull_request":{"href":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/5214"}}},"pull_request":{"url":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/5214","id":44245444,"html_url":"https://github.com/scikit-learn/scikit-learn/pull/5214","diff_url":"https://github.com/scikit-learn/scikit-learn/pull/5214.diff","patch_url":"https://github.com/scikit-learn/scikit-learn/pull/5214.patch","issue_url":"https://api.github.com/repos/scikit-learn/scikit-learn/issues/5214","number":5214,"state":"open","locked":false,"title":"[MRG + 1] Mlp with adam, nesterov's momentum, early stopping","user":{"login":"glennq","id":5950453,"avatar_url":"https://avatars.githubusercontent.com/u/5950453?v=3","gravatar_id":"","url":"https://api.github.com/users/glennq","html_url":"https://github.com/glennq","followers_url":"https://api.github.com/users/glennq/followers","following_url":"https://api.github.com/users/glennq/following{/other_user}","gists_url":"https://api.github.com/users/glennq/gists{/gist_id}","starred_url":"https://api.github.com/users/glennq/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/glennq/subscriptions","organizations_url":"https://api.github.com/users/glennq/orgs","repos_url":"https://api.github.com/users/glennq/repos","events_url":"https://api.github.com/users/glennq/events{/privacy}","received_events_url":"https://api.github.com/users/glennq/received_events","type":"User","site_admin":false},"body":"Based on #3204 and #3939.\r\n\r\nCode and results for timing and performance benchmarks on MNIST, 20 New Groups and RCV1 can be found at https://gist.github.com/glennq/44ca8b66770430ee10f9\r\n\r\nBasically my observation is that adam performs consistently well. Early stopping cannot help to improve performance for adam, but can reduce training time significantly. In other updating schemes, adaptive learning rate (divide by 5 if not improving) with nesterov's momentum also lines just after adam, followed by constant learning rate with nesterov's momentum. The problem with adaptive learning rate though, is that training time is significantly longer.\r\n\r\nAlso, there are cases when early stopping can increase training time, which I think is due to the fact that the heuristic we use (stop if loss not improving over best loss by tol for more than two consecutive iterations) can be affected if training loss has large variation but validation score is more smoothly improving. \r\nThis affects update schemes using momentum, but it seems to be not a problem for adam.\r\n\r\nThe following is a visualized classifier comparison modified from http://scikit-learn.org/dev/auto_examples/classification/plot_classifier_comparison.html\r\n\r\n![figure_1](https://cloud.githubusercontent.com/assets/5950453/9690764/60b1c5c8-530c-11e5-83f7-1095741a170c.png)\r\n\r\nI'll still work on benchmarking small datasets such as digits and iris, and probably plot training/validation loss for each iterations for more insights.","created_at":"2015-09-04T18:04:07Z","updated_at":"2015-10-20T12:57:20Z","closed_at":null,"merged_at":null,"merge_commit_sha":"661d38923fc5048b2c66aa0a905789c5c9994191","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/5214/commits","review_comments_url":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/5214/comments","review_comment_url":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/comments{/number}","comments_url":"https://api.github.com/repos/scikit-learn/scikit-learn/issues/5214/comments","statuses_url":"https://api.github.com/repos/scikit-learn/scikit-learn/statuses/7264be70a4cfcdbcee7ff37e48846e2f8fa6cda3","head":{"label":"glennq:mlp_refactoring","ref":"mlp_refactoring","sha":"7264be70a4cfcdbcee7ff37e48846e2f8fa6cda3","user":{"login":"glennq","id":5950453,"avatar_url":"https://avatars.githubusercontent.com/u/5950453?v=3","gravatar_id":"","url":"https://api.github.com/users/glennq","html_url":"https://github.com/glennq","followers_url":"https://api.github.com/users/glennq/followers","following_url":"https://api.github.com/users/glennq/following{/other_user}","gists_url":"https://api.github.com/users/glennq/gists{/gist_id}","starred_url":"https://api.github.com/users/glennq/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/glennq/subscriptions","organizations_url":"https://api.github.com/users/glennq/orgs","repos_url":"https://api.github.com/users/glennq/repos","events_url":"https://api.github.com/users/glennq/events{/privacy}","received_events_url":"https://api.github.com/users/glennq/received_events","type":"User","site_admin":false},"repo":{"id":41526478,"name":"scikit-learn","full_name":"glennq/scikit-learn","owner":{"login":"glennq","id":5950453,"avatar_url":"https://avatars.githubusercontent.com/u/5950453?v=3","gravatar_id":"","url":"https://api.github.com/users/glennq","html_url":"https://github.com/glennq","followers_url":"https://api.github.com/users/glennq/followers","following_url":"https://api.github.com/users/glennq/following{/other_user}","gists_url":"https://api.github.com/users/glennq/gists{/gist_id}","starred_url":"https://api.github.com/users/glennq/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/glennq/subscriptions","organizations_url":"https://api.github.com/users/glennq/orgs","repos_url":"https://api.github.com/users/glennq/repos","events_url":"https://api.github.com/users/glennq/events{/privacy}","received_events_url":"https://api.github.com/users/glennq/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/glennq/scikit-learn","description":"scikit-learn main repo","fork":true,"url":"https://api.github.com/repos/glennq/scikit-learn","forks_url":"https://api.github.com/repos/glennq/scikit-learn/forks","keys_url":"https://api.github.com/repos/glennq/scikit-learn/keys{/key_id}","collaborators_url":"https://api.github.com/repos/glennq/scikit-learn/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/glennq/scikit-learn/teams","hooks_url":"https://api.github.com/repos/glennq/scikit-learn/hooks","issue_events_url":"https://api.github.com/repos/glennq/scikit-learn/issues/events{/number}","events_url":"https://api.github.com/repos/glennq/scikit-learn/events","assignees_url":"https://api.github.com/repos/glennq/scikit-learn/assignees{/user}","branches_url":"https://api.github.com/repos/glennq/scikit-learn/branches{/branch}","tags_url":"https://api.github.com/repos/glennq/scikit-learn/tags","blobs_url":"https://api.github.com/repos/glennq/scikit-learn/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/glennq/scikit-learn/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/glennq/scikit-learn/git/refs{/sha}","trees_url":"https://api.github.com/repos/glennq/scikit-learn/git/trees{/sha}","statuses_url":"https://api.github.com/repos/glennq/scikit-learn/statuses/{sha}","languages_url":"https://api.github.com/repos/glennq/scikit-learn/languages","stargazers_url":"https://api.github.com/repos/glennq/scikit-learn/stargazers","contributors_url":"https://api.github.com/repos/glennq/scikit-learn/contributors","subscribers_url":"https://api.github.com/repos/glennq/scikit-learn/subscribers","subscription_url":"https://api.github.com/repos/glennq/scikit-learn/subscription","commits_url":"https://api.github.com/repos/glennq/scikit-learn/commits{/sha}","git_commits_url":"https://api.github.com/repos/glennq/scikit-learn/git/commits{/sha}","comments_url":"https://api.github.com/repos/glennq/scikit-learn/comments{/number}","issue_comment_url":"https://api.github.com/repos/glennq/scikit-learn/issues/comments{/number}","contents_url":"https://api.github.com/repos/glennq/scikit-learn/contents/{+path}","compare_url":"https://api.github.com/repos/glennq/scikit-learn/compare/{base}...{head}","merges_url":"https://api.github.com/repos/glennq/scikit-learn/merges","archive_url":"https://api.github.com/repos/glennq/scikit-learn/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/glennq/scikit-learn/downloads","issues_url":"https://api.github.com/repos/glennq/scikit-learn/issues{/number}","pulls_url":"https://api.github.com/repos/glennq/scikit-learn/pulls{/number}","milestones_url":"https://api.github.com/repos/glennq/scikit-learn/milestones{/number}","notifications_url":"https://api.github.com/repos/glennq/scikit-learn/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/glennq/scikit-learn/labels{/name}","releases_url":"https://api.github.com/repos/glennq/scikit-learn/releases{/id}","created_at":"2015-08-28T04:12:24Z","updated_at":"2015-08-28T04:12:30Z","pushed_at":"2015-10-08T21:00:10Z","git_url":"git://github.com/glennq/scikit-learn.git","ssh_url":"git@github.com:glennq/scikit-learn.git","clone_url":"https://github.com/glennq/scikit-learn.git","svn_url":"https://github.com/glennq/scikit-learn","homepage":"http://scikit-learn.sourceforge.net","size":89568,"stargazers_count":0,"watchers_count":0,"language":"Python","has_issues":false,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":0,"mirror_url":null,"open_issues_count":0,"forks":0,"open_issues":0,"watchers":0,"default_branch":"master"}},"base":{"label":"scikit-learn:master","ref":"master","sha":"735dd1fdca26d210b6e2aa0fe907397297220c32","user":{"login":"scikit-learn","id":365630,"avatar_url":"https://avatars.githubusercontent.com/u/365630?v=3","gravatar_id":"","url":"https://api.github.com/users/scikit-learn","html_url":"https://github.com/scikit-learn","followers_url":"https://api.github.com/users/scikit-learn/followers","following_url":"https://api.github.com/users/scikit-learn/following{/other_user}","gists_url":"https://api.github.com/users/scikit-learn/gists{/gist_id}","starred_url":"https://api.github.com/users/scikit-learn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scikit-learn/subscriptions","organizations_url":"https://api.github.com/users/scikit-learn/orgs","repos_url":"https://api.github.com/users/scikit-learn/repos","events_url":"https://api.github.com/users/scikit-learn/events{/privacy}","received_events_url":"https://api.github.com/users/scikit-learn/received_events","type":"Organization","site_admin":false},"repo":{"id":843222,"name":"scikit-learn","full_name":"scikit-learn/scikit-learn","owner":{"login":"scikit-learn","id":365630,"avatar_url":"https://avatars.githubusercontent.com/u/365630?v=3","gravatar_id":"","url":"https://api.github.com/users/scikit-learn","html_url":"https://github.com/scikit-learn","followers_url":"https://api.github.com/users/scikit-learn/followers","following_url":"https://api.github.com/users/scikit-learn/following{/other_user}","gists_url":"https://api.github.com/users/scikit-learn/gists{/gist_id}","starred_url":"https://api.github.com/users/scikit-learn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scikit-learn/subscriptions","organizations_url":"https://api.github.com/users/scikit-learn/orgs","repos_url":"https://api.github.com/users/scikit-learn/repos","events_url":"https://api.github.com/users/scikit-learn/events{/privacy}","received_events_url":"https://api.github.com/users/scikit-learn/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/scikit-learn/scikit-learn","description":"scikit-learn: machine learning in Python","fork":false,"url":"https://api.github.com/repos/scikit-learn/scikit-learn","forks_url":"https://api.github.com/repos/scikit-learn/scikit-learn/forks","keys_url":"https://api.github.com/repos/scikit-learn/scikit-learn/keys{/key_id}","collaborators_url":"https://api.github.com/repos/scikit-learn/scikit-learn/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/scikit-learn/scikit-learn/teams","hooks_url":"https://api.github.com/repos/scikit-learn/scikit-learn/hooks","issue_events_url":"https://api.github.com/repos/scikit-learn/scikit-learn/issues/events{/number}","events_url":"https://api.github.com/repos/scikit-learn/scikit-learn/events","assignees_url":"https://api.github.com/repos/scikit-learn/scikit-learn/assignees{/user}","branches_url":"https://api.github.com/repos/scikit-learn/scikit-learn/branches{/branch}","tags_url":"https://api.github.com/repos/scikit-learn/scikit-learn/tags","blobs_url":"https://api.github.com/repos/scikit-learn/scikit-learn/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/scikit-learn/scikit-learn/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/scikit-learn/scikit-learn/git/refs{/sha}","trees_url":"https://api.github.com/repos/scikit-learn/scikit-learn/git/trees{/sha}","statuses_url":"https://api.github.com/repos/scikit-learn/scikit-learn/statuses/{sha}","languages_url":"https://api.github.com/repos/scikit-learn/scikit-learn/languages","stargazers_url":"https://api.github.com/repos/scikit-learn/scikit-learn/stargazers","contributors_url":"https://api.github.com/repos/scikit-learn/scikit-learn/contributors","subscribers_url":"https://api.github.com/repos/scikit-learn/scikit-learn/subscribers","subscription_url":"https://api.github.com/repos/scikit-learn/scikit-learn/subscription","commits_url":"https://api.github.com/repos/scikit-learn/scikit-learn/commits{/sha}","git_commits_url":"https://api.github.com/repos/scikit-learn/scikit-learn/git/commits{/sha}","comments_url":"https://api.github.com/repos/scikit-learn/scikit-learn/comments{/number}","issue_comment_url":"https://api.github.com/repos/scikit-learn/scikit-learn/issues/comments{/number}","contents_url":"https://api.github.com/repos/scikit-learn/scikit-learn/contents/{+path}","compare_url":"https://api.github.com/repos/scikit-learn/scikit-learn/compare/{base}...{head}","merges_url":"https://api.github.com/repos/scikit-learn/scikit-learn/merges","archive_url":"https://api.github.com/repos/scikit-learn/scikit-learn/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/scikit-learn/scikit-learn/downloads","issues_url":"https://api.github.com/repos/scikit-learn/scikit-learn/issues{/number}","pulls_url":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls{/number}","milestones_url":"https://api.github.com/repos/scikit-learn/scikit-learn/milestones{/number}","notifications_url":"https://api.github.com/repos/scikit-learn/scikit-learn/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/scikit-learn/scikit-learn/labels{/name}","releases_url":"https://api.github.com/repos/scikit-learn/scikit-learn/releases{/id}","created_at":"2010-08-17T09:43:38Z","updated_at":"2015-10-20T07:29:52Z","pushed_at":"2015-10-20T12:56:13Z","git_url":"git://github.com/scikit-learn/scikit-learn.git","ssh_url":"git@github.com:scikit-learn/scikit-learn.git","clone_url":"https://github.com/scikit-learn/scikit-learn.git","svn_url":"https://github.com/scikit-learn/scikit-learn","homepage":"http://scikit-learn.org","size":138656,"stargazers_count":7916,"watchers_count":7916,"language":"Python","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":false,"forks_count":4712,"mirror_url":null,"open_issues_count":770,"forks":4712,"open_issues":770,"watchers":7916,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/5214"},"html":{"href":"https://github.com/scikit-learn/scikit-learn/pull/5214"},"issue":{"href":"https://api.github.com/repos/scikit-learn/scikit-learn/issues/5214"},"comments":{"href":"https://api.github.com/repos/scikit-learn/scikit-learn/issues/5214/comments"},"review_comments":{"href":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/5214/comments"},"review_comment":{"href":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/scikit-learn/scikit-learn/pulls/5214/commits"},"statuses":{"href":"https://api.github.com/repos/scikit-learn/scikit-learn/statuses/7264be70a4cfcdbcee7ff37e48846e2f8fa6cda3"}}}}