Showing preview only (1,772K chars total). Download the full file or copy to clipboard to get everything.
Repository: instillai/TensorFlow-Course
Branch: master
Commit: 3ac35a2e8766
Files: 52
Total size: 1.7 MB
Directory structure:
gitextract_9uld941z/
├── .gitattributes
├── .github/
│ └── FUNDING.yml
├── .travis.yml
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.rst
├── LICENSE
├── README.rst
├── _img/
│ └── 1-basics/
│ └── readme.rst
├── codes/
│ ├── ipython/
│ │ ├── 0-welcome/
│ │ │ └── welcome.ipynb
│ │ ├── 1-basics/
│ │ │ ├── automatic_differentiation.ipynb
│ │ │ ├── graph.ipynb
│ │ │ ├── models.ipynb
│ │ │ └── tensors.ipynb
│ │ ├── advanced/
│ │ │ ├── custom_training.ipynb
│ │ │ ├── dataset_generator.ipynb
│ │ │ └── tfrecords.ipynb
│ │ ├── basics_in_machine_learning/
│ │ │ ├── dataaugmentation.ipynb
│ │ │ └── linearregression.ipynb
│ │ └── neural_networks/
│ │ ├── CNNs.ipynb
│ │ └── mlp.ipynb
│ └── python/
│ ├── 0-welcome/
│ │ └── welcome.py
│ ├── 1-basics/
│ │ ├── automatic_differentiation.py
│ │ ├── graph.py
│ │ ├── models.py
│ │ └── tensors.py
│ ├── advanced/
│ │ ├── custom_training.py
│ │ ├── dataset_generator.py
│ │ └── tfrecords.py
│ ├── application/
│ │ └── image/
│ │ └── image_classification.py
│ ├── basics_in_machine_learning/
│ │ ├── dataaugmentation.py
│ │ └── linearregression.py
│ └── neural_networks/
│ ├── cnns.py
│ └── mlp.py
├── docs/
│ ├── Makefile
│ ├── README.rst
│ ├── _img/
│ │ ├── 1-basics/
│ │ │ └── readme.rst
│ │ └── 3-neural_network/
│ │ └── autoencoder/
│ │ └── README.rst
│ ├── conf.py
│ ├── index.rst
│ ├── make.bat
│ └── tutorials/
│ ├── 0-welcome/
│ │ └── README.rst
│ ├── 1-basics/
│ │ ├── basic_math_operations/
│ │ │ └── README.rst
│ │ ├── readme.rst
│ │ └── variables/
│ │ └── README.rst
│ ├── 2-basics_in_machine_learning/
│ │ ├── linear_regression/
│ │ │ └── README.rst
│ │ └── logistic_regression/
│ │ └── README.rst
│ ├── 3-neural_network/
│ │ ├── autoencoder/
│ │ │ └── README.rst
│ │ └── convolutiona_neural_network/
│ │ └── README.rst
│ └── installation/
│ └── README.rst
├── requirements.txt
├── travis.sh
└── welcome.py
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
codes/ipython/* linguist-vendored
# Basic .gitattributes for a python repo.
# Source files
# ============
.pxd text diff=python
.py text diff=python
.py3 text diff=python
.pyw text diff=python
.pyx text diff=python
# Binary files
# ============
.db binary
.p binary
.pkl binary
.pyc binary
.pyd binary
.pyo binary
# Note: .db, .p, and .pkl files are associated
# with the python modules ``pickle``, ``dbm.*``,
# ``shelve``, ``marshal``, ``anydbm``, & ``bsddb``
# (among others).
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: [astorfi]
================================================
FILE: .travis.yml
================================================
sudo: required
language: python
cache: pip
git:
depth: false
quiet: true
python: # The following versions
- "3.6"
- "3.7"
# command to install dependencies
build:
stage: build
only:
- paths:
- "^codes/*"
install:
- pip install numpy
- pip install matplotlib
- pip install pandas
- pip install seaborn
- pip install pathlib
- pip install tensorflow_datasets
- pip install scikit-image
# install TensorFlow from https://storage.googleapis.com/tensorflow/
- if [[ "$TRAVIS_PYTHON_VERSION" == "3.6" ]]; then
pip install https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow_cpu-2.3.0-cp36-cp36m-manylinux2010_x86_64.whl;
elif [[ "$TRAVIS_PYTHON_VERSION" == "3.7" ]]; then
pip install https://storage.googleapis.com/tensorflow/linux/cpu/tensorflow_cpu-2.3.0-cp37-cp37m-manylinux2010_x86_64.whl;
fi
script:
# You can run all python files in parallel, http://stackoverflow.com/questions/5015316
# - find codes/python/ -type f -name "*.py" |xargs -n 1 python
# get list of changed files and if they are Python files, run them.
- ./travis.sh
================================================
FILE: CODE_OF_CONDUCT.md
================================================
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at amirsina.torfi@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/
================================================
FILE: CONTRIBUTING.rst
================================================
*************
Contributing
*************
When contributing to this repository, please first discuss the change you wish to make via issue,
email, or any other method with the owners of this repository before making a change. *For typos, please
do not create a pull request. Instead, declare them in issues or email the repository owner*.
Please note we have a code of conduct, please follow it in all your interactions with the project.
====================
Pull Request Process
====================
Please consider the following criterions in order to help us in a better way:
1. The pull request is mainly expected to be a code script suggestion or improvement.
2. A pull request related to non-code-script sections is expected to make a significant difference in the documentation. Otherwise, it is expected to be announced in the issues section.
3. Ensure any install or build dependencies are removed before the end of the layer when doing a
build and creating a pull request.
4. Add comments with details of changes to the interface, this includes new environment
variables, exposed ports, useful file locations and container parameters.
5. You may merge the Pull Request in once you have the sign-off of at least one other developer, or if you
do not have permission to do that, you may request the owner to merge it for you if you believe all checks are passed.
============
Final Note
============
We are looking forward to your kind feedback. Please help us to improve this open source project and make our work better.
For contribution, please create a pull request and we will investigate it promptly. Once again, we appreciate
your kind feedback and elaborate code inspections.
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2017 Amirsina Torfi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: README.rst
================================================
********************
`TensorFlow Course`_
********************
.. image:: https://travis-ci.org/instillai/TensorFlow-Course.svg?branch=master
:target: https://travis-ci.org/instillai/TensorFlow-Course
.. image:: https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat
:target: https://github.com/open-source-for-science/TensorFlow-Course/pulls
.. image:: https://img.shields.io/twitter/follow/machinemindset.svg?label=Follow&style=social
:target: https://twitter.com/machinemindset
.. image:: https://zenodo.org/badge/151300862.svg
:target: https://zenodo.org/badge/latestdoi/151300862
This repository aims to provide simple and ready-to-use tutorials for TensorFlow.
Each tutorial includes ``source code`` and most of them are associated with a ``documentation``.
.. .. image:: _img/mainpage/TensorFlow_World.gif
.. The links.
.. _TensorFlow: https://www.tensorflow.org/install/
.. _Wikipedia: https://en.wikipedia.org/wiki/TensorFlow/
##########################################################################
Sponsorship
##########################################################################
To support maintaining and upgrading this project, please kindly consider `Sponsoring the project developer <https://github.com/sponsors/astorfi/dashboard>`_.
Any level of support is a great contribution here :heart:
**Status:** *This project has been updated to **TensorFlow 2.3**.*
#################
Table of Contents
#################
.. contents::
:local:
:depth: 3
==========================================
Download Free TensorFlow Roadmap EBook
==========================================
.. raw:: html
<div align="center">
.. raw:: html
<a href="http://www.machinelearningmindset.com/tensorflow-roadmap-ebook/" target="_blank">
<img width="710" height="500" align="center" src="https://github.com/machinelearningmindset/TensorFlow-Course/blob/master/_img/mainpage/booksubscribe.png"/>
</a>
.. raw:: html
</div>
==========================================
Slack Group
==========================================
.. raw:: html
<div align="center">
.. raw:: html
<a href="https://www.machinelearningmindset.com/slack-group/" target="_blank">
<img width="1033" height="350" align="center" src="https://github.com/machinelearningmindset/TensorFlow-Course/blob/master/_img/0-welcome/joinslack.png"/>
</a>
.. raw:: html
</div>
~~~~~~~~~~~~~~~~~~~~~
What is TensorFlow?
~~~~~~~~~~~~~~~~~~~~~
TensorFlow is an open-source software library for dataflow programming across a range of tasks. It is a symbolic math library, and is also used for machine learning applications such as neural networks. It is used for both research and production at Google often replacing its closed-source predecessor, DistBelief.
TensorFlow was developed by the Google Brain team for internal Google use. It was released under the Apache 2.0 open source license on November 9, 2015.
============
Motivation
============
There are different motivations for this open source project. TensorFlow (as we write this document) is one of / the best deep learning frameworks available. The question that should be asked is why has this repository been created when there are so many other tutorials about TensorFlow available on the web?
~~~~~~~~~~~~~~~~~~~~~
Why use TensorFlow?
~~~~~~~~~~~~~~~~~~~~~
Deep Learning is in very high interest these days - there's a crucial need for rapid and optimized implementations of the algorithms and architectures. TensorFlow is designed to facilitate this goal.
The strong advantage of TensorFlow is it flexibility in designing highly modular models which can also be a disadvantage for beginners since a lot of the pieces must be considered together when creating the model.
This issue has been facilitated as well by developing high-level APIs such as `Keras <https://keras.io/>`_ and `Slim <https://github.com/tensorflow/models/blob/031a5a4ab41170d555bc3e8f8545cf9c8e3f1b28/research/inception/inception/slim/README.md>`_ which abstract a lot of the pieces used in designing machine learning algorithms.
The interesting thing about TensorFlow is that **it can be found anywhere these days**. Lots of the researchers and developers are using it and *its community is growing at the speed of light*! So many issues can be dealt with easily since they're usually the same issues that a lot of other people run into considering the large number of people involved in the TensorFlow community.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
What's the point of this repository?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**Developing open source projects for the sake of just developing something is not the reason behind this effort**.
Considering the large number of tutorials that are being added to this large community, this repository has been created to break the jump-in and jump-out process that usually happens to most of the open source projects, **but why and how**?
First of all, what's the point of putting effort into something that most of the people won't stop by and take a look? What's the point of creating something that does not help anyone in the developers and researchers community? Why spend time for something that can easily be forgotten? But **how we try to do it?** Even up to this
very moment there are countless tutorials on TensorFlow whether on the model design or TensorFlow
workflow.
Most of them are too complicated or suffer from a lack of documentation. There are only a few available tutorials which are concise and well-structured and provide enough insight for their specific implemented models.
The goal of this project is to help the community with structured tutorials and simple and optimized code implementations to provide better insight about how to use TensorFlow *quick and effectively*.
It is worth noting that, **the main goal of this project is to provide well-documented tutorials and less-complicated code**!
=================================================
TensorFlow Installation and Setup the Environment
=================================================
.. image:: _img/mainpage/installation-logo.gif
:height: 100px
:width: 200 px
:scale: 50 %
:alt: alternate text
:align: right
:target: docs/tutorials/installation
.. _TensorFlow Installation: https://www.tensorflow.org/install
In order to install TensorFlow please refer to the following link:
* `TensorFlow Installation`_
.. image:: _img/mainpage/installation.gif
:target: https://www.tensorflow.org/install
The virtual environment installation is recommended in order to prevent package conflict and having the capacity to customize the working environment.
====================
TensorFlow Tutorials
====================
The tutorials in this repository are partitioned into relevant categories.
==========================
~~~~~~~~
Warm-up
~~~~~~~~
.. image:: _img/mainpage/welcome.gif
:height: 100px
:width: 200 px
:scale: 50 %
:alt: alternate text
:align: right
.. _colab: https://colab.research.google.com/github/instillai/TensorFlow-Course/blob/master/codes/ipython/0-welcome/welcome.ipynb
.. _Documentationcnnwelcome: docs/tutorials/0-welcome
.. _ipythonwelcome: codes/ipython/0-welcome/welcome.ipynb
.. _pythonwelcome: https://github.com/instillai/TensorFlow-Course/blob/master/codes/python/0-welcome/welcome.py
.. _videowelcome: https://youtu.be/xd0DVygHlNE
.. |Welcome| image:: https://colab.research.google.com/assets/colab-badge.svg
:target: https://colab.research.google.com/github/instillai/TensorFlow-Course/blob/master/codes/ipython/0-welcome/welcome.ipynb
.. |youtubeim| image:: _img/mainpage/YouTube.png
:target: https://github.com/instillai/TensorFlow-Course/blob/master/_img/mainpage/YouTube.png
+----+---------------------+--------------------------+------------------------------------------------------------------------+-------------------------------------------+
| # | topic | Run | Source Code | Media |
+====+=====================+==========================+========================================================================+===========================================+
| 1 | Start-up | |Welcome| | `Notebook <ipythonwelcome_>`_ / `Python <pythonwelcome_>`_ | `Video Tutorial <videowelcome_>`_ |
+----+---------------------+--------------------------+------------------------------------------------------------------------+-------------------------------------------+
==========================
~~~~~~
Basics
~~~~~~
.. raw:: html
<div align="left">
.. raw:: html
<a href="https://github.com/instillai/TensorFlow-Course/blob/master/_img/mainpage/basics.gif" target="_blank">
<img width="250" height="250" align="center" src="https://github.com/instillai/TensorFlow-Course/blob/master/_img/mainpage/basics.gif"/>
</a>
.. raw:: html
</div>
.. raw:: html
<br>
.. _ipythontensors: codes/ipython/1-basics/tensors.ipynb
.. _pythontensors: codes/python/1-basics/tensors.py
.. _videotensors: https://youtu.be/Od-VvnYUbFw
.. |Tensors| image:: https://colab.research.google.com/assets/colab-badge.svg
:target: https://colab.research.google.com/github/instillai/TensorFlow-Course/blob/master/codes/ipython/1-basics/tensors.ipynb
.. _ipythonad: codes/ipython/1-basics/automatic_differentiation.ipynb
.. _pythonad: codes/python/1-basics/automatic_differentiation.py
.. _videoad: https://youtu.be/l-MGydWW-UE
.. |AD| image:: https://colab.research.google.com/assets/colab-badge.svg
:target: https://colab.research.google.com/github/instillai/TensorFlow-Course/blob/master/codes/ipython/1-basics/automatic_differentiation.ipynb
.. _ipythongraphs: codes/ipython/1-basics/graph.ipynb
.. _pythongraphs: codes/python/1-basics/graph.py
.. _videographs: https://youtu.be/P9xA1s6AUNk
.. |graphs| image:: https://colab.research.google.com/assets/colab-badge.svg
:target: https://colab.research.google.com/github/instillai/TensorFlow-Course/blob/master/codes/ipython/1-basics/graph.ipynb
.. _ipythonmodels: codes/ipython/1-basics/models.ipynb
.. _pythonmodels: codes/python/1-basics/models.py
.. _videomodels: https://youtu.be/WnlUE04REOY
.. |models| image:: https://colab.research.google.com/assets/colab-badge.svg
:target: https://colab.research.google.com/github/instillai/TensorFlow-Course/blob/master/codes/ipython/1-basics/models.ipynb
+----+-----------------------------------+--------------------------+------------------------------------------------------------------------+-----------------------------------------+
| # | topic | Run | Source Code | Media |
+====+===================================+==========================+========================================================================+=========================================+
| 1 | Tensors | |Tensors| | `Notebook <ipythontensors_>`_ / `Python <pythontensors_>`_ | `Video Tutorial <videotensors_>`_ |
+----+-----------------------------------+--------------------------+------------------------------------------------------------------------+-----------------------------------------+
| 2 | Automatic Differentiation | |AD| | `Notebook <ipythonad_>`_ / `Python <pythonad_>`_ | `Video Tutorial <videoad_>`_ |
+----+-----------------------------------+--------------------------+------------------------------------------------------------------------+-----------------------------------------+
| 3 | Introduction to Graphs | |graphs| | `Notebook <ipythongraphs_>`_ / `Python <pythongraphs_>`_ | `Video Tutorial <videographs_>`_ |
+----+-----------------------------------+--------------------------+------------------------------------------------------------------------+-----------------------------------------+
| 4 | TensorFlow Models | |models| | `Notebook <ipythonmodels_>`_ / `Python <pythonmodels_>`_ | `Video Tutorial <videomodels_>`_ |
+----+-----------------------------------+--------------------------+------------------------------------------------------------------------+-----------------------------------------+
==========================
~~~~~~~~~~~~~~~~~~~~~~
Basic Machine Learning
~~~~~~~~~~~~~~~~~~~~~~
.. raw:: html
<div align="left">
.. raw:: html
<a href="https://github.com/instillai/TensorFlow-Course/blob/master/_img/mainpage/basicmodels.gif" target="_blank">
<img width="250" height="250" align="center" src="https://github.com/instillai/TensorFlow-Course/blob/master/_img/mainpage/basicmodels.gif"/>
</a>
.. raw:: html
</div>
.. raw:: html
<br>
.. .. image:: _img/mainpage/basicmodels.gif
.. :height: 100px
.. :width: 200 px
.. :scale: 50 %
.. :alt: alternate text
.. :align: right
.. _ipythonlinearreg: codes/ipython/basics_in_machine_learning/linearregression.ipynb
.. _pythonlinearreg: codes/python/basics_in_machine_learning/linearregression.py
.. _tutoriallinearreg: https://www.machinelearningmindset.com/linear-regression-with-tensorflow/
.. _videoinearreg: https://youtu.be/2RTBBiKKuLI
.. _tutorialdataaugmentation: https://www.machinelearningmindset.com/data-augmentation-with-tensorflow/
.. _ipythondataaugmentation: https://github.com/instillai/TensorFlow-Course/blob/master/codes/ipython/basics_in_machine_learning/dataaugmentation.ipynb
.. _pythondataaugmentation: https://github.com/instillai/TensorFlow-Course/blob/master/codes/python/basics_in_machine_learning/dataaugmentation.py
.. _videodataaugmentation: https://youtu.be/HbzR2snHJF0
.. |lr| image:: https://colab.research.google.com/assets/colab-badge.svg
:target: https://colab.research.google.com/github/instillai/TensorFlow-Course/blob/master/codes/ipython/basics_in_machine_learning/linearregression.ipynb
.. |da| image:: https://colab.research.google.com/assets/colab-badge.svg
:target: https://colab.research.google.com/github/instillai/TensorFlow-Course/blob/master/codes/ipython/basics_in_machine_learning/dataaugmentation.ipynb
+----+-----------------------------------+--------------------------+------------------------------------------------------------------------------------+----------------------------------------------+----------------------------------------------+
| # | topic | Run | Source Code | More | Media |
+====+===================================+==========================+====================================================================================+==============================================+==============================================+
| 1 | Linear Regression | |lr| | `Notebook <ipythonlinearreg_>`_ / `Python <pythonlinearreg_>`_ | `Tutorial <tutoriallinearreg_>`_ | `Video Tutorial <videoinearreg_>`_ |
+----+-----------------------------------+--------------------------+------------------------------------------------------------------------------------+----------------------------------------------+----------------------------------------------+
| 2 | Data Augmentation | |da| | `Notebook <ipythondataaugmentation_>`_ / `Python <pythondataaugmentation_>`_ | `Tutorial <tutorialdataaugmentation_>`_ | `Video Tutorial <videodataaugmentation_>`_ |
+----+-----------------------------------+--------------------------+------------------------------------------------------------------------------------+----------------------------------------------+----------------------------------------------+
.. +----+----------------------------+----------------------------------------------------------------------------------------+----------------------------------------------+
==========================
~~~~~~~~~~~~~~~~
Neural Networks
~~~~~~~~~~~~~~~~
.. raw:: html
<div align="left">
.. raw:: html
<a href="https://github.com/instillai/TensorFlow-Course/blob/master/_img/mainpage/CNNs.png" target="_blank">
<img width="600" height="180" align="center" src="https://github.com/instillai/TensorFlow-Course/blob/master/_img/mainpage/CNNs.png"/>
</a>
.. raw:: html
</div>
.. raw:: html
<br>
.. _ipythonmlp: https://github.com/instillai/TensorFlow-Course/blob/master/codes/ipython/neural_networks/mlp.ipynb
.. _pythonmlp: https://github.com/instillai/TensorFlow-Course/blob/master/codes/python/neural_networks/mlp.py
.. _videomlp: https://youtu.be/w20efZqSK2Y
.. _ipythoncnn: https://github.com/instillai/TensorFlow-Course/blob/master/codes/ipython/neural_networks/CNNs.ipynb
.. _pythoncnn: https://github.com/instillai/TensorFlow-Course/blob/master/codes/python/neural_networks/cnns.py
.. _videocnn: https://youtu.be/WVifZBCRz8g
.. |mlp| image:: https://colab.research.google.com/assets/colab-badge.svg
:target: https://colab.research.google.com/github/instillai/TensorFlow-Course/blob/master/codes/ipython/neural_networks/mlp.ipynb
.. |cnn| image:: https://colab.research.google.com/assets/colab-badge.svg
:target: https://colab.research.google.com/github/instillai/TensorFlow-Course/blob/master/codes/ipython/neural_networks/CNNs.ipynb
+----+------------------------------------------+--------------------------+------------------------------------------------------+------------------------------------+
| # | topic | Run | Source Code | Media |
+====+==========================================+==========================+======================================================+====================================+
| 1 | *Multi Layer Perceptron* | |mlp| | `Notebook <ipythonmlp_>`_ / `Python <pythonmlp_>`_ | `Video Tutorial <videomlp_>`_ |
+----+------------------------------------------+--------------------------+------------------------------------------------------+------------------------------------+
| 2 | *Convolutional Neural Networks* | |cnn| | `Notebook <ipythoncnn_>`_ / `Python <pythoncnn_>`_ | `Video Tutorial <videocnn_>`_ |
+----+------------------------------------------+--------------------------+------------------------------------------------------+------------------------------------+
==========================
~~~~~~~~~~~~~~~~
Advanced
~~~~~~~~~~~~~~~~
.. raw:: html
<div align="left">
.. raw:: html
<a href="https://github.com/instillai/TensorFlow-Course/blob/master/_img/mainpage/Build.png" target="_blank">
<img width="180" height="180" align="center" src="https://github.com/instillai/TensorFlow-Course/blob/master/_img/mainpage/Build.png"/>
</a>
.. raw:: html
</div>
.. raw:: html
<br>
.. _ipythoncustomtr: https://github.com/instillai/TensorFlow-Course/blob/master/codes/ipython/advanced/custom_training.ipynb
.. _pythoncustomtr: https://github.com/instillai/TensorFlow-Course/blob/master/codes/python/advanced/custom_training.py
.. _videocustomtr: https://youtu.be/z5gcabfyPfA
.. _ipythondgenerator: https://github.com/instillai/TensorFlow-Course/blob/master/codes/ipython/advanced/dataset_generator.ipynb
.. _pythondgenerator: https://github.com/instillai/TensorFlow-Course/blob/master/codes/python/advanced/dataset_generator.py
.. _videodgenerator: https://youtu.be/-YsgMdDPu3g
.. _ipythontfrecords: https://github.com/instillai/TensorFlow-Course/blob/master/codes/ipython/advanced/tfrecords.ipynb
.. _pythontfrecords: https://github.com/instillai/TensorFlow-Course/blob/master/codes/python/advanced/tfrecords.py
.. _videotfrecords: https://youtu.be/zqavy_5QMk8
.. |ctraining| image:: https://colab.research.google.com/assets/colab-badge.svg
:target: https://colab.research.google.com/github/instillai/TensorFlow-Course/blob/master/codes/ipython/advanced/custom_training.ipynb
.. |dgenerator| image:: https://colab.research.google.com/assets/colab-badge.svg
:target: https://colab.research.google.com/github/instillai/TensorFlow-Course/blob/master/codes/ipython/advanced/dataset_generator.ipynb
.. |tfrecords| image:: https://colab.research.google.com/assets/colab-badge.svg
:target: https://colab.research.google.com/github/instillai/TensorFlow-Course/blob/master/codes/ipython/advanced/tfrecords.ipynb
+----+------------------------------------------+--------------------------+--------------------------------------------------------------------+----------------------------------------+
| # | topic | Run | Source Code | Media |
+====+==========================================+==========================+====================================================================+========================================+
| 1 | *Custom Training* | |ctraining| | `Notebook <ipythoncustomtr_>`_ / `Python <pythoncustomtr_>`_ | `Video Tutorial <videocustomtr_>`_ |
+----+------------------------------------------+--------------------------+--------------------------------------------------------------------+----------------------------------------+
| 2 | *Dataset Generator* | |dgenerator| | `Notebook <ipythondgenerator_>`_ / `Python <pythondgenerator_>`_ | `Video Tutorial <videodgenerator_>`_ |
+----+------------------------------------------+--------------------------+--------------------------------------------------------------------+----------------------------------------+
| 3 | *Create TFRecords* | |tfrecords| | `Notebook <ipythontfrecords_>`_ / `Python <pythontfrecords_>`_ | `Video Tutorial <videotfrecords_>`_ |
+----+------------------------------------------+--------------------------+--------------------------------------------------------------------+----------------------------------------+
=====================
Some Useful Tutorials
=====================
* `TensorFlow Examples <https://github.com/aymericdamien/TensorFlow-Examples>`_ - TensorFlow tutorials and code examples for beginners
* `Sungjoon's TensorFlow-101 <https://github.com/sjchoi86/Tensorflow-101>`_ - TensorFlow tutorials written in Python with Jupyter Notebook
* `Terry Um’s TensorFlow Exercises <https://github.com/terryum/TensorFlow_Exercises>`_ - Re-create the codes from other TensorFlow examples
* `Classification on time series <https://github.com/guillaume-chevalier/LSTM-Human-Activity-Recognition>`_ - Recurrent Neural Network classification in TensorFlow with LSTM on cellphone sensor data
=============
Contributing
=============
When contributing to this repository, please first discuss the change you wish to make via issue,
email, or any other method with the owners of this repository before making a change. *For typos, please
do not create a pull request. Instead, declare them in issues or email the repository owner*.
Please note we have a code of conduct, please follow it in all your interactions with the project.
~~~~~~~~~~~~~~~~~~~~
Pull Request Process
~~~~~~~~~~~~~~~~~~~~
Please consider the following criterions in order to help us in a better way:
* The pull request is mainly expected to be a code script suggestion or improvement.
* Please do NOT change the ipython files. Instead, change the corresponsing PYTHON files.
* A pull request related to non-code-script sections is expected to make a significant difference in the documentation. Otherwise, it is expected to be announced in the issues section.
* Ensure any install or build dependencies are removed before the end of the layer when doing a build and creating a pull request.
* Add comments with details of changes to the interface, this includes new environment variables, exposed ports, useful file locations and container parameters.
* You may merge the Pull Request in once you have the sign-off of at least one other developer, or if you do not have permission to do that, you may request the owner to merge it for you if you believe all checks are passed.
~~~~~~~~~~~
Final Note
~~~~~~~~~~~
We are looking forward to your kind feedback. Please help us to improve this open source project and make our work better.
For contribution, please create a pull request and we will investigate it promptly. Once again, we appreciate
your kind feedback and elaborate code inspections.
========================
Developers
========================
**Company**: Instill AI [`Website
<https://instillai.com/>`_]
**Creator**: Machine Learning Mindset [`Blog
<https://machinelearningmindset.com/blog/>`_, `GitHub
<https://github.com/machinelearningmindset>`_, `Twitter
<https://twitter.com/machinemindset>`_]
**Developer**: Amirsina Torfi [`GitHub
<https://github.com/astorfi>`_, `Personal Website
<https://astorfi.github.io/>`_, `Linkedin
<https://www.linkedin.com/in/amirsinatorfi/>`_ ]
================================================
FILE: _img/1-basics/readme.rst
================================================
==============================
Basics
==============================
================================================
FILE: codes/ipython/0-welcome/welcome.ipynb
================================================
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"name": "welcome.ipynb",
"provenance": [],
"collapsed_sections": []
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"accelerator": "GPU"
},
"cells": [
{
"cell_type": "code",
"metadata": {
"id": "7i1UqqIkNxAt",
"outputId": "2f9c0fc3-ba1a-499d-f4ea-0bfe160ae841",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 129
}
},
"source": [
"# Import tensorflow\n",
"import tensorflow as tf\n",
"\n",
"# Check version\n",
"print(\"Tensorflow version: \", tf.__version__)\n",
"\n",
"# Test TensorFlow for cuda availibility\n",
"print(\"Tensorflow is built with CUDA: \", tf.test.is_built_with_cuda())\n",
"\n",
"# Check devices\n",
"print(\"All devices: \", tf.config.list_physical_devices(device_type=None))\n",
"print(\"GPU devices: \", tf.config.list_physical_devices(device_type='GPU'))\n",
"\n",
"# Print a randomly generated tensor\n",
"# tf.math.reduce_sum: https://www.tensorflow.org/api_docs/python/tf/math/reduce_sum\n",
"# tf.random.normal: https://www.tensorflow.org/api_docs/python/tf/random/normal\n",
"print(tf.math.reduce_sum(tf.random.normal([1, 10])))\n"
],
"execution_count": 2,
"outputs": [
{
"output_type": "stream",
"text": [
"Tensorflow version: 2.3.0\n",
"Tensorflow is built with CUDA: True\n",
"All devices: [PhysicalDevice(name='/physical_device:CPU:0', device_type='CPU'), PhysicalDevice(name='/physical_device:XLA_CPU:0', device_type='XLA_CPU'), PhysicalDevice(name='/physical_device:XLA_GPU:0', device_type='XLA_GPU'), PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU')]\n",
"GPU devices: [PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU')]\n",
"tf.Tensor(-4.315793, shape=(), dtype=float32)\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "rcU8_F3fPUb5"
},
"source": [
""
],
"execution_count": null,
"outputs": []
}
]
}
================================================
FILE: codes/ipython/1-basics/automatic_differentiation.ipynb
================================================
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"name": "automatic_differentiation.ipynb",
"provenance": [],
"collapsed_sections": []
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "MDevcewD85Im"
},
"source": [
"## Automatic Differentiation\n",
"\n",
"The [automatic differentiation](https://en.wikipedia.org/wiki/Automatic_differentiation) is to calculate derivative of functions which is useful for algorithms such as [stochastic gradient descent](https://en.wikipedia.org/wiki/Stochastic_gradient_descent).\n",
"\n",
"It's is particularly useful when we implement neural networks and desire to calculate differentiation of the output with respect to an input that are connected with a **chain of functions**:\n",
"\n",
"$L(x)=f(g(h(x)))$\n",
"\n",
"The differentiation is as below:\n",
"\n",
"$\\frac{dL}{dx} = \\frac{df}{dg}\\frac{dg}{dh}\\frac{dh}{dx}$\n",
"\n",
"The above rule is called the [chain rule](https://en.wikipedia.org/wiki/Chain_rule).\n",
"\n",
"So the [gradients](https://en.wikipedia.org/wiki/Gradient) needs to be calculated for ultimate derivative calculations.\n",
"\n",
"Let's see how TensorFlow does it!"
]
},
{
"cell_type": "code",
"metadata": {
"id": "iJoLp_aUvBFR"
},
"source": [
"# Loading necessary libraries\n",
"import tensorflow as tf\n",
"import numpy as np"
],
"execution_count": 1,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "w9fp4we9FWQU"
},
"source": [
"### Introduction\n",
"\n",
"Some general information are useful to be addressed here:\n",
"\n",
"* To compute gradients, TensorFlow uses [tf.GradientTape](https://www.tensorflow.org/api_docs/python/tf/GradientTape) which records the operation for later being used for gradient computation.\n",
"\n",
"Let's have three similar example:"
]
},
{
"cell_type": "code",
"metadata": {
"id": "26FVM1x8F-k0",
"outputId": "556b8873-8aab-4525-eb58-30053330f99b",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 54
}
},
"source": [
"x = tf.constant([2.0])\n",
"\n",
"with tf.GradientTape(persistent=False, watch_accessed_variables=True) as grad:\n",
" f = x ** 2\n",
"\n",
"# Print gradient output\n",
"print('The gradient df/dx where f=(x^2):\\n', grad.gradient(f, x))"
],
"execution_count": 11,
"outputs": [
{
"output_type": "stream",
"text": [
"The gradient df/dx where f=(x^2):\n",
" None\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "y_UO_8xnG6SY",
"outputId": "fcc2f51b-8bf4-4ee3-c6a6-9180a7a94d90",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 54
}
},
"source": [
"x = tf.constant([2.0])\n",
"x = tf.Variable(x)\n",
"\n",
"with tf.GradientTape(persistent=False, watch_accessed_variables=True) as grad:\n",
" f = x ** 2\n",
"\n",
"# Print gradient output\n",
"print('The gradient df/dx where f=(x^2):\\n', grad.gradient(f, x))"
],
"execution_count": 12,
"outputs": [
{
"output_type": "stream",
"text": [
"The gradient df/dx where f=(x^2):\n",
" tf.Tensor([4.], shape=(1,), dtype=float32)\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "s2SxqhpeHab7",
"outputId": "5c0be66e-171b-400b-8d55-d80fde11c7fd",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 54
}
},
"source": [
"x = tf.constant([2.0])\n",
"\n",
"with tf.GradientTape(persistent=False, watch_accessed_variables=True) as grad:\n",
" grad.watch(x)\n",
" f = x ** 2\n",
"\n",
"# Print gradient output\n",
"print('The gradient df/dx where f=(x^2):\\n', grad.gradient(f, x))"
],
"execution_count": 13,
"outputs": [
{
"output_type": "stream",
"text": [
"The gradient df/dx where f=(x^2):\n",
" tf.Tensor([4.], shape=(1,), dtype=float32)\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "7TptydLkH2hh"
},
"source": [
"What's the difference between above examples?\n",
"\n",
"1. Using tf.Variable on top of the tensor to transform it into a [tf.Variable](https://www.tensorflow.org/guide/variable).\n",
"2. Using [.watch()](https://www.tensorflow.org/api_docs/python/tf/GradientTape#watch) operation.\n",
"\n",
"The tf.Variable turn tensor to a variable tensor which is the recommended approach by TensorFlow. The .watch() method ensures the variable is being tracked by the tf.GradientTape(). \n",
"\n",
"**You can see if we use neither, we get NONE as the gradient which means gradients were not being tracked!**\n",
"\n",
"NOTE: In general it's always safe to work with variable as well as using .watch() to ensure tracking gradients.\n",
"\n",
"We used default arguments as:\n",
"\n",
"1. **persistent=False**: It says, any variable that is hold with tf.GradientTape(), after one calling of gradient will be released. \n",
"2. **watch_accessed_variables=True**: By default watching variables. So if we have a variable, we do not need to use .watch() with this default setting.\n",
"\n",
"Let's have an example with **persistent=True**:\n"
]
},
{
"cell_type": "code",
"metadata": {
"id": "V-ugyJGRHc7S",
"outputId": "a4813975-128b-4ad2-ad35-5c7270060ed1",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 90
}
},
"source": [
"x = tf.constant([2.0])\n",
"x = tf.Variable(x)\n",
"\n",
"# For practice, turn persistent to False to see what happens.\n",
"with tf.GradientTape(persistent=True, watch_accessed_variables=True) as grad:\n",
" f = x ** 2\n",
" h = x ** 3\n",
"\n",
"# Print gradient output\n",
"print('The gradient df/dx where f=(x^2):\\n', grad.gradient(f, x))\n",
"print('The gradient dh/dx where h=(x^3):\\n', grad.gradient(h, x))"
],
"execution_count": 16,
"outputs": [
{
"output_type": "stream",
"text": [
"The gradient df/dx where f=(x^2):\n",
" tf.Tensor([4.], shape=(1,), dtype=float32)\n",
"The gradient dh/dx where h=(x^3):\n",
" tf.Tensor([12.], shape=(1,), dtype=float32)\n"
],
"name": "stdout"
}
]
}
]
}
================================================
FILE: codes/ipython/1-basics/graph.ipynb
================================================
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"name": "graph.ipynb",
"provenance": [],
"collapsed_sections": []
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "MDevcewD85Im"
},
"source": [
"## Intorduction to TensorFlow graphs.\n",
"\n",
"For long, the big complaint about TensorFlow was *It's not flexible for debugging!* With the advent of TensorFlow 2.0, that changed drastically.\n",
"\n",
"Now TensorFlow allows you to run the oprations **eagerly**. That means, you can run TensorFlow operations by Python and return the outputs to Python again. That creates a lot of flexibility, especially for debugging. \n",
"\n",
"But there are some merits in NOT using the eagerly option. You can run operations on TensorFlow graphs that in some scenarios leads to significant speed up. According to TensorFlow:\n",
"\n",
"> Graphs are data structures that contain a set of [tf.Operation](https://www.tensorflow.org/api_docs/python/tf/Operation) objects, which represent units of computation; and [tf.Tensor](https://www.tensorflow.org/api_docs/python/tf/Tensor) objects, which represent the units of data that flow between operations. They are defined in a [tf.Graph](https://www.tensorflow.org/api_docs/python/tf/Graph) context. Since these graphs are data structures, they can be saved, run, and restored all without the original Python code.\n",
"\n",
"Let's have some example for transforming functions to graphs!\n"
]
},
{
"cell_type": "code",
"metadata": {
"id": "iJoLp_aUvBFR"
},
"source": [
"# Loading necessary libraries\n",
"import tensorflow as tf\n",
"import numpy as np\n",
"import timeit"
],
"execution_count": 1,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "8a9c8qUj-MSj"
},
"source": [
"### Operation\n",
"\n",
"We can take a Python function on graph with [@tf.function](https://www.tensorflow.org/api_docs/python/tf/function) decorator."
]
},
{
"cell_type": "code",
"metadata": {
"id": "V-ugyJGRHc7S",
"outputId": "0bc0f944-4f6b-4fc2-95e2-8f517141cac6",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 54
}
},
"source": [
"@tf.function\n",
"def multiply_fn(a, b):\n",
" return tf.matmul(a, b)\n",
"\n",
"# Create some tensors\n",
"a = tf.constant([[0.5, 0.5]])\n",
"b = tf.constant([[10.0], [1.0]])\n",
"\n",
"# Check function\n",
"print('Multiple a of shape {} with b of shape {}'.format(a.shape, b.shape))\n",
"print(multiply_fn(a, b).numpy())\n"
],
"execution_count": 2,
"outputs": [
{
"output_type": "stream",
"text": [
"Multiple a of shape (1, 2) with b of shape (2, 1)\n",
"[[5.5]]\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "zoh1CmJ59n-N",
"outputId": "addb1205-94a9-4f7b-d5b1-882ebed24a8a",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 72
}
},
"source": [
"# Function without neing take to graph, i.e., with eager execution.\n",
"def add_fn(a, b):\n",
" return tf.add(a, b)\n",
"\n",
"# Create some tensors\n",
"a = tf.constant([[0.5, 0.5]])\n",
"b = tf.constant([[10.0], [1.0]])\n",
"\n",
"# Check function\n",
"print('Add a of shape {} with b of shape {}'.format(a.shape, b.shape))\n",
"print(add_fn(a, b).numpy())"
],
"execution_count": 3,
"outputs": [
{
"output_type": "stream",
"text": [
"Add a of shape (1, 2) with b of shape (2, 1)\n",
"[[10.5 10.5]\n",
" [ 1.5 1.5]]\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "j-WywaJU_MXR"
},
"source": [
"### Speedup\n",
"\n",
"Now let's define a custom model and run it:\n",
"\n",
"1. eagerly\n",
"2. on graph\n",
"\n",
"To check how to define models refer to: https://www.tensorflow.org/api_docs/python/tf/keras/Model"
]
},
{
"cell_type": "code",
"metadata": {
"id": "ynGAYcDC-y2W",
"outputId": "98d5fb24-d34b-4c53-85a9-b7456cfca4da",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 90
}
},
"source": [
"class ModelShallow(tf.keras.Model):\n",
"\n",
" def __init__(self):\n",
" super(ModelShallow, self).__init__()\n",
" self.dense1 = tf.keras.layers.Dense(10, activation=tf.nn.relu)\n",
" self.dense2 = tf.keras.layers.Dense(20, activation=tf.nn.relu)\n",
" self.dense3 = tf.keras.layers.Dense(30, activation=tf.nn.softmax)\n",
" self.dropout = tf.keras.layers.Dropout(0.5)\n",
"\n",
" def call(self, inputs, training=False):\n",
" x = self.dense1(inputs)\n",
" if training:\n",
" x = self.dropout(x, training=training)\n",
" x = self.dense2(x)\n",
" out = self.dense3(x)\n",
" return out\n",
"\n",
"class ModelDeep(tf.keras.Model):\n",
"\n",
" def __init__(self):\n",
" super(ModelDeep, self).__init__()\n",
" self.dense1 = tf.keras.layers.Dense(1000, activation=tf.nn.relu)\n",
" self.dense2 = tf.keras.layers.Dense(2000, activation=tf.nn.relu)\n",
" self.dense3 = tf.keras.layers.Dense(3000, activation=tf.nn.softmax)\n",
" self.dropout = tf.keras.layers.Dropout(0.5)\n",
"\n",
" def call(self, inputs, training=False):\n",
" x = self.dense1(inputs)\n",
" if training:\n",
" x = self.dropout(x, training=training)\n",
" x = self.dense2(x)\n",
" out = self.dense3(x)\n",
" return out\n",
"\n",
"# Create the model with eager esxecution by default\n",
"model_shallow_with_eager = ModelShallow()\n",
"\n",
"# Take model to graph. \n",
"# NOTE: Instead of using decorators, we can ditectly operate tf.function on the model.\n",
"model_shallow_on_graph = tf.function(ModelShallow())\n",
"\n",
"# Model deep\n",
"model_deep_with_eager = ModelDeep()\n",
"model_deep_on_graph = tf.function(ModelDeep())\n",
"\n",
"# sample input\n",
"sample_input = tf.random.uniform([60, 28, 28])\n",
"\n",
"# Check time for shallow model\n",
"print(\"Shallow Model - Eager execution time:\", timeit.timeit(lambda: model_shallow_with_eager(sample_input), number=1000))\n",
"print(\"Shallow Model - Graph-based execution time:\", timeit.timeit(lambda: model_shallow_on_graph(sample_input), number=1000))\n",
"\n",
"# Check time for deep model\n",
"print(\"Deep Model - Eager execution time:\", timeit.timeit(lambda: model_deep_with_eager(sample_input), number=1000))\n",
"print(\"Deep Model - Graph-based execution time:\", timeit.timeit(lambda: model_deep_on_graph(sample_input), number=1000))"
],
"execution_count": 4,
"outputs": [
{
"output_type": "stream",
"text": [
"Shallow Model - Eager execution time: 2.758659444999921\n",
"Shallow Model - Graph-based execution time: 1.1618621510001503\n",
"Deep Model - Eager execution time: 477.634194022\n",
"Deep Model - Graph-based execution time: 460.01053104599987\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "oBvht7tVAX4I"
},
"source": [
""
],
"execution_count": 4,
"outputs": []
}
]
}
================================================
FILE: codes/ipython/1-basics/models.ipynb
================================================
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"name": "models.ipynb",
"provenance": [],
"collapsed_sections": []
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "MDevcewD85Im"
},
"source": [
"## Models in TensorFlow\n",
"\n",
"In TensorFlow, you always need to define models to train a machine learning model. A model consists of layers that conduct operations and can be reused in the model's structure. Let's get started.\n"
]
},
{
"cell_type": "code",
"metadata": {
"id": "iJoLp_aUvBFR"
},
"source": [
"# Loading necessary libraries\n",
"import tensorflow as tf\n",
"import numpy as np"
],
"execution_count": 1,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "t2tybyJb7Vdf"
},
"source": [
"### Layer\n",
"\n",
"In TensorFlow, we can implement layers using the high-level [tf.Module](https://www.tensorflow.org/api_docs/python/tf/Module) class."
]
},
{
"cell_type": "code",
"metadata": {
"id": "oBvht7tVAX4I",
"outputId": "820d740e-87b7-499e-cea0-f949c4e3bb4f",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 72
}
},
"source": [
"class SampleLayer(tf.Module):\n",
" \"\"\"\n",
" We define the layer with a class that inherited the structure of tf.Module class.\n",
" \"\"\"\n",
" def __init__(self, name=None):\n",
" super().__init__(name=name)\n",
"\n",
" # Define a trainable variable\n",
" self.x = tf.Variable([[1.0, 3.0]], name=\"x_trainable\")\n",
"\n",
" # Define a non-trainable variable\n",
" self.y = tf.Variable(2.0, trainable=False, name=\"y_non_trainable\")\n",
" def __call__(self, input):\n",
" return self.x * input + self.y\n",
"\n",
"# Initialize the layer\n",
"# Here, __call__ function will not be called\n",
"simple_layer = SampleLayer(name=\"my_layer\")\n",
"\n",
"# Call the layer and extract some information\n",
"output = simple_layer(tf.constant(1.0))\n",
"print(\"Output:\", output)\n",
"print(\"Layer name:\", simple_layer.name)\n",
"print(\"Trainable variables:\", simple_layer.trainable_variables)"
],
"execution_count": 2,
"outputs": [
{
"output_type": "stream",
"text": [
"Output: tf.Tensor([[3. 5.]], shape=(1, 2), dtype=float32)\n",
"Layer name: my_layer\n",
"Trainable variables: (<tf.Variable 'x_trainable:0' shape=(1, 2) dtype=float32, numpy=array([[1., 3.]], dtype=float32)>,)\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "DFVkXDUK9tlj"
},
"source": [
"### Model\n",
"\n",
"Now. let's define a model. A model consists of multiple layers."
]
},
{
"cell_type": "code",
"metadata": {
"id": "cp01Jsqg84ps",
"outputId": "4107ee01-08c0-4617-fad7-8d1183bfef26",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 92
}
},
"source": [
"class Model(tf.Module):\n",
" def __init__(self, name=None):\n",
" super().__init__(name=name)\n",
"\n",
" self.layer_1 = SampleLayer('layer_1')\n",
" self.layer_2 = SampleLayer('layer_2')\n",
"\n",
" def __call__(self, x):\n",
" x = self.layer_1(x)\n",
" output = self.layer_2(x)\n",
" return output\n",
"\n",
"# Initialize the model\n",
"custom_model = Model(name=\"model_name\")\n",
"\n",
"# Call the model\n",
"# Call the layer and extract some information\n",
"output = custom_model(tf.constant(1.0))\n",
"print(\"Output:\", output)\n",
"print(\"Model name:\", custom_model.name)\n",
"print(\"Trainable variables:\", custom_model.trainable_variables)\n"
],
"execution_count": 3,
"outputs": [
{
"output_type": "stream",
"text": [
"Output: tf.Tensor([[ 5. 17.]], shape=(1, 2), dtype=float32)\n",
"Model name: model_name\n",
"Trainable variables: (<tf.Variable 'x_trainable:0' shape=(1, 2) dtype=float32, numpy=array([[1., 3.]], dtype=float32)>, <tf.Variable 'x_trainable:0' shape=(1, 2) dtype=float32, numpy=array([[1., 3.]], dtype=float32)>)\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "jdpeI9wg-65r"
},
"source": [
"### Keras Models\n",
"\n",
"Keras is a high-level API that is part of TensorFlow now. You can use [tf.keras.Model](https://www.tensorflow.org/api_docs/python/tf/keras/Model) to define a model. You can also use the collection of [tf.keras.layers](https://www.tensorflow.org/api_docs/python/tf/keras/layers) for your convenience. It's straightforward as below to define a model that has two fully-connected layers:"
]
},
{
"cell_type": "code",
"metadata": {
"id": "_D3TjQXm-p3y",
"outputId": "59f662e1-fc73-4d39-c152-7c40d746b2f5",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 72
}
},
"source": [
"class CustomModel(tf.keras.Model):\n",
"\n",
" def __init__(self):\n",
" super(CustomModel, self).__init__()\n",
" self.layer_1 = tf.keras.layers.Dense(16, activation=tf.nn.relu)\n",
" self.layer_2 = tf.keras.layers.Dense(32, activation=None)\n",
"\n",
" def call(self, inputs):\n",
" x = self.layer_1(inputs)\n",
" out = self.layer_2(inputs)\n",
" return out\n",
"\n",
"# Create model\n",
"custom_model = CustomModel()\n",
"\n",
"# Call the model\n",
"# Call the layer and extract some information\n",
"output = custom_model(tf.constant([[1.0, 2.0, 3.0]]))\n",
"print(\"Output shape:\", output.shape)\n",
"print(\"Model name:\", custom_model.name)\n",
"\n",
"# Count total trainable variables\n",
"total_trainable_var = np.sum([tf.size(var).numpy() for var in custom_model.trainable_variables])\n",
"print(\"Number of trainable variables:\", total_trainable_var)"
],
"execution_count": 4,
"outputs": [
{
"output_type": "stream",
"text": [
"Output shape: (1, 32)\n",
"Model name: custom_model\n",
"Number of trainable variables: 192\n"
],
"name": "stdout"
}
]
}
]
}
================================================
FILE: codes/ipython/1-basics/tensors.ipynb
================================================
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"name": "tensors.ipynb",
"provenance": [],
"collapsed_sections": []
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
},
"accelerator": "GPU"
},
"cells": [
{
"cell_type": "code",
"metadata": {
"id": "7i1UqqIkNxAt"
},
"source": [
"# Import necessary libraries\n",
"import tensorflow as tf\n",
"import numpy as np"
],
"execution_count": 1,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "I4YPHO9ba3Bc"
},
"source": [
"## Tensors\n",
"\n",
"Tensor are multi-dimensitonal arrays that are used in Tensorflow.\n",
"\n",
"We use the following definition:\n",
"\n",
"* **Rank:** The number of dimensions that a vector has.\n",
"\n",
"Below, we will define different kinds of tensors and show their rank using [tf.rank](https://www.tensorflow.org/api_docs/python/tf/rank) function."
]
},
{
"cell_type": "code",
"metadata": {
"id": "rcU8_F3fPUb5",
"outputId": "9f9a8970-4e6b-4550-f90f-64acf7d372b4",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"source": [
"tensor = tf.constant(0)\n",
"print(\"Print constant tensor {} of rank {}\".format(tensor, tf.rank(tensor)))\n",
"print(\"Show full tensor:\", tensor)"
],
"execution_count": 2,
"outputs": [
{
"output_type": "stream",
"text": [
"Print constant tensor 0 of rank 0\n",
"Show full tensor: tf.Tensor(0, shape=(), dtype=int32)\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "ahIBf6_4cRnm",
"outputId": "b716e303-7c30-4bc5-84d0-f9e0142b710d",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"source": [
"# NOTE: We use .numpy() to transform tf.tensor to numpy\n",
"tensor = tf.constant([1,2,3])\n",
"print(\"Tensor:\", tensor)\n",
"print(\"Rank:\", tf.rank(tensor).numpy())"
],
"execution_count": 3,
"outputs": [
{
"output_type": "stream",
"text": [
"Tensor: tf.Tensor([1 2 3], shape=(3,), dtype=int32)\n",
"Rank: 1\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "ss3aDmDTd-LS"
},
"source": [
"### Tensor Operations"
]
},
{
"cell_type": "code",
"metadata": {
"id": "TKX2U0Imcm7d",
"outputId": "7ea93f1e-a98b-418d-8f9c-5f117e1405b2",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"source": [
"x = tf.constant([[1, 1],\n",
" [1, 1]])\n",
"y = tf.constant([[2, 4],\n",
" [6, 8]])\n",
"\n",
"# Add two tensors\n",
"print(tf.add(x, y), \"\\n\")\n",
"\n",
"# Add two tensors\n",
"print(tf.matmul(x, y), \"\\n\")\n"
],
"execution_count": 4,
"outputs": [
{
"output_type": "stream",
"text": [
"tf.Tensor(\n",
"[[3 5]\n",
" [7 9]], shape=(2, 2), dtype=int32) \n",
"\n",
"tf.Tensor(\n",
"[[ 8 12]\n",
" [ 8 12]], shape=(2, 2), dtype=int32) \n",
"\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "BlEgQ2t2edKl"
},
"source": [
"### Muti-dimentional Tensors\n",
"\n",
"This part is not much different compared to what we learned so far. However, it would be nice to try extracting as much information as we can from a multi-dimentional tensor.\n",
"\n",
"\n",
"Let's use [tf.ones](https://www.tensorflow.org/api_docs/python/tf/ones) for our purpose here. It creates an all-one tensor."
]
},
{
"cell_type": "code",
"metadata": {
"id": "Gdtt0e4-fDkl",
"outputId": "c59185ab-84f1-4e02-d327-255df4cb2b1f",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"source": [
"# We set the shape of the tensor and the desired data type.\n",
"tensor = tf.ones(shape = [2, 3, 6], dtype = tf.float32)\n",
"print('Tensor:', tensor)"
],
"execution_count": 5,
"outputs": [
{
"output_type": "stream",
"text": [
"Tensor: tf.Tensor(\n",
"[[[1. 1. 1. 1. 1. 1.]\n",
" [1. 1. 1. 1. 1. 1.]\n",
" [1. 1. 1. 1. 1. 1.]]\n",
"\n",
" [[1. 1. 1. 1. 1. 1.]\n",
" [1. 1. 1. 1. 1. 1.]\n",
" [1. 1. 1. 1. 1. 1.]]], shape=(2, 3, 6), dtype=float32)\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "c5PChFhlfXmx",
"outputId": "15da08f5-98df-4b54-a580-f90881976b38",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"source": [
"print(\"Tensor Rank: \", tf.rank(tensor).numpy())\n",
"print(\"Shape: \", tensor.shape)\n",
"print(\"Elements' type\", tensor.dtype)\n",
"print(\"The size of the second axis:\", tensor.shape[1])\n",
"print(\"The size of the last axis:\", tensor.shape[-1])\n",
"print(\"Total number of elements: \", tf.size(tensor).numpy())\n",
"print(\"How many dimensions? \", tensor.ndim)"
],
"execution_count": 6,
"outputs": [
{
"output_type": "stream",
"text": [
"Tensor Rank: 3\n",
"Shape: (2, 3, 6)\n",
"Elements' type <dtype: 'float32'>\n",
"The size of the second axis: 3\n",
"The size of the last axis: 6\n",
"Total number of elements: 36\n",
"How many dimensions? 3\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "cnYwTBqPhW1I"
},
"source": [
"### Indexing\n",
"\n",
"TensorFlow indexing is aligned with Python indexing. See the following examples."
]
},
{
"cell_type": "code",
"metadata": {
"id": "34-Tfcsnf6uG"
},
"source": [
"x = tf.constant([[1, 2, 3],\n",
" [4, 5, 6],\n",
" [7, 8, 9]])"
],
"execution_count": 7,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "tNZhisXDhoLp",
"outputId": "5a955103-8ca5-496c-bee1-8828d437491a",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"source": [
"# All elements\n",
"print(x[:].numpy())"
],
"execution_count": 8,
"outputs": [
{
"output_type": "stream",
"text": [
"[[1 2 3]\n",
" [4 5 6]\n",
" [7 8 9]]\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "KUghwlZ7hr10",
"outputId": "bc6bac99-c1f7-4f16-bb53-7cea60feb1b1",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"source": [
"# All elements of the first row\n",
"print(x[0,:].numpy())"
],
"execution_count": 9,
"outputs": [
{
"output_type": "stream",
"text": [
"[1 2 3]\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "NSCMESaPhwnV",
"outputId": "71ce4701-7169-4538-f535-a5f22e5b9a1d",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"source": [
"# First row and last column\n",
"print(x[0,-1].numpy())"
],
"execution_count": 10,
"outputs": [
{
"output_type": "stream",
"text": [
"3\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "hH8Fhi2Sh2rt",
"outputId": "d2d85c95-df34-4cbf-cc74-f0f9214319ac",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"source": [
"# From second row to last and from third column to last\n",
"print(x[1:,2:].numpy)"
],
"execution_count": 11,
"outputs": [
{
"output_type": "stream",
"text": [
"<bound method _EagerTensorBase.numpy of <tf.Tensor: shape=(2, 1), dtype=int32, numpy=\n",
"array([[6],\n",
" [9]], dtype=int32)>>\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "Y_zEE3-7iUmu"
},
"source": [
"### Data types\n",
"\n",
"You can change the data type of the tesnorflow tensors for your purpose. This will be done easily by [tf.cast](https://www.tensorflow.org/api_docs/python/tf/cast)."
]
},
{
"cell_type": "code",
"metadata": {
"id": "mFsqRDxAiK95",
"outputId": "5f3aa9b1-b5c1-4fad-cd18-96e376b742d8",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"source": [
"original_tensor = tf.constant([1, 2, 3, 4], dtype=tf.int32)\n",
"print('Original tensor: ', original_tensor)\n",
"print(\"Tensor type before casting: \", original_tensor.dtype)\n",
"\n",
"# Casting to change dtype\n",
"casted_tensor = tf.cast(original_tensor, dtype=tf.float32)\n",
"print('New tensor: ', casted_tensor)\n",
"print(\"Tensor type after casting: \", casted_tensor.dtype)"
],
"execution_count": 12,
"outputs": [
{
"output_type": "stream",
"text": [
"Original tensor: tf.Tensor([1 2 3 4], shape=(4,), dtype=int32)\n",
"Tensor type before casting: <dtype: 'int32'>\n",
"New tensor: tf.Tensor([1. 2. 3. 4.], shape=(4,), dtype=float32)\n",
"Tensor type after casting: <dtype: 'float32'>\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "81XDYbnxi-nx"
},
"source": [
""
],
"execution_count": 12,
"outputs": []
}
]
}
================================================
FILE: codes/ipython/advanced/custom_training.ipynb
================================================
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"name": "custom_training.ipynb",
"provenance": [],
"collapsed_sections": []
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
}
},
"cells": [
{
"cell_type": "code",
"metadata": {
"id": "zkYRcDwhl1UW"
},
"source": [
"import tensorflow as tf\n",
"import numpy as np"
],
"execution_count": 1,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "CZqKCk9pl1rx",
"outputId": "0cf1105b-c501-4298-e313-7caaebd18f60",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"source": [
"# Load MNIST data\n",
"(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()\n",
"# Preprocessing\n",
"x_train = x_train / 255.0\n",
"x_test = x_test / 255.0\n",
"\n",
"# Add one domention to make 3D images\n",
"x_train = x_train[...,tf.newaxis]\n",
"x_test = x_test[...,tf.newaxis]\n",
"\n",
"# Track the data type\n",
"dataType, dataShape = x_train.dtype, x_train.shape\n",
"print(f\"Data type and shape x_train: {dataType} {dataShape}\")\n",
"labelType, labelShape = y_train.dtype, y_train.shape\n",
"print(f\"Data type and shape y_train: {labelType} {labelShape}\")"
],
"execution_count": 2,
"outputs": [
{
"output_type": "stream",
"text": [
"Data type and shape x_train: float64 (60000, 28, 28, 1)\n",
"Data type and shape y_train: uint8 (60000,)\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "6MbJ-XLIl4M2",
"outputId": "e9ae43c7-0d8d-43c6-9b48-76502bfc07f0",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 268
}
},
"source": [
"im_list = []\n",
"n_samples_to_show = 16\n",
"c = 0\n",
"for i in range(n_samples_to_show):\n",
" im_list.append(x_train[i])\n",
"# Visualization\n",
"import matplotlib.pyplot as plt\n",
"from mpl_toolkits.axes_grid1 import ImageGrid\n",
"fig = plt.figure(figsize=(4., 4.))\n",
"# Ref: https://matplotlib.org/3.1.1/gallery/axes_grid1/simple_axesgrid.html\n",
"grid = ImageGrid(fig, 111, # similar to subplot(111)\n",
" nrows_ncols=(4, 4), # creates 2x2 grid of axes\n",
" axes_pad=0.1, # pad between axes in inch.\n",
" )\n",
"# Show image grid\n",
"for ax, im in zip(grid, im_list):\n",
" # Iterating over the grid returns the Axes.\n",
" ax.imshow(im[:,:,0], 'gray')\n",
"plt.show()"
],
"execution_count": 3,
"outputs": [
{
"output_type": "display_data",
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 288x288 with 32 Axes>"
]
},
"metadata": {
"tags": [],
"needs_background": "light"
}
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "GuuBbYItqTMq"
},
"source": [
"batch_size = 32\n",
"# Prepare the training dataset.\n",
"train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))\n",
"train_dataset = train_dataset.shuffle(buffer_size=1024).batch(batch_size)\n",
"\n",
"# Prepare the validation dataset.\n",
"test_dataset = tf.data.Dataset.from_tensor_slices((x_test, y_test))\n",
"test_dataset = test_dataset.batch(batch_size)"
],
"execution_count": 4,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "Jbj9lFh1l4iL"
},
"source": [
"# Model building\n",
"NUM_CLASSES = 10\n",
"model = tf.keras.Sequential([\n",
" tf.keras.layers.Conv2D(16, (3, 3), activation='relu', input_shape=(28, 28, 1)),\n",
" tf.keras.layers.MaxPooling2D((2, 2)),\n",
" tf.keras.layers.Conv2D(32, (3, 3), activation='relu'),\n",
" tf.keras.layers.Flatten(),\n",
" tf.keras.layers.Dense(32, activation='relu'),\n",
" tf.keras.layers.Dense(NUM_CLASSES, activation='sigmoid')]\n",
" )"
],
"execution_count": 5,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "PldDIuQvl7iB"
},
"source": [
"# Defining loss function\n",
"loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False)\n",
"accuracy_metric = tf.keras.metrics.Accuracy()\n",
"\n",
"# Calculate loss\n",
"def loss_fn(gt_label, pred):\n",
" # training argument define the beehaviour of layers with respect\n",
" # to whether we are training the model or not. It is important for layers\n",
" # such as BatchNorm and Dropout.\n",
" return loss_object(y_true=gt_label, y_pred=pred)\n",
"\n",
"def accuracy_fn(gt_label, output):\n",
" # calculate the accuracy by turning output into labels with argmax\n",
" pred = tf.argmax(output, axis=1, output_type=tf.int32)\n",
" return accuracy_metric(pred, gt_label)\n"
],
"execution_count": 6,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "T1a1hEY6m0Ji"
},
"source": [
"# Define the optimizer\n",
"optimizer = tf.keras.optimizers.Adam(learning_rate=0.01)"
],
"execution_count": 7,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "k_2OZcLjnAhC",
"outputId": "3a6a00c8-7acf-426f-b4c6-6dda64e5a3df",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"source": [
"NUM_EPOCHS = 5\n",
"EPOCH_PER_DISPLAY = 1\n",
"total_loss = []\n",
"for epoch in range(NUM_EPOCHS):\n",
"\n",
" running_loss = []\n",
" running_accuracy = []\n",
"\n",
" # Training\n",
" for input, target in train_dataset:\n",
"\n",
" # Calculate and track graduents\n",
" with tf.GradientTape() as tape:\n",
"\n",
" # Calculate model output and loss\n",
" output = model(input, training=True)\n",
" loss_ = loss_fn(target, output)\n",
" accuracy_ = accuracy_fn(target, output)\n",
" \n",
" # Tape gradients\n",
" grads = tape.gradient(loss_, model.trainable_variables)\n",
" \n",
" # Track batch loss and accuracy\n",
" running_loss.append(loss_)\n",
" running_accuracy.append(accuracy_)\n",
"\n",
" # Optimize model based on the gradients\n",
" optimizer.apply_gradients(zip(grads, model.trainable_variables))\n",
" \n",
" # Epoch calculations\n",
" epoch_loss = np.mean(running_loss)\n",
" epoch_accuracy = np.mean(running_accuracy)\n",
" if (epoch + 1) % EPOCH_PER_DISPLAY == 0:\n",
" print(\"Epoch {}: Loss: {:.4f} Accuracy: {:.2f}%\".format(epoch+1, epoch_loss, epoch_accuracy * 100))\n"
],
"execution_count": 8,
"outputs": [
{
"output_type": "stream",
"text": [
"Epoch 1: Loss: 0.1450 Accuracy: 92.06%\n",
"Epoch 2: Loss: 0.0816 Accuracy: 96.28%\n",
"Epoch 3: Loss: 0.0834 Accuracy: 96.90%\n",
"Epoch 4: Loss: 0.0825 Accuracy: 97.21%\n",
"Epoch 5: Loss: 0.0784 Accuracy: 97.41%\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "Njorv726pcUW",
"outputId": "1ecef430-86ca-45db-f48d-479fc27372ed",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"source": [
"# Calculate the accurcy on the test set\n",
"running_accuracy = []\n",
"for (input, gt_label) in test_dataset:\n",
" output = model(input, training=False)\n",
" accuracy_ = accuracy_fn(gt_label, output)\n",
" running_accuracy.append(accuracy_)\n",
"\n",
"print(\"Test accuracy: {:.3%}\".format(np.mean(running_accuracy)))\n"
],
"execution_count": 9,
"outputs": [
{
"output_type": "stream",
"text": [
"Test accuracy: 97.509%\n"
],
"name": "stdout"
}
]
}
]
}
================================================
FILE: codes/ipython/advanced/dataset_generator.ipynb
================================================
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"name": "dataset_generator.ipynb",
"provenance": [],
"collapsed_sections": []
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
}
},
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "WyBjnWt7FXsl"
},
"source": [
"# Dataset generators\n",
"\n",
"In this advanced tutorials I demonstrate an efficient way of using the TensorFlow [tf.Data API](https://www.tensorflow.org/guide/data) to create a dataset. This approach has some important advantages:\n",
"\n",
"1. It provides a lot of flexibility in terms of using Python and packages such as NumPy to create a dataset.\n",
"2. By working with large databases, you can call the samples and shuffling **on-demand** which significantly reduce memory usage. In fact, memory won't be a bottleneck anymore. \n",
"\n",
"This will be done by [Python generator functions](https://www.tensorflow.org/guide/data#consuming_python_generators) to create [tf.data.Dataset](https://www.tensorflow.org/api_docs/python/tf/data/Dataset) objects. The process is as follows:\n",
"\n",
"1. By using a generator function, we dictate the way data must be generated.\n",
"2. By using [tf.data.Dataset.from_generator](https://www.tensorflow.org/api_docs/python/tf/data/Dataset#from_generator) method, we create the TensorFlow dataset."
]
},
{
"cell_type": "code",
"metadata": {
"id": "zkYRcDwhl1UW"
},
"source": [
"import tensorflow as tf\n",
"import numpy as np"
],
"execution_count": 1,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "CZqKCk9pl1rx",
"outputId": "15f0524b-0e36-4eb7-ce60-f6cfc3cd5f86",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"source": [
"# Load MNIST data\n",
"(x_train, y_train), (x_test, y_test) = tf.keras.datasets.mnist.load_data()\n",
"# Preprocessing\n",
"x_train = x_train / 255.0\n",
"x_test = x_test / 255.0\n",
"\n",
"# Add one domention to make 3D images\n",
"x_train = x_train[...,tf.newaxis]\n",
"x_test = x_test[...,tf.newaxis]\n",
"\n",
"# Track the data type\n",
"dataType, dataShape = x_train.dtype, x_train.shape\n",
"print(f\"Data type and shape x_train: {dataType} {dataShape}\")\n",
"labelType, labelShape = y_train.dtype, y_train.shape\n",
"print(f\"Data type and shape y_train: {labelType} {labelShape}\")"
],
"execution_count": 2,
"outputs": [
{
"output_type": "stream",
"text": [
"Data type and shape x_train: float64 (60000, 28, 28, 1)\n",
"Data type and shape y_train: uint8 (60000,)\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "3QYe8Uw0HEax"
},
"source": [
"## Generators\n",
"\n",
"Here, I define separate generators for train/test. The generator function, pick a random sample from the dataset at each step. This create a shuffled dataset without the need to use the [.shuffle()](https://www.tensorflow.org/api_docs/python/tf/data/Dataset#shuffle) method. Sometimes [.shuffle()](https://www.tensorflow.org/api_docs/python/tf/data/Dataset#shuffle) method can be very [memory consuming](https://www.tensorflow.org/guide/data_performance#reducing_memory_footprint)."
]
},
{
"cell_type": "code",
"metadata": {
"id": "RqDCX9dnCH7Z"
},
"source": [
"# Defining generator functions for train/test samples\n",
"TRAIN_LEN = x_train.shape[0]\n",
"def gen_pairs_train():\n",
" for i in range(TRAIN_LEN):\n",
" # Get a random image each time\n",
" idx = np.random.randint(0,TRAIN_LEN)\n",
" yield (x_train[idx], y_train[idx])\n",
"\n",
"\n",
"TEST_LEN = x_test.shape[0]\n",
"def gen_pairs_test():\n",
" for i in range(TEST_LEN):\n",
" # Get a random image each time\n",
" idx = np.random.randint(0,TEST_LEN)\n",
" yield (x_test[idx], y_test[idx])\n",
"\n",
"# Function to test input pipeline\n",
"sample_image, sample_label = next(gen_pairs_train())"
],
"execution_count": 3,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "CSoF5IsxH18W"
},
"source": [
"## Dataset creation\n",
"\n",
"Here I just used tf.data.Dataset.from_generator on top of the *gen_pairs_train()* and *gen_pairs_test()* generator functions."
]
},
{
"cell_type": "code",
"metadata": {
"id": "XTC4nXfTCd20"
},
"source": [
"batch_size = 32\n",
"# Prepare the training dataset.\n",
"train_dataset = tf.data.Dataset.from_generator(generator=gen_pairs_train, output_types=(tf.float64, tf.uint8))\n",
"train_dataset = train_dataset.batch(batch_size)\n",
"\n",
"# Prepare the validation dataset.\n",
"test_dataset = tf.data.Dataset.from_generator(generator=gen_pairs_test, output_types=(tf.float64, tf.uint8))\n",
"test_dataset = test_dataset.batch(batch_size)"
],
"execution_count": 4,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "6MbJ-XLIl4M2",
"outputId": "09ef4fbb-1b31-4812-f9f7-194002762620",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 268
}
},
"source": [
"im_list = []\n",
"n_samples_to_show = 16\n",
"c = 0\n",
"for i in range(n_samples_to_show):\n",
" img, label = next(gen_pairs_train())\n",
" im_list.append(img)\n",
"# Visualization\n",
"import matplotlib.pyplot as plt\n",
"from mpl_toolkits.axes_grid1 import ImageGrid\n",
"fig = plt.figure(figsize=(4., 4.))\n",
"# Ref: https://matplotlib.org/3.1.1/gallery/axes_grid1/simple_axesgrid.html\n",
"grid = ImageGrid(fig, 111, # similar to subplot(111)\n",
" nrows_ncols=(4, 4), # creates 2x2 grid of axes\n",
" axes_pad=0.1, # pad between axes in inch.\n",
" )\n",
"# Show image grid\n",
"for ax, im in zip(grid, im_list):\n",
" # Iterating over the grid returns the Axes.\n",
" ax.imshow(im[:,:,0], 'gray')\n",
"plt.show()"
],
"execution_count": 5,
"outputs": [
{
"output_type": "display_data",
"data": {
"image/png": "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\n",
"text/plain": [
"<Figure size 288x288 with 32 Axes>"
]
},
"metadata": {
"tags": [],
"needs_background": "light"
}
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "Jbj9lFh1l4iL"
},
"source": [
"# Model building\n",
"NUM_CLASSES = 10\n",
"model = tf.keras.Sequential([\n",
" tf.keras.layers.Conv2D(16, (3, 3), activation='relu', input_shape=(28, 28, 1)),\n",
" tf.keras.layers.MaxPooling2D((2, 2)),\n",
" tf.keras.layers.Conv2D(32, (3, 3), activation='relu'),\n",
" tf.keras.layers.Flatten(),\n",
" tf.keras.layers.Dense(32, activation='relu'),\n",
" tf.keras.layers.Dense(NUM_CLASSES, activation='sigmoid')]\n",
" )"
],
"execution_count": 6,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "PldDIuQvl7iB"
},
"source": [
"# Defining loss function\n",
"loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=False)\n",
"accuracy_metric = tf.keras.metrics.Accuracy()\n",
"\n",
"# Calculate loss\n",
"def loss_fn(gt_label, pred):\n",
" # training argument define the beehaviour of layers with respect\n",
" # to whether we are training the model or not. It is important for layers\n",
" # such as BatchNorm and Dropout.\n",
" return loss_object(y_true=gt_label, y_pred=pred)\n",
"\n",
"def accuracy_fn(gt_label, output):\n",
" # calculate the accuracy by turning output into labels with argmax\n",
" pred = tf.argmax(output, axis=1, output_type=tf.int32)\n",
" return accuracy_metric(pred, gt_label)\n"
],
"execution_count": 7,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "T1a1hEY6m0Ji"
},
"source": [
"# Define the optimizer\n",
"optimizer = tf.keras.optimizers.Adam(learning_rate=0.01)"
],
"execution_count": 8,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "k_2OZcLjnAhC",
"outputId": "d4e5e3ee-27ac-4d1e-8b31-8b4e1f59521d",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"source": [
"NUM_EPOCHS = 5\n",
"EPOCH_PER_DISPLAY = 1\n",
"total_loss = []\n",
"for epoch in range(NUM_EPOCHS):\n",
"\n",
" running_loss = []\n",
" running_accuracy = []\n",
"\n",
" # Training\n",
" for input, target in train_dataset:\n",
"\n",
" # Calculate and track graduents\n",
" with tf.GradientTape() as tape:\n",
"\n",
" # Calculate model output and loss\n",
" output = model(input, training=True)\n",
" loss_ = loss_fn(target, output)\n",
" accuracy_ = accuracy_fn(target, output)\n",
" \n",
" # Tape gradients\n",
" grads = tape.gradient(loss_, model.trainable_variables)\n",
" \n",
" # Track batch loss and accuracy\n",
" running_loss.append(loss_)\n",
" running_accuracy.append(accuracy_)\n",
"\n",
" # Optimize model based on the gradients\n",
" optimizer.apply_gradients(zip(grads, model.trainable_variables))\n",
" \n",
" # Epoch calculations\n",
" epoch_loss = np.mean(running_loss)\n",
" epoch_accuracy = np.mean(running_accuracy)\n",
" if (epoch + 1) % EPOCH_PER_DISPLAY == 0:\n",
" print(\"Epoch {}: Loss: {:.4f} Accuracy: {:.2f}%\".format(epoch+1, epoch_loss, epoch_accuracy * 100))\n"
],
"execution_count": 9,
"outputs": [
{
"output_type": "stream",
"text": [
"Epoch 1: Loss: 0.1329 Accuracy: 92.25%\n",
"Epoch 2: Loss: 0.0746 Accuracy: 96.68%\n",
"Epoch 3: Loss: 0.0679 Accuracy: 97.26%\n",
"Epoch 4: Loss: 0.0649 Accuracy: 97.58%\n",
"Epoch 5: Loss: 0.0695 Accuracy: 97.79%\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "Njorv726pcUW",
"outputId": "9c0327b2-3d75-45be-916f-084e7e440970",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"source": [
"# Calculate the accurcy on the test set\n",
"running_accuracy = []\n",
"for (input, gt_label) in test_dataset:\n",
" output = model(input, training=False)\n",
" accuracy_ = accuracy_fn(gt_label, output)\n",
" running_accuracy.append(accuracy_)\n",
"\n",
"print(\"Test accuracy: {:.3%}\".format(np.mean(running_accuracy)))\n"
],
"execution_count": 10,
"outputs": [
{
"output_type": "stream",
"text": [
"Test accuracy: 97.881%\n"
],
"name": "stdout"
}
]
}
]
}
================================================
FILE: codes/ipython/advanced/tfrecords.ipynb
================================================
{
"nbformat": 4,
"nbformat_minor": 0,
"metadata": {
"colab": {
"name": "tfrecords.ipynb",
"provenance": [],
"collapsed_sections": []
},
"kernelspec": {
"name": "python3",
"display_name": "Python 3"
}
},
"cells": [
{
"cell_type": "code",
"metadata": {
"id": "KrQ9AQrUY_xr"
},
"source": [
"import tensorflow as tf\n",
"from tensorflow import keras\n",
"import numpy as np"
],
"execution_count": 1,
"outputs": []
},
{
"cell_type": "code",
"metadata": {
"id": "Vj1ySX2vZJkw",
"outputId": "f3032080-d3b8-4d78-bbff-dd63e74c40fc",
"colab": {
"base_uri": "https://localhost:8080/"
}
},
"source": [
"# Load MNIST data\n",
"(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()\n",
"\n",
"# Preprocessing\n",
"x_train = x_train / 255.0\n",
"x_test = x_test / 255.0\n",
"\n",
"# Track the data type\n",
"dataType = x_train.dtype\n",
"print(f\"Data type: {dataType}\")\n",
"\n",
"labelType = y_test.dtype\n",
"print(f\"Data type: {labelType}\")"
],
"execution_count": 2,
"outputs": [
{
"output_type": "stream",
"text": [
"Data type: float64\n",
"Data type: uint8\n"
],
"name": "stdout"
}
]
},
{
"cell_type": "code",
"metadata": {
"id": "L_pwbAUe6WV5",
"outputId": "c16f7f78-0feb-43d1-ada2-c6cef9b6fe4c",
"colab": {
"base_uri": "https://localhost:8080/",
"height": 268
}
},
"source": [
"im_list = []\n",
"n_samples_to_show = 16\n",
"c = 0\n",
"for i in range(n_samples_to_show):\n",
" im_list.append(x_train[i])\n",
"\n",
"# Visualization\n",
"import matplotlib.pyplot as plt\n",
"from mpl_toolkits.axes_grid1 import ImageGrid\n",
"fig = plt.figure(figsize=(4., 4.))\n",
"# Ref: https://matplotlib.org/3.1.1/gallery/axes_grid1/simple_axesgrid.html\n",
"grid = ImageGrid(fig, 111, # similar to subplot(111)\n",
" nrows_ncols=(4, 4), # creates 2x2 grid of axes\n",
" axes_pad=0.1, # pad between axes in inch.\n",
" )\n",
"# Show image grid\n",
"for ax, im in zip(grid, im_list):\n",
" # Iterating over the grid returns the Axes.\n",
" ax.imshow(im, 'gray')\n",
"plt.show()"
],
"execution_count": 3,
"outputs": [
{
"output_type": "display_data",
"data": {
"image/png": "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
gitextract_9uld941z/ ├── .gitattributes ├── .github/ │ └── FUNDING.yml ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.rst ├── LICENSE ├── README.rst ├── _img/ │ └── 1-basics/ │ └── readme.rst ├── codes/ │ ├── ipython/ │ │ ├── 0-welcome/ │ │ │ └── welcome.ipynb │ │ ├── 1-basics/ │ │ │ ├── automatic_differentiation.ipynb │ │ │ ├── graph.ipynb │ │ │ ├── models.ipynb │ │ │ └── tensors.ipynb │ │ ├── advanced/ │ │ │ ├── custom_training.ipynb │ │ │ ├── dataset_generator.ipynb │ │ │ └── tfrecords.ipynb │ │ ├── basics_in_machine_learning/ │ │ │ ├── dataaugmentation.ipynb │ │ │ └── linearregression.ipynb │ │ └── neural_networks/ │ │ ├── CNNs.ipynb │ │ └── mlp.ipynb │ └── python/ │ ├── 0-welcome/ │ │ └── welcome.py │ ├── 1-basics/ │ │ ├── automatic_differentiation.py │ │ ├── graph.py │ │ ├── models.py │ │ └── tensors.py │ ├── advanced/ │ │ ├── custom_training.py │ │ ├── dataset_generator.py │ │ └── tfrecords.py │ ├── application/ │ │ └── image/ │ │ └── image_classification.py │ ├── basics_in_machine_learning/ │ │ ├── dataaugmentation.py │ │ └── linearregression.py │ └── neural_networks/ │ ├── cnns.py │ └── mlp.py ├── docs/ │ ├── Makefile │ ├── README.rst │ ├── _img/ │ │ ├── 1-basics/ │ │ │ └── readme.rst │ │ └── 3-neural_network/ │ │ └── autoencoder/ │ │ └── README.rst │ ├── conf.py │ ├── index.rst │ ├── make.bat │ └── tutorials/ │ ├── 0-welcome/ │ │ └── README.rst │ ├── 1-basics/ │ │ ├── basic_math_operations/ │ │ │ └── README.rst │ │ ├── readme.rst │ │ └── variables/ │ │ └── README.rst │ ├── 2-basics_in_machine_learning/ │ │ ├── linear_regression/ │ │ │ └── README.rst │ │ └── logistic_regression/ │ │ └── README.rst │ ├── 3-neural_network/ │ │ ├── autoencoder/ │ │ │ └── README.rst │ │ └── convolutiona_neural_network/ │ │ └── README.rst │ └── installation/ │ └── README.rst ├── requirements.txt ├── travis.sh └── welcome.py
SYMBOL INDEX (40 symbols across 8 files)
FILE: codes/python/1-basics/graph.py
function multiply_fn (line 33) | def multiply_fn(a, b):
function add_fn (line 45) | def add_fn(a, b):
class ModelShallow (line 66) | class ModelShallow(tf.keras.Model):
method __init__ (line 68) | def __init__(self):
method call (line 75) | def call(self, inputs, training=False):
class ModelDeep (line 83) | class ModelDeep(tf.keras.Model):
method __init__ (line 85) | def __init__(self):
method call (line 92) | def call(self, inputs, training=False):
FILE: codes/python/1-basics/models.py
class SampleLayer (line 23) | class SampleLayer(tf.Module):
method __init__ (line 27) | def __init__(self, name=None):
method __call__ (line 35) | def __call__(self, input):
class Model (line 53) | class Model(tf.Module):
method __init__ (line 54) | def __init__(self, name=None):
method __call__ (line 60) | def __call__(self, x):
class CustomModel (line 80) | class CustomModel(tf.keras.Model):
method __init__ (line 82) | def __init__(self):
method call (line 87) | def call(self, inputs):
FILE: codes/python/advanced/custom_training.py
function loss_fn (line 65) | def loss_fn(gt_label, pred):
function accuracy_fn (line 71) | def accuracy_fn(gt_label, output):
FILE: codes/python/advanced/dataset_generator.py
function gen_pairs_train (line 48) | def gen_pairs_train():
function gen_pairs_test (line 56) | def gen_pairs_test():
function loss_fn (line 116) | def loss_fn(gt_label, pred):
function accuracy_fn (line 122) | def accuracy_fn(gt_label, output):
FILE: codes/python/advanced/tfrecords.py
function _bytes_feature (line 51) | def _bytes_feature(value):
function _float_feature (line 58) | def _float_feature(value):
function _int64_feature (line 63) | def _int64_feature(value):
function image_example (line 68) | def image_example(image, label, dimension):
function parse_record (line 92) | def parse_record(record):
function decode_record (line 100) | def decode_record(record):
FILE: codes/python/application/image/image_classification.py
function get_class (line 41) | def get_class(path):
function get_look_up_dict (line 50) | def get_look_up_dict(df):
function imResize (line 85) | def imResize(image):
function visualize_training (line 106) | def visualize_training():
function train_gen (line 133) | def train_gen():
function test_gen (line 158) | def test_gen():
FILE: codes/python/basics_in_machine_learning/dataaugmentation.py
function visualize (line 40) | def visualize(im, imAgmented, operation):
FILE: codes/python/basics_in_machine_learning/linearregression.py
function linear_model (line 65) | def linear_model():
class NEPOCHLogger (line 104) | class NEPOCHLogger(tf.keras.callbacks.Callback):
method __init__ (line 105) | def __init__(self,per_epoch=100):
method on_epoch_end (line 112) | def on_epoch_end(self, epoch, logs=None):
Condensed preview — 52 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,783K chars).
[
{
"path": ".gitattributes",
"chars": 503,
"preview": "codes/ipython/* linguist-vendored\n\n# Basic .gitattributes for a python repo.\n\n# Source files\n# ============\n.pxd\t\ttext d"
},
{
"path": ".github/FUNDING.yml",
"chars": 65,
"preview": "# These are supported funding model platforms\n\ngithub: [astorfi]\n"
},
{
"path": ".travis.yml",
"chars": 1113,
"preview": "sudo: required\nlanguage: python\ncache: pip\ngit:\n depth: false\n quiet: true\n\npython: # The following versions\n - \"3.6\""
},
{
"path": "CODE_OF_CONDUCT.md",
"chars": 3221,
"preview": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, w"
},
{
"path": "CONTRIBUTING.rst",
"chars": 1714,
"preview": "\n*************\nContributing\n*************\n\nWhen contributing to this repository, please first discuss the change you wis"
},
{
"path": "LICENSE",
"chars": 1071,
"preview": "MIT License\n\nCopyright (c) 2017 Amirsina Torfi\n\nPermission is hereby granted, free of charge, to any person obtaining a "
},
{
"path": "README.rst",
"chars": 25578,
"preview": "\n\n********************\n`TensorFlow Course`_\n********************\n.. image:: https://travis-ci.org/instillai/TensorFlow-C"
},
{
"path": "_img/1-basics/readme.rst",
"chars": 71,
"preview": "==============================\nBasics\n==============================\n\n\n"
},
{
"path": "codes/ipython/0-welcome/welcome.ipynb",
"chars": 2380,
"preview": "{\n \"nbformat\": 4,\n \"nbformat_minor\": 0,\n \"metadata\": {\n \"colab\": {\n \"name\": \"welcome.ipynb\",\n \"provenanc"
},
{
"path": "codes/ipython/1-basics/automatic_differentiation.ipynb",
"chars": 7474,
"preview": "{\n \"nbformat\": 4,\n \"nbformat_minor\": 0,\n \"metadata\": {\n \"colab\": {\n \"name\": \"automatic_differentiation.ipynb\""
},
{
"path": "codes/ipython/1-basics/graph.ipynb",
"chars": 8452,
"preview": "{\n \"nbformat\": 4,\n \"nbformat_minor\": 0,\n \"metadata\": {\n \"colab\": {\n \"name\": \"graph.ipynb\",\n \"provenance\""
},
{
"path": "codes/ipython/1-basics/models.ipynb",
"chars": 7200,
"preview": "{\n \"nbformat\": 4,\n \"nbformat_minor\": 0,\n \"metadata\": {\n \"colab\": {\n \"name\": \"models.ipynb\",\n \"provenance"
},
{
"path": "codes/ipython/1-basics/tensors.ipynb",
"chars": 11001,
"preview": "{\n \"nbformat\": 4,\n \"nbformat_minor\": 0,\n \"metadata\": {\n \"colab\": {\n \"name\": \"tensors.ipynb\",\n \"provenanc"
},
{
"path": "codes/ipython/advanced/custom_training.ipynb",
"chars": 43639,
"preview": "{\n \"nbformat\": 4,\n \"nbformat_minor\": 0,\n \"metadata\": {\n \"colab\": {\n \"name\": \"custom_training.ipynb\",\n \"p"
},
{
"path": "codes/ipython/advanced/dataset_generator.ipynb",
"chars": 50572,
"preview": "{\n \"nbformat\": 4,\n \"nbformat_minor\": 0,\n \"metadata\": {\n \"colab\": {\n \"name\": \"dataset_generator.ipynb\",\n "
},
{
"path": "codes/ipython/advanced/tfrecords.ipynb",
"chars": 78207,
"preview": "{\n \"nbformat\": 4,\n \"nbformat_minor\": 0,\n \"metadata\": {\n \"colab\": {\n \"name\": \"tfrecords.ipynb\",\n \"provena"
},
{
"path": "codes/ipython/basics_in_machine_learning/dataaugmentation.ipynb",
"chars": 1186514,
"preview": "{\n \"nbformat\": 4,\n \"nbformat_minor\": 0,\n \"metadata\": {\n \"colab\": {\n \"name\": \"dataaugmentation.ipynb\",\n \""
},
{
"path": "codes/ipython/basics_in_machine_learning/linearregression.ipynb",
"chars": 85158,
"preview": "{\n \"nbformat\": 4,\n \"nbformat_minor\": 0,\n \"metadata\": {\n \"colab\": {\n \"name\": \"linearregression.ipynb\",\n \""
},
{
"path": "codes/ipython/neural_networks/CNNs.ipynb",
"chars": 41660,
"preview": "{\n \"nbformat\": 4,\n \"nbformat_minor\": 0,\n \"metadata\": {\n \"colab\": {\n \"name\": \"CNNs.ipynb\",\n \"provenance\":"
},
{
"path": "codes/ipython/neural_networks/mlp.ipynb",
"chars": 41537,
"preview": "{\n \"nbformat\": 4,\n \"nbformat_minor\": 0,\n \"metadata\": {\n \"colab\": {\n \"name\": \"mlp.ipynb\",\n \"provenance\": "
},
{
"path": "codes/python/0-welcome/welcome.py",
"chars": 880,
"preview": "# -*- coding: utf-8 -*-\n\"\"\"welcome.ipynb\n\nAutomatically generated by Colaboratory.\n\nOriginal file is located at\n http"
},
{
"path": "codes/python/1-basics/automatic_differentiation.py",
"chars": 3508,
"preview": "# -*- coding: utf-8 -*-\n\"\"\"automatic_differentiation.ipynb\n\nAutomatically generated by Colaboratory.\n\nOriginal file is l"
},
{
"path": "codes/python/1-basics/graph.py",
"chars": 4444,
"preview": "# -*- coding: utf-8 -*-\n\"\"\"graph.ipynb\n\nAutomatically generated by Colaboratory.\n\nOriginal file is located at\n https:"
},
{
"path": "codes/python/1-basics/models.py",
"chars": 3257,
"preview": "# -*- coding: utf-8 -*-\n\"\"\"models.ipynb\n\nAutomatically generated by Colaboratory.\n\nOriginal file is located at\n https"
},
{
"path": "codes/python/1-basics/tensors.py",
"chars": 2910,
"preview": "# -*- coding: utf-8 -*-\n\"\"\"tensors.ipynb\n\nAutomatically generated by Colaboratory.\n\nOriginal file is located at\n http"
},
{
"path": "codes/python/advanced/custom_training.py",
"chars": 3954,
"preview": "import tensorflow as tf\nimport numpy as np\n\n# Load MNIST data\n(x_train, y_train), (x_test, y_test) = tf.keras.datasets.m"
},
{
"path": "codes/python/advanced/dataset_generator.py",
"chars": 6458,
"preview": "# -*- coding: utf-8 -*-\n\"\"\"dataset_generator.ipynb\n\nAutomatically generated by Colaboratory.\n\nOriginal file is located a"
},
{
"path": "codes/python/advanced/tfrecords.py",
"chars": 4044,
"preview": "# -*- coding: utf-8 -*-\n\"\"\"TFRecords.ipynb\n\nAutomatically generated by Colaboratory.\n\nOriginal file is located at\n ht"
},
{
"path": "codes/python/application/image/image_classification.py",
"chars": 5526,
"preview": "# Import python libraries\nimport matplotlib.pyplot as plt\nimport numpy as np\nimport os\nimport pathlib\nimport tensorflow "
},
{
"path": "codes/python/basics_in_machine_learning/dataaugmentation.py",
"chars": 2699,
"preview": "# -*- coding: utf-8 -*-\n\"\"\"dataaugmentation.ipynb\n\nAutomatically generated by Colaboratory.\n\nOriginal file is located at"
},
{
"path": "codes/python/basics_in_machine_learning/linearregression.py",
"chars": 7172,
"preview": "# -*- coding: utf-8 -*-\n\"\"\"linearregression.ipynb\n\nAutomatically generated by Colaboratory.\n\nOriginal file is located at"
},
{
"path": "codes/python/neural_networks/cnns.py",
"chars": 2318,
"preview": "# -*- coding: utf-8 -*-\n\"\"\"CNNs.ipynb\n\nAutomatically generated by Colaboratory.\n\nOriginal file is located at\n https:/"
},
{
"path": "codes/python/neural_networks/mlp.py",
"chars": 1781,
"preview": "# -*- coding: utf-8 -*-\n\"\"\"mlp.ipynb\n\nAutomatically generated by Colaboratory.\n\nOriginal file is located at\n https://"
},
{
"path": "docs/Makefile",
"chars": 613,
"preview": "# Minimal makefile for Sphinx documentation\n#\n\n# You can set these variables from the command line.\nSPHINXOPTS =\nSPHI"
},
{
"path": "docs/README.rst",
"chars": 18201,
"preview": "\n****************\nTensorFlow World\n****************\n.. image:: https://travis-ci.org/astorfi/TensorFlow-World.svg?branc"
},
{
"path": "docs/_img/1-basics/readme.rst",
"chars": 71,
"preview": "==============================\nBasics\n==============================\n\n\n"
},
{
"path": "docs/_img/3-neural_network/autoencoder/README.rst",
"chars": 1,
"preview": "\n"
},
{
"path": "docs/conf.py",
"chars": 5320,
"preview": "# -*- coding: utf-8 -*-\n#\n# TensorFlow-World documentation build configuration file, created by\n# sphinx-quickstart on W"
},
{
"path": "docs/index.rst",
"chars": 27,
"preview": ".. include:: ../README.rst\n"
},
{
"path": "docs/make.bat",
"chars": 820,
"preview": "@ECHO OFF\r\n\r\npushd %~dp0\r\n\r\nREM Command file for Sphinx documentation\r\n\r\nif \"%SPHINXBUILD%\" == \"\" (\r\n\tset SPHINXBUILD=sp"
},
{
"path": "docs/tutorials/0-welcome/README.rst",
"chars": 3841,
"preview": "============================\nWelcome to TensorFlow World\n============================\n\n.. _this link: https://github.com"
},
{
"path": "docs/tutorials/1-basics/basic_math_operations/README.rst",
"chars": 4417,
"preview": "============================\nWelcome to TensorFlow World\n============================\n\n.. _this link: https://github.com"
},
{
"path": "docs/tutorials/1-basics/readme.rst",
"chars": 71,
"preview": "==============================\nBasics\n==============================\n\n\n"
},
{
"path": "docs/tutorials/1-basics/variables/README.rst",
"chars": 5547,
"preview": "Introduction to TensorFlow Variables: Creation, Initialization\n---------------------------------------------------------"
},
{
"path": "docs/tutorials/2-basics_in_machine_learning/linear_regression/README.rst",
"chars": 6715,
"preview": "\nSections\n~~~~~~~~\n\n- `Introduction <#Introduction>`__\n- `Description of the Overall\n Process <#Description%20of%20t"
},
{
"path": "docs/tutorials/2-basics_in_machine_learning/logistic_regression/README.rst",
"chars": 8715,
"preview": "\n\nSections\n~~~~~~~~\n\n- `Introduction <#Introduction>`__\n- `Description of the Overall\n Process <#Description%20of%20"
},
{
"path": "docs/tutorials/3-neural_network/autoencoder/README.rst",
"chars": 7410,
"preview": "Autoencoders and their implementations in TensorFlow\n----------------------------------------------------\n\nIn this post,"
},
{
"path": "docs/tutorials/3-neural_network/convolutiona_neural_network/README.rst",
"chars": 40822,
"preview": "==============================================\nConvolutional Neural Networks using TensorFlow\n=========================="
},
{
"path": "docs/tutorials/installation/README.rst",
"chars": 12728,
"preview": "==================================\nInstall TensorFlow from the source\n==================================\n\n.. _TensorFlow"
},
{
"path": "requirements.txt",
"chars": 89,
"preview": "numpy\nscipy\npython-coveralls\ntensorflow\nmatplotlib\nxlrd\nscikit-learn\npandas\nscikit-image\n"
},
{
"path": "travis.sh",
"chars": 390,
"preview": "# CHANGED_FILES=$(find codes/python/ -type f -name \"*.py\")\n\nCHANGED_FILES=($(git diff --name-only $TRAVIS_COMMIT_RANGE))"
},
{
"path": "welcome.py",
"chars": 0,
"preview": ""
}
]
About this extraction
This page contains the full source code of the instillai/TensorFlow-Course GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 52 files (1.7 MB), approximately 1.1M tokens, and a symbol index with 40 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.