mirror of
https://github.com/wassname/pytorch-lightning.git
synced 2026-09-16 12:36:51 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf110af384 | ||
|
|
76cc1c6eab | ||
|
|
efd750565e | ||
|
|
86261b7404 | ||
|
|
8eca3ffa41 | ||
|
|
413c343d83 | ||
|
|
ea2f50f1a4 | ||
|
|
4809de8765 | ||
|
|
b79b011d5e | ||
|
|
7d3399964b | ||
|
|
64827b7029 | ||
|
|
18eaa59c28 | ||
|
|
10b796b5c7 | ||
|
|
18b0c5a122 | ||
|
|
f26488bd16 | ||
|
|
bca1c4b594 | ||
|
|
a01e2ade25 | ||
|
|
3e9f37a382 | ||
|
|
89be81863e | ||
|
|
7f00fa1409 | ||
|
|
fc5583c8cd | ||
|
|
d7d52ae2e7 | ||
|
|
4a2d21fc91 | ||
|
|
9fe005b0d9 | ||
|
|
461fed19b6 | ||
|
|
ec57b3fe6d | ||
|
|
e43b1d1d31 | ||
|
|
8e2e95e55d | ||
|
|
71113ca770 | ||
|
|
7e81a17c11 | ||
|
|
5943438316 | ||
|
|
72239b4419 | ||
|
|
9ff6108af1 | ||
|
|
9d56b1744f | ||
|
|
31fa5a74e2 | ||
|
|
9f7caa2131 | ||
|
|
d286206e86 | ||
|
|
9e2679bbce | ||
|
|
d0f4764467 | ||
|
|
e4261de4a6 | ||
|
|
52a3f48e7f | ||
|
|
cef3731255 | ||
|
|
86c800d49b | ||
|
|
c5ff27d0ca | ||
|
|
b34aea10de | ||
|
|
7a86f7a048 | ||
|
|
24b0ef6593 | ||
|
|
7ca0590bc8 | ||
|
|
a0e2334a67 | ||
|
|
9ca35a985c | ||
|
|
4b1e3ad639 | ||
|
|
7611f0e8bb | ||
|
|
968fbfaacf | ||
|
|
d795d99697 | ||
|
|
f3b43b6154 |
+117
@@ -0,0 +1,117 @@
|
|||||||
|
# project
|
||||||
|
.DS_Store
|
||||||
|
.data/
|
||||||
|
run_configs/
|
||||||
|
test_tube_logs/
|
||||||
|
test_tube_data/
|
||||||
|
datasets/
|
||||||
|
model_weights/
|
||||||
|
app/models/
|
||||||
|
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
example.py
|
||||||
|
timit_data/
|
||||||
|
LJSpeech-1.1/
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
env/
|
||||||
|
ide_layouts/
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
.hypothesis/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
.python-version
|
||||||
|
|
||||||
|
# celery beat schedule file
|
||||||
|
celerybeat-schedule
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# dotenv
|
||||||
|
.env
|
||||||
|
|
||||||
|
# virtualenv
|
||||||
|
.venv
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
graft docs
|
||||||
|
|
||||||
|
include COPYING
|
||||||
|
include AUTHORS
|
||||||
|
|
||||||
|
recursive-include src/einsteinpy/tests *.py *.html
|
||||||
|
|
||||||
|
prune docs/source/examples/.ipynb_checkpoints
|
||||||
|
global-exclude *.py[cod] __pycache__ *.so *.dylib
|
||||||
@@ -1,66 +1,127 @@
|
|||||||
# Pytorch-lightning
|
<p align="center">
|
||||||
The Keras for ML-researchers in PyTorch.
|
<a href="https://williamfalcon.github.io/pytorch-lightning/">
|
||||||
|
<img alt="" src="https://github.com/williamFalcon/pytorch-lightning/blob/master/docs/source/_static/lightning_logo.png" width="50">
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<h3 align="center">
|
||||||
|
Pytorch Lightning
|
||||||
|
</h3>
|
||||||
|
<p align="center">
|
||||||
|
The Keras for ML researchers using PyTorch. More control. Less boilerplate.
|
||||||
|
</p>
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://badge.fury.io/py/pytorch-lightning"><img src="https://badge.fury.io/py/pytorch-lightning.svg" alt="PyPI version" height="18"></a>
|
||||||
|
<!-- <a href="https://travis-ci.org/williamFalcon/test-tube"><img src="https://travis-ci.org/williamFalcon/pytorch-lightning.svg?branch=master"></a> -->
|
||||||
|
<a href="https://github.com/williamFalcon/pytorch-lightning/blob/master/COPYING"><img src="https://img.shields.io/badge/License-MIT-yellow.svg"></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install pytorch-lightning
|
||||||
|
```
|
||||||
|
|
||||||
|
## Docs
|
||||||
|
In progress. Documenting now!
|
||||||
|
|
||||||
|
## Disclaimer
|
||||||
|
This is a research tool I built for myself internally while doing my PhD. The API is not 100% production quality, but my hope is that by open-sourcing, we can all get it there (I don't have too much time nowadays to write production-level code).
|
||||||
|
|
||||||
|
## What is it?
|
||||||
|
Keras is too abstract for researchers. Lightning makes it so you only have to define your model but still control all details of training if you need to.
|
||||||
|
|
||||||
|
Pytorch
|
||||||
|
<-- Lightning
|
||||||
|
Your model.
|
||||||
|
|
||||||
|
**Lightning will do the following for you:**
|
||||||
|
|
||||||
|
1. Run the training loop.
|
||||||
|
2. Run the validation loop.
|
||||||
|
3. Run the testing loop.
|
||||||
|
4. Early stopping.
|
||||||
|
5. Learning rate annealing.
|
||||||
|
6. Can train complex models like GANs or anything with multiple optimizers.
|
||||||
|
7. Weight checkpointing.
|
||||||
|
8. Model saving.
|
||||||
|
9. Model loading.
|
||||||
|
10. Log training details (through test-tube).
|
||||||
|
11. Run training on multiple GPUs (through test-tube).
|
||||||
|
12. Run training on a GPU cluster managed by SLURM (through test-tube).
|
||||||
|
13. Distribute memory-bound models on multiple GPUs.
|
||||||
|
14. Give your model hyperparameters parsed from the command line OR a JSON file.
|
||||||
|
15. Run your model in a dev environment where nothing logs.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
To use lightning do 2 things:
|
To use lightning do 2 things:
|
||||||
1. [Define a trainer](https://github.com/williamFalcon/pytorch-lightning/blob/master/pytorch_lightning/trainer_main.py) (which will run ALL your models).
|
1. [Define a trainer](https://github.com/williamFalcon/pytorch-lightning/blob/master/docs/source/examples/basic_trainer.py) (which will run ALL your models).
|
||||||
2. [Define a model](https://github.com/williamFalcon/pytorch-lightning/blob/master/pytorch_lightning/models/sample_model_template/model_template.py).
|
2. [Define a model](https://github.com/williamFalcon/pytorch-lightning/blob/master/docs/source/examples/example_model.py).
|
||||||
|
|
||||||
### Example:
|
#### Basic trainer example
|
||||||
|
See [this demo](https://github.com/williamFalcon/pytorch-lightning/blob/master/docs/source/examples/fully_featured_trainer.py) for a more robust trainer example.
|
||||||
#### Define the trainer
|
|
||||||
|
|
||||||
```python
|
```python
|
||||||
# trainer.py
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
from pytorch_lightning.models.trainer import Trainer
|
from test_tube import HyperOptArgumentParser, Experiment
|
||||||
|
from pytorch_lightning.models.trainer import Trainer
|
||||||
|
from pytorch_lightning.utils.arg_parse import add_default_args
|
||||||
from pytorch_lightning.utils.pt_callbacks import EarlyStopping, ModelCheckpoint
|
from pytorch_lightning.utils.pt_callbacks import EarlyStopping, ModelCheckpoint
|
||||||
from my_project import My_Model
|
from demo.example_model import ExampleModel
|
||||||
from test_tube import HyperOptArgumentParser, Experiment, SlurmCluster
|
|
||||||
|
|
||||||
# --------------
|
|
||||||
# TEST TUBE INIT
|
|
||||||
exp = Experiment(
|
|
||||||
name='my_exp',
|
|
||||||
debug=True,
|
|
||||||
save_dir='/some/path',
|
|
||||||
autosave=False,
|
|
||||||
description='my desc'
|
|
||||||
)
|
|
||||||
|
|
||||||
# --------------------
|
def main(hparams):
|
||||||
# CALLBACKS
|
"""
|
||||||
early_stop = EarlyStopping(
|
Main training routine specific for this project
|
||||||
monitor='val_loss',
|
:param hparams:
|
||||||
patience=3,
|
:return:
|
||||||
verbose=True,
|
"""
|
||||||
mode='min'
|
# init experiment
|
||||||
)
|
exp = Experiment(
|
||||||
|
name=hparams.tt_name,
|
||||||
|
debug=hparams.debug,
|
||||||
|
save_dir=hparams.tt_save_path,
|
||||||
|
version=hparams.hpc_exp_number,
|
||||||
|
autosave=False,
|
||||||
|
description=hparams.tt_description
|
||||||
|
)
|
||||||
|
|
||||||
model_save_path = 'PATH/TO/SAVE'
|
exp.argparse(hparams)
|
||||||
checkpoint = ModelCheckpoint(
|
exp.save()
|
||||||
filepath=model_save_path,
|
|
||||||
save_function=None,
|
|
||||||
save_best_only=True,
|
|
||||||
verbose=True,
|
|
||||||
monitor='val_acc',
|
|
||||||
mode='min'
|
|
||||||
)
|
|
||||||
|
|
||||||
# configure trainer
|
model_save_path = '{}/{}/{}'.format(hparams.model_save_path, exp.name, exp.version)
|
||||||
trainer = Trainer(
|
|
||||||
experiment=experiment,
|
# build model
|
||||||
cluster=cluster,
|
model = ExampleModel(hparams)
|
||||||
checkpoint_callback=checkpoint,
|
|
||||||
early_stop_callback=early_stop
|
# callbacks
|
||||||
)
|
early_stop = EarlyStopping(monitor='val_acc', patience=3, mode='min', verbose=True)
|
||||||
|
checkpoint = ModelCheckpoint(filepath=model_save_path, save_function=None, save_best_only=True, verbose=True, monitor='val_acc', mode='min')
|
||||||
|
|
||||||
|
# configure trainer
|
||||||
|
trainer = Trainer(experiment=exp, checkpoint_callback=checkpoint, early_stop_callback=early_stop)
|
||||||
|
|
||||||
|
# train model
|
||||||
|
trainer.fit(model)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
# use default args given by lightning
|
||||||
|
root_dir = os.path.split(os.path.dirname(sys.modules['__main__'].__file__))[0]
|
||||||
|
parent_parser = HyperOptArgumentParser(strategy='random_search', add_help=False)
|
||||||
|
add_default_args(parent_parser, root_dir)
|
||||||
|
|
||||||
|
# allow model to overwrite or extend args
|
||||||
|
parser = ExampleModel.add_model_specific_args(parent_parser)
|
||||||
|
hyperparams = parser.parse_args()
|
||||||
|
|
||||||
|
# train model
|
||||||
|
main(hyperparams)
|
||||||
|
|
||||||
# init model and train
|
|
||||||
model = My_Model()
|
|
||||||
trainer.fit(model)
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Define the model
|
#### Basic model example
|
||||||
|
Here we only show the method signatures. It's up to you to define the content.
|
||||||
|
|
||||||
```python
|
```python
|
||||||
from torch import nn
|
from torch import nn
|
||||||
@@ -69,32 +130,32 @@ class My_Model(RootModule):
|
|||||||
def __init__(self):
|
def __init__(self):
|
||||||
# define model
|
# define model
|
||||||
self.l1 = nn.Linear(200, 10)
|
self.l1 = nn.Linear(200, 10)
|
||||||
|
|
||||||
# ---------------
|
# ---------------
|
||||||
# TRAINING
|
# TRAINING
|
||||||
def training_step(self, data_batch):
|
def training_step(self, data_batch):
|
||||||
x, y = data_batch
|
x, y = data_batch
|
||||||
y_hat = self.l1(x)
|
y_hat = self.l1(x)
|
||||||
loss = some_loss(y_hat)
|
loss = some_loss(y_hat)
|
||||||
|
|
||||||
return loss_val, {'train_loss': loss}
|
return loss_val, {'train_loss': loss}
|
||||||
|
|
||||||
def validation_step(self, data_batch):
|
def validation_step(self, data_batch):
|
||||||
x, y = data_batch
|
x, y = data_batch
|
||||||
y_hat = self.l1(x)
|
y_hat = self.l1(x)
|
||||||
loss = some_loss(y_hat)
|
loss = some_loss(y_hat)
|
||||||
|
|
||||||
return loss_val, {'val_loss': loss}
|
return loss_val, {'val_loss': loss}
|
||||||
|
|
||||||
def validation_end(self, outputs):
|
def validation_end(self, outputs):
|
||||||
total_accs = []
|
total_accs = []
|
||||||
|
|
||||||
for output in outputs:
|
for output in outputs:
|
||||||
total_accs.append(output['val_acc'].item())
|
total_accs.append(output['val_acc'].item())
|
||||||
|
|
||||||
# return a dict
|
# return a dict
|
||||||
return {'total_acc': np.mean(total_accs)}
|
return {'total_acc': np.mean(total_accs)}
|
||||||
|
|
||||||
# ---------------
|
# ---------------
|
||||||
# SAVING
|
# SAVING
|
||||||
def get_save_dict(self):
|
def get_save_dict(self):
|
||||||
@@ -106,7 +167,7 @@ class My_Model(RootModule):
|
|||||||
def load_model_specific(self, checkpoint):
|
def load_model_specific(self, checkpoint):
|
||||||
# lightning loads for you. Here's your chance to say what you want to load
|
# lightning loads for you. Here's your chance to say what you want to load
|
||||||
self.load_state_dict(checkpoint['state_dict'])
|
self.load_state_dict(checkpoint['state_dict'])
|
||||||
|
|
||||||
# ---------------
|
# ---------------
|
||||||
# TRAINING CONFIG
|
# TRAINING CONFIG
|
||||||
def configure_optimizers(self):
|
def configure_optimizers(self):
|
||||||
@@ -114,7 +175,7 @@ class My_Model(RootModule):
|
|||||||
# lightning will call automatically
|
# lightning will call automatically
|
||||||
optimizer = self.choose_optimizer('adam', self.parameters(), {'lr': self.hparams.learning_rate}, 'optimizer')
|
optimizer = self.choose_optimizer('adam', self.parameters(), {'lr': self.hparams.learning_rate}, 'optimizer')
|
||||||
return [optimizer]
|
return [optimizer]
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def tng_dataloader(self):
|
def tng_dataloader(self):
|
||||||
return pytorch_dataloader('train')
|
return pytorch_dataloader('train')
|
||||||
@@ -126,7 +187,7 @@ class My_Model(RootModule):
|
|||||||
@property
|
@property
|
||||||
def test_dataloader(self):
|
def test_dataloader(self):
|
||||||
return pytorch_dataloader('test')
|
return pytorch_dataloader('test')
|
||||||
|
|
||||||
# ---------------
|
# ---------------
|
||||||
# MODIFY YOUR COMMAND LINE ARGS
|
# MODIFY YOUR COMMAND LINE ARGS
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -135,6 +196,8 @@ class My_Model(RootModule):
|
|||||||
parser.add_argument('--out_features', default=20)
|
parser.add_argument('--out_features', default=20)
|
||||||
return parser
|
return parser
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### Details
|
### Details
|
||||||
|
|
||||||
#### Model definition
|
#### Model definition
|
||||||
@@ -143,7 +206,7 @@ class My_Model(RootModule):
|
|||||||
| training_step | Called with a batch of data during training | data from your dataloaders | tuple: scalar, dict |
|
| training_step | Called with a batch of data during training | data from your dataloaders | tuple: scalar, dict |
|
||||||
| validation_step | Called with a batch of data during validation | data from your dataloaders | tuple: scalar, dict |
|
| validation_step | Called with a batch of data during validation | data from your dataloaders | tuple: scalar, dict |
|
||||||
| validation_end | Collate metrics from all validation steps | outputs: array where each item is the output of a validation step | dict: for logging |
|
| validation_end | Collate metrics from all validation steps | outputs: array where each item is the output of a validation step | dict: for logging |
|
||||||
| get_save_dict | called when your model needs to be saved (checkpoints, hpc save, etc...) | None | dict to be saved |
|
| get_save_dict | called when your model needs to be saved (checkpoints, hpc save, etc...) | None | dict to be saved |
|
||||||
|
|
||||||
#### Model training
|
#### Model training
|
||||||
| Name | Description | Input | Return |
|
| Name | Description | Input | Return |
|
||||||
@@ -159,7 +222,7 @@ class My_Model(RootModule):
|
|||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| get_save_dict | called when your model needs to be saved (checkpoints, hpc save, etc...) | None | dict to be saved |
|
| get_save_dict | called when your model needs to be saved (checkpoints, hpc save, etc...) | None | dict to be saved |
|
||||||
| load_model_specific | called when loading a model | checkpoint: dict you created in get_save_dict | dict: modified in whatever way you want |
|
| load_model_specific | called when loading a model | checkpoint: dict you created in get_save_dict | dict: modified in whatever way you want |
|
||||||
|
|
||||||
## Optional model hooks.
|
## Optional model hooks.
|
||||||
Add these to the model whenever you want to configure training behavior.
|
Add these to the model whenever you want to configure training behavior.
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,74 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from test_tube import HyperOptArgumentParser, Experiment
|
||||||
|
from pytorch-lightning.models.trainer import Trainer
|
||||||
|
from pytorch-lightning.utils.arg_parse import add_default_args
|
||||||
|
from pytorch-lightning.utils.pt_callbacks import EarlyStopping, ModelCheckpoint
|
||||||
|
from demo.example_model import ExampleModel
|
||||||
|
|
||||||
|
|
||||||
|
def main(hparams):
|
||||||
|
"""
|
||||||
|
Main training routine specific for this project
|
||||||
|
:param hparams:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# init experiment
|
||||||
|
exp = Experiment(
|
||||||
|
name=hparams.tt_name,
|
||||||
|
debug=hparams.debug,
|
||||||
|
save_dir=hparams.tt_save_path,
|
||||||
|
version=hparams.hpc_exp_number,
|
||||||
|
autosave=False,
|
||||||
|
description=hparams.tt_description
|
||||||
|
)
|
||||||
|
|
||||||
|
exp.argparse(hparams)
|
||||||
|
exp.save()
|
||||||
|
|
||||||
|
# build model
|
||||||
|
model = ExampleModel(hparams)
|
||||||
|
|
||||||
|
# callbacks
|
||||||
|
early_stop = EarlyStopping(
|
||||||
|
monitor='val_acc',
|
||||||
|
patience=3,
|
||||||
|
mode='min',
|
||||||
|
verbose=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
model_save_path = '{}/{}/{}'.format(hparams.model_save_path, exp.name, exp.version)
|
||||||
|
checkpoint = ModelCheckpoint(
|
||||||
|
filepath=model_save_path,
|
||||||
|
save_function=None,
|
||||||
|
save_best_only=True,
|
||||||
|
verbose=True,
|
||||||
|
monitor='val_acc',
|
||||||
|
mode='min'
|
||||||
|
)
|
||||||
|
|
||||||
|
# configure trainer
|
||||||
|
trainer = Trainer(
|
||||||
|
experiment=exp,
|
||||||
|
checkpoint_callback=checkpoint,
|
||||||
|
early_stop_callback=early_stop,
|
||||||
|
)
|
||||||
|
|
||||||
|
# train model
|
||||||
|
trainer.fit(model)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
# use default args given by lightning
|
||||||
|
root_dir = os.path.split(os.path.dirname(sys.modules['__main__'].__file__))[0]
|
||||||
|
parent_parser = HyperOptArgumentParser(strategy='random_search', add_help=False)
|
||||||
|
add_default_args(parent_parser, root_dir)
|
||||||
|
|
||||||
|
# allow model to overwrite or extend args
|
||||||
|
parser = ExampleModel.add_model_specific_args(parent_parser)
|
||||||
|
hyperparams = parser.parse_args()
|
||||||
|
|
||||||
|
# train model
|
||||||
|
main(hyperparams)
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
import torch.nn as nn
|
||||||
|
import numpy as np
|
||||||
|
from pytorch-lightning.root_module.root_module import RootModule
|
||||||
|
from test_tube import HyperOptArgumentParser
|
||||||
|
from torchvision.datasets import MNIST
|
||||||
|
import torchvision.transforms as transforms
|
||||||
|
import torch
|
||||||
|
import torch.nn.functional as F
|
||||||
|
|
||||||
|
|
||||||
|
class ExampleModel(RootModule):
|
||||||
|
"""
|
||||||
|
Sample model to show how to define a template
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, hparams):
|
||||||
|
# init superclass
|
||||||
|
super(ExampleModel, self).__init__(hparams)
|
||||||
|
|
||||||
|
self.batch_size = hparams.batch_size
|
||||||
|
|
||||||
|
# build model
|
||||||
|
self.__build_model()
|
||||||
|
|
||||||
|
# ---------------------
|
||||||
|
# MODEL SETUP
|
||||||
|
# ---------------------
|
||||||
|
def __build_model(self):
|
||||||
|
"""
|
||||||
|
Layout model
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.c_d1 = nn.Linear(in_features=self.hparams.in_features, out_features=self.hparams.hidden_dim)
|
||||||
|
self.c_d1_bn = nn.BatchNorm1d(self.hparams.hidden_dim)
|
||||||
|
self.c_d1_drop = nn.Dropout(self.hparams.drop_prob)
|
||||||
|
|
||||||
|
self.c_d2 = nn.Linear(in_features=self.hparams.hidden_dim, out_features=self.hparams.out_features)
|
||||||
|
|
||||||
|
# ---------------------
|
||||||
|
# TRAINING
|
||||||
|
# ---------------------
|
||||||
|
def forward(self, x):
|
||||||
|
x = self.c_d1(x)
|
||||||
|
x = F.tanh(x)
|
||||||
|
x = self.c_d1_bn(x)
|
||||||
|
x = self.c_d1_drop(x)
|
||||||
|
|
||||||
|
x = self.c_d2(x)
|
||||||
|
logits = F.log_softmax(x, dim=1)
|
||||||
|
|
||||||
|
return logits
|
||||||
|
|
||||||
|
def loss(self, labels, logits):
|
||||||
|
nll = F.nll_loss(logits, labels)
|
||||||
|
return nll
|
||||||
|
|
||||||
|
def training_step(self, data_batch):
|
||||||
|
"""
|
||||||
|
Called inside the training loop
|
||||||
|
:param data_batch:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# forward pass
|
||||||
|
x, y = data_batch
|
||||||
|
x = x.view(x.size(0), -1)
|
||||||
|
y_hat = self.forward(x)
|
||||||
|
|
||||||
|
# calculate loss
|
||||||
|
loss_val = self.loss(y, y_hat)
|
||||||
|
|
||||||
|
tqdm_dic = {'tng_loss': loss_val.item()}
|
||||||
|
return loss_val, tqdm_dic
|
||||||
|
|
||||||
|
def validation_step(self, data_batch):
|
||||||
|
"""
|
||||||
|
Called inside the validation loop
|
||||||
|
:param data_batch:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
x, y = data_batch
|
||||||
|
x = x.view(x.size(0), -1)
|
||||||
|
y_hat = self.forward(x)
|
||||||
|
|
||||||
|
loss_val = self.loss(y, y_hat)
|
||||||
|
|
||||||
|
# acc
|
||||||
|
labels_hat = torch.argmax(y_hat, dim=1)
|
||||||
|
val_acc = torch.sum(y == labels_hat).item() / (len(y) * 1.0)
|
||||||
|
|
||||||
|
output = {'y_hat': y_hat, 'val_loss': loss_val.item(), 'val_acc': val_acc}
|
||||||
|
return output
|
||||||
|
|
||||||
|
def validation_end(self, outputs):
|
||||||
|
"""
|
||||||
|
Called at the end of validation to aggregate outputs
|
||||||
|
:param outputs: list of individual outputs of each validation step
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
val_loss_mean = 0
|
||||||
|
accs = []
|
||||||
|
for output in outputs:
|
||||||
|
val_loss_mean += output['val_loss']
|
||||||
|
accs.append(output['val_acc'])
|
||||||
|
|
||||||
|
val_loss_mean /= len(outputs)
|
||||||
|
tqdm_dic = {'val_loss': val_loss_mean, 'val_acc': np.mean(accs)}
|
||||||
|
return tqdm_dic
|
||||||
|
|
||||||
|
def update_tng_log_metrics(self, logs):
|
||||||
|
return logs
|
||||||
|
|
||||||
|
# ---------------------
|
||||||
|
# MODEL SAVING
|
||||||
|
# ---------------------
|
||||||
|
def get_save_dict(self):
|
||||||
|
checkpoint = {'state_dict': self.state_dict()}
|
||||||
|
return checkpoint
|
||||||
|
|
||||||
|
def load_model_specific(self, checkpoint):
|
||||||
|
self.load_state_dict(checkpoint['state_dict'])
|
||||||
|
pass
|
||||||
|
|
||||||
|
# ---------------------
|
||||||
|
# TRAINING SETUP
|
||||||
|
# ---------------------
|
||||||
|
def configure_optimizers(self):
|
||||||
|
"""
|
||||||
|
return whatever optimizers we want here
|
||||||
|
:return: list of optimizers
|
||||||
|
"""
|
||||||
|
optimizer = self.choose_optimizer(self.hparams.optimizer_name, self.parameters(), {'lr': self.hparams.learning_rate}, 'optimizer')
|
||||||
|
self.optimizers = [optimizer]
|
||||||
|
return self.optimizers
|
||||||
|
|
||||||
|
def __dataloader(self, train):
|
||||||
|
# init data generators
|
||||||
|
transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (1.0,))])
|
||||||
|
|
||||||
|
dataset = MNIST(root=self.hparams.data_root, train=train, transform=transform, download=True)
|
||||||
|
|
||||||
|
loader = torch.utils.data.DataLoader(
|
||||||
|
dataset=dataset,
|
||||||
|
batch_size=self.hparams.batch_size,
|
||||||
|
shuffle=True
|
||||||
|
)
|
||||||
|
|
||||||
|
return loader
|
||||||
|
|
||||||
|
@property
|
||||||
|
def tng_dataloader(self):
|
||||||
|
if self._tng_dataloader is None:
|
||||||
|
try:
|
||||||
|
self._tng_dataloader = self.__dataloader(train=True)
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
raise e
|
||||||
|
return self._tng_dataloader
|
||||||
|
|
||||||
|
@property
|
||||||
|
def val_dataloader(self):
|
||||||
|
if self._val_dataloader is None:
|
||||||
|
try:
|
||||||
|
self._val_dataloader = self.__dataloader(train=False)
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
raise e
|
||||||
|
return self._val_dataloader
|
||||||
|
|
||||||
|
@property
|
||||||
|
def test_dataloader(self):
|
||||||
|
if self._test_dataloader is None:
|
||||||
|
try:
|
||||||
|
self._test_dataloader = self.__dataloader(train=False)
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
raise e
|
||||||
|
return self._test_dataloader
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def add_model_specific_args(parent_parser):
|
||||||
|
parser = HyperOptArgumentParser(strategy=parent_parser.strategy, parents=[parent_parser])
|
||||||
|
|
||||||
|
# param overwrites
|
||||||
|
# parser.set_defaults(gradient_clip=5.0)
|
||||||
|
|
||||||
|
# network params
|
||||||
|
parser.opt_list('--drop_prob', default=0.2, options=[0.2, 0.5], type=float, tunable=False)
|
||||||
|
parser.add_argument('--in_features', default=28*28)
|
||||||
|
parser.add_argument('--hidden_dim', default=500)
|
||||||
|
parser.add_argument('--out_features', default=10)
|
||||||
|
|
||||||
|
# data
|
||||||
|
parser.add_argument('--data_root', default='/Users/williamfalcon/Developer/personal/research_lib/research_proj/datasets/mnist', type=str)
|
||||||
|
|
||||||
|
# training params (opt)
|
||||||
|
parser.opt_list('--learning_rate', default=0.001, type=float, options=[0.0001, 0.0005, 0.001, 0.005],
|
||||||
|
tunable=False)
|
||||||
|
parser.opt_list('--batch_size', default=256, type=int, options=[32, 64, 128, 256], tunable=False)
|
||||||
|
parser.opt_list('--optimizer_name', default='adam', type=str, options=['adam'], tunable=False)
|
||||||
|
return parser
|
||||||
@@ -0,0 +1,201 @@
|
|||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import numpy as np
|
||||||
|
from time import sleep
|
||||||
|
import torch
|
||||||
|
|
||||||
|
from test_tube import HyperOptArgumentParser, Experiment, SlurmCluster
|
||||||
|
from pytorch-lightning.models.trainer import Trainer
|
||||||
|
from pytorch-lightning.utils.arg_parse import add_default_args
|
||||||
|
|
||||||
|
from pytorch-lightning.utils.pt_callbacks import EarlyStopping, ModelCheckpoint
|
||||||
|
|
||||||
|
SEED = 2334
|
||||||
|
torch.manual_seed(SEED)
|
||||||
|
np.random.seed(SEED)
|
||||||
|
|
||||||
|
# ---------------------
|
||||||
|
# DEFINE MODEL HERE
|
||||||
|
# ---------------------
|
||||||
|
from example_model import ExampleModel
|
||||||
|
# ---------------------
|
||||||
|
|
||||||
|
AVAILABLE_MODELS = {
|
||||||
|
'model_template': ExampleModel
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
Allows training by using command line arguments
|
||||||
|
Run by:
|
||||||
|
# TYPE YOUR RUN COMMAND HERE
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def main_local(hparams):
|
||||||
|
main(hparams, None, None)
|
||||||
|
|
||||||
|
|
||||||
|
def main(hparams, cluster, results_dict):
|
||||||
|
"""
|
||||||
|
Main training routine specific for this project
|
||||||
|
:param hparams:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
on_gpu = torch.cuda.is_available()
|
||||||
|
if hparams.disable_cuda:
|
||||||
|
on_gpu = False
|
||||||
|
|
||||||
|
device = 'cuda' if on_gpu else 'cpu'
|
||||||
|
hparams.__setattr__('device', device)
|
||||||
|
hparams.__setattr__('on_gpu', on_gpu)
|
||||||
|
hparams.__setattr__('nb_gpus', torch.cuda.device_count())
|
||||||
|
hparams.__setattr__('inference_mode', hparams.model_load_weights_path is not None)
|
||||||
|
|
||||||
|
# delay each training start to not overwrite logs
|
||||||
|
process_position, current_gpu = TRAINING_MODEL.get_process_position(hparams.gpus)
|
||||||
|
sleep(process_position + 1)
|
||||||
|
|
||||||
|
# init experiment
|
||||||
|
log_dir = os.path.dirname(os.path.realpath(__file__))
|
||||||
|
exp = Experiment(
|
||||||
|
name='test_tube_exp',
|
||||||
|
debug=True,
|
||||||
|
save_dir=log_dir,
|
||||||
|
version=0,
|
||||||
|
autosave=False,
|
||||||
|
description='test demo'
|
||||||
|
)
|
||||||
|
|
||||||
|
exp.argparse(hparams)
|
||||||
|
exp.save()
|
||||||
|
|
||||||
|
# build model
|
||||||
|
print('loading model...')
|
||||||
|
model = TRAINING_MODEL(hparams)
|
||||||
|
print('model built')
|
||||||
|
|
||||||
|
# callbacks
|
||||||
|
early_stop = EarlyStopping(
|
||||||
|
monitor=hparams.early_stop_metric,
|
||||||
|
patience=hparams.early_stop_patience,
|
||||||
|
verbose=True,
|
||||||
|
mode=hparams.early_stop_mode
|
||||||
|
)
|
||||||
|
|
||||||
|
model_save_path = '{}/{}/{}'.format(hparams.model_save_path, exp.name, exp.version)
|
||||||
|
checkpoint = ModelCheckpoint(
|
||||||
|
filepath=model_save_path,
|
||||||
|
save_function=None,
|
||||||
|
save_best_only=True,
|
||||||
|
verbose=True,
|
||||||
|
monitor=hparams.model_save_monitor_value,
|
||||||
|
mode=hparams.model_save_monitor_mode
|
||||||
|
)
|
||||||
|
|
||||||
|
# configure trainer
|
||||||
|
trainer = Trainer(
|
||||||
|
experiment=exp,
|
||||||
|
cluster=cluster,
|
||||||
|
checkpoint_callback=checkpoint,
|
||||||
|
early_stop_callback=early_stop,
|
||||||
|
)
|
||||||
|
|
||||||
|
# train model
|
||||||
|
trainer.fit(model)
|
||||||
|
|
||||||
|
|
||||||
|
def get_default_parser(strategy, root_dir):
|
||||||
|
|
||||||
|
possible_model_names = list(AVAILABLE_MODELS.keys())
|
||||||
|
parser = HyperOptArgumentParser(strategy=strategy, add_help=False)
|
||||||
|
add_default_args(parser, root_dir, possible_model_names=possible_model_names, rand_seed=SEED)
|
||||||
|
return parser
|
||||||
|
|
||||||
|
|
||||||
|
def get_model_name(args):
|
||||||
|
for i, arg in enumerate(args):
|
||||||
|
if 'model_name' in arg:
|
||||||
|
return args[i+1]
|
||||||
|
|
||||||
|
|
||||||
|
def optimize_on_cluster(hyperparams):
|
||||||
|
# enable cluster training
|
||||||
|
cluster = SlurmCluster(
|
||||||
|
hyperparam_optimizer=hyperparams,
|
||||||
|
log_path=hyperparams.tt_save_path,
|
||||||
|
test_tube_exp_name=hyperparams.tt_name
|
||||||
|
)
|
||||||
|
|
||||||
|
# email for cluster coms
|
||||||
|
cluster.notify_job_status(email='add_email_here', on_done=True, on_fail=True)
|
||||||
|
|
||||||
|
# configure cluster
|
||||||
|
cluster.per_experiment_nb_gpus = hyperparams.per_experiment_nb_gpus
|
||||||
|
cluster.job_time = '48:00:00'
|
||||||
|
cluster.gpu_type = '1080ti'
|
||||||
|
cluster.memory_mb_per_node = 48000
|
||||||
|
|
||||||
|
# any modules for code to run in env
|
||||||
|
cluster.add_command('source activate pytorch_lightning')
|
||||||
|
|
||||||
|
# name of exp
|
||||||
|
job_display_name = hyperparams.tt_name.split('_')[0]
|
||||||
|
job_display_name = job_display_name[0:3]
|
||||||
|
|
||||||
|
# run hopt
|
||||||
|
print('submitting jobs...')
|
||||||
|
cluster.optimize_parallel_cluster_gpu(
|
||||||
|
main,
|
||||||
|
nb_trials=hyperparams.nb_hopt_trials,
|
||||||
|
job_name=job_display_name
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
|
||||||
|
model_name = get_model_name(sys.argv)
|
||||||
|
if model_name is None:
|
||||||
|
model_name = 'model_template'
|
||||||
|
|
||||||
|
# use default args
|
||||||
|
root_dir = os.path.split(os.path.dirname(sys.modules['__main__'].__file__))[0]
|
||||||
|
parent_parser = get_default_parser(strategy='random_search', root_dir=root_dir)
|
||||||
|
|
||||||
|
# allow model to overwrite or extend args
|
||||||
|
TRAINING_MODEL = AVAILABLE_MODELS[model_name]
|
||||||
|
parser = TRAINING_MODEL.add_model_specific_args(parent_parser)
|
||||||
|
hyperparams = parser.parse_args()
|
||||||
|
|
||||||
|
# format GPU layout
|
||||||
|
os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID"
|
||||||
|
gpu_ids = hyperparams.gpus.split(';')
|
||||||
|
|
||||||
|
# RUN TRAINING
|
||||||
|
if hyperparams.on_cluster:
|
||||||
|
# Gets called when running via HPC cluster
|
||||||
|
print('RUNNING ON SLURM CLUSTER')
|
||||||
|
os.environ["CUDA_VISIBLE_DEVICES"] = ','.join(gpu_ids)
|
||||||
|
optimize_on_cluster(hyperparams)
|
||||||
|
|
||||||
|
elif hyperparams.single_run_gpu:
|
||||||
|
# run on 1 gpu
|
||||||
|
print(f'RUNNING 1 TRIAL ON GPU. gpu: {gpu_ids[0]}')
|
||||||
|
os.environ["CUDA_VISIBLE_DEVICES"] = gpu_ids[0]
|
||||||
|
main(hyperparams, None, None)
|
||||||
|
|
||||||
|
elif hyperparams.local or hyperparams.single_run:
|
||||||
|
# run 1 trial but on CPU
|
||||||
|
os.environ["CUDA_VISIBLE_DEVICES"] = '0'
|
||||||
|
print('RUNNING LOCALLY')
|
||||||
|
main(hyperparams, None, None)
|
||||||
|
|
||||||
|
else:
|
||||||
|
# multiple GPUs on same machine
|
||||||
|
print(f'RUNNING MULTI GPU. GPU ids: {gpu_ids}')
|
||||||
|
hyperparams.optimize_parallel_gpu(
|
||||||
|
main_local,
|
||||||
|
gpu_ids=gpu_ids,
|
||||||
|
nb_trials=hyperparams.nb_hopt_trials,
|
||||||
|
nb_workers=len(gpu_ids)
|
||||||
|
)
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = [
|
||||||
|
"setuptools",
|
||||||
|
"wheel",
|
||||||
|
]
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
import torch.nn as nn
|
|
||||||
import numpy as np
|
|
||||||
|
|
||||||
from test_tube import HyperOptArgumentParser
|
|
||||||
import torch
|
|
||||||
from torch.autograd import Variable
|
|
||||||
from sklearn.metrics import confusion_matrix, f1_score
|
|
||||||
from torch.nn import functional as F
|
|
||||||
|
|
||||||
|
|
||||||
class BiLSTMPack(nn.Module):
|
|
||||||
"""
|
|
||||||
Sample model to show how to define a template
|
|
||||||
"""
|
|
||||||
def __init__(self, hparams):
|
|
||||||
# init superclass
|
|
||||||
super(BiLSTMPack, self).__init__(hparams)
|
|
||||||
|
|
||||||
self.hidden = None
|
|
||||||
|
|
||||||
# trigger tag building
|
|
||||||
self.ner_tagset = {'O': 0, 'I-Bio': 1}
|
|
||||||
self.nb_tags = len(self.ner_tagset)
|
|
||||||
|
|
||||||
# build model
|
|
||||||
print('building model...')
|
|
||||||
if hparams.model_load_weights_path is None:
|
|
||||||
self.__build_model()
|
|
||||||
print('model built')
|
|
||||||
else:
|
|
||||||
self = BiLSTMPack.load(hparams.model_load_weights_path, hparams.on_gpu, hparams)
|
|
||||||
print('model loaded from: {}'.format(hparams.model_load_weights_path))
|
|
||||||
|
|
||||||
def __build_model(self):
|
|
||||||
"""
|
|
||||||
Layout model
|
|
||||||
:return:
|
|
||||||
"""
|
|
||||||
# design the number of final units
|
|
||||||
self.output_dim = self.hparams.nb_lstm_units
|
|
||||||
|
|
||||||
# when it's bidirectional our weights double
|
|
||||||
if self.hparams.bidirectional:
|
|
||||||
self.output_dim *= 2
|
|
||||||
|
|
||||||
# total number of words
|
|
||||||
total_words = len(self.tng_dataloader.dataset.words_token_to_idx)
|
|
||||||
|
|
||||||
# word embeddings
|
|
||||||
self.word_embedding = nn.Embedding(
|
|
||||||
num_embeddings=total_words + 1,
|
|
||||||
embedding_dim=self.hparams.embedding_dim,
|
|
||||||
padding_idx=0
|
|
||||||
)
|
|
||||||
|
|
||||||
# design the LSTM
|
|
||||||
self.lstm = nn.LSTM(
|
|
||||||
self.hparams.embedding_dim,
|
|
||||||
self.hparams.nb_lstm_units,
|
|
||||||
num_layers=self.hparams.nb_lstm_layers,
|
|
||||||
bidirectional=self.hparams.bidirectional,
|
|
||||||
dropout=self.hparams.drop_prob,
|
|
||||||
batch_first=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
# map to tag space
|
|
||||||
self.fc_out = nn.Linear(self.output_dim, self.out_dim)
|
|
||||||
self.hidden_to_tag = nn.Linear(self.output_dim, self.nb_tags)
|
|
||||||
|
|
||||||
|
|
||||||
def init_hidden(self, batch_size):
|
|
||||||
|
|
||||||
# the weights are of the form (nb_layers * 2 if bidirectional, batch_size, nb_lstm_units)
|
|
||||||
mult = 2 if self.hparams.bidirectional else 1
|
|
||||||
hidden_a = torch.randn(self.hparams.nb_layers * mult, batch_size, self.nb_rnn_units)
|
|
||||||
hidden_b = torch.randn(self.hparams.nb_layers * mult, batch_size, self.nb_rnn_units)
|
|
||||||
|
|
||||||
if self.hparams.on_gpu:
|
|
||||||
hidden_a = hidden_a.cuda()
|
|
||||||
hidden_b = hidden_b.cuda()
|
|
||||||
|
|
||||||
hidden_a = Variable(hidden_a)
|
|
||||||
hidden_b = Variable(hidden_b)
|
|
||||||
|
|
||||||
return (hidden_a, hidden_b)
|
|
||||||
|
|
||||||
def forward(self, model_in):
|
|
||||||
# layout data (expand it, etc...)
|
|
||||||
# x = sequences
|
|
||||||
x, seq_lengths = model_in
|
|
||||||
batch_size, seq_len = x.size()
|
|
||||||
|
|
||||||
# reset RNN hidden state
|
|
||||||
self.hidden = self.init_hidden(batch_size)
|
|
||||||
|
|
||||||
# embed
|
|
||||||
x = self.word_embedding(x)
|
|
||||||
|
|
||||||
# run through rnn using packed sequences
|
|
||||||
x = torch.nn.utils.rnn.pack_padded_sequence(x, seq_lengths, batch_first=True)
|
|
||||||
x, self.hidden = self.lstm(x, self.hidden)
|
|
||||||
x, _ = torch.nn.utils.rnn.pad_packed_sequence(x, batch_first=True)
|
|
||||||
|
|
||||||
# if asked for only last state, use the h_n which is the same as out(t=n)
|
|
||||||
if not self.return_sequence:
|
|
||||||
# pull out hidden states
|
|
||||||
# h_n = (nb_directions * nb_layers, batch_size, emb_size)
|
|
||||||
nb_directions = 2 if self.bidirectional else 1
|
|
||||||
(h_n, _) = self.hidden
|
|
||||||
|
|
||||||
# reshape to make indexing easier
|
|
||||||
# forward = 0, backward = 1 (of nb_directions)
|
|
||||||
h_n = h_n.view(self.nb_layers, nb_directions, batch_size, self.nb_rnn_units)
|
|
||||||
|
|
||||||
# pull out last forward
|
|
||||||
forward_h_n = h_n[-1, 0, :, :]
|
|
||||||
x = forward_h_n
|
|
||||||
|
|
||||||
# if bidirectional, also pull out the last hidden of backward network
|
|
||||||
if self.bidirectional:
|
|
||||||
backward_h_n = h_n[-1, 1, :, :]
|
|
||||||
x = torch.cat([forward_h_n, backward_h_n], dim=1)
|
|
||||||
|
|
||||||
# project to tag space
|
|
||||||
x = x.contiguous()
|
|
||||||
x = x.view(-1, self.output_dim)
|
|
||||||
x = self.hidden_to_tag(x)
|
|
||||||
|
|
||||||
return x
|
|
||||||
|
|
||||||
def loss(self, model_out):
|
|
||||||
# cross entropy loss
|
|
||||||
logits, y = model_out
|
|
||||||
y, y_lens = y
|
|
||||||
|
|
||||||
# flatten y and logits
|
|
||||||
y = y.view(-1)
|
|
||||||
logits = logits.view(-1, self.nb_tags)
|
|
||||||
|
|
||||||
# calculate a mask to remove padding tokens
|
|
||||||
mask = (y >= 0).float()
|
|
||||||
|
|
||||||
# count how many tokens we have
|
|
||||||
num_tokens = int(torch.sum(mask).data[0])
|
|
||||||
|
|
||||||
# pick the correct values and mask out
|
|
||||||
logits = logits[range(logits.shape[0]), y] * mask
|
|
||||||
|
|
||||||
# compute the ce loss
|
|
||||||
ce_loss = -torch.sum(logits)/num_tokens
|
|
||||||
|
|
||||||
return ce_loss
|
|
||||||
|
|
||||||
def pull_out_last_embedding(self, x, seq_lengths, batch_size, on_gpu):
|
|
||||||
# grab only the last activations from the non-padded ouput
|
|
||||||
x_last = torch.zeros([batch_size, 1, x.size(-1)])
|
|
||||||
for i, seq_len in enumerate(seq_lengths):
|
|
||||||
x_last[i, :, :] = x[i, seq_len-1, :]
|
|
||||||
|
|
||||||
# put on gpu when requested
|
|
||||||
if on_gpu:
|
|
||||||
x_last = x_last.cuda()
|
|
||||||
|
|
||||||
# turn into torch var
|
|
||||||
x_last = Variable(x_last)
|
|
||||||
|
|
||||||
return x_last
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
[tool:pytest]
|
||||||
|
norecursedirs =
|
||||||
|
.git
|
||||||
|
dist
|
||||||
|
build
|
||||||
|
python_files =
|
||||||
|
test_*.py
|
||||||
|
doctest_plus = disabled
|
||||||
|
addopts = --strict
|
||||||
|
markers =
|
||||||
|
slow
|
||||||
|
remote_data
|
||||||
|
filterwarnings
|
||||||
|
|
||||||
|
[pycodestyle]
|
||||||
|
ignore = E731,W504
|
||||||
|
max-line-length = 120
|
||||||
|
|
||||||
|
[flake8]
|
||||||
|
ignore = E731,W504,F401,F841
|
||||||
|
max-line-length = 120
|
||||||
@@ -1,13 +1,58 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
from setuptools import setup, find_packages
|
from setuptools import setup, find_packages, os
|
||||||
|
|
||||||
setup(name='pytorch-lightning',
|
# https://packaging.python.org/guides/single-sourcing-package-version/
|
||||||
version='0.0.1',
|
version = {}
|
||||||
description='Rapid research framework',
|
with open(os.path.join("src", "pytorch-lightning", "__init__.py")) as fp:
|
||||||
author='',
|
exec(fp.read(), version)
|
||||||
author_email='',
|
|
||||||
url='https://github.com/williamFalcon/pytorch-lightning',
|
# http://blog.ionelmc.ro/2014/05/25/python-packaging/
|
||||||
install_requires=[],
|
setup(
|
||||||
packages=find_packages()
|
name="pytorch-lightning",
|
||||||
)
|
version=version["__version__"],
|
||||||
|
description="The Keras for ML researchers using PyTorch",
|
||||||
|
author="William Falcon",
|
||||||
|
author_email="waf2107@columbia.edu",
|
||||||
|
url="https://github.com/williamFalcon/pytorch-lightning",
|
||||||
|
download_url="https://github.com/williamFalcon/pytorch-lightning",
|
||||||
|
license="MIT",
|
||||||
|
keywords=["deep learning", "pytorch", "AI"],
|
||||||
|
python_requires=">=3.5",
|
||||||
|
install_requires=[
|
||||||
|
"torch",
|
||||||
|
"tqdm",
|
||||||
|
"test-tube",
|
||||||
|
],
|
||||||
|
extras_require={
|
||||||
|
"dev": [
|
||||||
|
"black ; python_version>='3.6'",
|
||||||
|
"coverage",
|
||||||
|
"isort",
|
||||||
|
"pytest",
|
||||||
|
"pytest-cov<2.6.0",
|
||||||
|
"pycodestyle",
|
||||||
|
"sphinx",
|
||||||
|
"nbsphinx",
|
||||||
|
"ipython>=5.0",
|
||||||
|
"jupyter-client",
|
||||||
|
]
|
||||||
|
},
|
||||||
|
packages=find_packages("src"),
|
||||||
|
package_dir={"": "src"},
|
||||||
|
classifiers=[
|
||||||
|
"Development Status :: 4 - Beta",
|
||||||
|
"Intended Audience :: Education",
|
||||||
|
"Intended Audience :: Science/Research",
|
||||||
|
"License :: OSI Approved :: MIT License",
|
||||||
|
"Operating System :: OS Independent",
|
||||||
|
"Programming Language :: Python",
|
||||||
|
"Programming Language :: Python :: 3",
|
||||||
|
"Programming Language :: Python :: 3.5",
|
||||||
|
"Programming Language :: Python :: 3.6",
|
||||||
|
"Programming Language :: Python :: 3.7",
|
||||||
|
],
|
||||||
|
long_description=open("README.md", encoding="utf-8").read(),
|
||||||
|
include_package_data=True,
|
||||||
|
zip_safe=False,
|
||||||
|
)
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
"""
|
||||||
|
=================
|
||||||
|
pytorch-lightning
|
||||||
|
=================
|
||||||
|
|
||||||
|
The Keras for ML researchers using PyTorch. More control. Less boilerplate.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
__version__ = "0.1.dev01"
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import torch
|
import torch
|
||||||
import tqdm
|
import tqdm
|
||||||
import numpy as np
|
import numpy as np
|
||||||
from research_lib.root_module.memory import get_gpu_memory_map
|
from pytorch_lightning.root_module.memory import get_gpu_memory_map
|
||||||
import traceback
|
import traceback
|
||||||
from research_lib.root_module.model_saving import TrainerIO
|
from pytorch_lightning.root_module.model_saving import TrainerIO
|
||||||
from torch.optim.lr_scheduler import MultiStepLR
|
from torch.optim.lr_scheduler import MultiStepLR
|
||||||
|
|
||||||
|
|
||||||
@@ -11,17 +11,17 @@ class Trainer(TrainerIO):
|
|||||||
|
|
||||||
def __init__(self,
|
def __init__(self,
|
||||||
experiment,
|
experiment,
|
||||||
cluster,
|
|
||||||
checkpoint_callback, early_stop_callback,
|
checkpoint_callback, early_stop_callback,
|
||||||
|
cluster=None,
|
||||||
process_position=0,
|
process_position=0,
|
||||||
current_gpu_name=0,
|
current_gpu_name=0,
|
||||||
on_gpu=False,
|
on_gpu=False,
|
||||||
enable_tqdm=True,
|
enable_tqdm=True,
|
||||||
overfit_pct=None,
|
overfit_pct=0.0,
|
||||||
track_grad_norm=-1,
|
track_grad_norm=-1,
|
||||||
check_val_every_n_epoch=1,
|
check_val_every_n_epoch=1,
|
||||||
fast_dev_run=False,
|
fast_dev_run=False,
|
||||||
accumulate_grad_batches=False,
|
accumulate_grad_batches=1,
|
||||||
enable_early_stop=True, max_nb_epochs=5, min_nb_epochs=1,
|
enable_early_stop=True, max_nb_epochs=5, min_nb_epochs=1,
|
||||||
train_percent_check=1.0, val_percent_check=1.0, test_percent_check=1.0, val_check_interval=0.95,
|
train_percent_check=1.0, val_percent_check=1.0, test_percent_check=1.0, val_check_interval=0.95,
|
||||||
log_save_interval=1, add_log_row_interval=1,
|
log_save_interval=1, add_log_row_interval=1,
|
||||||
@@ -226,7 +226,8 @@ class Trainer(TrainerIO):
|
|||||||
self.experiment.save()
|
self.experiment.save()
|
||||||
|
|
||||||
# enable cluster checkpointing
|
# enable cluster checkpointing
|
||||||
self.enable_auto_hpc_walltime_manager()
|
if self.cluster is not None:
|
||||||
|
self.enable_auto_hpc_walltime_manager()
|
||||||
|
|
||||||
# ---------------------------
|
# ---------------------------
|
||||||
# CORE TRAINING LOOP
|
# CORE TRAINING LOOP
|
||||||
@@ -265,6 +266,10 @@ class Trainer(TrainerIO):
|
|||||||
if met_batch_limit:
|
if met_batch_limit:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# give model a chance to end epoch early
|
||||||
|
if self.model.should_stop_epoch(data_batch):
|
||||||
|
break
|
||||||
|
|
||||||
# ---------------
|
# ---------------
|
||||||
# RUN TRAIN STEP
|
# RUN TRAIN STEP
|
||||||
# ---------------
|
# ---------------
|
||||||
@@ -18,3 +18,6 @@ class ModelHooks(torch.nn.Module):
|
|||||||
|
|
||||||
def on_post_performance_check(self):
|
def on_post_performance_check(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def should_stop_epoch(self, data_batch):
|
||||||
|
return False
|
||||||
+8
-1
@@ -14,7 +14,7 @@ class RootModule(GradInformation, ModelIO, OptimizerConfig, ModelHooks):
|
|||||||
def __init__(self, hparams):
|
def __init__(self, hparams):
|
||||||
super(RootModule, self).__init__()
|
super(RootModule, self).__init__()
|
||||||
self.hparams = hparams
|
self.hparams = hparams
|
||||||
self.on_gpu = hparams.on_gpu
|
|
||||||
self.dtype = torch.FloatTensor
|
self.dtype = torch.FloatTensor
|
||||||
self.exp_save_path = None
|
self.exp_save_path = None
|
||||||
self.current_epoch = 0
|
self.current_epoch = 0
|
||||||
@@ -25,6 +25,13 @@ class RootModule(GradInformation, ModelIO, OptimizerConfig, ModelHooks):
|
|||||||
self.gradient_clip = hparams.gradient_clip
|
self.gradient_clip = hparams.gradient_clip
|
||||||
self.num = 2
|
self.num = 2
|
||||||
|
|
||||||
|
# track if gpu was requested for checkpointing
|
||||||
|
self.on_gpu = False
|
||||||
|
try:
|
||||||
|
self.on_gpu = hparams.on_gpu
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
|
||||||
# computed vars for the dataloaders
|
# computed vars for the dataloaders
|
||||||
self._tng_dataloader = None
|
self._tng_dataloader = None
|
||||||
self._val_dataloader = None
|
self._val_dataloader = None
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
def add_default_args(parser, root_dir, possible_model_names, rand_seed):
|
def add_default_args(parser, root_dir, rand_seed=None, possible_model_names=None):
|
||||||
|
|
||||||
# tng, test, val check intervals
|
# tng, test, val check intervals
|
||||||
parser.add_argument('--eval_test_set', dest='eval_test_set', action='store_true', help='true = run test set also')
|
parser.add_argument('--eval_test_set', dest='eval_test_set', action='store_true', help='true = run test set also')
|
||||||
@@ -32,7 +32,9 @@ def add_default_args(parser, root_dir, possible_model_names, rand_seed):
|
|||||||
|
|
||||||
# model paths
|
# model paths
|
||||||
parser.add_argument('--model_load_weights_path', default=None, type=str)
|
parser.add_argument('--model_load_weights_path', default=None, type=str)
|
||||||
parser.add_argument('--model_name', default='', help=','.join(possible_model_names))
|
|
||||||
|
if possible_model_names is not None:
|
||||||
|
parser.add_argument('--model_name', default='', help=','.join(possible_model_names))
|
||||||
|
|
||||||
# test_tube settings
|
# test_tube settings
|
||||||
parser.add_argument('-en', '--tt_name', default='r_lib_')
|
parser.add_argument('-en', '--tt_name', default='r_lib_')
|
||||||
@@ -58,7 +60,9 @@ def add_default_args(parser, root_dir, possible_model_names, rand_seed):
|
|||||||
parser.add_argument('--overfit', default=-1, type=float, help='% of dataset to use with this option. float, or -1 for none')
|
parser.add_argument('--overfit', default=-1, type=float, help='% of dataset to use with this option. float, or -1 for none')
|
||||||
|
|
||||||
# debug args
|
# debug args
|
||||||
parser.add_argument('--random_seed', default=rand_seed, type=int)
|
if rand_seed is not None:
|
||||||
|
parser.add_argument('--random_seed', default=rand_seed, type=int)
|
||||||
|
|
||||||
parser.add_argument('--live', dest='live', action='store_true', help='runs on gpu without cluster')
|
parser.add_argument('--live', dest='live', action='store_true', help='runs on gpu without cluster')
|
||||||
parser.add_argument('--enable_debug', dest='debug', action='store_true', help='enables/disables test tube')
|
parser.add_argument('--enable_debug', dest='debug', action='store_true', help='enables/disables test tube')
|
||||||
parser.add_argument('--enable_local', dest='local', action='store_true', help='enables local tng')
|
parser.add_argument('--enable_local', dest='local', action='store_true', help='enables local tng')
|
||||||
@@ -13,11 +13,7 @@ class PretrainedEmbedding(torch.nn.Embedding):
|
|||||||
>>> emb = PretrainedEmbedding(embedding_path='glove.840B.300d.txt',embedding_dim=300, task_vocab={'hello': 1, 'world': 2})
|
>>> emb = PretrainedEmbedding(embedding_path='glove.840B.300d.txt',embedding_dim=300, task_vocab={'hello': 1, 'world': 2})
|
||||||
>>> data = torch.Tensor([[0, 1], [0, 2]]).long()
|
>>> data = torch.Tensor([[0, 1], [0, 2]]).long()
|
||||||
>>> embedded = emb(data)
|
>>> embedded = emb(data)
|
||||||
tensor([[[ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000],
|
|
||||||
[ 0.2523, 0.1018, -0.6748, ..., 0.1787, -0.5192, 0.3359]],
|
|
||||||
|
|
||||||
[[ 0.0000, 0.0000, 0.0000, ..., 0.0000, 0.0000, 0.0000],
|
|
||||||
[-0.0067, 0.2224, 0.2771, ..., 0.0594, 0.0014, 0.0987]]])
|
|
||||||
|
|
||||||
|
|
||||||
:param embedding_path:
|
:param embedding_path:
|
||||||
@@ -37,7 +33,8 @@ class PretrainedEmbedding(torch.nn.Embedding):
|
|||||||
self.weight = new_emb.weight
|
self.weight = new_emb.weight
|
||||||
|
|
||||||
# apply freeze
|
# apply freeze
|
||||||
self.weight.requires_grad = not freeze
|
should_freeze = not freeze
|
||||||
|
self.weight.requires_grad = should_freeze
|
||||||
|
|
||||||
def __load_task_specific_embeddings(self, vocab_words, embedding_path, emb_dim, freeze):
|
def __load_task_specific_embeddings(self, vocab_words, embedding_path, emb_dim, freeze):
|
||||||
"""
|
"""
|
||||||
@@ -97,11 +94,11 @@ class PretrainedEmbedding(torch.nn.Embedding):
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
emb = PretrainedEmbedding(
|
emb = PretrainedEmbedding(
|
||||||
embedding_path='/Users/waf/Developer/NGV/research-fermat/fermat/.vector_cache/glove.840B.300d.txt',
|
embedding_path='/Users/waf/Developer',
|
||||||
embedding_dim=300,
|
embedding_dim=300,
|
||||||
task_vocab={'hello': 1, 'world': 2}
|
task_vocab={'hello': 1, 'world': 2}
|
||||||
)
|
)
|
||||||
|
|
||||||
data = torch.Tensor([[0, 1], [0, 2]]).long()
|
data = torch.Tensor([[0, 1], [0, 2]]).long()
|
||||||
embedded = emb(data)
|
embedded = emb(data)
|
||||||
print(embedded)
|
print(embedded)
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
# Testing setup
|
|
||||||
|
|
||||||
## A. Enable CircleCI for your project
|
|
||||||
1. Integrate CircleCI by clicking "Set up Project" at [this link](https://circleci.com/add-projects/gh/NextGenVest).
|
|
||||||
|
|
||||||
## B. Add your own tests
|
|
||||||
1. In the /tests, emulate exactly the folder structure for your module found under /bot_seed
|
|
||||||
2. To create a test for file ```/bot_seed/folder/example.py```:
|
|
||||||
- create the file ```/tests/folder/example_test.py```
|
|
||||||
- notice the **_test**
|
|
||||||
- notice the mirror path under **/tests**
|
|
||||||
|
|
||||||
3. Your ```example_test.py``` file should have these main components
|
|
||||||
|
|
||||||
```python
|
|
||||||
# example.py
|
|
||||||
|
|
||||||
def function_i_want_to_test(x):
|
|
||||||
return x*2
|
|
||||||
|
|
||||||
def square(x):
|
|
||||||
return x*x
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
```python
|
|
||||||
# example_test.py
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
# do whatever imports you need
|
|
||||||
from app.bot_seed.folder.example import function_i_want_to_test, square
|
|
||||||
|
|
||||||
def test_function_i_want_to_test():
|
|
||||||
answer = function_i_want_to_test(4)
|
|
||||||
assert answer == 8
|
|
||||||
|
|
||||||
# -----------------------------------
|
|
||||||
# Your function must start with test_
|
|
||||||
# -----------------------------------
|
|
||||||
def test_square():
|
|
||||||
answer = square(3)
|
|
||||||
assert answer == 9
|
|
||||||
|
|
||||||
# -----------------------------------
|
|
||||||
# boilerplate (link this file to pytest)
|
|
||||||
# -----------------------------------
|
|
||||||
if __name__ == '__main__':
|
|
||||||
pytest.main([__file__])
|
|
||||||
```
|
|
||||||
|
|
||||||
## C. Add build passing badge
|
|
||||||
1. Create a CircleCI status token:
|
|
||||||
- Go here: https://circleci.com/gh/NextGenVest/your-project-name/edit#api
|
|
||||||
- Click create token
|
|
||||||
- Select status
|
|
||||||
- Type "badge status"
|
|
||||||
|
|
||||||
2. Get a copy of the markdown code:
|
|
||||||
- Go here: https://circleci.com/gh/NextGenVest/your-project-name/edit#badges
|
|
||||||
- Select master
|
|
||||||
- Select "badge status" token
|
|
||||||
- Select image URL
|
|
||||||
- Copy the image url link and change the html at the top of the root README.md file for your project
|
|
||||||
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
import pytest
|
|
||||||
|
|
||||||
"""
|
|
||||||
Example test to show how to add a test for anything in the project.
|
|
||||||
Look at the README for more instructions
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
def test_cube():
|
|
||||||
assert 27 == 27
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
pytest.main([__file__])
|
|
||||||
Reference in New Issue
Block a user