{"action":"created","issue":{"url":"https://api.github.com/repos/scikit-learn/scikit-learn/issues/8570","repository_url":"https://api.github.com/repos/scikit-learn/scikit-learn","labels_url":"https://api.github.com/repos/scikit-learn/scikit-learn/issues/8570/labels{/name}","comments_url":"https://api.github.com/repos/scikit-learn/scikit-learn/issues/8570/comments","events_url":"https://api.github.com/repos/scikit-learn/scikit-learn/issues/8570/events","html_url":"https://github.com/scikit-learn/scikit-learn/issues/8570","id":213310937,"number":8570,"title":"Memory leak in SGDClassifier","user":{"login":"LouisAmon","id":6024791,"avatar_url":"https://avatars1.githubusercontent.com/u/6024791?v=3","gravatar_id":"","url":"https://api.github.com/users/LouisAmon","html_url":"https://github.com/LouisAmon","followers_url":"https://api.github.com/users/LouisAmon/followers","following_url":"https://api.github.com/users/LouisAmon/following{/other_user}","gists_url":"https://api.github.com/users/LouisAmon/gists{/gist_id}","starred_url":"https://api.github.com/users/LouisAmon/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/LouisAmon/subscriptions","organizations_url":"https://api.github.com/users/LouisAmon/orgs","repos_url":"https://api.github.com/users/LouisAmon/repos","events_url":"https://api.github.com/users/LouisAmon/events{/privacy}","received_events_url":"https://api.github.com/users/LouisAmon/received_events","type":"User","site_admin":false},"labels":[{"id":12995755,"url":"https://api.github.com/repos/scikit-learn/scikit-learn/labels/Bug","name":"Bug","color":"e10c02","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":2,"created_at":"2017-03-10T11:01:30Z","updated_at":"2017-03-12T11:17:32Z","closed_at":null,"body":"#### Description\r\n\r\nThere seems to be a memory leak during the `fit_binary` (OvA) phase of the `SGDClassifier`\r\n\r\nI've traced it as far as the `fit_binary` function but I'm not sure if it comes from the [low-level Cython code](https://github.com/scikit-learn/scikit-learn/blob/master/sklearn/linear_model/sgd_fast.pyx#L529)\r\n\r\nWhat I've noticed so far is that using a dense matrix makes the memory grow even faster, and using the `average` version of SGD also ends up bursting the memory and getting killed by the OS memory management.\r\n\r\n#### Steps/Code to Reproduce\r\n\r\n```python\r\nimport os\r\nimport time\r\nimport numpy as np\r\nfrom scipy.sparse import csr_matrix\r\nfrom sklearn.linear_model.stochastic_gradient import fit_binary\r\nfrom sklearn.linear_model import SGDClassifier\r\nimport psutil\r\n\r\n# Create a dataset to work on\r\nn_features = 100000\r\nn_samples = 34000\r\nX = csr_matrix((n_samples,n_features), dtype=np.float64)\r\ny = np.arange(n_samples)\r\n\r\n# Create the classifier with default params\r\nest = SGDClassifier()\r\nest.classes_ = np.unique(y)\r\nn_classes = len(est.classes_)\r\nest._allocate_parameter_mem(n_classes, n_features, None, None)\r\nest.loss_function = est.loss_function_ = est._get_loss_function('hinge')\r\nest.t_ = 1.0\r\nsample_weight = np.full(n_classes, 1, dtype=np.float64)\r\n\r\n# Create the process\r\nprocess = psutil.Process(os.getpid())\r\nlast_output_time = 0\r\nfor i in range(n_classes):\r\n    fit_binary(\r\n        est=est,\r\n        i=i,\r\n        X=X,\r\n        y=y,\r\n        alpha=est.alpha,\r\n        C=est.C,\r\n        learning_rate=est.learning_rate,\r\n        n_iter=est.n_iter,\r\n        pos_weight=1,\r\n        neg_weight=1,\r\n        sample_weight=sample_weight,\r\n    )\r\n    if time.time()-last_output_time >= 5:\r\n        print(\"%f GB\" % (process.memory_info().rss/1E9))\r\n        last_output_time = time.time()\r\n```\r\n\r\n#### Expected Results\r\n\r\nBased on the fact that the matrix in the above example is actually empty (`print(X.data.nbytes) == 0`), the above iteration is expected to have virtually no memory footprint\r\n\r\n#### Actual Results\r\n\r\n```\r\n0.063582 GB\r\n0.863699 GB\r\n1.643700 GB\r\n2.441302 GB\r\n3.238900 GB\r\n4.032500 GB\r\n4.826100 GB\r\n5.622100 GB\r\n6.363701 GB\r\n7.139701 GB\r\n7.906902 GB\r\n8.689299 GB\r\n9.416499 GB\r\n...\r\n```\r\n\r\n#### Versions\r\nDarwin-16.4.0-x86_64-i386-64bit\r\n('Python', '2.7.13 (default, Dec 20 2016, 11:29:35) \\n[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)]')\r\n('NumPy', '1.12.0')\r\n('SciPy', '0.18.1')\r\n('Scikit-Learn', '0.18.1')"},"comment":{"url":"https://api.github.com/repos/scikit-learn/scikit-learn/issues/comments/285937927","html_url":"https://github.com/scikit-learn/scikit-learn/issues/8570#issuecomment-285937927","issue_url":"https://api.github.com/repos/scikit-learn/scikit-learn/issues/8570","id":285937927,"user":{"login":"jnothman","id":78827,"avatar_url":"https://avatars1.githubusercontent.com/u/78827?v=3","gravatar_id":"","url":"https://api.github.com/users/jnothman","html_url":"https://github.com/jnothman","followers_url":"https://api.github.com/users/jnothman/followers","following_url":"https://api.github.com/users/jnothman/following{/other_user}","gists_url":"https://api.github.com/users/jnothman/gists{/gist_id}","starred_url":"https://api.github.com/users/jnothman/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/jnothman/subscriptions","organizations_url":"https://api.github.com/users/jnothman/orgs","repos_url":"https://api.github.com/users/jnothman/repos","events_url":"https://api.github.com/users/jnothman/events{/privacy}","received_events_url":"https://api.github.com/users/jnothman/received_events","type":"User","site_admin":false},"created_at":"2017-03-12T11:17:32Z","updated_at":"2017-03-12T11:17:32Z","body":"I don't get a leak with the following test of `plain_sgd`.\r\n```\r\n# Create the process\r\nprocess = psutil.Process(os.getpid())\r\nlast_output_time = 0\r\nfor i in range(n_classes):\r\n    ds = CSRDataset(X.data, X.indptr, X.indices, y[y == i].astype(np.float64), sample_weight, 1)\r\n    coef = np.zeros(X.shape[1])\r\n    plain_sgd(\r\n        coef, 0, Hinge(), 2, 1e-4, 0, 0,\r\n        ds, 5, 1, 0, 1, 1,\r\n        1, 1, 2, 0.0, .5,\r\n        1.0, 0.01\r\n    )\r\n    gc.collect()\r\n    if time.time()-last_output_time >= 5:\r\n        print(\"%f GB\" % (process.memory_info().rss/1E9))\r\n        last_output_time = time.time()\r\n```"}}
{"actor":{"display_login":"jnothman"}}