Repository: calvinschmdt/EasyTensorflow Branch: master Commit: 41ac269218f0 Files: 21 Total size: 90.5 KB Directory structure: gitextract_0gqp2iax/ ├── LICENSE ├── MANIFEST.in ├── README.rst ├── docs/ │ ├── Makefile │ ├── make.bat │ └── source/ │ ├── README │ ├── _static/ │ │ └── .gitkeep │ ├── conf.py │ └── index.rst ├── easy_tensorflow/ │ ├── __init__.py │ ├── evolve_functions.py │ ├── main.py │ ├── metadata.py │ ├── tf_dictionaries.py │ └── tf_functions.py ├── pavement.py ├── requirements-dev.txt ├── requirements.txt ├── setup.py ├── tests/ │ └── test_main.py └── tox.ini ================================================ FILE CONTENTS ================================================ ================================================ FILE: LICENSE ================================================ The MIT License (MIT) Copyright (c) 2015 Calvin Schmidt Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. CTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: MANIFEST.in ================================================ # Informational files include README.rst include LICENSE # Include docs and tests. It's unclear whether convention dictates # including built docs. However, Sphinx doesn't include built docs, so # we are following their lead. graft docs prune docs/build graft tests # Exclude any compile Python files (most likely grafted by tests/ directory). global-exclude *.pyc # Setup-related things include pavement.py include requirements-dev.txt include requirements.txt include setup.py include tox.ini ================================================ FILE: README.rst ================================================ ========================= Easy Tensorflow ========================= This package provides users with methods for the automated building, training, and testing of complex neural networks using Google's Tensorflow package. The project includes objects that perform both regression and classification tasks. In addition, there is a function included that uses the DEAP genetic algorithm package to evolve the optimal network architecture. The evolution function is almost entirely based off of the sample DEAP evolution. This project is meant to simplify the tensorflow experience, and therefore it reduces the customizibility of the networks. Patches that expand functionality are welcome and encouraged, as long they do not reduce the simplicity of usage. I will try to keep up with maintenance as best as I can, but please be patient; I am new to this. Project Setup ============= Dependancies ------------ Full support for Python 2.7. Python 3.3 not tested. Requires tensorflow (tested on version 0.6.0). Installation instructions `on the Tensorflow website `_ . Requires DEAP (tested on version 1.0) for evolving. Installation instructions `here `_. Installation ------------ 1. Either download and extract the zipped file, or clone directly from github using:: git clone https://github.com/calvinschmdt/EasyTensorflow.git easy_tensorflow This should create a new directory containing the required files. 2. Install the dependancies manually or by running this command while in the easy_tensorflow directory:: sudo pip install -r requirements.txt 3. Install the project by running this command while in the easy_tensorflow directory:: sudo python setup.py install Usage ===== Prediction Objects ------------------ This package uses objects to hold the neural networks. There are separate objects for performing regression and classification (the two objects are Regresser and Classifier), but the two objects have the same basic functions. Instantiation ------------- Instantiate the object by assigning it to a variable. The only required argument for instantiation is a list that describes the neural network:: net_type = ['none', 20, 'sigmoid', 30, 'bias_add', 30, 'sigmoid'] reg = etf.tf_functions.Regresser(net_type) The net_type list needs to be in a specific format: alternating strings and integers starting and ending with a string. The strings describe the transformation that is made between each layer of the neural network, while the integers denote the number of size of the layer after the transformation is made. For example, the above network would look like this: +---------------------------------------------------------------------------+ | Input with n samples and f features | +---------------------------------------------------------------------------+ | *Matrix multiplication by adjustable weights* | +---------------------------------------------------------------------------+ | Layer 1 with n samples and 20 features | +---------------------------------------------------------------------------+ | *Sigmoid transformation on a matrix multiplication by adjustable weights* | +---------------------------------------------------------------------------+ | Layer 2 with n samples and 30 features | +---------------------------------------------------------------------------+ | *Addition to each feature of an adjustable weight* | +---------------------------------------------------------------------------+ | Layer 3 with n samples and 30 features | +---------------------------------------------------------------------------+ | *Sigmoid transformation on a matrix multiplication by adjustable weights* | +---------------------------------------------------------------------------+ | Output with n samples and o features | +---------------------------------------------------------------------------+ The number of input and output features do not have to be specified upon instantiation, but are learned during training. The transformations available are: - 'relu': Relu transformation on a matrix multiplication by adjustable weights. Relu applies a ramp function. - 'softplus': Softplus transformation on a matrix multiplication by adjustable weights. Softplus applies a smoothed ramp function. - 'dropout': Randomly pushes features to zero. Prevents overfitting. Does not change the number of features. - 'bias_add': Adds an adjustable weight to each feature. Does not change the number of features. - 'sigmoid': Sigmoid transformation on a matrix multiplication by adjustable weights. Sigmoid forces values to approach 0 or 1. - 'tanh': Hyperbolic tangent transformation on a matrix multiplication by adjustable weights. Tanh forces values very positive or very negative. - 'none': Matrix multiplication with adjustable weights. - 'normalize': Normalizes features of a sample using an L2 norm. Does not change the number of features. - 'sum': Sum across all features. Reduces to 1 feature, so most useful as a final transformation in a regression. - 'prod': Multiplies all features. Reduces to 1 feature, so most useful as a final transformation in a regression. - 'min': Takes minimum value of all features. Reduces to 1 feature, so most useful as a final transformation in a regression. - 'max': Takes maximum value of all features. Reduces to 1 feature, so most useful as a final transformation in a regression. - 'mean': Takes mean of all features. Reduces to 1 feature, so most useful as a final transformation in a regression. - 'softmax': Normalizes the features so that the sum equals 1 on a matrix multiplication by adjustable weights. Most useful as a final transformation in a classification to give class probabilities. The object has several optional arguments: loss_type: String that defines the error measurement term. This is used during training to determine the weights that give the most accurate output. The loss types available are: - 'l2_loss' - Uses tensorflow's nn.l2_loss function on the difference between the predicted and actual. Computes half the L2 norm without the sqrt. Use for regression, and the default loss_type for the regression object. - 'cross_entropy' - Calculates the cross-entropy between two probability distributions as defined in Tensorflow's MNIST tutorial (-tf.reduce_sum(y * tf.log(py_x))). Use for classification, and the default loss_type for the classification object. optimizer: String that defines the optimization algorithm for training. If a string is passed, the optimizers will be used with default learning rates. If you wish to use a custom training rate, instead of a string, pass in a tuple with the tensorflow optimizer as the first index, and a tuple with the arguments to pass in as the second index. The optimizers available are: - 'GradientDescent': Implements the gradient descent algorithm with a default learning rate of 0.001. - 'Adagrad': Implements the Adagrad algorithm with a default learning rate of 0.001. - 'Momentum': Implements the Momentum algorithm with a default learning rate of 0.001 and momentum of 0.1. - 'Adam': Implements the Adam algorithm. - 'FTRL': Implements the FTRL algorithm with a learning rate of 0.001. - 'RMSProp': Implements the RMSProp algorithm with a learning rate of 0.001 and a decay of 0.9. Training -------- Objects are trained by calling object.train() with certain arguments:: trX = training_data try = training_output training_steps = 50 reg.train(trX, try, training_steps) Both objects are trained by passing in a set of data with known outputs. The training input data should be passed in as a numpy array, with each sample as a row and features as the columns. The training output data can take multiple forms: - For regression tasks, it can be an iterable list with one output value for each sample, or it can be a numpy array of shape (n, 1). - For classification tasks, it can be a numpy array of shape (n, m), where m is the number of classes. In this array, there is a 1 in each row in the column of the class that that sample belongs to, and a 0 in all other rows. Otherwise, an iterable list can be passed in with the class name for each sample. This is required is the class names, and not a probability matrix, are to be returned during testing. In addition to the training data and training output, the number of times to iterate over training must be passed in as the third argument. There are several optional arguments for training that control how long training the network takes: - full_train: Denotes whether to use the entire training set each iteration. Set to True by default. - train_size: If full train is set to False, denotes how many samples to use from the training set each iteration of training. Pulls randomly from the training set with possible repeats. Predicting ---------- After the object is trained, the network can be used to predict the output of test data that is given to it by calling object.predict() with certain arguments:: teX = test_data p = reg.predict(teX) The test data should have the same number of features as the training data, though the number of samples may be different. The output for a regression object will be a numpy array of shape (n, ) with the predicted value for each sample. The output for a classification object will be a list with a predicted class for each sample. If a probability matrix is desired, the pass the argument return_encoded = False when predicting, and a numpy array of shape (n, m) will be returned. Closing ------- Calling object.close() will close the network, freeing up resources. It cannot be used again, and a new object must be started for training and predicting to occur. Evolving ======== For those who do not know the neural network architecture for your problem, we can use a genetic selection algorithm to evolve the optimal architecture. To do this, use the command evolve() with several required arguments: - predict_type: String denoting the type of neural network to evolve. Two options: 'regression' and 'classification'. - fitness_measure: String denoting the type of measurement to use for evaluating the performance of the network type. Options: - 'rmse': Root mean squared error between the predicted values and known values. Use for regression. - 'r_squared': Coefficient of determination for determining how well the data fits the model. Use for regression. - 'accuracy': Fraction of samples that were classified correctly. Use for classification, and can be used for multi-class classification. - 'sensitivity': Fraction of positive samples correctly identified as positive. Use for classification with two classes, and the second class is the positive class. - 'specificity': Fraction of negative samples correctly identified as negative. Use for classification with two classes, and the first class is the negative class. - trX: Numpy array with input data to use for training. Will pull randomly from this array to create test and training sets. - trY: Numpy array with output data to use for training. After the evolution finishes, it will return a net_type and optimizer that can be fed into an regression or classification object, along with the measurement that net_type produced. If "Error during training" is printed, it only means that an error was encountered at some point during the evolution.:: net_type, opt, m = etf.evolve_functions.evolve('classification', 'accuracy', trX, trY) There are many optional arguments that allow for customization of the evolution: - max_layers: Integer denoting the maximum number of layers that exist between the input and output layer. Set at 5 by default. - num_gens: Number of generations to simulate. Set at 10 by default. - gen_size: Number of individual members per generation. Set at 40 by default. - teX: If a specific test set is desired, enter the input data here as a numpy array. - teY: Test output data as a numpy array. - layer_types: List of strings denoting the layer types possible to be used. Can have repeated types for an increased probability of incorporation. Set to ['relu', 'softplus', 'dropout', 'bias_add', 'sigmoid', 'tanh', 'none', 'normalize'] by default. - layer_sizes: List of integers denoting the layer sizes possible to be used. Layer sizes of 0 drop out a layer. List must be of the same length as layer_types. Set to [0, 0, 10, 50, 100, 200, 500, 1000] by default. - end_types: List of strings denoting the options for the type of transformation that gives the output. Forced to be softmax by default during classification. List must be of same length as layer_types. Set to ['sum', 'prod', 'min', 'max', 'mean', 'none', 'sigmoid', 'tanh'] by default. - train_types: List of strings denoting the optimizer types possible to be used. List must be of same length as layer_types. Set to ['GradientDescent', 'GradientDescent', 'GradientDescent', 'Adagrad', 'Momentum', 'Adam', 'Ftrl', 'RMSProp'] by default. - cross_prob: Float value denoting the probability of crossing the genetics of different individuals. Set at 0.2 by default. - mut_prob: Float value denoting the probability of changing the genetics of a single individual. Set at 0.2 by default. - tourn_size: Integer denoting the number of individuals to carry from each generation. Set at 5 by default. - train_iters: Integer denoting the number of training iterations to use for each neural network. Set at 5 by default. - squash_errors: Boolean value denoting whether to give a fail value if the network results in an error. Set to True by default. Recommended to leave true, as it is difficult to complete a long evolution without running into some type of error. Licenses ======== The code which makes up this Python project template is licensed under the MIT/X11 license. Feel free to use it in your free software/open-source or proprietary projects. Issues ====== Please report any bugs or requests that you have using the GitHub issue tracker! Development =========== If you wish to contribute, first make your changes. Then run the following from the project root directory:: source internal/test.sh This will copy the template directory to a temporary directory, run the generation, then run tox. Any arguments passed will go directly to the tox command line, e.g.:: source internal/test.sh -e py27 This command line would just test Python 2.7. Acknowledgements ================ Both Tensorflow and DEAP were creating by other (very smart) people, this package just combines the two. This package was set up using Sean Fisk's Python Project Template package. Authors ======= * Calvin Schmidt ================================================ FILE: docs/Makefile ================================================ # Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = -W SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/EasyTensorflow.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/EasyTensorflow.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $HOME/.local/share/devhelp/EasyTensorflow" @echo "# ln -s $(BUILDDIR)/devhelp $HOME/.local/share/devhelp/EasyTensorflow" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." ================================================ FILE: docs/make.bat ================================================ @ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=build set SPHINXOPTS=-W set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source set I18NSPHINXOPTS=%SPHINXOPTS% source if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\EasyTensorflow.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\EasyTensorflow.qhc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) :end ================================================ FILE: docs/source/README ================================================ Run `sphinx-apidoc -o . ../../easy_tensorflow' in this directory. This will generate `modules.rst' and `easy_tensorflow.rst'. Then include `modules.rst' in your `index.rst' file. ================================================ FILE: docs/source/_static/.gitkeep ================================================ ================================================ FILE: docs/source/conf.py ================================================ # -*- coding: utf-8 -*- # This file is based upon the file generated by sphinx-quickstart. However, # where sphinx-quickstart hardcodes values in this file that you input, this # file has been changed to pull from your module's metadata module. # # This file is execfile()d with the current directory set to its containing # dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import os import sys # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. sys.path.insert(0, os.path.abspath('../..')) # Import project metadata from easy_tensorflow import metadata # -- General configuration ---------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.todo', 'sphinx.ext.coverage', 'sphinx.ext.viewcode'] # show todos todo_include_todos = True # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = metadata.project copyright = metadata.copyright # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = metadata.version # The full version, including alpha/beta/rc tags. release = metadata.version # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. #language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # -- Options for HTML output -------------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'nature' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. #html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. #html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Output file base name for HTML help builder. htmlhelp_basename = metadata.project_no_spaces + 'doc' # -- Options for LaTeX output ------------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, # documentclass [howto/manual]). latex_documents = [ ('index', metadata.project_no_spaces + '.tex', metadata.project + ' Documentation', metadata.authors_string, 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output ------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', metadata.package, metadata.project + ' Documentation', metadata.authors_string, 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ----------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', metadata.project_no_spaces, metadata.project + ' Documentation', metadata.authors_string, metadata.project_no_spaces, metadata.description, 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = { 'python': ('http://docs.python.org/', None), } # Extra local configuration. This is useful for placing the class description # in the class docstring and the __init__ parameter documentation in the # __init__ docstring. See # for more # information. autoclass_content = 'both' ================================================ FILE: docs/source/index.rst ================================================ EasyTensorflow ============== Contents: .. toctree:: :maxdepth: 2 .. only:: html Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` ================================================ FILE: easy_tensorflow/__init__.py ================================================ # -*- coding: utf-8 -*- """Provides objects that allow for easy setup, training, and running of neural networks, based on Google's tensorflow library. Also allows for the evolution of effective networks using a genetic algorithm derived from the DEAP package.""" from easy_tensorflow import metadata, tf_functions, evolve_functions __version__ = metadata.version __author__ = metadata.authors[0] __license__ = metadata.license __copyright__ = metadata.copyright ================================================ FILE: easy_tensorflow/evolve_functions.py ================================================ import numpy as np import scipy.stats import random from deap import base from deap import creator from deap import tools import tf_functions def rmse(p, t): ''' Calculates the root means squared error between two vectors. :param p: One-dimensional numpy array with the predicted values. :param t: One-dimensional numpy array with the known values. :return: Float value between 0 and infinity. ''' return np.sqrt(np.average(np.square(np.subtract(p, t)))) def r_squared(p, t): ''' Coefficient of determination for determining how well the data fits the model. :param p: Numpy array with the predicted values. :param t: Numpy array with the known values. :return: Float value between 0 and 1. ''' # Reshapes into one-dimensional array if necessary. if len(t.shape) == 2: t = [i[0] for i in t] if len(p.shape) == 2: p = [i[0] for i in p] # Get r value for squaring. slope, intercept, r_value, p_value, std_err = scipy.stats.linregress(p, t) return r_value ** 2 def accuracy(p, t): ''' Fraction of samples that were classified correctly. :param p: Multi-dimensional numpy array with the predicted values. :param t: Multi-dimensional numpy array with the known values. :return: Float value between 0 and 1. ''' return sum([1 for i, j in zip(np.argmax(p, axis=1), np.argmax(t, axis=1)) if i == j]) / float(len(p)) def specificity(p, t): ''' Fraction of positive samples correctly identified as positive. :param p: Multi-dimensional numpy array with the predicted values. :param t: Multi-dimensional numpy array with the known values. :return: Float value between 0 and 1. ''' # Calculates number of correctly identified positive samples. num = sum([1 for i, j in zip(np.argmax(p, axis=1), np.argmax(t, axis=1)) if i == 0 and j == 0]) # Calculates total number of positive samples. denom = sum([1 for i in np.argmax(t, axis=1) if i == 0]) return num / float(denom) def sensitivity(p, t): ''' Fraction of negative samples correctly identified as negative. :param p: Multi-dimensional numpy array with the predicted values. :param t: Multi-dimensional numpy array with the known values. :return: Float value between 0 and 1. ''' # Calculates number of correctly identified negative samples. num = sum([1 for i, j in zip(np.argmax(p, axis=1), np.argmax(t, axis=1)) if i == 1 and j == 1]) # Calculates total number of negative samples. denom = sum([1 for i in np.argmax(t, axis=1) if i == 1]) return num / float(denom) def test_train_split(X, y, num_test = 0): ''' Splits a full set of input and output data randomly into train and test sets. Keeps the input and output values connected. :param X: Numpy array with input data. :param y: Numpy array with output data. :param num_test: Number of samples to use for the test set. Set to 0 by default, which cause 1/5 of the full set to be split off for testing. :return: Four numpy arrays corresponding to the training input, training output, testing input, and testing output data. ''' # Splits off 1/5 of data if no specific amount is given. if num_test == 0: num_test = y.shape[0] / 5 # Turns one-dimensional vector into array of shape (n, 1). if len(y.shape) == 1: y = y.reshape(len(y), 1) # Records the number of output features. output_features = y.shape[1] # Combines the input and output features so that the outputs can stay connected. all_vals = np.append(X, y, 1) # Randomly shuffles the samples. np.random.shuffle(all_vals) # Pulls out the test and train input and output features. teX, teY = all_vals[:num_test, :-output_features], all_vals[:num_test, -output_features:] trX, trY = all_vals[num_test:, :-output_features], all_vals[num_test:, -output_features:] return trX, trY, teX, teY def evolve(predict_type, fitness_measure, trX, trY, max_layers = 5, num_gens = 10, gen_size = 40, teX = [], teY = [], layer_types = ['relu', 'softplus', 'dropout', 'bias_add', 'sigmoid', 'tanh', 'none', 'normalize'], layer_sizes = [0, 0, 10, 50, 100, 200, 500, 1000], end_types = ['sum', 'prod', 'min', 'max', 'mean', 'none', 'sigmoid', 'tanh'], train_types = ['GradientDescent', 'GradientDescent', 'GradientDescent', 'Adagrad', 'Momentum', 'Adam', 'Ftrl', 'RMSProp'], cross_prob = 0.2, mut_prob = 0.2, tourn_size = 5, train_iters = 5, squash_errors = True): ''' :param predict_type: String denoting the type of neural network to evolve. Two options: 'regression' and 'classification'. :param fitness_measure: String denoting the type of measurement to use for evaluating the performance of the network type. Options: - 'rmse': Root mean squared error between the predicted values and known values. Use for regression. - 'r_squared': Coefficient of determination for determining how well the data fits the model. Use for regression. - 'accuracy': Fraction of samples that were classified correctly. Use for classification, and can be used for multi-class classification. - 'sensitivity': Fraction of positive samples correctly identified as positive. Use for classification with two classes, and the second class is the positive class. - 'specificity': Fraction of negative samples correctly identified as negative. Use for classification with two classes, and the first class is the negative class. :param trX: Numpy array with input data to use for training. Will pull randomly from this array to create test and training sets. :param trY: Numpy array with output data to use for training. :param max_layers: Integer denoting the maximum number of layers that exist between the input and output layer. Set at 5 by default. :param num_gens: Number of generations to simulate. Set at 10 by default. :param gen_size: Number of individual members per generation. Set at 40 by default. :param teX: If a specific test set is desired, enter the input data here as a numpy array. :param teY: Test output data as a numpy array. :param layer_types: List of strings denoting the layer types possible to be used. Set to ['relu', 'softplus', 'dropout', 'bias_add', 'sigmoid', 'tanh', 'none', 'normalize'] by default. :param layer_sizes: List of integers denoting the layer sizes possible to be used. List must be of the same length as layer_types. Set to [0, 0, 10, 50, 100, 200, 500, 1000] by default. :param end_types: List of strings denoting the options for the type of transformation that gives the output. List must be of same length as layer_types. Set to ['sum', 'prod', 'min', 'max', 'mean', 'none', 'sigmoid', 'tanh'] by default. :param train_types: List of strings denoting the optimizer types possible to be used. List must be of same length as layer_types. Set to ['GradientDescent', 'GradientDescent', 'GradientDescent', 'Adagrad', 'Momentum', 'Adam', 'Ftrl', 'RMSProp'] by default. :param cross_prob: Float value denoting the probability of crossing the genetics of different individuals. Set at 0.2 by default. :param mut_prob: Float value denoting the probability of changing the genetics of a single individual. Set at 0.2 by default. :param tourn_size: Integer denoting the number of individuals to carry from each generation. Set at 5 by default. :param train_iters: Integer denoting the number of training iterations to use for each neural network. Set at 5 by default. :param squash_errors: Boolean value denoting whether to give a fail value if the network results in an error. Set to True by default. :return: List of strings giving the best net_type, string denoting the best optimizer, and Float value denoting the measure of the best network type. ''' # Checks that the different options have the same size. if not len(layer_types) == len(layer_sizes) == len(end_types) == len(train_types): print('Input attribute lists have different sizes.') return None # Gets the type of network to check. if predict_type == 'regression': predictor = tf_functions.Regresser elif predict_type == 'classification': predictor = tf_functions.Classifier end_types = ['softmax'] * len(layer_types) # Gets the type of success measure to use. if fitness_measure == 'rmse': measure = rmse creator.create("FitnessMin", base.Fitness, weights=(-1.0,)) creator.create("Individual", list, fitness=creator.FitnessMin) fail_val = np.inf elif fitness_measure == 'r_squared': measure = r_squared creator.create("FitnessMax", base.Fitness, weights=(1.0,)) creator.create("Individual", list, fitness=creator.FitnessMax) fail_val = 0 elif fitness_measure == 'accuracy': measure = accuracy creator.create("FitnessMax", base.Fitness, weights=(1.0,)) creator.create("Individual", list, fitness=creator.FitnessMax) fail_val = 0 elif fitness_measure == 'specificity': measure = specificity creator.create("FitnessMax", base.Fitness, weights=(1.0,)) creator.create("Individual", list, fitness=creator.FitnessMax) fail_val = 0 elif fitness_measure == 'sensitivity': measure = sensitivity creator.create("FitnessMax", base.Fitness, weights=(1.0,)) creator.create("Individual", list, fitness=creator.FitnessMax) fail_val = 0 toolbox = base.Toolbox() # Attribute generator. toolbox.register("attr_ints", random.randint, 0, len(layer_types) - 1) # Structure initializers. toolbox.register("individual", tools.initRepeat, creator.Individual, toolbox.attr_ints, n=(max_layers * 2) + 2) toolbox.register("population", tools.initRepeat, list, toolbox.individual) # Operator registering. toolbox.register("mate", tools.cxTwoPoint) toolbox.register("mutate", tools.mutUniformInt,low = 0, up = len(layer_types) - 1, indpb=0.5) toolbox.register("select", tools.selTournament, tournsize = tourn_size) # Gets the initial population. pop = toolbox.population(n = gen_size) # Performs an initial selection. for ind in pop: # Turns the individual's genes into a net_type and an optimizer. net_type = [] for i in range(max_layers): net_type.append(layer_types[ind[i * 2]]) net_type.append(layer_sizes[ind[i * 2 + 1]]) net_type.append(end_types[ind[-2]]) # Splits into test and train if needed. if teX == []: ttrX, ttrY, tteX, tteY = test_train_split(trX, trY) else: ttrX, ttrY, tteX, tteY = trX, trY, teX, teY # Attempts to test network if errors to be squashed. if squash_errors: try: # Sets up, trains, and tests network. ind_predictor = predictor(net_type, optimizer = train_types[ind[-1]]) ind_predictor.train(ttrX, ttrY, train_iters) p = ind_predictor.predict(tteX) m = measure(p, tteY) ind_predictor.close() # Upon an error, gives the worst possible value. except: m = fail_val if np.isnan(m): m = fail_val else: ind_predictor = predictor(net_type, optimizer = train_types[ind[-1]]) ind_predictor.train(ttrX, ttrY, train_iters) p = ind_predictor.predict(tteX) m = measure(p, tteY) ind_predictor.close() ind.fitness.values = (m,) # Begins the evolution. for g in range(num_gens): # Selects the next generation individuals. offspring = toolbox.select(pop, len(pop)) # Clones the selected individuals. offspring = list(map(toolbox.clone, offspring)) # Applies crossover and mutation on the offspring. for child1, child2 in zip(offspring[::2], offspring[1::2]): if random.random() < cross_prob: toolbox.mate(child1, child2) del child1.fitness.values del child2.fitness.values for mutant in offspring: if random.random() < mut_prob: toolbox.mutate(mutant) del mutant.fitness.values # Evaluates the individuals with an invalid fitness. invalid_ind = [ind for ind in offspring if not ind.fitness.valid] for ind in invalid_ind: net_type = [] for i in range(max_layers): net_type.append(layer_types[ind[i * 2]]) net_type.append(layer_sizes[ind[i * 2 + 1]]) net_type.append(end_types[ind[-2]]) if squash_errors: try: ind_predictor = predictor(net_type, optimizer = train_types[ind[-1]]) ind_predictor.train(ttrX, ttrY, train_iters) p = ind_predictor.predict(tteX) m = measure(p, tteY) ind_predictor.close() except: m = fail_val if np.isnan(m): m = fail_val else: ind_predictor = predictor(net_type, optimizer = train_types[ind[-1]]) ind_predictor.train(ttrX, ttrY, train_iters) p = ind_predictor.predict(tteX) m = measure(p, tteY) ind_predictor.close() ind.fitness.values = (m,) # The population is entirely replaced by the offspring. pop[:] = offspring # Gets the best individual remaining after best_ind = tools.selBest(pop, 1)[0] net_type = [] for i in range(max_layers): net_type.append(layer_types[best_ind[i * 2]]) net_type.append(layer_sizes[best_ind[i * 2 + 1]]) net_type.append(end_types[best_ind[-2]]) optimizer = train_types[best_ind[-1]] return net_type, optimizer, best_ind.fitness.values ================================================ FILE: easy_tensorflow/main.py ================================================ #!/usr/bin/env python # -*- coding: utf-8 -*- """Program entry point""" from __future__ import print_function import argparse import sys from easy_tensorflow import metadata, tf_functions, evolve_functions def main(argv): """Program entry point. :param argv: command-line arguments :type argv: :class:`list` """ author_strings = [] for name, email in zip(metadata.authors, metadata.emails): author_strings.append('Author: {0} <{1}>'.format(name, email)) epilog = ''' {project} {version} {authors} URL: <{url}> '''.format( project=metadata.project, version=metadata.version, authors='\n'.join(author_strings), url=metadata.url) arg_parser = argparse.ArgumentParser( prog=argv[0], formatter_class=argparse.RawDescriptionHelpFormatter, description=metadata.description, epilog=epilog) arg_parser.add_argument( '-V', '--version', action='version', version='{0} {1}'.format(metadata.project, metadata.version)) arg_parser.parse_args(args=argv[1:]) print(epilog) return 0 def entry_point(): """Zero-argument entry point for use with setuptools/distribute.""" raise SystemExit(main(sys.argv)) if __name__ == '__main__': entry_point() ================================================ FILE: easy_tensorflow/metadata.py ================================================ # -*- coding: utf-8 -*- """Project metadata Information describing the project. """ # The package name, which is also the "UNIX name" for the project. package = 'easy_tensorflow' project = "EasyTensorflow" project_no_spaces = project.replace(' ', '') version = '0.1' description = 'Provides objects that allow for easy setup, training, and running of neural networks, based on Google\'s ' \ 'tensorflow library. Also allows for the evolution of effective networks using a genetic algorithm derived' \ ' from the DEAP package.' authors = ['Calvin Schmidt'] authors_string = ', '.join(authors) emails = ['calvins@stanford.edu'] license = 'MIT' copyright = '2015 ' + authors_string url = 'https://github.com/calvinschmdt/EasyTensorflow' ================================================ FILE: easy_tensorflow/tf_dictionaries.py ================================================ import tensorflow as tf transform_dict = { 'relu': (tf.nn.relu, (tf.nn.bias_add, ((tf.matmul, ('X', 'weight1')), 'weight2'))), 'softplus': (tf.nn.softplus, (tf.nn.bias_add, ((tf.matmul, ('X', 'weight1')), 'weight2'))), 'dropout': (tf.nn.dropout, ('X', 'weight')), 'bias_add': (tf.nn.bias_add, ('X', 'weight')), 'sigmoid': (tf.nn.sigmoid, (tf.nn.bias_add, ((tf.matmul, ('X', 'weight1')), 'weight2'))), 'tanh': (tf.nn.tanh, (tf.nn.bias_add, ((tf.matmul, ('X', 'weight1')), 'weight2'))), 'none': (tf.nn.bias_add, ((tf.matmul, ('X', 'weight1')), 'weight2')), 'normalize': (tf.nn.l2_normalize, ('X', 1)), 'sum': (tf.reduce_sum, ('X', 1)), 'prod': (tf.reduce_prod, ('X', 1)), 'min': (tf.reduce_min, ('X', 1)), 'max': (tf.reduce_max, ('X', 1)), 'mean': (tf.reduce_mean, ('X', 1)), 'softmax': (tf.nn.softmax, (tf.matmul, ('X', 'weight'))) } optimizer_dict = { 'GradientDescent': (tf.train.GradientDescentOptimizer, (0.001, )), 'Adagrad': (tf.train.AdagradOptimizer, (0.001, )), 'Momentum': (tf.train.MomentumOptimizer, (0.001, 0.1)), 'Adam': (tf.train.AdamOptimizer, ()), 'Ftrl': (tf.train.FtrlOptimizer, (0.001, )), 'RMSProp': (tf.train.RMSPropOptimizer, (0.001, 0.9)) } ================================================ FILE: easy_tensorflow/tf_functions.py ================================================ import tensorflow as tf import numpy as np import random import tf_dictionaries def random_index_list(list_size, sample_size): ''' Creates a list of random integers that are constrained in the max value. May have repeating indexes. :param list_size: Integer denoting the length of the final list. :param sample_size: Integer denoting the maximum index to include. :return: List of integers. ''' return [random.randint(0, sample_size - 1) for i in range(0, list_size)] def encode_classifications(class_list): ''' Given a list of class labels, encodes the list in a manner that is usable in neural networks. For each sample, there will be a list of 0s and one 1 corresponding to the label that sample is encoded. Also returns the list of so that encoded sample lists can be decoded. :param class_list: List of labels, that can be either strings or numbers. :return: classes: List of strings denoting the different labels found in the list. encoded: Numpy array of encoded labels. One row for each sample, with the numbers in the row corresponding to the label for that sample. ''' # Iterates through the list, recording all the samples. classes = [] for i in class_list: if i not in classes: classes.append(i) # Creates a list of 0s for each sample. encoded = [[0] * len(classes) for i in range(len(class_list))] # Iterates through the list to be encoded, marking a 1 in the position for the given label for that sample. for e, i in enumerate(class_list): encoded[e][classes.index(i)] = 1 return classes, np.array(encoded) def decode_classifications(classes, encoded): ''' Given an array of labels encoded in array format, returns a that array as their original labels. :param classes: List of strings denoting the different labels found in the list. :param encoded: Numpy array of encoded labels. One row for each sample, with the numbers in the row corresponding to the label for that sample. :return: List of strings denoting the label of each sample. ''' return [classes[i] for i in encoded] def unpack_transform(transform, X, weight): ''' Turns a tuple of tensorflow functions and their inputs into tensorflow functions ready to use. :param transform: Tuple containing a tensorflow tensor as the first part, and a tuple containing the tensor input in the second part. :param X: Tensor containing the input matrix to be transformed. :param weight: Tensor, integer, or float containing a weight as the second tensor input. :return: Completed tensor, with inputs placed in. ''' # If the first part of the tensor's input should be X, then applies the X as the incoming tensor. if transform[1][0] == 'X': # If a weight tensor or value should be applied to the tensor, then uses the supplied weight. If there is not a # weight, uses 1 to direct the axis of tensor transformation. if transform[1][1] == 'weight' or transform[1][1] == 'weight1': w = weight elif transform[1][1] == 1: w = 1 return transform[0](X, w) if transform[1][1] == 'weight2': return transform[0](unpack_transform(transform[1][0], X, weight[0]), weight[1]) # If the first part of the tensor's input is not X, then unpacks the tensor that should be the input, and uses that. else: return transform[0](unpack_transform(transform[1], X, weight)) def compile_model(X, weights, models, transform_dict): ''' Converts lists of weights and models into a tensorflow neural network that can take in an input tensor and return an output tensor. :param X: Tensor that contains the input values to be transformed through the network. :param weights: List of tensors (or single value in the case of dropouts) that transform the X value through the layers. :param models: List of the different types of layers. Should be in the same order as the weights list, and be the desired order of layers. :return: Tensor that has been transformed through the different layers. ''' # Iterates through each layer. for model, weight in zip(models, weights): # Finds the layer type, and transforms through that layer type. transform = transform_dict[model] X = unpack_transform(transform, X, weight) return X def make_model(X, input_size, output_size, net_type, transform_dict): ''' Creates the lists of weight tensors and layer types that are used to compile a tensorflow neural network. :param X: Tensor that contains the input values to be transformed through the network. :param input_size: Float value giving the number of features being fed into the network. :param output_size: Float value giving the number of output values desired. :param net_type: List of alternating string values and integer values. Must always start and end with a string values. The strings denote the type of each layer. The integer values denote the end size of each layer, though this is constrained for certain layer types. Sizes of zero drop that layer out. :return: Function that represents the neural network. ''' weights = [] models = [] started = False last = input_size # Iterates through each layer of the network. for e in range(0, len(net_type) - 1, 2): # Creates the weight type for the layer, depending on the layer type. Dropouts don't use a tensor, just a float # value. if net_type[e] == 'dropout' or net_type[e] == 'normalize': next = last weight = random.random() # Bias add uses a linear layer to transform without changing the size. elif net_type[e] == 'bias_add': next = last weight = tf.Variable(tf.constant(0.0, shape=[next])) elif net_type[e] == 'relu' or net_type[e] == 'softplus' or net_type[e] == 'tanh' or \ net_type[e] == 'sigmoid' or net_type[e] == 'none': next = last weight1 = tf.Variable(tf.random_normal([last, next], stddev=0.01)) weight2 = tf.Variable(tf.constant(0.0, shape=[next])) weight = (weight1, weight2) # Other types use a matrix to transform the size. else: next = net_type[e + 1] weight = tf.Variable(tf.random_normal([last, next], stddev=0.01)) # Records the layer type and weight used for transforming into that layer for each layer. if net_type[e + 1] > 0: # The first layer pulls from the input size, while the rest pull from the last size. if not started: weights.append(weight) models.append(net_type[e]) last = next started = True else: weights.append(weight) models.append(net_type[e]) last = next # Makes sure the last layer can change the size. if net_type[-1] == 'bias_add' or net_type[-1] == 'dropout' or net_type[-1] == 'normalize': final = 'sigmoid' else: final = net_type[-1] # Adds in the last layer. if final == 'relu' or final == 'softplus' or final == 'tanh' or \ final == 'sigmoid' or final == 'none': weight1 = tf.Variable(tf.random_normal([last, output_size], stddev=0.01)) weight2 = tf.Variable(tf.random_normal([output_size], stddev=0.01)) weight = (weight1, weight2) else: weight = tf.Variable(tf.random_normal([last, output_size], stddev=0.01)) weights.append(weight) models.append(final) # Returns the compiled function. return compile_model(X, weights, models, transform_dict) def train_tensorflow(sess, trX, trY, train_steps, full_train, train_size, net_type, transform_dict, loss_type, optimizer, optimizer_dict): ''' Automatically constructs, trains, and tests a tensorflow neural network, returning the r squared value of the output. :param sess: A tensorflow session. :param trX: Numpy array that contains the training features. :param trY: Numpy array that contains the training outputs. Must have shape of at least 1 on columns. :param train_steps: Integer value denoting the number of times to iterate through training. :param full_train: Boolean value denoting whether to use the full training set for each iteration. :param train_size: Integer value denoting the number of samples to pull from the training set for each iteration of training. :param net_type: List of alternating string values and integer values. Must always start and end with a string values. The strings denote the type of each layer. The integer values denote the end size of each layer, though this is constrained for certain layer types. Sizes of zero drop that layer out. :param transform_dict: Dictionary of strings to tuples of tensors that encode how to set up the layers of the neural network. :param loss_type: String denoting type of tensor to use for loss type. Use l2_loss for regression, cross_entropy for classification. :param optimizer: String denoting the type of optimization tensor to use for training the neural network. :param optimizer_dict: Dictionary of strings to tuples of tensors that encode how to set up the optimizers of the neural network. :return: predict_op: Tensor that encodes the neural network. X: Placeholder tensor for the features array. y: Placeholder tensor for the output array. ''' # Set up input and output tensors. X = tf.placeholder("float", [None, trX.shape[1]]) y = tf.placeholder("float", [None, trY.shape[1]]) # Set up network. py_x = make_model(X, trX.shape[1], trY.shape[1], net_type, transform_dict) # Set up cost and training type. if loss_type == 'l2_loss': cost = tf.nn.l2_loss(tf.sub(py_x, y)) elif loss_type == 'cross_entropy': cost = -tf.reduce_sum(y * tf.log(py_x)) # Gets the optimizer to be used for training and sets it up. if type(optimizer) == str: train_op = optimizer_dict[optimizer][0](*optimizer_dict[optimizer][1]).minimize(cost) else: train_op = optimizer[0](*optimizer[1]).minimize(cost) predict_op = py_x # Initialize session. init = tf.initialize_all_variables() sess.run(init) # Trains given number of times try: for i in range(train_steps): # If full_train is selected, the trains on the full set of training data, in 100 sample increments. if full_train: for start, end in zip(range(0, len(trX), 100), range(100, len(trX), 100)): sess.run(train_op, feed_dict={X: trX[start:end], y: trY[start:end]}) # If full_train is not selected, then trains on a random set of samples from the training data. else: indices = random_index_list(train_size, len(trY)) sess.run(train_op, feed_dict={X: trX[indices], y: trY[indices]}) # If training throws an error for whatever reason, stops the program from breaking. Throws an error message and \ # closes the session. except: print("Error during training") sess.close() return None return predict_op, X, y class Classifier: ''' Object that holds a neural network used for classifying a set of data. ''' def __init__(self, net_type, loss_type = 'cross_entropy', optimizer = 'Adam'): ''' Initializing function. Records the neural network type and, if given, the loss type and optimizer type. :param net_type: List of alternating string values and integer values. Must always start and end with a string values. The strings denote the type of each layer. The integer values denote the end size of each layer, though this is constrained for certain layer types. Sizes of zero drop that layer out. :param loss_type: String denoting type of tensor to use for loss type. Set as cross_entropy by default. :param optimizer: String denoting the type of optimization tensor to use for training the neural network. Set as Adam by default. Tuple containing optimization tensor and input tuple can be used in place of a string to set specific parameters for the optimization. ''' # Sets up initial parameters and starts the tensorflow session. self.net_type = net_type self.loss_type = loss_type self.optimizer = optimizer self.sess = tf.Session() self.transform_dict = tf_dictionaries.transform_dict self.optimizer_dict = tf_dictionaries.optimizer_dict def train(self, trX, trY, iterations, full_train = True, train_size = 0): ''' Sets up and trains the neural network. :param trX: Numpy array that contains the training features. :param trY: Numpy array that contains the training outputs. Can be in the encoded or unencoded format. :param iterations: Integer denoting the number of iterations to train the model. :param full_train: Boolean value denoting whether to use the full training set for each iteration. Set as True by default. :param train_size: Integer value denoting the number of samples to pull from the training set for each iteration of training. Set as 0 by default. :return: Does not return anything, but stores the input, output, and transformation tensors for predicting. ''' # If the labels are not encoded in matrix format, does that and stores the list of classes. if trY.shape[1] == 1: self.class_list, trY = encode_classifications(trY) # Sets up and trains tensorflow. self.predict_op, self.X, self.y = train_tensorflow(self.sess, trX, trY, iterations, full_train, train_size, self.net_type, self.transform_dict, self.loss_type, self.optimizer, self.optimizer_dict) def predict(self, teX, return_encoded = True): ''' Uses the trained neural network to classify the samples based on their features. :param teX: Numpy array of features to be used for the classification. :param return_encoded: Boolean value denoting whether to decode the classifications if needed. Must have generated class list by encoding during the training. :return: Either encoded or decoded classifications for the input samples as a numpy array or list. ''' # Use the neural network for predicting the classes. p = self.sess.run(self.predict_op, feed_dict={self.X: teX}) # Decodes if desired. if not return_encoded: p = np.argmax(p, axis=1) return decode_classifications(p, self.class_list) return p def close(self): ''' Closes the session. :return: Nothing, but closes the session. ''' self.sess.close() class Regresser: ''' Object that holds a neural network used for predicting a set of data's numerical outputs. ''' def __init__(self, net_type, loss_type = 'l2_loss', optimizer = 'Adam'): ''' Initializing function. Records the neural network type and, if given, the loss type and optimizer type. :param net_type: List of alternating string values and integer values. Must always start and end with a string values. The strings denote the type of each layer. The integer values denote the end size of each layer, though this is constrained for certain layer types. Sizes of zero drop that layer out. :param loss_type: String denoting type of tensor to use for loss type. Set as l2_loss by default. :param optimizer: String denoting the type of optimization tensor to use for training the neural network. Set as Adam by default. Tuple containing optimization tensor and input tuple can be used in place of a string to set specific parameters for the optimization. ''' # Sets up initial parameters and starts the tensorflow session. self.net_type = net_type self.loss_type = loss_type self.optimizer = optimizer self.sess = tf.Session() self.transform_dict = tf_dictionaries.transform_dict self.optimizer_dict = tf_dictionaries.optimizer_dict def train(self, trX, trY, iterations, full_train = True, train_size = 0): ''' Sets up and trains the neural network. :param trX: Numpy array that contains the training features. :param trY: Numpy array that contains the training outputs. If if in vector format, then reshapes as array. :param iterations: Integer denoting the number of iterations to train the model. :param full_train: Boolean value denoting whether to use the full training set for each iteration. Set as True by default. :param train_size: Integer value denoting the number of samples to pull from the training set for each iteration of training. Set as 0 by default. :return: Does not return anything, but stores the input, output, and transformation tensors for predicting. ''' # The training outputs are not in the correct shape, fits into correct shape. if len(trY.shape) == 1: trY = trY.reshape(len(trY), 1) # Sets up and trains tensorflow. self.predict_op, self.X, self.y = train_tensorflow(self.sess, trX, trY, iterations, full_train, train_size, self.net_type, self.transform_dict, self.loss_type, self.optimizer, self.optimizer_dict) def predict(self, teX): ''' Uses the trained neural network to classify the samples based on their features. :param teX: Numpy array of features to be used for the classification. :return: Numpy vector of predicted outputs.. ''' # Use the neural network for predicting the classes. p = self.sess.run(self.predict_op, feed_dict={self.X: teX}) # Reshapes array of columns into vector. if len(p.shape) > 1: p = np.array([i[0] for i in np.ndarray.tolist(p)]) return np.array(p) def close(self): ''' Closes the session. :return: Nothing, but closes the session. ''' self.sess.close() ================================================ FILE: pavement.py ================================================ # -*- coding: utf-8 -*- from __future__ import print_function import os import sys import time import subprocess # Import parameters from the setup file. sys.path.append('.') from setup import ( setup_dict, get_project_files, print_success_message, print_failure_message, _lint, _test, _test_all, CODE_DIRECTORY, DOCS_DIRECTORY, TESTS_DIRECTORY, PYTEST_FLAGS) from paver.easy import options, task, needs, consume_args from paver.setuputils import install_distutils_tasks options(setup=setup_dict) install_distutils_tasks() ## Miscellaneous helper functions def print_passed(): # generated on http://patorjk.com/software/taag/#p=display&f=Small&t=PASSED print_success_message(r''' ___ _ ___ ___ ___ ___ | _ \/_\ / __/ __| __| \ | _/ _ \\__ \__ \ _|| |) | |_|/_/ \_\___/___/___|___/ ''') def print_failed(): # generated on http://patorjk.com/software/taag/#p=display&f=Small&t=FAILED print_failure_message(r''' ___ _ ___ _ ___ ___ | __/_\ |_ _| | | __| \ | _/ _ \ | || |__| _|| |) | |_/_/ \_\___|____|___|___/ ''') class cwd(object): """Class used for temporarily changing directories. Can be though of as a `pushd /my/dir' then a `popd' at the end. """ def __init__(self, newcwd): """:param newcwd: directory to make the cwd :type newcwd: :class:`str` """ self.newcwd = newcwd def __enter__(self): self.oldcwd = os.getcwd() os.chdir(self.newcwd) return os.getcwd() def __exit__(self, type_, value, traceback): # This acts like a `finally' clause: it will always be executed. os.chdir(self.oldcwd) ## Task-related functions def _doc_make(*make_args): """Run make in sphinx' docs directory. :return: exit code """ if sys.platform == 'win32': # Windows make_cmd = ['make.bat'] else: # Linux, Mac OS X, and others make_cmd = ['make'] make_cmd.extend(make_args) # Account for a stupid Python "bug" on Windows: # with cwd(DOCS_DIRECTORY): retcode = subprocess.call(make_cmd) return retcode ## Tasks @task @needs('doc_html', 'setuptools.command.sdist') def sdist(): """Build the HTML docs and the tarball.""" pass @task def test(): """Run the unit tests.""" raise SystemExit(_test()) @task def lint(): # This refuses to format properly when running `paver help' unless # this ugliness is used. ('Perform PEP8 style check, run PyFlakes, and run McCabe complexity ' 'metrics on the code.') raise SystemExit(_lint()) @task def test_all(): """Perform a style check and run all unit tests.""" retcode = _test_all() if retcode == 0: print_passed() else: print_failed() raise SystemExit(retcode) @task @consume_args def run(args): """Run the package's main script. All arguments are passed to it.""" # The main script expects to get the called executable's name as # argv[0]. However, paver doesn't provide that in args. Even if it did (or # we dove into sys.argv), it wouldn't be useful because it would be paver's # executable. So we just pass the package name in as the executable name, # since it's close enough. This should never be seen by an end user # installing through Setuptools anyway. from easy_tensorflow.main import main raise SystemExit(main([CODE_DIRECTORY] + args)) @task def commit(): """Commit only if all the tests pass.""" if _test_all() == 0: subprocess.check_call(['git', 'commit']) else: print_failure_message('\nTests failed, not committing.') @task def coverage(): """Run tests and show test coverage report.""" try: import pytest_cov # NOQA except ImportError: print_failure_message( 'Install the pytest coverage plugin to use this task, ' "i.e., `pip install pytest-cov'.") raise SystemExit(1) import pytest pytest.main(PYTEST_FLAGS + [ '--cov', CODE_DIRECTORY, '--cov-report', 'term-missing', TESTS_DIRECTORY]) @task # NOQA def doc_watch(): """Watch for changes in the docs and rebuild HTML docs when changed.""" try: from watchdog.events import FileSystemEventHandler from watchdog.observers import Observer except ImportError: print_failure_message('Install the watchdog package to use this task, ' "i.e., `pip install watchdog'.") raise SystemExit(1) class RebuildDocsEventHandler(FileSystemEventHandler): def __init__(self, base_paths): self.base_paths = base_paths def dispatch(self, event): """Dispatches events to the appropriate methods. :param event: The event object representing the file system event. :type event: :class:`watchdog.events.FileSystemEvent` """ for base_path in self.base_paths: if event.src_path.endswith(base_path): super(RebuildDocsEventHandler, self).dispatch(event) # We found one that matches. We're done. return def on_modified(self, event): print_failure_message('Modification detected. Rebuilding docs.') # # Strip off the path prefix. # import os # if event.src_path[len(os.getcwd()) + 1:].startswith( # CODE_DIRECTORY): # # sphinx-build doesn't always pick up changes on code files, # # even though they are used to generate the documentation. As # # a workaround, just clean before building. doc_html() print_success_message('Docs have been rebuilt.') print_success_message( 'Watching for changes in project files, press Ctrl-C to cancel...') handler = RebuildDocsEventHandler(get_project_files()) observer = Observer() observer.schedule(handler, path='.', recursive=True) observer.start() try: while True: time.sleep(1) except KeyboardInterrupt: observer.stop() observer.join() @task @needs('doc_html') def doc_open(): """Build the HTML docs and open them in a web browser.""" doc_index = os.path.join(DOCS_DIRECTORY, 'build', 'html', 'index.html') if sys.platform == 'darwin': # Mac OS X subprocess.check_call(['open', doc_index]) elif sys.platform == 'win32': # Windows subprocess.check_call(['start', doc_index], shell=True) elif sys.platform == 'linux2': # All freedesktop-compatible desktops subprocess.check_call(['xdg-open', doc_index]) else: print_failure_message( "Unsupported platform. Please open `{0}' manually.".format( doc_index)) @task def get_tasks(): """Get all paver-defined tasks.""" from paver.tasks import environment for task in environment.get_tasks(): print(task.shortname) @task def doc_html(): """Build the HTML docs.""" retcode = _doc_make('html') if retcode: raise SystemExit(retcode) @task def doc_clean(): """Clean (delete) the built docs.""" retcode = _doc_make('clean') if retcode: raise SystemExit(retcode) ================================================ FILE: requirements-dev.txt ================================================ # Runtime requirements --requirement requirements.txt # Testing pytest==2.5.1 py==1.4.19 mock==1.0.1 # Linting flake8==2.1.0 mccabe==0.2.1 pep8==1.4.6 pyflakes==0.7.3 # Documentation Sphinx==1.2 docutils==0.11 Jinja2==2.7.1 MarkupSafe==0.18 Pygments==1.6 # Miscellaneous Paver==1.2.1 colorama==0.2.7 # Function deap==1.0 tensorflow ================================================ FILE: requirements.txt ================================================ # Python 2.6 compatibility # argparse==1.2.1 # Function deap==1.0 tensorflow ================================================ FILE: setup.py ================================================ # -*- coding: utf-8 -*- from __future__ import print_function import os import sys import imp import subprocess ## Python 2.6 subprocess.check_output compatibility. Thanks Greg Hewgill! if 'check_output' not in dir(subprocess): def check_output(cmd_args, *args, **kwargs): proc = subprocess.Popen( cmd_args, *args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, **kwargs) out, err = proc.communicate() if proc.returncode != 0: raise subprocess.CalledProcessError(args) return out subprocess.check_output = check_output from setuptools import setup, find_packages from setuptools.command.test import test as TestCommand from distutils import spawn try: import colorama colorama.init() # Initialize colorama on Windows except ImportError: # Don't require colorama just for running paver tasks. This allows us to # run `paver install' without requiring the user to first have colorama # installed. pass # Add the current directory to the module search path. sys.path.insert(0, os.path.abspath('.')) ## Constants CODE_DIRECTORY = 'easy_tensorflow' DOCS_DIRECTORY = 'docs' TESTS_DIRECTORY = 'tests' PYTEST_FLAGS = ['--doctest-modules'] # Import metadata. Normally this would just be: # # from easy_tensorflow import metadata # # However, when we do this, we also import `easy_tensorflow/__init__.py'. If this # imports names from some other modules and these modules have third-party # dependencies that need installing (which happens after this file is run), the # script will crash. What we do instead is to load the metadata module by path # instead, effectively side-stepping the dependency problem. Please make sure # metadata has no dependencies, otherwise they will need to be added to # the setup_requires keyword. metadata = imp.load_source( 'metadata', os.path.join(CODE_DIRECTORY, 'metadata.py')) ## Miscellaneous helper functions def get_project_files(): """Retrieve a list of project files, ignoring hidden files. :return: sorted list of project files :rtype: :class:`list` """ if is_git_project() and has_git(): return get_git_project_files() project_files = [] for top, subdirs, files in os.walk('.'): for subdir in subdirs: if subdir.startswith('.'): subdirs.remove(subdir) for f in files: if f.startswith('.'): continue project_files.append(os.path.join(top, f)) return project_files def is_git_project(): return os.path.isdir('.git') def has_git(): return bool(spawn.find_executable("git")) def get_git_project_files(): """Retrieve a list of all non-ignored files, including untracked files, excluding deleted files. :return: sorted list of git project files :rtype: :class:`list` """ cached_and_untracked_files = git_ls_files( '--cached', # All files cached in the index '--others', # Untracked files # Exclude untracked files that would be excluded by .gitignore, etc. '--exclude-standard') uncommitted_deleted_files = git_ls_files('--deleted') # Since sorting of files in a set is arbitrary, return a sorted list to # provide a well-defined order to tools like flake8, etc. return sorted(cached_and_untracked_files - uncommitted_deleted_files) def git_ls_files(*cmd_args): """Run ``git ls-files`` in the top-level project directory. Arguments go directly to execution call. :return: set of file names :rtype: :class:`set` """ cmd = ['git', 'ls-files'] cmd.extend(cmd_args) return set(subprocess.check_output(cmd).splitlines()) def print_success_message(message): """Print a message indicating success in green color to STDOUT. :param message: the message to print :type message: :class:`str` """ try: import colorama print(colorama.Fore.GREEN + message + colorama.Fore.RESET) except ImportError: print(message) def print_failure_message(message): """Print a message indicating failure in red color to STDERR. :param message: the message to print :type message: :class:`str` """ try: import colorama print(colorama.Fore.RED + message + colorama.Fore.RESET, file=sys.stderr) except ImportError: print(message, file=sys.stderr) def read(filename): """Return the contents of a file. :param filename: file path :type filename: :class:`str` :return: the file's content :rtype: :class:`str` """ with open(os.path.join(os.path.dirname(__file__), filename)) as f: return f.read() def _lint(): """Run lint and return an exit code.""" # Flake8 doesn't have an easy way to run checks using a Python function, so # just fork off another process to do it. # Python 3 compat: # - The result of subprocess call outputs are byte strings, meaning we need # to pass a byte string to endswith. project_python_files = [filename for filename in get_project_files() if filename.endswith(b'.py')] retcode = subprocess.call( ['flake8', '--max-complexity=10'] + project_python_files) if retcode == 0: print_success_message('No style errors') return retcode def _test(): """Run the unit tests. :return: exit code """ # Make sure to import pytest in this function. For the reason, see here: # # NOPEP8 import pytest # This runs the unit tests. # It also runs doctest, but only on the modules in TESTS_DIRECTORY. return pytest.main(PYTEST_FLAGS + [TESTS_DIRECTORY]) def _test_all(): """Run lint and tests. :return: exit code """ return _lint() + _test() # The following code is to allow tests to be run with `python setup.py test'. # The main reason to make this possible is to allow tests to be run as part of # Setuptools' automatic run of 2to3 on the source code. The recommended way to # run tests is still `paver test_all'. # See # Code based on # NOPEP8 class TestAllCommand(TestCommand): def finalize_options(self): TestCommand.finalize_options(self) # These are fake, and just set to appease distutils and setuptools. self.test_suite = True self.test_args = [] def run_tests(self): raise SystemExit(_test_all()) # define install_requires for specific Python versions python_version_specific_requires = [] # as of Python >= 2.7 and >= 3.2, the argparse module is maintained within # the Python standard library, otherwise we install it as a separate package if sys.version_info < (2, 7) or (3, 0) <= sys.version_info < (3, 3): python_version_specific_requires.append('argparse') # See here for more options: # setup_dict = dict( name=metadata.package, version=metadata.version, author=metadata.authors[0], author_email=metadata.emails[0], maintainer=metadata.authors[0], maintainer_email=metadata.emails[0], url=metadata.url, description=metadata.description, long_description=read('README.rst'), # Find a list of classifiers here: # classifiers=[ 'Development Status :: 1 - Planning', 'Environment :: Console', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Natural Language :: English', 'Operating System :: OS Independent', 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: Implementation :: PyPy', 'Topic :: Documentation', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: System :: Installation/Setup', 'Topic :: System :: Software Distribution', ], packages=find_packages(exclude=(TESTS_DIRECTORY,)), install_requires=[ # your module dependencies ] + python_version_specific_requires, # Allow tests to be run with `python setup.py test'. tests_require=[ 'pytest==2.5.1', 'mock==1.0.1', 'flake8==2.1.0', ], cmdclass={'test': TestAllCommand}, zip_safe=False, # don't use eggs entry_points={ 'console_scripts': [ 'easy_tensorflow_cli = easy_tensorflow.main:entry_point' ], # if you have a gui, use this # 'gui_scripts': [ # 'easy_tensorflow_gui = easy_tensorflow.gui:entry_point' # ] } ) def main(): setup(**setup_dict) if __name__ == '__main__': main() ================================================ FILE: tests/test_main.py ================================================ # -*- coding: utf-8 -*- from pytest import raises # The parametrize function is generated, so this doesn't work: # # from pytest.mark import parametrize # import pytest parametrize = pytest.mark.parametrize from easy_tensorflow import metadata from easy_tensorflow.main import main class TestMain(object): @parametrize('helparg', ['-h', '--help']) def test_help(self, helparg, capsys): with raises(SystemExit) as exc_info: main(['progname', helparg]) out, err = capsys.readouterr() # Should have printed some sort of usage message. We don't # need to explicitly test the content of the message. assert 'usage' in out # Should have used the program name from the argument # vector. assert 'progname' in out # Should exit with zero return code. assert exc_info.value.code == 0 @parametrize('versionarg', ['-V', '--version']) def test_version(self, versionarg, capsys): with raises(SystemExit) as exc_info: main(['progname', versionarg]) out, err = capsys.readouterr() # Should print out version. assert err == '{0} {1}\n'.format(metadata.project, metadata.version) # Should exit with zero return code. assert exc_info.value.code == 0 ================================================ FILE: tox.ini ================================================ # Tox (http://tox.testrun.org/) is a tool for running tests in # multiple virtualenvs. This configuration file will run the test # suite on all supported python versions. To use it, "pip install tox" # and then run "tox" from this directory. # # To run tox faster, check out Detox # (https://pypi.python.org/pypi/detox), which runs your tox runs in # parallel. To use it, "pip install detox" and then run "detox" from # this directory. [tox] envlist = py26,py27,py33,pypy,docs [testenv] deps = --no-deps --requirement {toxinidir}/requirements-dev.txt commands = paver test_all [testenv:docs] basepython = python commands = paver doc_html