mirror of
https://github.com/hanzo-ml/katib.git
synced 2026-09-24 00:31:27 +00:00
Enhance pre-commit hooks with flake8 and black (#2407)
* Add black formater and flake8 linter to pre-commit Also add's the flake8 config file Signed-off-by: Ignas Baranauskas <[email protected]> * Fixes black formating Signed-off-by: Ignas Baranauskas <[email protected]> * Fixes flake8 linting errors Signed-off-by: Ignas Baranauskas <[email protected]> --------- Signed-off-by: Ignas Baranauskas <[email protected]>
This commit is contained in:
committed by
GitHub
parent
2e30a54fa5
commit
bcb199ddc3
@@ -0,0 +1,4 @@
|
||||
[flake8]
|
||||
max-line-length = 100
|
||||
# E203 is ignored to avoid conflicts with Black's formatting, as it's not PEP 8 compliant
|
||||
extend-ignore = W503, E203
|
||||
@@ -11,6 +11,16 @@ repos:
|
||||
- id: isort
|
||||
name: isort
|
||||
entry: isort --profile google
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 24.2.0
|
||||
hooks:
|
||||
- id: black
|
||||
files: (sdk|examples)/.*
|
||||
- repo: https://github.com/pycqa/flake8
|
||||
rev: 7.1.1
|
||||
hooks:
|
||||
- id: flake8
|
||||
files: (sdk|examples)/.*
|
||||
exclude: |
|
||||
(?x)^(
|
||||
.*zz_generated.deepcopy.*|
|
||||
|
||||
@@ -21,10 +21,12 @@
|
||||
|
||||
# This Experiment is similar to this:
|
||||
# https://github.com/kubeflow/katib/blob/master/examples/v1beta1/kubeflow-training-operator/mpijob-horovod.yaml
|
||||
# Check the training container source code here: https://github.com/kubeflow/mpi-operator/tree/master/examples/horovod.
|
||||
# Check the training container source code here:
|
||||
# https://github.com/kubeflow/mpi-operator/tree/master/examples/horovod.
|
||||
|
||||
# Note: To run this example, your Kubernetes cluster should run MPIJob operator.
|
||||
# Follow this guide to install MPIJob on your cluster: https://www.kubeflow.org/docs/components/training/mpi/
|
||||
# Follow this guide to install MPIJob on your cluster:
|
||||
# https://www.kubeflow.org/docs/components/training/mpi/
|
||||
|
||||
import kfp
|
||||
from kfp import components
|
||||
@@ -42,13 +44,12 @@ from kubeflow.katib import V1beta1TrialTemplate
|
||||
|
||||
@dsl.pipeline(
|
||||
name="Launch Katib MPIJob Experiment",
|
||||
description="An example to launch Katib Experiment with MPIJob"
|
||||
description="An example to launch Katib Experiment with MPIJob",
|
||||
)
|
||||
def horovod_mnist_hpo(
|
||||
experiment_name: str = "mpi-horovod-mnist",
|
||||
experiment_namespace: str = "kubeflow-user-example-com",
|
||||
):
|
||||
|
||||
# Trial count specification.
|
||||
max_trial_count = 6
|
||||
max_failed_trial_count = 3
|
||||
@@ -64,12 +65,7 @@ def horovod_mnist_hpo(
|
||||
# Algorithm specification.
|
||||
algorithm = V1beta1AlgorithmSpec(
|
||||
algorithm_name="bayesianoptimization",
|
||||
algorithm_settings=[
|
||||
V1beta1AlgorithmSetting(
|
||||
name="random_state",
|
||||
value="10"
|
||||
)
|
||||
]
|
||||
algorithm_settings=[V1beta1AlgorithmSetting(name="random_state", value="10")],
|
||||
)
|
||||
|
||||
# Experiment search space.
|
||||
@@ -78,19 +74,12 @@ def horovod_mnist_hpo(
|
||||
V1beta1ParameterSpec(
|
||||
name="lr",
|
||||
parameter_type="double",
|
||||
feasible_space=V1beta1FeasibleSpace(
|
||||
min="0.001",
|
||||
max="0.003"
|
||||
),
|
||||
feasible_space=V1beta1FeasibleSpace(min="0.001", max="0.003"),
|
||||
),
|
||||
V1beta1ParameterSpec(
|
||||
name="num-steps",
|
||||
parameter_type="int",
|
||||
feasible_space=V1beta1FeasibleSpace(
|
||||
min="50",
|
||||
max="150",
|
||||
step="10"
|
||||
),
|
||||
feasible_space=V1beta1FeasibleSpace(min="50", max="150", step="10"),
|
||||
),
|
||||
]
|
||||
|
||||
@@ -106,18 +95,14 @@ def horovod_mnist_hpo(
|
||||
"replicas": 1,
|
||||
"template": {
|
||||
"metadata": {
|
||||
"annotations": {
|
||||
"sidecar.istio.io/inject": "false"
|
||||
}
|
||||
"annotations": {"sidecar.istio.io/inject": "false"}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
"image": "docker.io/kubeflow/mpi-horovod-mnist",
|
||||
"name": "mpi-launcher",
|
||||
"command": [
|
||||
"mpirun"
|
||||
],
|
||||
"command": ["mpirun"],
|
||||
"args": [
|
||||
"-np",
|
||||
"2",
|
||||
@@ -141,26 +126,21 @@ def horovod_mnist_hpo(
|
||||
"--lr",
|
||||
"${trialParameters.learningRate}",
|
||||
"--num-steps",
|
||||
"${trialParameters.numberSteps}"
|
||||
"${trialParameters.numberSteps}",
|
||||
],
|
||||
"resources": {
|
||||
"limits": {
|
||||
"cpu": "500m",
|
||||
"memory": "2Gi"
|
||||
}
|
||||
}
|
||||
"limits": {"cpu": "500m", "memory": "2Gi"}
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
"Worker": {
|
||||
"replicas": 2,
|
||||
"template": {
|
||||
"metadata": {
|
||||
"annotations": {
|
||||
"sidecar.istio.io/inject": "false"
|
||||
}
|
||||
"annotations": {"sidecar.istio.io/inject": "false"}
|
||||
},
|
||||
"spec": {
|
||||
"containers": [
|
||||
@@ -168,25 +148,20 @@ def horovod_mnist_hpo(
|
||||
"image": "docker.io/kubeflow/mpi-horovod-mnist",
|
||||
"name": "mpi-worker",
|
||||
"resources": {
|
||||
"limits": {
|
||||
"cpu": "500m",
|
||||
"memory": "4Gi"
|
||||
}
|
||||
}
|
||||
"limits": {"cpu": "500m", "memory": "4Gi"}
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
# Configure parameters for the Trial template.
|
||||
trial_template = V1beta1TrialTemplate(
|
||||
primary_pod_labels={
|
||||
"mpi-job-role": "launcher"
|
||||
},
|
||||
primary_pod_labels={"mpi-job-role": "launcher"},
|
||||
primary_container_name="mpi-launcher",
|
||||
success_condition='status.conditions.#(type=="Succeeded")#|#(status=="True")#',
|
||||
failure_condition='status.conditions.#(type=="Failed")#|#(status=="True")#',
|
||||
@@ -194,15 +169,15 @@ def horovod_mnist_hpo(
|
||||
V1beta1TrialParameterSpec(
|
||||
name="learningRate",
|
||||
description="Learning rate for the training model",
|
||||
reference="lr"
|
||||
reference="lr",
|
||||
),
|
||||
V1beta1TrialParameterSpec(
|
||||
name="numberSteps",
|
||||
description="Number of training steps",
|
||||
reference="num-steps"
|
||||
reference="num-steps",
|
||||
),
|
||||
],
|
||||
trial_spec=trial_spec
|
||||
trial_spec=trial_spec,
|
||||
)
|
||||
|
||||
# Create Experiment specification.
|
||||
@@ -213,13 +188,15 @@ def horovod_mnist_hpo(
|
||||
objective=objective,
|
||||
algorithm=algorithm,
|
||||
parameters=parameters,
|
||||
trial_template=trial_template
|
||||
trial_template=trial_template,
|
||||
)
|
||||
|
||||
# Get the Katib launcher.
|
||||
# Load component from the URL or from the file.
|
||||
katib_experiment_launcher_op = components.load_component_from_url(
|
||||
"https://raw.githubusercontent.com/kubeflow/pipelines/master/components/kubeflow/katib-launcher/component.yaml")
|
||||
"https://raw.githubusercontent.com/kubeflow/pipelines/master/"
|
||||
"components/kubeflow/katib-launcher/component.yaml"
|
||||
)
|
||||
# katib_experiment_launcher_op = components.load_component_from_file(
|
||||
# "../../../components/kubeflow/katib-launcher/component.yaml"
|
||||
# )
|
||||
@@ -231,7 +208,8 @@ def horovod_mnist_hpo(
|
||||
experiment_name=experiment_name,
|
||||
experiment_namespace=experiment_namespace,
|
||||
experiment_spec=ApiClient().sanitize_for_serialization(experiment_spec),
|
||||
experiment_timeout_minutes=60)
|
||||
experiment_timeout_minutes=60,
|
||||
)
|
||||
|
||||
# Output container to print the results.
|
||||
dsl.ContainerOp(
|
||||
|
||||
@@ -17,9 +17,8 @@ import copy
|
||||
import torch
|
||||
|
||||
|
||||
class Architect():
|
||||
"""" Architect controls architecture of cell by computing gradients of alphas
|
||||
"""
|
||||
class Architect:
|
||||
""" " Architect controls architecture of cell by computing gradients of alphas"""
|
||||
|
||||
def __init__(self, model, w_momentum, w_weight_decay, device):
|
||||
self.model = model
|
||||
@@ -54,19 +53,26 @@ class Architect():
|
||||
with torch.no_grad():
|
||||
# dict key is not the value, but the pointer. So original network weight have to
|
||||
# be iterated also.
|
||||
for w, vw, g in zip(self.model.getWeights(), self.v_model.getWeights(), gradients):
|
||||
m = w_optim.state[w].get("momentum_buffer", 0.) * self.w_momentum
|
||||
if(self.device == 'cuda'):
|
||||
vw.copy_(w - torch.cuda.FloatTensor(xi) * (m + g + self.w_weight_decay * w))
|
||||
elif(self.device == 'cpu'):
|
||||
vw.copy_(w - torch.FloatTensor(xi) * (m + g + self.w_weight_decay * w))
|
||||
for w, vw, g in zip(
|
||||
self.model.getWeights(), self.v_model.getWeights(), gradients
|
||||
):
|
||||
m = w_optim.state[w].get("momentum_buffer", 0.0) * self.w_momentum
|
||||
if self.device == "cuda":
|
||||
vw.copy_(
|
||||
w
|
||||
- torch.cuda.FloatTensor(xi) * (m + g + self.w_weight_decay * w)
|
||||
)
|
||||
elif self.device == "cpu":
|
||||
vw.copy_(
|
||||
w - torch.FloatTensor(xi) * (m + g + self.w_weight_decay * w)
|
||||
)
|
||||
|
||||
# Sync alphas
|
||||
for a, va in zip(self.model.getAlphas(), self.v_model.getAlphas()):
|
||||
va.copy_(a)
|
||||
|
||||
def unrolled_backward(self, train_x, train_y, valid_x, valid_y, xi, w_optim):
|
||||
""" Compute unrolled loss and backward its gradients
|
||||
"""Compute unrolled loss and backward its gradients
|
||||
Args:
|
||||
xi: learning rate for virtual gradient step (same as model lr)
|
||||
w_optim: weights optimizer - for virtual step
|
||||
@@ -83,17 +89,17 @@ class Architect():
|
||||
v_weights = tuple(self.v_model.getWeights())
|
||||
v_grads = torch.autograd.grad(loss, v_alphas + v_weights)
|
||||
|
||||
dalpha = v_grads[:len(v_alphas)]
|
||||
dws = v_grads[len(v_alphas):]
|
||||
dalpha = v_grads[: len(v_alphas)]
|
||||
dws = v_grads[len(v_alphas) :]
|
||||
|
||||
hessian = self.compute_hessian(dws, train_x, train_y)
|
||||
|
||||
# Update final gradient = dalpha - xi * hessian
|
||||
with torch.no_grad():
|
||||
for alpha, da, h in zip(self.model.getAlphas(), dalpha, hessian):
|
||||
if(self.device == 'cuda'):
|
||||
if self.device == "cuda":
|
||||
alpha.grad = da - torch.cuda.FloatTensor(xi) * h
|
||||
elif(self.device == 'cpu'):
|
||||
elif self.device == "cpu":
|
||||
alpha.grad = da - torch.cpu.FloatTensor(xi) * h
|
||||
|
||||
def compute_hessian(self, dws, train_x, train_y):
|
||||
@@ -121,7 +127,7 @@ class Architect():
|
||||
with torch.no_grad():
|
||||
for p, dw in zip(self.model.getWeights(), dws):
|
||||
# TODO (andreyvelich): Do we need this * 2.0 ?
|
||||
p -= 2. * eps * dw
|
||||
p -= 2.0 * eps * dw
|
||||
|
||||
loss = self.model.loss(train_x, train_y)
|
||||
# dalpha { L_train(w-, alpha) }
|
||||
@@ -132,5 +138,7 @@ class Architect():
|
||||
for p, dw in zip(self.model.getWeights(), dws):
|
||||
p += eps * dw
|
||||
|
||||
hessian = [(p-n) / (2. * eps) for p, n in zip(dalpha_positive, dalpha_negative)]
|
||||
hessian = [
|
||||
(p - n) / (2.0 * eps) for p, n in zip(dalpha_positive, dalpha_negative)
|
||||
]
|
||||
return hessian
|
||||
|
||||
@@ -21,11 +21,20 @@ import torch.nn.functional as F
|
||||
|
||||
|
||||
class Cell(nn.Module):
|
||||
""" Cell for search
|
||||
"""Cell for search
|
||||
Each edge is mixed and continuous relaxed.
|
||||
"""
|
||||
|
||||
def __init__(self, num_nodes, c_prev_prev, c_prev, c_cur, reduction_prev, reduction_cur, search_space):
|
||||
def __init__(
|
||||
self,
|
||||
num_nodes,
|
||||
c_prev_prev,
|
||||
c_prev,
|
||||
c_cur,
|
||||
reduction_prev,
|
||||
reduction_cur,
|
||||
search_space,
|
||||
):
|
||||
"""
|
||||
Args:
|
||||
num_nodes: Number of intermediate cell nodes
|
||||
@@ -45,7 +54,9 @@ class Cell(nn.Module):
|
||||
if reduction_prev:
|
||||
self.preprocess0 = FactorizedReduce(c_prev_prev, c_cur)
|
||||
else:
|
||||
self.preprocess0 = StdConv(c_prev_prev, c_cur, kernel_size=1, stride=1, padding=0)
|
||||
self.preprocess0 = StdConv(
|
||||
c_prev_prev, c_cur, kernel_size=1, stride=1, padding=0
|
||||
)
|
||||
self.preprocess1 = StdConv(c_prev, c_cur, kernel_size=1, stride=1, padding=0)
|
||||
|
||||
# Generate dag from mixed operations
|
||||
@@ -54,7 +65,7 @@ class Cell(nn.Module):
|
||||
for i in range(self.num_nodes):
|
||||
self.dag_ops.append(nn.ModuleList())
|
||||
# Include 2 input nodes
|
||||
for j in range(2+i):
|
||||
for j in range(2 + i):
|
||||
# Reduction with stride = 2 must be only for the input node
|
||||
stride = 2 if reduction_cur and j < 2 else 1
|
||||
op = MixedOp(c_cur, stride, search_space)
|
||||
@@ -66,7 +77,9 @@ class Cell(nn.Module):
|
||||
|
||||
states = [s0, s1]
|
||||
for edges, w_list in zip(self.dag_ops, w_dag):
|
||||
state_cur = sum(edges[i](s, w) for i, (s, w) in enumerate((zip(states, w_list))))
|
||||
state_cur = sum(
|
||||
edges[i](s, w) for i, (s, w) in enumerate((zip(states, w_list)))
|
||||
)
|
||||
states.append(state_cur)
|
||||
|
||||
state_out = torch.cat(states[2:], dim=1)
|
||||
@@ -75,8 +88,17 @@ class Cell(nn.Module):
|
||||
|
||||
class NetworkCNN(nn.Module):
|
||||
|
||||
def __init__(self, init_channels, input_channels, num_classes,
|
||||
num_layers, criterion, search_space, num_nodes, stem_multiplier):
|
||||
def __init__(
|
||||
self,
|
||||
init_channels,
|
||||
input_channels,
|
||||
num_classes,
|
||||
num_layers,
|
||||
criterion,
|
||||
search_space,
|
||||
num_nodes,
|
||||
stem_multiplier,
|
||||
):
|
||||
super(NetworkCNN, self).__init__()
|
||||
|
||||
self.init_channels = init_channels
|
||||
@@ -87,11 +109,11 @@ class NetworkCNN(nn.Module):
|
||||
self.num_nodes = num_nodes
|
||||
self.stem_multiplier = stem_multiplier
|
||||
|
||||
c_cur = self.stem_multiplier*self.init_channels
|
||||
c_cur = self.stem_multiplier * self.init_channels
|
||||
|
||||
self.stem = nn.Sequential(
|
||||
nn.Conv2d(input_channels, c_cur, 3, padding=1, bias=False),
|
||||
nn.BatchNorm2d(c_cur)
|
||||
nn.BatchNorm2d(c_cur),
|
||||
)
|
||||
|
||||
# In first Cell stem is used for s0 and s1
|
||||
@@ -110,14 +132,24 @@ class NetworkCNN(nn.Module):
|
||||
# For Network with two layers: First layer - Normal, Second - Reduction
|
||||
# For Other Networks: [1/3, 2/3] Layers - Reduction cell with double channels
|
||||
# Others - Normal cell
|
||||
if ((self.num_layers == 2 and i == 1) or
|
||||
(self.num_layers > 2 and i in [self.num_layers//3, 2*self.num_layers//3])):
|
||||
if (self.num_layers == 2 and i == 1) or (
|
||||
self.num_layers > 2
|
||||
and i in [self.num_layers // 3, 2 * self.num_layers // 3]
|
||||
):
|
||||
c_cur *= 2
|
||||
reduction_cur = True
|
||||
else:
|
||||
reduction_cur = False
|
||||
|
||||
cell = Cell(self.num_nodes, c_prev_prev, c_prev, c_cur, reduction_prev, reduction_cur, search_space)
|
||||
cell = Cell(
|
||||
self.num_nodes,
|
||||
c_prev_prev,
|
||||
c_prev,
|
||||
c_cur,
|
||||
reduction_prev,
|
||||
reduction_cur,
|
||||
search_space,
|
||||
)
|
||||
reduction_prev = reduction_cur
|
||||
self.cells.append(cell)
|
||||
|
||||
@@ -134,9 +166,11 @@ class NetworkCNN(nn.Module):
|
||||
self.alpha_reduce = nn.ParameterList()
|
||||
|
||||
for i in range(self.num_nodes):
|
||||
self.alpha_normal.append(nn.Parameter(1e-3*torch.randn(i+2, num_ops)))
|
||||
self.alpha_normal.append(nn.Parameter(1e-3 * torch.randn(i + 2, num_ops)))
|
||||
if self.num_layers > 1:
|
||||
self.alpha_reduce.append(nn.Parameter(1e-3*torch.randn(i+2, num_ops)))
|
||||
self.alpha_reduce.append(
|
||||
nn.Parameter(1e-3 * torch.randn(i + 2, num_ops))
|
||||
)
|
||||
|
||||
# Setup alphas list
|
||||
self.alphas = []
|
||||
@@ -192,5 +226,9 @@ class NetworkCNN(nn.Module):
|
||||
# concat all intermediate nodes
|
||||
concat = range(2, 2 + self.num_nodes)
|
||||
|
||||
return search_space.genotype(normal=gene_normal, normal_concat=concat,
|
||||
reduce=gene_reduce, reduce_concat=concat)
|
||||
return search_space.genotype(
|
||||
normal=gene_normal,
|
||||
normal_concat=concat,
|
||||
reduce=gene_reduce,
|
||||
reduce_concat=concat,
|
||||
)
|
||||
|
||||
@@ -16,18 +16,30 @@ import torch
|
||||
import torch.nn as nn
|
||||
|
||||
OPS = {
|
||||
'none': lambda channels, stride: Zero(stride),
|
||||
'avg_pooling_3x3': lambda channels, stride: PoolBN('avg', channels, kernel_size=3, stride=stride, padding=1),
|
||||
'max_pooling_3x3': lambda channels, stride: PoolBN('max', channels, kernel_size=3, stride=stride, padding=1),
|
||||
'skip_connection': lambda channels, stride: Identity() if stride == 1 else FactorizedReduce(channels, channels),
|
||||
'separable_convolution_3x3': lambda channels, stride: SepConv(channels, kernel_size=3, stride=stride, padding=1),
|
||||
'separable_convolution_5x5': lambda channels, stride: SepConv(channels, kernel_size=5, stride=stride, padding=2),
|
||||
"none": lambda channels, stride: Zero(stride),
|
||||
"avg_pooling_3x3": lambda channels, stride: PoolBN(
|
||||
"avg", channels, kernel_size=3, stride=stride, padding=1
|
||||
),
|
||||
"max_pooling_3x3": lambda channels, stride: PoolBN(
|
||||
"max", channels, kernel_size=3, stride=stride, padding=1
|
||||
),
|
||||
"skip_connection": lambda channels, stride: (
|
||||
Identity() if stride == 1 else FactorizedReduce(channels, channels)
|
||||
),
|
||||
"separable_convolution_3x3": lambda channels, stride: SepConv(
|
||||
channels, kernel_size=3, stride=stride, padding=1
|
||||
),
|
||||
"separable_convolution_5x5": lambda channels, stride: SepConv(
|
||||
channels, kernel_size=5, stride=stride, padding=2
|
||||
),
|
||||
# 3x3 -> 5x5
|
||||
'dilated_convolution_3x3': lambda channels, stride: DilConv(channels,
|
||||
kernel_size=3, stride=stride, padding=2, dilation=2),
|
||||
"dilated_convolution_3x3": lambda channels, stride: DilConv(
|
||||
channels, kernel_size=3, stride=stride, padding=2, dilation=2
|
||||
),
|
||||
# 5x5 -> 9x9
|
||||
'dilated_convolution_5x5': lambda channels, stride: DilConv(channels,
|
||||
kernel_size=5, stride=stride, padding=4, dilation=2),
|
||||
"dilated_convolution_5x5": lambda channels, stride: DilConv(
|
||||
channels, kernel_size=5, stride=stride, padding=4, dilation=2
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
@@ -42,9 +54,9 @@ class Zero(nn.Module):
|
||||
|
||||
def forward(self, x):
|
||||
if self.stride == 1:
|
||||
return x * 0.
|
||||
return x * 0.0
|
||||
# Resize by stride
|
||||
return x[:, :, ::self.stride, ::self.stride] * 0.
|
||||
return x[:, :, :: self.stride, :: self.stride] * 0.0
|
||||
|
||||
|
||||
class PoolBN(nn.Module):
|
||||
@@ -55,15 +67,14 @@ class PoolBN(nn.Module):
|
||||
def __init__(self, pool_type, channels, kernel_size, stride, padding):
|
||||
super(PoolBN, self).__init__()
|
||||
if pool_type == "avg":
|
||||
self.pool = nn.AvgPool2d(kernel_size, stride, padding, count_include_pad=False)
|
||||
self.pool = nn.AvgPool2d(
|
||||
kernel_size, stride, padding, count_include_pad=False
|
||||
)
|
||||
elif pool_type == "max":
|
||||
self.pool = nn.MaxPool2d(kernel_size, stride, padding)
|
||||
|
||||
self.bn = nn.BatchNorm2d(channels, affine=False)
|
||||
self.net = nn.Sequential(
|
||||
self.pool,
|
||||
self.bn
|
||||
)
|
||||
self.net = nn.Sequential(self.pool, self.bn)
|
||||
|
||||
def forward(self, x):
|
||||
# out = self.pool(x),
|
||||
@@ -91,8 +102,12 @@ class FactorizedReduce(nn.Module):
|
||||
def __init__(self, c_in, c_out):
|
||||
super(FactorizedReduce, self).__init__()
|
||||
self.relu = nn.ReLU()
|
||||
self.conv1 = nn.Conv2d(c_in, c_out // 2, kernel_size=1, stride=2, padding=0, bias=False)
|
||||
self.conv2 = nn.Conv2d(c_in, c_out // 2, kernel_size=1, stride=2, padding=0, bias=False)
|
||||
self.conv1 = nn.Conv2d(
|
||||
c_in, c_out // 2, kernel_size=1, stride=2, padding=0, bias=False
|
||||
)
|
||||
self.conv2 = nn.Conv2d(
|
||||
c_in, c_out // 2, kernel_size=1, stride=2, padding=0, bias=False
|
||||
)
|
||||
self.bn = nn.BatchNorm2d(c_out, affine=False)
|
||||
|
||||
def forward(self, x):
|
||||
@@ -105,7 +120,7 @@ class FactorizedReduce(nn.Module):
|
||||
|
||||
|
||||
class StdConv(nn.Module):
|
||||
""" Standard convolition
|
||||
"""Standard convolition
|
||||
ReLU - Conv - BN
|
||||
"""
|
||||
|
||||
@@ -113,8 +128,15 @@ class StdConv(nn.Module):
|
||||
super(StdConv, self).__init__()
|
||||
self.net = nn.Sequential(
|
||||
nn.ReLU(),
|
||||
nn.Conv2d(c_in, c_out, kernel_size=kernel_size, stride=stride, padding=padding, bias=False),
|
||||
nn.BatchNorm2d(c_out, affine=False)
|
||||
nn.Conv2d(
|
||||
c_in,
|
||||
c_out,
|
||||
kernel_size=kernel_size,
|
||||
stride=stride,
|
||||
padding=padding,
|
||||
bias=False,
|
||||
),
|
||||
nn.BatchNorm2d(c_out, affine=False),
|
||||
)
|
||||
|
||||
def forward(self, x):
|
||||
@@ -122,7 +144,7 @@ class StdConv(nn.Module):
|
||||
|
||||
|
||||
class DilConv(nn.Module):
|
||||
""" (Dilated) depthwise separable conv
|
||||
"""(Dilated) depthwise separable conv
|
||||
ReLU - (Dilated) depthwise separable - Pointwise - BN
|
||||
|
||||
If dilation == 2, 3x3 conv => 5x5 receptive field
|
||||
@@ -134,9 +156,20 @@ class DilConv(nn.Module):
|
||||
|
||||
self.net = nn.Sequential(
|
||||
nn.ReLU(),
|
||||
nn.Conv2d(channels, channels, kernel_size, stride, padding, dilation=dilation, groups=channels, bias=False),
|
||||
nn.Conv2d(channels, channels, kernel_size=1, stride=1, padding=0, bias=False),
|
||||
nn.BatchNorm2d(channels, affine=False)
|
||||
nn.Conv2d(
|
||||
channels,
|
||||
channels,
|
||||
kernel_size,
|
||||
stride,
|
||||
padding,
|
||||
dilation=dilation,
|
||||
groups=channels,
|
||||
bias=False,
|
||||
),
|
||||
nn.Conv2d(
|
||||
channels, channels, kernel_size=1, stride=1, padding=0, bias=False
|
||||
),
|
||||
nn.BatchNorm2d(channels, affine=False),
|
||||
)
|
||||
|
||||
def forward(self, x):
|
||||
@@ -144,7 +177,7 @@ class DilConv(nn.Module):
|
||||
|
||||
|
||||
class SepConv(nn.Module):
|
||||
""" Depthwise separable conv
|
||||
"""Depthwise separable conv
|
||||
DilConv (dilation=1) * 2
|
||||
"""
|
||||
|
||||
@@ -152,7 +185,7 @@ class SepConv(nn.Module):
|
||||
super(SepConv, self).__init__()
|
||||
self.net = nn.Sequential(
|
||||
DilConv(channels, kernel_size, stride=stride, padding=padding, dilation=1),
|
||||
DilConv(channels, kernel_size, stride=1, padding=padding, dilation=1)
|
||||
DilConv(channels, kernel_size, stride=1, padding=padding, dilation=1),
|
||||
)
|
||||
|
||||
def forward(self, x):
|
||||
@@ -160,8 +193,7 @@ class SepConv(nn.Module):
|
||||
|
||||
|
||||
class MixedOp(nn.Module):
|
||||
""" Mixed operation
|
||||
"""
|
||||
"""Mixed operation"""
|
||||
|
||||
def __init__(self, channels, stride, search_space):
|
||||
super(MixedOp, self).__init__()
|
||||
|
||||
@@ -27,15 +27,27 @@ import utils
|
||||
|
||||
def main():
|
||||
|
||||
parser = argparse.ArgumentParser(description='TrainingContainer')
|
||||
parser.add_argument('--algorithm-settings', type=str, default="", help="algorithm settings")
|
||||
parser.add_argument('--search-space', type=str, default="", help="search space for the neural architecture search")
|
||||
parser.add_argument('--num-layers', type=str, default="", help="number of layers of the neural network")
|
||||
parser = argparse.ArgumentParser(description="TrainingContainer")
|
||||
parser.add_argument(
|
||||
"--algorithm-settings", type=str, default="", help="algorithm settings"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--search-space",
|
||||
type=str,
|
||||
default="",
|
||||
help="search space for the neural architecture search",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--num-layers",
|
||||
type=str,
|
||||
default="",
|
||||
help="number of layers of the neural network",
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
# Get Algorithm Settings
|
||||
algorithm_settings = args.algorithm_settings.replace("\'", "\"")
|
||||
algorithm_settings = args.algorithm_settings.replace("'", '"')
|
||||
algorithm_settings = json.loads(algorithm_settings)
|
||||
print(">>> Algorithm settings")
|
||||
for key, value in algorithm_settings.items():
|
||||
@@ -69,7 +81,7 @@ def main():
|
||||
stem_multiplier = int(algorithm_settings["stem_multiplier"])
|
||||
|
||||
# Get Search Space
|
||||
search_space = args.search_space.replace("\'", "\"")
|
||||
search_space = args.search_space.replace("'", '"')
|
||||
search_space = json.loads(search_space)
|
||||
search_space = SearchSpace(search_space)
|
||||
|
||||
@@ -103,16 +115,28 @@ def main():
|
||||
|
||||
criterion = nn.CrossEntropyLoss().to(device)
|
||||
|
||||
model = NetworkCNN(init_channels, input_channels, num_classes, num_layers,
|
||||
criterion, search_space, num_nodes, stem_multiplier)
|
||||
model = NetworkCNN(
|
||||
init_channels,
|
||||
input_channels,
|
||||
num_classes,
|
||||
num_layers,
|
||||
criterion,
|
||||
search_space,
|
||||
num_nodes,
|
||||
stem_multiplier,
|
||||
)
|
||||
|
||||
model = model.to(device)
|
||||
|
||||
# Weights optimizer
|
||||
w_optim = torch.optim.SGD(model.getWeights(), w_lr, momentum=w_momentum, weight_decay=w_weight_decay)
|
||||
w_optim = torch.optim.SGD(
|
||||
model.getWeights(), w_lr, momentum=w_momentum, weight_decay=w_weight_decay
|
||||
)
|
||||
|
||||
# Alphas optimizer
|
||||
alpha_optim = torch.optim.Adam(model.getAlphas(), alpha_lr, betas=(0.5, 0.999), weight_decay=alpha_weight_decay)
|
||||
alpha_optim = torch.optim.Adam(
|
||||
model.getAlphas(), alpha_lr, betas=(0.5, 0.999), weight_decay=alpha_weight_decay
|
||||
)
|
||||
|
||||
# Split data to train/validation
|
||||
num_train = len(train_data)
|
||||
@@ -122,27 +146,30 @@ def main():
|
||||
train_sampler = torch.utils.data.sampler.SubsetRandomSampler(indices[:split])
|
||||
valid_sampler = torch.utils.data.sampler.SubsetRandomSampler(indices[split:])
|
||||
|
||||
train_loader = torch.utils.data.DataLoader(train_data,
|
||||
train_loader = torch.utils.data.DataLoader(
|
||||
train_data,
|
||||
batch_size=batch_size,
|
||||
sampler=train_sampler,
|
||||
num_workers=num_workers,
|
||||
pin_memory=True)
|
||||
pin_memory=True,
|
||||
)
|
||||
|
||||
valid_loader = torch.utils.data.DataLoader(train_data,
|
||||
valid_loader = torch.utils.data.DataLoader(
|
||||
train_data,
|
||||
batch_size=batch_size,
|
||||
sampler=valid_sampler,
|
||||
num_workers=num_workers,
|
||||
pin_memory=True)
|
||||
pin_memory=True,
|
||||
)
|
||||
|
||||
lr_scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(
|
||||
w_optim,
|
||||
num_epochs,
|
||||
eta_min=w_lr_min)
|
||||
w_optim, num_epochs, eta_min=w_lr_min
|
||||
)
|
||||
|
||||
architect = Architect(model, w_momentum, w_weight_decay, device)
|
||||
|
||||
# Start training
|
||||
best_top1 = 0.
|
||||
best_top1 = 0.0
|
||||
|
||||
for epoch in range(num_epochs):
|
||||
lr = lr_scheduler.get_last_lr()
|
||||
@@ -151,14 +178,28 @@ def main():
|
||||
|
||||
# Training
|
||||
print(">>> Training")
|
||||
train(train_loader, valid_loader, model, architect, w_optim, alpha_optim,
|
||||
lr, epoch, num_epochs, device, w_grad_clip, print_step)
|
||||
train(
|
||||
train_loader,
|
||||
valid_loader,
|
||||
model,
|
||||
architect,
|
||||
w_optim,
|
||||
alpha_optim,
|
||||
lr,
|
||||
epoch,
|
||||
num_epochs,
|
||||
device,
|
||||
w_grad_clip,
|
||||
print_step,
|
||||
)
|
||||
lr_scheduler.step()
|
||||
|
||||
# Validation
|
||||
print("\n>>> Validation")
|
||||
cur_step = (epoch + 1) * len(train_loader)
|
||||
top1 = validate(valid_loader, model, epoch, cur_step, num_epochs, device, print_step)
|
||||
top1 = validate(
|
||||
valid_loader, model, epoch, cur_step, num_epochs, device, print_step
|
||||
)
|
||||
|
||||
# Print genotype
|
||||
genotype = model.genotype(search_space)
|
||||
@@ -173,18 +214,36 @@ def main():
|
||||
print("\nBest-Genotype={}".format(str(best_genotype).replace(" ", "")))
|
||||
|
||||
|
||||
def train(train_loader, valid_loader, model, architect, w_optim, alpha_optim,
|
||||
lr, epoch, num_epochs, device, w_grad_clip, print_step):
|
||||
def train(
|
||||
train_loader,
|
||||
valid_loader,
|
||||
model,
|
||||
architect,
|
||||
w_optim,
|
||||
alpha_optim,
|
||||
lr,
|
||||
epoch,
|
||||
num_epochs,
|
||||
device,
|
||||
w_grad_clip,
|
||||
print_step,
|
||||
):
|
||||
top1 = utils.AverageMeter()
|
||||
top5 = utils.AverageMeter()
|
||||
losses = utils.AverageMeter()
|
||||
cur_step = epoch * len(train_loader)
|
||||
|
||||
model.train()
|
||||
for step, ((train_x, train_y), (valid_x, valid_y)) in enumerate(zip(train_loader, valid_loader)):
|
||||
for step, ((train_x, train_y), (valid_x, valid_y)) in enumerate(
|
||||
zip(train_loader, valid_loader)
|
||||
):
|
||||
|
||||
train_x, train_y = train_x.to(device, non_blocking=True), train_y.to(device, non_blocking=True)
|
||||
valid_x, valid_y = valid_x.to(device, non_blocking=True), valid_y.to(device, non_blocking=True)
|
||||
train_x, train_y = train_x.to(device, non_blocking=True), train_y.to(
|
||||
device, non_blocking=True
|
||||
)
|
||||
valid_x, valid_y = valid_x.to(device, non_blocking=True), valid_y.to(
|
||||
device, non_blocking=True
|
||||
)
|
||||
|
||||
train_size = train_x.size(0)
|
||||
|
||||
@@ -213,12 +272,21 @@ def train(train_loader, valid_loader, model, architect, w_optim, alpha_optim,
|
||||
print(
|
||||
"Train: [{:2d}/{}] Step {:03d}/{:03d} Loss {losses.avg:.3f} "
|
||||
"Prec@(1,5) ({top1.avg:.1%}, {top5.avg:.1%})".format(
|
||||
epoch+1, num_epochs, step, len(train_loader)-1, losses=losses,
|
||||
top1=top1, top5=top5))
|
||||
epoch + 1,
|
||||
num_epochs,
|
||||
step,
|
||||
len(train_loader) - 1,
|
||||
losses=losses,
|
||||
top1=top1,
|
||||
top5=top5,
|
||||
)
|
||||
)
|
||||
|
||||
cur_step += 1
|
||||
|
||||
print("Train: [{:2d}/{}] Final Prec@1 {:.4%}".format(epoch+1, num_epochs, top1.avg))
|
||||
print(
|
||||
"Train: [{:2d}/{}] Final Prec@1 {:.4%}".format(epoch + 1, num_epochs, top1.avg)
|
||||
)
|
||||
|
||||
|
||||
def validate(valid_loader, model, epoch, cur_step, num_epochs, device, print_step):
|
||||
@@ -230,7 +298,9 @@ def validate(valid_loader, model, epoch, cur_step, num_epochs, device, print_ste
|
||||
|
||||
with torch.no_grad():
|
||||
for step, (valid_x, valid_y) in enumerate(valid_loader):
|
||||
valid_x, valid_y = valid_x.to(device, non_blocking=True), valid_y.to(device, non_blocking=True)
|
||||
valid_x, valid_y = valid_x.to(device, non_blocking=True), valid_y.to(
|
||||
device, non_blocking=True
|
||||
)
|
||||
|
||||
valid_size = valid_x.size(0)
|
||||
|
||||
@@ -246,10 +316,19 @@ def validate(valid_loader, model, epoch, cur_step, num_epochs, device, print_ste
|
||||
print(
|
||||
"Validation: [{:2d}/{}] Step {:03d}/{:03d} Loss {losses.avg:.3f} "
|
||||
"Prec@(1,5) ({top1.avg:.1%}, {top5.avg:.1%})".format(
|
||||
epoch+1, num_epochs, step, len(valid_loader)-1, losses=losses,
|
||||
top1=top1, top5=top5))
|
||||
epoch + 1,
|
||||
num_epochs,
|
||||
step,
|
||||
len(valid_loader) - 1,
|
||||
losses=losses,
|
||||
top1=top1,
|
||||
top5=top5,
|
||||
)
|
||||
)
|
||||
|
||||
print("Valid: [{:2d}/{}] Final Prec@1 {:.4%}".format(epoch+1, num_epochs, top1.avg))
|
||||
print(
|
||||
"Valid: [{:2d}/{}] Final Prec@1 {:.4%}".format(epoch + 1, num_epochs, top1.avg)
|
||||
)
|
||||
|
||||
return top1.avg
|
||||
|
||||
|
||||
@@ -17,14 +17,16 @@ from collections import namedtuple
|
||||
import torch
|
||||
|
||||
|
||||
class SearchSpace():
|
||||
class SearchSpace:
|
||||
def __init__(self, search_space):
|
||||
self.primitives = search_space
|
||||
self.primitives.append("none")
|
||||
|
||||
print(">>> All Primitives")
|
||||
print("{}\n".format(self.primitives))
|
||||
self.genotype = namedtuple('Genotype', 'normal normal_concat reduce reduce_concat')
|
||||
self.genotype = namedtuple(
|
||||
"Genotype", "normal normal_concat reduce reduce_concat"
|
||||
)
|
||||
|
||||
def parse(self, alpha, k):
|
||||
"""
|
||||
@@ -46,7 +48,7 @@ class SearchSpace():
|
||||
"""
|
||||
|
||||
gene = []
|
||||
assert self.primitives[-1] == 'none' # assume last PRIMITIVE is 'none'
|
||||
assert self.primitives[-1] == "none" # assume last PRIMITIVE is 'none'
|
||||
|
||||
# 1) Convert the mixed op to discrete edge (single op) by choosing top-1 weight edge
|
||||
# 2) Choose top-k edges per node by edge score (top-1 weight in edge)
|
||||
|
||||
@@ -16,21 +16,21 @@ import torchvision.datasets as dset
|
||||
import torchvision.transforms as transforms
|
||||
|
||||
|
||||
class AverageMeter():
|
||||
""" Computes and stores the average and current value """
|
||||
class AverageMeter:
|
||||
"""Computes and stores the average and current value"""
|
||||
|
||||
def __init__(self):
|
||||
self.reset()
|
||||
|
||||
def reset(self):
|
||||
""" Reset all statistics """
|
||||
"""Reset all statistics"""
|
||||
self.val = 0
|
||||
self.avg = 0
|
||||
self.sum = 0
|
||||
self.count = 0
|
||||
|
||||
def update(self, val, n=1):
|
||||
""" Update statistics """
|
||||
"""Update statistics"""
|
||||
self.val = val
|
||||
self.sum += val * n
|
||||
self.count += n
|
||||
@@ -38,7 +38,7 @@ class AverageMeter():
|
||||
|
||||
|
||||
def accuracy(output, target, topk=(1,)):
|
||||
""" Computes the precision@k for the specified values of k """
|
||||
"""Computes the precision@k for the specified values of k"""
|
||||
maxk = max(topk)
|
||||
batch_size = target.size(0)
|
||||
|
||||
@@ -67,18 +67,14 @@ def get_dataset():
|
||||
# Do preprocessing
|
||||
MEAN = [0.49139968, 0.48215827, 0.44653124]
|
||||
STD = [0.24703233, 0.24348505, 0.26158768]
|
||||
transf = [
|
||||
transforms.RandomCrop(32, padding=4),
|
||||
transforms.RandomHorizontalFlip()
|
||||
]
|
||||
transf = [transforms.RandomCrop(32, padding=4), transforms.RandomHorizontalFlip()]
|
||||
|
||||
normalize = [
|
||||
transforms.ToTensor(),
|
||||
transforms.Normalize(MEAN, STD)
|
||||
]
|
||||
normalize = [transforms.ToTensor(), transforms.Normalize(MEAN, STD)]
|
||||
|
||||
train_transform = transforms.Compose(transf + normalize)
|
||||
|
||||
train_data = dataset_cls(root="./data", train=True, download=True, transform=train_transform)
|
||||
train_data = dataset_cls(
|
||||
root="./data", train=True, download=True, transform=train_transform
|
||||
)
|
||||
|
||||
return input_channels, num_classes, train_data
|
||||
|
||||
@@ -14,20 +14,11 @@
|
||||
|
||||
import json
|
||||
|
||||
from keras import backend as K
|
||||
from keras.layers import Activation
|
||||
from keras.layers import AveragePooling2D
|
||||
from keras.layers import BatchNormalization
|
||||
from keras.layers import concatenate
|
||||
from keras.layers import Conv2D
|
||||
from keras.layers import Dense
|
||||
from keras.layers import Dropout
|
||||
from keras.layers import GlobalAveragePooling2D
|
||||
from keras.layers import Input
|
||||
from keras.layers import MaxPooling2D
|
||||
from keras.layers import ZeroPadding2D
|
||||
from keras.models import Model
|
||||
import numpy as np
|
||||
from op_library import concat
|
||||
from op_library import conv
|
||||
from op_library import dw_conv
|
||||
@@ -39,16 +30,14 @@ class ModelConstructor(object):
|
||||
def __init__(self, arc_json, nn_json):
|
||||
self.arch = json.loads(arc_json)
|
||||
nn_config = json.loads(nn_json)
|
||||
self.num_layers = nn_config['num_layers']
|
||||
self.input_sizes = nn_config['input_sizes']
|
||||
self.output_size = nn_config['output_sizes'][-1]
|
||||
self.embedding = nn_config['embedding']
|
||||
self.num_layers = nn_config["num_layers"]
|
||||
self.input_sizes = nn_config["input_sizes"]
|
||||
self.output_size = nn_config["output_sizes"][-1]
|
||||
self.embedding = nn_config["embedding"]
|
||||
|
||||
def build_model(self):
|
||||
# a list of the data all layers
|
||||
all_layers = [0 for _ in range(self.num_layers + 1)]
|
||||
# a list of all the dimensions of all layers
|
||||
all_dims = [0 for _ in range(self.num_layers + 1)]
|
||||
|
||||
# ================= Stacking layers =================
|
||||
# Input Layer. Layer 0
|
||||
@@ -56,38 +45,37 @@ class ModelConstructor(object):
|
||||
all_layers[0] = input_layer
|
||||
|
||||
# Intermediate Layers. Starting from layer 1.
|
||||
for l in range(1, self.num_layers + 1):
|
||||
for l_index in range(1, self.num_layers + 1):
|
||||
input_layers = list()
|
||||
opt = self.arch[l - 1][0]
|
||||
opt = self.arch[l_index - 1][0]
|
||||
opt_config = self.embedding[str(opt)]
|
||||
skip = self.arch[l - 1][1:l+1]
|
||||
skip = self.arch[l_index - 1][1 : l_index + 1]
|
||||
|
||||
# set up the connection to the previous layer first
|
||||
input_layers.append(all_layers[l - 1])
|
||||
input_layers.append(all_layers[l_index - 1])
|
||||
|
||||
# then add skip connections
|
||||
for i in range(l - 1):
|
||||
if l > 1 and skip[i] == 1:
|
||||
for i in range(l_index - 1):
|
||||
if l_index > 1 and skip[i] == 1:
|
||||
input_layers.append(all_layers[i])
|
||||
|
||||
layer_input = concat(input_layers)
|
||||
if opt_config['opt_type'] == 'convolution':
|
||||
if opt_config["opt_type"] == "convolution":
|
||||
layer_output = conv(layer_input, opt_config)
|
||||
if opt_config['opt_type'] == 'separable_convolution':
|
||||
if opt_config["opt_type"] == "separable_convolution":
|
||||
layer_output = sp_conv(layer_input, opt_config)
|
||||
if opt_config['opt_type'] == 'depthwise_convolution':
|
||||
if opt_config["opt_type"] == "depthwise_convolution":
|
||||
layer_output = dw_conv(layer_input, opt_config)
|
||||
elif opt_config['opt_type'] == 'reduction':
|
||||
elif opt_config["opt_type"] == "reduction":
|
||||
layer_output = reduction(layer_input, opt_config)
|
||||
|
||||
all_layers[l] = layer_output
|
||||
all_layers[l_index] = layer_output
|
||||
|
||||
# Final Layer
|
||||
# Global Average Pooling, then Fully connected with softmax.
|
||||
avgpooled = GlobalAveragePooling2D()(all_layers[self.num_layers])
|
||||
dropped = Dropout(0.4)(avgpooled)
|
||||
logits = Dense(units=self.output_size,
|
||||
activation='softmax')(dropped)
|
||||
logits = Dense(units=self.output_size, activation="softmax")(dropped)
|
||||
|
||||
# Encapsulate the model
|
||||
self.model = Model(inputs=input_layer, outputs=logits)
|
||||
|
||||
@@ -24,22 +24,42 @@ from tensorflow.keras.layers import Rescaling
|
||||
from tensorflow.keras.utils import to_categorical
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = argparse.ArgumentParser(description='TrainingContainer')
|
||||
parser.add_argument('--architecture', type=str, default="", metavar='N',
|
||||
help='architecture of the neural network')
|
||||
parser.add_argument('--nn_config', type=str, default="", metavar='N',
|
||||
help='configurations and search space embeddings')
|
||||
parser.add_argument('--num_epochs', type=int, default=10, metavar='N',
|
||||
help='number of epoches that each child will be trained')
|
||||
parser.add_argument('--num_gpus', type=int, default=1, metavar='N',
|
||||
help='number of GPU that used for training')
|
||||
parser = argparse.ArgumentParser(description="TrainingContainer")
|
||||
parser.add_argument(
|
||||
"--architecture",
|
||||
type=str,
|
||||
default="",
|
||||
metavar="N",
|
||||
help="architecture of the neural network",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--nn_config",
|
||||
type=str,
|
||||
default="",
|
||||
metavar="N",
|
||||
help="configurations and search space embeddings",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--num_epochs",
|
||||
type=int,
|
||||
default=10,
|
||||
metavar="N",
|
||||
help="number of epoches that each child will be trained",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--num_gpus",
|
||||
type=int,
|
||||
default=1,
|
||||
metavar="N",
|
||||
help="number of GPU that used for training",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
arch = args.architecture.replace("\'", "\"")
|
||||
arch = args.architecture.replace("'", '"')
|
||||
print(">>> arch received by trial")
|
||||
print(arch)
|
||||
|
||||
nn_config = args.nn_config.replace("\'", "\"")
|
||||
nn_config = args.nn_config.replace("'", '"')
|
||||
print(">>> nn_config received by trial")
|
||||
print(nn_config)
|
||||
|
||||
@@ -54,36 +74,40 @@ if __name__ == "__main__":
|
||||
print("\n>>> Constructing Model...")
|
||||
constructor = ModelConstructor(arch, nn_config)
|
||||
|
||||
num_physical_gpus = len(tf.config.experimental.list_physical_devices('GPU'))
|
||||
num_physical_gpus = len(tf.config.experimental.list_physical_devices("GPU"))
|
||||
if 1 <= num_gpus <= num_physical_gpus:
|
||||
devices = ["/gpu:"+str(i) for i in range(num_physical_gpus)]
|
||||
devices = ["/gpu:" + str(i) for i in range(num_physical_gpus)]
|
||||
else:
|
||||
num_physical_cpu = len(tf.config.experimental.list_physical_devices('CPU'))
|
||||
devices = ["/cpu:"+str(j) for j in range(num_physical_cpu)]
|
||||
num_physical_cpu = len(tf.config.experimental.list_physical_devices("CPU"))
|
||||
devices = ["/cpu:" + str(j) for j in range(num_physical_cpu)]
|
||||
|
||||
strategy = tf.distribute.MirroredStrategy(devices)
|
||||
with strategy.scope():
|
||||
test_model = constructor.build_model()
|
||||
test_model.summary()
|
||||
test_model.compile(loss=keras.losses.categorical_crossentropy,
|
||||
test_model.compile(
|
||||
loss=keras.losses.categorical_crossentropy,
|
||||
optimizer=keras.optimizers.Adam(learning_rate=1e-3),
|
||||
metrics=['accuracy'])
|
||||
metrics=["accuracy"],
|
||||
)
|
||||
|
||||
print(">>> Model Constructed Successfully\n")
|
||||
|
||||
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
|
||||
x_train = x_train.astype('float32')
|
||||
x_test = x_test.astype('float32')
|
||||
x_train = x_train.astype("float32")
|
||||
x_test = x_test.astype("float32")
|
||||
x_train /= 255
|
||||
x_test /= 255
|
||||
y_train = to_categorical(y_train)
|
||||
y_test = to_categorical(y_test)
|
||||
|
||||
augmentation = tf.keras.Sequential([
|
||||
Rescaling(1./255),
|
||||
RandomFlip('horizontal'),
|
||||
augmentation = tf.keras.Sequential(
|
||||
[
|
||||
Rescaling(1.0 / 255),
|
||||
RandomFlip("horizontal"),
|
||||
RandomTranslation(height_factor=0.1, width_factor=0.1),
|
||||
])
|
||||
]
|
||||
)
|
||||
|
||||
train_dataset = tf.data.Dataset.from_tensor_slices((x_train, y_train))
|
||||
train_dataset = train_dataset.map(lambda x, y: (augmentation(x, training=True), y))
|
||||
@@ -93,11 +117,14 @@ if __name__ == "__main__":
|
||||
print(">>> Data Loaded. Training starts.")
|
||||
for e in range(num_epochs):
|
||||
print("\nTotal Epoch {}/{}".format(e + 1, num_epochs))
|
||||
history = test_model.fit(train_dataset,
|
||||
history = test_model.fit(
|
||||
train_dataset,
|
||||
steps_per_epoch=int(len(x_train) / 128) + 1,
|
||||
epochs=1, verbose=1,
|
||||
validation_data=(x_test, y_test))
|
||||
print("Training-Accuracy={}".format(history.history['accuracy'][-1]))
|
||||
print("Training-Loss={}".format(history.history['loss'][-1]))
|
||||
print("Validation-Accuracy={}".format(history.history['val_accuracy'][-1]))
|
||||
print("Validation-Loss={}".format(history.history['val_loss'][-1]))
|
||||
epochs=1,
|
||||
verbose=1,
|
||||
validation_data=(x_test, y_test),
|
||||
)
|
||||
print("Training-Accuracy={}".format(history.history["accuracy"][-1]))
|
||||
print("Training-Loss={}".format(history.history["loss"][-1]))
|
||||
print("Validation-Accuracy={}".format(history.history["val_accuracy"][-1]))
|
||||
print("Validation-Loss={}".format(history.history["val_loss"][-1]))
|
||||
|
||||
@@ -18,10 +18,7 @@ from keras.layers import AveragePooling2D
|
||||
from keras.layers import BatchNormalization
|
||||
from keras.layers import concatenate
|
||||
from keras.layers import Conv2D
|
||||
from keras.layers import Dense
|
||||
from keras.layers import DepthwiseConv2D
|
||||
from keras.layers import GlobalAveragePooling2D
|
||||
from keras.layers import Input
|
||||
from keras.layers import MaxPooling2D
|
||||
from keras.layers import SeparableConv2D
|
||||
from keras.layers import ZeroPadding2D
|
||||
@@ -46,10 +43,13 @@ def concat(inputs):
|
||||
diff = max_dim - total_dim[i][1]
|
||||
half_diff = int(diff / 2)
|
||||
if diff % 2 == 0:
|
||||
padded_input[i] = ZeroPadding2D(padding=(half_diff, half_diff))(inputs[i])
|
||||
padded_input[i] = ZeroPadding2D(padding=(half_diff, half_diff))(
|
||||
inputs[i]
|
||||
)
|
||||
else:
|
||||
padded_input[i] = ZeroPadding2D(padding=((half_diff, half_diff + 1),
|
||||
(half_diff, half_diff + 1)))(inputs[i])
|
||||
padded_input[i] = ZeroPadding2D(
|
||||
padding=((half_diff, half_diff + 1), (half_diff, half_diff + 1))
|
||||
)(inputs[i])
|
||||
else:
|
||||
padded_input[i] = inputs[i]
|
||||
|
||||
@@ -67,13 +67,14 @@ def conv(x, config):
|
||||
if k in config:
|
||||
parameters[k] = int(config[k])
|
||||
|
||||
activated = Activation('relu')(x)
|
||||
activated = Activation("relu")(x)
|
||||
|
||||
conved = Conv2D(
|
||||
filters=parameters['num_filter'],
|
||||
kernel_size=parameters['filter_size'],
|
||||
strides=parameters['stride'],
|
||||
padding='same')(activated)
|
||||
filters=parameters["num_filter"],
|
||||
kernel_size=parameters["filter_size"],
|
||||
strides=parameters["stride"],
|
||||
padding="same",
|
||||
)(activated)
|
||||
|
||||
result = BatchNormalization()(conved)
|
||||
|
||||
@@ -92,19 +93,21 @@ def sp_conv(x, config):
|
||||
if k in config:
|
||||
parameters[k] = int(config[k])
|
||||
|
||||
activated = Activation('relu')(x)
|
||||
activated = Activation("relu")(x)
|
||||
|
||||
conved = SeparableConv2D(
|
||||
filters=parameters['num_filter'],
|
||||
kernel_size=parameters['filter_size'],
|
||||
strides=parameters['stride'],
|
||||
depth_multiplier=parameters['depth_multiplier'],
|
||||
padding='same')(activated)
|
||||
filters=parameters["num_filter"],
|
||||
kernel_size=parameters["filter_size"],
|
||||
strides=parameters["stride"],
|
||||
depth_multiplier=parameters["depth_multiplier"],
|
||||
padding="same",
|
||||
)(activated)
|
||||
|
||||
result = BatchNormalization()(conved)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def dw_conv(x, config):
|
||||
parameters = {
|
||||
"filter_size": 3,
|
||||
@@ -115,13 +118,14 @@ def dw_conv(x, config):
|
||||
if k in config:
|
||||
parameters[k] = int(config[k])
|
||||
|
||||
activated = Activation('relu')(x)
|
||||
activated = Activation("relu")(x)
|
||||
|
||||
conved = DepthwiseConv2D(
|
||||
kernel_size=parameters['filter_size'],
|
||||
strides=parameters['stride'],
|
||||
depth_multiplier=parameters['depth_multiplier'],
|
||||
padding='same')(activated)
|
||||
kernel_size=parameters["filter_size"],
|
||||
strides=parameters["stride"],
|
||||
depth_multiplier=parameters["depth_multiplier"],
|
||||
padding="same",
|
||||
)(activated)
|
||||
|
||||
result = BatchNormalization()(conved)
|
||||
|
||||
@@ -134,31 +138,32 @@ def reduction(x, config):
|
||||
# such situation is very likely to appear though
|
||||
dim = K.int_shape(x)
|
||||
if dim[1] == 1 or dim[2] == 1:
|
||||
print("WARNING: One or more dimensions of the input of the reduction layer is 1. It cannot be further reduced. A identity layer will be used instead.")
|
||||
print(
|
||||
"WARNING: One or more dimensions of the input of the reduction layer is 1. "
|
||||
"It cannot be further reduced. A identity layer will be used instead."
|
||||
)
|
||||
return x
|
||||
|
||||
parameters = {
|
||||
'reduction_type': "max_pooling",
|
||||
'pool_size': 2,
|
||||
'stride': None,
|
||||
"reduction_type": "max_pooling",
|
||||
"pool_size": 2,
|
||||
"stride": None,
|
||||
}
|
||||
|
||||
if 'reduction_type' in config:
|
||||
parameters['reduction_type'] = config['reduction_type']
|
||||
if 'pool_size' in config:
|
||||
parameters['pool_size'] = int(config['pool_size'])
|
||||
if 'stride' in config:
|
||||
parameters['stride'] = int(config['stride'])
|
||||
if "reduction_type" in config:
|
||||
parameters["reduction_type"] = config["reduction_type"]
|
||||
if "pool_size" in config:
|
||||
parameters["pool_size"] = int(config["pool_size"])
|
||||
if "stride" in config:
|
||||
parameters["stride"] = int(config["stride"])
|
||||
|
||||
if parameters['reduction_type'] == 'max_pooling':
|
||||
if parameters["reduction_type"] == "max_pooling":
|
||||
result = MaxPooling2D(
|
||||
pool_size=parameters['pool_size'],
|
||||
strides=parameters['stride']
|
||||
pool_size=parameters["pool_size"], strides=parameters["stride"]
|
||||
)(x)
|
||||
elif parameters['reduction_type'] == 'avg_pooling':
|
||||
elif parameters["reduction_type"] == "avg_pooling":
|
||||
result = AveragePooling2D(
|
||||
pool_size=parameters['pool_size'],
|
||||
strides=parameters['stride']
|
||||
pool_size=parameters["pool_size"], strides=parameters["stride"]
|
||||
)(x)
|
||||
|
||||
return result
|
||||
|
||||
@@ -35,7 +35,7 @@ class Net(nn.Module):
|
||||
super(Net, self).__init__()
|
||||
self.conv1 = nn.Conv2d(1, 20, 5, 1)
|
||||
self.conv2 = nn.Conv2d(20, 50, 5, 1)
|
||||
self.fc1 = nn.Linear(4*4*50, 500)
|
||||
self.fc1 = nn.Linear(4 * 4 * 50, 500)
|
||||
self.fc2 = nn.Linear(500, 10)
|
||||
|
||||
def forward(self, x):
|
||||
@@ -43,7 +43,7 @@ class Net(nn.Module):
|
||||
x = F.max_pool2d(x, 2, 2)
|
||||
x = F.relu(self.conv2(x))
|
||||
x = F.max_pool2d(x, 2, 2)
|
||||
x = x.view(-1, 4*4*50)
|
||||
x = x.view(-1, 4 * 4 * 50)
|
||||
x = F.relu(self.fc1(x))
|
||||
x = self.fc2(x)
|
||||
return F.log_softmax(x, dim=1)
|
||||
@@ -60,10 +60,14 @@ def train(args, model, device, train_loader, optimizer, epoch):
|
||||
optimizer.step()
|
||||
if batch_idx % args.log_interval == 0:
|
||||
msg = "Train Epoch: {} [{}/{} ({:.0f}%)]\tloss={:.4f}".format(
|
||||
epoch, batch_idx * len(data), len(train_loader.dataset),
|
||||
100. * batch_idx / len(train_loader), loss.item())
|
||||
epoch,
|
||||
batch_idx * len(data),
|
||||
len(train_loader.dataset),
|
||||
100.0 * batch_idx / len(train_loader),
|
||||
loss.item(),
|
||||
)
|
||||
logging.info(msg)
|
||||
niter = epoch * len(train_loader) + batch_idx
|
||||
niter = epoch * len(train_loader) + batch_idx # noqa: F841
|
||||
|
||||
|
||||
def test(args, model, device, test_loader, epoch, hpt):
|
||||
@@ -74,24 +78,31 @@ def test(args, model, device, test_loader, epoch, hpt):
|
||||
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() # sum up batch loss
|
||||
pred = output.max(1, keepdim=True)[1] # get the index of the max log-probability
|
||||
test_loss += F.nll_loss(
|
||||
output, target, reduction="sum"
|
||||
).item() # sum up batch loss
|
||||
pred = output.max(1, keepdim=True)[
|
||||
1
|
||||
] # get the index of the max log-probability
|
||||
correct += pred.eq(target.view_as(pred)).sum().item()
|
||||
|
||||
test_loss /= len(test_loader.dataset)
|
||||
test_accuracy = float(correct) / len(test_loader.dataset)
|
||||
logging.info("{{metricName: accuracy, metricValue: {:.4f}}};{{metricName: loss, metricValue: {:.4f}}}\n".format(
|
||||
test_accuracy, test_loss))
|
||||
|
||||
logging.info(
|
||||
"{{metricName: accuracy, metricValue: {:.4f}}};"
|
||||
"{{metricName: loss, metricValue: {:.4f}}}\n".format(test_accuracy, test_loss)
|
||||
)
|
||||
|
||||
if args.logger == "hypertune":
|
||||
hpt.report_hyperparameter_tuning_metric(
|
||||
hyperparameter_metric_tag='loss',
|
||||
metric_value=test_loss,
|
||||
global_step=epoch)
|
||||
hyperparameter_metric_tag="loss", metric_value=test_loss, global_step=epoch
|
||||
)
|
||||
hpt.report_hyperparameter_tuning_metric(
|
||||
hyperparameter_metric_tag='accuracy',
|
||||
hyperparameter_metric_tag="accuracy",
|
||||
metric_value=test_accuracy,
|
||||
global_step=epoch)
|
||||
global_step=epoch,
|
||||
)
|
||||
|
||||
|
||||
def should_distribute():
|
||||
@@ -105,33 +116,82 @@ def is_distributed():
|
||||
def main():
|
||||
# Training settings
|
||||
parser = argparse.ArgumentParser(description="PyTorch MNIST Example")
|
||||
parser.add_argument("--batch-size", type=int, default=64, metavar="N",
|
||||
help="input batch size for training (default: 64)")
|
||||
parser.add_argument("--test-batch-size", type=int, default=1000, metavar="N",
|
||||
help="input batch size for testing (default: 1000)")
|
||||
parser.add_argument("--epochs", type=int, default=10, metavar="N",
|
||||
help="number of epochs to train (default: 10)")
|
||||
parser.add_argument("--lr", type=float, default=0.01, metavar="LR",
|
||||
help="learning rate (default: 0.01)")
|
||||
parser.add_argument("--momentum", type=float, default=0.5, metavar="M",
|
||||
help="SGD momentum (default: 0.5)")
|
||||
parser.add_argument("--no-cuda", action="store_true", default=False,
|
||||
help="disables CUDA training")
|
||||
parser.add_argument("--seed", type=int, default=1, metavar="S",
|
||||
help="random seed (default: 1)")
|
||||
parser.add_argument("--log-interval", type=int, default=10, metavar="N",
|
||||
help="how many batches to wait before logging training status")
|
||||
parser.add_argument("--log-path", type=str, default="",
|
||||
help="Path to save logs. Print to StdOut if log-path is not set")
|
||||
parser.add_argument("--save-model", action="store_true", default=False,
|
||||
help="For Saving the current Model")
|
||||
parser.add_argument("--logger", type=str, choices=["standard", "hypertune"],
|
||||
help="Logger", default="standard")
|
||||
parser.add_argument(
|
||||
"--batch-size",
|
||||
type=int,
|
||||
default=64,
|
||||
metavar="N",
|
||||
help="input batch size for training (default: 64)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--test-batch-size",
|
||||
type=int,
|
||||
default=1000,
|
||||
metavar="N",
|
||||
help="input batch size for testing (default: 1000)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--epochs",
|
||||
type=int,
|
||||
default=10,
|
||||
metavar="N",
|
||||
help="number of epochs to train (default: 10)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--lr",
|
||||
type=float,
|
||||
default=0.01,
|
||||
metavar="LR",
|
||||
help="learning rate (default: 0.01)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--momentum",
|
||||
type=float,
|
||||
default=0.5,
|
||||
metavar="M",
|
||||
help="SGD momentum (default: 0.5)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--no-cuda", action="store_true", default=False, help="disables CUDA training"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--seed", type=int, default=1, metavar="S", help="random seed (default: 1)"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--log-interval",
|
||||
type=int,
|
||||
default=10,
|
||||
metavar="N",
|
||||
help="how many batches to wait before logging training status",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--log-path",
|
||||
type=str,
|
||||
default="",
|
||||
help="Path to save logs. Print to StdOut if log-path is not set",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--save-model",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="For Saving the current Model",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--logger",
|
||||
type=str,
|
||||
choices=["standard", "hypertune"],
|
||||
help="Logger",
|
||||
default="standard",
|
||||
)
|
||||
|
||||
if dist.is_available():
|
||||
parser.add_argument("--backend", type=str, help="Distributed backend",
|
||||
parser.add_argument(
|
||||
"--backend",
|
||||
type=str,
|
||||
help="Distributed backend",
|
||||
choices=[dist.Backend.GLOO, dist.Backend.NCCL, dist.Backend.MPI],
|
||||
default=dist.Backend.GLOO)
|
||||
default=dist.Backend.GLOO,
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# Use this format (%Y-%m-%dT%H:%M:%SZ) to record timestamp of the metrics.
|
||||
@@ -140,16 +200,18 @@ def main():
|
||||
logging.basicConfig(
|
||||
format="%(asctime)s %(levelname)-8s %(message)s",
|
||||
datefmt="%Y-%m-%dT%H:%M:%SZ",
|
||||
level=logging.DEBUG)
|
||||
level=logging.DEBUG,
|
||||
)
|
||||
else:
|
||||
logging.basicConfig(
|
||||
format="%(asctime)s %(levelname)-8s %(message)s",
|
||||
datefmt="%Y-%m-%dT%H:%M:%SZ",
|
||||
level=logging.DEBUG,
|
||||
filename=args.log_path)
|
||||
filename=args.log_path,
|
||||
)
|
||||
|
||||
if args.logger == "hypertune" and args.log_path != "":
|
||||
os.environ['CLOUD_ML_HP_METRIC_FILE'] = args.log_path
|
||||
os.environ["CLOUD_ML_HP_METRIC_FILE"] = args.log_path
|
||||
|
||||
# For JSON logging
|
||||
hpt = hypertune.HyperTune()
|
||||
@@ -169,27 +231,34 @@ def main():
|
||||
kwargs = {"num_workers": 1, "pin_memory": True} if use_cuda else {}
|
||||
|
||||
train_loader = torch.utils.data.DataLoader(
|
||||
datasets.FashionMNIST("./data",
|
||||
datasets.FashionMNIST(
|
||||
"./data",
|
||||
train=True,
|
||||
download=True,
|
||||
transform=transforms.Compose([
|
||||
transforms.ToTensor()
|
||||
])),
|
||||
batch_size=args.batch_size, shuffle=True, **kwargs)
|
||||
transform=transforms.Compose([transforms.ToTensor()]),
|
||||
),
|
||||
batch_size=args.batch_size,
|
||||
shuffle=True,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
test_loader = torch.utils.data.DataLoader(
|
||||
datasets.FashionMNIST("./data",
|
||||
train=False,
|
||||
transform=transforms.Compose([
|
||||
transforms.ToTensor()
|
||||
])),
|
||||
batch_size=args.test_batch_size, shuffle=False, **kwargs)
|
||||
datasets.FashionMNIST(
|
||||
"./data", train=False, transform=transforms.Compose([transforms.ToTensor()])
|
||||
),
|
||||
batch_size=args.test_batch_size,
|
||||
shuffle=False,
|
||||
**kwargs,
|
||||
)
|
||||
|
||||
model = Net().to(device)
|
||||
|
||||
if is_distributed():
|
||||
Distributor = nn.parallel.DistributedDataParallel if use_cuda \
|
||||
Distributor = (
|
||||
nn.parallel.DistributedDataParallel
|
||||
if use_cuda
|
||||
else nn.parallel.DistributedDataParallelCPU
|
||||
)
|
||||
model = Distributor(model)
|
||||
|
||||
optimizer = optim.SGD(model.parameters(), lr=args.lr, momentum=args.momentum)
|
||||
@@ -198,7 +267,7 @@ def main():
|
||||
train(args, model, device, train_loader, optimizer, epoch)
|
||||
test(args, model, device, test_loader, epoch, hpt)
|
||||
|
||||
if (args.save_model):
|
||||
if args.save_model:
|
||||
torch.save(model.state_dict(), "mnist_cnn.pt")
|
||||
|
||||
|
||||
|
||||
@@ -25,9 +25,9 @@ from tensorflow.keras.layers import Flatten
|
||||
class MyModel(Model):
|
||||
def __init__(self):
|
||||
super(MyModel, self).__init__()
|
||||
self.conv1 = Conv2D(32, 3, activation='relu')
|
||||
self.conv1 = Conv2D(32, 3, activation="relu")
|
||||
self.flatten = Flatten()
|
||||
self.d1 = Dense(128, activation='relu')
|
||||
self.d1 = Dense(128, activation="relu")
|
||||
self.d2 = Dense(10)
|
||||
|
||||
def call(self, x):
|
||||
@@ -37,7 +37,17 @@ class MyModel(Model):
|
||||
return self.d2(x)
|
||||
|
||||
|
||||
def train_step(args, model, optimizer, train_ds, epoch, loss_object, train_summary_writer, train_loss, train_accuracy):
|
||||
def train_step(
|
||||
args,
|
||||
model,
|
||||
optimizer,
|
||||
train_ds,
|
||||
epoch,
|
||||
loss_object,
|
||||
train_summary_writer,
|
||||
train_loss,
|
||||
train_accuracy,
|
||||
):
|
||||
for step, (images, labels) in enumerate(train_ds):
|
||||
with tf.GradientTape() as tape:
|
||||
# training=True is only needed if there are layers with different
|
||||
@@ -51,18 +61,25 @@ def train_step(args, model, optimizer, train_ds, epoch, loss_object, train_summa
|
||||
train_accuracy(labels, predictions)
|
||||
|
||||
if step % args.log_interval == 0:
|
||||
print("Train Epoch: {} [{}/60000 ({:.0f}%)]\tloss={:.4f}, accuracy={:.4f}".format(
|
||||
epoch + 1, step * args.batch_size, 100. * step * args.batch_size / 60000,
|
||||
train_loss.result(), train_accuracy.result() * 100)
|
||||
print(
|
||||
"Train Epoch: {} [{}/60000 ({:.0f}%)]\tloss={:.4f}, accuracy={:.4f}".format(
|
||||
epoch + 1,
|
||||
step * args.batch_size,
|
||||
100.0 * step * args.batch_size / 60000,
|
||||
train_loss.result(),
|
||||
train_accuracy.result() * 100,
|
||||
)
|
||||
)
|
||||
|
||||
with train_summary_writer.as_default():
|
||||
tf.summary.scalar('loss', train_loss.result(), step=epoch)
|
||||
tf.summary.scalar('accuracy', train_accuracy.result(), step=epoch)
|
||||
tf.summary.scalar("loss", train_loss.result(), step=epoch)
|
||||
tf.summary.scalar("accuracy", train_accuracy.result(), step=epoch)
|
||||
|
||||
|
||||
def test_step(model, test_ds, epoch, loss_object, test_summary_writer, test_loss, test_accuracy):
|
||||
for (images, labels) in test_ds:
|
||||
def test_step(
|
||||
model, test_ds, epoch, loss_object, test_summary_writer, test_loss, test_accuracy
|
||||
):
|
||||
for images, labels in test_ds:
|
||||
# training=False is only needed if there are layers with different
|
||||
# behavior during training versus inference (e.g. Dropout).
|
||||
predictions = model(images, training=False)
|
||||
@@ -72,30 +89,53 @@ def test_step(model, test_ds, epoch, loss_object, test_summary_writer, test_loss
|
||||
test_accuracy(labels, predictions)
|
||||
|
||||
with test_summary_writer.as_default():
|
||||
tf.summary.scalar('loss', test_loss.result(), step=epoch)
|
||||
tf.summary.scalar('accuracy', test_accuracy.result(), step=epoch)
|
||||
tf.summary.scalar("loss", test_loss.result(), step=epoch)
|
||||
tf.summary.scalar("accuracy", test_accuracy.result(), step=epoch)
|
||||
|
||||
print("Test Loss: {:.4f}, Test Accuracy: {:.4f}\n".format(
|
||||
test_loss.result(), test_accuracy.result() * 100)
|
||||
print(
|
||||
"Test Loss: {:.4f}, Test Accuracy: {:.4f}\n".format(
|
||||
test_loss.result(), test_accuracy.result() * 100
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--batch-size', type=int, default=64,
|
||||
help='input batch size for training (default: 64)')
|
||||
parser.add_argument('--learning-rate', type=float, default=0.001,
|
||||
help='learning rate (default: 0.001)')
|
||||
parser.add_argument("--epochs", type=int, default=10, metavar="N",
|
||||
help="number of epochs to train (default: 10)")
|
||||
parser.add_argument("--log-interval", type=int, default=100, metavar="N",
|
||||
help="how many batches to wait before logging training status (default: 100)")
|
||||
parser.add_argument(
|
||||
'--log-path',
|
||||
"--batch-size",
|
||||
type=int,
|
||||
default=64,
|
||||
help="input batch size for training (default: 64)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--learning-rate",
|
||||
type=float,
|
||||
default=0.001,
|
||||
help="learning rate (default: 0.001)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--epochs",
|
||||
type=int,
|
||||
default=10,
|
||||
metavar="N",
|
||||
help="number of epochs to train (default: 10)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--log-interval",
|
||||
type=int,
|
||||
default=100,
|
||||
metavar="N",
|
||||
help="how many batches to wait before logging training status (default: 100)",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--log-path",
|
||||
type=str,
|
||||
default=os.path.join(os.getenv('TEST_TMPDIR', '/tmp'),
|
||||
'tensorflow/mnist/logs/mnist_with_summaries'),
|
||||
help='Summaries log PATH')
|
||||
default=os.path.join(
|
||||
os.getenv("TEST_TMPDIR", "/tmp"),
|
||||
"tensorflow/mnist/logs/mnist_with_summaries",
|
||||
),
|
||||
help="Summaries log PATH",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# Setup dataset
|
||||
@@ -105,12 +145,18 @@ def main():
|
||||
# Add a channels dimension
|
||||
x_train = x_train[..., tf.newaxis].astype("float32")
|
||||
x_test = x_test[..., tf.newaxis].astype("float32")
|
||||
train_ds = tf.data.Dataset.from_tensor_slices((x_train, y_train)).shuffle(10000).batch(args.batch_size)
|
||||
test_ds = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(args.batch_size)
|
||||
train_ds = (
|
||||
tf.data.Dataset.from_tensor_slices((x_train, y_train))
|
||||
.shuffle(10000)
|
||||
.batch(args.batch_size)
|
||||
)
|
||||
test_ds = tf.data.Dataset.from_tensor_slices((x_test, y_test)).batch(
|
||||
args.batch_size
|
||||
)
|
||||
|
||||
# Setup tensorflow summaries
|
||||
train_log_dir = os.path.join(args.log_path, 'train')
|
||||
test_log_dir = os.path.join(args.log_path, 'test')
|
||||
train_log_dir = os.path.join(args.log_path, "train")
|
||||
test_log_dir = os.path.join(args.log_path, "test")
|
||||
train_summary_writer = tf.summary.create_file_writer(train_log_dir)
|
||||
test_summary_writer = tf.summary.create_file_writer(test_log_dir)
|
||||
|
||||
@@ -119,20 +165,37 @@ def main():
|
||||
loss_object = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
|
||||
optimizer = tf.keras.optimizers.Adam(learning_rate=args.learning_rate)
|
||||
|
||||
train_loss = tf.keras.metrics.Mean(name='train_loss')
|
||||
train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='train_accuracy')
|
||||
train_loss = tf.keras.metrics.Mean(name="train_loss")
|
||||
train_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name="train_accuracy")
|
||||
|
||||
test_loss = tf.keras.metrics.Mean(name='test_loss')
|
||||
test_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name='test_accuracy')
|
||||
test_loss = tf.keras.metrics.Mean(name="test_loss")
|
||||
test_accuracy = tf.keras.metrics.SparseCategoricalAccuracy(name="test_accuracy")
|
||||
|
||||
for epoch in range(args.epochs):
|
||||
# Reset the metrics at the start of the next epoch
|
||||
train_summary_writer.flush()
|
||||
test_summary_writer.flush()
|
||||
|
||||
train_step(args, model, optimizer, train_ds, epoch, loss_object, train_summary_writer,
|
||||
train_loss, train_accuracy)
|
||||
test_step(model, test_ds, epoch, loss_object, test_summary_writer, test_loss, test_accuracy)
|
||||
train_step(
|
||||
args,
|
||||
model,
|
||||
optimizer,
|
||||
train_ds,
|
||||
epoch,
|
||||
loss_object,
|
||||
train_summary_writer,
|
||||
train_loss,
|
||||
train_accuracy,
|
||||
)
|
||||
test_step(
|
||||
model,
|
||||
test_ds,
|
||||
epoch,
|
||||
loss_object,
|
||||
test_summary_writer,
|
||||
test_loss,
|
||||
test_accuracy,
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -18,14 +18,14 @@ import sys
|
||||
|
||||
IGNORE_LINES = [
|
||||
"from kubeflow.katib.models.v1_unstructured_unstructured import V1UnstructuredUnstructured",
|
||||
"from kubeflow.katib.models.v1_time import V1Time"
|
||||
"from kubeflow.katib.models.v1_time import V1Time",
|
||||
]
|
||||
|
||||
|
||||
def _rewrite_helper(input_file, output_file, rewrite_rules):
|
||||
rules = rewrite_rules or []
|
||||
lines = []
|
||||
with open(input_file, 'r') as f:
|
||||
with open(input_file, "r") as f:
|
||||
while True:
|
||||
line = f.readline()
|
||||
if not line:
|
||||
@@ -34,11 +34,11 @@ def _rewrite_helper(input_file, output_file, rewrite_rules):
|
||||
for rule in rules:
|
||||
line = rule(line)
|
||||
# Remove ignored lines.
|
||||
if not any(l in line for l in IGNORE_LINES):
|
||||
if not any(li in line for li in IGNORE_LINES):
|
||||
lines.append(line)
|
||||
|
||||
# Add Katib APIs to the init file.
|
||||
if (output_file == "sdk/python/v1beta1/kubeflow/katib/__init__.py"):
|
||||
if output_file == "sdk/python/v1beta1/kubeflow/katib/__init__.py":
|
||||
lines.append("# Import Katib API client.\n")
|
||||
lines.append("from kubeflow.katib.api.katib_client import KatibClient\n")
|
||||
lines.append("# Import Katib report metrics functions")
|
||||
@@ -46,62 +46,79 @@ def _rewrite_helper(input_file, output_file, rewrite_rules):
|
||||
lines.append("# Import Katib helper functions.\n")
|
||||
lines.append("import kubeflow.katib.api.search as search\n")
|
||||
lines.append("# Import Katib helper constants.\n")
|
||||
lines.append("from kubeflow.katib.constants.constants import BASE_IMAGE_TENSORFLOW\n")
|
||||
lines.append("from kubeflow.katib.constants.constants import BASE_IMAGE_TENSORFLOW_GPU\n")
|
||||
lines.append("from kubeflow.katib.constants.constants import BASE_IMAGE_PYTORCH\n")
|
||||
lines.append("from kubeflow.katib.constants.constants import BASE_IMAGE_MXNET\n")
|
||||
lines.append(
|
||||
"from kubeflow.katib.constants.constants import BASE_IMAGE_TENSORFLOW\n"
|
||||
)
|
||||
lines.append(
|
||||
"from kubeflow.katib.constants.constants import BASE_IMAGE_TENSORFLOW_GPU\n"
|
||||
)
|
||||
lines.append(
|
||||
"from kubeflow.katib.constants.constants import BASE_IMAGE_PYTORCH\n"
|
||||
)
|
||||
lines.append(
|
||||
"from kubeflow.katib.constants.constants import BASE_IMAGE_MXNET\n"
|
||||
)
|
||||
|
||||
# Add Kubernetes models to proper deserialization of Katib models.
|
||||
if (output_file == "sdk/python/v1beta1/kubeflow/katib/models/__init__.py"):
|
||||
if output_file == "sdk/python/v1beta1/kubeflow/katib/models/__init__.py":
|
||||
lines.append("\n")
|
||||
lines.append("# Import Kubernetes models.\n")
|
||||
lines.append("from kubernetes.client import *\n")
|
||||
|
||||
with open(output_file, 'w') as f:
|
||||
with open(output_file, "w") as f:
|
||||
f.writelines(lines)
|
||||
|
||||
|
||||
def update_python_sdk(src, dest, versions=('v1beta1')):
|
||||
def update_python_sdk(src, dest, versions=("v1beta1")):
|
||||
# tiny transformers to refine generated codes
|
||||
rewrite_rules = [
|
||||
# Models rules.
|
||||
lambda l: l.replace('import katib', 'import kubeflow.katib'),
|
||||
lambda l: l.replace('from katib', 'from kubeflow.katib'),
|
||||
lambda line: line.replace("import katib", "import kubeflow.katib"),
|
||||
lambda line: line.replace("from katib", "from kubeflow.katib"),
|
||||
# For the api_client.py.
|
||||
lambda l: l.replace('klass = getattr(katib.models, klass)', 'klass = getattr(kubeflow.katib.models, klass)'),
|
||||
lambda line: line.replace(
|
||||
"klass = getattr(katib.models, klass)",
|
||||
"klass = getattr(kubeflow.katib.models, klass)",
|
||||
),
|
||||
# Doc rules.
|
||||
lambda l: l.replace('[**datetime**](V1Time.md)', '**datetime**'),
|
||||
lambda l: l.replace('[**object**](V1UnstructuredUnstructured.md)', '**object**'),
|
||||
|
||||
lambda l: l.replace('[**V1Container**](V1Container.md)',
|
||||
'[**V1Container**](https://github.com/kubernetes-client/'
|
||||
'python/blob/master/kubernetes/docs/V1Container.md)'),
|
||||
|
||||
lambda l: l.replace('[**V1ObjectMeta**](V1ObjectMeta.md)',
|
||||
'[**V1ObjectMeta**](https://github.com/kubernetes-client/'
|
||||
'python/blob/master/kubernetes/docs/V1ObjectMeta.md)'),
|
||||
|
||||
lambda l: l.replace('[**V1ListMeta**](V1ListMeta.md)',
|
||||
'[**V1ListMeta**](https://github.com/kubernetes-client/'
|
||||
'python/blob/master/kubernetes/docs/V1ListMeta.md)'),
|
||||
|
||||
lambda l: l.replace('[**V1HTTPGetAction**](V1HTTPGetAction.md)',
|
||||
'[**V1HTTPGetAction**](https://github.com/kubernetes-client/'
|
||||
'python/blob/master/kubernetes/docs/V1HTTPGetAction.md)')
|
||||
lambda line: line.replace("[**datetime**](V1Time.md)", "**datetime**"),
|
||||
lambda line: line.replace(
|
||||
"[**object**](V1UnstructuredUnstructured.md)", "**object**"
|
||||
),
|
||||
lambda line: line.replace(
|
||||
"[**V1Container**](V1Container.md)",
|
||||
"[**V1Container**](https://github.com/kubernetes-client/"
|
||||
"python/blob/master/kubernetes/docs/V1Container.md)",
|
||||
),
|
||||
lambda line: line.replace(
|
||||
"[**V1ObjectMeta**](V1ObjectMeta.md)",
|
||||
"[**V1ObjectMeta**](https://github.com/kubernetes-client/"
|
||||
"python/blob/master/kubernetes/docs/V1ObjectMeta.md)",
|
||||
),
|
||||
lambda line: line.replace(
|
||||
"[**V1ListMeta**](V1ListMeta.md)",
|
||||
"[**V1ListMeta**](https://github.com/kubernetes-client/"
|
||||
"python/blob/master/kubernetes/docs/V1ListMeta.md)",
|
||||
),
|
||||
lambda line: line.replace(
|
||||
"[**V1HTTPGetAction**](V1HTTPGetAction.md)",
|
||||
"[**V1HTTPGetAction**](https://github.com/kubernetes-client/"
|
||||
"python/blob/master/kubernetes/docs/V1HTTPGetAction.md)",
|
||||
),
|
||||
]
|
||||
|
||||
# TODO (andreyvelich): Currently test can't be generated properly.
|
||||
src_dirs = [
|
||||
os.path.join(src, 'katib'),
|
||||
os.path.join(src, 'katib', 'models'),
|
||||
os.path.join(src, "katib"),
|
||||
os.path.join(src, "katib", "models"),
|
||||
# os.path.join(src, 'test'),
|
||||
os.path.join(src, 'docs')
|
||||
os.path.join(src, "docs"),
|
||||
]
|
||||
dest_dirs = [
|
||||
os.path.join(dest, 'kubeflow', 'katib'),
|
||||
os.path.join(dest, 'kubeflow', 'katib', 'models'),
|
||||
os.path.join(dest, "kubeflow", "katib"),
|
||||
os.path.join(dest, "kubeflow", "katib", "models"),
|
||||
# os.path.join(dest, 'test'),
|
||||
os.path.join(dest, 'docs')
|
||||
os.path.join(dest, "docs"),
|
||||
]
|
||||
|
||||
for src_dir, dest_dir in zip(src_dirs, dest_dirs):
|
||||
@@ -128,13 +145,13 @@ def update_python_sdk(src, dest, versions=('v1beta1')):
|
||||
update_buffer = []
|
||||
|
||||
# Get data from generated doc
|
||||
with open(os.path.join(src, 'README.md'), 'r') as src_f:
|
||||
with open(os.path.join(src, "README.md"), "r") as src_f:
|
||||
anchor = 0
|
||||
for line in src_f.readlines():
|
||||
if line.startswith('## Documentation For Models'):
|
||||
if line.startswith("## Documentation For Models"):
|
||||
if anchor == 0:
|
||||
anchor = 1
|
||||
elif line.startswith('##') and anchor == 1:
|
||||
elif line.startswith("##") and anchor == 1:
|
||||
anchor = 2
|
||||
if anchor == 0:
|
||||
continue
|
||||
@@ -148,24 +165,24 @@ def update_python_sdk(src, dest, versions=('v1beta1')):
|
||||
update_buffer = update_buffer[:-1]
|
||||
|
||||
# Update README with new models
|
||||
with open(os.path.join(dest, 'README.md'), 'r') as dest_f:
|
||||
with open(os.path.join(dest, "README.md"), "r") as dest_f:
|
||||
anchor = 0
|
||||
for line in dest_f.readlines():
|
||||
if line.startswith('## Documentation For Models'):
|
||||
if line.startswith("## Documentation For Models"):
|
||||
if anchor == 0:
|
||||
buffer.extend(update_buffer)
|
||||
anchor = 1
|
||||
elif line.startswith('##') and anchor == 1:
|
||||
elif line.startswith("##") and anchor == 1:
|
||||
anchor = 2
|
||||
if anchor == 1:
|
||||
continue
|
||||
buffer.append(line)
|
||||
with open(os.path.join(dest, 'README.md'), 'w') as dest_f:
|
||||
with open(os.path.join(dest, "README.md"), "w") as dest_f:
|
||||
dest_f.writelines(buffer)
|
||||
|
||||
# Clear working dictionary
|
||||
shutil.rmtree(src)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if __name__ == "__main__":
|
||||
update_python_sdk(src=sys.argv[1], dest=sys.argv[2])
|
||||
|
||||
@@ -1 +1 @@
|
||||
__path__ = __import__('pkgutil').extend_path(__path__, __name__)
|
||||
__path__ = __import__("pkgutil").extend_path(__path__, __name__)
|
||||
|
||||
@@ -106,7 +106,7 @@ class KatibClient(object):
|
||||
namespace = namespace or self.namespace
|
||||
|
||||
experiment_name = None
|
||||
if type(experiment) == models.V1beta1Experiment:
|
||||
if type(experiment) is models.V1beta1Experiment:
|
||||
if experiment.metadata.name is not None:
|
||||
experiment_name = experiment.metadata.name
|
||||
elif experiment.metadata.generate_name is not None:
|
||||
@@ -137,7 +137,8 @@ class KatibClient(object):
|
||||
except Exception as e:
|
||||
if hasattr(e, "status") and e.status == 409:
|
||||
raise Exception(
|
||||
f"A Katib Experiment with the name {namespace}/{experiment_name} already exists."
|
||||
f"A Katib Experiment with the name "
|
||||
f"{namespace}/{experiment_name} already exists."
|
||||
)
|
||||
raise RuntimeError(
|
||||
f"Failed to create Katib Experiment: {namespace}/{experiment_name}"
|
||||
@@ -152,7 +153,8 @@ class KatibClient(object):
|
||||
IPython.display.display(
|
||||
IPython.display.HTML(
|
||||
"Katib Experiment {} "
|
||||
'link <a href="/_/katib/#/katib/hp_monitor/{}/{}" target="_blank">here</a>'.format(
|
||||
'link <a href="/_/katib/#/katib/hp_monitor/{}/{}" '
|
||||
'target="_blank">here</a>'.format(
|
||||
experiment_name,
|
||||
namespace,
|
||||
experiment_name,
|
||||
@@ -215,7 +217,8 @@ class KatibClient(object):
|
||||
https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/V1EnvFromSource.md)
|
||||
algorithm_name: Search algorithm for the HyperParameter tuning.
|
||||
algorithm_settings: Settings for the search algorithm given.
|
||||
For available fields, check this doc: https://www.kubeflow.org/docs/components/katib/experiment/#search-algorithms-in-detail.
|
||||
For available fields, check this doc:
|
||||
https://www.kubeflow.org/docs/components/katib/experiment/#search-algorithms-in-detail.
|
||||
objective_metric_name: Objective metric that Katib optimizes.
|
||||
additional_metric_names: List of metrics that Katib collects from the
|
||||
objective function in addition to objective metric.
|
||||
@@ -223,7 +226,8 @@ class KatibClient(object):
|
||||
Must be one of `minimize` or `maximize`.
|
||||
objective_goal: Objective goal that Experiment should reach to be Succeeded.
|
||||
max_trial_count: Maximum number of Trials to run. For the default
|
||||
values check this doc: https://www.kubeflow.org/docs/components/katib/experiment/#configuration-spec.
|
||||
values check this doc:
|
||||
https://www.kubeflow.org/docs/components/katib/experiment/#configuration-spec.
|
||||
parallel_trial_count: Number of Trials that Experiment runs in parallel.
|
||||
max_failed_trial_count: Maximum number of Trials allowed to fail.
|
||||
resources_per_trial: A parameter that lets you specify how much
|
||||
@@ -330,10 +334,14 @@ class KatibClient(object):
|
||||
# Otherwise, add value to the function input.
|
||||
input_params[p_name] = p_value
|
||||
|
||||
# Wrap objective function to execute it from the file. For example
|
||||
# Wrap objective function to execute it from the file. For example:
|
||||
# def objective(parameters):
|
||||
# print(f'Parameters are {parameters}')
|
||||
# objective({'lr': '${trialParameters.lr}', 'epochs': '${trialParameters.epochs}', 'is_dist': False})
|
||||
# objective({
|
||||
# 'lr': '${trialParameters.lr}',
|
||||
# 'epochs': '${trialParameters.epochs}',
|
||||
# 'is_dist': False
|
||||
# })
|
||||
objective_code = f"{objective_code}\n{objective.__name__}({input_params})\n"
|
||||
|
||||
# Prepare execute script template.
|
||||
@@ -385,7 +393,8 @@ class KatibClient(object):
|
||||
)
|
||||
|
||||
# Add metrics collector to the Katib Experiment.
|
||||
# Up to now, We only support parameter `kind`, of which default value is `StdOut`, to specify the kind of metrics collector.
|
||||
# Up to now, we only support parameter `kind`, of which default value
|
||||
# is `StdOut`, to specify the kind of metrics collector.
|
||||
experiment.spec.metrics_collector_spec = models.V1beta1MetricsCollectorSpec(
|
||||
collector=models.V1beta1CollectorSpec(kind=metrics_collector_config["kind"])
|
||||
)
|
||||
@@ -764,7 +773,9 @@ class KatibClient(object):
|
||||
)
|
||||
):
|
||||
utils.print_experiment_status(experiment)
|
||||
logger.debug(f"Experiment: {namespace}/{name} is {expected_condition}\n\n\n")
|
||||
logger.debug(
|
||||
f"Experiment: {namespace}/{name} is {expected_condition}\n\n\n"
|
||||
)
|
||||
return experiment
|
||||
|
||||
# Raise exception if Experiment is Failed.
|
||||
@@ -784,7 +795,9 @@ class KatibClient(object):
|
||||
)
|
||||
):
|
||||
utils.print_experiment_status(experiment)
|
||||
logger.debug(f"Experiment: {namespace}/{name} is {expected_condition}\n\n\n")
|
||||
logger.debug(
|
||||
f"Experiment: {namespace}/{name} is {expected_condition}\n\n\n"
|
||||
)
|
||||
return experiment
|
||||
|
||||
# Check if Experiment reaches Running condition.
|
||||
@@ -795,7 +808,9 @@ class KatibClient(object):
|
||||
)
|
||||
):
|
||||
utils.print_experiment_status(experiment)
|
||||
logger.debug(f"Experiment: {namespace}/{name} is {expected_condition}\n\n\n")
|
||||
logger.debug(
|
||||
f"Experiment: {namespace}/{name} is {expected_condition}\n\n\n"
|
||||
)
|
||||
return experiment
|
||||
|
||||
# Check if Experiment reaches Restarting condition.
|
||||
@@ -806,7 +821,9 @@ class KatibClient(object):
|
||||
)
|
||||
):
|
||||
utils.print_experiment_status(experiment)
|
||||
logger.debug(f"Experiment: {namespace}/{name} is {expected_condition}\n\n\n")
|
||||
logger.debug(
|
||||
f"Experiment: {namespace}/{name} is {expected_condition}\n\n\n"
|
||||
)
|
||||
return experiment
|
||||
|
||||
# Check if Experiment reaches Succeeded condition.
|
||||
@@ -817,18 +834,24 @@ class KatibClient(object):
|
||||
)
|
||||
):
|
||||
utils.print_experiment_status(experiment)
|
||||
logger.debug(f"Experiment: {namespace}/{name} is {expected_condition}\n\n\n")
|
||||
|
||||
logger.debug(
|
||||
f"waiting for experiment: {namespace}/{name} "
|
||||
f"to reach {expected_condition} condition\n\n\n"
|
||||
)
|
||||
return experiment
|
||||
|
||||
# Otherwise, print the current Experiment results and sleep for the pooling interval.
|
||||
utils.print_experiment_status(experiment)
|
||||
logger.debug(
|
||||
f"Waiting for Experiment: {namespace}/{name} to reach {expected_condition} condition\n\n\n"
|
||||
f"waiting for experiment: {namespace}/{name} "
|
||||
f"to reach {expected_condition} condition\n\n\n"
|
||||
)
|
||||
time.sleep(polling_interval)
|
||||
|
||||
raise TimeoutError(
|
||||
f"Timeout waiting for Experiment: {namespace}/{name} to reach {expected_condition} state"
|
||||
f"Timeout waiting for Experiment: {namespace}/{name} "
|
||||
f"to reach {expected_condition} state"
|
||||
)
|
||||
|
||||
def edit_experiment_budget(
|
||||
@@ -844,7 +867,8 @@ class KatibClient(object):
|
||||
budget to resume Succeeded Experiments with `LongRunning` and `FromVolume`
|
||||
resume policies.
|
||||
|
||||
Learn about resuming Experiments here: https://www.kubeflow.org/docs/components/katib/resume-experiment/
|
||||
Learn about resuming Experiments here:
|
||||
https://www.kubeflow.org/docs/components/katib/resume-experiment/
|
||||
|
||||
Args:
|
||||
name: Name for the Experiment.
|
||||
@@ -1257,10 +1281,12 @@ class KatibClient(object):
|
||||
use the default Katib DB Manager address: `katib-db-manager.kubeflow:6789`.
|
||||
|
||||
If you run this API outside the cluster, you have to port-forward the
|
||||
Katib DB Manager before getting the Trial metrics: `kubectl port-forward svc/katib-db-manager -n kubeflow 6789`.
|
||||
Katib DB Manager before getting the Trial metrics:
|
||||
`kubectl port-forward svc/katib-db-manager -n kubeflow 6789`.
|
||||
In that case, you can use this Katib DB Manager address: `localhost:6789`.
|
||||
|
||||
You can use `curl` to verify that Katib DB Manager is reachable: `curl <db-manager-address>`.
|
||||
You can use `curl` to verify that Katib DB Manager is reachable:
|
||||
`curl <db-manager-address>`.
|
||||
|
||||
Args:
|
||||
name: Name for the Trial.
|
||||
|
||||
@@ -38,16 +38,12 @@ def create_namespaced_custom_object_response(*args, **kwargs):
|
||||
|
||||
|
||||
def generate_trial_template() -> V1beta1TrialTemplate:
|
||||
trial_spec={
|
||||
trial_spec = {
|
||||
"apiVersion": "batch/v1",
|
||||
"kind": "Job",
|
||||
"spec": {
|
||||
"template": {
|
||||
"metadata": {
|
||||
"annotations": {
|
||||
"sidecar.istio.io/inject": "false"
|
||||
}
|
||||
},
|
||||
"metadata": {"annotations": {"sidecar.istio.io/inject": "false"}},
|
||||
"spec": {
|
||||
"containers": [
|
||||
{
|
||||
@@ -60,13 +56,13 @@ def generate_trial_template() -> V1beta1TrialTemplate:
|
||||
"--batch-size=64",
|
||||
"--lr=${trialParameters.learningRate}",
|
||||
"--momentum=${trialParameters.momentum}",
|
||||
]
|
||||
],
|
||||
}
|
||||
],
|
||||
"restartPolicy": "Never"
|
||||
}
|
||||
}
|
||||
"restartPolicy": "Never",
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
return V1beta1TrialTemplate(
|
||||
@@ -75,15 +71,15 @@ def generate_trial_template() -> V1beta1TrialTemplate:
|
||||
V1beta1TrialParameterSpec(
|
||||
name="learningRate",
|
||||
description="Learning rate for the training model",
|
||||
reference="lr"
|
||||
reference="lr",
|
||||
),
|
||||
V1beta1TrialParameterSpec(
|
||||
name="momentum",
|
||||
description="Momentum for the training model",
|
||||
reference="momentum"
|
||||
reference="momentum",
|
||||
),
|
||||
],
|
||||
trial_spec=trial_spec
|
||||
trial_spec=trial_spec,
|
||||
)
|
||||
|
||||
|
||||
@@ -106,60 +102,49 @@ def generate_experiment(
|
||||
objective=objective_spec,
|
||||
parameters=parameters,
|
||||
trial_template=trial_template,
|
||||
)
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
def create_experiment(
|
||||
name: Optional[str] = None,
|
||||
generate_name: Optional[str] = None
|
||||
name: Optional[str] = None, generate_name: Optional[str] = None
|
||||
) -> V1beta1Experiment:
|
||||
experiment_namespace = "test"
|
||||
|
||||
if name is not None:
|
||||
metadata = V1ObjectMeta(name=name, namespace=experiment_namespace)
|
||||
elif generate_name is not None:
|
||||
metadata = V1ObjectMeta(generate_name=generate_name, namespace=experiment_namespace)
|
||||
metadata = V1ObjectMeta(
|
||||
generate_name=generate_name, namespace=experiment_namespace
|
||||
)
|
||||
else:
|
||||
metadata = V1ObjectMeta(namespace=experiment_namespace)
|
||||
|
||||
algorithm_spec=V1beta1AlgorithmSpec(
|
||||
algorithm_name="random"
|
||||
)
|
||||
algorithm_spec = V1beta1AlgorithmSpec(algorithm_name="random")
|
||||
|
||||
objective_spec=V1beta1ObjectiveSpec(
|
||||
objective_spec = V1beta1ObjectiveSpec(
|
||||
type="minimize",
|
||||
goal= 0.001,
|
||||
goal=0.001,
|
||||
objective_metric_name="loss",
|
||||
)
|
||||
|
||||
parameters=[
|
||||
parameters = [
|
||||
V1beta1ParameterSpec(
|
||||
name="lr",
|
||||
parameter_type="double",
|
||||
feasible_space=V1beta1FeasibleSpace(
|
||||
min="0.01",
|
||||
max="0.06"
|
||||
),
|
||||
feasible_space=V1beta1FeasibleSpace(min="0.01", max="0.06"),
|
||||
),
|
||||
V1beta1ParameterSpec(
|
||||
name="momentum",
|
||||
parameter_type="double",
|
||||
feasible_space=V1beta1FeasibleSpace(
|
||||
min="0.5",
|
||||
max="0.9"
|
||||
),
|
||||
feasible_space=V1beta1FeasibleSpace(min="0.5", max="0.9"),
|
||||
),
|
||||
]
|
||||
|
||||
trial_template = generate_trial_template()
|
||||
|
||||
experiment = generate_experiment(
|
||||
metadata,
|
||||
algorithm_spec,
|
||||
objective_spec,
|
||||
parameters,
|
||||
trial_template
|
||||
metadata, algorithm_spec, objective_spec, parameters, trial_template
|
||||
)
|
||||
return experiment
|
||||
|
||||
@@ -246,15 +231,14 @@ def katib_client():
|
||||
side_effect=create_namespaced_custom_object_response
|
||||
)
|
||||
),
|
||||
), patch(
|
||||
"kubernetes.config.load_kube_config",
|
||||
return_value=Mock()
|
||||
):
|
||||
), patch("kubernetes.config.load_kube_config", return_value=Mock()):
|
||||
client = KatibClient()
|
||||
yield client
|
||||
|
||||
|
||||
@pytest.mark.parametrize("test_name,kwargs,expected_output", test_create_experiment_data)
|
||||
@pytest.mark.parametrize(
|
||||
"test_name,kwargs,expected_output", test_create_experiment_data
|
||||
)
|
||||
def test_create_experiment(katib_client, test_name, kwargs, expected_output):
|
||||
"""
|
||||
test create_experiment function of katib client
|
||||
|
||||
@@ -48,9 +48,7 @@ def report_metrics(
|
||||
namespace = utils.get_current_k8s_namespace()
|
||||
name = os.getenv("KATIB_TRIAL_NAME")
|
||||
if name is None:
|
||||
raise ValueError(
|
||||
"The Trial name is not passed to environment variables"
|
||||
)
|
||||
raise ValueError("The Trial name is not passed to environment variables")
|
||||
|
||||
# Get channel for grpc call to db manager
|
||||
db_manager_address = db_manager_address.split(":")
|
||||
@@ -73,11 +71,13 @@ def report_metrics(
|
||||
metric_logs=[
|
||||
katib_api_pb2.MetricLog(
|
||||
time_stamp=timestamp,
|
||||
metric=katib_api_pb2.Metric(name=name,value=str(value))
|
||||
metric=katib_api_pb2.Metric(
|
||||
name=name, value=str(value)
|
||||
),
|
||||
)
|
||||
for name, value in metrics.items()
|
||||
]
|
||||
)
|
||||
),
|
||||
),
|
||||
timeout=timeout,
|
||||
)
|
||||
|
||||
@@ -61,5 +61,6 @@ def categorical(list: List):
|
||||
"""
|
||||
|
||||
return models.V1beta1ParameterSpec(
|
||||
parameter_type="categorical", feasible_space=models.V1beta1FeasibleSpace(list),
|
||||
parameter_type="categorical",
|
||||
feasible_space=models.V1beta1FeasibleSpace(list),
|
||||
)
|
||||
|
||||
@@ -72,6 +72,7 @@ def print_experiment_status(experiment: models.V1beta1Experiment):
|
||||
print(f"Current Optimal Trial:\n {experiment.status.current_optimal_trial}")
|
||||
print(f"Experiment conditions:\n {experiment.status.conditions}")
|
||||
|
||||
|
||||
def validate_metrics_value(value: Any):
|
||||
"""Validate if the metrics value can be converted to type `float`."""
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user