Skip to content

Instantly share code, notes, and snippets.

@alexlenail
Last active January 22, 2019 16:08
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alexlenail/7a31a0e5e8bb64256b59493d5add5e85 to your computer and use it in GitHub Desktop.
Save alexlenail/7a31a0e5e8bb64256b59493d5add5e85 to your computer and use it in GitHub Desktop.
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Test SPAMS.fistaGraph\n",
"\n",
"Docs http://spams-devel.gforge.inria.fr/doc-R/html/doc_spams006.html#sec39"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"%matplotlib inline\n",
"import numpy as np\n",
"import pandas as pd\n",
"import networkx as nx\n",
"import scipy\n",
"import scipy.sparse as ssp\n",
"\n",
"import time\n",
"import spams\n",
"\n",
"def flatten(list_of_lists): return [item for sublist in list_of_lists for item in sublist]\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## I. Generate synthetic \"easy\" graph and graph signals"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"num_nodes = 15\n",
"p_edge = 0.3\n",
"g1 = nx.fast_gnp_random_graph(num_nodes, p_edge)\n",
"g2 = nx.fast_gnp_random_graph(num_nodes, p_edge)\n",
"\n",
"nx.relabel_nodes(g2, {number: number + 15 for number in g2.nodes}, copy=False)\n",
"\n",
"g = nx.compose(g1, g2)\n",
"\n",
"g.add_edge(1, 25)\n",
"g.add_edge(2, 20)\n",
"g.add_edge(3, 15)"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/Users/alex/miniconda3/lib/python3.7/site-packages/networkx/drawing/nx_pylab.py:611: MatplotlibDeprecationWarning: isinstance(..., numbers.Number)\n",
" if cb.is_numlike(alpha):\n"
]
},
{
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 432x288 with 1 Axes>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"nx.draw_spring(g)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"edgelist = nx.to_pandas_edgelist(g).values"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### True signal comes from 0 and its neighbors"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[0, 2, 11, 12, 14]"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"signal_nodes = [0]+[n for n in g.neighbors(0)]\n",
"signal_nodes"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### First half is class 1, second half is class 0"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
" 1., 1., 1., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,\n",
" 0., 0., 0., 0., 0., 0.])"
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"NUM_POSITIVES = 20\n",
"NUM_NEGATIVES = 20\n",
"y = np.concatenate((np.ones(NUM_POSITIVES), np.zeros(NUM_NEGATIVES)))\n",
"y"
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>0</th>\n",
" <th>1</th>\n",
" <th>2</th>\n",
" <th>3</th>\n",
" <th>4</th>\n",
" <th>5</th>\n",
" <th>6</th>\n",
" <th>7</th>\n",
" <th>8</th>\n",
" <th>9</th>\n",
" <th>...</th>\n",
" <th>20</th>\n",
" <th>21</th>\n",
" <th>22</th>\n",
" <th>23</th>\n",
" <th>24</th>\n",
" <th>25</th>\n",
" <th>26</th>\n",
" <th>27</th>\n",
" <th>28</th>\n",
" <th>29</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>1.0</td>\n",
" <td>-1.626836</td>\n",
" <td>1.0</td>\n",
" <td>-1.667516</td>\n",
" <td>-0.108261</td>\n",
" <td>0.390096</td>\n",
" <td>-0.861851</td>\n",
" <td>-0.156386</td>\n",
" <td>1.577740</td>\n",
" <td>1.355505</td>\n",
" <td>...</td>\n",
" <td>0.494552</td>\n",
" <td>0.357644</td>\n",
" <td>-0.999935</td>\n",
" <td>0.391030</td>\n",
" <td>-0.382606</td>\n",
" <td>1.747134</td>\n",
" <td>-0.019657</td>\n",
" <td>2.119312</td>\n",
" <td>0.032667</td>\n",
" <td>-0.146469</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>1.0</td>\n",
" <td>0.362366</td>\n",
" <td>1.0</td>\n",
" <td>-0.635774</td>\n",
" <td>-0.836827</td>\n",
" <td>-0.276659</td>\n",
" <td>-0.809957</td>\n",
" <td>0.002832</td>\n",
" <td>0.183393</td>\n",
" <td>1.224518</td>\n",
" <td>...</td>\n",
" <td>0.983904</td>\n",
" <td>0.622035</td>\n",
" <td>-0.619685</td>\n",
" <td>0.268317</td>\n",
" <td>-1.240670</td>\n",
" <td>-1.471808</td>\n",
" <td>0.637272</td>\n",
" <td>0.492749</td>\n",
" <td>0.539597</td>\n",
" <td>-0.780348</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>1.0</td>\n",
" <td>0.126977</td>\n",
" <td>1.0</td>\n",
" <td>-0.112256</td>\n",
" <td>1.780466</td>\n",
" <td>0.342744</td>\n",
" <td>-0.631568</td>\n",
" <td>-1.072373</td>\n",
" <td>-2.127742</td>\n",
" <td>3.137458</td>\n",
" <td>...</td>\n",
" <td>0.010640</td>\n",
" <td>0.457412</td>\n",
" <td>-0.346469</td>\n",
" <td>0.837266</td>\n",
" <td>0.216169</td>\n",
" <td>-1.255527</td>\n",
" <td>2.048838</td>\n",
" <td>-1.903665</td>\n",
" <td>1.030514</td>\n",
" <td>1.808216</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>1.0</td>\n",
" <td>-1.144054</td>\n",
" <td>1.0</td>\n",
" <td>-0.673975</td>\n",
" <td>-0.488836</td>\n",
" <td>-0.597826</td>\n",
" <td>-0.467641</td>\n",
" <td>-0.079044</td>\n",
" <td>-0.620556</td>\n",
" <td>-0.000212</td>\n",
" <td>...</td>\n",
" <td>-1.718976</td>\n",
" <td>-1.400485</td>\n",
" <td>0.870043</td>\n",
" <td>-0.137372</td>\n",
" <td>-0.695848</td>\n",
" <td>-0.011468</td>\n",
" <td>-0.990155</td>\n",
" <td>0.890150</td>\n",
" <td>-0.059695</td>\n",
" <td>2.337334</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>1.0</td>\n",
" <td>-1.383579</td>\n",
" <td>1.0</td>\n",
" <td>-0.772876</td>\n",
" <td>-0.020679</td>\n",
" <td>1.385638</td>\n",
" <td>-0.299431</td>\n",
" <td>-0.074756</td>\n",
" <td>-0.465050</td>\n",
" <td>0.641146</td>\n",
" <td>...</td>\n",
" <td>-0.083365</td>\n",
" <td>0.821267</td>\n",
" <td>0.327011</td>\n",
" <td>1.879729</td>\n",
" <td>0.859166</td>\n",
" <td>-1.927820</td>\n",
" <td>-0.632995</td>\n",
" <td>-0.974812</td>\n",
" <td>1.312887</td>\n",
" <td>0.643763</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>5 rows × 30 columns</p>\n",
"</div>"
],
"text/plain": [
" 0 1 2 3 4 5 6 7 \\\n",
"0 1.0 -1.626836 1.0 -1.667516 -0.108261 0.390096 -0.861851 -0.156386 \n",
"1 1.0 0.362366 1.0 -0.635774 -0.836827 -0.276659 -0.809957 0.002832 \n",
"2 1.0 0.126977 1.0 -0.112256 1.780466 0.342744 -0.631568 -1.072373 \n",
"3 1.0 -1.144054 1.0 -0.673975 -0.488836 -0.597826 -0.467641 -0.079044 \n",
"4 1.0 -1.383579 1.0 -0.772876 -0.020679 1.385638 -0.299431 -0.074756 \n",
"\n",
" 8 9 ... 20 21 22 23 \\\n",
"0 1.577740 1.355505 ... 0.494552 0.357644 -0.999935 0.391030 \n",
"1 0.183393 1.224518 ... 0.983904 0.622035 -0.619685 0.268317 \n",
"2 -2.127742 3.137458 ... 0.010640 0.457412 -0.346469 0.837266 \n",
"3 -0.620556 -0.000212 ... -1.718976 -1.400485 0.870043 -0.137372 \n",
"4 -0.465050 0.641146 ... -0.083365 0.821267 0.327011 1.879729 \n",
"\n",
" 24 25 26 27 28 29 \n",
"0 -0.382606 1.747134 -0.019657 2.119312 0.032667 -0.146469 \n",
"1 -1.240670 -1.471808 0.637272 0.492749 0.539597 -0.780348 \n",
"2 0.216169 -1.255527 2.048838 -1.903665 1.030514 1.808216 \n",
"3 -0.695848 -0.011468 -0.990155 0.890150 -0.059695 2.337334 \n",
"4 0.859166 -1.927820 -0.632995 -0.974812 1.312887 0.643763 \n",
"\n",
"[5 rows x 30 columns]"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"data = pd.DataFrame(np.random.normal(size=(NUM_POSITIVES+NUM_NEGATIVES,30)))\n",
"data.loc[0:NUM_POSITIVES, signal_nodes] = 1\n",
"data.loc[NUM_POSITIVES:NUM_POSITIVES+NUM_NEGATIVES, signal_nodes] = -1\n",
"\n",
"data.head()"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>0</th>\n",
" <th>1</th>\n",
" <th>2</th>\n",
" <th>3</th>\n",
" <th>4</th>\n",
" <th>5</th>\n",
" <th>6</th>\n",
" <th>7</th>\n",
" <th>8</th>\n",
" <th>9</th>\n",
" <th>...</th>\n",
" <th>20</th>\n",
" <th>21</th>\n",
" <th>22</th>\n",
" <th>23</th>\n",
" <th>24</th>\n",
" <th>25</th>\n",
" <th>26</th>\n",
" <th>27</th>\n",
" <th>28</th>\n",
" <th>29</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>35</th>\n",
" <td>-1.0</td>\n",
" <td>0.711782</td>\n",
" <td>-1.0</td>\n",
" <td>-0.300740</td>\n",
" <td>0.771417</td>\n",
" <td>0.419459</td>\n",
" <td>0.134785</td>\n",
" <td>-1.810631</td>\n",
" <td>1.698634</td>\n",
" <td>0.089898</td>\n",
" <td>...</td>\n",
" <td>0.161639</td>\n",
" <td>0.903178</td>\n",
" <td>-0.773467</td>\n",
" <td>0.145566</td>\n",
" <td>0.344990</td>\n",
" <td>0.548841</td>\n",
" <td>0.444323</td>\n",
" <td>0.594573</td>\n",
" <td>0.014439</td>\n",
" <td>0.111183</td>\n",
" </tr>\n",
" <tr>\n",
" <th>36</th>\n",
" <td>-1.0</td>\n",
" <td>-0.363750</td>\n",
" <td>-1.0</td>\n",
" <td>0.319550</td>\n",
" <td>0.582871</td>\n",
" <td>-0.366024</td>\n",
" <td>2.009703</td>\n",
" <td>-0.849986</td>\n",
" <td>0.252170</td>\n",
" <td>-0.349139</td>\n",
" <td>...</td>\n",
" <td>-1.003231</td>\n",
" <td>0.109738</td>\n",
" <td>-0.425068</td>\n",
" <td>-2.085400</td>\n",
" <td>-0.354644</td>\n",
" <td>-0.923705</td>\n",
" <td>1.521942</td>\n",
" <td>-0.776429</td>\n",
" <td>0.755865</td>\n",
" <td>-1.172837</td>\n",
" </tr>\n",
" <tr>\n",
" <th>37</th>\n",
" <td>-1.0</td>\n",
" <td>-0.212570</td>\n",
" <td>-1.0</td>\n",
" <td>0.809884</td>\n",
" <td>-1.436927</td>\n",
" <td>0.728994</td>\n",
" <td>-0.141139</td>\n",
" <td>-0.658784</td>\n",
" <td>0.074903</td>\n",
" <td>1.463954</td>\n",
" <td>...</td>\n",
" <td>-0.643148</td>\n",
" <td>-0.576813</td>\n",
" <td>-1.599052</td>\n",
" <td>1.233322</td>\n",
" <td>-1.907276</td>\n",
" <td>1.503759</td>\n",
" <td>0.632590</td>\n",
" <td>-0.616824</td>\n",
" <td>1.147559</td>\n",
" <td>1.531823</td>\n",
" </tr>\n",
" <tr>\n",
" <th>38</th>\n",
" <td>-1.0</td>\n",
" <td>-0.561549</td>\n",
" <td>-1.0</td>\n",
" <td>0.625856</td>\n",
" <td>-0.822934</td>\n",
" <td>-0.979575</td>\n",
" <td>0.199540</td>\n",
" <td>-1.276706</td>\n",
" <td>0.575109</td>\n",
" <td>-0.314934</td>\n",
" <td>...</td>\n",
" <td>-1.487596</td>\n",
" <td>0.377102</td>\n",
" <td>1.008312</td>\n",
" <td>0.622991</td>\n",
" <td>0.523273</td>\n",
" <td>-1.566366</td>\n",
" <td>0.030276</td>\n",
" <td>0.092158</td>\n",
" <td>1.426438</td>\n",
" <td>1.271735</td>\n",
" </tr>\n",
" <tr>\n",
" <th>39</th>\n",
" <td>-1.0</td>\n",
" <td>-1.453606</td>\n",
" <td>-1.0</td>\n",
" <td>1.025963</td>\n",
" <td>-0.199703</td>\n",
" <td>0.196850</td>\n",
" <td>-0.033403</td>\n",
" <td>-0.131592</td>\n",
" <td>0.952464</td>\n",
" <td>-1.219901</td>\n",
" <td>...</td>\n",
" <td>-0.476985</td>\n",
" <td>1.392822</td>\n",
" <td>-1.000271</td>\n",
" <td>-0.647108</td>\n",
" <td>0.612353</td>\n",
" <td>1.975893</td>\n",
" <td>-0.744764</td>\n",
" <td>-1.440025</td>\n",
" <td>-0.248124</td>\n",
" <td>1.060998</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>5 rows × 30 columns</p>\n",
"</div>"
],
"text/plain": [
" 0 1 2 3 4 5 6 7 \\\n",
"35 -1.0 0.711782 -1.0 -0.300740 0.771417 0.419459 0.134785 -1.810631 \n",
"36 -1.0 -0.363750 -1.0 0.319550 0.582871 -0.366024 2.009703 -0.849986 \n",
"37 -1.0 -0.212570 -1.0 0.809884 -1.436927 0.728994 -0.141139 -0.658784 \n",
"38 -1.0 -0.561549 -1.0 0.625856 -0.822934 -0.979575 0.199540 -1.276706 \n",
"39 -1.0 -1.453606 -1.0 1.025963 -0.199703 0.196850 -0.033403 -0.131592 \n",
"\n",
" 8 9 ... 20 21 22 23 \\\n",
"35 1.698634 0.089898 ... 0.161639 0.903178 -0.773467 0.145566 \n",
"36 0.252170 -0.349139 ... -1.003231 0.109738 -0.425068 -2.085400 \n",
"37 0.074903 1.463954 ... -0.643148 -0.576813 -1.599052 1.233322 \n",
"38 0.575109 -0.314934 ... -1.487596 0.377102 1.008312 0.622991 \n",
"39 0.952464 -1.219901 ... -0.476985 1.392822 -1.000271 -0.647108 \n",
"\n",
" 24 25 26 27 28 29 \n",
"35 0.344990 0.548841 0.444323 0.594573 0.014439 0.111183 \n",
"36 -0.354644 -0.923705 1.521942 -0.776429 0.755865 -1.172837 \n",
"37 -1.907276 1.503759 0.632590 -0.616824 1.147559 1.531823 \n",
"38 0.523273 -1.566366 0.030276 0.092158 1.426438 1.271735 \n",
"39 0.612353 1.975893 -0.744764 -1.440025 -0.248124 1.060998 \n",
"\n",
"[5 rows x 30 columns]"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"data.tail()"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
"edge_weights = [1]*len(edgelist)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Groups are assigned as the one-hop neighborhood of every node"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"30"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"neighborhoods = [[i]+[n for n in g.neighbors(node)] for i, node in enumerate(g.nodes)]\n",
"num_groups = len(neighborhoods)\n",
"num_groups"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## II. Test SPAMS.fistaGraph on \"easy\" graph"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
"# Name: spams.fistaGraph\n",
"#\n",
"# Description:\n",
"# spams.fistaGraph solves sparse regularized problems.\n",
"# X is a design matrix of size m x p\n",
"# X=[x^1,...,x^n]', where the x_i's are the rows of X\n",
"# Y=[y^1,...,y^n] is a matrix of size m x n\n",
"# \n",
"# It implements the algorithms FISTA, ISTA and subgradient descent for solving\n",
"# \n",
"# min_W loss(W) + lambda1 psi(W)\n",
"# \n",
"# The function psi are those used by spams.proximalGraph (see documentation)\n",
"# for the loss functions, see the documentation of spams.fistaFlat\n",
"# \n",
"# This function can also handle intercepts (last row of W is not regularized),\n",
"# and/or non-negativity constraints on W.\n",
"#"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"# graph: struct\n",
"# with three fields, eta_g, groups, and groups_var\n",
"# \n",
"# The first fields sets the weights for every group\n",
"# graph.eta_g double N vector \n",
"\n",
"eta_g = np.ones(num_groups)\n",
" \n",
"# The next field sets inclusion relations between groups (but not between groups and variables):\n",
"# graph.groups sparse (double or boolean) N x N matrix \n",
"# the (i,j) entry is non-zero if and only if i is different than j and \n",
"# gi is included in gj.\n",
"\n",
"groups = scipy.sparse.csc_matrix(np.zeros((num_groups,num_groups)),dtype=np.bool)"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"i, j = zip(*flatten([[(i, j) for j in neighbors] for i, neighbors in enumerate(neighborhoods)]))"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [],
"source": [
"# The next field sets inclusion relations between groups and variables\n",
"# graph.groups_var sparse (double or boolean) p x N matrix\n",
"# the (i,j) entry is non-zero if and only if the variable i is included \n",
"# in gj, but not in any children of gj.\n",
"\n",
"# scipy.sparse.csc_matrix((data, (row_ind, col_ind)), [shape=(M, N)])\n",
"# where data, row_ind and col_ind satisfy the relationship a[row_ind[k], col_ind[k]] = data[k].\n",
"\n",
"groups_var = scipy.sparse.csc_matrix((np.ones(len(i)),(i,j)),dtype=np.bool)\n",
"\n",
"# graph: struct\n",
"# with three fields, eta_g, groups, and groups_var\n",
"# \n",
"graph = {'eta_g':eta_g,'groups':groups,'groups_var':groups_var}"
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"array([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,\n",
" 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.])"
]
},
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"graph['eta_g']"
]
},
{
"cell_type": "code",
"execution_count": 16,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<30x30 sparse matrix of type '<class 'numpy.bool_'>'\n",
"\twith 0 stored elements in Compressed Sparse Column format>"
]
},
"execution_count": 16,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"graph['groups']"
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<30x30 sparse matrix of type '<class 'numpy.bool_'>'\n",
"\twith 152 stored elements in Compressed Sparse Column format>"
]
},
"execution_count": 17,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"graph['groups_var']"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [],
"source": [
"# Usage: spams.fistaGraph( Y,\n",
"# X,\n",
"# W0,\n",
"# graph,\n",
"# return_optim_info=False,\n",
"# numThreads=-1,\n",
"# max_it=1000,\n",
"# L0=1.0,\n",
"# fixed_step=False,\n",
"# gamma=1.5,\n",
"# lambda1=1.0,\n",
"# lambda2=0.,\n",
"# lambda3=0.,\n",
"# a=1.0,\n",
"# b=0.,\n",
"# tol=0.000001,\n",
"# it0=100,\n",
"# compute_gram=False,\n",
"# intercept=False,\n",
"# regul=\"\",\n",
"# loss=\"\",\n",
"# verbose=False,\n",
"# pos=False,\n",
"# ista=False,\n",
"# subgrad=False,\n",
"# linesearch_mode=0) \n",
"#\n",
"# Inputs:\n",
"# Y : double dense m x n matrix\n",
"\n",
"Y = np.asfortranarray(np.expand_dims(y, axis=1)).astype(float)\n",
"Y = spams.normalize(Y)\n",
"\n",
"# X : double dense or sparse m x p matrix\n",
"\n",
"X = np.asfortranarray(data.values).astype(float)\n",
"X = spams.normalize(X)\n",
"\n",
"# W0 : double dense p x n matrix or p x Nn matrix for multi-logistic loss initial guess\n",
"\n",
"W0 = np.zeros((X.shape[1],Y.shape[1]),dtype=np.float64,order=\"F\")\n",
"\n",
"# graph : struct see documentation of proximalGraph\n",
"# return_optim_info : if true the function will return a tuple of matrices.\n",
"# loss : choice of loss, see above\n",
"# regul : choice of regularization, see below\n",
"# lambda1 : regularization parameter\n",
"# lambda2 : regularization parameter, 0 by default\n",
"# lambda3 : regularization parameter, 0 by default\n",
"# verbose : verbosity level, false by default\n",
"# pos : adds positivity constraints on the coefficients, false by default\n",
"# numThreads : number of threads for exploiting multi-core / multi-cpus. By default, it takes the value -1, which automatically selects all the available CPUs/cores.\n",
"# max_it : maximum number of iterations, 100 by default\n",
"# it0 : frequency for computing duality gap, every 10 iterations by default\n",
"# tol : tolerance for stopping criteration, which is a relative duality gap if it is available, or a relative change of parameters.\n",
"# gamma : multiplier for increasing the parameter L in fista, 1.5 by default\n",
"# L0 : initial parameter L in fista, 0.1 by default, should be small enough\n",
"# fixed_step : deactive the line search for L in fista and use L0 instead\n",
"# compute_gram : pre-compute X^TX, false by default.\n",
"# intercept : do not regularize last row of W, false by default.\n",
"# ista : use ista instead of fista, false by default.\n",
"# subgrad : if not ista, use subradient descent instead of fista, false by default.\n",
"# a :\n",
"# b : if subgrad, the gradient step is a/(t+b) also similar options as proximalTree\n"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [],
"source": [
"# Regularizers: \n",
"# Given an input matrix U=[u^1,\\ldots,u^n], it computes a matrix V=[v^1,\\ldots,v^n] such that\n",
"#\n",
"# if one chooses a regularization functions on vectors, it computes for each column u of U, a column v of V solving\n",
"# if regul='l0' argmin 0.5||u-v||_2^2 + lambda1||v||_0\n",
"# if regul='l1' argmin 0.5||u-v||_2^2 + lambda1||v||_1\n",
"# if regul='l2' argmin 0.5||u-v||_2^2 + 0.5lambda1||v||_2^2\n",
"# if regul='elastic-net' argmin 0.5||u-v||_2^2 + lambda1||v||_1 + lambda1_2||v||_2^2\n",
"# if regul='fused-lasso' argmin 0.5||u-v||_2^2 + lambda1 FL(v) + lambda1_2||v||_1 + lambda1_3||v||_2^2\n",
"# if regul='linf' argmin 0.5||u-v||_2^2 + lambda1||v||_inf\n",
"# if regul='l1-constraint' argmin 0.5||u-v||_2^2 s.t. ||v||_1 <= lambda1\n",
"# if regul='l2-not-squared' argmin 0.5||u-v||_2^2 + lambda1||v||_2\n",
"# if regul='group-lasso-l2' argmin 0.5||u-v||_2^2 + lambda1 sum_g ||v_g||_2 where the groups are either defined by groups or by size_group,\n",
"# if regul='group-lasso-linf' argmin 0.5||u-v||_2^2 + lambda1 sum_g ||v_g||_inf\n",
"# if regul='sparse-group-lasso-l2' argmin 0.5||u-v||_2^2 + lambda1 sum_g ||v_g||_2 + lambda1_2 ||v||_1 where the groups are either defined by groups or by size_group,\n",
"# if regul='sparse-group-lasso-linf' argmin 0.5||u-v||_2^2 + lambda1 sum_g ||v_g||_inf + lambda1_2 ||v||_1\n",
"# if regul='trace-norm-vec' argmin 0.5||u-v||_2^2 + lambda1 ||mat(v)||_* where mat(v) has size_group rows\n",
"#\n",
"# if regul='graph' argmin 0.5||u-v||_2^2 + lambda1\\sum_{g \\in G} \\eta_g||v_g||_inf\n",
"# if regul='graph+ridge' argmin 0.5||u-v||_2^2 + lambda1\\sum_{g \\in G} \\eta_g||v_g||_inf + lambda1_2||v||_2^2\n",
"#\n",
"# if one chooses a regularization function on matrices\n",
"# if regul='l1l2', V= argmin 0.5||U-V||_F^2 + lambda1||V||_{1/2}\n",
"# if regul='l1linf', V= argmin 0.5||U-V||_F^2 + lambda1||V||_{1/inf}\n",
"# if regul='l1l2+l1', V= argmin 0.5||U-V||_F^2 + lambda1||V||_{1/2} + lambda1_2||V||_{1/1}\n",
"# if regul='l1linf+l1', V= argmin 0.5||U-V||_F^2 + lambda1||V||_{1/inf} + lambda1_2||V||_{1/1}\n",
"# if regul='l1linf+row-column', V= argmin 0.5||U-V||_F^2 + lambda1||V||_{1/inf} + lambda1_2||V'||_{1/inf}\n",
"# if regul='trace-norm', V= argmin 0.5||U-V||_F^2 + lambda1||V||_*\n",
"# if regul='rank', V= argmin 0.5||U-V||_F^2 + lambda1 rank(V)\n",
"# if regul='none', V= argmin 0.5||U-V||_F^2\n",
"#\n",
"# if regul='multi-task-graph' V=argmin 0.5||U-V||_F^2 + lambda1 \\sum_{i=1}^n\\sum_{g \\in G} \\eta_g||v^i_g||_inf + lambda1_2 \\sum_{g \\in G} \\eta_g max_{j in g}||V_j||_{inf}\n",
"#\n",
"# for all these regularizations, it is possible to enforce non-negativity constraints\n",
"# with the option pos, and to prevent the last row of U to be regularized, with\n",
"# the option intercept\n",
"\n",
"# Note:\n",
"# Valid values for the regularization parameter (regul) for fistaGraph (beyond those listed above) are:\n",
"# \"tree-l0\"\n",
"# \"tree-l2\"\n",
"# \"tree-linf\"\n",
"# \"graph-l2\",\n",
"# \"multi-task-tree\"\n",
"# \"rank-vec\"\n"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [],
"source": [
"# Loss: \n",
"# - if loss='square' and regul is a regularization function for vectors,\n",
"# the entries of Y are real-valued, W = [w^1,...,w^n] is a matrix of size p x n\n",
"# For all column y of Y, it computes a column w of W such that\n",
"# w = argmin 0.5||y- X w||_2^2 + lambda1 psi(w)\n",
"# \n",
"# - if loss='square' and regul is a regularization function for matrices\n",
"# the entries of Y are real-valued, W is a matrix of size p x n. \n",
"# It computes the matrix W such that\n",
"# W = argmin 0.5||Y- X W||_F^2 + lambda1 psi(W)\n",
"# \n",
"# - loss='square-missing' same as loss='square', but handles missing data\n",
"# represented by NaN (not a number) in the matrix Y\n",
"# \n",
"# - if loss='logistic' and regul is a regularization function for vectors,\n",
"# the entries of Y are either -1 or +1, W = [w^1,...,w^n] is a matrix of size p x n\n",
"# For all column y of Y, it computes a column w of W such that\n",
"# w = argmin (1/m)sum_{j=1}^m log(1+e^(-y_j x^j' w)) + lambda1 psi(w),\n",
"# where x^j is the j-th row of X.\n",
"# \n",
"# - if loss='logistic' and regul is a regularization function for matrices\n",
"# the entries of Y are either -1 or +1, W is a matrix of size p x n\n",
"# W = argmin sum_{i=1}^n(1/m)sum_{j=1}^m log(1+e^(-y^i_j x^j' w^i)) + lambda1 psi(W)\n",
"# \n",
"# - if loss='multi-logistic' and regul is a regularization function for vectors,\n",
"# the entries of Y are in {0,1,...,N} where N is the total number of classes\n",
"# W = [W^1,...,W^n] is a matrix of size p x Nn, each submatrix W^i is of size p x N\n",
"# for all submatrix WW of W, and column y of Y, it computes\n",
"# WW = argmin (1/m)sum_{j=1}^m log(sum_{j=1}^r e^(x^j'(ww^j-ww^{y_j}))) + lambda1 sum_{j=1}^N psi(ww^j),\n",
"# where ww^j is the j-th column of WW.\n",
"# \n",
"# - if loss='multi-logistic' and regul is a regularization function for matrices,\n",
"# the entries of Y are in {0,1,...,N} where N is the total number of classes\n",
"# W is a matrix of size p x N, it computes\n",
"# W = argmin (1/m)sum_{j=1}^m log(sum_{j=1}^r e^(x^j'(w^j-w^{y_j}))) + lambda1 psi(W)\n",
"# where ww^j is the j-th column of WW.\n",
"# \n",
"# - loss='cur' useful to perform sparse CUR matrix decompositions, \n",
"# W = argmin 0.5||Y-X*W*X||_F^2 + lambda1 psi(W)"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"mean loss: 0.094094, mean relative duality_gap: -1.000000, time: 0.002162, number of iterations: 482.000000\n"
]
}
],
"source": [
"verbose = True\n",
"lambda1 = 0 # regularization term (no regularization)\n",
"max_it = 100 # maximum number of iterations\n",
"L0 = 0.1\n",
"tol = 1e-5\n",
"intercept = False\n",
"pos = False\n",
"compute_gram = True\n",
"\n",
"loss = 'square'\n",
"regul = 'none'\n",
"tic = time.time()\n",
"\n",
"(W, optim_info) = spams.fistaGraph(Y, X, W0, graph, return_optim_info=True, loss=loss, regul=regul, verbose=verbose)\n",
"\n",
"tac = time.time()\n",
"t = tac - tic\n",
"\n",
"print('mean loss: %f, mean relative duality_gap: %f, time: %f, number of iterations: %f' %(np.mean(optim_info[0,:]),np.mean(optim_info[2,:]),t,np.mean(optim_info[3,:])))"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"mean loss: 0.500000, mean relative duality_gap: 0.000000, time: 0.003627, number of iterations: 100.000000\n"
]
}
],
"source": [
"verbose = True\n",
"lambda1 = 0 # regularization term (no regularization)\n",
"max_it = 100 # maximum number of iterations\n",
"L0 = 0.1\n",
"tol = 1e-5\n",
"intercept = False\n",
"pos = False\n",
"compute_gram = True\n",
"\n",
"loss = 'square'\n",
"regul = 'graph'\n",
"tic = time.time()\n",
"\n",
"(W, optim_info) = spams.fistaGraph(Y, X, W0, graph, return_optim_info=True, loss=loss, regul=regul, verbose=verbose)\n",
"\n",
"tac = time.time()\n",
"t = tac - tic\n",
"\n",
"print('mean loss: %f, mean relative duality_gap: %f, time: %f, number of iterations: %f' %(np.mean(optim_info[0,:]),np.mean(optim_info[2,:]),t,np.mean(optim_info[3,:])))"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [],
"source": [
"# Output:\n",
"# W: double dense p x n matrix or p x Nn matrix (for multi-logistic loss)\n",
"# optim: optional, double dense 4 x n matrix.\n",
"# first row: values of the objective functions.\n",
"# third row: values of the relative duality gap (if available)\n",
"# fourth row: number of iterations\n",
"# optim_info: vector of size 4, containing information of the optimization.\n",
"# W = spams.fistaGraph(Y,X,W0,graph,return_optim_info = False,...)\n",
"# (W,optim_info) = spams.fistaGraph(Y,X,W0,graph,return_optim_info = True,...)\n",
"#"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[0, 2, 11, 12, 14]"
]
},
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"signal_nodes"
]
},
{
"cell_type": "code",
"execution_count": 25,
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>0</th>\n",
" <th>1</th>\n",
" <th>2</th>\n",
" <th>3</th>\n",
" <th>4</th>\n",
" <th>5</th>\n",
" <th>6</th>\n",
" <th>7</th>\n",
" <th>8</th>\n",
" <th>9</th>\n",
" <th>...</th>\n",
" <th>20</th>\n",
" <th>21</th>\n",
" <th>22</th>\n",
" <th>23</th>\n",
" <th>24</th>\n",
" <th>25</th>\n",
" <th>26</th>\n",
" <th>27</th>\n",
" <th>28</th>\n",
" <th>29</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>0.0</td>\n",
" <td>-0.0</td>\n",
" <td>0.0</td>\n",
" <td>-0.0</td>\n",
" <td>-0.0</td>\n",
" <td>0.0</td>\n",
" <td>-0.0</td>\n",
" <td>-0.0</td>\n",
" <td>-0.0</td>\n",
" <td>0.0</td>\n",
" <td>...</td>\n",
" <td>0.0</td>\n",
" <td>-0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>-0.0</td>\n",
" <td>-0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" <td>0.0</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"<p>1 rows × 30 columns</p>\n",
"</div>"
],
"text/plain": [
" 0 1 2 3 4 5 6 7 8 9 ... 20 21 22 23 \\\n",
"0 0.0 -0.0 0.0 -0.0 -0.0 0.0 -0.0 -0.0 -0.0 0.0 ... 0.0 -0.0 0.0 0.0 \n",
"\n",
" 24 25 26 27 28 29 \n",
"0 -0.0 -0.0 0.0 0.0 0.0 0.0 \n",
"\n",
"[1 rows x 30 columns]"
]
},
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"W = pd.DataFrame(W)\n",
"W.T"
]
},
{
"cell_type": "code",
"execution_count": 26,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"0 False\n",
"dtype: bool"
]
},
"execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"W[W != 0].any()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.1"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment