From 7b077624203d6314aafbd528a8ae20661a758b18 Mon Sep 17 00:00:00 2001 From: Sunyanan Choochotkaew Date: Wed, 28 Aug 2024 15:52:48 +0900 Subject: [PATCH] feat: get trainer from model_name in weight Signed-off-by: Sunyanan Choochotkaew --- data/model_weight/acpi_AbsPowerModel.json | 2 +- data/model_weight/acpi_DynPowerModel.json | 2 +- .../intel_rapl_AbsPowerModel.json | 2 +- .../intel_rapl_DynPowerModel.json | 2 +- pkg/config/config.go | 3 +- .../local/regressor/exponential_test.go | 2 +- .../local/regressor/logarithm_test.go | 2 +- .../local/regressor/logistic_test.go | 2 +- .../local/regressor/model_weights.go | 24 +++++++++++++++ .../estimator/local/regressor/regressor.go | 21 ++++++++++---- .../local/regressor/regressor_test.go | 29 ++++++++++++++----- pkg/model/model_test.go | 9 ++++++ pkg/model/types/types.go | 9 ++++++ pkg/model/utils/utils.go | 10 +++++++ 14 files changed, 99 insertions(+), 20 deletions(-) diff --git a/data/model_weight/acpi_AbsPowerModel.json b/data/model_weight/acpi_AbsPowerModel.json index a9e3077f54..8f74af34d6 100644 --- a/data/model_weight/acpi_AbsPowerModel.json +++ b/data/model_weight/acpi_AbsPowerModel.json @@ -1 +1 @@ -{"platform": {"All_Weights": {"Bias_Weight": 220.9079278650894, "Categorical_Variables": {}, "Numerical_Variables": {"bpf_cpu_time_ms": {"scale": 5911.969193263386, "mean": 0, "variance": 0, "weight": 29.028228361462897}}}}} +{"model_name": "SGDRegressorTrainer_0", "platform": {"All_Weights": {"Bias_Weight": 220.9079278650894, "Categorical_Variables": {}, "Numerical_Variables": {"bpf_cpu_time_ms": {"scale": 5911.969193263386, "mean": 0, "variance": 0, "weight": 29.028228361462897}}}}} diff --git a/data/model_weight/acpi_DynPowerModel.json b/data/model_weight/acpi_DynPowerModel.json index df09966a83..8149bb4456 100644 --- a/data/model_weight/acpi_DynPowerModel.json +++ b/data/model_weight/acpi_DynPowerModel.json @@ -1 +1 @@ -{"platform": {"All_Weights": {"Bias_Weight": 49.56491877218095, "Categorical_Variables": {}, "Numerical_Variables": {"bpf_cpu_time_ms": {"scale": 5911.969193263386, "mean": 0, "variance": 0, "weight": 28.501356366108837}}}}} +{"model_name": "SGDRegressorTrainer_0", "platform": {"All_Weights": {"Bias_Weight": 49.56491877218095, "Categorical_Variables": {}, "Numerical_Variables": {"bpf_cpu_time_ms": {"scale": 5911.969193263386, "mean": 0, "variance": 0, "weight": 28.501356366108837}}}}} diff --git a/data/model_weight/intel_rapl_AbsPowerModel.json b/data/model_weight/intel_rapl_AbsPowerModel.json index 7c96474651..c77f702950 100644 --- a/data/model_weight/intel_rapl_AbsPowerModel.json +++ b/data/model_weight/intel_rapl_AbsPowerModel.json @@ -1 +1 @@ -{"package": {"All_Weights": {"Bias_Weight": 69.91739430907396, "Categorical_Variables": {}, "Numerical_Variables": {"bpf_cpu_time_ms": {"scale": 5911.969193263386, "mean": 0, "variance": 0, "weight": 22.16772409328642}}}}, "core": {"All_Weights": {"Bias_Weight": 0.0, "Categorical_Variables": {}, "Numerical_Variables": {"bpf_cpu_time_ms": {"scale": 5911.969193263386, "mean": 0, "variance": 0, "weight": 0.0}}}}, "uncore": {"All_Weights": {"Bias_Weight": 0.0, "Categorical_Variables": {}, "Numerical_Variables": {"bpf_cpu_time_ms": {"scale": 5911.969193263386, "mean": 0, "variance": 0, "weight": 0.0}}}}, "dram": {"All_Weights": {"Bias_Weight": 47.142633336743344, "Categorical_Variables": {}, "Numerical_Variables": {"bpf_cpu_time_ms": {"scale": 5911.969193263386, "mean": 0, "variance": 0, "weight": 3.57348245077466}}}}} +{"model_name": "SGDRegressorTrainer_0", "package": {"All_Weights": {"Bias_Weight": 69.91739430907396, "Categorical_Variables": {}, "Numerical_Variables": {"bpf_cpu_time_ms": {"scale": 5911.969193263386, "mean": 0, "variance": 0, "weight": 22.16772409328642}}}}, "core": {"All_Weights": {"Bias_Weight": 0.0, "Categorical_Variables": {}, "Numerical_Variables": {"bpf_cpu_time_ms": {"scale": 5911.969193263386, "mean": 0, "variance": 0, "weight": 0.0}}}}, "uncore": {"All_Weights": {"Bias_Weight": 0.0, "Categorical_Variables": {}, "Numerical_Variables": {"bpf_cpu_time_ms": {"scale": 5911.969193263386, "mean": 0, "variance": 0, "weight": 0.0}}}}, "dram": {"All_Weights": {"Bias_Weight": 47.142633336743344, "Categorical_Variables": {}, "Numerical_Variables": {"bpf_cpu_time_ms": {"scale": 5911.969193263386, "mean": 0, "variance": 0, "weight": 3.57348245077466}}}}} diff --git a/data/model_weight/intel_rapl_DynPowerModel.json b/data/model_weight/intel_rapl_DynPowerModel.json index 14791a9deb..0ef3801fba 100644 --- a/data/model_weight/intel_rapl_DynPowerModel.json +++ b/data/model_weight/intel_rapl_DynPowerModel.json @@ -1 +1 @@ -{"package": {"All_Weights": {"Bias_Weight": 38.856412561925055, "Categorical_Variables": {}, "Numerical_Variables": {"bpf_cpu_time_ms": {"scale": 5911.969193263386, "mean": 0, "variance": 0, "weight": 22.258830113477515}}}}, "core": {"All_Weights": {"Bias_Weight": 0.0, "Categorical_Variables": {}, "Numerical_Variables": {"bpf_cpu_time_ms": {"scale": 5911.969193263386, "mean": 0, "variance": 0, "weight": 0.0}}}}, "uncore": {"All_Weights": {"Bias_Weight": 0.0, "Categorical_Variables": {}, "Numerical_Variables": {"bpf_cpu_time_ms": {"scale": 5911.969193263386, "mean": 0, "variance": 0, "weight": 0.0}}}}, "dram": {"All_Weights": {"Bias_Weight": 9.080889901856153, "Categorical_Variables": {}, "Numerical_Variables": {"bpf_cpu_time_ms": {"scale": 5911.969193263386, "mean": 0, "variance": 0, "weight": 3.0358946796490924}}}}} +{"model_name": "SGDRegressorTrainer_0", "package": {"All_Weights": {"Bias_Weight": 38.856412561925055, "Categorical_Variables": {}, "Numerical_Variables": {"bpf_cpu_time_ms": {"scale": 5911.969193263386, "mean": 0, "variance": 0, "weight": 22.258830113477515}}}}, "core": {"All_Weights": {"Bias_Weight": 0.0, "Categorical_Variables": {}, "Numerical_Variables": {"bpf_cpu_time_ms": {"scale": 5911.969193263386, "mean": 0, "variance": 0, "weight": 0.0}}}}, "uncore": {"All_Weights": {"Bias_Weight": 0.0, "Categorical_Variables": {}, "Numerical_Variables": {"bpf_cpu_time_ms": {"scale": 5911.969193263386, "mean": 0, "variance": 0, "weight": 0.0}}}}, "dram": {"All_Weights": {"Bias_Weight": 9.080889901856153, "Categorical_Variables": {}, "Numerical_Variables": {"bpf_cpu_time_ms": {"scale": 5911.969193263386, "mean": 0, "variance": 0, "weight": 3.0358946796490924}}}}} diff --git a/pkg/config/config.go b/pkg/config/config.go index 160fbf13e0..eaeb63bc6f 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -25,6 +25,7 @@ import ( "strconv" "strings" + "github.com/sustainable-computing-io/kepler/pkg/model/types" "golang.org/x/sys/unix" "k8s.io/klog/v2" ) @@ -129,7 +130,7 @@ var ( FixedTrainerNameKey = "TRAINER" FixedNodeTypeKey = "NODE_TYPE" ModelFiltersKey = "FILTERS" - DefaultTrainerName = "SGDRegressorTrainer" + DefaultTrainerName = types.LinearRegressionTrainer //////////////////////////////////// // KubeConfig is used to start k8s client with the pod running outside the cluster diff --git a/pkg/model/estimator/local/regressor/exponential_test.go b/pkg/model/estimator/local/regressor/exponential_test.go index 8748300af7..8a97b087c4 100644 --- a/pkg/model/estimator/local/regressor/exponential_test.go +++ b/pkg/model/estimator/local/regressor/exponential_test.go @@ -25,7 +25,7 @@ import ( var ( exponentialCurveFits = []float64{1, 1, 1} - dummyExponentialWeightHandler = genHandlerFunc(exponentialCurveFits) + dummyExponentialWeightHandler = genHandlerFunc(exponentialCurveFits, types.ExponentialTrainer) ) var _ = Describe("Test Exponential Predictor Unit", func() { diff --git a/pkg/model/estimator/local/regressor/logarithm_test.go b/pkg/model/estimator/local/regressor/logarithm_test.go index 3b6e818aff..0b31c92d36 100644 --- a/pkg/model/estimator/local/regressor/logarithm_test.go +++ b/pkg/model/estimator/local/regressor/logarithm_test.go @@ -25,7 +25,7 @@ import ( var ( logarithmicCurveFits = []float64{1, 1, 1} - dummyLogarithmicWeightHandler = genHandlerFunc(logarithmicCurveFits) + dummyLogarithmicWeightHandler = genHandlerFunc(logarithmicCurveFits, types.LogarithmicTrainer) ) var _ = Describe("Test Logarithmic Predictor Unit", func() { diff --git a/pkg/model/estimator/local/regressor/logistic_test.go b/pkg/model/estimator/local/regressor/logistic_test.go index a2f091475e..6d4df59038 100644 --- a/pkg/model/estimator/local/regressor/logistic_test.go +++ b/pkg/model/estimator/local/regressor/logistic_test.go @@ -25,7 +25,7 @@ import ( var ( logisticCurveFits = []float64{1, 1, 1, 1} - dummyLogisticWeightHandler = genHandlerFunc(logisticCurveFits) + dummyLogisticWeightHandler = genHandlerFunc(logisticCurveFits, types.LogisticTrainer) ) var _ = Describe("Test Logistic Predictor Unit", func() { diff --git a/pkg/model/estimator/local/regressor/model_weights.go b/pkg/model/estimator/local/regressor/model_weights.go index 457b0fab79..fb830b9fd3 100644 --- a/pkg/model/estimator/local/regressor/model_weights.go +++ b/pkg/model/estimator/local/regressor/model_weights.go @@ -19,6 +19,9 @@ package regressor import ( "errors" "fmt" + "strings" + + "github.com/sustainable-computing-io/kepler/pkg/model/types" "github.com/sustainable-computing-io/kepler/pkg/config" ) @@ -113,3 +116,24 @@ func (w ComponentModelWeights) String() string { } return fmt.Sprintf("%s (package: %v (core: %v, uncore: %v), dram: %v)", w.ModelName, w.Package, w.Core, w.Uncore, w.DRAM) } + +func (w ComponentModelWeights) Trainer() string { + if w.ModelName == "" { + return "" + } + modelNameSplits := strings.Split(w.ModelName, "_") + splitTrainer := strings.Join(modelNameSplits[0:len(modelNameSplits)-1], "_") + if isSupportedTrainer(splitTrainer) { + return splitTrainer + } + return "" +} + +func isSupportedTrainer(trainer string) bool { + for _, supportedTrainer := range types.WeightSupportedTrainers { + if trainer == supportedTrainer { + return true + } + } + return false +} diff --git a/pkg/model/estimator/local/regressor/regressor.go b/pkg/model/estimator/local/regressor/regressor.go index 56bdbeeebd..285fc1168e 100644 --- a/pkg/model/estimator/local/regressor/regressor.go +++ b/pkg/model/estimator/local/regressor/regressor.go @@ -186,7 +186,8 @@ func (r *Regressor) getWeightFromServer() (*ComponentModelWeights, error) { return nil, fmt.Errorf("model unmarshal error: %v (%s)", err, string(body)) } if weightResponse.ModelName != "" { - klog.V(3).Infof("Using weights trained by %s", weightResponse.ModelName) + r.TrainerName = weightResponse.Trainer() + klog.V(3).Infof("Using weights from model %s trained by %s for %s", weightResponse.ModelName, r.TrainerName, r.EnergySource) } r.updateCoreRatio(weightResponse.ModelMachineSpec) return &weightResponse, nil @@ -195,6 +196,7 @@ func (r *Regressor) getWeightFromServer() (*ComponentModelWeights, error) { // loadWeightFromURLorLocal get weight from either local or URL // if string start with '/', we take it as local file func (r *Regressor) loadWeightFromURLorLocal() (*ComponentModelWeights, error) { + var modelName string // to be set by ModelWeightsURL var body []byte var err error @@ -204,12 +206,21 @@ func (r *Regressor) loadWeightFromURLorLocal() (*ComponentModelWeights, error) { if err != nil { return nil, err } + } else { + modelName = utils.GetModelNameFromURL(r.ModelWeightsURL) } var content ComponentModelWeights err = json.Unmarshal(body, &content) if err != nil { return nil, fmt.Errorf("model unmarshal error: %v (%s)", err, string(body)) } + if content.ModelName == "" { + // Expect for the case loadWeightFromURL + // ModelWeightsFilepath should contain model_name field + content.ModelName = modelName + } + r.TrainerName = content.Trainer() + klog.V(3).Infof("Using weights from model %s trained by %s for %s", content.ModelName, r.TrainerName, r.EnergySource) r.updateCoreRatio(content.ModelMachineSpec) return &content, nil } @@ -250,13 +261,13 @@ func (r *Regressor) loadWeightFromURL() ([]byte, error) { // Create Predictor based on trainer name func (r *Regressor) createPredictor(weight ModelWeights) (predictor Predictor, err error) { switch r.TrainerName { - case "SGDRegressorTrainer": + case types.LinearRegressionTrainer: predictor, err = NewLinearPredictor(weight) - case "LogarithmicRegressionTrainer": + case types.LogarithmicTrainer: predictor, err = NewLogarithmicPredictor(weight) - case "LogisticRegressionTrainer": + case types.LogisticTrainer: predictor, err = NewLogisticPredictor(weight) - case "ExponentialRegressionTrainer": + case types.ExponentialTrainer: predictor, err = NewExponentialPredictor(weight) default: predictor, err = NewLinearPredictor(weight) diff --git a/pkg/model/estimator/local/regressor/regressor_test.go b/pkg/model/estimator/local/regressor/regressor_test.go index f7126366b7..ebd9138a9c 100644 --- a/pkg/model/estimator/local/regressor/regressor_test.go +++ b/pkg/model/estimator/local/regressor/regressor_test.go @@ -65,16 +65,15 @@ var ( SampleDramNumbericalVars = map[string]NormalizedNumericalFeature{ "cache_miss": {Weight: 1.0, Scale: 2}, } - DummyWeightHandler = http.HandlerFunc(genHandlerFunc([]float64{})) - DummyModelName = "dummy" + DummyWeightHandler = http.HandlerFunc(genHandlerFunc([]float64{}, types.LinearRegressionTrainer)) ModelCores = config.GenerateSpec().Cores ExpectedAbsPowerFromDummyWeightHandler = 2500 ExpectedIdlePowerFromDummyWeightHandler = 2000 ) -func GenPlatformModelWeights(curveFitWeights []float64) ComponentModelWeights { +func GenPlatformModelWeights(curveFitWeights []float64, trainerName string) ComponentModelWeights { return ComponentModelWeights{ - ModelName: DummyModelName, + ModelName: trainerName + "_0", ModelMachineSpec: &config.MachineSpec{ Cores: ModelCores, }, @@ -100,7 +99,7 @@ func genWeights(numericalVars map[string]NormalizedNumericalFeature, curveFitWei } } -func genHandlerFunc(curvefit []float64) (handlerFunc func(w http.ResponseWriter, r *http.Request)) { +func genHandlerFunc(curvefit []float64, trainerName string) (handlerFunc func(w http.ResponseWriter, r *http.Request)) { return func(w http.ResponseWriter, r *http.Request) { reqBody, err := io.ReadAll(r.Body) if err != nil { @@ -114,7 +113,7 @@ func genHandlerFunc(curvefit []float64) (handlerFunc func(w http.ResponseWriter, if req.EnergySource == types.ComponentEnergySource { err = json.NewEncoder(w).Encode(GenComponentModelWeights(curvefit)) } else { - err = json.NewEncoder(w).Encode(GenPlatformModelWeights(curvefit)) + err = json.NewEncoder(w).Encode(GenPlatformModelWeights(curvefit, trainerName)) } if err != nil { panic(err) @@ -177,7 +176,7 @@ var _ = Describe("Test Regressor Weight Unit (default trainer)", func() { }) It("Get Node Components Power By Default Regression Estimator with ModelServerEndpoint", func() { - compPowers := GetNodeComponentsPowerFromDummyServer(genHandlerFunc([]float64{}), "") + compPowers := GetNodeComponentsPowerFromDummyServer(genHandlerFunc([]float64{}, types.LinearRegressionTrainer), "") // TODO: verify if the power makes sense Expect(compPowers[0].Core).Should(BeEquivalentTo(3000)) }) @@ -317,4 +316,20 @@ var _ = Describe("Test Regressor Weight Unit (default trainer)", func() { Entry("invalid model core", 16, 0, 1.0), ) }) + + DescribeTable("Test ComponentModelWeights.Trainer", func(modelName, expectedTrainer string) { + w := ComponentModelWeights{ + ModelName: modelName, + } + Expect(w.Trainer()).To(Equal(expectedTrainer)) + }, + Entry("empty model name", "", ""), + Entry("invalid model name", "some", ""), + Entry("invalid model name with _", "some_invalid", ""), + Entry("valid SGDRegressorTrainer", "SGDRegressorTrainer_0", "SGDRegressorTrainer"), + Entry("valid LogarithmicRegressionTrainer", "LogarithmicRegressionTrainer_0", "LogarithmicRegressionTrainer"), + Entry("valid LogisticRegressionTrainer", "LogisticRegressionTrainer_0", "LogisticRegressionTrainer"), + Entry("valid ExponentialRegressionTrainer", "ExponentialRegressionTrainer_0", "ExponentialRegressionTrainer"), + Entry("invalid GradientBoostingRegressorTrainer", "GradientBoostingRegressorTrainer_0", ""), + ) }) diff --git a/pkg/model/model_test.go b/pkg/model/model_test.go index 8b8a712e76..b5a37baf52 100644 --- a/pkg/model/model_test.go +++ b/pkg/model/model_test.go @@ -67,6 +67,15 @@ var _ = Describe("Test Model Unit", func() { Entry("IdlePower with invalid coreRatio = -1", true, -1.0, 1.0), Entry("IdlePower with invalid coreRatio > 1", true, 1.2, 1.0), ) + + DescribeTable("Test GetModelNameFromURL()", func(url, expectedModelName string) { + Expect(utils.GetModelNameFromURL(url)).To(Equal(expectedModelName)) + }, + Entry("empty", "", ""), + Entry("some model with multiple subfolders", "http://some/path/to/some_model.json", "some_model"), + Entry("some model with direct path", "http://some_model.json", "some_model"), + Entry("some model without file extension", "http://some_model", "some_model"), + ) }) }) diff --git a/pkg/model/types/types.go b/pkg/model/types/types.go index 8de52ae81a..a74f0053f1 100644 --- a/pkg/model/types/types.go +++ b/pkg/model/types/types.go @@ -46,6 +46,15 @@ const ( ExponentialTrainer = "ExponentialRegressionTrainer" ) +var ( + WeightSupportedTrainers = []string{ + LinearRegressionTrainer, + LogarithmicTrainer, + LogisticTrainer, + ExponentialTrainer, + } +) + func getModelOutputTypeConverter() []string { return []string{ "AbsPower", "DynPower", diff --git a/pkg/model/utils/utils.go b/pkg/model/utils/utils.go index 6ec3d1391d..c5ca55e4b8 100644 --- a/pkg/model/utils/utils.go +++ b/pkg/model/utils/utils.go @@ -18,6 +18,7 @@ package utils import ( "math" + "strings" "github.com/sustainable-computing-io/kepler/pkg/sensors/components/source" ) @@ -69,3 +70,12 @@ func GetCoreRatio(isIdlePower bool, inCoreRatio float64) float64 { } return coreRatio } + +func GetModelNameFromURL(url string) string { + urlSplits := strings.Split(url, "/") + if len(urlSplits) > 0 { + lastItem := urlSplits[len(urlSplits)-1] + return strings.Split(lastItem, ".")[0] + } + return "" +}