├── Chapter01 ├── Activity1.01 │ ├── mnist.py │ └── requirements.txt ├── Exercise1.01 │ ├── requirements.txt │ └── test_stack.py └── Exercise1.02 │ ├── mnist.py │ └── requirements.txt ├── Chapter02 ├── Activity2.01 │ ├── .ipynb_checkpoints │ │ └── Activity2.01_Assembling_a_Deep_Learning_System-checkpoint.ipynb │ ├── Activity2.01_Assembling_a_Deep_Learning_System.ipynb │ ├── bitcoin_lstm_v0.h5 │ ├── bitcoin_lstm_v0_trained.h5 │ └── data │ │ ├── BTC-USD.csv │ │ ├── bitcoin_dataset.csv │ │ ├── bitcoin_historical_prices.csv │ │ ├── bitcoin_recent.csv │ │ ├── test_dataset.csv │ │ └── train_dataset.csv ├── Exercise2.01 │ ├── Exercise2.01_Exploring_Bitcoin_Dataset.ipynb │ ├── data │ │ ├── BTC-USD.csv │ │ ├── bitcoin_dataset.csv │ │ ├── bitcoin_historical_prices.csv │ │ ├── bitcoin_recent.csv │ │ ├── test_dataset.csv │ │ └── train_dataset.csv │ └── normalizations.py ├── Exercise2.02 │ ├── Exercise2.02_Creating_a_TensorFlow_Model_Using_Keras.ipynb │ └── bitcoin_lstm_v0.h5 └── images │ ├── 2.1.jpg │ ├── 2.2.png │ └── 2.3.jpg ├── Chapter03 ├── Activity3.01 │ ├── .ipynb_checkpoints │ │ └── Activity3.01_Optimizing_a_deep_learning_model-checkpoint.ipynb │ ├── Activity3.01_Optimizing_a_deep_learning_model.ipynb │ ├── bitcoin_lstm_v0.h5 │ ├── data │ │ ├── BTC-USD.csv │ │ ├── bitcoin_dataset.csv │ │ ├── bitcoin_historical_prices.csv │ │ ├── bitcoin_recent.csv │ │ ├── test_dataset.csv │ │ └── train_dataset.csv │ ├── logs │ │ ├── bitcoin_lstm_v0_run_0_7d47b6 │ │ │ └── train │ │ │ │ ├── events.out.tfevents.1580899648.L-156196731.39696.334.v2 │ │ │ │ ├── events.out.tfevents.1580899654.L-156196731.profile-empty │ │ │ │ └── plugins │ │ │ │ └── profile │ │ │ │ └── 2020-02-05_16-17-34 │ │ │ │ └── local.trace │ │ ├── bitcoin_lstm_v1_run_0_12e0e4 │ │ │ └── train │ │ │ │ ├── events.out.tfevents.1580899657.L-156196731.39696.3231.v2 │ │ │ │ ├── events.out.tfevents.1580899670.L-156196731.profile-empty │ │ │ │ └── plugins │ │ │ │ └── profile │ │ │ │ └── 2020-02-05_16-17-50 │ │ │ │ └── local.trace │ │ ├── bitcoin_lstm_v2_run_0_a4ff9d │ │ │ └── train │ │ │ │ ├── events.out.tfevents.1580899698.L-156196731.39696.9795.v2 │ │ │ │ ├── events.out.tfevents.1580899711.L-156196731.profile-empty │ │ │ │ └── plugins │ │ │ │ └── profile │ │ │ │ └── 2020-02-05_16-18-31 │ │ │ │ └── local.trace │ │ ├── bitcoin_lstm_v3_run_0_5e2911 │ │ │ └── train │ │ │ │ ├── events.out.tfevents.1580899791.L-156196731.39696.18160.v2 │ │ │ │ ├── events.out.tfevents.1580899803.L-156196731.profile-empty │ │ │ │ └── plugins │ │ │ │ └── profile │ │ │ │ └── 2020-02-05_16-20-03 │ │ │ │ └── local.trace │ │ └── bitcoin_lstm_v4_run_0_4b899b │ │ │ └── train │ │ │ ├── events.out.tfevents.1580899832.L-156196731.39696.24785.v2 │ │ │ ├── events.out.tfevents.1580899845.L-156196731.profile-empty │ │ │ └── plugins │ │ │ └── profile │ │ │ └── 2020-02-05_16-20-45 │ │ │ └── local.trace │ └── scripts │ │ ├── __pycache__ │ │ └── utilities.cpython-36.pyc │ │ ├── bitcoin_model.py │ │ ├── utilities - Copy.py │ │ └── utilities.py ├── Exercise3.01 │ ├── .ipynb_checkpoints │ │ └── Exercise3.01_Creating_an_active_training_environment-checkpoint.ipynb │ ├── Exercise3.01_Creating_an_active_training_environment.ipynb │ ├── bitcoin_lstm_v0.h5 │ ├── bitcoin_lstm_v0_trained.h5 │ ├── data │ │ ├── BTC-USD.csv │ │ ├── bitcoin_dataset.csv │ │ ├── bitcoin_historical_prices.csv │ │ ├── bitcoin_recent.csv │ │ ├── test_dataset.csv │ │ └── train_dataset.csv │ ├── logs │ │ └── bitcoin_lstm_v0_run_0 │ │ │ └── train │ │ │ └── events.out.tfevents.1581417838.L-156196731.1744.10130.v2 │ └── scripts │ │ ├── __pycache__ │ │ └── utilities.cpython-36.pyc │ │ ├── bitcoin_model.py │ │ └── utilities.py └── images │ ├── 3.2.jpg │ ├── 3.3.png │ └── 3.4.jpg ├── Chapter04 ├── Activity4.01 │ ├── .dockerignore │ ├── .gitignore │ ├── Dockerfile │ ├── Makefile │ ├── README.md │ ├── bin │ │ └── build_docker_image.sh │ ├── cryptonic-cache │ │ ├── Dockerfile │ │ └── bin │ │ │ └── build_docker_image.sh │ ├── cryptonic-ui │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── postcss.config.js │ │ ├── src │ │ │ ├── App.vue │ │ │ ├── assets │ │ │ │ ├── botstream.png │ │ │ │ └── botstream_slack.png │ │ │ ├── css │ │ │ │ ├── forbes_logo.css │ │ │ │ ├── infinite.css │ │ │ │ ├── loading_animation.css │ │ │ │ └── style.css │ │ │ ├── index.html │ │ │ └── main.js │ │ ├── webpack.config.js │ │ └── yarn.lock │ ├── cryptonic.env │ ├── cryptonic │ │ ├── __init__.py │ │ ├── api │ │ │ ├── __init__.py │ │ │ └── routes.py │ │ ├── markets │ │ │ ├── __init__.py │ │ │ └── coinmarketcap.py │ │ ├── models │ │ │ ├── __init__.py │ │ │ ├── helper.py │ │ │ ├── model.py │ │ │ └── normalizations.py │ │ └── server.py │ ├── cryptonic_old │ │ ├── __init__.py │ │ ├── api │ │ │ ├── __init__.py │ │ │ └── routes.py │ │ ├── markets │ │ │ ├── __init__.py │ │ │ └── coinmarketcap.py │ │ ├── models │ │ │ ├── __init__.py │ │ │ ├── helper.py │ │ │ ├── model.py │ │ │ └── normalizations.py │ │ └── server.py │ ├── docker-compose.yml │ ├── requirements.txt │ ├── run.py │ └── screenshot.png ├── Exercise4.01 │ ├── .ipynb_checkpoints │ │ └── Exercise4.01_Re_training_a_model_dynamically-checkpoint.ipynb │ ├── Exercise4.01_Re_training_a_model_dynamically.ipynb │ ├── bitcoin_lstm_v0.h5 │ ├── bitcoin_model_prod_v0.h5 │ └── cryptonic │ │ ├── __init__.py │ │ ├── __pycache__ │ │ └── __init__.cpython-36.pyc │ │ ├── api │ │ ├── __init__.py │ │ └── routes.py │ │ ├── markets │ │ ├── __init__.py │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-36.pyc │ │ │ └── coinmarketcap.cpython-36.pyc │ │ └── coinmarketcap.py │ │ ├── models │ │ ├── __init__.py │ │ ├── __pycache__ │ │ │ ├── __init__.cpython-36.pyc │ │ │ ├── helper.cpython-36.pyc │ │ │ ├── model.cpython-36.pyc │ │ │ └── normalizations.cpython-36.pyc │ │ ├── helper.py │ │ ├── model.py │ │ └── normalizations.py │ │ └── server.py └── Exercise4.02 │ ├── .dockerignore │ ├── .gitignore │ ├── Dockerfile │ ├── Makefile │ ├── README.md │ ├── bin │ └── build_docker_image.sh │ ├── cryptonic-cache │ ├── Dockerfile │ └── bin │ │ └── build_docker_image.sh │ ├── cryptonic-ui │ ├── package-lock.json │ ├── package.json │ ├── postcss.config.js │ ├── src │ │ ├── App.vue │ │ ├── assets │ │ │ ├── botstream.png │ │ │ └── botstream_slack.png │ │ ├── css │ │ │ ├── forbes_logo.css │ │ │ ├── infinite.css │ │ │ ├── loading_animation.css │ │ │ └── style.css │ │ ├── index.html │ │ └── main.js │ ├── webpack.config.js │ └── yarn.lock │ ├── cryptonic.env │ ├── cryptonic │ ├── __init__.py │ ├── api │ │ ├── __init__.py │ │ └── routes.py │ ├── markets │ │ ├── __init__.py │ │ └── coinmarketcap.py │ ├── models │ │ ├── __init__.py │ │ ├── helper.py │ │ ├── model.py │ │ └── normalizations.py │ └── server.py │ ├── cryptonic_old │ ├── __init__.py │ ├── api │ │ ├── __init__.py │ │ └── routes.py │ ├── markets │ │ ├── __init__.py │ │ └── coinmarketcap.py │ ├── models │ │ ├── __init__.py │ │ ├── helper.py │ │ ├── model.py │ │ └── normalizations.py │ └── server.py │ ├── docker-compose.yml │ ├── requirements.txt │ ├── run.py │ └── screenshot.png ├── LICENSE ├── README.md └── requirements.txt /Chapter01/Activity1.01/mnist.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # In[5]: 5 | 6 | 7 | import tensorflow as tf 8 | from tensorflow import keras 9 | from tensorflow.keras.models import Sequential 10 | from tensorflow.keras.layers import Convolution2D, Flatten, Dense, Dropout 11 | import datetime 12 | 13 | from keras.optimizers import adam 14 | 15 | 16 | # In[17]: 17 | 18 | 19 | mnist = tf.keras.datasets.mnist 20 | 21 | (X_train, y_train), (X_test, y_test) = mnist.load_data() 22 | X_train, X_test = X_train / 255.0, X_test / 255.0 23 | 24 | 25 | # In[20]: 26 | 27 | log_dir="logs/fit/" + datetime.datetime.now().strftime("%Y%m%d-%H%M%S") 28 | tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir=log_dir, histogram_freq=1) 29 | 30 | 31 | X_train = X_train.reshape(X_train.shape[0],X_train.shape[1], X_train.shape[2],1) 32 | X_test = X_test.reshape(X_test.shape[0],X_test.shape[1], X_test.shape[2],1) 33 | 34 | 35 | # In[21]: 36 | 37 | 38 | model = Sequential() 39 | model.add(Convolution2D(filters = 10, kernel_size = 3, input_shape=(28,28,1))) 40 | model.add(Flatten()) 41 | model.add(Dense(128, activation = 'relu')) 42 | model.add(Dropout(0.2)) 43 | model.add(Dense(10, activation = 'softmax')) 44 | 45 | 46 | 47 | 48 | # In[22]: 49 | 50 | learning_rate=0.001 51 | 52 | 53 | model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate = learning_rate), 54 | loss='sparse_categorical_crossentropy', 55 | metrics=['accuracy']) 56 | 57 | 58 | # In[23]: 59 | 60 | 61 | model.fit(X_train, y_train, epochs=5, validation_data=(X_test, y_test),callbacks=[tensorboard_callback]) 62 | 63 | 64 | # In[ ]: 65 | 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /Chapter01/Activity1.01/requirements.txt: -------------------------------------------------------------------------------- 1 | absl-py==0.8.1 2 | astor==0.8.0 3 | attrs==19.3.0 4 | backcall==0.1.0 5 | beautifulsoup4==4.8.1 6 | bleach==3.1.0 7 | cachetools==3.1.1 8 | certifi==2019.9.11 9 | chardet==3.0.4 10 | Click==7.0 11 | cycler==0.10.0 12 | decorator==4.4.1 13 | defusedxml==0.6.0 14 | entrypoints==0.3 15 | Flask==1.1.1 16 | Flask-API==2.0 17 | Flask-Caching==1.8.0 18 | Flask-Cors==3.0.8 19 | Flask-Testing==0.7.1 20 | gast==0.2.2 21 | google-auth==1.7.0 22 | google-auth-oauthlib==0.4.1 23 | google-pasta==0.1.8 24 | graphviz==0.13.2 25 | grpcio==1.24.3 26 | h5py==2.10.0 27 | html5lib==1.0.1 28 | idna==2.8 29 | importlib-metadata==1.3.0 30 | ipykernel==5.1.3 31 | ipython==7.10.2 32 | ipython-genutils==0.2.0 33 | ipywidgets==7.5.1 34 | itsdangerous==1.1.0 35 | jedi==0.15.2 36 | Jinja2==2.10.3 37 | jsonschema==3.2.0 38 | jupyter==1.0.0 39 | jupyter-client==5.3.4 40 | jupyter-console==6.0.0 41 | jupyter-core==4.6.1 42 | Keras==2.2.4 43 | Keras-Applications==1.0.8 44 | Keras-Preprocessing==1.1.0 45 | kiwisolver==1.1.0 46 | Markdown==3.1.1 47 | MarkupSafe==1.1.1 48 | matplotlib==3.1.2 49 | mistune==0.8.4 50 | more-itertools==8.0.2 51 | nbconvert==5.6.1 52 | nbformat==4.4.0 53 | notebook==6.0.2 54 | numpy==1.17.3 55 | oauthlib==3.1.0 56 | opt-einsum==3.1.0 57 | pandas==0.25.3 58 | pandocfilters==1.4.2 59 | parso==0.5.2 60 | pexpect==4.7.0 61 | pickleshare==0.7.5 62 | prometheus-client==0.7.1 63 | prompt-toolkit==2.0.10 64 | protobuf==3.10.0 65 | ptyprocess==0.6.0 66 | pyasn1==0.4.7 67 | pyasn1-modules==0.2.7 68 | Pygments==2.5.2 69 | pyparsing==2.4.5 70 | pyrsistent==0.15.6 71 | python-dateutil==2.8.1 72 | pytz==2019.3 73 | PyYAML==5.2 74 | pyzmq==18.1.1 75 | qtconsole==4.6.0 76 | requests==2.22.0 77 | requests-oauthlib==1.2.0 78 | rsa==4.0 79 | scipy==1.4.1 80 | seaborn==0.9.0 81 | Send2Trash==1.5.0 82 | six==1.13.0 83 | soupsieve==1.9.5 84 | tensorboard==2.0.1 85 | tensorflow==2.0.0 86 | tensorflow-estimator==2.0.1 87 | termcolor==1.1.0 88 | terminado==0.8.3 89 | testpath==0.4.4 90 | tornado==6.0.3 91 | tqdm==4.41.0 92 | traitlets==4.3.3 93 | urllib3==1.25.6 94 | wcwidth==0.1.7 95 | webencodings==0.5.1 96 | Werkzeug==0.16.0 97 | widgetsnbextension==3.5.1 98 | wrapt==1.11.2 99 | zipp==0.6.0 100 | -------------------------------------------------------------------------------- /Chapter01/Exercise1.01/requirements.txt: -------------------------------------------------------------------------------- 1 | absl-py==0.8.1 2 | astor==0.8.0 3 | attrs==19.3.0 4 | backcall==0.1.0 5 | beautifulsoup4==4.8.1 6 | bleach==3.1.0 7 | cachetools==3.1.1 8 | certifi==2019.9.11 9 | chardet==3.0.4 10 | Click==7.0 11 | cycler==0.10.0 12 | decorator==4.4.1 13 | defusedxml==0.6.0 14 | entrypoints==0.3 15 | Flask==1.1.1 16 | Flask-API==2.0 17 | Flask-Caching==1.8.0 18 | Flask-Cors==3.0.8 19 | Flask-Testing==0.7.1 20 | gast==0.2.2 21 | google-auth==1.7.0 22 | google-auth-oauthlib==0.4.1 23 | google-pasta==0.1.8 24 | graphviz==0.13.2 25 | grpcio==1.24.3 26 | h5py==2.10.0 27 | html5lib==1.0.1 28 | idna==2.8 29 | importlib-metadata==1.3.0 30 | ipykernel==5.1.3 31 | ipython==7.10.2 32 | ipython-genutils==0.2.0 33 | ipywidgets==7.5.1 34 | itsdangerous==1.1.0 35 | jedi==0.15.2 36 | Jinja2==2.10.3 37 | jsonschema==3.2.0 38 | jupyter==1.0.0 39 | jupyter-client==5.3.4 40 | jupyter-console==6.0.0 41 | jupyter-core==4.6.1 42 | Keras==2.2.4 43 | Keras-Applications==1.0.8 44 | Keras-Preprocessing==1.1.0 45 | kiwisolver==1.1.0 46 | Markdown==3.1.1 47 | MarkupSafe==1.1.1 48 | matplotlib==3.1.2 49 | mistune==0.8.4 50 | more-itertools==8.0.2 51 | nbconvert==5.6.1 52 | nbformat==4.4.0 53 | notebook==6.0.2 54 | numpy==1.17.3 55 | oauthlib==3.1.0 56 | opt-einsum==3.1.0 57 | pandas==0.25.3 58 | pandocfilters==1.4.2 59 | parso==0.5.2 60 | pexpect==4.7.0 61 | pickleshare==0.7.5 62 | prometheus-client==0.7.1 63 | prompt-toolkit==2.0.10 64 | protobuf==3.10.0 65 | ptyprocess==0.6.0 66 | pyasn1==0.4.7 67 | pyasn1-modules==0.2.7 68 | Pygments==2.5.2 69 | pyparsing==2.4.5 70 | pyrsistent==0.15.6 71 | python-dateutil==2.8.1 72 | pytz==2019.3 73 | PyYAML==5.2 74 | pyzmq==18.1.1 75 | qtconsole==4.6.0 76 | requests==2.22.0 77 | requests-oauthlib==1.2.0 78 | rsa==4.0 79 | scipy==1.4.1 80 | seaborn==0.9.0 81 | Send2Trash==1.5.0 82 | setuptools==41.0.0 83 | six==1.13.0 84 | soupsieve==1.9.5 85 | tensorboard==2.0.1 86 | tensorflow==2.0.0 87 | tensorflow-estimator==2.0.1 88 | termcolor==1.1.0 89 | terminado==0.8.3 90 | testpath==0.4.4 91 | tornado==6.0.3 92 | tqdm==4.41.0 93 | traitlets==4.3.3 94 | urllib3==1.25.6 95 | wcwidth==0.1.7 96 | webencodings==0.5.1 97 | Werkzeug==0.16.0 98 | widgetsnbextension==3.5.1 99 | wrapt==1.11.2 100 | zipp==0.6.0 101 | -------------------------------------------------------------------------------- /Chapter01/Exercise1.01/test_stack.py: -------------------------------------------------------------------------------- 1 | """ 2 | Simple tests for verifying software requirements. 3 | These tests will check if the following conditions 4 | are met: 5 | 6 | * Python is 3.0 or higher. 7 | * TensorFlow is 2.0 or higher. 8 | * Keras is 2.2 or higher. 9 | 10 | The program returns helpful error messages if 11 | the conditions above are not met.abs 12 | 13 | Proceed to Chapter 02 when all tests pass. 14 | 15 | -- 16 | Author: Luis Capelo 17 | Date: October 17, 2017 18 | """ 19 | import sys 20 | 21 | 22 | def __separator(c): 23 | """ 24 | Prints a pretty separator. 25 | 26 | Parameters 27 | ---------- 28 | c: str 29 | Character to use. 30 | """ 31 | print(c * 65) 32 | 33 | def test_python(): 34 | """ 35 | Tests if Python 3 is installed. 36 | """ 37 | message = None 38 | if sys.version_info[0] == 3: 39 | success = True 40 | log = """ 41 | PASS: Python 3.0 (or higher) is installed. 42 | """ 43 | 44 | else: 45 | success = False 46 | log = """ 47 | FAIL: Python 3.0 (or higher) not detected. 48 | """ 49 | message = """ 50 | Please install it before proceeding. 51 | Follow instructions in the official Python 52 | website in order to install it in your platform: 53 | 54 | https://www.python.org/downloads/ 55 | """ 56 | 57 | print(log) 58 | if message: 59 | print(message) 60 | 61 | __separator('~') 62 | return success 63 | 64 | def test_tensorflow(): 65 | """ 66 | Tests if TensorFlow is installed. 67 | """ 68 | message = None 69 | try: 70 | import tensorflow; 71 | 72 | if tensorflow.__version__ >= '2.0.0': 73 | success = True 74 | log = """ 75 | PASS: TensorFlow 2.0.0 (or higher) is installed. 76 | """ 77 | 78 | else: 79 | success = False 80 | log = """ 81 | FAIL: TensorFlow 2.0.0 (or higher) not detected. 82 | """ 83 | message = """ 84 | Please install it before proceeding. 85 | Follow instructions in the official TensorFlow 86 | website in order to install it in your platform: 87 | 88 | https://www.tensorflow.org/install/ 89 | """ 90 | 91 | except ModuleNotFoundError: 92 | success = False 93 | log = """ 94 | FAIL: TensorFlow 2.0.0 (or higher) not detected. 95 | """ 96 | message = """ 97 | Please install it before proceeding. 98 | Follow instructions in the official TensorFlow 99 | website in order to install it in your platform: 100 | 101 | https://www.tensorflow.org/install/ 102 | """ 103 | 104 | print(log) 105 | if message: 106 | print(message) 107 | 108 | __separator('~') 109 | return success 110 | 111 | def test_keras(): 112 | """ 113 | Tests if Keras is installed. 114 | """ 115 | message = None 116 | try: 117 | import keras 118 | 119 | if sys.version_info[0] == 3: 120 | success = True 121 | log = """ 122 | PASS: Keras 2.2 (or higher) is installed. 123 | """ 124 | 125 | else: 126 | success = False 127 | log = """ 128 | FAIL: Keras 2.2 (or higher) not detected. 129 | """ 130 | message = """ 131 | Please install it before proceeding. 132 | Follow instructions in the official Keras 133 | website in order to install it in your platform: 134 | 135 | https://keras.io/#installation 136 | """ 137 | 138 | except ModuleNotFoundError: 139 | success = False 140 | log = """ 141 | FAIL: Keras 2.2 (or higher) not detected. 142 | """ 143 | message = """ 144 | Please install it before proceeding. 145 | Follow instructions in the official Keras 146 | website in order to install it in your platform: 147 | 148 | https://keras.io/#installation 149 | """ 150 | 151 | print(log) 152 | if message: 153 | print(message) 154 | 155 | __separator('~') 156 | return success 157 | 158 | 159 | if __name__ == '__main__': 160 | __separator('=') 161 | test_results = [ 162 | test_python(), 163 | test_tensorflow(), 164 | test_keras()] 165 | 166 | if False in test_results: 167 | print( 168 | """ 169 | ** Please review software requirements before 170 | ** proceeding to Chapter 02. 171 | """ 172 | ) 173 | else: 174 | print( 175 | """ 176 | ** Python, TensorFlow, and Keras are available. 177 | """ 178 | ) 179 | __separator('=') 180 | -------------------------------------------------------------------------------- /Chapter01/Exercise1.02/mnist.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # In[5]: 5 | 6 | 7 | import tensorflow as tf 8 | from tensorflow import keras 9 | from tensorflow.keras.models import Sequential 10 | from tensorflow.keras.layers import Convolution2D, Flatten, Dense, Dropout 11 | import datetime 12 | 13 | # In[17]: 14 | 15 | 16 | mnist = tf.keras.datasets.mnist 17 | 18 | (X_train, y_train), (X_test, y_test) = mnist.load_data() 19 | X_train, X_test = X_train / 255.0, X_test / 255.0 20 | 21 | 22 | # In[20]: 23 | 24 | log_dir="logs/fit/" + datetime.datetime.now().strftime("%Y%m%d-%H%M%S") 25 | tensorboard_callback = tf.keras.callbacks.TensorBoard(log_dir=log_dir, histogram_freq=1) 26 | 27 | 28 | X_train = X_train.reshape(X_train.shape[0],X_train.shape[1], X_train.shape[2],1) 29 | X_test = X_test.reshape(X_test.shape[0],X_test.shape[1], X_test.shape[2],1) 30 | 31 | 32 | # In[21]: 33 | 34 | 35 | model = Sequential() 36 | model.add(Convolution2D(filters = 10, kernel_size = 3, input_shape=(28,28,1))) 37 | model.add(Flatten()) 38 | model.add(Dense(128, activation = 'relu')) 39 | model.add(Dropout(0.2)) 40 | model.add(Dense(10, activation = 'softmax')) 41 | 42 | 43 | # In[22]: 44 | 45 | 46 | model.compile(optimizer='adam', 47 | loss='sparse_categorical_crossentropy', 48 | metrics=['accuracy']) 49 | 50 | 51 | # In[23]: 52 | 53 | 54 | model.fit(X_train, y_train, epochs=5, validation_data=(X_test, y_test),callbacks=[tensorboard_callback]) 55 | 56 | 57 | # In[ ]: 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /Chapter01/Exercise1.02/requirements.txt: -------------------------------------------------------------------------------- 1 | absl-py==0.8.1 2 | astor==0.8.0 3 | attrs==19.3.0 4 | backcall==0.1.0 5 | beautifulsoup4==4.8.1 6 | bleach==3.1.0 7 | cachetools==3.1.1 8 | certifi==2019.9.11 9 | chardet==3.0.4 10 | Click==7.0 11 | cycler==0.10.0 12 | decorator==4.4.1 13 | defusedxml==0.6.0 14 | entrypoints==0.3 15 | Flask==1.1.1 16 | Flask-API==2.0 17 | Flask-Caching==1.8.0 18 | Flask-Cors==3.0.8 19 | Flask-Testing==0.7.1 20 | gast==0.2.2 21 | google-auth==1.7.0 22 | google-auth-oauthlib==0.4.1 23 | google-pasta==0.1.8 24 | graphviz==0.13.2 25 | grpcio==1.24.3 26 | h5py==2.10.0 27 | html5lib==1.0.1 28 | idna==2.8 29 | importlib-metadata==1.3.0 30 | ipykernel==5.1.3 31 | ipython==7.10.2 32 | ipython-genutils==0.2.0 33 | ipywidgets==7.5.1 34 | itsdangerous==1.1.0 35 | jedi==0.15.2 36 | Jinja2==2.10.3 37 | jsonschema==3.2.0 38 | jupyter==1.0.0 39 | jupyter-client==5.3.4 40 | jupyter-console==6.0.0 41 | jupyter-core==4.6.1 42 | Keras==2.2.4 43 | Keras-Applications==1.0.8 44 | Keras-Preprocessing==1.1.0 45 | kiwisolver==1.1.0 46 | Markdown==3.1.1 47 | MarkupSafe==1.1.1 48 | matplotlib==3.1.2 49 | mistune==0.8.4 50 | more-itertools==8.0.2 51 | nbconvert==5.6.1 52 | nbformat==4.4.0 53 | notebook==6.0.2 54 | numpy==1.17.3 55 | oauthlib==3.1.0 56 | opt-einsum==3.1.0 57 | pandas==0.25.3 58 | pandocfilters==1.4.2 59 | parso==0.5.2 60 | pexpect==4.7.0 61 | pickleshare==0.7.5 62 | prometheus-client==0.7.1 63 | prompt-toolkit==2.0.10 64 | protobuf==3.10.0 65 | ptyprocess==0.6.0 66 | pyasn1==0.4.7 67 | pyasn1-modules==0.2.7 68 | Pygments==2.5.2 69 | pyparsing==2.4.5 70 | pyrsistent==0.15.6 71 | python-dateutil==2.8.1 72 | pytz==2019.3 73 | PyYAML==5.2 74 | pyzmq==18.1.1 75 | qtconsole==4.6.0 76 | requests==2.22.0 77 | requests-oauthlib==1.2.0 78 | rsa==4.0 79 | scipy==1.4.1 80 | seaborn==0.9.0 81 | Send2Trash==1.5.0 82 | six==1.13.0 83 | soupsieve==1.9.5 84 | tensorboard==2.0.1 85 | tensorflow==2.0.0 86 | tensorflow-estimator==2.0.1 87 | termcolor==1.1.0 88 | terminado==0.8.3 89 | testpath==0.4.4 90 | tornado==6.0.3 91 | tqdm==4.41.0 92 | traitlets==4.3.3 93 | urllib3==1.25.6 94 | wcwidth==0.1.7 95 | webencodings==0.5.1 96 | Werkzeug==0.16.0 97 | widgetsnbextension==3.5.1 98 | wrapt==1.11.2 99 | zipp==0.6.0 100 | -------------------------------------------------------------------------------- /Chapter02/Activity2.01/bitcoin_lstm_v0.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter02/Activity2.01/bitcoin_lstm_v0.h5 -------------------------------------------------------------------------------- /Chapter02/Activity2.01/bitcoin_lstm_v0_trained.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter02/Activity2.01/bitcoin_lstm_v0_trained.h5 -------------------------------------------------------------------------------- /Chapter02/Exercise2.01/normalizations.py: -------------------------------------------------------------------------------- 1 | """ 2 | Series of normalization functions useful 3 | for normalizing time-series data. 4 | 5 | Author: Luis Capelo 6 | """ 7 | def z_score(series): 8 | """ 9 | Computes the normalized value using the Z-score 10 | technique. The Z-score is a technique used for 11 | normalizing Gaussian distributions representing 12 | each observation in relation to the distribution's 13 | mean and standard deviation. For precise definitions, 14 | see the Wikipedia article: 15 | 16 | https://en.wikipedia.org/wiki/Standard_score 17 | 18 | Parameters 19 | ---------- 20 | serie: list 21 | List with sequential values to use. 22 | 23 | Returns 24 | ------- 25 | result: list 26 | List with the normalized results. 27 | """ 28 | result = (series - series.mean()) / series.std(ddof=0) 29 | return result 30 | 31 | def point_relative_normalization(series): 32 | """ 33 | Computes the normalized value for the values of a 34 | given series by using the first element of the serie as p_0 35 | as a reference for each p_i. 36 | 37 | This technique comes from Siraj Raval's YouTube video 38 | "How to Predict Stock Prices Easily - Intro to Deep Learning #7", 39 | available at: 40 | 41 | https://www.youtube.com/watch?v=ftMq5ps503w 42 | 43 | Parameters 44 | ---------- 45 | serie: list 46 | List with sequential values to use. 47 | 48 | Returns 49 | ------- 50 | result: list 51 | List with the normalized results. 52 | """ 53 | result = (series / series.values[0]) - 1 54 | return result 55 | 56 | def maximum_and_minimum_normalization(series, boundary=(0, 1)): 57 | """ 58 | Computes the normalized value for the values of a 59 | given serie by using that series maximum and minimum 60 | values. 61 | 62 | This technique is a direct implementation from 63 | scikit-learn, available at: 64 | 65 | http://scikit-learn.org/stable/modules/generated/\ 66 | sklearn.preprocessing.MinMaxScaler.html 67 | 68 | Parameters 69 | ---------- 70 | serie: list 71 | List with sequential values to use. 72 | 73 | boundary: set 74 | Maximum and minimum values used to 75 | scale the series. 76 | 77 | Returns 78 | ------- 79 | result: list 80 | List with the normalized results. 81 | """ 82 | range_min, range_max = boundary 83 | standard_deviation = (series - series.min(axis=0)) / (series.max(axis=0) - series.min(axis=0)) 84 | result = standard_deviation * (range_max - range_min) + range_min 85 | 86 | return result 87 | -------------------------------------------------------------------------------- /Chapter02/Exercise2.02/bitcoin_lstm_v0.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter02/Exercise2.02/bitcoin_lstm_v0.h5 -------------------------------------------------------------------------------- /Chapter02/images/2.1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter02/images/2.1.jpg -------------------------------------------------------------------------------- /Chapter02/images/2.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter02/images/2.2.png -------------------------------------------------------------------------------- /Chapter02/images/2.3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter02/images/2.3.jpg -------------------------------------------------------------------------------- /Chapter03/Activity3.01/bitcoin_lstm_v0.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter03/Activity3.01/bitcoin_lstm_v0.h5 -------------------------------------------------------------------------------- /Chapter03/Activity3.01/logs/bitcoin_lstm_v0_run_0_7d47b6/train/events.out.tfevents.1580899648.L-156196731.39696.334.v2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter03/Activity3.01/logs/bitcoin_lstm_v0_run_0_7d47b6/train/events.out.tfevents.1580899648.L-156196731.39696.334.v2 -------------------------------------------------------------------------------- /Chapter03/Activity3.01/logs/bitcoin_lstm_v0_run_0_7d47b6/train/events.out.tfevents.1580899654.L-156196731.profile-empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter03/Activity3.01/logs/bitcoin_lstm_v0_run_0_7d47b6/train/events.out.tfevents.1580899654.L-156196731.profile-empty -------------------------------------------------------------------------------- /Chapter03/Activity3.01/logs/bitcoin_lstm_v0_run_0_7d47b6/train/plugins/profile/2020-02-05_16-17-34/local.trace: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter03/Activity3.01/logs/bitcoin_lstm_v0_run_0_7d47b6/train/plugins/profile/2020-02-05_16-17-34/local.trace -------------------------------------------------------------------------------- /Chapter03/Activity3.01/logs/bitcoin_lstm_v1_run_0_12e0e4/train/events.out.tfevents.1580899657.L-156196731.39696.3231.v2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter03/Activity3.01/logs/bitcoin_lstm_v1_run_0_12e0e4/train/events.out.tfevents.1580899657.L-156196731.39696.3231.v2 -------------------------------------------------------------------------------- /Chapter03/Activity3.01/logs/bitcoin_lstm_v1_run_0_12e0e4/train/events.out.tfevents.1580899670.L-156196731.profile-empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter03/Activity3.01/logs/bitcoin_lstm_v1_run_0_12e0e4/train/events.out.tfevents.1580899670.L-156196731.profile-empty -------------------------------------------------------------------------------- /Chapter03/Activity3.01/logs/bitcoin_lstm_v1_run_0_12e0e4/train/plugins/profile/2020-02-05_16-17-50/local.trace: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter03/Activity3.01/logs/bitcoin_lstm_v1_run_0_12e0e4/train/plugins/profile/2020-02-05_16-17-50/local.trace -------------------------------------------------------------------------------- /Chapter03/Activity3.01/logs/bitcoin_lstm_v2_run_0_a4ff9d/train/events.out.tfevents.1580899698.L-156196731.39696.9795.v2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter03/Activity3.01/logs/bitcoin_lstm_v2_run_0_a4ff9d/train/events.out.tfevents.1580899698.L-156196731.39696.9795.v2 -------------------------------------------------------------------------------- /Chapter03/Activity3.01/logs/bitcoin_lstm_v2_run_0_a4ff9d/train/events.out.tfevents.1580899711.L-156196731.profile-empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter03/Activity3.01/logs/bitcoin_lstm_v2_run_0_a4ff9d/train/events.out.tfevents.1580899711.L-156196731.profile-empty -------------------------------------------------------------------------------- /Chapter03/Activity3.01/logs/bitcoin_lstm_v2_run_0_a4ff9d/train/plugins/profile/2020-02-05_16-18-31/local.trace: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter03/Activity3.01/logs/bitcoin_lstm_v2_run_0_a4ff9d/train/plugins/profile/2020-02-05_16-18-31/local.trace -------------------------------------------------------------------------------- /Chapter03/Activity3.01/logs/bitcoin_lstm_v3_run_0_5e2911/train/events.out.tfevents.1580899791.L-156196731.39696.18160.v2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter03/Activity3.01/logs/bitcoin_lstm_v3_run_0_5e2911/train/events.out.tfevents.1580899791.L-156196731.39696.18160.v2 -------------------------------------------------------------------------------- /Chapter03/Activity3.01/logs/bitcoin_lstm_v3_run_0_5e2911/train/events.out.tfevents.1580899803.L-156196731.profile-empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter03/Activity3.01/logs/bitcoin_lstm_v3_run_0_5e2911/train/events.out.tfevents.1580899803.L-156196731.profile-empty -------------------------------------------------------------------------------- /Chapter03/Activity3.01/logs/bitcoin_lstm_v3_run_0_5e2911/train/plugins/profile/2020-02-05_16-20-03/local.trace: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter03/Activity3.01/logs/bitcoin_lstm_v3_run_0_5e2911/train/plugins/profile/2020-02-05_16-20-03/local.trace -------------------------------------------------------------------------------- /Chapter03/Activity3.01/logs/bitcoin_lstm_v4_run_0_4b899b/train/events.out.tfevents.1580899832.L-156196731.39696.24785.v2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter03/Activity3.01/logs/bitcoin_lstm_v4_run_0_4b899b/train/events.out.tfevents.1580899832.L-156196731.39696.24785.v2 -------------------------------------------------------------------------------- /Chapter03/Activity3.01/logs/bitcoin_lstm_v4_run_0_4b899b/train/events.out.tfevents.1580899845.L-156196731.profile-empty: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter03/Activity3.01/logs/bitcoin_lstm_v4_run_0_4b899b/train/events.out.tfevents.1580899845.L-156196731.profile-empty -------------------------------------------------------------------------------- /Chapter03/Activity3.01/logs/bitcoin_lstm_v4_run_0_4b899b/train/plugins/profile/2020-02-05_16-20-45/local.trace: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter03/Activity3.01/logs/bitcoin_lstm_v4_run_0_4b899b/train/plugins/profile/2020-02-05_16-20-45/local.trace -------------------------------------------------------------------------------- /Chapter03/Activity3.01/scripts/__pycache__/utilities.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter03/Activity3.01/scripts/__pycache__/utilities.cpython-36.pyc -------------------------------------------------------------------------------- /Chapter03/Activity3.01/scripts/bitcoin_model.py: -------------------------------------------------------------------------------- 1 | """ 2 | Classes and methods for working with a Bitcoin 3 | LSTM model. 4 | """ 5 | import numpy as np 6 | 7 | from tensorflow.keras.models import load_model 8 | 9 | 10 | class BitcoinModel: 11 | """ 12 | Class that encapsulates the Bitcoin LSTM model 13 | that we have been building. This class makes it 14 | easy to work with the different functions 15 | used to work with the model. 16 | 17 | Parameters 18 | ---------- 19 | path: str 20 | Location to load model from. 21 | 22 | """ 23 | def __init__(self, model_path): 24 | self.model_path = model_path 25 | self.load() 26 | 27 | def load(self): 28 | """ 29 | Loads model from a known location. 30 | """ 31 | self.model = load_model(self.model_path) 32 | return self.model 33 | 34 | def save(self, path): 35 | """ 36 | Stores trained model in disk. 37 | 38 | Parameters 39 | ---------- 40 | path: str 41 | Location of where to store. 42 | """ 43 | return self.model.save(path) 44 | 45 | def create_groups(self, data, group_size=7): 46 | """ 47 | Creates distinct groups from a given continuous series. 48 | 49 | Parameters 50 | ---------- 51 | data: np.array 52 | Series of continious observations. 53 | 54 | group_size: int, default 7 55 | Determines how large the groups are. That is, 56 | how many observations each group contains. 57 | 58 | Returns 59 | ------- 60 | A Numpy array object. 61 | """ 62 | samples = list() 63 | for i in range(0, len(data), group_size): 64 | sample = list(data[i:i + group_size]) 65 | if len(sample) == group_size: 66 | samples.append(np.array(sample).reshape(1, group_size).tolist()) 67 | 68 | A = np.array(samples) 69 | return A.reshape(1, A.shape[0], group_size) 70 | 71 | def denormalize(self, series, last_value): 72 | """ 73 | De-normalizes a series using the latest 74 | value available from data. 75 | 76 | Parameters 77 | ---------- 78 | series: numpy array 79 | Series with normalized values. 80 | 81 | last_value: float 82 | Numerical value that represents the 83 | last value from the dataset. 84 | """ 85 | result = last_value * (series + 1) 86 | return result 87 | 88 | def predict(self, X): 89 | """ 90 | """ 91 | return self.model.predict(x=X) 92 | 93 | def train(self): 94 | """ 95 | """ 96 | self.model.fit() 97 | pass 98 | 99 | def evaluate(self): 100 | """ 101 | """ 102 | pass 103 | -------------------------------------------------------------------------------- /Chapter03/Exercise3.01/bitcoin_lstm_v0.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter03/Exercise3.01/bitcoin_lstm_v0.h5 -------------------------------------------------------------------------------- /Chapter03/Exercise3.01/bitcoin_lstm_v0_trained.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter03/Exercise3.01/bitcoin_lstm_v0_trained.h5 -------------------------------------------------------------------------------- /Chapter03/Exercise3.01/logs/bitcoin_lstm_v0_run_0/train/events.out.tfevents.1581417838.L-156196731.1744.10130.v2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter03/Exercise3.01/logs/bitcoin_lstm_v0_run_0/train/events.out.tfevents.1581417838.L-156196731.1744.10130.v2 -------------------------------------------------------------------------------- /Chapter03/Exercise3.01/scripts/__pycache__/utilities.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter03/Exercise3.01/scripts/__pycache__/utilities.cpython-36.pyc -------------------------------------------------------------------------------- /Chapter03/Exercise3.01/scripts/bitcoin_model.py: -------------------------------------------------------------------------------- 1 | """ 2 | Classes and methods for working with a Bitcoin 3 | LSTM model. 4 | """ 5 | import numpy as np 6 | 7 | from tensorflow.keras.models import load_model 8 | 9 | 10 | class BitcoinModel: 11 | """ 12 | Class that encapsulates the Bitcoin LSTM model 13 | that we have been building. This class makes it 14 | easy to work with the different functions 15 | used to work with the model. 16 | 17 | Parameters 18 | ---------- 19 | path: str 20 | Location to load model from. 21 | 22 | """ 23 | def __init__(self, model_path): 24 | self.model_path = model_path 25 | self.load() 26 | 27 | def load(self): 28 | """ 29 | Loads model from a known location. 30 | """ 31 | self.model = load_model(self.model_path) 32 | return self.model 33 | 34 | def save(self, path): 35 | """ 36 | Stores trained model in disk. 37 | 38 | Parameters 39 | ---------- 40 | path: str 41 | Location of where to store. 42 | """ 43 | return self.model.save(path) 44 | 45 | def create_groups(self, data, group_size=7): 46 | """ 47 | Creates distinct groups from a given continuous series. 48 | 49 | Parameters 50 | ---------- 51 | data: np.array 52 | Series of continious observations. 53 | 54 | group_size: int, default 7 55 | Determines how large the groups are. That is, 56 | how many observations each group contains. 57 | 58 | Returns 59 | ------- 60 | A Numpy array object. 61 | """ 62 | samples = list() 63 | for i in range(0, len(data), group_size): 64 | sample = list(data[i:i + group_size]) 65 | if len(sample) == group_size: 66 | samples.append(np.array(sample).reshape(1, group_size).tolist()) 67 | 68 | A = np.array(samples) 69 | return A.reshape(1, A.shape[0], group_size) 70 | 71 | def denormalize(self, series, last_value): 72 | """ 73 | De-normalizes a series using the latest 74 | value available from data. 75 | 76 | Parameters 77 | ---------- 78 | series: numpy array 79 | Series with normalized values. 80 | 81 | last_value: float 82 | Numerical value that represents the 83 | last value from the dataset. 84 | """ 85 | result = last_value * (series + 1) 86 | return result 87 | 88 | def predict(self, X): 89 | """ 90 | """ 91 | return self.model.predict(x=X) 92 | 93 | def train(self): 94 | """ 95 | """ 96 | self.model.fit() 97 | pass 98 | 99 | def evaluate(self): 100 | """ 101 | """ 102 | pass 103 | -------------------------------------------------------------------------------- /Chapter03/Exercise3.01/scripts/utilities.py: -------------------------------------------------------------------------------- 1 | """ 2 | Utility functions used in Exercise 3.01. 3 | 4 | Author: Luis Capelo 5 | Date: 2017-12-18 6 | """ 7 | import numpy as np 8 | 9 | 10 | def create_groups(data, group_size=15): 11 | """ 12 | Creates distinct groups from a given continuous series. 13 | 14 | Parameters 15 | ---------- 16 | data: np.array 17 | Series of continious observations. 18 | 19 | group_size: int, default 7 20 | Determines how large the groups are. That is, 21 | how many observations each group contains. 22 | 23 | Returns 24 | ------- 25 | A Numpy array object. 26 | """ 27 | samples = list() 28 | for i in range(0, len(data), group_size): 29 | sample = list(data[i:i + group_size]) 30 | if len(sample) == group_size: 31 | samples.append(np.array(sample).reshape(1, group_size).tolist()) 32 | 33 | A = np.array(samples) 34 | return A.reshape(1, A.shape[0], group_size) 35 | 36 | def split_lstm_input(groups): 37 | """ 38 | Splits groups in a format expected by 39 | the LSTM layer. 40 | 41 | Parameters 42 | ---------- 43 | groups: np.array 44 | Numpy array with the organized sequences. 45 | 46 | Returns 47 | ------- 48 | X, Y: np.array 49 | Numpy arrays with the shapes required by 50 | the LSTM layer. X with (1, a - 1, b) 51 | and Y with (1, b). Where a is the total 52 | number of groups in `group` and b the 53 | number of observations per group. 54 | """ 55 | X = groups[0:,:-1].reshape(1, groups.shape[1] - 1, groups.shape[2]) 56 | Y = groups[0:,-1:][0] 57 | 58 | return X, Y 59 | 60 | def mape(A, B): 61 | """ 62 | Calcualtes the mean absolute persent error 63 | from two series. Original solution from: 64 | 65 | https://stats.stackexchange.com/questions/58391/\ 66 | mean-absolute-percentage-error-mape-in-scikit-learn 67 | """ 68 | return np.mean(np.abs((A - B) / A)) * 100 69 | 70 | def rmse(A, B): 71 | """ 72 | Calculates the root mean square error from 73 | two series. Original solution from: 74 | 75 | https://stackoverflow.com/questions/16774849\ 76 | /mean-squared-error-in-numpy 77 | """ 78 | return np.sqrt(np.square(np.subtract(A, B)).mean()) 79 | -------------------------------------------------------------------------------- /Chapter03/images/3.2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter03/images/3.2.jpg -------------------------------------------------------------------------------- /Chapter03/images/3.3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter03/images/3.3.png -------------------------------------------------------------------------------- /Chapter03/images/3.4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter03/images/3.4.jpg -------------------------------------------------------------------------------- /Chapter04/Activity4.01/.dockerignore: -------------------------------------------------------------------------------- 1 | # MacOS Files 2 | .DS_Store 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Node modules 13 | node_modules/ 14 | 15 | # Distribution / packaging 16 | .Python 17 | env/ 18 | build/ 19 | develop-eggs/ 20 | dist/ 21 | downloads/ 22 | eggs/ 23 | .eggs/ 24 | lib/ 25 | lib64/ 26 | parts/ 27 | sdist/ 28 | var/ 29 | *.egg-info/ 30 | .installed.cfg 31 | *.egg 32 | 33 | # PyInstaller 34 | # Usually these files are written by a python script from a template 35 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 36 | *.manifest 37 | *.spec 38 | 39 | # Installer logs 40 | pip-log.txt 41 | pip-delete-this-directory.txt 42 | 43 | # Unit test / coverage reports 44 | htmlcov/ 45 | .tox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *,cover 52 | .hypothesis/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | 62 | # Flask stuff: 63 | instance/ 64 | .webassets-cache 65 | 66 | # Scrapy stuff: 67 | .scrapy 68 | 69 | # Sphinx documentation 70 | docs/_build/ 71 | 72 | # PyBuilder 73 | target/ 74 | 75 | # IPython Notebook 76 | .ipynb_checkpoints 77 | 78 | # pyenv 79 | .python-version 80 | 81 | # celery beat schedule file 82 | celerybeat-schedule 83 | 84 | # dotenv 85 | .env 86 | 87 | # virtualenv 88 | venv/ 89 | ENV/ 90 | 91 | # Spyder project settings 92 | .spyderproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # Visual Studio Code configuration files 98 | .vscode/* 99 | 100 | # Testing artifacts 101 | .noseids 102 | coverage/ 103 | 104 | # Ignore other applications 105 | cryptonic-cache/ 106 | 107 | # Ignoring cache and database data 108 | data/ 109 | logs/ 110 | 111 | # Ignore tests folder 112 | tests/ 113 | 114 | # Packaged files 115 | *.tar.gz 116 | 117 | # Deploy files 118 | deploy.sh -------------------------------------------------------------------------------- /Chapter04/Activity4.01/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | 103 | # Deploy files 104 | deploy.sh 105 | 106 | # Node modules 107 | node_modules/ 108 | 109 | # Visual Studio config files 110 | .vscode/ 111 | *.h5 112 | 113 | # Cache data 114 | cache_data/ 115 | 116 | # Compressed images 117 | *.tar.gz -------------------------------------------------------------------------------- /Chapter04/Activity4.01/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # Docker image for the Cryptonic application. 3 | # The image copies the complete application 4 | # directory and starts a Flask server. 5 | # 6 | FROM python:3.6 7 | ENV TZ=America/New_York 8 | 9 | # 10 | # Setting up timezone to EST (New York). 11 | # Change this to whichever timezone your 12 | # data is configured to use. 13 | # 14 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone 15 | 16 | 17 | COPY . /cryptonic 18 | 19 | WORKDIR "/cryptonic" 20 | RUN pip install -r requirements.txt 21 | 22 | EXPOSE 5000 23 | 24 | CMD ["python", "run.py"] -------------------------------------------------------------------------------- /Chapter04/Activity4.01/Makefile: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------- # 2 | # # 3 | # MAKEFILE # 4 | # -------- # 5 | # # 6 | # Makefile commands for Cryptonic: # 7 | # # 8 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 9 | # # 10 | # test: run tests via nosetest. # 11 | # # 12 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 13 | # # 14 | # build: builds all Docker images for # 15 | # Cryptonic. # 16 | # # 17 | # build-app-image: build Crytonic's # 18 | # API Docker image. # 19 | # # 20 | # build-cache-image: build Crytonic's # 21 | # cache Docker image. # 22 | # # 23 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 24 | # # 25 | # deploy: deploy combination of Docker # 26 | # containers locally. # 27 | # # 28 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 29 | # # 30 | # package: packages all components into # 31 | # a few Docker images ready for deployment. # 32 | # # 33 | # package-ui: packages the Cryptonic UI. # 34 | # # 35 | # ------------------------------------------------- # 36 | all: test package 37 | 38 | test: 39 | bash bin/test.sh; 40 | 41 | # 42 | # Build Docker images. 43 | # 44 | .PHONY: build 45 | build: build-cache-image build-app-image 46 | 47 | build-app-image: 48 | bash bin/build_docker_image.sh "latest"; 49 | 50 | build-cache-image: 51 | bash cryptonic-cache/bin/build_docker_image.sh "latest"; 52 | 53 | # 54 | # Packages all components of the 55 | # application for deployment. 56 | # 57 | package: package-ui build 58 | @echo "Packaging Docker images."; 59 | docker save -o cryptonic-latest.tar cryptonic:latest; 60 | docker save -o cryptonic-cache-latest.tar cryptonic-cache:latest; 61 | 62 | package-ui: 63 | cd cryptonic-ui && npm run build; 64 | 65 | # 66 | # Deploy Docker containers. 67 | # 68 | deploy: 69 | docker-compose up -d; 70 | -------------------------------------------------------------------------------- /Chapter04/Activity4.01/README.md: -------------------------------------------------------------------------------- 1 | # Cryptonic 2 | Cryptonic is a Dockerized web-application that predicts Bitcoin prices using a deep learning model. The model used to predict prices can be easily changed--given that certain parameters are passed--making it easy to experiment and deploy a working model. 3 | 4 | ![screenshot](screenshot.png) 5 | 6 | The application was developed for educational purposes and is part of the book "Beginning Application Development with TensorFlow" by [Luis Capelo](https://luiscapelo.info/). 7 | 8 | ### Usage 9 | A demo of the application is available at 10 | 11 | * Demo: https://cryptonic.market/ 12 | 13 | ### API 14 | The application has the following endpoints: 15 | 16 | * `/status`: returns the status of the application and its error rates. 17 | * `/historic`: returns available Bitcoin prices up to date. 18 | * `/predict`: predicts the next N days of Bitcoin closing prices. 19 | 20 | ### Requirements 21 | You will need: 22 | 23 | * Docker `17.09.1` or higher. 24 | * If using the `Makefile` commands, you will also need `make`. 25 | 26 | ### Deployment 27 | Deploy this application using the available `Makefile` recipes. Navigate to the application's root directory, then execute: 28 | 29 | ```shell 30 | $ make deploy 31 | ``` 32 | 33 | Now visit http://localhost:5000 on your browser and the application should be available. -------------------------------------------------------------------------------- /Chapter04/Activity4.01/bin/build_docker_image.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Builds Docker image of the 4 | # `Cryptonic` program. 5 | # 6 | VERSION=$1 7 | docker build --tag cryptonic:$VERSION \ 8 | --tag cryptonic:latest \ 9 | . -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic-cache/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM redis:alpine 2 | ENV TZ=America/New_York 3 | # COPY redis.conf /usr/local/etc/redis/redis.conf 4 | # CMD [ "redis-server", "/usr/local/etc/redis/redis.conf" ] 5 | 6 | # 7 | # Setting up timezone to EST (New York). 8 | # Change this to whichever timezone your 9 | # data is configured to use. 10 | # 11 | RUN apk add -U tzdata \ 12 | && cp /usr/share/zoneinfo/$TZ /etc/localtime 13 | 14 | EXPOSE 6379 15 | 16 | CMD [ "redis-server" ] -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic-cache/bin/build_docker_image.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Builds Docker image for the 4 | # backend Redis cache used by Cryptonic. 5 | # 6 | VERSION=$1 7 | docker build --tag cryptonic-cache:$VERSION \ 8 | --tag cryptonic-cache:latest \ 9 | ./cryptonic-cache/ -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic-ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cryptonic", 3 | "description": "Cryptonic UI application.", 4 | "author": "luiscape@gmail.com", 5 | "private": true, 6 | "scripts": { 7 | "dev": "webpack-dev-server --inline --hot --env.dev", 8 | "build": "rimraf dist && webpack --progress --hide-modules" 9 | }, 10 | "dependencies": { 11 | "babel-preset-es2015": "^6.24.1", 12 | "c3": "^0.4.18", 13 | "d3": "^4.10.0", 14 | "dsv-loader": "^2.0.0", 15 | "element-ui": "^1.4.2", 16 | "lodash": "^4.17.4", 17 | "moment-timezone": "^0.5.14", 18 | "vue": "^2.4.2", 19 | "vue-c3": "^1.1.1", 20 | "vue-instant": "^1.0.1", 21 | "vue-lodash": "^1.0.4", 22 | "vue-moment": "^3.1.0", 23 | "vue-resource": "^1.3.4", 24 | "vuetrend": "^0.2.3" 25 | }, 26 | "engines": { 27 | "node": ">=6" 28 | }, 29 | "devDependencies": { 30 | "autoprefixer": "^6.6.0", 31 | "babel-core": "^6.24.1", 32 | "babel-loader": "^6.4.0", 33 | "babel-preset-vue-app": "^1.2.0", 34 | "css-loader": "^0.27.0", 35 | "file-loader": "^0.10.1", 36 | "html-webpack-plugin": "^2.24.1", 37 | "postcss-loader": "^1.3.3", 38 | "rimraf": "^2.5.4", 39 | "style-loader": "^0.13.2", 40 | "url-loader": "^0.5.8", 41 | "vue-loader": "^13.0.4", 42 | "vue-template-compiler": "^2.4.2", 43 | "webpack": "^2.4.1", 44 | "webpack-dev-server": "^2.4.2" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic-ui/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('autoprefixer')() 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic-ui/src/assets/botstream.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter04/Activity4.01/cryptonic-ui/src/assets/botstream.png -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic-ui/src/assets/botstream_slack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter04/Activity4.01/cryptonic-ui/src/assets/botstream_slack.png -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic-ui/src/css/forbes_logo.css: -------------------------------------------------------------------------------- 1 | /* 2 | Icon Font: forbesicon 3 | */ 4 | 5 | @font-face { 6 | font-family: "forbesicon"; 7 | src: url("https://i.forbesimg.com/assets/fonts/fbs-typography/0223/forbesicon.eot"); 8 | src: url("https://i.forbesimg.com/assets/fonts/fbs-typography/0223/forbesicon.eot?#iefix") format("embedded-opentype"), 9 | url("https://i.forbesimg.com/assets/fonts/fbs-typography/0223/forbesicon.woff") format("woff"), 10 | url("https://i.forbesimg.com/assets/fonts/fbs-typography/0223/forbesicon.ttf") format("truetype"), 11 | url("https://i.forbesimg.com/assets/fonts/fbs-typography/0223/forbesicon.svg#forbesicon") format("svg"); 12 | font-weight: normal; 13 | font-style: normal; 14 | } 15 | 16 | @media screen and (-webkit-min-device-pixel-ratio:0) { 17 | @font-face { 18 | font-family: "forbesicon"; 19 | src: url("https://i.forbesimg.com/assets/fonts/fbs-typography/0223/forbesicon.svg#forbesicon") format("svg"); 20 | } 21 | } 22 | 23 | .icon { 24 | font-family: "forbesicon"; 25 | speak: none; 26 | font-style: normal; 27 | font-weight: normal; 28 | font-variant: normal; 29 | text-transform: none; 30 | line-height: 1; 31 | 32 | /* Better Font Rendering =========== */ 33 | -webkit-font-smoothing: antialiased; 34 | -moz-osx-font-smoothing: grayscale; 35 | } 36 | 37 | .icon-add-person:before { content: "\f168"; } 38 | .icon-amazon:before { content: "\f162"; } 39 | .icon-arrow-down:before { content: "\f100"; } 40 | .icon-arrow-down-bar:before { content: "\f149"; } 41 | .icon-arrow-up:before { content: "\f101"; } 42 | .icon-badge:before { content: "\f15f"; } 43 | .icon-baidu:before { content: "\f16a"; } 44 | .icon-book:before { content: "\f102"; } 45 | .icon-brandvoice-logo:before { content: "\f150"; } 46 | .icon-brandvoice-logo-brand:before { content: "\f151"; } 47 | .icon-brandvoice-logo-voice:before { content: "\f152"; } 48 | .icon-calendar:before { content: "\f143"; } 49 | .icon-cart:before { content: "\f15b"; } 50 | .icon-chat:before { content: "\f15c"; } 51 | .icon-check:before { content: "\f103"; } 52 | .icon-chevron-down:before { content: "\f135"; } 53 | .icon-chevron-left:before { content: "\f136"; } 54 | .icon-chevron-right:before { content: "\f137"; } 55 | .icon-chevron-up:before { content: "\f138"; } 56 | .icon-clipboard:before { content: "\f148"; } 57 | .icon-clock:before { content: "\f146"; } 58 | .icon-close:before { content: "\f104"; } 59 | .icon-cog:before { content: "\f134"; } 60 | .icon-comment-bubble:before { content: "\f105"; } 61 | .icon-copy:before { content: "\f153"; } 62 | .icon-desktop:before { content: "\f164"; } 63 | .icon-donald-trump:before { content: "\f163"; } 64 | .icon-download:before { content: "\f14a"; } 65 | .icon-edit:before { content: "\f141"; } 66 | .icon-email:before { content: "\f106"; } 67 | .icon-enlarge:before { content: "\f147"; } 68 | .icon-explore:before { content: "\f161"; } 69 | .icon-facebook:before { content: "\f139"; } 70 | .icon-flame:before { content: "\f15e"; } 71 | .icon-forbes-china-logo:before { content: "\f16b"; } 72 | .icon-forbes-life:before { content: "\f107"; } 73 | .icon-forbes-logo:before { content: "\f108"; } 74 | .icon-forbes-logo-f:before { content: "\f109"; } 75 | .icon-gallery:before { content: "\f140"; } 76 | .icon-georgia-daquo:before { content: "\f10a"; } 77 | .icon-georgia-uaquo:before { content: "\f10b"; } 78 | .icon-google:before { content: "\f13a"; } 79 | .icon-group:before { content: "\f10c"; } 80 | .icon-hamburger:before { content: "\f10d"; } 81 | .icon-handshake:before { content: "\f14b"; } 82 | .icon-home:before { content: "\f10e"; } 83 | .icon-insights:before { content: "\f165"; } 84 | .icon-instagram:before { content: "\f13b"; } 85 | .icon-link:before { content: "\f10f"; } 86 | .icon-linkedin:before { content: "\f13c"; } 87 | .icon-location:before { content: "\f156"; } 88 | .icon-lock:before { content: "\f154"; } 89 | .icon-megaphone:before { content: "\f14c"; } 90 | .icon-microphone:before { content: "\f14d"; } 91 | .icon-mobile:before { content: "\f166"; } 92 | .icon-pause:before { content: "\f15d"; } 93 | .icon-phone-call:before { content: "\f14e"; } 94 | .icon-photo:before { content: "\f167"; } 95 | .icon-pinterest:before { content: "\f13d"; } 96 | .icon-plus:before { content: "\f110"; } 97 | .icon-preview-eye:before { content: "\f145"; } 98 | .icon-print:before { content: "\f111"; } 99 | .icon-qzone:before { content: "\f16c"; } 100 | .icon-renren:before { content: "\f16d"; } 101 | .icon-reply:before { content: "\f112"; } 102 | .icon-reset:before { content: "\f113"; } 103 | .icon-rss-feed:before { content: "\f114"; } 104 | .icon-search:before { content: "\f115"; } 105 | .icon-share:before { content: "\f116"; } 106 | .icon-snapchat:before { content: "\f169"; } 107 | .icon-square-bracket-left:before { content: "\f11e"; } 108 | .icon-square-bracket-right:before { content: "\f11f"; } 109 | .icon-staff-verified:before { content: "\f158"; } 110 | .icon-star:before { content: "\f120"; } 111 | .icon-terms:before { content: "\f14f"; } 112 | .icon-trash:before { content: "\f142"; } 113 | .icon-tumblr:before { content: "\f13e"; } 114 | .icon-twitter:before { content: "\f13f"; } 115 | .icon-twitter-verified:before { content: "\f157"; } 116 | .icon-u30-logo:before { content: "\f155"; } 117 | .icon-under-30-logo:before { content: "\f159"; } 118 | .icon-user:before { content: "\f125"; } 119 | .icon-video:before { content: "\f126"; } 120 | .icon-wechat:before { content: "\f16e"; } 121 | .icon-weibo:before { content: "\f16f"; } 122 | .icon-womenforbes-logo:before { content: "\f160"; } 123 | .icon-youtube:before { content: "\f15a"; } -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic-ui/src/css/infinite.css: -------------------------------------------------------------------------------- 1 | /* Make the element pulse (grow large and small slowly) */ 2 | /* Usage 3 | .myElement { 4 | animation: pulsate 1s ease-out; 5 | animation-iteration-count: infinite; 6 | opacity: 1; 7 | } 8 | */ 9 | @-webkit-keyframes pulsate { 10 | 0% {-webkit-transform: scale(0.1, 0.1); opacity: 0.0;} 11 | 50% {opacity: 1.0;} 12 | 100% {-webkit-transform: scale(1.2, 1.2); opacity: 0.0;} 13 | } 14 | 15 | /* Make the element's opacity pulse*/ 16 | /* Usage 17 | .myElement { 18 | animation: opacityPulse 1s ease-out; 19 | animation-iteration-count: infinite; 20 | opacity: 0; 21 | } 22 | */ 23 | @-webkit-keyframes opacityPulse { 24 | 0% {opacity: 0.0;} 25 | 50% {opacity: 1.0;} 26 | 100% {opacity: 0.0;} 27 | } 28 | 29 | /* Make the element's background pulse. I call this alertPulse because it is red. You can call it something more generic. */ 30 | /* Usage 31 | .myElement { 32 | animation: alertPulse 1s ease-out; 33 | animation-iteration-count: infinite; 34 | opacity: 1; 35 | } 36 | */ 37 | @-webkit-keyframes alertPulse { 38 | 0% {background-color: #9A2727; opacity: 1;} 39 | 50% {opacity: red; opacity: 0.75; } 40 | 100% {opacity: #9A2727; opacity: 1;} 41 | } 42 | 43 | 44 | /* Make the element rotate infinitely. */ 45 | /* 46 | Usage 47 | .myElement { 48 | animation: rotating 3s linear infinite; 49 | } 50 | */ 51 | @keyframes rotating { 52 | from { 53 | -ms-transform: rotate(0deg); 54 | -moz-transform: rotate(0deg); 55 | -webkit-transform: rotate(0deg); 56 | -o-transform: rotate(0deg); 57 | transform: rotate(0deg); 58 | } 59 | to { 60 | -ms-transform: rotate(360deg); 61 | -moz-transform: rotate(360deg); 62 | -webkit-transform: rotate(360deg); 63 | -o-transform: rotate(360deg); 64 | transform: rotate(360deg); 65 | } 66 | } -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic-ui/src/css/loading_animation.css: -------------------------------------------------------------------------------- 1 | body { 2 | -webkit-perspective: 3000; 3 | perspective: 3000; 4 | } 5 | 6 | section { 7 | margin: 10% auto; 8 | padding: 0; 9 | width: 100%; 10 | max-width: 1000px; } 11 | 12 | section h2 { 13 | font: 100 2em 'Helvetica Neue', Helvetica, Arial, sans-serif; 14 | margin: 0 5%; } 15 | 16 | 17 | .loading { 18 | padding: 10% 0; 19 | text-align: center; } 20 | 21 | .loader-1 .loading-spinner { 22 | position: relative; 23 | width: 2em; 24 | height: 2em; 25 | display: inline-block; 26 | font-size: 2em; } 27 | 28 | .loader-1 .loading-spinner .loader { 29 | position: absolute; 30 | top: 0; 31 | right: 0; 32 | bottom: 0; 33 | left: 0; 34 | -webkit-animation: loader 4s infinite ease-in-out; 35 | -moz-animation: loader 4s infinite ease-in-out; 36 | -ms-animation: loader 4s infinite ease-in-out; 37 | -o-animation: loader 4s infinite ease-in-out; 38 | animation: loader 4s infinite ease-in-out; 39 | -moz-box-shadow: inset 0 0 0 .0666666667em #888; 40 | -webkit-box-shadow: inset 0 0 0 .0666666667em #888; 41 | box-shadow: inset 0 0 0 .0666666667em #888; } 42 | 43 | .loader-1 .loading-spinner .loader:after { 44 | content: ' '; 45 | display: inline-block; 46 | vertical-align: top; 47 | width: 100%; 48 | background: #888; 49 | opacity: .5; 50 | -webkit-animation: loader-inner 4s infinite ease-in-out; 51 | -moz-animation: loader-inner 4s infinite ease-in-out; 52 | -ms-animation: loader-inner 4s infinite ease-in-out; 53 | -o-animation: loader-inner 4s infinite ease-in-out; 54 | animation: loader-inner 4s infinite ease-in-out; } 55 | 56 | .loader-1 .loading-spinner .icon { 57 | position: absolute; 58 | top: 0; 59 | right: 0; 60 | bottom: 0; 61 | left: 0; 62 | line-height: 2em; 63 | text-align: center; 64 | color: #888; 65 | -webkit-animation: icon-anim 4s infinite ease-in-out; 66 | animation: icon-anim 4s infinite ease-in-out; 67 | } 68 | 69 | @-webkit-keyframes icon-anim { 70 | 0% { 71 | transform: scale(1); } 72 | 25% { 73 | transform: scale(1.25); } 74 | 50% { 75 | transform: scale(1); } 76 | 75% { 77 | transform: scale(1.25); } 78 | 100% { 79 | transform: scale(1); } 80 | } 81 | @-webkit-keyframes loader { 82 | 0% { 83 | transform: rotate(0deg) scale(1); } 84 | 25% { 85 | transform: rotate(180deg) scale(1.25); } 86 | 50% { 87 | transform: rotate(180deg) scale(1); } 88 | 75% { 89 | transform: rotate(360deg) scale(1.25); } 90 | 100% { 91 | transform: rotate(360deg) scale(1); } } 92 | @-moz-keyframes loader { 93 | 0% { 94 | transform: rotate(0deg) scale(1); } 95 | 25% { 96 | transform: rotate(180deg) scale(1.25); } 97 | 50% { 98 | transform: rotate(180deg) scale(1); } 99 | 75% { 100 | transform: rotate(360deg) scale(1.25); } 101 | 100% { 102 | transform: rotate(360deg) scale(1); } } 103 | @-ms-keyframes loader { 104 | 0% { 105 | transform: rotate(0deg) scale(1); } 106 | 25% { 107 | transform: rotate(180deg) scale(1.25); } 108 | 50% { 109 | transform: rotate(180deg) scale(1); } 110 | 75% { 111 | transform: rotate(360deg) scale(1.25); } 112 | 100% { 113 | transform: rotate(360deg) scale(1); } } 114 | @-o-keyframes loader { 115 | 0% { 116 | transform: rotate(0deg) scale(1); } 117 | 25% { 118 | transform: rotate(180deg) scale(1.25); } 119 | 50% { 120 | transform: rotate(180deg) scale(1); } 121 | 75% { 122 | transform: rotate(360deg) scale(1.25); } 123 | 100% { 124 | transform: rotate(360deg) scale(1); } } 125 | @keyframes loader { 126 | 0% { 127 | transform: rotate(0deg) scale(1); } 128 | 25% { 129 | transform: rotate(180deg) scale(1.25); } 130 | 50% { 131 | transform: rotate(180deg) scale(1); } 132 | 75% { 133 | transform: rotate(360deg) scale(1.25); } 134 | 100% { 135 | transform: rotate(360deg) scale(1); } } 136 | 137 | 138 | @-webkit-keyframes loader-inner { 139 | 0%, 25%, 100% { 140 | height: 0; } 141 | 50%, 75% { 142 | height: 100%; } } 143 | @-moz-keyframes loader-inner { 144 | 0%, 25%, 100% { 145 | height: 0; } 146 | 50%, 75% { 147 | height: 100%; } } 148 | @-ms-keyframes loader-inner { 149 | 0%, 25%, 100% { 150 | height: 0; } 151 | 50%, 75% { 152 | height: 100%; } } 153 | @-o-keyframes loader-inner { 154 | 0%, 25%, 100% { 155 | height: 0; } 156 | 50%, 75% { 157 | height: 100%; } } 158 | @keyframes loader-inner { 159 | 0%, 25%, 100% { 160 | height: 0; } 161 | 50%, 75% { 162 | height: 100%; } } -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic-ui/src/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Space Mono', monospace; 3 | background-color: #fbfdff; 4 | font-size: 9px; 5 | } 6 | h1,h2,h3,h4,h5 { 7 | color: #34495e; 8 | } 9 | h1,h2,h3,h4,h5,p { 10 | text-align: left; 11 | } 12 | h1 { 13 | font-family: 'Space Mono', monospace; 14 | font-weight: 700; 15 | } 16 | h2 { 17 | font-family: 'Space Mono', monospace; 18 | font-weight: 100; 19 | font-size: 25px; 20 | } 21 | h3 { 22 | font-family: 'Space Mono', monospace; 23 | font-weight: 100; 24 | font-size: 16px; 25 | } 26 | h5 { 27 | font-weight: 100; 28 | font-size: 12px; 29 | } 30 | 31 | p { 32 | text-align: left; 33 | font-weight: 300; 34 | line-height: 1.7em; 35 | font-size: 12px; 36 | color: #000000; 37 | } 38 | img { 39 | max-width: 100%; 40 | display: block; 41 | } 42 | iframe { 43 | max-width: 100%; 44 | display: block; 45 | } 46 | svg:not(:root) { 47 | position: relative; 48 | overflow: visible; 49 | } 50 | 51 | .sidebar-top { 52 | padding-bottom: 50px; 53 | } 54 | .related { 55 | font-size: 11px !important; 56 | } 57 | .hide-overflow { 58 | overflow: hidden; 59 | } 60 | .line-divisor { 61 | padding: 20px 0pt; 62 | margin: 20px 0pt; 63 | border-bottom: 1px solid #DDDDDD; 64 | border-top: 1px solid #DDDDDD; 65 | } 66 | 67 | #graph { 68 | height: 800px; 69 | width: 100%; 70 | position: relative; 71 | overflow: visible; 72 | margin: 0px; 73 | } 74 | 75 | .logo-container { 76 | padding: 0px; 77 | } 78 | .logo { 79 | max-height: 35px; 80 | vertical-align: middle; 81 | padding: 0px; 82 | margin: 0px; 83 | } 84 | .article-card .title { 85 | font-family: 'Merriweather'; 86 | font-weight: 500; 87 | font-size: 26px; 88 | } 89 | .article-card { 90 | font-family: 'Roboto Mono'; 91 | font-weight: 100; 92 | font-size: 10px; 93 | } 94 | .total-scheduled-instances { 95 | font-family: 'Roboto Mono'; 96 | font-weight: 300; 97 | font-size: 10px; 98 | padding-left: 10px; 99 | } 100 | 101 | /* 102 | 103 | Pulsing effect. 104 | 105 | */ 106 | .alert_green { 107 | display: block; 108 | width: 10px; 109 | height: 10px; 110 | border-radius: 50%; 111 | background: #2ecc71; 112 | cursor: pointer; 113 | box-shadow: 0 0 0 rgba(209, 193, 139, 0.40); 114 | } 115 | .alert_yellow { 116 | display: block; 117 | width: 10px; 118 | height: 10px; 119 | border-radius: 50%; 120 | background: #f1c40f; 121 | cursor: pointer; 122 | box-shadow: 0 0 0 rgba(209, 193, 139, 0.40); 123 | } 124 | .alert_red { 125 | display: block; 126 | width: 10px; 127 | height: 10px; 128 | border-radius: 50%; 129 | background: #e74c3c; 130 | cursor: pointer; 131 | box-shadow: 0 0 0 rgba(209, 193, 139, 0.40); 132 | animation: pulse 1.5s infinite; 133 | } 134 | .alert_red:hover { 135 | animation: pulse 1.5s infinite; 136 | } 137 | 138 | @-webkit-keyframes pulse { 139 | 0% { 140 | -webkit-box-shadow: 0 0 0 0 rgba(182, 182, 182, 0.40); 141 | } 142 | 70% { 143 | -webkit-box-shadow: 0 0 0 20px rgba(182, 182, 182, 0); 144 | } 145 | 100% { 146 | -webkit-box-shadow: 0 0 0 0 rgba(182, 182, 182, 0); 147 | } 148 | } 149 | @keyframes pulse { 150 | 0% { 151 | -moz-box-shadow: 0 0 0 0 rgba(182, 182, 182, 0.40); 152 | box-shadow: 0 0 0 0 rgba(182, 182, 182, 0.40); 153 | } 154 | 70% { 155 | -moz-box-shadow: 0 0 0 20px rgba(182, 182, 182, 0); 156 | box-shadow: 0 0 0 20px rgba(182, 182, 182, 0); 157 | } 158 | 100% { 159 | -moz-box-shadow: 0 0 0 0 rgba(182, 182, 182, 0); 160 | box-shadow: 0 0 0 0 rgba(182, 182, 182, 0); 161 | } 162 | } -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic-ui/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Cryptonic 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic-ui/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import ElementUI from 'element-ui' 3 | import lodash from 'lodash' 4 | import c3 from 'c3' 5 | import Trend from 'vuetrend' 6 | import moment from 'moment-timezone' 7 | 8 | import 'c3/c3.min.css' 9 | import 'element-ui/lib/theme-default/index.css' 10 | import './css/style.css' 11 | import './css/forbes_logo.css' 12 | import './css/loading_animation.css' 13 | import './css/infinite.css' 14 | import App from './App.vue' 15 | import locale from 'element-ui/lib/locale/lang/en' 16 | 17 | import VueMoment from 'vue-moment' 18 | import VueResource from 'vue-resource' 19 | import VueLodash from 'vue-lodash' 20 | 21 | 22 | Vue.prototype.$c3 = c3 23 | 24 | Vue.use(VueLodash, lodash) 25 | Vue.use(ElementUI, { locale }) 26 | Vue.use(VueResource) 27 | Vue.use(VueMoment, { moment }) 28 | Vue.use(Trend) 29 | Vue.use(c3) 30 | 31 | 32 | new Vue({ 33 | el: '#app', 34 | render: h => h(App) 35 | }) 36 | -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic-ui/webpack.config.js: -------------------------------------------------------------------------------- 1 | const resolve = require('path').resolve 2 | const webpack = require('webpack') 3 | const HtmlWebpackPlugin = require('html-webpack-plugin') 4 | const url = require('url') 5 | const publicPath = '' 6 | 7 | module.exports = (options = {}) => ({ 8 | entry: { 9 | vendor: './src/vendor', 10 | index: './src/main.js' 11 | }, 12 | output: { 13 | path: resolve(__dirname, 'dist'), 14 | filename: options.dev ? '[name].js' : '[name].js?[chunkhash]', 15 | chunkFilename: '[id].js?[chunkhash]', 16 | publicPath: options.dev ? '/assets/' : publicPath 17 | }, 18 | module: { 19 | rules: [{ 20 | test: /\.vue$/, 21 | use: ['vue-loader'] 22 | }, 23 | { 24 | test: /\.js$/, 25 | use: ['babel-loader'], 26 | exclude: /node_modules/, 27 | include: [ 28 | resolve(__dirname, 'src'), 29 | resolve(__dirname, 'test'), 30 | resolve(__dirname, 'node_modules/element-ui/packages') 31 | ] 32 | }, 33 | { 34 | test: /\.css$/, 35 | use: ['style-loader', 'css-loader', 'postcss-loader'] 36 | }, 37 | { 38 | test: /\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(\?.+)?$/, 39 | use: [{ 40 | loader: 'url-loader', 41 | options: { 42 | limit: 10000 43 | } 44 | }] 45 | } 46 | ] 47 | }, 48 | plugins: [ 49 | new webpack.optimize.CommonsChunkPlugin({ 50 | names: ['vendor', 'manifest'] 51 | }), 52 | new HtmlWebpackPlugin({ 53 | template: 'src/index.html' 54 | }) 55 | ], 56 | resolve: { 57 | alias: { 58 | '~': resolve(__dirname, 'src') 59 | } 60 | }, 61 | devServer: { 62 | host: '127.0.0.1', 63 | port: 8010, 64 | proxy: { 65 | '/api/': { 66 | target: 'http://127.0.0.1:8080', 67 | changeOrigin: true, 68 | pathRewrite: { 69 | '^/api': '' 70 | } 71 | } 72 | }, 73 | historyApiFallback: { 74 | index: url.parse(options.dev ? '/assets/' : publicPath).pathname 75 | } 76 | }, 77 | devtool: options.dev ? '#eval-source-map' : '#source-map' 78 | }) 79 | -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic.env: -------------------------------------------------------------------------------- 1 | REDIS_URL=redis://redis@cache:6379/0 2 | CRYPTONIC_VERSION=latest 3 | BITCOIN_START_DATE=2017-01-01 4 | EPOCHS=300 5 | PERIOD_SIZE=7 -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Cryptonic is an educational application created for 3 | the purposes of learning how to use deep learning 4 | to predict Bitcoin prices. 5 | """ 6 | from cryptonic.models.model import Model 7 | from cryptonic.markets.coinmarketcap import CoinMarketCap 8 | 9 | __version__ = 'v1.0.1' 10 | __author__ = 'Luis Capelo' 11 | __email__ = 'luiscape@gmail.com' 12 | 13 | __all__ = ['Model', 'CoinMarketCap'] 14 | -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic/api/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | HTTP API interface for Cryptonic. 3 | """ 4 | 5 | __version__ = 1 6 | -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic/api/routes.py: -------------------------------------------------------------------------------- 1 | """ 2 | Creates all application routes. 3 | """ 4 | import os 5 | 6 | from datetime import datetime, timedelta 7 | from flask_api import status as http_status 8 | from flask import jsonify, request, make_response, send_file, send_from_directory 9 | 10 | from cryptonic.api import __version__ 11 | 12 | 13 | def __cache_identifier(*args, **kwargs): 14 | """ 15 | Creates a cache identifier based on the complete 16 | URL provided in the request. Original solution: 17 | 18 | * https://stackoverflow.com/questions/9413566/\ 19 | flask-cache-memoize-url-query-string-parameters-as-well 20 | 21 | """ 22 | # 23 | # The following snippet will create a 24 | # cache key for the URL and all GET and POST 25 | # parameters. 26 | # 27 | key = request.url + \ 28 | '='.join([k + '=' + v for k,v in request.args.items()]) + '&' + \ 29 | '='.join([k + '=' + v for k,v in request.form.items()]) 30 | 31 | return key 32 | 33 | 34 | def create_routes(app, cache, model): 35 | """ 36 | Function that creates the application routes. 37 | 38 | Parameters 39 | ---------- 40 | app: Flask object 41 | Initialized Flask object. 42 | 43 | cache: Flask-Caching instance 44 | Cache instance that can be used 45 | as a decorator. 46 | 47 | model: instantiated Model() 48 | Module() class instantiated as a model 49 | and trained. 50 | 51 | Returns 52 | ------- 53 | app: Flask object 54 | Modified Flask app object. 55 | """ 56 | # 57 | # Let's clear the cache at every 58 | # startup. 59 | # 60 | cache.clear() 61 | 62 | @cache.cached(timeout=60 * 5, key_prefix=__cache_identifier) 63 | @app.errorhandler(404) 64 | def page_not_found(e): 65 | return app.send_static_file('index.html'), 404 66 | 67 | @cache.cached(timeout=60 * 5, key_prefix=__cache_identifier) 68 | @app.route('/') 69 | def root(): 70 | """ 71 | Endpoint for serving the index page. 72 | """ 73 | return app.send_static_file('index.html') 74 | 75 | @app.route('/') 76 | def send_static_files(path): 77 | return send_from_directory( 78 | os.getenv('UI_DIST_DIRECTORY', '../cryptonic-ui/dist/'), path) 79 | 80 | @app.route('/status') 81 | def status(): 82 | """ 83 | Endpoint for returning the application status. 84 | """ 85 | r = { 86 | 'version': __version__, 87 | 'success': True, 88 | 'message': 'Predict Bitcoin prices with deep learning.', 89 | 'model': { 90 | 'name': os.getenv('MODEL_NAME'), 91 | 'last_trained': model.last_trained, 92 | 'error_rates': model.evaluate() 93 | } 94 | } 95 | return jsonify(r) 96 | 97 | @cache.cached(timeout=60 * 24, key_prefix=__cache_identifier) 98 | @app.route('/historic') 99 | def historic(): 100 | """ 101 | Returns a series of historic observations. 102 | 103 | Parameters 104 | ---------- 105 | start: str, default six months ago 106 | Start date to filter the Bitcoin historic price 107 | data set. 108 | 109 | """ 110 | six_months_ago = ( 111 | datetime.now() - timedelta(days=30 * 6)).strftime('%Y-%m-%d') 112 | start = request.args.get('start', six_months_ago) 113 | 114 | historic_data = model.data.to_dict(orient='records') 115 | filtered_historic_data = list(filter(lambda x: x['date'] > six_months_ago, historic_data)) 116 | 117 | r = { 118 | 'version': __version__, 119 | 'success': True, 120 | 'message': 'Historic Bitcoin prices from CoinMarketCap.', 121 | 'start_date': os.getenv('BITCOIN_START_DATE'), 122 | 'result': filtered_historic_data 123 | } 124 | 125 | return jsonify(r) 126 | 127 | @cache.cached(timeout=60 * 24, key_prefix=__cache_identifier) 128 | @app.route('/predict') 129 | def predict(): 130 | """ 131 | Endpoint for predicting bitcoin prices. 132 | """ 133 | r = { 134 | 'version': __version__, 135 | 'success': True, 136 | 'message': 'Endpoint for making predictions.', 137 | 'period_length': os.getenv('PERIOD_SIZE', 7), 138 | 'result': model.predict(denormalized=True, return_dict=True) 139 | } 140 | return jsonify(r) 141 | -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic/markets/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Interface for manipulating data from different markets. 3 | """ 4 | -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic/markets/coinmarketcap.py: -------------------------------------------------------------------------------- 1 | """ 2 | Logic for collecting data directly from the 3 | CoinMarketCap API. 4 | """ 5 | import requests 6 | import pandas as pd 7 | 8 | from datetime import datetime 9 | from bs4 import BeautifulSoup 10 | 11 | 12 | class CoinMarketCap: 13 | """ 14 | Class interface to data from CoinMarketCap. 15 | Original data can be found at: 16 | 17 | https://coinmarketcap.com/ 18 | 19 | """ 20 | def __repr__(self): 21 | message = """ 22 | 23 | Crypto-currency data comes from the website CoinMarketCap. 24 | CoinMarketCap is can be accessed at: https://coinmarketcap.com/ 25 | 26 | The permission to use the data is available on their FAQ 27 | 28 | https://coinmarketcap.com/faq/ 29 | 30 | and reads: 31 | 32 | "Q: Am I allowed to use content (screenshots, data, graphs, etc.) 33 | for one of my personal projects and/or commercial use? 34 | 35 | R: Absolutely! Feel free to use any content as you see fit. 36 | We kindly ask that you cite us as a source." 37 | 38 | """ 39 | return message 40 | 41 | @classmethod 42 | def historic(cls, start='2013-04-28', stop=None, ticker='bitcoin', return_json=False): 43 | """ 44 | Retrieves historic data within a time 45 | period. 46 | 47 | Parameters 48 | ---------- 49 | start, stop: str 50 | Start and stop dates in ISO format (YYYY-MM-DD). 51 | 52 | ticker: str 53 | Name of ticker to be used (e.g. `bitcoin`). 54 | 55 | Returns 56 | ------- 57 | Pandas dataframe with historical ticker data. 58 | """ 59 | start = start.replace('-', '') 60 | if not stop: 61 | stop = datetime.now().strftime('%Y%m%d') 62 | 63 | url = 'https://coinmarketcap.com/currencies/{}/historical-data/?start={}&end={}'.format(ticker, start, stop) 64 | r = requests.get(url) 65 | 66 | soup = BeautifulSoup(r.content, 'lxml') 67 | table = soup.find_all('table')[0] 68 | df = pd.read_html(str(table))[0] 69 | 70 | # 71 | # Cleans variables from the original. 72 | # 73 | df['Date'] = df['Date'].apply(lambda x: datetime.strptime(x, '%b %d, %Y').strftime('%Y-%m-%d')) 74 | df['Volume'] = df['Volume'].apply(lambda x: None if x == '-' else x) 75 | df.columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'market_cap'] 76 | 77 | if return_json: 78 | df.to_json(orient='records') 79 | 80 | return df 81 | 82 | @classmethod 83 | def current(cls, ticker='bitcoin'): 84 | """ 85 | Fetches current prices from CoinMarketCap. 86 | 87 | Returns 88 | ------- 89 | Dictionary with a single record form the 90 | """ 91 | url = 'https://api.coinmarketcap.com/v1/ticker/{}/'.format(ticker) 92 | r = requests.get(url) 93 | 94 | return r.json() 95 | -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic/models/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Cryptocurrency prediction models. 3 | """ 4 | -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic/models/helper.py: -------------------------------------------------------------------------------- 1 | """ 2 | Helper class and methods for making manipulating 3 | data for models. 4 | """ 5 | import numpy as np 6 | 7 | import cryptonic.models.normalizations as normalizations 8 | 9 | 10 | class ModelHelper: 11 | """ 12 | Class with utility functions that aid in 13 | the process of training LSTM models with Keras. 14 | 15 | """ 16 | def __init__(self): 17 | pass 18 | 19 | def create_groups(self, data, start=0, group_size=7, normalize=True): 20 | """ 21 | Creates distinct groups from a given continuous series. 22 | 23 | Parameters 24 | ---------- 25 | data: np.array 26 | Series of continious observations. 27 | 28 | start: int 29 | Starting point for the series. This 30 | is used to prune earlier observations 31 | from the series in case the series is 32 | too long or too short. 33 | 34 | group_size: int, default 7 35 | Determines how large the groups are. That is, 36 | how many observations each group contains. 37 | 38 | normalize: bool 39 | If the method should normalize data or not. 40 | Normalization is done using 41 | 42 | normalizations.point_relative_normalization() 43 | 44 | Returns 45 | ------- 46 | A Numpy array object. 47 | """ 48 | samples = list() 49 | for i in range(0, len(data), group_size): 50 | sample = list(data[start + i:i + group_size]) 51 | if len(sample) == group_size: 52 | if normalize: 53 | sample = normalizations.point_relative_normalization(sample) 54 | 55 | samples.append(np.array(sample).reshape(1, group_size).tolist()) 56 | 57 | A = np.array(samples) 58 | return A.reshape(1, A.shape[0], group_size) 59 | 60 | def split_lstm_input(self, groups): 61 | """ 62 | Splits groups in a format expected by 63 | the LSTM layer. 64 | 65 | Parameters 66 | ---------- 67 | groups: np.array 68 | Numpy array with the organized sequences. 69 | 70 | Returns 71 | ------- 72 | X, Y: np.array 73 | Numpy arrays with the shapes required by 74 | the LSTM layer. X with (1, a - 1, b) 75 | and Y with (1, b). Where a is the total 76 | number of groups in `group` and b the 77 | number of observations per group. 78 | """ 79 | X = groups[0:,:-1].reshape(1, groups.shape[1] - 1, groups.shape[2]) 80 | Y = groups[0:,-1:][0] 81 | 82 | return X, Y 83 | 84 | def normalize(self): 85 | """ 86 | Normalizes a series using point-relative normalization. 87 | 88 | Parameters 89 | ---------- 90 | 91 | Returns 92 | ------- 93 | """ 94 | normalizations.point_relative_normalization() 95 | 96 | def denormalize(self, series, last_value): 97 | """ 98 | De-normalizes a series using the latest 99 | value available from data. 100 | 101 | Parameters 102 | ---------- 103 | series: numpy array 104 | Series with normalized values. 105 | 106 | last_value: float 107 | Numerical value that represents the 108 | last value from the dataset. 109 | 110 | Returns 111 | ------- 112 | """ 113 | result = last_value * (series + 1) 114 | return result 115 | 116 | def mape(self, A, B): 117 | """ 118 | Calcualtes the mean absolute persentage error 119 | from two series. Original solution from: 120 | 121 | https://stats.stackexchange.com/questions/58391/\ 122 | mean-absolute-percentage-error-mape-in-scikit-learn 123 | """ 124 | return np.mean(np.abs((A - B) / (1 - A))) * 100 125 | 126 | def rmse(self, A, B): 127 | """ 128 | Calculates the root mean square error from 129 | two series. Original solution from: 130 | 131 | https://stackoverflow.com/questions/16774849\ 132 | /mean-squared-error-in-numpy 133 | """ 134 | return np.sqrt(np.square(np.subtract(A, B)).mean()) 135 | 136 | def mse(self, A, B): 137 | """ 138 | Calculates the mean square error from 139 | two series. Original solution from: 140 | 141 | https://stackoverflow.com/questions/16774849\ 142 | /mean-squared-error-in-numpy 143 | """ 144 | return np.square(np.subtract(A, B)).mean() 145 | -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic/models/normalizations.py: -------------------------------------------------------------------------------- 1 | """ 2 | Series of normalization functions useful 3 | for normalizing time-series data. 4 | """ 5 | 6 | def z_score(series): 7 | """ 8 | Computes the normalized value using the Z-score 9 | technique. The Z-score is a technique used for 10 | normalizing Gaussian distributions representing 11 | each observation in relation to the distribution's 12 | mean and standard deviation. For precise definitions, 13 | see the Wikipedia article: 14 | 15 | https://en.wikipedia.org/wiki/Standard_score 16 | 17 | Parameters 18 | ---------- 19 | serie: list 20 | List with sequential values to use. 21 | 22 | Returns 23 | ------- 24 | result: list 25 | List with the normalized results. 26 | """ 27 | result = (series - series.mean()) / series.std(ddof=0) 28 | return result 29 | 30 | def point_relative_normalization(series, reverse=False, last_value=None): 31 | """ 32 | Computes the normalized value for the values of a 33 | given series by using the first element of the serie as p_0 34 | as a reference for each p_i. 35 | 36 | This technique comes from Siraj Raval's YouTube video 37 | "How to Predict Stock Prices Easily - Intro to Deep Learning #7", 38 | available at: 39 | 40 | https://www.youtube.com/watch?v=ftMq5ps503w 41 | 42 | Parameters 43 | ---------- 44 | serie: list 45 | List with sequential values to use. 46 | 47 | reverse: bool, default True 48 | If the method should de-normalize data. 49 | 50 | last_value: int or float 51 | Used to de-normalize a dataset. Needs to 52 | be passed if `reverse` is True. 53 | 54 | Returns 55 | ------- 56 | result: list 57 | List with the normalized results. 58 | """ 59 | if reverse: 60 | result = last_value * (series + 1) 61 | else: 62 | result = (series / series[0]) - 1 63 | 64 | return result 65 | 66 | def maximum_and_minimum_normalization(series, boundary=(0, 1)): 67 | """ 68 | Computes the normalized value for the values of a 69 | given serie by using that series maximum and minimum 70 | values. 71 | 72 | This technique is a direct implementation from 73 | scikit-learn, available at: 74 | 75 | http://scikit-learn.org/stable/modules/generated/\ 76 | sklearn.preprocessing.MinMaxScaler.html 77 | 78 | Parameters 79 | ---------- 80 | serie: list 81 | List with sequential values to use. 82 | 83 | boundary: set 84 | Maximum and minimum values used to 85 | scale the series. 86 | 87 | Returns 88 | ------- 89 | result: list 90 | List with the normalized results. 91 | """ 92 | range_min, range_max = boundary 93 | standard_deviation = (series - series.min(axis=0)) / (series.max(axis=0) - series.min(axis=0)) 94 | result = standard_deviation * (range_max - range_min) + range_min 95 | return result 96 | -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic/server.py: -------------------------------------------------------------------------------- 1 | """ 2 | Functions to start and configure the Flask 3 | application. This simply configures the server 4 | and its routes. 5 | """ 6 | import os 7 | import flask 8 | 9 | from flask_caching import Cache 10 | from flask_cors import CORS, cross_origin 11 | 12 | from cryptonic import Model 13 | #from cryptonic import CoinMarketCap 14 | from cryptonic.api.routes import create_routes 15 | import yfinance as yf 16 | 17 | UI_DIST_DIRECTORY = os.getenv('UI_DIST_DIRECTORY', '../cryptonic-ui/dist/') 18 | 19 | 20 | class Server: 21 | """ 22 | Cryptonic server representation. This class 23 | contains logic for managing the configuration 24 | and deployment of Flask server. 25 | 26 | Parameters 27 | ---------- 28 | debug: bool, default False 29 | If should start with a debugger. 30 | 31 | cors: bool, default True 32 | If the application should accept CORS 33 | requests. 34 | 35 | """ 36 | def __init__(self, debug=False, cors=True): 37 | self.debug = debug 38 | self.cors = cors 39 | 40 | self.create_model() 41 | self.app = self.create() 42 | 43 | def create_model(self): 44 | """ 45 | Creates a model either using a model provided 46 | by user or by creating a new model using 47 | previously researched parameters. 48 | 49 | Returns 50 | ------- 51 | Trained Keras model. Ready to be used 52 | via the model.predict() method. 53 | """ 54 | ticker = yf.Ticker("BTC-USD") 55 | historic_data = ticker.history(period='max') 56 | historic_data = historic_data.rename(columns={'Open':'open', 'High':'high', 'Low':'low', 'Close':'close', 'Volume':'volume'}) 57 | historic_data.index.names = ['date'] 58 | historic_data = historic_data[['open','high', 'low', 'close', 'volume']] 59 | historic_data = historic_data.reset_index() 60 | 61 | model_path = os.getenv('MODEL_NAME') 62 | 63 | # 64 | # TODO: Figure out how large the data is for 65 | # the old model and re-train. Maybe what I have 66 | # to do here is to copy the weights of the 67 | # model into a new model. 68 | # 69 | 70 | self.model = Model(data=historic_data, 71 | path=model_path, 72 | variable='close', 73 | predicted_period_size=int(os.getenv('PERIOD_SIZE', 7))) 74 | 75 | if not model_path: 76 | self.model.build() 77 | self.model.train(epochs=int(os.getenv('EPOCHS', 50)), verbose=1) 78 | 79 | return self.model 80 | 81 | def create(self): 82 | """ 83 | Method for creating a Flask server. 84 | 85 | Returns 86 | ------- 87 | A Flask() application object. 88 | """ 89 | app = flask.Flask(__name__, static_url_path='/', static_folder=UI_DIST_DIRECTORY) 90 | 91 | # 92 | # Application configuration. Here we 93 | # configure the application to accept 94 | # CORS requests, its routes, and 95 | # its debug flag. 96 | # 97 | if self.cors: 98 | CORS(app) 99 | 100 | cache_configuration = { 101 | 'CACHE_TYPE': 'redis', 102 | 'CACHE_REDIS_URL': os.getenv('REDIS_URL', 103 | "redis://localhost:6379/2") 104 | } 105 | 106 | self.cache = Cache(app, config=cache_configuration) 107 | 108 | app.config['DEBUG'] = self.debug 109 | create_routes(app, self.cache, self.model) 110 | 111 | return app 112 | 113 | def run(self, *args, **kwargs): 114 | """ 115 | Method for running Flask server. 116 | Parameters 117 | ---------- 118 | *args, **kwargs: parameters passed to the Flask application. 119 | """ 120 | self.app.run(*args, **kwargs) -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic_old/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Cryptonic is an educational application created for 3 | the purposes of learning how to use deep learning 4 | to predict Bitcoin prices. 5 | """ 6 | from cryptonic.models.model import Model 7 | from cryptonic.markets.coinmarketcap import CoinMarketCap 8 | 9 | __version__ = 'v1.0.1' 10 | __author__ = 'Luis Capelo' 11 | __email__ = 'luiscape@gmail.com' 12 | 13 | __all__ = ['Model', 'CoinMarketCap'] 14 | -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic_old/api/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | HTTP API interface for Cryptonic. 3 | """ 4 | 5 | __version__ = 1 6 | -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic_old/api/routes.py: -------------------------------------------------------------------------------- 1 | """ 2 | Creates all application routes. 3 | """ 4 | import os 5 | 6 | from datetime import datetime, timedelta 7 | from flask_api import status as http_status 8 | from flask import jsonify, request, make_response, send_file, send_from_directory 9 | 10 | from cryptonic.api import __version__ 11 | 12 | 13 | def __cache_identifier(*args, **kwargs): 14 | """ 15 | Creates a cache identifier based on the complete 16 | URL provided in the request. Original solution: 17 | 18 | * https://stackoverflow.com/questions/9413566/\ 19 | flask-cache-memoize-url-query-string-parameters-as-well 20 | 21 | """ 22 | # 23 | # The following snippet will create a 24 | # cache key for the URL and all GET and POST 25 | # parameters. 26 | # 27 | key = request.url + \ 28 | '='.join([k + '=' + v for k,v in request.args.items()]) + '&' + \ 29 | '='.join([k + '=' + v for k,v in request.form.items()]) 30 | 31 | return key 32 | 33 | 34 | def create_routes(app, cache, model): 35 | """ 36 | Function that creates the application routes. 37 | 38 | Parameters 39 | ---------- 40 | app: Flask object 41 | Initialized Flask object. 42 | 43 | cache: Flask-Caching instance 44 | Cache instance that can be used 45 | as a decorator. 46 | 47 | model: instantiated Model() 48 | Module() class instantiated as a model 49 | and trained. 50 | 51 | Returns 52 | ------- 53 | app: Flask object 54 | Modified Flask app object. 55 | """ 56 | # 57 | # Let's clear the cache at every 58 | # startup. 59 | # 60 | cache.clear() 61 | 62 | @cache.cached(timeout=60 * 5, key_prefix=__cache_identifier) 63 | @app.errorhandler(404) 64 | def page_not_found(e): 65 | return app.send_static_file('index.html'), 404 66 | 67 | @cache.cached(timeout=60 * 5, key_prefix=__cache_identifier) 68 | @app.route('/') 69 | def root(): 70 | """ 71 | Endpoint for serving the index page. 72 | """ 73 | return app.send_static_file('index.html') 74 | 75 | @app.route('/') 76 | def send_static_files(path): 77 | return send_from_directory( 78 | os.getenv('UI_DIST_DIRECTORY', '../cryptonic-ui/dist/'), path) 79 | 80 | @app.route('/status') 81 | def status(): 82 | """ 83 | Endpoint for returning the application status. 84 | """ 85 | r = { 86 | 'version': __version__, 87 | 'success': True, 88 | 'message': 'Predict Bitcoin prices with deep learning.', 89 | 'model': { 90 | 'name': os.getenv('MODEL_NAME'), 91 | 'last_trained': model.last_trained, 92 | 'error_rates': model.evaluate() 93 | } 94 | } 95 | return jsonify(r) 96 | 97 | @cache.cached(timeout=60 * 24, key_prefix=__cache_identifier) 98 | @app.route('/historic') 99 | def historic(): 100 | """ 101 | Returns a series of historic observations. 102 | 103 | Parameters 104 | ---------- 105 | start: str, default six months ago 106 | Start date to filter the Bitcoin historic price 107 | data set. 108 | 109 | """ 110 | six_months_ago = ( 111 | datetime.now() - timedelta(days=30 * 6)).strftime('%Y-%m-%d') 112 | start = request.args.get('start', six_months_ago) 113 | 114 | historic_data = model.data.to_dict(orient='records') 115 | filtered_historic_data = list(filter(lambda x: x['date'] > six_months_ago, historic_data)) 116 | 117 | r = { 118 | 'version': __version__, 119 | 'success': True, 120 | 'message': 'Historic Bitcoin prices from CoinMarketCap.', 121 | 'start_date': os.getenv('BITCOIN_START_DATE'), 122 | 'result': filtered_historic_data 123 | } 124 | 125 | return jsonify(r) 126 | 127 | @cache.cached(timeout=60 * 24, key_prefix=__cache_identifier) 128 | @app.route('/predict') 129 | def predict(): 130 | """ 131 | Endpoint for predicting bitcoin prices. 132 | """ 133 | r = { 134 | 'version': __version__, 135 | 'success': True, 136 | 'message': 'Endpoint for making predictions.', 137 | 'period_length': os.getenv('PERIOD_SIZE', 7), 138 | 'result': model.predict(denormalized=True, return_dict=True) 139 | } 140 | return jsonify(r) 141 | -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic_old/markets/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Interface for manipulating data from different markets. 3 | """ 4 | -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic_old/markets/coinmarketcap.py: -------------------------------------------------------------------------------- 1 | """ 2 | Logic for collecting data directly from the 3 | CoinMarketCap API. 4 | """ 5 | import requests 6 | import pandas as pd 7 | 8 | from datetime import datetime 9 | from bs4 import BeautifulSoup 10 | 11 | 12 | class CoinMarketCap: 13 | """ 14 | Class interface to data from CoinMarketCap. 15 | Original data can be found at: 16 | 17 | https://coinmarketcap.com/ 18 | 19 | """ 20 | def __repr__(self): 21 | message = """ 22 | 23 | Crypto-currency data comes from the website CoinMarketCap. 24 | CoinMarketCap is can be accessed at: https://coinmarketcap.com/ 25 | 26 | The permission to use the data is available on their FAQ 27 | 28 | https://coinmarketcap.com/faq/ 29 | 30 | and reads: 31 | 32 | "Q: Am I allowed to use content (screenshots, data, graphs, etc.) 33 | for one of my personal projects and/or commercial use? 34 | 35 | R: Absolutely! Feel free to use any content as you see fit. 36 | We kindly ask that you cite us as a source." 37 | 38 | """ 39 | return message 40 | 41 | @classmethod 42 | def historic(cls, start='2013-04-28', stop=None, ticker='bitcoin', return_json=False): 43 | """ 44 | Retrieves historic data within a time 45 | period. 46 | 47 | Parameters 48 | ---------- 49 | start, stop: str 50 | Start and stop dates in ISO format (YYYY-MM-DD). 51 | 52 | ticker: str 53 | Name of ticker to be used (e.g. `bitcoin`). 54 | 55 | Returns 56 | ------- 57 | Pandas dataframe with historical ticker data. 58 | """ 59 | start = start.replace('-', '') 60 | if not stop: 61 | stop = datetime.now().strftime('%Y%m%d') 62 | 63 | url = 'https://coinmarketcap.com/currencies/{}/historical-data/?start={}&end={}'.format(ticker, start, stop) 64 | r = requests.get(url) 65 | 66 | soup = BeautifulSoup(r.content, 'lxml') 67 | table = soup.find_all('table')[0] 68 | df = pd.read_html(str(table))[0] 69 | 70 | # 71 | # Cleans variables from the original. 72 | # 73 | df['Date'] = df['Date'].apply(lambda x: datetime.strptime(x, '%b %d, %Y').strftime('%Y-%m-%d')) 74 | df['Volume'] = df['Volume'].apply(lambda x: None if x == '-' else x) 75 | df.columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'market_cap'] 76 | 77 | if return_json: 78 | df.to_json(orient='records') 79 | 80 | return df 81 | 82 | @classmethod 83 | def current(cls, ticker='bitcoin'): 84 | """ 85 | Fetches current prices from CoinMarketCap. 86 | 87 | Returns 88 | ------- 89 | Dictionary with a single record form the 90 | """ 91 | url = 'https://api.coinmarketcap.com/v1/ticker/{}/'.format(ticker) 92 | r = requests.get(url) 93 | 94 | return r.json() 95 | -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic_old/models/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Cryptocurrency prediction models. 3 | """ 4 | -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic_old/models/helper.py: -------------------------------------------------------------------------------- 1 | """ 2 | Helper class and methods for making manipulating 3 | data for models. 4 | """ 5 | import numpy as np 6 | 7 | import cryptonic.models.normalizations as normalizations 8 | 9 | 10 | class ModelHelper: 11 | """ 12 | Class with utility functions that aid in 13 | the process of training LSTM models with Keras. 14 | 15 | """ 16 | def __init__(self): 17 | pass 18 | 19 | def create_groups(self, data, start=0, group_size=7, normalize=True): 20 | """ 21 | Creates distinct groups from a given continuous series. 22 | 23 | Parameters 24 | ---------- 25 | data: np.array 26 | Series of continious observations. 27 | 28 | start: int 29 | Starting point for the series. This 30 | is used to prune earlier observations 31 | from the series in case the series is 32 | too long or too short. 33 | 34 | group_size: int, default 7 35 | Determines how large the groups are. That is, 36 | how many observations each group contains. 37 | 38 | normalize: bool 39 | If the method should normalize data or not. 40 | Normalization is done using 41 | 42 | normalizations.point_relative_normalization() 43 | 44 | Returns 45 | ------- 46 | A Numpy array object. 47 | """ 48 | samples = list() 49 | for i in range(0, len(data), group_size): 50 | sample = list(data[start + i:i + group_size]) 51 | if len(sample) == group_size: 52 | if normalize: 53 | sample = normalizations.point_relative_normalization(sample) 54 | 55 | samples.append(np.array(sample).reshape(1, group_size).tolist()) 56 | 57 | A = np.array(samples) 58 | return A.reshape(1, A.shape[0], group_size) 59 | 60 | def split_lstm_input(self, groups): 61 | """ 62 | Splits groups in a format expected by 63 | the LSTM layer. 64 | 65 | Parameters 66 | ---------- 67 | groups: np.array 68 | Numpy array with the organized sequences. 69 | 70 | Returns 71 | ------- 72 | X, Y: np.array 73 | Numpy arrays with the shapes required by 74 | the LSTM layer. X with (1, a - 1, b) 75 | and Y with (1, b). Where a is the total 76 | number of groups in `group` and b the 77 | number of observations per group. 78 | """ 79 | X = groups[0:,:-1].reshape(1, groups.shape[1] - 1, groups.shape[2]) 80 | Y = groups[0:,-1:][0] 81 | 82 | return X, Y 83 | 84 | def normalize(self): 85 | """ 86 | Normalizes a series using point-relative normalization. 87 | 88 | Parameters 89 | ---------- 90 | 91 | Returns 92 | ------- 93 | """ 94 | normalizations.point_relative_normalization() 95 | 96 | def denormalize(self, series, last_value): 97 | """ 98 | De-normalizes a series using the latest 99 | value available from data. 100 | 101 | Parameters 102 | ---------- 103 | series: numpy array 104 | Series with normalized values. 105 | 106 | last_value: float 107 | Numerical value that represents the 108 | last value from the dataset. 109 | 110 | Returns 111 | ------- 112 | """ 113 | result = last_value * (series + 1) 114 | return result 115 | 116 | def mape(self, A, B): 117 | """ 118 | Calcualtes the mean absolute persentage error 119 | from two series. Original solution from: 120 | 121 | https://stats.stackexchange.com/questions/58391/\ 122 | mean-absolute-percentage-error-mape-in-scikit-learn 123 | """ 124 | return np.mean(np.abs((A - B) / (1 - A))) * 100 125 | 126 | def rmse(self, A, B): 127 | """ 128 | Calculates the root mean square error from 129 | two series. Original solution from: 130 | 131 | https://stackoverflow.com/questions/16774849\ 132 | /mean-squared-error-in-numpy 133 | """ 134 | return np.sqrt(np.square(np.subtract(A, B)).mean()) 135 | 136 | def mse(self, A, B): 137 | """ 138 | Calculates the mean square error from 139 | two series. Original solution from: 140 | 141 | https://stackoverflow.com/questions/16774849\ 142 | /mean-squared-error-in-numpy 143 | """ 144 | return np.square(np.subtract(A, B)).mean() 145 | -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic_old/models/normalizations.py: -------------------------------------------------------------------------------- 1 | """ 2 | Series of normalization functions useful 3 | for normalizing time-series data. 4 | """ 5 | 6 | def z_score(series): 7 | """ 8 | Computes the normalized value using the Z-score 9 | technique. The Z-score is a technique used for 10 | normalizing Gaussian distributions representing 11 | each observation in relation to the distribution's 12 | mean and standard deviation. For precise definitions, 13 | see the Wikipedia article: 14 | 15 | https://en.wikipedia.org/wiki/Standard_score 16 | 17 | Parameters 18 | ---------- 19 | serie: list 20 | List with sequential values to use. 21 | 22 | Returns 23 | ------- 24 | result: list 25 | List with the normalized results. 26 | """ 27 | result = (series - series.mean()) / series.std(ddof=0) 28 | return result 29 | 30 | def point_relative_normalization(series, reverse=False, last_value=None): 31 | """ 32 | Computes the normalized value for the values of a 33 | given series by using the first element of the serie as p_0 34 | as a reference for each p_i. 35 | 36 | This technique comes from Siraj Raval's YouTube video 37 | "How to Predict Stock Prices Easily - Intro to Deep Learning #7", 38 | available at: 39 | 40 | https://www.youtube.com/watch?v=ftMq5ps503w 41 | 42 | Parameters 43 | ---------- 44 | serie: list 45 | List with sequential values to use. 46 | 47 | reverse: bool, default True 48 | If the method should de-normalize data. 49 | 50 | last_value: int or float 51 | Used to de-normalize a dataset. Needs to 52 | be passed if `reverse` is True. 53 | 54 | Returns 55 | ------- 56 | result: list 57 | List with the normalized results. 58 | """ 59 | if reverse: 60 | result = last_value * (series + 1) 61 | else: 62 | result = (series / series[0]) - 1 63 | 64 | return result 65 | 66 | def maximum_and_minimum_normalization(series, boundary=(0, 1)): 67 | """ 68 | Computes the normalized value for the values of a 69 | given serie by using that series maximum and minimum 70 | values. 71 | 72 | This technique is a direct implementation from 73 | scikit-learn, available at: 74 | 75 | http://scikit-learn.org/stable/modules/generated/\ 76 | sklearn.preprocessing.MinMaxScaler.html 77 | 78 | Parameters 79 | ---------- 80 | serie: list 81 | List with sequential values to use. 82 | 83 | boundary: set 84 | Maximum and minimum values used to 85 | scale the series. 86 | 87 | Returns 88 | ------- 89 | result: list 90 | List with the normalized results. 91 | """ 92 | range_min, range_max = boundary 93 | standard_deviation = (series - series.min(axis=0)) / (series.max(axis=0) - series.min(axis=0)) 94 | result = standard_deviation * (range_max - range_min) + range_min 95 | return result 96 | -------------------------------------------------------------------------------- /Chapter04/Activity4.01/cryptonic_old/server.py: -------------------------------------------------------------------------------- 1 | """ 2 | Functions to start and configure the Flask 3 | application. This simply configures the server 4 | and its routes. 5 | """ 6 | import os 7 | import flask 8 | 9 | from flask_caching import Cache 10 | from flask_cors import CORS, cross_origin 11 | 12 | from cryptonic import Model 13 | #from cryptonic import CoinMarketCap 14 | from cryptonic.api.routes import create_routes 15 | import yfinance as yf 16 | 17 | UI_DIST_DIRECTORY = os.getenv('UI_DIST_DIRECTORY', '../cryptonic-ui/dist/') 18 | 19 | 20 | class Server: 21 | """ 22 | Cryptonic server representation. This class 23 | contains logic for managing the configuration 24 | and deployment of Flask server. 25 | 26 | Parameters 27 | ---------- 28 | debug: bool, default False 29 | If should start with a debugger. 30 | 31 | cors: bool, default True 32 | If the application should accept CORS 33 | requests. 34 | 35 | """ 36 | def __init__(self, debug=False, cors=True): 37 | self.debug = debug 38 | self.cors = cors 39 | 40 | self.create_model() 41 | self.app = self.create() 42 | 43 | def create_model(self): 44 | """ 45 | Creates a model either using a model provided 46 | by user or by creating a new model using 47 | previously researched parameters. 48 | 49 | Returns 50 | ------- 51 | Trained Keras model. Ready to be used 52 | via the model.predict() method. 53 | """ 54 | ticker = yf.Ticker("BTC-USD") 55 | historic_data = ticker.history(period='max') 56 | 57 | model_path = os.getenv('MODEL_NAME') 58 | 59 | # 60 | # TODO: Figure out how large the data is for 61 | # the old model and re-train. Maybe what I have 62 | # to do here is to copy the weights of the 63 | # model into a new model. 64 | # 65 | 66 | self.model = Model(data=historic_data, 67 | path=model_path, 68 | variable='close', 69 | predicted_period_size=int(os.getenv('PERIOD_SIZE', 7))) 70 | 71 | if not model_path: 72 | self.model.build() 73 | self.model.train(epochs=int(os.getenv('EPOCHS', 300)), verbose=1) 74 | 75 | return self.model 76 | 77 | def create(self): 78 | """ 79 | Method for creating a Flask server. 80 | 81 | Returns 82 | ------- 83 | A Flask() application object. 84 | """ 85 | app = flask.Flask(__name__, static_url_path='/', static_folder=UI_DIST_DIRECTORY) 86 | 87 | # 88 | # Application configuration. Here we 89 | # configure the application to accept 90 | # CORS requests, its routes, and 91 | # its debug flag. 92 | # 93 | if self.cors: 94 | CORS(app) 95 | 96 | cache_configuration = { 97 | 'CACHE_TYPE': 'redis', 98 | 'CACHE_REDIS_URL': os.getenv('REDIS_URL', 99 | 'redis://redis@cache:6379/0') 100 | } 101 | 102 | self.cache = Cache(app, config=cache_configuration) 103 | 104 | app.config['DEBUG'] = self.debug 105 | create_routes(app, self.cache, self.model) 106 | 107 | return app 108 | 109 | def run(self, *args, **kwargs): 110 | """ 111 | Method for running Flask server. 112 | Parameters 113 | ---------- 114 | *args, **kwargs: parameters passed to the Flask application. 115 | """ 116 | self.app.run(*args, **kwargs) -------------------------------------------------------------------------------- /Chapter04/Activity4.01/docker-compose.yml: -------------------------------------------------------------------------------- 1 | # 2 | # CRYPTONIC DOCKER-COMPOSE 3 | # ------------------------ 4 | # 5 | # Here we configure the application stack 6 | # that is deployed with Cryptonic. This 7 | # includes three elements: 8 | # 9 | # i. Cryptonic App: a Python web-application 10 | # that exposes the Cryptonic API via 11 | # the HTTP protocol. 12 | # 13 | # ii. Cryptonic Cache: a Redis instance that 14 | # works as a caching layer. 15 | # 16 | version: "3" 17 | 18 | services: 19 | 20 | cache: 21 | image: cryptonic-cache:latest 22 | build: 23 | context: ./cryptonic-cache 24 | dockerfile: ./Dockerfile 25 | volumes: 26 | - $PWD/cache_data:/data 27 | networks: 28 | - cryptonic 29 | 30 | cryptonic: 31 | image: cryptonic:latest 32 | build: 33 | context: . 34 | dockerfile: ./Dockerfile 35 | ports: 36 | - "5000:5000" 37 | environment: 38 | - BITCOIN_START_DATE=2019-01-01 39 | - EPOCHS=50 40 | - PERIOD_SIZE=7 41 | volumes: 42 | - ./cryptonic_logs:/logs 43 | - ./models:/models 44 | env_file: 45 | - cryptonic.env 46 | depends_on: 47 | - cache 48 | networks: 49 | - cryptonic 50 | 51 | networks: 52 | cryptonic: 53 | -------------------------------------------------------------------------------- /Chapter04/Activity4.01/requirements.txt: -------------------------------------------------------------------------------- 1 | beautifulsoup4==4.6.0 2 | bleach==1.5.0 3 | certifi==2017.11.5 4 | chardet==3.0.4 5 | click==6.7 6 | colorama==0.3.9 7 | coverage==4.4.2 8 | enum34==1.1.6 9 | Flask==0.12.2 10 | Flask-API==1.0 11 | Flask-Caching==1.3.3 12 | Flask-Cors==3.0.3 13 | Flask-Testing==0.7.1 14 | h5py==2.7.1 15 | html5lib==0.9999999 16 | idna==2.6 17 | itsdangerous==0.24 18 | Jinja2==2.10 19 | Keras==2.1.2 20 | lxml==4.1.1 21 | Markdown==2.6.10 22 | MarkupSafe==1.0 23 | nose==1.3.7 24 | numpy 25 | pandas==0.25.3 26 | protobuf 27 | python-dateutil==2.6.1 28 | pytz==2017.3 29 | PyYAML==3.12 30 | redis==2.10.6 31 | rednose==1.2.3 32 | requests 33 | scipy==1.0.0 34 | six==1.11.0 35 | tensorboard==2.0.1 36 | tensorflow==2.0.0 37 | tensorflow-estimator==2.0.1 38 | termstyle==0.1.11 39 | urllib3==1.22 40 | yfinance 41 | Werkzeug==0.13 42 | -------------------------------------------------------------------------------- /Chapter04/Activity4.01/run.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | """ 3 | Script for starting the Cryptonic Flask server. 4 | """ 5 | import os 6 | import time 7 | 8 | from cryptonic.server import Server 9 | 10 | 11 | def main(): 12 | """ 13 | Wrapper function for starting a server. 14 | """ 15 | 16 | print('Starting server.') 17 | server = Server() 18 | 19 | server.run(host=os.getenv("HOST", "0.0.0.0")) 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /Chapter04/Activity4.01/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter04/Activity4.01/screenshot.png -------------------------------------------------------------------------------- /Chapter04/Exercise4.01/bitcoin_lstm_v0.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter04/Exercise4.01/bitcoin_lstm_v0.h5 -------------------------------------------------------------------------------- /Chapter04/Exercise4.01/bitcoin_model_prod_v0.h5: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter04/Exercise4.01/bitcoin_model_prod_v0.h5 -------------------------------------------------------------------------------- /Chapter04/Exercise4.01/cryptonic/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Cryptonic is an educational application created for 3 | the purposes of learning how to use deep learning 4 | to predict Bitcoin prices. 5 | """ 6 | from cryptonic.models.model import Model 7 | from cryptonic.markets.coinmarketcap import CoinMarketCap 8 | 9 | __version__ = 'v1.0.1' 10 | __author__ = 'Luis Capelo' 11 | __email__ = 'luiscape@gmail.com' 12 | 13 | __all__ = ['Model', 'CoinMarketCap'] 14 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.01/cryptonic/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter04/Exercise4.01/cryptonic/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /Chapter04/Exercise4.01/cryptonic/api/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | HTTP API interface for Cryptonic. 3 | """ 4 | 5 | __version__ = 1 6 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.01/cryptonic/api/routes.py: -------------------------------------------------------------------------------- 1 | """ 2 | Creates all application routes. 3 | """ 4 | import os 5 | 6 | from datetime import datetime, timedelta 7 | from flask_api import status as http_status 8 | from flask import jsonify, request, make_response, send_file, send_from_directory 9 | 10 | from cryptonic.api import __version__ 11 | 12 | 13 | def __cache_identifier(*args, **kwargs): 14 | """ 15 | Creates a cache identifier based on the complete 16 | URL provided in the request. Original solution: 17 | 18 | * https://stackoverflow.com/questions/9413566/\ 19 | flask-cache-memoize-url-query-string-parameters-as-well 20 | 21 | """ 22 | # 23 | # The following snippet will create a 24 | # cache key for the URL and all GET and POST 25 | # parameters. 26 | # 27 | key = request.url + \ 28 | '='.join([k + '=' + v for k,v in request.args.items()]) + '&' + \ 29 | '='.join([k + '=' + v for k,v in request.form.items()]) 30 | 31 | return key 32 | 33 | 34 | def create_routes(app, cache, model): 35 | """ 36 | Function that creates the application routes. 37 | 38 | Parameters 39 | ---------- 40 | app: Flask object 41 | Initialized Flask object. 42 | 43 | cache: Flask-Caching instance 44 | Cache instance that can be used 45 | as a decorator. 46 | 47 | model: instantiated Model() 48 | Module() class instantiated as a model 49 | and trained. 50 | 51 | Returns 52 | ------- 53 | app: Flask object 54 | Modified Flask app object. 55 | """ 56 | # 57 | # Let's clear the cache at every 58 | # startup. 59 | # 60 | cache.clear() 61 | 62 | @cache.cached(timeout=60 * 5, key_prefix=__cache_identifier) 63 | @app.errorhandler(404) 64 | def page_not_found(e): 65 | return app.send_static_file('index.html'), 404 66 | 67 | @cache.cached(timeout=60 * 5, key_prefix=__cache_identifier) 68 | @app.route('/') 69 | def root(): 70 | """ 71 | Endpoint for serving the index page. 72 | """ 73 | return app.send_static_file('index.html') 74 | 75 | @app.route('/') 76 | def send_static_files(path): 77 | return send_from_directory( 78 | os.getenv('UI_DIST_DIRECTORY', '../cryptonic-ui/dist/'), path) 79 | 80 | @app.route('/status') 81 | def status(): 82 | """ 83 | Endpoint for returning the application status. 84 | """ 85 | r = { 86 | 'version': __version__, 87 | 'success': True, 88 | 'message': 'Predict Bitcoin prices with deep learning.', 89 | 'model': { 90 | 'name': os.getenv('MODEL_NAME'), 91 | 'last_trained': model.last_trained, 92 | 'error_rates': model.evaluate() 93 | } 94 | } 95 | return jsonify(r) 96 | 97 | @cache.cached(timeout=60 * 24, key_prefix=__cache_identifier) 98 | @app.route('/historic') 99 | def historic(): 100 | """ 101 | Returns a series of historic observations. 102 | 103 | Parameters 104 | ---------- 105 | start: str, default six months ago 106 | Start date to filter the Bitcoin historic price 107 | data set. 108 | 109 | """ 110 | six_months_ago = ( 111 | datetime.now() - timedelta(days=30 * 6)).strftime('%Y-%m-%d') 112 | start = request.args.get('start', six_months_ago) 113 | 114 | historic_data = model.data.to_dict(orient='records') 115 | filtered_historic_data = list(filter(lambda x: x['date'] > six_months_ago, historic_data)) 116 | 117 | r = { 118 | 'version': __version__, 119 | 'success': True, 120 | 'message': 'Historic Bitcoin prices from CoinMarketCap.', 121 | 'start_date': os.getenv('BITCOIN_START_DATE'), 122 | 'result': filtered_historic_data 123 | } 124 | 125 | return jsonify(r) 126 | 127 | @cache.cached(timeout=60 * 24, key_prefix=__cache_identifier) 128 | @app.route('/predict') 129 | def predict(): 130 | """ 131 | Endpoint for predicting bitcoin prices. 132 | """ 133 | r = { 134 | 'version': __version__, 135 | 'success': True, 136 | 'message': 'Endpoint for making predictions.', 137 | 'period_length': os.getenv('PERIOD_SIZE', 7), 138 | 'result': model.predict(denormalized=True, return_dict=True) 139 | } 140 | return jsonify(r) 141 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.01/cryptonic/markets/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Interface for manipulating data from different markets. 3 | """ 4 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.01/cryptonic/markets/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter04/Exercise4.01/cryptonic/markets/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /Chapter04/Exercise4.01/cryptonic/markets/__pycache__/coinmarketcap.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter04/Exercise4.01/cryptonic/markets/__pycache__/coinmarketcap.cpython-36.pyc -------------------------------------------------------------------------------- /Chapter04/Exercise4.01/cryptonic/markets/coinmarketcap.py: -------------------------------------------------------------------------------- 1 | """ 2 | Logic for collecting data directly from the 3 | CoinMarketCap API. 4 | """ 5 | import requests 6 | import pandas as pd 7 | 8 | from datetime import datetime 9 | from bs4 import BeautifulSoup 10 | 11 | 12 | class CoinMarketCap: 13 | """ 14 | Class interface to data from CoinMarketCap. 15 | Original data can be found at: 16 | 17 | https://coinmarketcap.com/ 18 | 19 | """ 20 | def __repr__(self): 21 | message = """ 22 | 23 | Crypto-currency data comes from the website CoinMarketCap. 24 | CoinMarketCap is can be accessed at: https://coinmarketcap.com/ 25 | 26 | The permission to use the data is available on their FAQ 27 | 28 | https://coinmarketcap.com/faq/ 29 | 30 | and reads: 31 | 32 | "Q: Am I allowed to use content (screenshots, data, graphs, etc.) 33 | for one of my personal projects and/or commercial use? 34 | 35 | R: Absolutely! Feel free to use any content as you see fit. 36 | We kindly ask that you cite us as a source." 37 | 38 | """ 39 | return message 40 | 41 | @classmethod 42 | def historic(cls, start='2013-04-28', stop=None, ticker='bitcoin', return_json=False): 43 | """ 44 | Retrieves historic data within a time 45 | period. 46 | 47 | Parameters 48 | ---------- 49 | start, stop: str 50 | Start and stop dates in ISO format (YYYY-MM-DD). 51 | 52 | ticker: str 53 | Name of ticker to be used (e.g. `bitcoin`). 54 | 55 | Returns 56 | ------- 57 | Pandas dataframe with historical ticker data. 58 | """ 59 | start = start.replace('-', '') 60 | if not stop: 61 | stop = datetime.now().strftime('%Y%m%d') 62 | 63 | url = 'https://coinmarketcap.com/currencies/{}/historical-data/?start={}&end={}'.format(ticker, start, stop) 64 | r = requests.get(url) 65 | 66 | soup = BeautifulSoup(r.content, 'lxml') 67 | table = soup.find_all('table')[0] 68 | df = pd.read_html(str(table))[0] 69 | 70 | # 71 | # Cleans variables from the original. 72 | # 73 | df['Date'] = df['Date'].apply(lambda x: datetime.strptime(x, '%b %d, %Y').strftime('%Y-%m-%d')) 74 | df['Volume'] = df['Volume'].apply(lambda x: None if x == '-' else x) 75 | df.columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'market_cap'] 76 | 77 | if return_json: 78 | df.to_json(orient='records') 79 | 80 | return df 81 | 82 | @classmethod 83 | def current(cls, ticker='bitcoin'): 84 | """ 85 | Fetches current prices from CoinMarketCap. 86 | 87 | Returns 88 | ------- 89 | Dictionary with a single record form the 90 | """ 91 | url = 'https://api.coinmarketcap.com/v1/ticker/{}/'.format(ticker) 92 | r = requests.get(url) 93 | 94 | return r.json() 95 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.01/cryptonic/models/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Cryptocurrency prediction models. 3 | """ 4 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.01/cryptonic/models/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter04/Exercise4.01/cryptonic/models/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /Chapter04/Exercise4.01/cryptonic/models/__pycache__/helper.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter04/Exercise4.01/cryptonic/models/__pycache__/helper.cpython-36.pyc -------------------------------------------------------------------------------- /Chapter04/Exercise4.01/cryptonic/models/__pycache__/model.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter04/Exercise4.01/cryptonic/models/__pycache__/model.cpython-36.pyc -------------------------------------------------------------------------------- /Chapter04/Exercise4.01/cryptonic/models/__pycache__/normalizations.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter04/Exercise4.01/cryptonic/models/__pycache__/normalizations.cpython-36.pyc -------------------------------------------------------------------------------- /Chapter04/Exercise4.01/cryptonic/models/helper.py: -------------------------------------------------------------------------------- 1 | """ 2 | Helper class and methods for making manipulating 3 | data for models. 4 | """ 5 | import numpy as np 6 | 7 | import cryptonic.models.normalizations as normalizations 8 | 9 | 10 | class ModelHelper: 11 | """ 12 | Class with utility functions that aid in 13 | the process of training LSTM models with Keras. 14 | 15 | """ 16 | def __init__(self): 17 | pass 18 | 19 | def create_groups(self, data, start=0, group_size=7, normalize=True): 20 | """ 21 | Creates distinct groups from a given continuous series. 22 | 23 | Parameters 24 | ---------- 25 | data: np.array 26 | Series of continious observations. 27 | 28 | start: int 29 | Starting point for the series. This 30 | is used to prune earlier observations 31 | from the series in case the series is 32 | too long or too short. 33 | 34 | group_size: int, default 7 35 | Determines how large the groups are. That is, 36 | how many observations each group contains. 37 | 38 | normalize: bool 39 | If the method should normalize data or not. 40 | Normalization is done using 41 | 42 | normalizations.point_relative_normalization() 43 | 44 | Returns 45 | ------- 46 | A Numpy array object. 47 | """ 48 | samples = list() 49 | for i in range(0, len(data), group_size): 50 | sample = list(data[start + i:i + group_size]) 51 | if len(sample) == group_size: 52 | if normalize: 53 | sample = normalizations.point_relative_normalization(sample) 54 | 55 | samples.append(np.array(sample).reshape(1, group_size).tolist()) 56 | 57 | A = np.array(samples) 58 | return A.reshape(1, A.shape[0], group_size) 59 | 60 | def split_lstm_input(self, groups): 61 | """ 62 | Splits groups in a format expected by 63 | the LSTM layer. 64 | 65 | Parameters 66 | ---------- 67 | groups: np.array 68 | Numpy array with the organized sequences. 69 | 70 | Returns 71 | ------- 72 | X, Y: np.array 73 | Numpy arrays with the shapes required by 74 | the LSTM layer. X with (1, a - 1, b) 75 | and Y with (1, b). Where a is the total 76 | number of groups in `group` and b the 77 | number of observations per group. 78 | """ 79 | X = groups[0:,:-1].reshape(1, groups.shape[1] - 1, groups.shape[2]) 80 | Y = groups[0:,-1:][0] 81 | 82 | return X, Y 83 | 84 | def normalize(self): 85 | """ 86 | Normalizes a series using point-relative normalization. 87 | 88 | Parameters 89 | ---------- 90 | 91 | Returns 92 | ------- 93 | """ 94 | normalizations.point_relative_normalization() 95 | 96 | def denormalize(self, series, last_value): 97 | """ 98 | De-normalizes a series using the latest 99 | value available from data. 100 | 101 | Parameters 102 | ---------- 103 | series: numpy array 104 | Series with normalized values. 105 | 106 | last_value: float 107 | Numerical value that represents the 108 | last value from the dataset. 109 | 110 | Returns 111 | ------- 112 | """ 113 | result = last_value * (series + 1) 114 | return result 115 | 116 | def mape(self, A, B): 117 | """ 118 | Calcualtes the mean absolute persentage error 119 | from two series. Original solution from: 120 | 121 | https://stats.stackexchange.com/questions/58391/\ 122 | mean-absolute-percentage-error-mape-in-scikit-learn 123 | """ 124 | return np.mean(np.abs((A - B) / (1 - A))) * 100 125 | 126 | def rmse(self, A, B): 127 | """ 128 | Calculates the root mean square error from 129 | two series. Original solution from: 130 | 131 | https://stackoverflow.com/questions/16774849\ 132 | /mean-squared-error-in-numpy 133 | """ 134 | return np.sqrt(np.square(np.subtract(A, B)).mean()) 135 | 136 | def mse(self, A, B): 137 | """ 138 | Calculates the mean square error from 139 | two series. Original solution from: 140 | 141 | https://stackoverflow.com/questions/16774849\ 142 | /mean-squared-error-in-numpy 143 | """ 144 | return np.square(np.subtract(A, B)).mean() 145 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.01/cryptonic/models/normalizations.py: -------------------------------------------------------------------------------- 1 | """ 2 | Series of normalization functions useful 3 | for normalizing time-series data. 4 | """ 5 | 6 | def z_score(series): 7 | """ 8 | Computes the normalized value using the Z-score 9 | technique. The Z-score is a technique used for 10 | normalizing Gaussian distributions representing 11 | each observation in relation to the distribution's 12 | mean and standard deviation. For precise definitions, 13 | see the Wikipedia article: 14 | 15 | https://en.wikipedia.org/wiki/Standard_score 16 | 17 | Parameters 18 | ---------- 19 | serie: list 20 | List with sequential values to use. 21 | 22 | Returns 23 | ------- 24 | result: list 25 | List with the normalized results. 26 | """ 27 | result = (series - series.mean()) / series.std(ddof=0) 28 | return result 29 | 30 | def point_relative_normalization(series, reverse=False, last_value=None): 31 | """ 32 | Computes the normalized value for the values of a 33 | given series by using the first element of the serie as p_0 34 | as a reference for each p_i. 35 | 36 | This technique comes from Siraj Raval's YouTube video 37 | "How to Predict Stock Prices Easily - Intro to Deep Learning #7", 38 | available at: 39 | 40 | https://www.youtube.com/watch?v=ftMq5ps503w 41 | 42 | Parameters 43 | ---------- 44 | serie: list 45 | List with sequential values to use. 46 | 47 | reverse: bool, default True 48 | If the method should de-normalize data. 49 | 50 | last_value: int or float 51 | Used to de-normalize a dataset. Needs to 52 | be passed if `reverse` is True. 53 | 54 | Returns 55 | ------- 56 | result: list 57 | List with the normalized results. 58 | """ 59 | if reverse: 60 | result = last_value * (series + 1) 61 | else: 62 | result = (series / series[0]) - 1 63 | 64 | return result 65 | 66 | def maximum_and_minimum_normalization(series, boundary=(0, 1)): 67 | """ 68 | Computes the normalized value for the values of a 69 | given serie by using that series maximum and minimum 70 | values. 71 | 72 | This technique is a direct implementation from 73 | scikit-learn, available at: 74 | 75 | http://scikit-learn.org/stable/modules/generated/\ 76 | sklearn.preprocessing.MinMaxScaler.html 77 | 78 | Parameters 79 | ---------- 80 | serie: list 81 | List with sequential values to use. 82 | 83 | boundary: set 84 | Maximum and minimum values used to 85 | scale the series. 86 | 87 | Returns 88 | ------- 89 | result: list 90 | List with the normalized results. 91 | """ 92 | range_min, range_max = boundary 93 | standard_deviation = (series - series.min(axis=0)) / (series.max(axis=0) - series.min(axis=0)) 94 | result = standard_deviation * (range_max - range_min) + range_min 95 | return result 96 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.01/cryptonic/server.py: -------------------------------------------------------------------------------- 1 | """ 2 | Functions to start and configure the Flask 3 | application. This simply configures the server 4 | and its routes. 5 | """ 6 | import os 7 | import flask 8 | 9 | from flask_caching import Cache 10 | from flask_cors import CORS, cross_origin 11 | 12 | from cryptonic import Model 13 | from cryptonic import CoinMarketCap 14 | from cryptonic.api.routes import create_routes 15 | 16 | UI_DIST_DIRECTORY = os.getenv('UI_DIST_DIRECTORY', '../cryptonic-ui/dist/') 17 | 18 | 19 | class Server: 20 | """ 21 | Cryptonic server representation. This class 22 | contains logic for managing the configuration 23 | and deployment of Flask server. 24 | 25 | Parameters 26 | ---------- 27 | debug: bool, default False 28 | If should start with a debugger. 29 | 30 | cors: bool, default True 31 | If the application should accept CORS 32 | requests. 33 | 34 | """ 35 | def __init__(self, debug=False, cors=True): 36 | self.debug = debug 37 | self.cors = cors 38 | 39 | self.create_model() 40 | self.app = self.create() 41 | 42 | def create_model(self): 43 | """ 44 | Creates a model either using a model provided 45 | by user or by creating a new model using 46 | previously researched parameters. 47 | 48 | Returns 49 | ------- 50 | Trained Keras model. Ready to be used 51 | via the model.predict() method. 52 | """ 53 | historic_data = CoinMarketCap.historic(start=os.getenv('BITCOIN_START_DATE', '2017-01-01')) 54 | model_path = os.getenv('MODEL_NAME') 55 | 56 | # 57 | # TODO: Figure out how large the data is for 58 | # the old model and re-train. Maybe what I have 59 | # to do here is to copy the weights of the 60 | # model into a new model. 61 | # 62 | 63 | self.model = Model(data=historic_data, 64 | path=model_path, 65 | variable='close', 66 | predicted_period_size=int(os.getenv('PERIOD_SIZE', 7))) 67 | 68 | if not model_path: 69 | self.model.build() 70 | self.model.train(epochs=int(os.getenv('EPOCHS', 300)), verbose=1) 71 | 72 | return self.model 73 | 74 | def create(self): 75 | """ 76 | Method for creating a Flask server. 77 | 78 | Returns 79 | ------- 80 | A Flask() application object. 81 | """ 82 | app = flask.Flask(__name__, static_url_path='/', static_folder=UI_DIST_DIRECTORY) 83 | 84 | # 85 | # Application configuration. Here we 86 | # configure the application to accept 87 | # CORS requests, its routes, and 88 | # its debug flag. 89 | # 90 | if self.cors: 91 | CORS(app) 92 | 93 | cache_configuration = { 94 | 'CACHE_TYPE': 'redis', 95 | 'CACHE_REDIS_URL': os.getenv('REDIS_URL', 96 | 'redis://redis@cache:6379/0') 97 | } 98 | 99 | self.cache = Cache(app, config=cache_configuration) 100 | 101 | app.config['DEBUG'] = self.debug 102 | create_routes(app, self.cache, self.model) 103 | 104 | return app 105 | 106 | def run(self, *args, **kwargs): 107 | """ 108 | Method for running Flask server. 109 | Parameters 110 | ---------- 111 | *args, **kwargs: parameters passed to the Flask application. 112 | """ 113 | self.app.run(*args, **kwargs) -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/.dockerignore: -------------------------------------------------------------------------------- 1 | # MacOS Files 2 | .DS_Store 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Node modules 13 | node_modules/ 14 | 15 | # Distribution / packaging 16 | .Python 17 | env/ 18 | build/ 19 | develop-eggs/ 20 | dist/ 21 | downloads/ 22 | eggs/ 23 | .eggs/ 24 | lib/ 25 | lib64/ 26 | parts/ 27 | sdist/ 28 | var/ 29 | *.egg-info/ 30 | .installed.cfg 31 | *.egg 32 | 33 | # PyInstaller 34 | # Usually these files are written by a python script from a template 35 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 36 | *.manifest 37 | *.spec 38 | 39 | # Installer logs 40 | pip-log.txt 41 | pip-delete-this-directory.txt 42 | 43 | # Unit test / coverage reports 44 | htmlcov/ 45 | .tox/ 46 | .coverage 47 | .coverage.* 48 | .cache 49 | nosetests.xml 50 | coverage.xml 51 | *,cover 52 | .hypothesis/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | 62 | # Flask stuff: 63 | instance/ 64 | .webassets-cache 65 | 66 | # Scrapy stuff: 67 | .scrapy 68 | 69 | # Sphinx documentation 70 | docs/_build/ 71 | 72 | # PyBuilder 73 | target/ 74 | 75 | # IPython Notebook 76 | .ipynb_checkpoints 77 | 78 | # pyenv 79 | .python-version 80 | 81 | # celery beat schedule file 82 | celerybeat-schedule 83 | 84 | # dotenv 85 | .env 86 | 87 | # virtualenv 88 | venv/ 89 | ENV/ 90 | 91 | # Spyder project settings 92 | .spyderproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # Visual Studio Code configuration files 98 | .vscode/* 99 | 100 | # Testing artifacts 101 | .noseids 102 | coverage/ 103 | 104 | # Ignore other applications 105 | cryptonic-cache/ 106 | 107 | # Ignoring cache and database data 108 | data/ 109 | logs/ 110 | 111 | # Ignore tests folder 112 | tests/ 113 | 114 | # Packaged files 115 | *.tar.gz 116 | 117 | # Deploy files 118 | deploy.sh -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | env/ 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | *.egg-info/ 25 | .installed.cfg 26 | *.egg 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | 49 | # Translations 50 | *.mo 51 | *.pot 52 | 53 | # Django stuff: 54 | *.log 55 | local_settings.py 56 | 57 | # Flask stuff: 58 | instance/ 59 | .webassets-cache 60 | 61 | # Scrapy stuff: 62 | .scrapy 63 | 64 | # Sphinx documentation 65 | docs/_build/ 66 | 67 | # PyBuilder 68 | target/ 69 | 70 | # Jupyter Notebook 71 | .ipynb_checkpoints 72 | 73 | # pyenv 74 | .python-version 75 | 76 | # celery beat schedule file 77 | celerybeat-schedule 78 | 79 | # SageMath parsed files 80 | *.sage.py 81 | 82 | # dotenv 83 | .env 84 | 85 | # virtualenv 86 | .venv 87 | venv/ 88 | ENV/ 89 | 90 | # Spyder project settings 91 | .spyderproject 92 | .spyproject 93 | 94 | # Rope project settings 95 | .ropeproject 96 | 97 | # mkdocs documentation 98 | /site 99 | 100 | # mypy 101 | .mypy_cache/ 102 | 103 | # Deploy files 104 | deploy.sh 105 | 106 | # Node modules 107 | node_modules/ 108 | 109 | # Visual Studio config files 110 | .vscode/ 111 | *.h5 112 | 113 | # Cache data 114 | cache_data/ 115 | 116 | # Compressed images 117 | *.tar.gz -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # Docker image for the Cryptonic application. 3 | # The image copies the complete application 4 | # directory and starts a Flask server. 5 | # 6 | FROM python:3.6 7 | ENV TZ=America/New_York 8 | 9 | # 10 | # Setting up timezone to EST (New York). 11 | # Change this to whichever timezone your 12 | # data is configured to use. 13 | # 14 | RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone 15 | 16 | 17 | COPY . /cryptonic 18 | 19 | WORKDIR "/cryptonic" 20 | RUN pip install -r requirements.txt 21 | 22 | EXPOSE 5000 23 | 24 | CMD ["python", "run.py"] -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/Makefile: -------------------------------------------------------------------------------- 1 | # ------------------------------------------------- # 2 | # # 3 | # MAKEFILE # 4 | # -------- # 5 | # # 6 | # Makefile commands for Cryptonic: # 7 | # # 8 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 9 | # # 10 | # test: run tests via nosetest. # 11 | # # 12 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 13 | # # 14 | # build: builds all Docker images for # 15 | # Cryptonic. # 16 | # # 17 | # build-app-image: build Crytonic's # 18 | # API Docker image. # 19 | # # 20 | # build-cache-image: build Crytonic's # 21 | # cache Docker image. # 22 | # # 23 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 24 | # # 25 | # deploy: deploy combination of Docker # 26 | # containers locally. # 27 | # # 28 | # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ # 29 | # # 30 | # package: packages all components into # 31 | # a few Docker images ready for deployment. # 32 | # # 33 | # package-ui: packages the Cryptonic UI. # 34 | # # 35 | # ------------------------------------------------- # 36 | all: test package 37 | 38 | test: 39 | bash bin/test.sh; 40 | 41 | # 42 | # Build Docker images. 43 | # 44 | .PHONY: build 45 | build: build-cache-image build-app-image 46 | 47 | build-app-image: 48 | bash bin/build_docker_image.sh "latest"; 49 | 50 | build-cache-image: 51 | bash cryptonic-cache/bin/build_docker_image.sh "latest"; 52 | 53 | # 54 | # Packages all components of the 55 | # application for deployment. 56 | # 57 | package: package-ui build 58 | @echo "Packaging Docker images."; 59 | docker save -o cryptonic-latest.tar cryptonic:latest; 60 | docker save -o cryptonic-cache-latest.tar cryptonic-cache:latest; 61 | 62 | package-ui: 63 | cd cryptonic-ui && npm run build; 64 | 65 | # 66 | # Deploy Docker containers. 67 | # 68 | deploy: 69 | docker-compose up -d; 70 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/README.md: -------------------------------------------------------------------------------- 1 | # Cryptonic 2 | Cryptonic is a Dockerized web-application that predicts Bitcoin prices using a deep learning model. The model used to predict prices can be easily changed--given that certain parameters are passed--making it easy to experiment and deploy a working model. 3 | 4 | ![screenshot](screenshot.png) 5 | 6 | The application was developed for educational purposes and is part of the book "Beginning Application Development with TensorFlow" by [Luis Capelo](https://luiscapelo.info/). 7 | 8 | ### Usage 9 | A demo of the application is available at 10 | 11 | * Demo: https://cryptonic.market/ 12 | 13 | ### API 14 | The application has the following endpoints: 15 | 16 | * `/status`: returns the status of the application and its error rates. 17 | * `/historic`: returns available Bitcoin prices up to date. 18 | * `/predict`: predicts the next N days of Bitcoin closing prices. 19 | 20 | ### Requirements 21 | You will need: 22 | 23 | * Docker `17.09.1` or higher. 24 | * If using the `Makefile` commands, you will also need `make`. 25 | 26 | ### Deployment 27 | Deploy this application using the available `Makefile` recipes. Navigate to the application's root directory, then execute: 28 | 29 | ```shell 30 | $ make deploy 31 | ``` 32 | 33 | Now visit http://localhost:5000 on your browser and the application should be available. -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/bin/build_docker_image.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Builds Docker image of the 4 | # `Cryptonic` program. 5 | # 6 | VERSION=$1 7 | docker build --tag cryptonic:$VERSION \ 8 | --tag cryptonic:latest \ 9 | . -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic-cache/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM redis:alpine 2 | ENV TZ=America/New_York 3 | # COPY redis.conf /usr/local/etc/redis/redis.conf 4 | # CMD [ "redis-server", "/usr/local/etc/redis/redis.conf" ] 5 | 6 | # 7 | # Setting up timezone to EST (New York). 8 | # Change this to whichever timezone your 9 | # data is configured to use. 10 | # 11 | RUN apk add -U tzdata \ 12 | && cp /usr/share/zoneinfo/$TZ /etc/localtime 13 | 14 | EXPOSE 6379 15 | 16 | CMD [ "redis-server" ] -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic-cache/bin/build_docker_image.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Builds Docker image for the 4 | # backend Redis cache used by Cryptonic. 5 | # 6 | VERSION=$1 7 | docker build --tag cryptonic-cache:$VERSION \ 8 | --tag cryptonic-cache:latest \ 9 | ./cryptonic-cache/ -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic-ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cryptonic", 3 | "description": "Cryptonic UI application.", 4 | "author": "luiscape@gmail.com", 5 | "private": true, 6 | "scripts": { 7 | "dev": "webpack-dev-server --inline --hot --env.dev", 8 | "build": "rimraf dist && webpack --progress --hide-modules" 9 | }, 10 | "dependencies": { 11 | "babel-preset-es2015": "^6.24.1", 12 | "c3": "^0.4.18", 13 | "d3": "^4.10.0", 14 | "dsv-loader": "^2.0.0", 15 | "element-ui": "^1.4.2", 16 | "lodash": "^4.17.4", 17 | "moment-timezone": "^0.5.14", 18 | "vue": "^2.4.2", 19 | "vue-c3": "^1.1.1", 20 | "vue-instant": "^1.0.1", 21 | "vue-lodash": "^1.0.4", 22 | "vue-moment": "^3.1.0", 23 | "vue-resource": "^1.3.4", 24 | "vuetrend": "^0.2.3" 25 | }, 26 | "engines": { 27 | "node": ">=6" 28 | }, 29 | "devDependencies": { 30 | "autoprefixer": "^6.6.0", 31 | "babel-core": "^6.24.1", 32 | "babel-loader": "^6.4.0", 33 | "babel-preset-vue-app": "^1.2.0", 34 | "css-loader": "^0.27.0", 35 | "file-loader": "^0.10.1", 36 | "html-webpack-plugin": "^2.24.1", 37 | "postcss-loader": "^1.3.3", 38 | "rimraf": "^2.5.4", 39 | "style-loader": "^0.13.2", 40 | "url-loader": "^0.5.8", 41 | "vue-loader": "^13.0.4", 42 | "vue-template-compiler": "^2.4.2", 43 | "webpack": "^2.4.1", 44 | "webpack-dev-server": "^2.4.2" 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic-ui/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | require('autoprefixer')() 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic-ui/src/assets/botstream.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter04/Exercise4.02/cryptonic-ui/src/assets/botstream.png -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic-ui/src/assets/botstream_slack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter04/Exercise4.02/cryptonic-ui/src/assets/botstream_slack.png -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic-ui/src/css/infinite.css: -------------------------------------------------------------------------------- 1 | /* Make the element pulse (grow large and small slowly) */ 2 | /* Usage 3 | .myElement { 4 | animation: pulsate 1s ease-out; 5 | animation-iteration-count: infinite; 6 | opacity: 1; 7 | } 8 | */ 9 | @-webkit-keyframes pulsate { 10 | 0% {-webkit-transform: scale(0.1, 0.1); opacity: 0.0;} 11 | 50% {opacity: 1.0;} 12 | 100% {-webkit-transform: scale(1.2, 1.2); opacity: 0.0;} 13 | } 14 | 15 | /* Make the element's opacity pulse*/ 16 | /* Usage 17 | .myElement { 18 | animation: opacityPulse 1s ease-out; 19 | animation-iteration-count: infinite; 20 | opacity: 0; 21 | } 22 | */ 23 | @-webkit-keyframes opacityPulse { 24 | 0% {opacity: 0.0;} 25 | 50% {opacity: 1.0;} 26 | 100% {opacity: 0.0;} 27 | } 28 | 29 | /* Make the element's background pulse. I call this alertPulse because it is red. You can call it something more generic. */ 30 | /* Usage 31 | .myElement { 32 | animation: alertPulse 1s ease-out; 33 | animation-iteration-count: infinite; 34 | opacity: 1; 35 | } 36 | */ 37 | @-webkit-keyframes alertPulse { 38 | 0% {background-color: #9A2727; opacity: 1;} 39 | 50% {opacity: red; opacity: 0.75; } 40 | 100% {opacity: #9A2727; opacity: 1;} 41 | } 42 | 43 | 44 | /* Make the element rotate infinitely. */ 45 | /* 46 | Usage 47 | .myElement { 48 | animation: rotating 3s linear infinite; 49 | } 50 | */ 51 | @keyframes rotating { 52 | from { 53 | -ms-transform: rotate(0deg); 54 | -moz-transform: rotate(0deg); 55 | -webkit-transform: rotate(0deg); 56 | -o-transform: rotate(0deg); 57 | transform: rotate(0deg); 58 | } 59 | to { 60 | -ms-transform: rotate(360deg); 61 | -moz-transform: rotate(360deg); 62 | -webkit-transform: rotate(360deg); 63 | -o-transform: rotate(360deg); 64 | transform: rotate(360deg); 65 | } 66 | } -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic-ui/src/css/loading_animation.css: -------------------------------------------------------------------------------- 1 | body { 2 | -webkit-perspective: 3000; 3 | perspective: 3000; 4 | } 5 | 6 | section { 7 | margin: 10% auto; 8 | padding: 0; 9 | width: 100%; 10 | max-width: 1000px; } 11 | 12 | section h2 { 13 | font: 100 2em 'Helvetica Neue', Helvetica, Arial, sans-serif; 14 | margin: 0 5%; } 15 | 16 | 17 | .loading { 18 | padding: 10% 0; 19 | text-align: center; } 20 | 21 | .loader-1 .loading-spinner { 22 | position: relative; 23 | width: 2em; 24 | height: 2em; 25 | display: inline-block; 26 | font-size: 2em; } 27 | 28 | .loader-1 .loading-spinner .loader { 29 | position: absolute; 30 | top: 0; 31 | right: 0; 32 | bottom: 0; 33 | left: 0; 34 | -webkit-animation: loader 4s infinite ease-in-out; 35 | -moz-animation: loader 4s infinite ease-in-out; 36 | -ms-animation: loader 4s infinite ease-in-out; 37 | -o-animation: loader 4s infinite ease-in-out; 38 | animation: loader 4s infinite ease-in-out; 39 | -moz-box-shadow: inset 0 0 0 .0666666667em #888; 40 | -webkit-box-shadow: inset 0 0 0 .0666666667em #888; 41 | box-shadow: inset 0 0 0 .0666666667em #888; } 42 | 43 | .loader-1 .loading-spinner .loader:after { 44 | content: ' '; 45 | display: inline-block; 46 | vertical-align: top; 47 | width: 100%; 48 | background: #888; 49 | opacity: .5; 50 | -webkit-animation: loader-inner 4s infinite ease-in-out; 51 | -moz-animation: loader-inner 4s infinite ease-in-out; 52 | -ms-animation: loader-inner 4s infinite ease-in-out; 53 | -o-animation: loader-inner 4s infinite ease-in-out; 54 | animation: loader-inner 4s infinite ease-in-out; } 55 | 56 | .loader-1 .loading-spinner .icon { 57 | position: absolute; 58 | top: 0; 59 | right: 0; 60 | bottom: 0; 61 | left: 0; 62 | line-height: 2em; 63 | text-align: center; 64 | color: #888; 65 | -webkit-animation: icon-anim 4s infinite ease-in-out; 66 | animation: icon-anim 4s infinite ease-in-out; 67 | } 68 | 69 | @-webkit-keyframes icon-anim { 70 | 0% { 71 | transform: scale(1); } 72 | 25% { 73 | transform: scale(1.25); } 74 | 50% { 75 | transform: scale(1); } 76 | 75% { 77 | transform: scale(1.25); } 78 | 100% { 79 | transform: scale(1); } 80 | } 81 | @-webkit-keyframes loader { 82 | 0% { 83 | transform: rotate(0deg) scale(1); } 84 | 25% { 85 | transform: rotate(180deg) scale(1.25); } 86 | 50% { 87 | transform: rotate(180deg) scale(1); } 88 | 75% { 89 | transform: rotate(360deg) scale(1.25); } 90 | 100% { 91 | transform: rotate(360deg) scale(1); } } 92 | @-moz-keyframes loader { 93 | 0% { 94 | transform: rotate(0deg) scale(1); } 95 | 25% { 96 | transform: rotate(180deg) scale(1.25); } 97 | 50% { 98 | transform: rotate(180deg) scale(1); } 99 | 75% { 100 | transform: rotate(360deg) scale(1.25); } 101 | 100% { 102 | transform: rotate(360deg) scale(1); } } 103 | @-ms-keyframes loader { 104 | 0% { 105 | transform: rotate(0deg) scale(1); } 106 | 25% { 107 | transform: rotate(180deg) scale(1.25); } 108 | 50% { 109 | transform: rotate(180deg) scale(1); } 110 | 75% { 111 | transform: rotate(360deg) scale(1.25); } 112 | 100% { 113 | transform: rotate(360deg) scale(1); } } 114 | @-o-keyframes loader { 115 | 0% { 116 | transform: rotate(0deg) scale(1); } 117 | 25% { 118 | transform: rotate(180deg) scale(1.25); } 119 | 50% { 120 | transform: rotate(180deg) scale(1); } 121 | 75% { 122 | transform: rotate(360deg) scale(1.25); } 123 | 100% { 124 | transform: rotate(360deg) scale(1); } } 125 | @keyframes loader { 126 | 0% { 127 | transform: rotate(0deg) scale(1); } 128 | 25% { 129 | transform: rotate(180deg) scale(1.25); } 130 | 50% { 131 | transform: rotate(180deg) scale(1); } 132 | 75% { 133 | transform: rotate(360deg) scale(1.25); } 134 | 100% { 135 | transform: rotate(360deg) scale(1); } } 136 | 137 | 138 | @-webkit-keyframes loader-inner { 139 | 0%, 25%, 100% { 140 | height: 0; } 141 | 50%, 75% { 142 | height: 100%; } } 143 | @-moz-keyframes loader-inner { 144 | 0%, 25%, 100% { 145 | height: 0; } 146 | 50%, 75% { 147 | height: 100%; } } 148 | @-ms-keyframes loader-inner { 149 | 0%, 25%, 100% { 150 | height: 0; } 151 | 50%, 75% { 152 | height: 100%; } } 153 | @-o-keyframes loader-inner { 154 | 0%, 25%, 100% { 155 | height: 0; } 156 | 50%, 75% { 157 | height: 100%; } } 158 | @keyframes loader-inner { 159 | 0%, 25%, 100% { 160 | height: 0; } 161 | 50%, 75% { 162 | height: 100%; } } -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic-ui/src/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Space Mono', monospace; 3 | background-color: #fbfdff; 4 | font-size: 9px; 5 | } 6 | h1,h2,h3,h4,h5 { 7 | color: #34495e; 8 | } 9 | h1,h2,h3,h4,h5,p { 10 | text-align: left; 11 | } 12 | h1 { 13 | font-family: 'Space Mono', monospace; 14 | font-weight: 700; 15 | } 16 | h2 { 17 | font-family: 'Space Mono', monospace; 18 | font-weight: 100; 19 | font-size: 25px; 20 | } 21 | h3 { 22 | font-family: 'Space Mono', monospace; 23 | font-weight: 100; 24 | font-size: 16px; 25 | } 26 | h5 { 27 | font-weight: 100; 28 | font-size: 12px; 29 | } 30 | 31 | p { 32 | text-align: left; 33 | font-weight: 300; 34 | line-height: 1.7em; 35 | font-size: 12px; 36 | color: #000000; 37 | } 38 | img { 39 | max-width: 100%; 40 | display: block; 41 | } 42 | iframe { 43 | max-width: 100%; 44 | display: block; 45 | } 46 | svg:not(:root) { 47 | position: relative; 48 | overflow: visible; 49 | } 50 | 51 | .sidebar-top { 52 | padding-bottom: 50px; 53 | } 54 | .related { 55 | font-size: 11px !important; 56 | } 57 | .hide-overflow { 58 | overflow: hidden; 59 | } 60 | .line-divisor { 61 | padding: 20px 0pt; 62 | margin: 20px 0pt; 63 | border-bottom: 1px solid #DDDDDD; 64 | border-top: 1px solid #DDDDDD; 65 | } 66 | 67 | #graph { 68 | height: 800px; 69 | width: 100%; 70 | position: relative; 71 | overflow: visible; 72 | margin: 0px; 73 | } 74 | 75 | .logo-container { 76 | padding: 0px; 77 | } 78 | .logo { 79 | max-height: 35px; 80 | vertical-align: middle; 81 | padding: 0px; 82 | margin: 0px; 83 | } 84 | .article-card .title { 85 | font-family: 'Merriweather'; 86 | font-weight: 500; 87 | font-size: 26px; 88 | } 89 | .article-card { 90 | font-family: 'Roboto Mono'; 91 | font-weight: 100; 92 | font-size: 10px; 93 | } 94 | .total-scheduled-instances { 95 | font-family: 'Roboto Mono'; 96 | font-weight: 300; 97 | font-size: 10px; 98 | padding-left: 10px; 99 | } 100 | 101 | /* 102 | 103 | Pulsing effect. 104 | 105 | */ 106 | .alert_green { 107 | display: block; 108 | width: 10px; 109 | height: 10px; 110 | border-radius: 50%; 111 | background: #2ecc71; 112 | cursor: pointer; 113 | box-shadow: 0 0 0 rgba(209, 193, 139, 0.40); 114 | } 115 | .alert_yellow { 116 | display: block; 117 | width: 10px; 118 | height: 10px; 119 | border-radius: 50%; 120 | background: #f1c40f; 121 | cursor: pointer; 122 | box-shadow: 0 0 0 rgba(209, 193, 139, 0.40); 123 | } 124 | .alert_red { 125 | display: block; 126 | width: 10px; 127 | height: 10px; 128 | border-radius: 50%; 129 | background: #e74c3c; 130 | cursor: pointer; 131 | box-shadow: 0 0 0 rgba(209, 193, 139, 0.40); 132 | animation: pulse 1.5s infinite; 133 | } 134 | .alert_red:hover { 135 | animation: pulse 1.5s infinite; 136 | } 137 | 138 | @-webkit-keyframes pulse { 139 | 0% { 140 | -webkit-box-shadow: 0 0 0 0 rgba(182, 182, 182, 0.40); 141 | } 142 | 70% { 143 | -webkit-box-shadow: 0 0 0 20px rgba(182, 182, 182, 0); 144 | } 145 | 100% { 146 | -webkit-box-shadow: 0 0 0 0 rgba(182, 182, 182, 0); 147 | } 148 | } 149 | @keyframes pulse { 150 | 0% { 151 | -moz-box-shadow: 0 0 0 0 rgba(182, 182, 182, 0.40); 152 | box-shadow: 0 0 0 0 rgba(182, 182, 182, 0.40); 153 | } 154 | 70% { 155 | -moz-box-shadow: 0 0 0 20px rgba(182, 182, 182, 0); 156 | box-shadow: 0 0 0 20px rgba(182, 182, 182, 0); 157 | } 158 | 100% { 159 | -moz-box-shadow: 0 0 0 0 rgba(182, 182, 182, 0); 160 | box-shadow: 0 0 0 0 rgba(182, 182, 182, 0); 161 | } 162 | } -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic-ui/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Cryptonic 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic-ui/src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import ElementUI from 'element-ui' 3 | import lodash from 'lodash' 4 | import c3 from 'c3' 5 | import Trend from 'vuetrend' 6 | import moment from 'moment-timezone' 7 | 8 | import 'c3/c3.min.css' 9 | import 'element-ui/lib/theme-default/index.css' 10 | import './css/style.css' 11 | import './css/forbes_logo.css' 12 | import './css/loading_animation.css' 13 | import './css/infinite.css' 14 | import App from './App.vue' 15 | import locale from 'element-ui/lib/locale/lang/en' 16 | 17 | import VueMoment from 'vue-moment' 18 | import VueResource from 'vue-resource' 19 | import VueLodash from 'vue-lodash' 20 | 21 | 22 | Vue.prototype.$c3 = c3 23 | 24 | Vue.use(VueLodash, lodash) 25 | Vue.use(ElementUI, { locale }) 26 | Vue.use(VueResource) 27 | Vue.use(VueMoment, { moment }) 28 | Vue.use(Trend) 29 | Vue.use(c3) 30 | 31 | 32 | new Vue({ 33 | el: '#app', 34 | render: h => h(App) 35 | }) 36 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic-ui/webpack.config.js: -------------------------------------------------------------------------------- 1 | const resolve = require('path').resolve 2 | const webpack = require('webpack') 3 | const HtmlWebpackPlugin = require('html-webpack-plugin') 4 | const url = require('url') 5 | const publicPath = '' 6 | 7 | module.exports = (options = {}) => ({ 8 | entry: { 9 | vendor: './src/vendor', 10 | index: './src/main.js' 11 | }, 12 | output: { 13 | path: resolve(__dirname, 'dist'), 14 | filename: options.dev ? '[name].js' : '[name].js?[chunkhash]', 15 | chunkFilename: '[id].js?[chunkhash]', 16 | publicPath: options.dev ? '/assets/' : publicPath 17 | }, 18 | module: { 19 | rules: [{ 20 | test: /\.vue$/, 21 | use: ['vue-loader'] 22 | }, 23 | { 24 | test: /\.js$/, 25 | use: ['babel-loader'], 26 | exclude: /node_modules/, 27 | include: [ 28 | resolve(__dirname, 'src'), 29 | resolve(__dirname, 'test'), 30 | resolve(__dirname, 'node_modules/element-ui/packages') 31 | ] 32 | }, 33 | { 34 | test: /\.css$/, 35 | use: ['style-loader', 'css-loader', 'postcss-loader'] 36 | }, 37 | { 38 | test: /\.(png|jpg|jpeg|gif|eot|ttf|woff|woff2|svg|svgz)(\?.+)?$/, 39 | use: [{ 40 | loader: 'url-loader', 41 | options: { 42 | limit: 10000 43 | } 44 | }] 45 | } 46 | ] 47 | }, 48 | plugins: [ 49 | new webpack.optimize.CommonsChunkPlugin({ 50 | names: ['vendor', 'manifest'] 51 | }), 52 | new HtmlWebpackPlugin({ 53 | template: 'src/index.html' 54 | }) 55 | ], 56 | resolve: { 57 | alias: { 58 | '~': resolve(__dirname, 'src') 59 | } 60 | }, 61 | devServer: { 62 | host: '127.0.0.1', 63 | port: 8010, 64 | proxy: { 65 | '/api/': { 66 | target: 'http://127.0.0.1:8080', 67 | changeOrigin: true, 68 | pathRewrite: { 69 | '^/api': '' 70 | } 71 | } 72 | }, 73 | historyApiFallback: { 74 | index: url.parse(options.dev ? '/assets/' : publicPath).pathname 75 | } 76 | }, 77 | devtool: options.dev ? '#eval-source-map' : '#source-map' 78 | }) 79 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic.env: -------------------------------------------------------------------------------- 1 | REDIS_URL=redis://redis@cache:6379/0 2 | CRYPTONIC_VERSION=latest 3 | BITCOIN_START_DATE=2017-01-01 4 | EPOCHS=300 5 | PERIOD_SIZE=7 -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Cryptonic is an educational application created for 3 | the purposes of learning how to use deep learning 4 | to predict Bitcoin prices. 5 | """ 6 | from cryptonic.models.model import Model 7 | from cryptonic.markets.coinmarketcap import CoinMarketCap 8 | 9 | __version__ = 'v1.0.1' 10 | __author__ = 'Luis Capelo' 11 | __email__ = 'luiscape@gmail.com' 12 | 13 | __all__ = ['Model', 'CoinMarketCap'] 14 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic/api/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | HTTP API interface for Cryptonic. 3 | """ 4 | 5 | __version__ = 1 6 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic/api/routes.py: -------------------------------------------------------------------------------- 1 | """ 2 | Creates all application routes. 3 | """ 4 | import os 5 | 6 | from datetime import datetime, timedelta 7 | from flask_api import status as http_status 8 | from flask import jsonify, request, make_response, send_file, send_from_directory 9 | 10 | from cryptonic.api import __version__ 11 | 12 | 13 | def __cache_identifier(*args, **kwargs): 14 | """ 15 | Creates a cache identifier based on the complete 16 | URL provided in the request. Original solution: 17 | 18 | * https://stackoverflow.com/questions/9413566/\ 19 | flask-cache-memoize-url-query-string-parameters-as-well 20 | 21 | """ 22 | # 23 | # The following snippet will create a 24 | # cache key for the URL and all GET and POST 25 | # parameters. 26 | # 27 | key = request.url + \ 28 | '='.join([k + '=' + v for k,v in request.args.items()]) + '&' + \ 29 | '='.join([k + '=' + v for k,v in request.form.items()]) 30 | 31 | return key 32 | 33 | 34 | def create_routes(app, cache, model): 35 | """ 36 | Function that creates the application routes. 37 | 38 | Parameters 39 | ---------- 40 | app: Flask object 41 | Initialized Flask object. 42 | 43 | cache: Flask-Caching instance 44 | Cache instance that can be used 45 | as a decorator. 46 | 47 | model: instantiated Model() 48 | Module() class instantiated as a model 49 | and trained. 50 | 51 | Returns 52 | ------- 53 | app: Flask object 54 | Modified Flask app object. 55 | """ 56 | # 57 | # Let's clear the cache at every 58 | # startup. 59 | # 60 | cache.clear() 61 | 62 | @cache.cached(timeout=60 * 5, key_prefix=__cache_identifier) 63 | @app.errorhandler(404) 64 | def page_not_found(e): 65 | return app.send_static_file('index.html'), 404 66 | 67 | @cache.cached(timeout=60 * 5, key_prefix=__cache_identifier) 68 | @app.route('/') 69 | def root(): 70 | """ 71 | Endpoint for serving the index page. 72 | """ 73 | return app.send_static_file('index.html') 74 | 75 | @app.route('/') 76 | def send_static_files(path): 77 | return send_from_directory( 78 | os.getenv('UI_DIST_DIRECTORY', '../cryptonic-ui/dist/'), path) 79 | 80 | @app.route('/status') 81 | def status(): 82 | """ 83 | Endpoint for returning the application status. 84 | """ 85 | r = { 86 | 'version': __version__, 87 | 'success': True, 88 | 'message': 'Predict Bitcoin prices with deep learning.', 89 | 'model': { 90 | 'name': os.getenv('MODEL_NAME'), 91 | 'last_trained': model.last_trained, 92 | 'error_rates': model.evaluate() 93 | } 94 | } 95 | return jsonify(r) 96 | 97 | @cache.cached(timeout=60 * 24, key_prefix=__cache_identifier) 98 | @app.route('/historic') 99 | def historic(): 100 | """ 101 | Returns a series of historic observations. 102 | 103 | Parameters 104 | ---------- 105 | start: str, default six months ago 106 | Start date to filter the Bitcoin historic price 107 | data set. 108 | 109 | """ 110 | six_months_ago = ( 111 | datetime.now() - timedelta(days=30 * 6)).strftime('%Y-%m-%d') 112 | start = request.args.get('start', six_months_ago) 113 | 114 | historic_data = model.data.to_dict(orient='records') 115 | filtered_historic_data = list(filter(lambda x: x['date'] > six_months_ago, historic_data)) 116 | 117 | r = { 118 | 'version': __version__, 119 | 'success': True, 120 | 'message': 'Historic Bitcoin prices from CoinMarketCap.', 121 | 'start_date': os.getenv('BITCOIN_START_DATE'), 122 | 'result': filtered_historic_data 123 | } 124 | 125 | return jsonify(r) 126 | 127 | @cache.cached(timeout=60 * 24, key_prefix=__cache_identifier) 128 | @app.route('/predict') 129 | def predict(): 130 | """ 131 | Endpoint for predicting bitcoin prices. 132 | """ 133 | r = { 134 | 'version': __version__, 135 | 'success': True, 136 | 'message': 'Endpoint for making predictions.', 137 | 'period_length': os.getenv('PERIOD_SIZE', 7), 138 | 'result': model.predict(denormalized=True, return_dict=True) 139 | } 140 | return jsonify(r) 141 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic/markets/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Interface for manipulating data from different markets. 3 | """ 4 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic/markets/coinmarketcap.py: -------------------------------------------------------------------------------- 1 | """ 2 | Logic for collecting data directly from the 3 | CoinMarketCap API. 4 | """ 5 | import requests 6 | import pandas as pd 7 | 8 | from datetime import datetime 9 | from bs4 import BeautifulSoup 10 | 11 | 12 | class CoinMarketCap: 13 | """ 14 | Class interface to data from CoinMarketCap. 15 | Original data can be found at: 16 | 17 | https://coinmarketcap.com/ 18 | 19 | """ 20 | def __repr__(self): 21 | message = """ 22 | 23 | Crypto-currency data comes from the website CoinMarketCap. 24 | CoinMarketCap is can be accessed at: https://coinmarketcap.com/ 25 | 26 | The permission to use the data is available on their FAQ 27 | 28 | https://coinmarketcap.com/faq/ 29 | 30 | and reads: 31 | 32 | "Q: Am I allowed to use content (screenshots, data, graphs, etc.) 33 | for one of my personal projects and/or commercial use? 34 | 35 | R: Absolutely! Feel free to use any content as you see fit. 36 | We kindly ask that you cite us as a source." 37 | 38 | """ 39 | return message 40 | 41 | @classmethod 42 | def historic(cls, start='2013-04-28', stop=None, ticker='bitcoin', return_json=False): 43 | """ 44 | Retrieves historic data within a time 45 | period. 46 | 47 | Parameters 48 | ---------- 49 | start, stop: str 50 | Start and stop dates in ISO format (YYYY-MM-DD). 51 | 52 | ticker: str 53 | Name of ticker to be used (e.g. `bitcoin`). 54 | 55 | Returns 56 | ------- 57 | Pandas dataframe with historical ticker data. 58 | """ 59 | start = start.replace('-', '') 60 | if not stop: 61 | stop = datetime.now().strftime('%Y%m%d') 62 | 63 | url = 'https://coinmarketcap.com/currencies/{}/historical-data/?start={}&end={}'.format(ticker, start, stop) 64 | r = requests.get(url) 65 | 66 | soup = BeautifulSoup(r.content, 'lxml') 67 | table = soup.find_all('table')[0] 68 | df = pd.read_html(str(table))[0] 69 | 70 | # 71 | # Cleans variables from the original. 72 | # 73 | df['Date'] = df['Date'].apply(lambda x: datetime.strptime(x, '%b %d, %Y').strftime('%Y-%m-%d')) 74 | df['Volume'] = df['Volume'].apply(lambda x: None if x == '-' else x) 75 | df.columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'market_cap'] 76 | 77 | if return_json: 78 | df.to_json(orient='records') 79 | 80 | return df 81 | 82 | @classmethod 83 | def current(cls, ticker='bitcoin'): 84 | """ 85 | Fetches current prices from CoinMarketCap. 86 | 87 | Returns 88 | ------- 89 | Dictionary with a single record form the 90 | """ 91 | url = 'https://api.coinmarketcap.com/v1/ticker/{}/'.format(ticker) 92 | r = requests.get(url) 93 | 94 | return r.json() 95 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic/models/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Cryptocurrency prediction models. 3 | """ 4 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic/models/helper.py: -------------------------------------------------------------------------------- 1 | """ 2 | Helper class and methods for making manipulating 3 | data for models. 4 | """ 5 | import numpy as np 6 | 7 | import cryptonic.models.normalizations as normalizations 8 | 9 | 10 | class ModelHelper: 11 | """ 12 | Class with utility functions that aid in 13 | the process of training LSTM models with Keras. 14 | 15 | """ 16 | def __init__(self): 17 | pass 18 | 19 | def create_groups(self, data, start=0, group_size=7, normalize=True): 20 | """ 21 | Creates distinct groups from a given continuous series. 22 | 23 | Parameters 24 | ---------- 25 | data: np.array 26 | Series of continious observations. 27 | 28 | start: int 29 | Starting point for the series. This 30 | is used to prune earlier observations 31 | from the series in case the series is 32 | too long or too short. 33 | 34 | group_size: int, default 7 35 | Determines how large the groups are. That is, 36 | how many observations each group contains. 37 | 38 | normalize: bool 39 | If the method should normalize data or not. 40 | Normalization is done using 41 | 42 | normalizations.point_relative_normalization() 43 | 44 | Returns 45 | ------- 46 | A Numpy array object. 47 | """ 48 | samples = list() 49 | for i in range(0, len(data), group_size): 50 | sample = list(data[start + i:i + group_size]) 51 | if len(sample) == group_size: 52 | if normalize: 53 | sample = normalizations.point_relative_normalization(sample) 54 | 55 | samples.append(np.array(sample).reshape(1, group_size).tolist()) 56 | 57 | A = np.array(samples) 58 | return A.reshape(1, A.shape[0], group_size) 59 | 60 | def split_lstm_input(self, groups): 61 | """ 62 | Splits groups in a format expected by 63 | the LSTM layer. 64 | 65 | Parameters 66 | ---------- 67 | groups: np.array 68 | Numpy array with the organized sequences. 69 | 70 | Returns 71 | ------- 72 | X, Y: np.array 73 | Numpy arrays with the shapes required by 74 | the LSTM layer. X with (1, a - 1, b) 75 | and Y with (1, b). Where a is the total 76 | number of groups in `group` and b the 77 | number of observations per group. 78 | """ 79 | X = groups[0:,:-1].reshape(1, groups.shape[1] - 1, groups.shape[2]) 80 | Y = groups[0:,-1:][0] 81 | 82 | return X, Y 83 | 84 | def normalize(self): 85 | """ 86 | Normalizes a series using point-relative normalization. 87 | 88 | Parameters 89 | ---------- 90 | 91 | Returns 92 | ------- 93 | """ 94 | normalizations.point_relative_normalization() 95 | 96 | def denormalize(self, series, last_value): 97 | """ 98 | De-normalizes a series using the latest 99 | value available from data. 100 | 101 | Parameters 102 | ---------- 103 | series: numpy array 104 | Series with normalized values. 105 | 106 | last_value: float 107 | Numerical value that represents the 108 | last value from the dataset. 109 | 110 | Returns 111 | ------- 112 | """ 113 | result = last_value * (series + 1) 114 | return result 115 | 116 | def mape(self, A, B): 117 | """ 118 | Calcualtes the mean absolute persentage error 119 | from two series. Original solution from: 120 | 121 | https://stats.stackexchange.com/questions/58391/\ 122 | mean-absolute-percentage-error-mape-in-scikit-learn 123 | """ 124 | return np.mean(np.abs((A - B) / (1 - A))) * 100 125 | 126 | def rmse(self, A, B): 127 | """ 128 | Calculates the root mean square error from 129 | two series. Original solution from: 130 | 131 | https://stackoverflow.com/questions/16774849\ 132 | /mean-squared-error-in-numpy 133 | """ 134 | return np.sqrt(np.square(np.subtract(A, B)).mean()) 135 | 136 | def mse(self, A, B): 137 | """ 138 | Calculates the mean square error from 139 | two series. Original solution from: 140 | 141 | https://stackoverflow.com/questions/16774849\ 142 | /mean-squared-error-in-numpy 143 | """ 144 | return np.square(np.subtract(A, B)).mean() 145 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic/models/normalizations.py: -------------------------------------------------------------------------------- 1 | """ 2 | Series of normalization functions useful 3 | for normalizing time-series data. 4 | """ 5 | 6 | def z_score(series): 7 | """ 8 | Computes the normalized value using the Z-score 9 | technique. The Z-score is a technique used for 10 | normalizing Gaussian distributions representing 11 | each observation in relation to the distribution's 12 | mean and standard deviation. For precise definitions, 13 | see the Wikipedia article: 14 | 15 | https://en.wikipedia.org/wiki/Standard_score 16 | 17 | Parameters 18 | ---------- 19 | serie: list 20 | List with sequential values to use. 21 | 22 | Returns 23 | ------- 24 | result: list 25 | List with the normalized results. 26 | """ 27 | result = (series - series.mean()) / series.std(ddof=0) 28 | return result 29 | 30 | def point_relative_normalization(series, reverse=False, last_value=None): 31 | """ 32 | Computes the normalized value for the values of a 33 | given series by using the first element of the serie as p_0 34 | as a reference for each p_i. 35 | 36 | This technique comes from Siraj Raval's YouTube video 37 | "How to Predict Stock Prices Easily - Intro to Deep Learning #7", 38 | available at: 39 | 40 | https://www.youtube.com/watch?v=ftMq5ps503w 41 | 42 | Parameters 43 | ---------- 44 | serie: list 45 | List with sequential values to use. 46 | 47 | reverse: bool, default True 48 | If the method should de-normalize data. 49 | 50 | last_value: int or float 51 | Used to de-normalize a dataset. Needs to 52 | be passed if `reverse` is True. 53 | 54 | Returns 55 | ------- 56 | result: list 57 | List with the normalized results. 58 | """ 59 | if reverse: 60 | result = last_value * (series + 1) 61 | else: 62 | result = (series / series[0]) - 1 63 | 64 | return result 65 | 66 | def maximum_and_minimum_normalization(series, boundary=(0, 1)): 67 | """ 68 | Computes the normalized value for the values of a 69 | given serie by using that series maximum and minimum 70 | values. 71 | 72 | This technique is a direct implementation from 73 | scikit-learn, available at: 74 | 75 | http://scikit-learn.org/stable/modules/generated/\ 76 | sklearn.preprocessing.MinMaxScaler.html 77 | 78 | Parameters 79 | ---------- 80 | serie: list 81 | List with sequential values to use. 82 | 83 | boundary: set 84 | Maximum and minimum values used to 85 | scale the series. 86 | 87 | Returns 88 | ------- 89 | result: list 90 | List with the normalized results. 91 | """ 92 | range_min, range_max = boundary 93 | standard_deviation = (series - series.min(axis=0)) / (series.max(axis=0) - series.min(axis=0)) 94 | result = standard_deviation * (range_max - range_min) + range_min 95 | return result 96 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic/server.py: -------------------------------------------------------------------------------- 1 | """ 2 | Functions to start and configure the Flask 3 | application. This simply configures the server 4 | and its routes. 5 | """ 6 | import os 7 | import flask 8 | 9 | from flask_caching import Cache 10 | from flask_cors import CORS, cross_origin 11 | 12 | from cryptonic import Model 13 | #from cryptonic import CoinMarketCap 14 | from cryptonic.api.routes import create_routes 15 | import yfinance as yf 16 | 17 | UI_DIST_DIRECTORY = os.getenv('UI_DIST_DIRECTORY', '../cryptonic-ui/dist/') 18 | 19 | 20 | class Server: 21 | """ 22 | Cryptonic server representation. This class 23 | contains logic for managing the configuration 24 | and deployment of Flask server. 25 | 26 | Parameters 27 | ---------- 28 | debug: bool, default False 29 | If should start with a debugger. 30 | 31 | cors: bool, default True 32 | If the application should accept CORS 33 | requests. 34 | 35 | """ 36 | def __init__(self, debug=False, cors=True): 37 | self.debug = debug 38 | self.cors = cors 39 | 40 | self.create_model() 41 | self.app = self.create() 42 | 43 | def create_model(self): 44 | """ 45 | Creates a model either using a model provided 46 | by user or by creating a new model using 47 | previously researched parameters. 48 | 49 | Returns 50 | ------- 51 | Trained Keras model. Ready to be used 52 | via the model.predict() method. 53 | """ 54 | ticker = yf.Ticker("BTC-USD") 55 | historic_data = ticker.history(period='max') 56 | historic_data = historic_data.rename(columns={'Open':'open', 'High':'high', 'Low':'low', 'Close':'close', 'Volume':'volume'}) 57 | historic_data.index.names = ['date'] 58 | historic_data = historic_data[['open','high', 'low', 'close', 'volume']] 59 | historic_data = historic_data.reset_index() 60 | 61 | model_path = os.getenv('MODEL_NAME') 62 | 63 | # 64 | # TODO: Figure out how large the data is for 65 | # the old model and re-train. Maybe what I have 66 | # to do here is to copy the weights of the 67 | # model into a new model. 68 | # 69 | 70 | self.model = Model(data=historic_data, 71 | path=model_path, 72 | variable='close', 73 | predicted_period_size=int(os.getenv('PERIOD_SIZE', 7))) 74 | 75 | if not model_path: 76 | self.model.build() 77 | self.model.train(epochs=int(os.getenv('EPOCHS', 50)), verbose=1) 78 | 79 | return self.model 80 | 81 | def create(self): 82 | """ 83 | Method for creating a Flask server. 84 | 85 | Returns 86 | ------- 87 | A Flask() application object. 88 | """ 89 | app = flask.Flask(__name__, static_url_path='/', static_folder=UI_DIST_DIRECTORY) 90 | 91 | # 92 | # Application configuration. Here we 93 | # configure the application to accept 94 | # CORS requests, its routes, and 95 | # its debug flag. 96 | # 97 | if self.cors: 98 | CORS(app) 99 | 100 | cache_configuration = { 101 | 'CACHE_TYPE': 'redis', 102 | 'CACHE_REDIS_URL': os.getenv('REDIS_URL', 103 | "redis://localhost:6379/2") 104 | } 105 | 106 | self.cache = Cache(app, config=cache_configuration) 107 | 108 | app.config['DEBUG'] = self.debug 109 | create_routes(app, self.cache, self.model) 110 | 111 | return app 112 | 113 | def run(self, *args, **kwargs): 114 | """ 115 | Method for running Flask server. 116 | Parameters 117 | ---------- 118 | *args, **kwargs: parameters passed to the Flask application. 119 | """ 120 | self.app.run(*args, **kwargs) -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic_old/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Cryptonic is an educational application created for 3 | the purposes of learning how to use deep learning 4 | to predict Bitcoin prices. 5 | """ 6 | from cryptonic.models.model import Model 7 | from cryptonic.markets.coinmarketcap import CoinMarketCap 8 | 9 | __version__ = 'v1.0.1' 10 | __author__ = 'Luis Capelo' 11 | __email__ = 'luiscape@gmail.com' 12 | 13 | __all__ = ['Model', 'CoinMarketCap'] 14 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic_old/api/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | HTTP API interface for Cryptonic. 3 | """ 4 | 5 | __version__ = 1 6 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic_old/api/routes.py: -------------------------------------------------------------------------------- 1 | """ 2 | Creates all application routes. 3 | """ 4 | import os 5 | 6 | from datetime import datetime, timedelta 7 | from flask_api import status as http_status 8 | from flask import jsonify, request, make_response, send_file, send_from_directory 9 | 10 | from cryptonic.api import __version__ 11 | 12 | 13 | def __cache_identifier(*args, **kwargs): 14 | """ 15 | Creates a cache identifier based on the complete 16 | URL provided in the request. Original solution: 17 | 18 | * https://stackoverflow.com/questions/9413566/\ 19 | flask-cache-memoize-url-query-string-parameters-as-well 20 | 21 | """ 22 | # 23 | # The following snippet will create a 24 | # cache key for the URL and all GET and POST 25 | # parameters. 26 | # 27 | key = request.url + \ 28 | '='.join([k + '=' + v for k,v in request.args.items()]) + '&' + \ 29 | '='.join([k + '=' + v for k,v in request.form.items()]) 30 | 31 | return key 32 | 33 | 34 | def create_routes(app, cache, model): 35 | """ 36 | Function that creates the application routes. 37 | 38 | Parameters 39 | ---------- 40 | app: Flask object 41 | Initialized Flask object. 42 | 43 | cache: Flask-Caching instance 44 | Cache instance that can be used 45 | as a decorator. 46 | 47 | model: instantiated Model() 48 | Module() class instantiated as a model 49 | and trained. 50 | 51 | Returns 52 | ------- 53 | app: Flask object 54 | Modified Flask app object. 55 | """ 56 | # 57 | # Let's clear the cache at every 58 | # startup. 59 | # 60 | cache.clear() 61 | 62 | @cache.cached(timeout=60 * 5, key_prefix=__cache_identifier) 63 | @app.errorhandler(404) 64 | def page_not_found(e): 65 | return app.send_static_file('index.html'), 404 66 | 67 | @cache.cached(timeout=60 * 5, key_prefix=__cache_identifier) 68 | @app.route('/') 69 | def root(): 70 | """ 71 | Endpoint for serving the index page. 72 | """ 73 | return app.send_static_file('index.html') 74 | 75 | @app.route('/') 76 | def send_static_files(path): 77 | return send_from_directory( 78 | os.getenv('UI_DIST_DIRECTORY', '../cryptonic-ui/dist/'), path) 79 | 80 | @app.route('/status') 81 | def status(): 82 | """ 83 | Endpoint for returning the application status. 84 | """ 85 | r = { 86 | 'version': __version__, 87 | 'success': True, 88 | 'message': 'Predict Bitcoin prices with deep learning.', 89 | 'model': { 90 | 'name': os.getenv('MODEL_NAME'), 91 | 'last_trained': model.last_trained, 92 | 'error_rates': model.evaluate() 93 | } 94 | } 95 | return jsonify(r) 96 | 97 | @cache.cached(timeout=60 * 24, key_prefix=__cache_identifier) 98 | @app.route('/historic') 99 | def historic(): 100 | """ 101 | Returns a series of historic observations. 102 | 103 | Parameters 104 | ---------- 105 | start: str, default six months ago 106 | Start date to filter the Bitcoin historic price 107 | data set. 108 | 109 | """ 110 | six_months_ago = ( 111 | datetime.now() - timedelta(days=30 * 6)).strftime('%Y-%m-%d') 112 | start = request.args.get('start', six_months_ago) 113 | 114 | historic_data = model.data.to_dict(orient='records') 115 | filtered_historic_data = list(filter(lambda x: x['date'] > six_months_ago, historic_data)) 116 | 117 | r = { 118 | 'version': __version__, 119 | 'success': True, 120 | 'message': 'Historic Bitcoin prices from CoinMarketCap.', 121 | 'start_date': os.getenv('BITCOIN_START_DATE'), 122 | 'result': filtered_historic_data 123 | } 124 | 125 | return jsonify(r) 126 | 127 | @cache.cached(timeout=60 * 24, key_prefix=__cache_identifier) 128 | @app.route('/predict') 129 | def predict(): 130 | """ 131 | Endpoint for predicting bitcoin prices. 132 | """ 133 | r = { 134 | 'version': __version__, 135 | 'success': True, 136 | 'message': 'Endpoint for making predictions.', 137 | 'period_length': os.getenv('PERIOD_SIZE', 7), 138 | 'result': model.predict(denormalized=True, return_dict=True) 139 | } 140 | return jsonify(r) 141 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic_old/markets/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Interface for manipulating data from different markets. 3 | """ 4 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic_old/markets/coinmarketcap.py: -------------------------------------------------------------------------------- 1 | """ 2 | Logic for collecting data directly from the 3 | CoinMarketCap API. 4 | """ 5 | import requests 6 | import pandas as pd 7 | 8 | from datetime import datetime 9 | from bs4 import BeautifulSoup 10 | 11 | 12 | class CoinMarketCap: 13 | """ 14 | Class interface to data from CoinMarketCap. 15 | Original data can be found at: 16 | 17 | https://coinmarketcap.com/ 18 | 19 | """ 20 | def __repr__(self): 21 | message = """ 22 | 23 | Crypto-currency data comes from the website CoinMarketCap. 24 | CoinMarketCap is can be accessed at: https://coinmarketcap.com/ 25 | 26 | The permission to use the data is available on their FAQ 27 | 28 | https://coinmarketcap.com/faq/ 29 | 30 | and reads: 31 | 32 | "Q: Am I allowed to use content (screenshots, data, graphs, etc.) 33 | for one of my personal projects and/or commercial use? 34 | 35 | R: Absolutely! Feel free to use any content as you see fit. 36 | We kindly ask that you cite us as a source." 37 | 38 | """ 39 | return message 40 | 41 | @classmethod 42 | def historic(cls, start='2013-04-28', stop=None, ticker='bitcoin', return_json=False): 43 | """ 44 | Retrieves historic data within a time 45 | period. 46 | 47 | Parameters 48 | ---------- 49 | start, stop: str 50 | Start and stop dates in ISO format (YYYY-MM-DD). 51 | 52 | ticker: str 53 | Name of ticker to be used (e.g. `bitcoin`). 54 | 55 | Returns 56 | ------- 57 | Pandas dataframe with historical ticker data. 58 | """ 59 | start = start.replace('-', '') 60 | if not stop: 61 | stop = datetime.now().strftime('%Y%m%d') 62 | 63 | url = 'https://coinmarketcap.com/currencies/{}/historical-data/?start={}&end={}'.format(ticker, start, stop) 64 | r = requests.get(url) 65 | 66 | soup = BeautifulSoup(r.content, 'lxml') 67 | table = soup.find_all('table')[0] 68 | df = pd.read_html(str(table))[0] 69 | 70 | # 71 | # Cleans variables from the original. 72 | # 73 | df['Date'] = df['Date'].apply(lambda x: datetime.strptime(x, '%b %d, %Y').strftime('%Y-%m-%d')) 74 | df['Volume'] = df['Volume'].apply(lambda x: None if x == '-' else x) 75 | df.columns = ['date', 'open', 'high', 'low', 'close', 'volume', 'market_cap'] 76 | 77 | if return_json: 78 | df.to_json(orient='records') 79 | 80 | return df 81 | 82 | @classmethod 83 | def current(cls, ticker='bitcoin'): 84 | """ 85 | Fetches current prices from CoinMarketCap. 86 | 87 | Returns 88 | ------- 89 | Dictionary with a single record form the 90 | """ 91 | url = 'https://api.coinmarketcap.com/v1/ticker/{}/'.format(ticker) 92 | r = requests.get(url) 93 | 94 | return r.json() 95 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic_old/models/__init__.py: -------------------------------------------------------------------------------- 1 | """ 2 | Cryptocurrency prediction models. 3 | """ 4 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic_old/models/helper.py: -------------------------------------------------------------------------------- 1 | """ 2 | Helper class and methods for making manipulating 3 | data for models. 4 | """ 5 | import numpy as np 6 | 7 | import cryptonic.models.normalizations as normalizations 8 | 9 | 10 | class ModelHelper: 11 | """ 12 | Class with utility functions that aid in 13 | the process of training LSTM models with Keras. 14 | 15 | """ 16 | def __init__(self): 17 | pass 18 | 19 | def create_groups(self, data, start=0, group_size=7, normalize=True): 20 | """ 21 | Creates distinct groups from a given continuous series. 22 | 23 | Parameters 24 | ---------- 25 | data: np.array 26 | Series of continious observations. 27 | 28 | start: int 29 | Starting point for the series. This 30 | is used to prune earlier observations 31 | from the series in case the series is 32 | too long or too short. 33 | 34 | group_size: int, default 7 35 | Determines how large the groups are. That is, 36 | how many observations each group contains. 37 | 38 | normalize: bool 39 | If the method should normalize data or not. 40 | Normalization is done using 41 | 42 | normalizations.point_relative_normalization() 43 | 44 | Returns 45 | ------- 46 | A Numpy array object. 47 | """ 48 | samples = list() 49 | for i in range(0, len(data), group_size): 50 | sample = list(data[start + i:i + group_size]) 51 | if len(sample) == group_size: 52 | if normalize: 53 | sample = normalizations.point_relative_normalization(sample) 54 | 55 | samples.append(np.array(sample).reshape(1, group_size).tolist()) 56 | 57 | A = np.array(samples) 58 | return A.reshape(1, A.shape[0], group_size) 59 | 60 | def split_lstm_input(self, groups): 61 | """ 62 | Splits groups in a format expected by 63 | the LSTM layer. 64 | 65 | Parameters 66 | ---------- 67 | groups: np.array 68 | Numpy array with the organized sequences. 69 | 70 | Returns 71 | ------- 72 | X, Y: np.array 73 | Numpy arrays with the shapes required by 74 | the LSTM layer. X with (1, a - 1, b) 75 | and Y with (1, b). Where a is the total 76 | number of groups in `group` and b the 77 | number of observations per group. 78 | """ 79 | X = groups[0:,:-1].reshape(1, groups.shape[1] - 1, groups.shape[2]) 80 | Y = groups[0:,-1:][0] 81 | 82 | return X, Y 83 | 84 | def normalize(self): 85 | """ 86 | Normalizes a series using point-relative normalization. 87 | 88 | Parameters 89 | ---------- 90 | 91 | Returns 92 | ------- 93 | """ 94 | normalizations.point_relative_normalization() 95 | 96 | def denormalize(self, series, last_value): 97 | """ 98 | De-normalizes a series using the latest 99 | value available from data. 100 | 101 | Parameters 102 | ---------- 103 | series: numpy array 104 | Series with normalized values. 105 | 106 | last_value: float 107 | Numerical value that represents the 108 | last value from the dataset. 109 | 110 | Returns 111 | ------- 112 | """ 113 | result = last_value * (series + 1) 114 | return result 115 | 116 | def mape(self, A, B): 117 | """ 118 | Calcualtes the mean absolute persentage error 119 | from two series. Original solution from: 120 | 121 | https://stats.stackexchange.com/questions/58391/\ 122 | mean-absolute-percentage-error-mape-in-scikit-learn 123 | """ 124 | return np.mean(np.abs((A - B) / (1 - A))) * 100 125 | 126 | def rmse(self, A, B): 127 | """ 128 | Calculates the root mean square error from 129 | two series. Original solution from: 130 | 131 | https://stackoverflow.com/questions/16774849\ 132 | /mean-squared-error-in-numpy 133 | """ 134 | return np.sqrt(np.square(np.subtract(A, B)).mean()) 135 | 136 | def mse(self, A, B): 137 | """ 138 | Calculates the mean square error from 139 | two series. Original solution from: 140 | 141 | https://stackoverflow.com/questions/16774849\ 142 | /mean-squared-error-in-numpy 143 | """ 144 | return np.square(np.subtract(A, B)).mean() 145 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic_old/models/normalizations.py: -------------------------------------------------------------------------------- 1 | """ 2 | Series of normalization functions useful 3 | for normalizing time-series data. 4 | """ 5 | 6 | def z_score(series): 7 | """ 8 | Computes the normalized value using the Z-score 9 | technique. The Z-score is a technique used for 10 | normalizing Gaussian distributions representing 11 | each observation in relation to the distribution's 12 | mean and standard deviation. For precise definitions, 13 | see the Wikipedia article: 14 | 15 | https://en.wikipedia.org/wiki/Standard_score 16 | 17 | Parameters 18 | ---------- 19 | serie: list 20 | List with sequential values to use. 21 | 22 | Returns 23 | ------- 24 | result: list 25 | List with the normalized results. 26 | """ 27 | result = (series - series.mean()) / series.std(ddof=0) 28 | return result 29 | 30 | def point_relative_normalization(series, reverse=False, last_value=None): 31 | """ 32 | Computes the normalized value for the values of a 33 | given series by using the first element of the serie as p_0 34 | as a reference for each p_i. 35 | 36 | This technique comes from Siraj Raval's YouTube video 37 | "How to Predict Stock Prices Easily - Intro to Deep Learning #7", 38 | available at: 39 | 40 | https://www.youtube.com/watch?v=ftMq5ps503w 41 | 42 | Parameters 43 | ---------- 44 | serie: list 45 | List with sequential values to use. 46 | 47 | reverse: bool, default True 48 | If the method should de-normalize data. 49 | 50 | last_value: int or float 51 | Used to de-normalize a dataset. Needs to 52 | be passed if `reverse` is True. 53 | 54 | Returns 55 | ------- 56 | result: list 57 | List with the normalized results. 58 | """ 59 | if reverse: 60 | result = last_value * (series + 1) 61 | else: 62 | result = (series / series[0]) - 1 63 | 64 | return result 65 | 66 | def maximum_and_minimum_normalization(series, boundary=(0, 1)): 67 | """ 68 | Computes the normalized value for the values of a 69 | given serie by using that series maximum and minimum 70 | values. 71 | 72 | This technique is a direct implementation from 73 | scikit-learn, available at: 74 | 75 | http://scikit-learn.org/stable/modules/generated/\ 76 | sklearn.preprocessing.MinMaxScaler.html 77 | 78 | Parameters 79 | ---------- 80 | serie: list 81 | List with sequential values to use. 82 | 83 | boundary: set 84 | Maximum and minimum values used to 85 | scale the series. 86 | 87 | Returns 88 | ------- 89 | result: list 90 | List with the normalized results. 91 | """ 92 | range_min, range_max = boundary 93 | standard_deviation = (series - series.min(axis=0)) / (series.max(axis=0) - series.min(axis=0)) 94 | result = standard_deviation * (range_max - range_min) + range_min 95 | return result 96 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/cryptonic_old/server.py: -------------------------------------------------------------------------------- 1 | """ 2 | Functions to start and configure the Flask 3 | application. This simply configures the server 4 | and its routes. 5 | """ 6 | import os 7 | import flask 8 | 9 | from flask_caching import Cache 10 | from flask_cors import CORS, cross_origin 11 | 12 | from cryptonic import Model 13 | #from cryptonic import CoinMarketCap 14 | from cryptonic.api.routes import create_routes 15 | import yfinance as yf 16 | 17 | UI_DIST_DIRECTORY = os.getenv('UI_DIST_DIRECTORY', '../cryptonic-ui/dist/') 18 | 19 | 20 | class Server: 21 | """ 22 | Cryptonic server representation. This class 23 | contains logic for managing the configuration 24 | and deployment of Flask server. 25 | 26 | Parameters 27 | ---------- 28 | debug: bool, default False 29 | If should start with a debugger. 30 | 31 | cors: bool, default True 32 | If the application should accept CORS 33 | requests. 34 | 35 | """ 36 | def __init__(self, debug=False, cors=True): 37 | self.debug = debug 38 | self.cors = cors 39 | 40 | self.create_model() 41 | self.app = self.create() 42 | 43 | def create_model(self): 44 | """ 45 | Creates a model either using a model provided 46 | by user or by creating a new model using 47 | previously researched parameters. 48 | 49 | Returns 50 | ------- 51 | Trained Keras model. Ready to be used 52 | via the model.predict() method. 53 | """ 54 | ticker = yf.Ticker("BTC-USD") 55 | historic_data = ticker.history(period='max') 56 | 57 | model_path = os.getenv('MODEL_NAME') 58 | 59 | # 60 | # TODO: Figure out how large the data is for 61 | # the old model and re-train. Maybe what I have 62 | # to do here is to copy the weights of the 63 | # model into a new model. 64 | # 65 | 66 | self.model = Model(data=historic_data, 67 | path=model_path, 68 | variable='close', 69 | predicted_period_size=int(os.getenv('PERIOD_SIZE', 7))) 70 | 71 | if not model_path: 72 | self.model.build() 73 | self.model.train(epochs=int(os.getenv('EPOCHS', 300)), verbose=1) 74 | 75 | return self.model 76 | 77 | def create(self): 78 | """ 79 | Method for creating a Flask server. 80 | 81 | Returns 82 | ------- 83 | A Flask() application object. 84 | """ 85 | app = flask.Flask(__name__, static_url_path='/', static_folder=UI_DIST_DIRECTORY) 86 | 87 | # 88 | # Application configuration. Here we 89 | # configure the application to accept 90 | # CORS requests, its routes, and 91 | # its debug flag. 92 | # 93 | if self.cors: 94 | CORS(app) 95 | 96 | cache_configuration = { 97 | 'CACHE_TYPE': 'redis', 98 | 'CACHE_REDIS_URL': os.getenv('REDIS_URL', 99 | 'redis://redis@cache:6379/0') 100 | } 101 | 102 | self.cache = Cache(app, config=cache_configuration) 103 | 104 | app.config['DEBUG'] = self.debug 105 | create_routes(app, self.cache, self.model) 106 | 107 | return app 108 | 109 | def run(self, *args, **kwargs): 110 | """ 111 | Method for running Flask server. 112 | Parameters 113 | ---------- 114 | *args, **kwargs: parameters passed to the Flask application. 115 | """ 116 | self.app.run(*args, **kwargs) -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/docker-compose.yml: -------------------------------------------------------------------------------- 1 | # 2 | # CRYPTONIC DOCKER-COMPOSE 3 | # ------------------------ 4 | # 5 | # Here we configure the application stack 6 | # that is deployed with Cryptonic. This 7 | # includes three elements: 8 | # 9 | # i. Cryptonic App: a Python web-application 10 | # that exposes the Cryptonic API via 11 | # the HTTP protocol. 12 | # 13 | # ii. Cryptonic Cache: a Redis instance that 14 | # works as a caching layer. 15 | # 16 | version: "3" 17 | 18 | services: 19 | 20 | cache: 21 | image: cryptonic-cache:latest 22 | build: 23 | context: ./cryptonic-cache 24 | dockerfile: ./Dockerfile 25 | volumes: 26 | - $PWD/cache_data:/data 27 | networks: 28 | - cryptonic 29 | 30 | cryptonic: 31 | image: cryptonic:latest 32 | build: 33 | context: . 34 | dockerfile: ./Dockerfile 35 | ports: 36 | - "5000:5000" 37 | environment: 38 | - BITCOIN_START_DATE=2019-01-01 39 | - EPOCHS=50 40 | - PERIOD_SIZE=7 41 | volumes: 42 | - ./cryptonic_logs:/logs 43 | - ./models:/models 44 | env_file: 45 | - cryptonic.env 46 | depends_on: 47 | - cache 48 | networks: 49 | - cryptonic 50 | 51 | networks: 52 | cryptonic: 53 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/requirements.txt: -------------------------------------------------------------------------------- 1 | beautifulsoup4==4.6.0 2 | bleach==1.5.0 3 | certifi==2017.11.5 4 | chardet==3.0.4 5 | click==6.7 6 | colorama==0.3.9 7 | coverage==4.4.2 8 | enum34==1.1.6 9 | Flask==0.12.2 10 | Flask-API==1.0 11 | Flask-Caching==1.3.3 12 | Flask-Cors==3.0.3 13 | Flask-Testing==0.7.1 14 | h5py==2.7.1 15 | html5lib==0.9999999 16 | idna==2.6 17 | itsdangerous==0.24 18 | Jinja2==2.10 19 | Keras==2.1.2 20 | lxml==4.1.1 21 | Markdown==2.6.10 22 | MarkupSafe==1.0 23 | nose==1.3.7 24 | numpy 25 | pandas==0.25.3 26 | protobuf 27 | python-dateutil==2.6.1 28 | pytz==2017.3 29 | PyYAML==3.12 30 | redis==2.10.6 31 | rednose==1.2.3 32 | requests 33 | scipy==1.0.0 34 | six==1.11.0 35 | tensorboard==2.0.1 36 | tensorflow==2.0.0 37 | tensorflow-estimator==2.0.1 38 | termstyle==0.1.11 39 | urllib3==1.22 40 | yfinance 41 | Werkzeug==0.13 42 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/run.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | """ 3 | Script for starting the Cryptonic Flask server. 4 | """ 5 | import os 6 | import time 7 | 8 | from cryptonic.server import Server 9 | 10 | 11 | def main(): 12 | """ 13 | Wrapper function for starting a server. 14 | """ 15 | 16 | print('Starting server.') 17 | server = Server() 18 | 19 | server.run(host=os.getenv("HOST", "0.0.0.0")) 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /Chapter04/Exercise4.02/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/d503565ee073ce5baa776e4ea600134cb5cfaf31/Chapter04/Exercise4.02/screenshot.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Packt Workshops 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The Applied TensorFlow and Keras Workshop 2 | [![GitHub issues](https://img.shields.io/github/issues/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop.svg)](https://github.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/issues) 3 | [![GitHub forks](https://img.shields.io/github/forks/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop.svg)](https://github.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/network) 4 | [![GitHub stars](https://img.shields.io/github/stars/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop.svg)](https://github.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/stargazers) 5 | [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/PacktWorkshops/The-Applied-TensorFlow-and-Keras-Workshop/pulls) 6 | [![versions](https://img.shields.io/pypi/pyversions/pybadges.svg)](https://www.python.org/downloads/) 7 | 8 | This is the repository for [The Applied TensorFlow and Keras Workshop](https://www.amazon.com/Applied-TensorFlow-Keras-Workshop-real-world-ebook/dp/B08Q8F55ZS/ref=sr_1_1?dchild=1&keywords=The%20Applied%20TensorFlow%20and%20Keras%20Workshop&qid=1610976724&sr=8-1&utm_source=github&utm_medium=repository&utm_campaign=9781801078153&utm_term=Applied%20TensorFlow%20and%20Keras&utm_content=The%20Applied%20TensorFlow%20and%20Keras%20Workshop), published by [Packt](https://www.packtpub.com/?utm_source=github). It contains all the supporting project files necessary to work through the course from start to finish. 9 | 10 | ## Requirements and Setup 11 | The Applied TensorFlow and Keras Workshop 12 | 13 | To get started with the project files, you'll need to: 14 | 1. Install Jupyter on [Windows](https://www.python.org/downloads/windows/), [Mac](https://www.python.org/downloads/mac-osx/), [Linux](https://www.python.org/downloads/source/) 15 | 2. Install Anaconda on [Windows](https://www.anaconda.com/distribution/#windows), [Mac](https://www.anaconda.com/distribution/#macos), [Linux](https://www.anaconda.com/distribution/#linux) 16 | 17 | ## About The Applied TensorFlow and Keras Workshop 18 | [The Applied TensorFlow and Keras Workshop](https://www.amazon.com/Applied-TensorFlow-Keras-Workshop-real-world-ebook/dp/B08Q8F55ZS/ref=sr_1_1?dchild=1&keywords=The%20Applied%20TensorFlow%20and%20Keras%20Workshop&qid=1610976724&sr=8-1&utm_source=github&utm_medium=repository&utm_campaign=9781801078153&utm_term=Applied%20TensorFlow%20and%20Keras&utm_content=The%20Applied%20TensorFlow%20and%20Keras%20Workshop) provides you with a blueprint to build an application that generates predictions using a deep learning model. You’ll learn to apply techniques to improve the model: add more data and features, change its architecture, or create a new model by changing the core components to meet your own requirements. 19 | 20 | ## What you will learn 21 | * Familiarize yourself with the components of a neural network 22 | * Understand the different types of problems that can be solved using neural networks 23 | * Explore different ways to select the right architecture for your model 24 | * Make predictions with a trained model using TensorBoard 25 | * Discover the components of Keras and ways to leverage its features in your model 26 | * Explore how you can deal with new data by learning ways to retrain your model 27 | 28 | ## Related Workshops 29 | If you've found this repository useful, you might want to check out some of our other workshop titles: 30 | * [The Applied AI and Natural Language Processing Workshop](https://www.amazon.com/Applied-Natural-Language-Processing-Workshop-ebook/dp/B08Q8GNTGT/ref=sr_1_1?dchild=1&keywords=The%20Applied%20AI%20and%20Natural%20Language%20Processing%20Workshop&qid=1610976605&sr=8-1&utm_source=github&utm_medium=repository&utm_campaign=9781801071307&utm_term=Applied%20AI%20and%20Natural%20Language%20Processing&utm_content=The%20Applied%20AI%20and%20Natural%20Language%20Processing%20Workshop) 31 | * [The Deep Learning Workshop](https://www.amazon.com/Deep-Learning-Workshop-next-generation-TensorFlow-ebook/dp/B08Q8GP7DJ/ref=sr_1_2?dchild=1&keywords=The%20Deep%20Learning%20Workshop&qid=1611054533&sr=8-2&utm_source=GitHub&utm_medium=Repository&utm_campaign=9781801075169&utm_term=Deep%20Learning&utm_content=The%20Deep%20Learning%20Workshop) 32 | * [The Deep Learning with Keras Workshop](https://www.amazon.com/Deep-Learning-Keras-Workshop-network-ebook/dp/B08Q8JJ45N/ref=sr_1_1?dchild=1&keywords=The%20Deep%20Learning%20with%20Keras%20Workshop&qid=1611054389&sr=8-1&utm_source=GitHub&utm_medium=Repository&utm_campaign=9781801071185&utm_term=Deep%20Learning%20with%20Keras&utm_content=The%20Deep%20Learning%20with%20Keras%20Workshop) 33 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | absl-py==0.8.1 2 | astor==0.8.0 3 | attrs==19.3.0 4 | backcall==0.1.0 5 | beautifulsoup4==4.8.1 6 | bleach==3.1.0 7 | cachetools==3.1.1 8 | certifi==2019.9.11 9 | chardet==3.0.4 10 | Click==7.0 11 | cycler==0.10.0 12 | decorator==4.4.1 13 | defusedxml==0.6.0 14 | entrypoints==0.3 15 | Flask==1.1.1 16 | Flask-API==2.0 17 | Flask-Caching==1.8.0 18 | Flask-Cors==3.0.8 19 | Flask-Testing==0.7.1 20 | gast==0.2.2 21 | google-auth==1.7.0 22 | google-auth-oauthlib==0.4.1 23 | google-pasta==0.1.8 24 | graphviz==0.13.2 25 | grpcio==1.24.3 26 | h5py==2.10.0 27 | html5lib==1.0.1 28 | idna==2.8 29 | importlib-metadata==1.3.0 30 | ipykernel==5.1.3 31 | ipython==7.10.2 32 | ipython-genutils==0.2.0 33 | ipywidgets==7.5.1 34 | itsdangerous==1.1.0 35 | jedi==0.15.2 36 | Jinja2==2.10.3 37 | jsonschema==3.2.0 38 | jupyter==1.0.0 39 | jupyter-client==5.3.4 40 | jupyter-console==6.0.0 41 | jupyter-core==4.6.1 42 | Keras==2.2.4 43 | Keras-Applications==1.0.8 44 | Keras-Preprocessing==1.1.0 45 | kiwisolver==1.1.0 46 | Markdown==3.1.1 47 | MarkupSafe==1.1.1 48 | matplotlib==3.1.2 49 | mistune==0.8.4 50 | more-itertools==8.0.2 51 | nbconvert==5.6.1 52 | nbformat==4.4.0 53 | notebook==6.0.2 54 | numpy==1.17.3 55 | oauthlib==3.1.0 56 | opt-einsum==3.1.0 57 | pandas==0.25.3 58 | pandocfilters==1.4.2 59 | parso==0.5.2 60 | pexpect==4.7.0 61 | pickleshare==0.7.5 62 | prometheus-client==0.7.1 63 | prompt-toolkit==2.0.10 64 | protobuf==3.10.0 65 | ptyprocess==0.6.0 66 | pyasn1==0.4.7 67 | pyasn1-modules==0.2.7 68 | Pygments==2.5.2 69 | pyparsing==2.4.5 70 | pyrsistent==0.15.6 71 | python-dateutil==2.8.1 72 | pytz==2019.3 73 | PyYAML==5.2 74 | pyzmq==18.1.1 75 | qtconsole==4.6.0 76 | requests==2.22.0 77 | requests-oauthlib==1.2.0 78 | rsa==4.0 79 | scipy==1.4.1 80 | seaborn==0.9.0 81 | Send2Trash==1.5.0 82 | setuptools==41.0.0 83 | six==1.13.0 84 | soupsieve==1.9.5 85 | tensorboard==2.0.1 86 | tensorflow==2.0.0 87 | tensorflow-estimator==2.0.1 88 | termcolor==1.1.0 89 | terminado==0.8.3 90 | testpath==0.4.4 91 | tornado==6.0.3 92 | tqdm==4.41.0 93 | traitlets==4.3.3 94 | urllib3==1.25.6 95 | wcwidth==0.1.7 96 | webencodings==0.5.1 97 | Werkzeug==0.16.0 98 | widgetsnbextension==3.5.1 99 | wrapt==1.11.2 100 | yfinance==0.1.54 101 | zipp==0.6.0 102 | --------------------------------------------------------------------------------