Integrating AWS Machine Learning Models With Your Java Microservice
In this post we take a look at incorporating AWS machine learning into your Java microservice environment. Interested? Read on for the details and some code.
Join the DZone community and get the full member experience.
Join For FreeI built my first artificial intelligence (AI) program almost 30 years ago. I took an identification key from a bird field guide and turned it into an application riddled with an embarrassing number of GOTO
statements that led the user through a series of adaptive A/B questions and then presented them with the most probable species identification at the end.
Is the bird:
A - Larger than a dove
B - Smaller or the same size as a dove
Enter Selection =>
The ideology behind AI hasn’t changed since I wrote that piece of software for a high school science project. Underneath the covers, it still consists of code that asks adaptive A/B questions, narrowing the list of possibilities until it can deduce the most statistically probable answer.
What has changed in the world of machine learning is the complexity of the engines presenting the options and the models used to train those engines.
Machine learning (ML) is a process whereby a computer can be taught how to ask questions and make predictions. Developing a machine learning engine requires complex algorithms, a keen understanding of statistical probability and significant computing power. Fortunately, we don’t have to be data scientists, statistics gurus, or have access to a supercomputer to leverage the power of machine learning, thanks to Amazon Machine Learning.
In this post, I demonstrate how to deploy a machine learning model into a microservice in Java — which is much easier to do today than it was 30 years ago.
How Does Machine Learning Work?
AI relies on access to datasets that are analyzed to determine patterns and create statistical models. The model is then used to compare new data with those models, then make decisions based on the comparison results.
To create a model, historical data is gathered and divided into two distinct datasets. The first set is fed in and analyzed by the system to develop a model which will be used to make the prediction. The second group is used as test data to validate the accuracy of this model.
The model itself works on a concept called labeling. In the case of the data we will be using in the example, the label is the success of past marketing campaigns. The models collect examples of data which are labeled as successful and examples which are labeled as unsuccessful. When new or unlabeled data is compared against the model, an algorithm compares the values and issues a prediction on the likely success of the data.
AWS provides extensive documentation on creating ML models on AWS, including an easy-to-follow tutorial that will walk you through setting up the models which I’ll be using in this article. The tutorial is available here and the datasets used.
Setting Up the Environment and Building a Model
Machine learning is only available in the US East (N. Virginia) and EU (Ireland) regions at present. Once I compiled and executed the project located in the targeted-marketing-java project, I was able to view my ML Models on the AWS Machine Learning Dashboard.
It takes a while for AWS to build a model from the data source and evaluate that model for accuracy, but in time, you should have an ML model ready to go. In my case, the model was built and evaluated in less than 15 minutes.
Let’s Get Ready to Make Some Predictions
Once our model is created and validated, it’s time to build a Java microservice which will be able to use the model to make predictions about the result of a targeted marketing campaign for a particular customer.
You can view and download the complete source code from here. (Before getting started, there are a couple of things that I ran into which you may find useful.)
From the AWS Machine Learning Dashboard, click on the model you intend to use for this exercise. Scroll to the bottom of the summary and look for the Create endpoint button in the Predictions section. You will need to have the endpoint enabled if you would like to access it from your application.
I deployed this application on an EC2 instance of the t2.micro type. The instance was built using the base Amazon Linux AMI. Before cloning and running the application, I needed to install Git, Gradle, and update the Java JDK. I also set the instance up with AWS credentials.
$ sudo yum update
........
$ sudo yum install git
........
$ sudo yum remove java-1.7.0-openjdk
........
$ sudo yum install java-1.8.0-openjdk-devel
........
$ git clone https://github.com/echovue/aws_machine_learning_predictions.git
At the top of the PredictionService
class, I declared a static variable with the ID of my model. You’ll want to edit this variable and replace it with the ID of your model file from the dashboard above.
$ cd aws_machine_learning_predictions
$ vi ./src/main/java/com/echovue/service/PredictionService.java
@Service
public class PredictionService {
private static final String MODEL_ID = "ml-hLR6cepAThT";
Finally, it’s probably worth mentioning that this service was developed to interact exclusively with the model created by the AWSLabs learning sample. You should be able to reuse the prediction lookup code and concepts for other models, but be aware that this specific implementation uses the data model provided in that sample.
Interfacing With AWS to Obtain a Prediction
The PredictionService
class is the key to interacting with an ML Model. To complete the prediction, we’ll need to:
- Transform our object into a Map object. Both the key and value for each entry need to be Strings.
- Retrieve the Endpoint URL from AWS.
- Build a prediction request.
- Request the prediction and parse the result.
Each of these steps is broken into a function in the code below, and you can view the entire class here.
@Service
public class PredictionService {
private static final String MODEL_ID = "ml-hLR6cepAThT";
private AmazonMachineLearning client;
public Optional
getPrediction(final Customer customer) { client = AmazonMachineLearningClientBuilder.defaultClient(); PredictResult result = client.predict(buildPredictionRequest(customer)); Map
resultMap = result.getPrediction().getPredictedScores();
if (resultMap.isEmpty()) {
return Optional.empty(); }
return Optional.of(resultMap.get(
"0")); }
private Map
buildCustomerMap(final Customer customer) { Map
customerMap =
new ObjectMapper().convertValue(customer, Map.class); Map
customerStringMap =
new HashMap<>();
for (Map.Entry entry : customerMap.entrySet()) {
customerStringMap.put(entry.getKey().toString(), entry.getValue().toString());
}
return customerStringMap;
}
private PredictRequest buildPredictionRequest(final Customer customer) {
Map
customerMap = buildCustomerMap(customer);
return
new PredictRequest() .withMLModelId(MODEL_ID) .withPredictEndpoint(getModelEndpoint()) .withRecord(customerMap); }
private String getModelEndpoint() { GetMLModelRequest request =
new GetMLModelRequest().withMLModelId(MODEL_ID); GetMLModelResult model = client.getMLModel(request);
return model.getEndpointInfo().getEndpointUrl(); } }
The Prediction
response will be similar to that shown below:
{
"Prediction": {
"details": {
"Algorithm": "SGD",
"PredictiveModelType": "BINARY"
},
"predictedLabel": "0",
"predictedScores": {
"0": 0.021585624665021896
}
}
}
For our model, there will only be a single predictedScore
. This is a value of the Float type and indicates the probability that the result of the marketing campaign will be positive. In the example above, we can deduce that for the values passed in, the probability of a positive response is approximately 2.2%.
I added a little formatting to the PredictionController
class, which returns a string in response to a POST
call with customer details. If you initiate the service with:
$ gradle clean build bootRun
...then, using the following information, you can pass a JSON object to the /prediction
endpoint of the service and expect a response similar to “Chance of response is 31%.”
URL | http://{ip_address}:8080/prediction |
Method | POST |
Headers | Content-Type: application/json |
Body | {"age":30,"job":"professional","marital":"married","education":"basic.9y","housing":"yes" ,"loan":"yes","contact":"cellular","month":"dec","dayOfWeek":"mon","duration":487," campaign":2,"previous":0,"emp_var_rate":-1.8,"cons_price_idx":92.893,"cons_conf_idx": -46.2,"euribor3m":1.313,"nr_employed":5099.1,"default":"no","pdays":50,"poutcome ":"nonexistent"} |
You could also submit the request from the command-line using the curl
command below. Ensure that you are either executing this command from the machine hosting the service or that you update localhost to the IP address of the host machine.
$ curl -X POST \
http://localhost:8080/prediction \
-H 'cache-control: no-cache' \ -H 'content-type: application/json' \ -d '{"age":30,"job":"professional","marital":"married","education":"b asic.9y","housing":"yes","loan":"yes","contact":"cellular","month":"dec","dayOfWeek":"mon","duration":487,"campaign":2,"previous":0,"emp_var_rate":-1.8,"cons_price_idx":92.893,"cons_conf_idx":-46.2,"euribor3m":1.313,"nr_employed":5099.1,"default":"no","pdays":50,"poutcome":"nonexistent"}'
Manipulating the values within the JSON string and resubmitting the request should then result in different values for the prediction. For example, if you locate the age field inside the JSON object, you could change it from "age: 30”
to "age”: 57
and see how the prediction changes.
Published at DZone with permission of Mike Mackrory, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments