Skip to content

Commit

Permalink
suggestion: Refactor (#21)
Browse files Browse the repository at this point in the history
- Fix a typo in hyperband service `MaxParallel`
- Move the service to the suggestion package
- Move the port to the suggestion package

Signed-off-by: Ce Gao <[email protected]>
  • Loading branch information
gaocegege authored Apr 9, 2018
1 parent 293c6ba commit 3e0c030
Show file tree
Hide file tree
Showing 7 changed files with 221 additions and 203 deletions.
182 changes: 7 additions & 175 deletions suggestion/grid/main.go
Original file line number Diff line number Diff line change
@@ -1,192 +1,24 @@
package main

import (
"context"
"fmt"
"github.com/kubeflow/hp-tuning/api"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
"log"
"net"
"strconv"
)

const (
port = "0.0.0.0:6789"
)

type GridSuggestParameters struct {
defaultGridNum int
gridConfig map[string]int
MaxParallel int
}

type GridSuggestService struct {
parameters map[string]*GridSuggestParameters
grids map[string][][]*api.Parameter
gridPointer map[string]int
}

func NewGridSuggestService() *GridSuggestService {
return &GridSuggestService{parameters: make(map[string]*GridSuggestParameters), grids: make(map[string][][]*api.Parameter), gridPointer: make(map[string]int)}
}

func (s *GridSuggestService) SetSuggestionParameters(ctx context.Context, in *api.SetSuggestionParametersRequest) (*api.SetSuggestionParametersReply, error) {
p := &GridSuggestParameters{gridConfig: make(map[string]int)}
for _, sp := range in.SuggestionParameters {
switch sp.Name {
case "DefaultGrid":
p.defaultGridNum, _ = strconv.Atoi(sp.Value)
case "MaxParrallel":
p.MaxParallel, _ = strconv.Atoi(sp.Value)
default:
p.gridConfig[sp.Name], _ = strconv.Atoi(sp.Value)
}
}
s.parameters[in.StudyId] = p
return &api.SetSuggestionParametersReply{}, nil
}

func (s *GridSuggestService) allocInt(min int, max int, reqnum int) []string {
ret := make([]string, reqnum)
if reqnum == 1 {
ret[0] = strconv.Itoa(min)
} else {
for i := 0; i < reqnum; i++ {
ret[i] = strconv.Itoa(min + ((max - min) * i / (reqnum - 1)))
}
}
return ret
}

func (s *GridSuggestService) allocFloat(min float64, max float64, reqnum int) []string {
ret := make([]string, reqnum)
if reqnum == 1 {
ret[0] = strconv.FormatFloat(min, 'f', 4, 64)
} else {
for i := 0; i < reqnum; i++ {
ret[i] = strconv.FormatFloat(min+(((max-min)/float64(reqnum-1))*float64(i)), 'f', 4, 64)
}
}
return ret
}

func (s *GridSuggestService) allocCat(list []string, reqnum int) []string {
ret := make([]string, reqnum)
if reqnum == 1 {
ret[0] = list[0]
} else {
for i := 0; i < reqnum; i++ {
ret[i] = list[(((len(list) - 1) * i) / (reqnum - 1))]
fmt.Printf("ret %v %v\n", i, ret[i])
}
}
return ret
}

func (s *GridSuggestService) setP(gci int, p [][]*api.Parameter, pg [][]string, pcs []*api.ParameterConfig) {
if gci == len(pg)-1 {
for i := range pg[gci] {
p[i] = append(p[i], &api.Parameter{
Name: pcs[gci].Name,
ParameterType: pcs[gci].ParameterType,
Value: pg[gci][i],
})

}
return
} else {
d := len(p) / len(pg[gci])
for i := range pg[gci] {
for j := d * i; j < d*(i+1); j++ {
p[j] = append(p[j], &api.Parameter{
Name: pcs[gci].Name,
ParameterType: pcs[gci].ParameterType,
Value: pg[gci][i],
})
}
s.setP(gci+1, p[d*i:d*(i+1)], pg, pcs)
}
}
}

func (s *GridSuggestService) genGrids(studyId string, pcs []*api.ParameterConfig) [][]*api.Parameter {
var pg [][]string
var holenum int = 1
gcl := make([]int, len(pcs))
for i, pc := range pcs {
gc, ok := s.parameters[studyId].gridConfig[pc.Name]
if !ok {
gc = s.parameters[studyId].defaultGridNum
}
holenum *= gc
gcl[i] = gc
switch pc.ParameterType {
case api.ParameterType_INT:
imin, _ := strconv.Atoi(pc.Feasible.Min)
imax, _ := strconv.Atoi(pc.Feasible.Max)
pg = append(pg, s.allocInt(imin, imax, gc))
case api.ParameterType_DOUBLE:
dmin, _ := strconv.ParseFloat(pc.Feasible.Min, 64)
dmax, _ := strconv.ParseFloat(pc.Feasible.Max, 64)
pg = append(pg, s.allocFloat(dmin, dmax, gc))
case api.ParameterType_CATEGORICAL:
pg = append(pg, s.allocCat(pc.Feasible.List, gc))
}
}
ret := make([][]*api.Parameter, holenum)
s.setP(0, ret, pg, pcs)
log.Printf("Study %v : %v parameters generated", studyId, holenum)
return ret
}

func (s *GridSuggestService) GenerateTrials(ctx context.Context, in *api.GenerateTrialsRequest) (*api.GenerateTrialsReply, error) {
if _, ok := s.grids[in.StudyId]; !ok {
s.grids[in.StudyId] = s.genGrids(in.StudyId, in.Configs.ParameterConfigs.Configs)
s.gridPointer[in.StudyId] = 0
}
if s.gridPointer[in.StudyId] >= len(s.grids[in.StudyId]) {
if len(in.RunningTrials) == 0 {
s.StopSuggestion(ctx, &api.StopSuggestionRequest{StudyId: in.StudyId})
return &api.GenerateTrialsReply{Completed: true}, nil
} else {
return &api.GenerateTrialsReply{Completed: false}, nil
}
}
var reqnum int = 0
if s.parameters[in.StudyId].MaxParallel <= 0 {
reqnum = len(s.grids[in.StudyId])
} else if len(s.grids[in.StudyId])-s.gridPointer[in.StudyId] < s.parameters[in.StudyId].MaxParallel-len(in.RunningTrials) {
reqnum = len(s.grids[in.StudyId]) - s.gridPointer[in.StudyId]
} else if len(in.RunningTrials) < s.parameters[in.StudyId].MaxParallel {
reqnum = s.parameters[in.StudyId].MaxParallel - len(in.RunningTrials)
}
s_t := make([]*api.Trial, reqnum)
for i := 0; i < int(reqnum); i++ {
s_t[i] = &api.Trial{}
s_t[i].Status = api.TrialState_PENDING
s_t[i].EvalLogs = make([]*api.EvaluationLog, 0)
s_t[i].ParameterSet = s.grids[in.StudyId][s.gridPointer[in.StudyId]+i]
}
s.gridPointer[in.StudyId] += reqnum
return &api.GenerateTrialsReply{Trials: s_t, Completed: false}, nil
}
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"

func (s *GridSuggestService) StopSuggestion(ctx context.Context, in *api.StopSuggestionRequest) (*api.StopSuggestionReply, error) {
delete(s.gridPointer, in.StudyId)
delete(s.grids, in.StudyId)
delete(s.parameters, in.StudyId)
return &api.StopSuggestionReply{}, nil
}
pb "github.com/kubeflow/hp-tuning/api"
"github.com/kubeflow/hp-tuning/suggestion"
)

