From e4e4e9ec832dac9c5e4b565758af28a9d17b2e8d Mon Sep 17 00:00:00 2001 From: Yaliang Wu Date: Thu, 24 Mar 2022 22:18:04 -0700 Subject: [PATCH] use protostuff to serialize/deserialize RCF model Signed-off-by: Yaliang Wu --- ml-algorithms/build.gradle | 4 ++ .../lib/randomcutforest-core-2.0.1.jar | Bin 224943 -> 234947 bytes .../randomcutforest-parkservices-2.0.1.jar | Bin 72934 -> 72866 bytes .../algorithms/rcf/BatchRandomCutForest.java | 11 ++-- .../rcf/FixedInTimeRandomCutForest.java | 11 ++-- .../algorithms/rcf/RCFModelSerDeSer.java | 59 +++++++++++++++++ .../ml/engine/utils/ModelSerDeSer.java | 9 ++- .../opensearch/ml/engine/MLEngineTest.java | 14 ++-- .../ml/engine/ModelSerDeSerTest.java | 43 ++++++------ .../algorithms/clustering/KMeansTest.java | 6 +- .../algorithms/rcf/RCFModelSerDeSerTest.java | 61 ++++++++++++++++++ .../{KMeansHelper.java => MLTestHelper.java} | 28 ++++++-- .../ml/action/MLCommonsIntegTestCase.java | 39 ++++++++++- .../action/prediction/PredictionITTests.java | 53 ++++++++++++++- .../org/opensearch/ml/utils/TestData.java | 43 +++++++++++- 15 files changed, 322 insertions(+), 59 deletions(-) create mode 100644 ml-algorithms/src/main/java/org/opensearch/ml/engine/algorithms/rcf/RCFModelSerDeSer.java create mode 100644 ml-algorithms/src/test/java/org/opensearch/ml/engine/algorithms/rcf/RCFModelSerDeSerTest.java rename ml-algorithms/src/test/java/org/opensearch/ml/engine/helper/{KMeansHelper.java => MLTestHelper.java} (58%) diff --git a/ml-algorithms/build.gradle b/ml-algorithms/build.gradle index 8aef8cf349..bd3e14adf4 100644 --- a/ml-algorithms/build.gradle +++ b/ml-algorithms/build.gradle @@ -23,6 +23,10 @@ dependencies { implementation group: 'commons-io', name: 'commons-io', version: '2.11.0' implementation files('lib/randomcutforest-parkservices-2.0.1.jar') implementation files('lib/randomcutforest-core-2.0.1.jar') + implementation group: 'io.protostuff', name: 'protostuff-core', version: '1.8.0' + implementation group: 'io.protostuff', name: 'protostuff-runtime', version: '1.8.0' + implementation group: 'io.protostuff', name: 'protostuff-api', version: '1.8.0' + implementation group: 'io.protostuff', name: 'protostuff-collectionschema', version: '1.8.0' testImplementation group: 'junit', name: 'junit', version: '4.12' testImplementation group: 'org.mockito', name: 'mockito-core', version: '4.4.0' testImplementation group: 'org.mockito', name: 'mockito-inline', version: '4.4.0' diff --git a/ml-algorithms/lib/randomcutforest-core-2.0.1.jar b/ml-algorithms/lib/randomcutforest-core-2.0.1.jar index 4b0f6b79f06fad347f92f6243b3bfba6cdc00125..0131d21633db692b8e23ad90a6fbeddeb7ab09d3 100644 GIT binary patch delta 55050 zcmZ6xV{~9kv@IOlHah6ocE`4D+v@D3W4mM9wr$($*y=cW=bm%FG2Z+3pSi|bv#R!} znq$@4Rl8;jai9VTQBejQ0uJPVo?!~FctmpWp?`oa3B2?lm>~WaE-AswD7+G}Kso+J z1yI3%F&>onU(5riOYA-RzkB*0c04Fu%C00hVeax z@E3u?|Np2&;C%lZ{_(%#i2jQw;FAAhG8kP-?k6Zzik}uZbfPT;?7v=52!VgG4TAq) zBm!qh5n2R;Or(SSkH7^f@xKJRl-h6LFnIqv0Wcu{yq{|395+r0%s-QX4+6rKf(i{v zpZEcZ3TWW3V19~Q;*dc#tgae%=qua84>f~q)Dgl^lk}nx*or!e%$vnQ;pn6*6~^0i z$h6m-rgLmPb;;qEAR@Z0vu*i!zXA^mCkk>t*OQV_Y^;*%F)>F|9oO5}9Z%g)S3dVM zE7#p{y9l48q~s*OS?ZlADmFPDJoV=m#5vU10FCHQZKeZwbs*nTUh4xZh*aA0H>pvm zf8Hzo+i;{dWz`Yn!=I$igt5}Ju?(6rn-GCAT3TFodLd2oUtv+|;ycO8ZYonVHTueM zkP@LI8>Sb3Za*Lsa}&>0#MBhZSiQBo%Gw`!OkMLMVKk!hvzH&5oDPnH60|i+?xZ8U zfN-Y1hz>JXlUV=dejY2c#X@(TqB>ln^Z2<5Q+~X+eA=|aA`t+4!4DVuh7y!_>anxD zkP%;eczBiHL#{1vka}F!S70Pl+B}sWX^d+Ui@x}`8oIS~+tB=*X=fT?c%f7|wq#fD zegR8NopXf}n~Ru>Z8D#cm#TPAk-lO*z`bFIwZnJ-5xMbT>>hmIlB%Hwf07i{iYco- zQ97nWQeVn}>WtK1hG@Y9!mwLSiqmMf? z#qq~*AP&~-A)>l}skzG$J*ZFq$B!{j$ziGy?79_2<)(&LEL@X|%j^ewy)o-D0J14} zt6KIwC<>zDf_`fHmk~`h#So7QOEGR&2H{H5q*-86;_|!`8V8xlZ)|BP6S`z&7jvs* zQKnECG@@kY*a;Nm-D0Lsmz=G2Sq-bA$RVRwg;nS?gknd4!ZF2%8r)LG~ zW}2#%kc&fZBn^ds*spl|Yul=oWG(QtAR7^BNF5HPYq9Zq@wIdt!MH|zDo8!j#}b5a zzRfX-=0h-VkOVPJFf~x50*4{|TFs>7EHl))No26>5|Db#FDRFKRA;{BbXJhj&H}~f z@^W-Hovq7?YazAT&w8|)p~lu5RXHpVYIgK~ZH0oHhB)7^!-KByo=%dJzc~fJKx9U2 zN2X*V(~uO&b#~-Ottu;rQ@P4w!2JXV2S-eqFyle8N3u0S>;|n&1L#xpK(UXkt|mD` zPBkoA;$~W{rrUXuTX~N+v+l2^gE>*`e_e^_XyH3PLeY9-w&CF5pgDGv_M7?CLqSf) z(h00O;^IFN5&hAhr#ezOOB$wd_DKGq=Ez+z+hm&jm8RjGqUy3GiM^h2I{~BLO9;z2 z{*38IzuQf3E7xPB3uqj;NV`*1#Rb<(C-;voSZ$`eJ$)W_hjEE>^oco>6=1y{Asl!! zb6zORqj9(7H@7h@9^^c$U&EyQVr#;{Q+%!voua`y%Uf^HfoZ{$k$T@ z9!qnt46Tnki(Mrk@bfPK?RQ;{pwkz19=KK$#AuQ=%8Q$RL92Z zNtx#i|GU;buIEeMdtRKq__d`L#I?1 zP!PWD-?Tab;Doust@hroQWC8S8C05Bf-`rx%-V|^%i+hp%=O1o>a@r1C=9Q2&Jw{p z7-9<&nXl>lMQunY^7-A0B-N31d_;u2=eLx8dn zp3dwBye(YZ%|4(`&4Hv}rL%?J1~{LkpNy38c88BtE)qV(J*u1;gO>3|Rof{){YZ%3 z7a^(_$+UU7PPI==UA}9=zvrfG#;2(Rp}RP<)3bs9)exN9%Ca zC*Iu>f*7_)Y-rwy`N?Vkga=0Jn(||M+c4{RMZbh^b=wTz z2WeW2N`&4q1RK_K4!k7Zup!>iF1I_DH?D7J^aqHIfa&0|I|Wtf�dL>yr<`|F5+B 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

v+)p;^F}szf?-U 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

    %{aCeR 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%0A?8*Id2H4BW{ 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 HJKp~QtDH4N diff --git a/ml-algorithms/lib/randomcutforest-parkservices-2.0.1.jar b/ml-algorithms/lib/randomcutforest-parkservices-2.0.1.jar index 089a2097183eb79d388d59ac85994357ceeaaf51..692ce44f7f5e3c7be707baae1fe1eaa907242998 100644 GIT binary patch delta 12790 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 predictResult = process(dataFrame, forest, 0); return MLPredictionOutput.builder().predictionResult(DataFrameBuilder.load(predictResult)).build(); @@ -83,7 +82,7 @@ public Model train(DataFrame dataFrame) { model.setName(FunctionName.BATCH_RCF.name()); model.setVersion(1); RandomCutForestState state = rcfMapper.toState(forest); - model.setContent(ModelSerDeSer.serialize(state)); + model.setContent(RCFModelSerDeSer.serializeRCF(state)); return model; } diff --git a/ml-algorithms/src/main/java/org/opensearch/ml/engine/algorithms/rcf/FixedInTimeRandomCutForest.java b/ml-algorithms/src/main/java/org/opensearch/ml/engine/algorithms/rcf/FixedInTimeRandomCutForest.java index 85ccb9c818..bd881e7beb 100644 --- a/ml-algorithms/src/main/java/org/opensearch/ml/engine/algorithms/rcf/FixedInTimeRandomCutForest.java +++ b/ml-algorithms/src/main/java/org/opensearch/ml/engine/algorithms/rcf/FixedInTimeRandomCutForest.java @@ -12,6 +12,8 @@ import com.amazon.randomcutforest.parkservices.state.ThresholdedRandomCutForestMapper; import com.amazon.randomcutforest.parkservices.state.ThresholdedRandomCutForestState; import lombok.extern.log4j.Log4j2; +import org.opensearch.ml.common.FunctionName; +import org.opensearch.ml.common.Model; import org.opensearch.ml.common.dataframe.ColumnMeta; import org.opensearch.ml.common.dataframe.ColumnType; import org.opensearch.ml.common.dataframe.ColumnValue; @@ -19,15 +21,12 @@ import org.opensearch.ml.common.dataframe.DataFrameBuilder; import org.opensearch.ml.common.dataframe.Row; import org.opensearch.ml.common.exception.MLValidationException; -import org.opensearch.ml.common.FunctionName; import org.opensearch.ml.common.input.parameter.MLAlgoParams; +import org.opensearch.ml.common.input.parameter.rcf.FitRCFParams; import org.opensearch.ml.common.output.MLOutput; import org.opensearch.ml.common.output.MLPredictionOutput; -import org.opensearch.ml.common.Model; -import org.opensearch.ml.common.input.parameter.rcf.FitRCFParams; import org.opensearch.ml.engine.TrainAndPredictable; import org.opensearch.ml.engine.annotation.Function; -import org.opensearch.ml.engine.utils.ModelSerDeSer; import java.text.DateFormat; import java.text.ParseException; @@ -99,7 +98,7 @@ public MLOutput predict(DataFrame dataFrame, Model model) { if (model == null) { throw new IllegalArgumentException("No model found for FIT RCF prediction."); } - ThresholdedRandomCutForestState state = (ThresholdedRandomCutForestState) ModelSerDeSer.deserialize(model.getContent()); + ThresholdedRandomCutForestState state = RCFModelSerDeSer.deserializeTRCF(model.getContent()); ThresholdedRandomCutForest forest = trcfMapper.toModel(state); List> predictResult = process(dataFrame, forest); return MLPredictionOutput.builder().predictionResult(DataFrameBuilder.load(predictResult)).build(); @@ -113,7 +112,7 @@ public Model train(DataFrame dataFrame) { model.setName(FunctionName.FIT_RCF.name()); model.setVersion(1); ThresholdedRandomCutForestState state = trcfMapper.toState(forest); - model.setContent(ModelSerDeSer.serialize(state)); + model.setContent(RCFModelSerDeSer.serializeTRCF(state)); return model; } diff --git a/ml-algorithms/src/main/java/org/opensearch/ml/engine/algorithms/rcf/RCFModelSerDeSer.java b/ml-algorithms/src/main/java/org/opensearch/ml/engine/algorithms/rcf/RCFModelSerDeSer.java new file mode 100644 index 0000000000..8079268297 --- /dev/null +++ b/ml-algorithms/src/main/java/org/opensearch/ml/engine/algorithms/rcf/RCFModelSerDeSer.java @@ -0,0 +1,59 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.ml.engine.algorithms.rcf; + +import com.amazon.randomcutforest.parkservices.state.ThresholdedRandomCutForestState; +import com.amazon.randomcutforest.state.RandomCutForestState; +import io.protostuff.LinkedBuffer; +import io.protostuff.ProtostuffIOUtil; +import io.protostuff.Schema; +import io.protostuff.runtime.RuntimeSchema; +import lombok.experimental.UtilityClass; + +import java.security.AccessController; +import java.security.PrivilegedAction; + +@UtilityClass +public class RCFModelSerDeSer { + private static final int SERIALIZATION_BUFFER_BYTES = 512; + private static final Schema rcfSchema = + AccessController.doPrivileged((PrivilegedAction>) () -> + RuntimeSchema.getSchema(RandomCutForestState.class)); + private static final Schema trcfSchema = + AccessController.doPrivileged((PrivilegedAction>) () -> + RuntimeSchema.getSchema(ThresholdedRandomCutForestState.class)); + + public static byte[] serializeRCF(RandomCutForestState model) { + return serialize(model, rcfSchema); + } + + public static byte[] serializeTRCF(ThresholdedRandomCutForestState model) { + return serialize(model, trcfSchema); + } + + public static RandomCutForestState deserializeRCF(byte[] bytes) { + return deserialize(bytes, rcfSchema); + } + + public static ThresholdedRandomCutForestState deserializeTRCF(byte[] bytes) { + return deserialize(bytes, trcfSchema); + } + + private static byte[] serialize(T model, Schema schema) { + LinkedBuffer buffer = LinkedBuffer.allocate(SERIALIZATION_BUFFER_BYTES); + byte[] bytes = AccessController.doPrivileged((PrivilegedAction) () -> ProtostuffIOUtil.toByteArray(model, schema, buffer)); + return bytes; + } + + private static T deserialize(byte[] bytes, Schema schema) { + T model = schema.newMessage(); + AccessController.doPrivileged((PrivilegedAction) () -> { + ProtostuffIOUtil.mergeFrom(bytes, model, schema); + return null; + }); + return model; + } +} diff --git a/ml-algorithms/src/main/java/org/opensearch/ml/engine/utils/ModelSerDeSer.java b/ml-algorithms/src/main/java/org/opensearch/ml/engine/utils/ModelSerDeSer.java index 7b7306ebb6..4d477928b8 100644 --- a/ml-algorithms/src/main/java/org/opensearch/ml/engine/utils/ModelSerDeSer.java +++ b/ml-algorithms/src/main/java/org/opensearch/ml/engine/utils/ModelSerDeSer.java @@ -6,12 +6,12 @@ package org.opensearch.ml.engine.utils; import lombok.experimental.UtilityClass; +import org.apache.commons.io.serialization.ValidatingObjectInputStream; import org.opensearch.ml.engine.exceptions.ModelSerDeSerException; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.io.ObjectInputStream; import java.io.ObjectOutputStream; @UtilityClass @@ -41,8 +41,11 @@ public static byte[] serialize(Object model) { } public static Object deserialize(byte[] modelBin) { - try (ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(modelBin))) { - return objectInputStream.readObject(); + try (ByteArrayInputStream inputStream = new ByteArrayInputStream(modelBin); + ValidatingObjectInputStream validatingObjectInputStream = new ValidatingObjectInputStream(inputStream)){ + // Validate the model class type to avoid deserialization attack. + validatingObjectInputStream.accept(ACCEPT_CLASS_PATTERNS); + return validatingObjectInputStream.readObject(); } catch (IOException | ClassNotFoundException e) { throw new ModelSerDeSerException("Failed to deserialize model.", e.getCause()); } diff --git a/ml-algorithms/src/test/java/org/opensearch/ml/engine/MLEngineTest.java b/ml-algorithms/src/test/java/org/opensearch/ml/engine/MLEngineTest.java index e9b92f7317..aa154d62bb 100644 --- a/ml-algorithms/src/test/java/org/opensearch/ml/engine/MLEngineTest.java +++ b/ml-algorithms/src/test/java/org/opensearch/ml/engine/MLEngineTest.java @@ -30,9 +30,9 @@ import java.io.IOException; import java.util.Arrays; -import static org.opensearch.ml.engine.helper.KMeansHelper.constructKMeansDataFrame; import static org.opensearch.ml.engine.helper.LinearRegressionHelper.constructLinearRegressionPredictionDataFrame; import static org.opensearch.ml.engine.helper.LinearRegressionHelper.constructLinearRegressionTrainDataFrame; +import static org.opensearch.ml.engine.helper.MLTestHelper.constructTestDataFrame; public class MLEngineTest { @Rule @@ -41,7 +41,7 @@ public class MLEngineTest { @Test public void predictKMeans() { Model model = trainKMeansModel(); - DataFrame predictionDataFrame = constructKMeansDataFrame(10); + DataFrame predictionDataFrame = constructTestDataFrame(10); MLInputDataset inputDataset = DataFrameInputDataset.builder().dataFrame(predictionDataFrame).build(); Input mlInput = MLInput.builder().algorithm(FunctionName.KMEANS).inputDataset(inputDataset).build(); MLPredictionOutput output = (MLPredictionOutput)MLEngine.predict(mlInput, model); @@ -106,7 +106,7 @@ public void train_EmptyDataFrame() { FunctionName algoName = FunctionName.LINEAR_REGRESSION; try (MockedStatic loader = Mockito.mockStatic(MLEngineClassLoader.class)) { loader.when(() -> MLEngineClassLoader.initInstance(algoName, null, MLAlgoParams.class)).thenReturn(null); - MLInputDataset inputDataset = DataFrameInputDataset.builder().dataFrame(constructKMeansDataFrame(0)).build(); + MLInputDataset inputDataset = DataFrameInputDataset.builder().dataFrame(constructTestDataFrame(0)).build(); MLEngine.train(MLInput.builder().algorithm(algoName).inputDataset(inputDataset).build()); } } @@ -118,7 +118,7 @@ public void train_UnsupportedAlgorithm() { FunctionName algoName = FunctionName.LINEAR_REGRESSION; try (MockedStatic loader = Mockito.mockStatic(MLEngineClassLoader.class)) { loader.when(() -> MLEngineClassLoader.initInstance(algoName, null, MLAlgoParams.class)).thenReturn(null); - MLInputDataset inputDataset = DataFrameInputDataset.builder().dataFrame(constructKMeansDataFrame(10)).build(); + MLInputDataset inputDataset = DataFrameInputDataset.builder().dataFrame(constructTestDataFrame(10)).build(); MLEngine.train(MLInput.builder().algorithm(algoName).inputDataset(inputDataset).build()); } } @@ -134,7 +134,7 @@ public void predictNullInput() { public void predictWithoutAlgoName() { exceptionRule.expect(IllegalArgumentException.class); exceptionRule.expectMessage("algorithm can't be null"); - MLInputDataset inputDataset = DataFrameInputDataset.builder().dataFrame(constructKMeansDataFrame(10)).build(); + MLInputDataset inputDataset = DataFrameInputDataset.builder().dataFrame(constructTestDataFrame(10)).build(); Input mlInput = MLInput.builder().inputDataset(inputDataset).build(); MLEngine.predict(mlInput, null); } @@ -165,7 +165,7 @@ public void predictUnsupportedAlgorithm() { public void trainAndPredictWithKmeans() { int dataSize = 100; MLAlgoParams parameters = KMeansParams.builder().build(); - DataFrame dataFrame = constructKMeansDataFrame(dataSize); + DataFrame dataFrame = constructTestDataFrame(dataSize); MLInputDataset inputData = new DataFrameInputDataset(dataFrame); Input input = new MLInput(FunctionName.KMEANS, parameters, inputData); MLPredictionOutput output = (MLPredictionOutput) MLEngine.trainAndPredict(input); @@ -216,7 +216,7 @@ private Model trainKMeansModel() { .iterations(10) .distanceType(KMeansParams.DistanceType.EUCLIDEAN) .build(); - DataFrame trainDataFrame = constructKMeansDataFrame(100); + DataFrame trainDataFrame = constructTestDataFrame(100); MLInputDataset inputDataset = DataFrameInputDataset.builder().dataFrame(trainDataFrame).build(); Input mlInput = MLInput.builder().algorithm(FunctionName.KMEANS).parameters(parameters).inputDataset(inputDataset).build(); return MLEngine.train(mlInput); diff --git a/ml-algorithms/src/test/java/org/opensearch/ml/engine/ModelSerDeSerTest.java b/ml-algorithms/src/test/java/org/opensearch/ml/engine/ModelSerDeSerTest.java index be317a350a..ebff98035b 100644 --- a/ml-algorithms/src/test/java/org/opensearch/ml/engine/ModelSerDeSerTest.java +++ b/ml-algorithms/src/test/java/org/opensearch/ml/engine/ModelSerDeSerTest.java @@ -8,41 +8,40 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import org.opensearch.ml.common.input.parameter.clustering.KMeansParams; import org.opensearch.ml.common.Model; +import org.opensearch.ml.common.input.parameter.clustering.KMeansParams; +import org.opensearch.ml.common.input.parameter.regression.LinearRegressionParams; import org.opensearch.ml.engine.algorithms.clustering.KMeans; -import org.opensearch.ml.engine.exceptions.ModelSerDeSerException; +import org.opensearch.ml.engine.algorithms.regression.LinearRegression; import org.opensearch.ml.engine.utils.ModelSerDeSer; import org.tribuo.clustering.kmeans.KMeansModel; +import org.tribuo.regression.sgd.linear.LinearSGDModel; -import java.util.Arrays; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.opensearch.ml.engine.helper.KMeansHelper.constructKMeansDataFrame; +import static org.junit.Assert.assertNotNull; +import static org.opensearch.ml.engine.helper.MLTestHelper.constructTestDataFrame; public class ModelSerDeSerTest { @Rule public ExpectedException thrown = ExpectedException.none(); - private final Object dummyModel = new Object(); - - @Test - public void testModelSerDeSerBlocklModel() { - thrown.expect(ModelSerDeSerException.class); - byte[] modelBin = ModelSerDeSer.serialize(dummyModel); - Object model = ModelSerDeSer.deserialize(modelBin); - assertTrue(model.equals(dummyModel)); - } - @Test public void testModelSerDeSerKMeans() { KMeansParams params = KMeansParams.builder().build(); KMeans kMeans = new KMeans(params); - Model model = kMeans.train(constructKMeansDataFrame(100)); + Model model = kMeans.train(constructTestDataFrame(100)); + + KMeansModel deserializedModel = (KMeansModel) ModelSerDeSer.deserialize(model.getContent()); + assertNotNull(deserializedModel); + } - KMeansModel kMeansModel = (KMeansModel) ModelSerDeSer.deserialize(model.getContent()); - byte[] serializedModel = ModelSerDeSer.serialize(kMeansModel); - assertFalse(Arrays.equals(serializedModel, model.getContent())); + @Test + public void testModelSerDeSerLinearRegression() { + LinearRegressionParams params = LinearRegressionParams.builder().target("f2").build(); + LinearRegression linearRegression = new LinearRegression(params); + Model model = linearRegression.train(constructTestDataFrame(100)); + + LinearSGDModel deserializedModel = (LinearSGDModel) ModelSerDeSer.deserialize(model.getContent()); + assertNotNull(deserializedModel); } -} \ No newline at end of file + +} diff --git a/ml-algorithms/src/test/java/org/opensearch/ml/engine/algorithms/clustering/KMeansTest.java b/ml-algorithms/src/test/java/org/opensearch/ml/engine/algorithms/clustering/KMeansTest.java index 04695e277d..9ef7630f07 100644 --- a/ml-algorithms/src/test/java/org/opensearch/ml/engine/algorithms/clustering/KMeansTest.java +++ b/ml-algorithms/src/test/java/org/opensearch/ml/engine/algorithms/clustering/KMeansTest.java @@ -16,7 +16,7 @@ import org.opensearch.ml.common.output.MLPredictionOutput; import org.opensearch.ml.common.Model; -import static org.opensearch.ml.engine.helper.KMeansHelper.constructKMeansDataFrame; +import static org.opensearch.ml.engine.helper.MLTestHelper.constructTestDataFrame; public class KMeansTest { @@ -107,11 +107,11 @@ public void constructorWithNegtiveIterations() { } private void constructKMeansPredictionDataFrame() { - predictionDataFrame = constructKMeansDataFrame(predictionSize); + predictionDataFrame = constructTestDataFrame(predictionSize); } private void constructKMeansTrainDataFrame() { - trainDataFrame = constructKMeansDataFrame(trainSize); + trainDataFrame = constructTestDataFrame(trainSize); } } diff --git a/ml-algorithms/src/test/java/org/opensearch/ml/engine/algorithms/rcf/RCFModelSerDeSerTest.java b/ml-algorithms/src/test/java/org/opensearch/ml/engine/algorithms/rcf/RCFModelSerDeSerTest.java new file mode 100644 index 0000000000..9690009505 --- /dev/null +++ b/ml-algorithms/src/test/java/org/opensearch/ml/engine/algorithms/rcf/RCFModelSerDeSerTest.java @@ -0,0 +1,61 @@ +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + +package org.opensearch.ml.engine.algorithms.rcf; + +import com.amazon.randomcutforest.RandomCutForest; +import com.amazon.randomcutforest.parkservices.ThresholdedRandomCutForest; +import com.amazon.randomcutforest.parkservices.state.ThresholdedRandomCutForestMapper; +import com.amazon.randomcutforest.parkservices.state.ThresholdedRandomCutForestState; +import com.amazon.randomcutforest.state.RandomCutForestMapper; +import com.amazon.randomcutforest.state.RandomCutForestState; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.opensearch.ml.common.Model; +import org.opensearch.ml.common.input.parameter.rcf.BatchRCFParams; +import org.opensearch.ml.common.input.parameter.rcf.FitRCFParams; + +import java.util.Arrays; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.opensearch.ml.engine.helper.MLTestHelper.TIME_FIELD; +import static org.opensearch.ml.engine.helper.MLTestHelper.constructTestDataFrame; + +public class RCFModelSerDeSerTest { + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private final RandomCutForestMapper rcfMapper = new RandomCutForestMapper(); + private final ThresholdedRandomCutForestMapper trcfMapper = new ThresholdedRandomCutForestMapper(); + + @Test + public void testModelSerDeSerBatchRCF() { + BatchRCFParams params = BatchRCFParams.builder().build(); + BatchRandomCutForest batchRCF = new BatchRandomCutForest(params); + Model model = batchRCF.train(constructTestDataFrame(500)); + + RandomCutForestState deserializedState = RCFModelSerDeSer.deserializeRCF(model.getContent()); + RandomCutForest forest = rcfMapper.toModel(deserializedState); + assertNotNull(forest); + byte[] serializedModel = RCFModelSerDeSer.serializeRCF(deserializedState); + assertTrue(Arrays.equals(serializedModel, model.getContent())); + } + + @Test + public void testModelSerDeSerFitRCF() { + FitRCFParams params = FitRCFParams.builder().timeField(TIME_FIELD).build(); + FixedInTimeRandomCutForest fitRCF = new FixedInTimeRandomCutForest(params); + Model model = fitRCF.train(constructTestDataFrame(500, true)); + + ThresholdedRandomCutForestState deserializedState = RCFModelSerDeSer.deserializeTRCF(model.getContent()); + ThresholdedRandomCutForest forest = trcfMapper.toModel(deserializedState); + assertNotNull(forest); + byte[] serializedModel = RCFModelSerDeSer.serializeTRCF(deserializedState); + assertTrue(Arrays.equals(serializedModel, model.getContent())); + } + +} diff --git a/ml-algorithms/src/test/java/org/opensearch/ml/engine/helper/KMeansHelper.java b/ml-algorithms/src/test/java/org/opensearch/ml/engine/helper/MLTestHelper.java similarity index 58% rename from ml-algorithms/src/test/java/org/opensearch/ml/engine/helper/KMeansHelper.java rename to ml-algorithms/src/test/java/org/opensearch/ml/engine/helper/MLTestHelper.java index 7b45ea7f91..c98d2a0f3e 100644 --- a/ml-algorithms/src/test/java/org/opensearch/ml/engine/helper/KMeansHelper.java +++ b/ml-algorithms/src/test/java/org/opensearch/ml/engine/helper/MLTestHelper.java @@ -13,13 +13,27 @@ import org.opensearch.ml.common.dataframe.DataFrame; import org.opensearch.ml.common.dataframe.DataFrameBuilder; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Random; @UtilityClass -public class KMeansHelper { - public static DataFrame constructKMeansDataFrame(int size) { - ColumnMeta[] columnMetas = new ColumnMeta[]{new ColumnMeta("f1", ColumnType.DOUBLE), new ColumnMeta("f2", ColumnType.DOUBLE)}; +public class MLTestHelper { + + public static final String TIME_FIELD = "timestamp"; + public static DataFrame constructTestDataFrame(int size) { + return constructTestDataFrame(size, false); + } + + public static DataFrame constructTestDataFrame(int size, boolean addTimeFiled) { + List columnMetaList = new ArrayList<>(); + columnMetaList.add(new ColumnMeta("f1", ColumnType.DOUBLE)); + columnMetaList.add(new ColumnMeta("f2", ColumnType.DOUBLE)); + if (addTimeFiled) { + columnMetaList.add(new ColumnMeta(TIME_FIELD, ColumnType.LONG)); + } + ColumnMeta[] columnMetas = columnMetaList.toArray(new ColumnMeta[0]); DataFrame dataFrame = DataFrameBuilder.emptyDataFrame(columnMetas); Random random = new Random(1); @@ -28,13 +42,19 @@ public static DataFrame constructKMeansDataFrame(int size) { MultivariateNormalDistribution g2 = new MultivariateNormalDistribution(new JDKRandomGenerator(random.nextInt()), new double[]{10.0, 10.0}, new double[][]{{2.0, 1.0}, {1.0, 2.0}}); MultivariateNormalDistribution[] normalDistributions = new MultivariateNormalDistribution[]{g1, g2}; + long startTime = 1648154137000l; for (int i = 0; i < size; ++i) { int id = 0; if (Math.random() < 0.5) { id = 1; } double[] sample = normalDistributions[id].sample(); - dataFrame.appendRow(Arrays.stream(sample).boxed().toArray(Double[]::new)); + Object[] row = Arrays.stream(sample).boxed().toArray(Double[]::new); + if (addTimeFiled) { + long timestamp = startTime + 60_000 * i; + row = new Object[] {row[0], row[1], timestamp}; + } + dataFrame.appendRow(row); } return dataFrame; diff --git a/plugin/src/test/java/org/opensearch/ml/action/MLCommonsIntegTestCase.java b/plugin/src/test/java/org/opensearch/ml/action/MLCommonsIntegTestCase.java index 40dd6b1006..d6f9628579 100644 --- a/plugin/src/test/java/org/opensearch/ml/action/MLCommonsIntegTestCase.java +++ b/plugin/src/test/java/org/opensearch/ml/action/MLCommonsIntegTestCase.java @@ -5,6 +5,9 @@ package org.opensearch.ml.action; +import static org.opensearch.ml.utils.TestData.TARGET_FIELD; +import static org.opensearch.ml.utils.TestData.TIME_FIELD; + import java.util.Collection; import java.util.Collections; import java.util.Map; @@ -29,6 +32,8 @@ import org.opensearch.ml.common.input.parameter.MLAlgoParams; import org.opensearch.ml.common.input.parameter.clustering.KMeansParams; import org.opensearch.ml.common.input.parameter.rcf.BatchRCFParams; +import org.opensearch.ml.common.input.parameter.rcf.FitRCFParams; +import org.opensearch.ml.common.input.parameter.regression.LinearRegressionParams; import org.opensearch.ml.common.output.MLPredictionOutput; import org.opensearch.ml.common.output.MLTrainingOutput; import org.opensearch.ml.common.transport.MLTaskResponse; @@ -135,6 +140,30 @@ public String trainBatchRCFWithDataFrame(int dataSize, boolean async) { return trainModel(FunctionName.BATCH_RCF, BatchRCFParams.builder().build(), inputDataset, async); } + public String trainFitRCFWithDataFrame(int dataSize, boolean async) { + MLInputDataset inputDataset = new DataFrameInputDataset(TestData.constructTestDataFrame(dataSize, true)); + return trainModel(FunctionName.FIT_RCF, FitRCFParams.builder().timeField(TIME_FIELD).build(), inputDataset, async); + } + + public LinearRegressionParams getLinearRegressionParams() { + return LinearRegressionParams + .builder() + .objectiveType(LinearRegressionParams.ObjectiveType.SQUARED_LOSS) + .optimizerType(LinearRegressionParams.OptimizerType.LINEAR_DECAY_SGD) + .learningRate(0.01) + .epochs(10) + .epsilon(1e-5) + .beta1(0.9) + .beta2(0.99) + .target(TARGET_FIELD) + .build(); + } + + public String trainLinearRegressionWithDataFrame(int dataSize, boolean async) { + MLInputDataset inputDataset = new DataFrameInputDataset(TestData.constructTestDataFrameForLinearRegression(dataSize)); + return trainModel(FunctionName.LINEAR_REGRESSION, getLinearRegressionParams(), inputDataset, async); + } + public String trainModel(FunctionName functionName, MLAlgoParams params, MLInputDataset inputDataset, boolean async) { MLInput mlInput = MLInput.builder().algorithm(functionName).parameters(params).inputDataset(inputDataset).build(); MLTrainingTaskRequest trainingRequest = new MLTrainingTaskRequest(mlInput, async); @@ -155,8 +184,14 @@ public String trainModel(FunctionName functionName, MLAlgoParams params, MLInput return id; } - public DataFrame predictAndVerify(String modelId, MLInputDataset inputDataset, FunctionName functionName, int size) { - MLInput mlInput = MLInput.builder().algorithm(functionName).inputDataset(inputDataset).build(); + public DataFrame predictAndVerify( + String modelId, + MLInputDataset inputDataset, + FunctionName functionName, + MLAlgoParams parameters, + int size + ) { + MLInput mlInput = MLInput.builder().algorithm(functionName).inputDataset(inputDataset).parameters(parameters).build(); MLPredictionTaskRequest predictionRequest = new MLPredictionTaskRequest(modelId, mlInput); ActionFuture predictionFuture = client().execute(MLPredictionTaskAction.INSTANCE, predictionRequest); MLTaskResponse predictionResponse = predictionFuture.actionGet(); diff --git a/plugin/src/test/java/org/opensearch/ml/action/prediction/PredictionITTests.java b/plugin/src/test/java/org/opensearch/ml/action/prediction/PredictionITTests.java index 3f5734192c..a6caff2811 100644 --- a/plugin/src/test/java/org/opensearch/ml/action/prediction/PredictionITTests.java +++ b/plugin/src/test/java/org/opensearch/ml/action/prediction/PredictionITTests.java @@ -6,6 +6,10 @@ package org.opensearch.ml.action.prediction; import static org.opensearch.ml.utils.TestData.IRIS_DATA_SIZE; +import static org.opensearch.ml.utils.TestData.TIME_FIELD; + +import java.util.ArrayList; +import java.util.List; import org.apache.lucene.util.LuceneTestCase; import org.junit.Before; @@ -16,10 +20,18 @@ import org.opensearch.ml.action.MLCommonsIntegTestCase; import org.opensearch.ml.common.FunctionName; import org.opensearch.ml.common.MLModel; +import org.opensearch.ml.common.dataframe.ColumnMeta; +import org.opensearch.ml.common.dataframe.ColumnType; +import org.opensearch.ml.common.dataframe.ColumnValue; +import org.opensearch.ml.common.dataframe.DataFrame; +import org.opensearch.ml.common.dataframe.DefaultDataFrame; +import org.opensearch.ml.common.dataframe.DoubleValue; +import org.opensearch.ml.common.dataframe.Row; import org.opensearch.ml.common.dataset.DataFrameInputDataset; import org.opensearch.ml.common.dataset.MLInputDataset; import org.opensearch.ml.common.dataset.SearchQueryInputDataset; import org.opensearch.ml.common.input.MLInput; +import org.opensearch.ml.common.input.parameter.rcf.FitRCFParams; import org.opensearch.ml.common.transport.MLTaskResponse; import org.opensearch.ml.common.transport.prediction.MLPredictionTaskAction; import org.opensearch.ml.common.transport.prediction.MLPredictionTaskRequest; @@ -33,6 +45,8 @@ public class PredictionITTests extends MLCommonsIntegTestCase { private String irisIndexName; private String kMeansModelId; private String batchRcfModelId; + private String fitRcfModelId; + private String linearRegressionModelId; private int batchRcfDataSize = 100; @Rule @@ -50,6 +64,8 @@ public void setUp() throws Exception { // assertNotNull(kMeansModel); batchRcfModelId = trainBatchRCFWithDataFrame(500, false); + fitRcfModelId = trainFitRCFWithDataFrame(500, false); + linearRegressionModelId = trainLinearRegressionWithDataFrame(100, false); MLModel batchRcfModel = getModel(batchRcfModelId); assertNotNull(batchRcfModel); } @@ -57,13 +73,13 @@ public void setUp() throws Exception { @LuceneTestCase.AwaitsFix(bugUrl = "https://github.com/oracle/tribuo/issues/223") public void testPredictionWithSearchInput_KMeans() { MLInputDataset inputDataset = new SearchQueryInputDataset(ImmutableList.of(irisIndexName), irisDataQuery()); - predictAndVerify(kMeansModelId, inputDataset, FunctionName.KMEANS, IRIS_DATA_SIZE); + predictAndVerify(kMeansModelId, inputDataset, FunctionName.KMEANS, null, IRIS_DATA_SIZE); } @LuceneTestCase.AwaitsFix(bugUrl = "https://github.com/oracle/tribuo/issues/223") public void testPredictionWithDataInput_KMeans() { MLInputDataset inputDataset = new DataFrameInputDataset(irisDataFrame()); - predictAndVerify(kMeansModelId, inputDataset, FunctionName.KMEANS, IRIS_DATA_SIZE); + predictAndVerify(kMeansModelId, inputDataset, FunctionName.KMEANS, null, IRIS_DATA_SIZE); } @LuceneTestCase.AwaitsFix(bugUrl = "https://github.com/oracle/tribuo/issues/223") @@ -89,6 +105,37 @@ public void testPredictionWithEmptyDataset_KMeans() { public void testPredictionWithDataFrame_BatchRCF() { MLInputDataset inputDataset = new DataFrameInputDataset(TestData.constructTestDataFrame(batchRcfDataSize)); - predictAndVerify(batchRcfModelId, inputDataset, FunctionName.BATCH_RCF, batchRcfDataSize); + predictAndVerify(batchRcfModelId, inputDataset, FunctionName.BATCH_RCF, null, batchRcfDataSize); + } + + public void testPredictionWithDataFrame_FitRCF() { + MLInputDataset inputDataset = new DataFrameInputDataset(TestData.constructTestDataFrame(batchRcfDataSize, true)); + DataFrame dataFrame = predictAndVerify( + fitRcfModelId, + inputDataset, + FunctionName.FIT_RCF, + FitRCFParams.builder().timeField(TIME_FIELD).build(), + batchRcfDataSize + ); + System.out.println(dataFrame); + } + + public void testPredictionWithDataFrame_LinearRegression() { + int size = 1; + int feet = 20; + ColumnMeta[] columnMetas = new ColumnMeta[] { new ColumnMeta("feet", ColumnType.DOUBLE) }; + List rows = new ArrayList<>(); + rows.add(new Row(new ColumnValue[] { new DoubleValue(feet) })); + DataFrame inputDataFrame = new DefaultDataFrame(columnMetas, rows); + MLInputDataset inputDataset = new DataFrameInputDataset(inputDataFrame); + DataFrame dataFrame = predictAndVerify( + linearRegressionModelId, + inputDataset, + FunctionName.LINEAR_REGRESSION, + getLinearRegressionParams(), + size + ); + ColumnValue value = dataFrame.getRow(0).getValue(0); + assertNotNull(value); } } diff --git a/plugin/src/test/java/org/opensearch/ml/utils/TestData.java b/plugin/src/test/java/org/opensearch/ml/utils/TestData.java index 6aef9620dc..55bf304c26 100644 --- a/plugin/src/test/java/org/opensearch/ml/utils/TestData.java +++ b/plugin/src/test/java/org/opensearch/ml/utils/TestData.java @@ -5,23 +5,54 @@ package org.opensearch.ml.utils; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Random; import org.apache.commons.math3.distribution.MultivariateNormalDistribution; import org.apache.commons.math3.random.JDKRandomGenerator; import org.opensearch.ml.common.dataframe.ColumnMeta; import org.opensearch.ml.common.dataframe.ColumnType; +import org.opensearch.ml.common.dataframe.ColumnValue; import org.opensearch.ml.common.dataframe.DataFrame; import org.opensearch.ml.common.dataframe.DataFrameBuilder; +import org.opensearch.ml.common.dataframe.DefaultDataFrame; +import org.opensearch.ml.common.dataframe.DoubleValue; +import org.opensearch.ml.common.dataframe.Row; import com.google.gson.JsonArray; import com.google.gson.JsonObject; public class TestData { + public static final String TIME_FIELD = "timestamp"; + public static final String TARGET_FIELD = "price"; + public static DataFrame constructTestDataFrame(int size) { - ColumnMeta[] columnMetas = new ColumnMeta[] { new ColumnMeta("f1", ColumnType.DOUBLE), new ColumnMeta("f2", ColumnType.DOUBLE) }; + return constructTestDataFrame(size, false); + } + + public static DataFrame constructTestDataFrameForLinearRegression(int size) { + ColumnMeta[] columnMetas = new ColumnMeta[] { + new ColumnMeta("feet", ColumnType.DOUBLE), + new ColumnMeta(TARGET_FIELD, ColumnType.DOUBLE) }; + + List rows = new ArrayList<>(); + for (int i = 0; i < size; i++) { + rows.add(new Row(new ColumnValue[] { new DoubleValue(i), new DoubleValue(i) })); + } + return new DefaultDataFrame(columnMetas, rows); + } + + public static DataFrame constructTestDataFrame(int size, boolean addTimeFiled) { + List columnMetaList = new ArrayList<>(); + columnMetaList.add(new ColumnMeta("f1", ColumnType.DOUBLE)); + columnMetaList.add(new ColumnMeta("f2", ColumnType.DOUBLE)); + if (addTimeFiled) { + columnMetaList.add(new ColumnMeta(TIME_FIELD, ColumnType.LONG)); + } + ColumnMeta[] columnMetas = columnMetaList.toArray(new ColumnMeta[0]); DataFrame dataFrame = DataFrameBuilder.emptyDataFrame(columnMetas); Random random = new Random(1); @@ -36,13 +67,19 @@ public static DataFrame constructTestDataFrame(int size) { new double[][] { { 2.0, 1.0 }, { 1.0, 2.0 } } ); MultivariateNormalDistribution[] normalDistributions = new MultivariateNormalDistribution[] { g1, g2 }; + long startTime = 1648154137000l; for (int i = 0; i < size; ++i) { int id = 0; - if (random.nextDouble() < 0.5) { + if (Math.random() < 0.5) { id = 1; } double[] sample = normalDistributions[id].sample(); - dataFrame.appendRow(Arrays.stream(sample).boxed().toArray(Double[]::new)); + Object[] row = Arrays.stream(sample).boxed().toArray(Double[]::new); + if (addTimeFiled) { + long timestamp = startTime + 60_000 * i; + row = new Object[] { row[0], row[1], timestamp }; + } + dataFrame.appendRow(row); } return dataFrame;