Skip to content
This repository was archived by the owner on Jul 10, 2021. It is now read-only.

Commit 3f9c505

Browse files
committed
Merge pull request #41 from aigamedev/theano
Simple theano configuration via a `backend` pseudo-module.
2 parents e82cba8 + 97eb138 commit 3f9c505

File tree

8 files changed

+146
-21
lines changed

8 files changed

+146
-21
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,11 @@ pip-delete-this-directory.txt
4343
htmlcov/
4444
.tox/
4545
.coverage
46+
.coveragerc
47+
build
4648
.coverage.*
4749
.cache
50+
.settings
4851
nosetests.xml
4952
coverage.xml
5053
*,cover

README.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
scikit-neuralnetwork
22
====================
33

4-
Deep neural network implementation without the learning cliff! This library implements multi-layer perceptrons as a wrapper for the powerful ``pylearn2`` library that's compatible with ``scikit-learn`` for a more user-friendly and Pythonic interface. Oh, and it runs on your GPU by default.
4+
Deep neural network implementation without the learning cliff! This library implements multi-layer perceptrons as a wrapper for the powerful ``pylearn2`` library that's compatible with ``scikit-learn`` for a more user-friendly and Pythonic interface.
55

66
**NOTE**: This project is possible thanks to the `nucl.ai Conference <http://nucl.ai/>`_ on **July 20-22**. Join us in **Vienna**!
77

@@ -19,7 +19,7 @@ Thanks to the underlying ``pylearn2`` implementation, this library supports the
1919
* Linear: ``Linear``, ``Gaussian``, ``Softmax``.
2020
* **Layer Types —** ``Convolution`` (greyscale and color, 2D), ``Dense`` (standard, 1D).
2121
* **Learning Rules —** ``sgd``, ``momentum``, ``nesterov``, ``adadelta``, ``rmsprop``.
22-
* **Dataset Types —** ``numpy.ndarray``, coming soon ``scipy.sparse``.
22+
* **Dataset Types —** ``numpy.ndarray``, ``scipy.sparse``, coming soon: iterators.
2323

2424
If a feature you need is missing, consider opening a `GitHub Issue <https://github.com/aigamedev/scikit-neuralnetwork/issues>`_ with a detailed explanation about the use case and we'll see what we can do.
2525

docs/guide.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,20 @@ Here's how to setup such a pipeline with a multi-layer perceptron as a classifie
128128
pipeline.fit(X_train, y_train)
129129
130130
You can thes use the pipeline as you would the neural network, or any other standard API from scikit-learn.
131+
132+
133+
GPU Backend
134+
-----------
135+
136+
To setup the library to use your GPU or CPU explicitly in 32-bit or 64-bit mode, you can use the ``backend`` pseudo-module. It's a syntactic helper to setup ``THEANO_FLAGS`` in a Pythonic way, for example:
137+
138+
.. code:: python
139+
140+
# Use the GPU in 32-bit mode, falling back otherwise.
141+
from sknn.backend import gpu32
142+
143+
# Use the CPU in 64-bit mode.
144+
from sknn.backend import cpu64
145+
146+
147+
WARNING: This will only work if your program has not yet imported the ``theano`` module, due to the way the library is designed. If ``THEANO_FLAGS`` are set on the command-line, they are not overwridden.

examples/plot_mlp.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@
1111
import logging
1212
import argparse
1313
import itertools
14-
import numpy as np
1514

15+
import numpy
1616
from matplotlib import pyplot as plt
1717
from matplotlib.colors import ListedColormap
1818

@@ -24,8 +24,10 @@
2424
import logging
2525
logging.basicConfig(format="%(message)s", level=logging.WARNING, stream=sys.stdout)
2626

27+
from sknn.backend import gpu32
2728
from sknn import mlp
2829