func main() {
listener, err := net.Listen("tcp", port)
listener, err := net.Listen("tcp", suggestion.DefaultPort)
if err != nil {
log.Fatalf("Failed to listen: %v", err)
}
size := 1<<31 - 1
s := grpc.NewServer(grpc.MaxRecvMsgSize(size), grpc.MaxSendMsgSize(size))
api.RegisterSuggestionServer(s, NewGridSuggestService())
pb.RegisterSuggestionServer(s, suggestion.NewGridSuggestService())
reflection.Register(s)
log.Printf("Grid Search Suggestion Service\n")
if err = s.Serve(listener); err != nil {
Expand Down
174 changes: 174 additions & 0 deletions suggestion/grid_service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
package suggestion

import (
"context"
"fmt"
"log"
"strconv"

"github.com/kubeflow/hp-tuning/api"
)

type GridSuggestParameters struct {
defaultGridNum int
gridConfig map[string]int
MaxParallel int
}

type GridSuggestService struct {
parameters map[string]*GridSuggestParameters
grids map[string][][]*api.Parameter
gridPointer map[string]int
}

func NewGridSuggestService() *GridSuggestService {
return &GridSuggestService{parameters: make(map[string]*GridSuggestParameters), grids: make(map[string][][]*api.Parameter), gridPointer: make(map[string]int)}
}

func (s *GridSuggestService) SetSuggestionParameters(ctx context.Context, in *api.SetSuggestionParametersRequest) (*api.SetSuggestionParametersReply, error) {
p := &GridSuggestParameters{gridConfig: make(map[string]int)}
for _, sp := range in.SuggestionParameters {
switch sp.Name {
case "DefaultGrid":
p.defaultGridNum, _ = strconv.Atoi(sp.Value)
case "MaxParallel":
p.MaxParallel, _ = strconv.Atoi(sp.Value)
default:
p.gridConfig[sp.Name], _ = strconv.Atoi(sp.Value)
}
}
s.parameters[in.StudyId] = p
return &api.SetSuggestionParametersReply{}, nil
}

func (s *GridSuggestService) allocInt(min int, max int, reqnum int) []string {
ret := make([]string, reqnum)
if reqnum == 1 {
ret[0] = strconv.Itoa(min)
} else {
for i := 0; i < reqnum; i++ {
ret[i] = strconv.Itoa(min + ((max - min) * i / (reqnum - 1)))
}
}
return ret
}

func (s *GridSuggestService) allocFloat(min float64, max float64, reqnum int) []string {
ret := make([]string, reqnum)
if reqnum == 1 {
ret[0] = strconv.FormatFloat(min, 'f', 4, 64)
} else {
for i := 0; i < reqnum; i++ {
ret[i] = strconv.FormatFloat(min+(((max-min)/float64(reqnum-1))*float64(i)), 'f', 4, 64)
}
}
return ret
}

func (s *GridSuggestService) allocCat(list []string, reqnum int) []string {
ret := make([]string, reqnum)
if reqnum == 1 {
ret[0] = list[0]
} else {
for i := 0; i < reqnum; i++ {
ret[i] = list[(((len(list) - 1) * i) / (reqnum - 1))]
fmt.Printf("ret %v %v\n", i, ret[i])
}
}
return ret
}

func (s *GridSuggestService) setP(gci int, p [][]*api.Parameter, pg [][]string, pcs []*api.ParameterConfig) {
if gci == len(pg)-1 {
for i := range pg[gci] {
p[i] = append(p[i], &api.Parameter{
Name: pcs[gci].Name,
ParameterType: pcs[gci].ParameterType,
Value: pg[gci][i],
})

}
return
} else {
d := len(p) / len(pg[gci])
for i := range pg[gci] {
for j := d * i; j < d*(i+1); j++ {
p[j] = append(p[j], &api.Parameter{
Name: pcs[gci].Name,
ParameterType: pcs[gci].ParameterType,
Value: pg[gci][i],
})
}
s.setP(gci+1, p[d*i:d*(i+1)], pg, pcs)
}
}
}

func (s *GridSuggestService) genGrids(studyId string, pcs []*api.ParameterConfig) [][]*api.Parameter {
var pg [][]string
var holenum = 1
gcl := make([]int, len(pcs))
for i, pc := range pcs {
gc, ok := s.parameters[studyId].gridConfig[pc.Name]
if !ok {
gc = s.parameters[studyId].defaultGridNum
}
holenum *= gc
gcl[i] = gc
switch pc.ParameterType {
case api.ParameterType_INT:
imin, _ := strconv.Atoi(pc.Feasible.Min)
imax, _ := strconv.Atoi(pc.Feasible.Max)
pg = append(pg, s.allocInt(imin, imax, gc))
case api.ParameterType_DOUBLE:
dmin, _ := strconv.ParseFloat(pc.Feasible.Min, 64)
dmax, _ := strconv.ParseFloat(pc.Feasible.Max, 64)
pg = append(pg, s.allocFloat(dmin, dmax, gc))
case api.ParameterType_CATEGORICAL:
pg = append(pg, s.allocCat(pc.Feasible.List, gc))
}
}
ret := make([][]*api.Parameter, holenum)
s.setP(0, ret, pg, pcs)
log.Printf("Study %v : %v parameters generated", studyId, holenum)
return ret
}

func (s *GridSuggestService) GenerateTrials(ctx context.Context, in *api.GenerateTrialsRequest) (*api.GenerateTrialsReply, error) {
if _, ok := s.grids[in.StudyId]; !ok {
s.grids[in.StudyId] = s.genGrids(in.StudyId, in.Configs.ParameterConfigs.Configs)
s.gridPointer[in.StudyId] = 0
}
if s.gridPointer[in.StudyId] >= len(s.grids[in.StudyId]) {
if len(in.RunningTrials) == 0 {
s.StopSuggestion(ctx, &api.StopSuggestionRequest{StudyId: in.StudyId})
return &api.GenerateTrialsReply{Completed: true}, nil
} else {
return &api.GenerateTrialsReply{Completed: false}, nil
}
}
var reqnum = 0
if s.parameters[in.StudyId].MaxParallel <= 0 {
reqnum = len(s.grids[in.StudyId])
} else if len(s.grids[in.StudyId])-s.gridPointer[in.StudyId] < s.parameters[in.StudyId].MaxParallel-len(in.RunningTrials) {
reqnum = len(s.grids[in.StudyId]) - s.gridPointer[in.StudyId]
} else if len(in.RunningTrials) < s.parameters[in.StudyId].MaxParallel {
reqnum = s.parameters[in.StudyId].MaxParallel - len(in.RunningTrials)
}
trials := make([]*api.Trial, reqnum)
for i := 0; i < int(reqnum); i++ {
trials[i] = &api.Trial{}
trials[i].Status = api.TrialState_PENDING
trials[i].EvalLogs = make([]*api.EvaluationLog, 0)
trials[i].ParameterSet = s.grids[in.StudyId][s.gridPointer[in.StudyId]+i]
}
s.gridPointer[in.StudyId] += reqnum
return &api.GenerateTrialsReply{Trials: trials, Completed: false}, nil
}

func (s *GridSuggestService) StopSuggestion(ctx context.Context, in *api.StopSuggestionRequest) (*api.StopSuggestionReply, error) {
delete(s.gridPointer, in.StudyId)
delete(s.grids, in.StudyId)
delete(s.parameters, in.StudyId)
return &api.StopSuggestionReply{}, nil
}
Loading

0 comments on commit 3e0c030

Please sign in to comment.