Bender Python Client
Project description
Bender Client for Python
:warning: The full DOCUMENTATION on bender-python-client can be found HERE.
Setup
- Create an account for free at bender.dreem.com
- Install bender in your Python environment with
pip install bender-client
Usage Example
Let's use the famous MNIST example where we try to recognize handwritten digits in images.
The code of the algorithm using PyTorch is the following :
To use this example, do not forget to
pip install numpy torch torchvision
.
from __future__ import print_function
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
class Net(nn.Module):
def __init__(self, dropout=True, activation="relu", kernel_size=5, conv_depth=10, linear_depth=50):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, conv_depth, kernel_size=kernel_size)
self.conv2 = nn.Conv2d(conv_depth, 20, kernel_size=kernel_size)
self.conv2_drop = nn.Dropout2d() if dropout is True else lambda x: x
self.fc1 = nn.Linear(320, linear_depth)
self.fc2 = nn.Linear(linear_depth, 10)
self.activation = getattr(F, activation)
def forward(self, x):
x = self.activation(F.max_pool2d(self.conv1(x), 2))
x = self.activation(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
x = x.view(-1, 320)
x = self.activation(self.fc1(x))
x = F.dropout(x, training=self.training)
x = self.fc2(x)
return F.log_softmax(x, dim=1)
def train(model, device, train_loader, optimizer, epoch):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = F.nll_loss(output, target)
loss.backward()
optimizer.step()
if batch_idx % 10 == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
def test(model, device, test_loader):
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += F.nll_loss(output, target, reduction='sum').item()
pred = output.max(1, keepdim=True)[1]
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /= len(test_loader.dataset)
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
return (correct / len(test_loader.dataset))
def run(epochs=3, lr=0.01, momentum=0.5, dropout=True, activation="relu", kernel_size=5, conv_depth=10, linear_depth=50):
torch.manual_seed(1)
device = torch.device("cpu")
train_loader = torch.utils.data.DataLoader(
datasets.MNIST('../data', train=True, download=True,
transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])),
batch_size=32,
shuffle=True,
)
test_loader = torch.utils.data.DataLoader(
datasets.MNIST('../data', train=False, transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])),
batch_size=1000,
shuffle=True,
)
model = Net(dropout, activation).to(device)
optimizer = optim.SGD(model.parameters(), lr=lr, momentum=momentum)
accuracy = 0
for epoch in range(1, int(epochs) + 1):
train(model, device, train_loader, optimizer, epoch)
accuracy = test(model, device, test_loader)
return accuracy
if __name__ == '__main__':
# HYPERPARAMETERS (That's what bender is interested in)
# Here we select them on our own in an arbitrary way
hyperparameters = {
"kernel_size": 5,
"epochs": 3,
"lr": 0.05,
"momentum": 0.2,
"dropout": True,
"activation": "relu",
"conv_depth": 10,
"linear_depth": 50,
}
run(
epochs=hyperparameters["epochs"],
lr=hyperparameters["lr"],
momentum=hyperparameters["momentum"],
dropout=hyperparameters["dropout"],
activation=hyperparameters["activation"],
kernel_size=hyperparameters["kernel_size"],
conv_depth=hyperparameters["conv_depth"],
linear_depth=hyperparameters["linear_depth"],
)
Now let's plug Bender into It !
- Importing Bender
from benderclient import Bender
bender = Bender()
This will ask for your email and password. The client will use these to login and retrieve a TOKEN. This TOKEN is personal, it should not be shared, it will be stored in your home folder as "
.bender_token
", and you will not be asked for your login/password again until it expires. :warning: Again, your TOKEN is personal. You should not give it or add it to any public repository :warning:
- Create an Experiment
An experiment is related to the problem you are trying to solve, here : MNIST classification
bender.new_experiment(
name='MNIST Classification',
description='Simple image classification on handwritten digits',
metrics=[
{
"metric_name": "algorithm_accuracy", # It's just a name and there can be multiple watched metrics.
"type": "reward", # The type can either be "reward" or "loss" depending on if you want to maximize or minimize it.
}
],
dataset='MNIST'
)
- Create an Algo
An algo is simply corresponding to ONE solution to an Experiment problem : here it's as we saw a Neural Net with PyTorch
bender.new_algo(
name='PyTorch_NN',
# The parameters below are actually the Hyper-Parameters of your algo described in a list
parameters= [
{
"name": 'kernel_size',
"category": "categorical",
"search_space": {
"values": [3, 5, 7],
},
},
{
"name": 'conv_depth',
"category": "uniform",
"search_space": {
"low": 1,
"high": 100,
"step": 1,
},
},
{
"name": 'linear_depth',
"category": "uniform",
"search_space": {
"low": 1,
"high": 100,
"step": 1,
},
},
{
"name": 'epochs',
"category": "uniform",
"search_space": {
"low": 1,
"high": 4,
"step": 1,
},
},
{
"name": 'lr',
"category": "loguniform",
"search_space": {
"low": 1e-5,
"high": 1e-1,
"step": 1e-6,
},
},
{
"name": 'momentum',
"category": "uniform",
"search_space": {
"low": 0,
"high": 1,
"step": 0.05,
},
},
{
"name": 'dropout',
"category": "categorical",
"search_space": {
"values": [True, False],
},
},
{
"name": 'activation',
"category": "categorical",
"search_space": {
"values": ["relu", "softmax", "sigmoid", "tanh"],
},
},
]
)
- Get an Hyperparameters Set suggestion from Bender
The whole goal of what we did up there is to use Bender to get a new set of Hyperparameters to try according to the settings of your Experiment and Algo.
suggestion = bender.suggest()
# suggestion would for example contain something like :
{
"kernel_size": 5,
"epochs": 3,
"lr": 0.05,
"momentum": 0.2,
"dropout": True,
"activation": "tanh",
"conv_depth": 10,
"linear_depth": 50,
}
- Feed a Trial to Bender
A Trial is simply an attempt of you trying a Hyperparameters Set with your algorithm associated with the result metrics obtained. If you want bender to improve over time, feed him every trial you make.
bender.new_trial(
parameters={
"kernel_size": 5,
"epochs": 3,
"lr": 0.05,
"momentum": 0.2,
"dropout": True,
"activation": "tanh",
"conv_depth": 10,
"linear_depth": 50,
},
results={
"algorithm_accuracy": 0.7, # We put an arbitrary value here just for the example.
}
)
- The full code put together
Psssssst... The magic starts at line 443... ;)
To use this example, do not forget to
pip install numpy torch torchvision bender-client
.
from __future__ import print_function
import argparse
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
from benderclient import Bender
class Net(nn.Module):
def __init__(self, dropout=True, activation="relu", kernel_size=5, conv_depth=10, linear_depth=50):
super(Net, self).__init__()
self.conv1 = nn.Conv2d(1, conv_depth, kernel_size=kernel_size)
self.conv2 = nn.Conv2d(conv_depth, 20, kernel_size=kernel_size)
self.conv2_drop = nn.Dropout2d() if dropout is True else lambda x: x
self.fc1 = nn.Linear(320, linear_depth)
self.fc2 = nn.Linear(linear_depth, 10)
self.activation = getattr(F, activation)
def forward(self, x):
x = self.activation(F.max_pool2d(self.conv1(x), 2))
x = self.activation(F.max_pool2d(self.conv2_drop(self.conv2(x)), 2))
x = x.view(-1, 320)
x = self.activation(self.fc1(x))
x = F.dropout(x, training=self.training)
x = self.fc2(x)
return F.log_softmax(x, dim=1)
def train(model, device, train_loader, optimizer, epoch):
model.train()
for batch_idx, (data, target) in enumerate(train_loader):
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = F.nll_loss(output, target)
loss.backward()
optimizer.step()
if batch_idx % 10 == 0:
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
epoch, batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
def test(model, device, test_loader):
model.eval()
test_loss = 0
correct = 0
with torch.no_grad():
for data, target in test_loader:
data, target = data.to(device), target.to(device)
output = model(data)
test_loss += F.nll_loss(output, target, reduction='sum').item()
pred = output.max(1, keepdim=True)[1]
correct += pred.eq(target.view_as(pred)).sum().item()
test_loss /= len(test_loader.dataset)
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset),
100. * correct / len(test_loader.dataset)))
return (correct / len(test_loader.dataset))
def run(epochs=3, lr=0.01, momentum=0.5, dropout=True, activation="relu", kernel_size=5, conv_depth=10, linear_depth=50):
torch.manual_seed(1)
device = torch.device("cpu")
train_loader = torch.utils.data.DataLoader(
datasets.MNIST('../data', train=True, download=True,
transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])),
batch_size=32,
shuffle=True,
)
test_loader = torch.utils.data.DataLoader(
datasets.MNIST('../data', train=False, transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))
])),
batch_size=1000,
shuffle=True,
)
model = Net(dropout, activation).to(device)
optimizer = optim.SGD(model.parameters(), lr=lr, momentum=momentum)
accuracy = 0
for epoch in range(1, int(epochs) + 1):
train(model, device, train_loader, optimizer, epoch)
accuracy = test(model, device, test_loader)
return accuracy
def init_bender():
bender = Bender()
bender.create_experiment(
name='MNIST Classification',
description='Simple image classification on handwritten digits',
metrics=[{"metric_name": "algorithm_accuracy", "type": "reward"}],
dataset='MNIST'
)
bender.create_algo(
name='PyTorch_NN',
hyperparameters= [
{
"name": 'kernel_size',
"category": "categorical",
"search_space": {
"values": [3, 5, 7],
},
},
{
"name": 'conv_depth',
"category": "uniform",
"search_space": {
"low": 1,
"high": 100,
"step": 1,
},
},
{
"name": 'linear_depth',
"category": "uniform",
"search_space": {
"low": 1,
"high": 100,
"step": 1,
},
},
{
"name": 'epochs',
"category": "uniform",
"search_space": {
"low": 1,
"high": 4,
"step": 1,
},
},
{
"name": 'lr',
"category": "loguniform",
"search_space": {
"low": 1e-5,
"high": 1e-1,
"step": 1e-6,
},
},
{
"name": 'momentum',
"category": "uniform",
"search_space": {
"low": 0,
"high": 1,
"step": 0.05,
},
},
{
"name": 'dropout',
"category": "categorical",
"search_space": {
"values": [True, False],
},
},
{
"name": 'activation',
"category": "categorical",
"search_space": {
"values": ["relu", "softmax", "sigmoid", "tanh"],
},
},
]
)
return bender
if __name__ == '__main__':
# Create experiment and algo if they don't exist yet. Else, load them from the config file ./.benderconf
bender = init_bender()
while True:
# Get a set of Hyperparameters to test
suggestion = bender.suggest(metric="algorithm_accuracy")
# Get algo result with them
result = run(
epochs=suggestion["epochs"],
lr=suggestion["lr"],
momentum=suggestion["momentum"],
dropout=suggestion["dropout"],
activation=suggestion["activation"],
kernel_size=suggestion["kernel_size"],
conv_depth=suggestion["conv_depth"],
linear_depth=suggestion["linear_depth"],
)
# Feed Bender a Trial, AKA => suggestion + result
bender.create_trial(
hyperparameters=suggestion,
results={"algorithm_accuracy": result}
)
print('New trial sent -----------------------------------------------------\n\n')
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file bender-client-0.3.2.0.tar.gz
.
File metadata
- Download URL: bender-client-0.3.2.0.tar.gz
- Upload date:
- Size: 9.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.20.1 setuptools/40.4.3 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/3.6.6
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | 97f9a104fe7ebe2c7eaf0d022c743a94ec32be9a49311677d76cc26802e14fcf |
|
MD5 | cf854471f523c2837beed826e4354c42 |
|
BLAKE2b-256 | d415f35af37b8f3ece90fa5cb6f643d6a32a4bc901e5cfc6789a2d6c646cbc5f |
File details
Details for the file bender_client-0.3.2.0-py3-none-any.whl
.
File metadata
- Download URL: bender_client-0.3.2.0-py3-none-any.whl
- Upload date:
- Size: 13.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/1.12.1 pkginfo/1.4.2 requests/2.20.1 setuptools/40.4.3 requests-toolbelt/0.8.0 tqdm/4.28.1 CPython/3.6.6
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | a52b2fa79bce3ccf7d702ef89e510735d655d525447572e0e921a237702f6d72 |
|
MD5 | e972716750ad68aa51a0a15bd9fbdb33 |
|
BLAKE2b-256 | 424c137d1c54025efa23f6a39066c97feb371a6192516c96bafecf73a45c8160 |