30+
2931
# All possible parameter options that can be plotted, separately or combined.
3032
PARAMETERS = {
3133
'activation': ['Rectifier', 'Tanh', 'Sigmoid', 'Maxout'],
@@ -74,7 +76,7 @@
7476
seed = int(time.time())
7577
X, y = make_classification(n_features=2, n_redundant=0, n_informative=2,
7678
random_state=0, n_clusters_per_class=1)
77-
rng = np.random.RandomState(seed+1)
79+
rng = numpy.random.RandomState(seed+1)
7880
X += 2 * rng.uniform(size=X.shape)
7981
linearly_separable = (X, y)
8082

@@ -94,8 +96,8 @@
9496
# Prepare coordinates of 2D grid to be visualized.
9597
x_min, x_max = X[:, 0].min() - .5, X[:, 0].max() + .5
9698
y_min, y_max = X[:, 1].min() - .5, X[:, 1].max() + .5
97-
xx, yy = np.meshgrid(np.arange(x_min, x_max, GRID_RESOLUTION),
98-
np.arange(y_min, y_max, GRID_RESOLUTION))
99+
xx, yy = numpy.meshgrid(numpy.arange(x_min, x_max, GRID_RESOLUTION),
100+
numpy.arange(y_min, y_max, GRID_RESOLUTION))
99101

100102
# Plot the dataset on its own first.
101103
cm = plt.cm.get_cmap("PRGn")
@@ -118,7 +120,7 @@
118120
# Plot the decision boundary. For that, we will assign a color to each
119121
# point in the mesh [x_min, m_max]x[y_min, y_max].
120122

121-
Z = clf.predict_proba(np.c_[xx.ravel(), yy.ravel()])[:, 1]
123+
Z = clf.predict_proba(numpy.c_[xx.ravel(), yy.ravel()])[:, 1]
122124

123125
# Put the result into a color plot
124126
Z = Z.reshape(xx.shape)

sknn/__init__.py

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,55 @@
1-
from __future__ import (absolute_import, unicode_literals)
1+
# -*- coding: utf-8 -*-
2+
from __future__ import (absolute_import, unicode_literals, print_function)
23

34
__author__ = 'ssamot, alexjc'
45
__version__ = '0.1'
6+
7+
8+
import os
9+
import sys
10+
import logging
11+
12+
13+
class TheanoConfigurator(object):
14+
15+
def __init__(self):
16+
self.configured = False
17+
self.log = logging.getLogger('sknn')
18+
19+
def configure(self, flags):
20+
if self.configured is True:
21+
return
22+
self.configured = True
23+
24+
if 'theano' in sys.modules:
25+
self.log.warning('Theano was already imported and cannot be reconfigured.')
26+
return
27+
28+
os.environ.setdefault('THEANO_FLAGS', flags+',print_active_device=False')
29+
cuda = logging.getLogger('theano.sandbox.cuda')
30+
cuda.setLevel(logging.CRITICAL)
31+
import theano
32+
cuda.setLevel(logging.WARNING)
33+
34+
try:
35+
import theano.sandbox.cuda as cd
36+
self.log.info('Using device gpu%i: %s', cd.active_device_number(), cd.active_device_name())
37+
except AttributeError:
38+
self.log.info('Using device cpu0, with %r.', theano.config.floatX)
39+
40+
def __getattr__(self, name):
41+
flags = ''
42+
if name.endswith('32'):
43+
flags = ',floatX=float32'
44+
if name.endswith('64'):
45+
flags = ',floatX=float64'
46+
47+
if name.startswith('cpu'):
48+
return self.configure('device=cpu'+flags)
49+
if name.startswith('gpu'):
50+
return self.configure('device=gpu'+flags)
51+
52+
return getattr(sys.modules['sknn'], name)
53+
54+
55+
sys.modules['sknn.backend'] = TheanoConfigurator()

sknn/dataset.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@
77
from pylearn2.utils.iteration import (FiniteDatasetIterator, resolve_iterator_class)
88

99
import functools
10-
1110
import theano
12-
floatX = theano.config.floatX
1311

1412

1513
class SparseDesignMatrix(Dataset):
@@ -83,7 +81,7 @@ def iterator(self, mode=None, batch_size=None, num_batches=None,
8381
sub_spaces = space.components
8482
sub_sources = source
8583

86-
conv_fn = lambda x: x.todense().astype(floatX)
84+
conv_fn = lambda x: x.todense().astype(theano.config.floatX)
8785
convert = []
8886
for sp, src in safe_zip(sub_spaces, sub_sources):
8987
convert.append(conv_fn if src in ('features', 'targets') else None)

sknn/mlp.py

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,8 @@
1111
log = logging.getLogger('sknn')
1212

1313

14-
# By default, we force Theano to use a GPU and fallback to CPU, using 32-bits.
15-
# This must be done in the code before Theano is imported for the first time.
16-
os.environ['THEANO_FLAGS'] = "device=gpu,floatX=float32"
17-
18-
cuda = logging.getLogger('theano.sandbox.cuda')
19-
cuda.setLevel(logging.CRITICAL)
20-
import theano
21-
cuda.setLevel(logging.WARNING)
22-
23-
2414
import numpy
15+
import theano
2516
import sklearn.base
2617
import sklearn.pipeline
2718
import sklearn.preprocessing

sknn/tests/test_backend.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import unittest
2+
from nose.tools import (assert_in, assert_equal)
3+
4+
import io
5+
import os
6+
import sys
7+
import logging
8+
9+
import sknn
10+
11+
12+
class TestBackendPseudoModule(unittest.TestCase):
13+
14+
def setUp(self):
15+
if 'THEANO_FLAGS' in os.environ:
16+
del os.environ['THEANO_FLAGS']
17+
18+
import theano
19+
20+
self.removed = {}
21+
for name in list(sys.modules.keys()):
22+
if name.startswith('theano'):
23+
self.removed[name] = sys.modules[name]
24+
del sys.modules[name]
25+
sys.modules['sknn.backend'].configured = False
26+
27+
self.buf = io.StringIO()
28+
self.hnd = logging.StreamHandler(self.buf)
29+
logging.getLogger('sknn').addHandler(self.hnd)
30+
logging.getLogger().setLevel(logging.WARNING)
31+
32+
def tearDown(self):
33+
for name, module in self.removed.items():
34+
sys.modules[name] = module
35+
logging.getLogger('sknn').removeHandler(self.hnd)
36+
37+
def test_TheanoWarning(self):
38+
import theano
39+
from sknn.backend import cpu
40+
assert_equal('Theano was already imported and cannot be reconfigured.\n',
41+
self.buf.getvalue())
42+
43+
def _check(self, flags):
44+
assert_in('THEANO_FLAGS', os.environ)
45+
variable = os.environ['THEANO_FLAGS']
46+
for f in flags:
47+
assert_in(f, variable)
48+
49+
def test_FlagsGPU32(self):
50+
from sknn.backend import gpu32
51+
self._check(['floatX=float32','device=gpu'])
52+
53+
def test_FlagsCPU32(self):
54+
from sknn.backend import cpu32
55+
self._check(['floatX=float32','device=cpu'])
56+
57+
def test_FlagsGPU64(self):
58+
from sknn.backend import gpu64
59+
self._check(['floatX=float64','device=gpu'])
60+
61+
def test_FlagsCPU64(self):
62+
from sknn.backend import cpu64
63+
self._check(['floatX=float64','device=cpu'])

0 commit comments

Comments
 (0)