├── .gitignore ├── Agenda-BuildersDayDec2018.md ├── Agenda-JoburgLoftMar2019.md ├── README.md ├── banners ├── aws.png ├── buildersday.png ├── joburg-loft.png └── workshop.png ├── git-url.key ├── lab-1-Intro_to_Amazon_AI_ML ├── Intoduction to Amazon ML.docx ├── Intoduction to Amazon ML.pdf ├── bike_share_data.csv └── bike_share_engineered_data.csv ├── lab-2-Building_Chat_Bots_With_Lex ├── README.md ├── images │ ├── Picture01.png │ ├── Picture02.png │ ├── Picture03.png │ ├── Picture04.png │ ├── Picture05.png │ ├── Picture06.png │ ├── Picture07.png │ ├── Picture08.png │ ├── Picture09.png │ ├── Picture10.png │ ├── Picture11.png │ ├── Picture12.png │ ├── Picture13.png │ └── lex.png ├── lex.pptx └── myPersonalBanker_v1.py ├── lab-3-Hands_on_with_Apache_MXNet ├── HandsOnWithApacheMXNet.pptx ├── README.md └── images │ ├── image0-blue.jpeg │ ├── image0-green.jpeg │ ├── image0-red.jpeg │ ├── image1-blue.jpeg │ ├── image1-green.jpeg │ ├── image1-red.jpeg │ ├── mxnet.png │ ├── sample-image0.jpeg │ └── sample-image1.jpeg ├── lab-4-Getting_started_with_Sagemaker ├── README.md ├── images │ ├── Picture01.png │ ├── Picture02.png │ ├── Picture03.png │ ├── Picture04.png │ ├── Picture05.png │ ├── Picture06.png │ ├── Picture08.png │ ├── Picture09.png │ ├── Picture10.png │ ├── Picture11.png │ ├── Picture12.png │ ├── Picture13.png │ ├── Picture14.png │ ├── Picture15.png │ ├── Picture16.png │ └── sagemaker.png └── sa_ml_lab.tar.gz ├── lab-5-Hands_on_with_Rekognition_API ├── README.md ├── compare.sh ├── demo0.png ├── images │ └── rekognition.png ├── rekognitionDetect.py ├── ric.jpg ├── ric_crowd0.jpg ├── ric_crowd1.jpg ├── ric_crowd2.jpg └── ric_harvey.jpeg ├── lab-6-Personalize_your_Recommendations ├── README.md ├── cloudformation_template.yml ├── docs │ ├── personalize-demo-docs.docx │ └── personalize-demo-docs.pdf ├── images │ ├── Picture02.png │ ├── appFrontScreen.png │ ├── appFrontScreenWithModels.png │ ├── appRecNoModels.png │ ├── appRecWithModels.png │ ├── appUrlOutputs.png │ ├── campaignList.png │ ├── campaignSingle.png │ ├── cellTypes.png │ ├── cfnSelectTemplate.png │ ├── changeRegion.png │ ├── consoleCfnSelect.png │ ├── consoleEC2Select.png │ ├── consoleSMSelect.png │ ├── createCampaign.png │ ├── createKeyPair.png │ ├── datasetGroups.png │ ├── djangoAddModel.png │ ├── djangoAdmin.png │ ├── findSagemakerRole.png │ ├── loadBoto3Post.png │ ├── loadBoto3Pre.png │ ├── loadbalancerDNS.png │ ├── openNotebook.png │ ├── personalize.png │ ├── rdsDNS.png │ ├── recipeRanking.png │ ├── selectEC2details.png │ ├── solutionConfig.png │ ├── solutionList.png │ ├── terminateNotebook.png │ ├── uploadFiles.png │ └── uploadFiles2.png ├── personalize_sample_notebook.ipynb └── u.item └── lab-7-Forecast_your_retail_sales ├── WeeklySalesData_FY16-FY18.csv ├── merged-forecast-with-actuals.xlsx └── retail-food-forecast.ipynb /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | -------------------------------------------------------------------------------- /Agenda-BuildersDayDec2018.md: -------------------------------------------------------------------------------- 1 | ![Builders Days 2018](./banners/buildersday.png) 2 | 3 | # AI/ML Workshops 4 | 5 | ## AWS Builders Days 2018 6 | 7 | Welcome to the AI/ML workshops for the AWS Buidlers Days 2018. Today we are going to take a look at several AI/ML offerings from AWS that you can use to build and train models or easily intergrate AI/ML features into your applications. 8 | 9 | Below you'll find todays Agenda and our Evangelists and SA's will work with you in these labs to help you complete the sessions. 10 | 11 | ## Agenda 12 | 13 | | Time | Session | Workshop Title | 14 | |---|:---:|:---| 15 | | 10.00 AM - 11.00 AM | 1 | [Introduction to Amazon AI/ML Services](https://github.com/drandrewkane/AI_ML_Workshops/tree/master/lab-1-Intro_to_Amazon_AI_ML) | 16 | | 11:30 AM - 12:30 PM | 2 | [Building Chat Bots with Lex](https://github.com/drandrewkane/AI_ML_Workshops/tree/master/lab-2-Building_Chat_Bots_With_Lex) | 17 | | 1:30 PM - 2:30 PM | 3 | [Hands on with Apache MXNet and the deep learning AMI](https://github.com/drandrewkane/AI_ML_Workshops/tree/master/lab-3-Hands_on_with_Apache_MXNet) | 18 | | 2:30 PM - 3:30 PM | 4 | [Getting started with Sagemaker](https://github.com/drandrewkane/AI_ML_Workshops/tree/master/lab-4-Getting_started_with_Sagemaker) | 19 | | 3:45 PM – 4:45 PM | 5 | [A hands-on look at the Amazon Rekognition API](https://github.com/drandrewkane/AI_ML_Workshops/tree/master/lab-5-Hands_on_with_Rekognition_API) | 20 | 21 | ## Feedback 22 | 23 | Please leave comments on this git repository and we'd love pull requests also! 24 | 25 | -------------------------------------------------------------------------------- /Agenda-JoburgLoftMar2019.md: -------------------------------------------------------------------------------- 1 | ![JNB Loft 2019](./banners/joburg-loft.png) 2 | 3 | # AI/ML Workshops 4 | 5 | ## AWS Johannesburg Loft 2019 6 | 7 | Welcome to the AI/ML workshops for the AWS Johannesburg Loft 2019. Over 3 days we are going to take a look at several AI/ML offerings from AWS that you can use to build and train models or easily intergrate AI/ML features into your applications. 8 | 9 | Below you'll find this week's series Agenda and our SA's will work with you in these labs to help you complete the sessions. 10 | 11 | ## Agenda 12 | 13 | | # | Date | Time | Workshop Title | 14 | |---|:---:|:---:|:---| 15 | | 1 | Mon 25th Mar | 1.00 PM - 2.00 PM | [Personalize Your Recommendations](https://github.com/drandrewkane/AI_ML_Workshops/tree/master/lab-6-Personalize_your_Recommendations) | 16 | | 2 | Mon 25th Mar | 2:00 PM - 3:00 PM | [Hands on with Apache MXNet and the deep learning AMI](https://github.com/drandrewkane/AI_ML_Workshops/tree/master/lab-3-Hands_on_with_Apache_MXNet) | 17 | | 3 | Tue 26th Mar | 1:00 PM – 2:00 PM | [A hands-on look at the Amazon Rekognition API](https://github.com/drandrewkane/AI_ML_Workshops/tree/master/lab-5-Hands_on_with_Rekognition_API) | 18 | | 4 | Tue 26th Mar | 2:00 AM - 3:00 PM | [Building Chat Bots with Lex](https://github.com/drandrewkane/AI_ML_Workshops/tree/master/lab-2-Building_Chat_Bots_With_Lex) | 19 | | 5 | Tue 26th Mar | 3:00 PM - 4:00 PM | [Getting started with Sagemaker](https://github.com/drandrewkane/AI_ML_Workshops/tree/master/lab-4-Getting_started_with_Sagemaker) | 20 | 21 | ## Feedback 22 | 23 | Please leave comments on this git repository and we'd love pull requests also! 24 | 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Workshops](./banners/workshop.png) 2 | 3 | # AI/ML Workshops 4 | 5 | Welcome to our collection of AI/ML workshops. These are run as a series at AWS public events, such as Lofts, Summits or Builders' Days, but can all be run independently. They will walk you through several AI/ML offerings from AWS that you can use to build and train models or easily intergrate AI/ML features into your applications. 6 | 7 | Below you'll find a list of events where these have been run, so please select the session that your are in so that you can see what's being run today. Our Evangelists and SA's will work with you in these labs to help you complete the sessions. 8 | 9 | ## Agenda 10 | 11 | | Date | Labs | Event Title | 12 | |---|:---:|:---| 13 | | Mar 25th/26th 2019 | 5 | [AWS Loft - Johannesburg](https://github.com/drandrewkane/AI_ML_Workshops/tree/master/Agenda-JoburgLoftMar2019.md) | 14 | | Dec 10th/12th 2018 | 5 | [AWS Builders' Day - London and Dublin](https://github.com/drandrewkane/AI_ML_Workshops/tree/master/Agenda-BuildersDayDec2018.md) | 15 | 16 | ## Feedback 17 | 18 | Please leave comments on this git repository and we'd love pull requests also! 19 | 20 | -------------------------------------------------------------------------------- /banners/aws.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/banners/aws.png -------------------------------------------------------------------------------- /banners/buildersday.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/banners/buildersday.png -------------------------------------------------------------------------------- /banners/joburg-loft.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/banners/joburg-loft.png -------------------------------------------------------------------------------- /banners/workshop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/banners/workshop.png -------------------------------------------------------------------------------- /git-url.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/git-url.key -------------------------------------------------------------------------------- /lab-1-Intro_to_Amazon_AI_ML/Intoduction to Amazon ML.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-1-Intro_to_Amazon_AI_ML/Intoduction to Amazon ML.docx -------------------------------------------------------------------------------- /lab-1-Intro_to_Amazon_AI_ML/Intoduction to Amazon ML.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-1-Intro_to_Amazon_AI_ML/Intoduction to Amazon ML.pdf -------------------------------------------------------------------------------- /lab-2-Building_Chat_Bots_With_Lex/README.md: -------------------------------------------------------------------------------- 1 | ![Workshops](../banners/aws.png) ![Workshops](images/lex.png) 2 | **Last Updated:** March 2019 3 | 4 | # Building Chatbots with Amazon Lex 5 | 6 | ## Overview 7 | 8 | Amazon Lex is a service for building conversational interfaces into any application using voice and text. Amazon Lex provides the advanced deep learning functionalities of automatic speech recognition (ASR) for converting speech to text, and natural language understanding (NLU) to recognize the intent of the text, to enable you to build applications with highly engaging user experiences and lifelike conversational interactions. With Amazon Lex, the same deep learning technologies that power Amazon Alexa are now available to any developer, enabling you to quickly and easily build sophisticated, natural language, conversational bots ("chatbots"). This lab is designed to demonstrate how to create a new bot including defining intents and slots. This lab will walk you through the following: 9 | 10 | - Creating a Lex bot 11 | - Adding intents 12 | - Adding slot types 13 | - Using AWS Lambda as the back-end logic for Lex 14 | 15 | # Create the bot 16 | 17 | 1. Log in to the [AWS console](https://console.aws.amazon.com/lex/home) and navigate to the Amazon Lex service 18 | 2. If you have never created a bot, click "Get Started" 19 | 3. Choose "Custom bot", which will display a dialog asking you to defined your bot 20 | 4. Our bot name will be "PersonalBanker" 21 | 5. Choose your preferred output voice 22 | 6. Session timeout should be 5 minute 23 | 7. Choose "No" to the Children's Online Privacy Protection Act (COPPA) question 24 | 25 | The form should now look as follows, noting that we're going to accept the default IAM role. 26 | 27 | ![Create your Lex bot](images/Picture01.png) 28 | 29 | 8. Click "Create" 30 | 9. We will start by creating an intent, which represents an action that the user wants to perform. For example, we're going to create three intents in this lab for different actions a user can perform: Get Account Details; Get Loan Details; Get Loan Products. Click the "Create Intent" button. 31 | 10. In the window that pops-up click the "Create intent" link 32 | 33 | ![Add intent](images/Picture02.png) 34 | 35 | 11. Our first intent enables the user to get account details, so name this intent "GetAccountDetail" then click "Add". 36 | 37 | ![Create intent](images/Picture03.png) 38 | 39 | 12. We now want to provide samples of what our user would type or say to perform this action (i.e. to activate this intent). Under "Sample utterances", type the below phrases and hit [enter] or click the blue "+" sign after each phrase. Make sure you do not add a question mark at the end of the phrase as this will cause build issues later on. 40 | 41 | - _What is my {AccountType} balance_ 42 | - _{AccountType} account balance_ 43 | 44 | **NOTE** : {AccountType} represents a 'slot' which is information that we need to process the users request. Type it exactly as shown above with the braces, and in the next step we will define the 'AccountType' slot and list the acceptable values (checking, savings). Once the lab is complete, the user will interact with our bot by saying something like "What is my checking balance". 45 | 46 | ![Sample utterance](images/Picture04.png) 47 | 48 | 13. Next we define a slot which is information we need to process the users request. This information can be included in the utterance (query) that the user types or says, and if not included, Lex will prompt the user for the information. While Lex includes many built-in slot types (such as number, colour, city, food, etc), in this case we want to define a custom slot to get the account type that the user is referring to. 49 | 50 | Click on the blue "+" sign next to "Slot types" on the left hand side of the screen and select the "Create slot type" link - note, "Slot types" is initially greyed out, and on some laptop screens may not be obvious 51 | 52 | ![Add slot type](images/Picture05.png) 53 | 54 | 14. For 'Slot type name' enter "AccountType" and optionally enter a description (although description is not required) 55 | 15. For Value, we want to allow the user to make queries against either their "checking" or "savings" account so enter those as values, clicking the blue "+" sign after each word. 56 | 57 | ![Create slot AccountType](images/Picture06.png) 58 | 59 | 16. Click "Add slot to intent" 60 | 17. Now that have defined what the user is going to ask via an utterance, and also defined our customer "vocabularly" via a slot type, we now have to additionally link the slot types to the intent with additional information such as whether it is required or not an the prompt to use if Lex has ask the user to provide it. 61 | 62 | In the existing Slot list cange the "Name" field from "slotOne" to "AccountType" so that it matches the slot name that we specified when we created the sample utterences – note that these do not have to match, but this example will be clearer if we keep them the same. 63 | 64 | 18. Specify "What type of account?" for the "Prompt" field. This prompt will be used by our bot if the user does not specify an account type when asking a question. 65 | 66 | ![Prompt for AccountType](images/Picture07.png) 67 | 68 | 19. Scroll down and click "Save Intent" 69 | 70 | If at any point you made a mistake in the steps above, selecting the "Latest" version of the intent at the top, next to the intent name, will allow you to edit your choices. 71 | 72 | 20. Let's build this simple Bot: Hit the grey Build button at the top right corner. You will be asked for confirmation to build. Click "Build". 73 | 74 | The build process takes approximately a minute. Once complete, you can ask your bot a question as a way to test it. For example, you could type "What is my checking balance?" in the chat window, or click the microphone symbol, speak your request and client it again to have Lex translate your speech to text. At this stage since we have not added in the backend Lambda function, the response will be that the bot is ready for fulfillment. 75 | 76 | Let's add 2 more intents and one more slot type. 77 | 78 | 21. Click the blue "+" sign next to "Intents" on the left hand side of the page, then click "Create intent". 79 | 22. This intent will allow users to get information on their outstanding home or car loan balance, so set the name to "GetLoanDetail" then click "Add" 80 | 23. For sample utterences (the things we expect out users to type/say to our bot to trigger a specific intent/action), add the following phrase then click the blue "+" at the end of the sample utterance box. 81 | - "Get my {LoanType} loan balance" 82 | 24. Now we'll create a new slot type which we'll use to store a response from the user as to whether they are wanting the intent to access their _car_ loan balance or their _home_ loan balance. Click the blue "+" sign next to 'Slot types' on the left hand side of the screen 83 | - For 'Slot type name' enter "LoanType" and optionally provide a description 84 | - Enter the following two options as values: 85 | - car 86 | - home 87 | 88 | ![Create slot LoanType](images/Picture08.png) 89 | 90 | 25. Click "Add slot to intent" 91 | 26. Change the name of the slot on the intent detail screen from "slotOne" to "LoanType" 92 | 27. Provide a prompt (such as "Which loan account?") 93 | 28. Make sure that the "Required" column is selected 94 | 95 | ![Add slot LoanDetail](images/Picture09.png) 96 | 97 | 29. Click 'Save Intent' 98 | 99 | Now we'll add a final intent to allow users to get more information on the loan products that we have. 100 | 101 | 30. Click the blue "+" sign next to "Intents" on the left hand side of the page, then click "Create new intent". 102 | 31. Set the name of the intent to "GetLoanProducts" then click 'Add' 103 | 32. For sample utterences (the things we expect our users to type/say to our bot to trigger a specific intent/action), add the following two phrases. Make sure **not** to include punctuation (such as comma's or question marks). 104 | 105 | - "What is the rate on the {LoanType} loan" 106 | - "More loan info" 107 | 108 | The "More loan info" utterance will be used as a follow-up after a user has asked for information on their outstanding loan balance. We're not going to create a new slot type for this intent, as we're going to re-use the LoanType slot from the intent that we defined previously. 109 | 110 | 33. As we haven't defined a new slot type for this intent there isn't a slot entry yet under on the intent detail panel. Enter the following values into the empty slot line: 111 | - Name: LoanType 112 | - Slot Type: LoanType (select from the drop-down list) 113 | - Prompt: Which loan type? 114 | 34. Click the blue "+" button to the right of the Slots information to add this slot to the intent 115 | 116 | ![Add slot LoanType](images/Picture10.png) 117 | 118 | 35. Click 'Save Intent' 119 | 36. Click Build, and click Build again when prompted for confirmation. 120 | 121 | Our bot is almost ready, and you can test it like before with the new utterances, but it is still quite dumb – all it needs now is a smart backend. 122 | 123 | # Create a Lambda function 124 | 125 | Here we will create a Lambda function that has some Python code to detect the intent name ('GetAccountDetail', 'GetLoanDetail' or 'GetLoanProducts') and to return static values based on the AccountType (checking, saving) or LoanType (car, home) included in the intent. In a real world example we would have already authenticated the user and would write Python code to do a database lookup for the account balances. 126 | 127 | 1. Use the AWS Console to navigate to Lambda. 128 | 2. Click on the orange 'Create a function' link under the 'Getting Started' section 129 | 3. On the "Create function" page, click the "Author from scratch" button 130 | 4. Let's give our function the name of "myPersonalBanker" and optionally provide a description 131 | 5. Choose Python 2.7 as the Runtime 132 | 6. We will "Create new role from template – give it a Lex-style role name (such as "LexRole") and select "Test Harness permissions" as the policy template. 133 | 134 | ![Author new Lambda function](images/Picture11.png) 135 | 136 | 7. Hit "Create function" on the bottom right and you'll be take to the "Configuration" window. We are not adding any additional triggers, nor are we using Lambda Layers, so scroll down to the "Function code" section 137 | 8. Open the lambda function code you will find [here](https://github.com/drandrewkane/AI_ML_Workshops/blob/master/lab-2-Building_Chat_Bots_With_Lex/myPersonalBanker_v1.py) (myPersonalBanker\_v1.py). Copy and paste the code into the inline editor – make sure that you overwrite any template code that is already in the code box 138 | 9. Scroll down to the '"Execution role" section and ensure that the role you created previously is selected in the "Existing role" drop-down – if not then please select it 139 | 10. Leave the rest unchanged, then hit the orange "Save" button at the top of the screen 140 | 141 | # Link the bot with the Lambda function 142 | 143 | In this step we will link the three intents we created to the Lambda function. We do this by providing the Lambda function as the method that contains the business logic used to 'fulfill' the users requests. Once this is done (and the bot rebuilt), when a user specifies an intent (such as 'what is my checking account balance'), Lex will call our Lambda function and pass it the intent name ('GetAccountDetail') and the slot value ('checking'). 144 | 145 | To do this, we go back to the [Lex Console](https://console.aws.amazon.com/lex). 146 | 147 | 1. Click on Personal Banker 148 | 2. Enure the 'GetAccountDetail' intent is selected 149 | 3. Make sure that the 'Latest' version is selected for both the bot and the intent 150 | 151 | ![Set Lex latest version](images/Picture12.png) 152 | 153 | 4. Scroll down to "Fulfillment", select "AWS Lambda function", choose "myPersonalBanker" and click "OK" in the popup warning window which opens. It indicates you are giving Lex the permission to run this Lambda function. 154 | 155 | ![Add Lambda permission](images/Picture13.png) 156 | 157 | 5. Click "Save intent" 158 | 6. Repeat the above steps **3, 4 and 5** for intents "GetLoanDetail" and "GetLoanProducts" 159 | 7. Click "Build" and then click "Build" again on the confirmation screen. 160 | 161 | It is time we give this bot a run! 162 | 163 | # Chat with your own bot 164 | 165 | 1. Start by typing "What is my checking account balance?" (or press the microphone button and ask your question using your computer mic). 166 | 2. You should get an answer. 167 | 3. Then type "What is my home loan balance?" (or ask using your mic) 168 | - Notice that Lex is able to recognize that you are wanting to trigger the GetLoanDetail intent even though what you typed, " **What is** my home loan balance?", did not exactly match the sample utterance that you configured the intent with which was " **Get my** {LoanType} loan balance". 169 | 4. Type 'more loan info' and see how Lex returns information on the current, new home loan rate. In this case, because we didn't set the 'slot' to be required, we didn't need to specify whether we were looking for more information on _car_ or _home_ loans … Lex returned information on the loan type (in this case, home) that we had just asked about. 170 | 171 | # Conclusion 172 | 173 | In this lab you have learned the basic operations to manage a Lex bot. First, you created a bot, then you defined intents and slot types. Finally you defined a Lambda function and attached it to your chatbot. -------------------------------------------------------------------------------- /lab-2-Building_Chat_Bots_With_Lex/images/Picture01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-2-Building_Chat_Bots_With_Lex/images/Picture01.png -------------------------------------------------------------------------------- /lab-2-Building_Chat_Bots_With_Lex/images/Picture02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-2-Building_Chat_Bots_With_Lex/images/Picture02.png -------------------------------------------------------------------------------- /lab-2-Building_Chat_Bots_With_Lex/images/Picture03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-2-Building_Chat_Bots_With_Lex/images/Picture03.png -------------------------------------------------------------------------------- /lab-2-Building_Chat_Bots_With_Lex/images/Picture04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-2-Building_Chat_Bots_With_Lex/images/Picture04.png -------------------------------------------------------------------------------- /lab-2-Building_Chat_Bots_With_Lex/images/Picture05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-2-Building_Chat_Bots_With_Lex/images/Picture05.png -------------------------------------------------------------------------------- /lab-2-Building_Chat_Bots_With_Lex/images/Picture06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-2-Building_Chat_Bots_With_Lex/images/Picture06.png -------------------------------------------------------------------------------- /lab-2-Building_Chat_Bots_With_Lex/images/Picture07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-2-Building_Chat_Bots_With_Lex/images/Picture07.png -------------------------------------------------------------------------------- /lab-2-Building_Chat_Bots_With_Lex/images/Picture08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-2-Building_Chat_Bots_With_Lex/images/Picture08.png -------------------------------------------------------------------------------- /lab-2-Building_Chat_Bots_With_Lex/images/Picture09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-2-Building_Chat_Bots_With_Lex/images/Picture09.png -------------------------------------------------------------------------------- /lab-2-Building_Chat_Bots_With_Lex/images/Picture10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-2-Building_Chat_Bots_With_Lex/images/Picture10.png -------------------------------------------------------------------------------- /lab-2-Building_Chat_Bots_With_Lex/images/Picture11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-2-Building_Chat_Bots_With_Lex/images/Picture11.png -------------------------------------------------------------------------------- /lab-2-Building_Chat_Bots_With_Lex/images/Picture12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-2-Building_Chat_Bots_With_Lex/images/Picture12.png -------------------------------------------------------------------------------- /lab-2-Building_Chat_Bots_With_Lex/images/Picture13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-2-Building_Chat_Bots_With_Lex/images/Picture13.png -------------------------------------------------------------------------------- /lab-2-Building_Chat_Bots_With_Lex/images/lex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-2-Building_Chat_Bots_With_Lex/images/lex.png -------------------------------------------------------------------------------- /lab-2-Building_Chat_Bots_With_Lex/lex.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-2-Building_Chat_Bots_With_Lex/lex.pptx -------------------------------------------------------------------------------- /lab-2-Building_Chat_Bots_With_Lex/myPersonalBanker_v1.py: -------------------------------------------------------------------------------- 1 | 2 | import logging 3 | 4 | logger = logging.getLogger() 5 | logger.setLevel(logging.DEBUG) 6 | 7 | 8 | 9 | def close(session_attributes, fulfillment_state, message): 10 | response = { 11 | 'sessionAttributes': session_attributes, 12 | 'dialogAction': { 13 | 'type': 'Close', 14 | 'fulfillmentState': fulfillment_state, 15 | 'message': message 16 | } 17 | } 18 | 19 | return response 20 | 21 | """ --- Functions that control the bot's behavior --- """ 22 | 23 | 24 | def get_balance(intent_request): 25 | logger.debug('in get_balance') 26 | session_attributes = intent_request['sessionAttributes'] if intent_request['sessionAttributes'] is not None else {} 27 | slots = intent_request['currentIntent']['slots'] 28 | account_type = slots['AccountType'] 29 | balance = 0 30 | if account_type.lower() == 'checking': 31 | balance = 5000 32 | if account_type.lower() == 'savings': 33 | balance = 10000 34 | 35 | session_attributes['currentIntent'] = intent_request['currentIntent']['name'] 36 | 37 | return close( 38 | session_attributes, 39 | 'Fulfilled', 40 | { 41 | 'contentType': 'PlainText', 42 | 'content': 'You account balance is ${}.'.format(balance) 43 | } 44 | ) 45 | 46 | def get_loan_balance(intent_request): 47 | logger.debug('in get_loan_balance') 48 | 49 | session_attributes = intent_request['sessionAttributes'] if intent_request['sessionAttributes'] is not None else {} 50 | slots = intent_request['currentIntent']['slots'] 51 | loan_type = slots['LoanType'] 52 | balance = 0 53 | rate = 0 54 | 55 | if loan_type.lower() == 'car': 56 | balance = 35000 57 | rate = 0.05 58 | if loan_type.lower() == 'home': 59 | balance = 1200000 60 | rate = 0.035 61 | 62 | session_attributes['currentIntent'] = intent_request['currentIntent']['name'] 63 | session_attributes['loan_type'] = loan_type 64 | return close( 65 | session_attributes, 66 | 'Fulfilled', 67 | { 68 | 'contentType': 'PlainText', 69 | 'content': "You {} balance is ${}, and your current rate is {:.2%}. You might qualify for lower rates, please type 'more loan info' to hear more?".format(loan_type, balance, rate) 70 | } 71 | ) 72 | 73 | def get_loan_offer(intent_request): 74 | logger.debug('in get_loan_balance') 75 | 76 | session_attributes = intent_request['sessionAttributes'] if intent_request['sessionAttributes'] is not None else {} 77 | slots = intent_request['currentIntent']['slots'] 78 | 79 | loan_type = slots['LoanType'] 80 | 81 | 82 | if loan_type is None and session_attributes['currentIntent'] == 'GetLoanDetail': 83 | loan_type = session_attributes['loan_type'] 84 | 85 | 86 | rate = 0 87 | 88 | if loan_type.lower() == 'car': 89 | rate = 0.04 90 | if loan_type.lower() == 'home': 91 | rate = 0.03 92 | 93 | session_attributes['currentIntent'] = intent_request['currentIntent']['name'] 94 | 95 | return close( 96 | session_attributes, 97 | 'Fulfilled', 98 | { 99 | 'contentType': 'PlainText', 100 | 'content': 'The current rate for {} loan is {:.2%}'.format(loan_type.lower(), rate) 101 | } 102 | ) 103 | 104 | 105 | # --- Intents --- 106 | 107 | 108 | def dispatch(intent_request): 109 | """ 110 | Called when the user specifies an intent for this bot. 111 | """ 112 | 113 | logger.debug('dispatch userId={}, intentName={}'.format(intent_request['userId'], intent_request['currentIntent']['name'])) 114 | 115 | intent_name = intent_request['currentIntent']['name'] 116 | 117 | 118 | # Dispatch to your bot's intent handlers 119 | 120 | if intent_name == 'GetAccountDetail': 121 | return get_balance(intent_request) 122 | elif intent_name == 'GetLoanDetail': 123 | return get_loan_balance(intent_request) 124 | elif intent_name == 'GetLoanProducts': 125 | return get_loan_offer(intent_request) 126 | 127 | 128 | raise Exception('Intent with name ' + intent_name + ' not supported') 129 | 130 | 131 | # --- Main handler --- 132 | 133 | 134 | def lambda_handler(event, context): 135 | """ 136 | Route the incoming request based on intent. 137 | The JSON body of the request is provided in the event slot. 138 | """ 139 | 140 | logger.debug('event.bot.name={}'.format(event['bot']['name'])) 141 | 142 | return dispatch(event) 143 | 144 | -------------------------------------------------------------------------------- /lab-3-Hands_on_with_Apache_MXNet/HandsOnWithApacheMXNet.pptx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-3-Hands_on_with_Apache_MXNet/HandsOnWithApacheMXNet.pptx -------------------------------------------------------------------------------- /lab-3-Hands_on_with_Apache_MXNet/README.md: -------------------------------------------------------------------------------- 1 | ![Workshops](../banners/aws.png) ![Workshops](images/mxnet.png) 2 | **Last Updated:** December 2018 3 | # Hands on with Apache MXNet 4 | 5 | ### Requirements 6 | 7 | - AWS Account 8 | - Basic understanding of ML and NDArrays 9 | - SSH Client and key 10 | - Linux Skills 11 | 12 | ### Credits 13 | 14 | This workshop is adapted from Julien Simons blog posts: [https://medium.com/@julsimon/getting-started-with-deep-learning-and-apache-mxnet-34a978a854b4](https://medium.com/@julsimon/getting-started-with-deep-learning-and-apache-mxnet-34a978a854b4) 15 | 16 | ## Workshop Overview 17 | 18 | In this workshop we are going to take a look at running Apache MXNet on the Amazon Linux Deep Learning AMI. We'll take a pre-trained image reckognition model and use this to predict the contents of images we feed into the model. 19 | 20 | ## Running MXNet on AWS 21 | 22 | AWS provides you with the Deep Learning AMI, available both for Amazon Linux and Ubuntu - simply search for __Deep Learning AMI__ when launching an EC2 instance, but just remember to select the **Amazon Linux**. This AMI comes pre-installed with many Deep Learning frameworks (MXNet included), as well as all the Nvidia tools and more. No plumbing needed. 23 | 24 | You can run this AMI either on a standard instance or on a GPU instance. If you want to train a model and don’t have a NVidia GPU on your machine your most inexpensive option with this AMI will be to use a *p2.xlarge* instance at $0.90 per hour. 25 | 26 | However in these labs we are using pre-trained models for speed so a standard *m5.2xlarge* instance of Amazon Linux will be fine . This will allow us to get going with the lab without installing any special tools as the Deep Learning AMI comes with those pre-baked. 27 | 28 | When you launch the instance use configuration like the following: 29 | 30 | - Use a VPC with an Internet Gateway 31 | - Enable Auto-Assign Public IP 32 | - 75Gb of SSD storage 33 | - Security Group allowed SSH from the Internet 34 | - Create a new EC2 key-pair, or re-use an existing EC2 key-pair that you have locally 35 | 36 | Once the instance has launched, connect to it using SSH with a tool of your preference, using the public IP address of the instance and the user **ec2-user**. For instance, on a Mac the standard CLI command for doing would be similar to: 37 | 38 | `ssh -i myEC2keypairfile.pem ec2-user@10.11.12.13` 39 | 40 | Remember to replade the IP address with that of your instance! You should see the following screen banner, followed by a series of instructions to enable the various available frameworks; the list is long, so the screen below is just the start of the output. 41 | 42 | ```bash 43 | ============================================================================= 44 | __| __|_ ) 45 | _| ( / Deep Learning AMI (Amazon Linux) Version 22.0 46 | ___|\___|___| 47 | ============================================================================= 48 | 49 | Please use one of the following commands to start the required environment with the framework of your choice: 50 | for MXNet(+Keras2) with Python3 (CUDA 9.0 and Intel MKL-DNN) _____________________________________ source activate mxnet_p36 51 | for MXNet(+Keras2) with Python2 (CUDA 9.0 and Intel MKL-DNN) _____________________________________ source activate mxnet_p27 52 | 53 | <> 54 | 55 | [ec2-user@ip-172-31-42-173 ~] _ 56 | ``` 57 | 58 | If you have launched a GPU-enabled intance then you can quickly see what's available with this command: 59 | 60 | ```bash 61 | [ec2-user@ip-172-31-29-159 ~]$ nvidia-smi -L 62 | GPU 0: Tesla K80 (UUID: GPU-cf550255-cb68-db1d-e9ad-ff9e6681c0d8) 63 | ``` 64 | 65 | Before we start we need to activate our framework, and in our case we want to use MXNet and python 2, so enter the following command: 66 | 67 | ```bash 68 | [ec2-user@ip-172-31-29-159 ~]$ source activate mxnet_p27 69 | WARNING: First activation might take some time (1+ min). 70 | Installing MXNet optimized for your Amazon EC2 instance...... 71 | Env where framework will be re-installed: mxnet_p27 72 | <> 73 | Installation complete. 74 | ``` 75 | 76 | ## Using a pre-trained model 77 | 78 | ### The MXNet model zoo 79 | 80 | In this first part of the lab we are going to recognising images with Inception v3, published in December 2015, Inception v3 is an evolution of the GoogleNet model (which won the 2014 ImageNet challenge). We won’t go into the details of the research paper, but paraphrasing its conclusion, Inception v3 is 15–25% more accurate than the best models available at the time, while being six times cheaper computationally and using at least five times less parameters (i.e. less RAM is required to use the model). 81 | 82 | The model zoo is a collection of pre-trained models ready for use. You’ll find the model definition, the model parameters (i.e. the neuron weights) and instructions. 83 | 84 | Let’s download the definition and the parameters. Feel free to open the first file you’ll see the definition of all the layers. The second one is a binary file, so don’t try and open that. 85 | 86 | ```bash 87 | $ wget http://data.mxnet.io/models/imagenet/inception-bn/Inception-BN-symbol.json 88 | 89 | $ wget -O Inception-BN-0000.params http://data.mxnet.io/models/imagenet/inception-bn/Inception-BN-0126.params 90 | ``` 91 | 92 | __Note:__ if your compute resource is defaulting an IPv6 download, and the connection is simply taking too long (or not starting), then you can always force __wget__ to use IPv4 by appending the __-4__ parameter. 93 | 94 | Since this model has been trained on the ImageNet data set, we also need to download the corresponding list of image categories which contains the 1000 categories, that way we can see the human readable prediction output. You can take a look at this file also. 95 | 96 | ```bash 97 | $ wget https://s3-eu-west-1.amazonaws.com/ak-public-docs/synset.txt 98 | 99 | $ wc -l synset.txt 100 | 1000 synset.txt 101 | 102 | $ head -5 synset.txt 103 | n01440764 tench, Tinca tinca 104 | n01443537 goldfish, Carassius auratus 105 | n01484850 great white shark, white shark, man-eater, man-eating shark, Carcharodon carcharias 106 | n01491361 tiger shark, Galeocerdo cuvieri 107 | n01494475 hammerhead, hammerhead shark 108 | ``` 109 | 110 | Now we are also going to need some sample images to test the model against. I'm going to suggest two images and if you are feeling adventurous, feel free to add some of your own images and have a look at the outputs 111 | 112 | ```bash 113 | wget -O image0.jpeg https://cdn-images-1.medium.com/max/1600/1*sPdrfGtDd_6RQfYvD5qcyg.jpeg 114 | 115 | wget -O image1.jpeg http://kidszoo.org/wp-content/uploads/2015/02/clownfish3-1500x630.jpg 116 | ``` 117 | 118 | ### Loading the model for use 119 | 120 | Open your python shell, 121 | 122 | ```bash 123 | python 124 | >>> 125 | ``` 126 | 127 | Load the model from its saved state - note that this first call may take a number of seconds to complete. MXNet calls this a checkpoint. In return, we get the input Symbol and the model parameters. 128 | 129 | ```python 130 | import mxnet as mx 131 | 132 | sym, arg_params, aux_params = mx.model.load_checkpoint('Inception-BN', 0) 133 | ``` 134 | 135 | Create a new Module and assign it the input Symbol. We could also use a context parameter indicating where we want to run the model. By default the module uses the value cpu(0), but we could also use gpu(0) to run this on a GPU. 136 | 137 | ```python 138 | mod = mx.mod.Module(symbol=sym) 139 | ``` 140 | 141 | Bind the input Symbol to input data. We’ll call it ‘data’ because that’s its name in the input layer of the network (look at the first few lines of the JSON file). 142 | 143 | Define the shape of ‘data’ as 1 x 3 x 224 x 224. 144 | 145 | ```python 146 | mod.bind(for_training=False, data_shapes=[('data', (1,3,224,224))]) 147 | ``` 148 | 149 | ‘224 x 224’ is the image resolution, that’s how the model was trained. ‘3’ is the number of channels : red, green and blue (in this order). ‘1’ is the batch size: we’ll predict one image at a time. **Note:** You may get an error around label-names - you can ignore this. 150 | 151 | Set the model parameters. 152 | 153 | ```python 154 | mod.set_params(arg_params, aux_params) 155 | ``` 156 | 157 | That’s all it takes. Four lines of code! Now it’s time to push some data in there and see what happens. 158 | 159 | ### Data preparation 160 | 161 | Before we get some predictions out of our model we'll need to prep the data (the images you downloaded) 162 | 163 | Remember that the model expects a 4-dimension NDArray holding the red, green and blue channels of a single 224 x 224 image. We’re going to use the popular OpenCV library to build this NDArray from our input image. This is already installed on the Amazon Deep Learning AMI. 164 | 165 | First lets load some libaries we'll need. 166 | 167 | ```python 168 | import numpy as np 169 | import cv2 170 | ``` 171 | 172 | Now we read the image, this will return a numpy array shaped as (image height, image width, 3), with the three channels in BGR order (blue, green and red). 173 | 174 | ```python 175 | img = cv2.imread('') 176 | ``` 177 | 178 | Let’s convert the image to RGB, so we have the correct order (RGB) for the pre-trained model we are using. 179 | 180 | ```python 181 | img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) 182 | ``` 183 | 184 | Now resize the image to 224 x 224. 185 | 186 | ```python 187 | img = cv2.resize(img, (224, 224,)) 188 | ``` 189 | 190 | reshape the array from (image height, image width, 3) to (3, image height, image width). 191 | 192 | ```python 193 | img = np.swapaxes(img, 0, 2) 194 | img = np.swapaxes(img, 1, 2) 195 | ``` 196 | 197 | Add a fourth dimension and build the NDArray 198 | 199 | ```python 200 | img = img[np.newaxis, :] 201 | array = mx.nd.array(img) 202 | 203 | >>> print array.shape 204 | (1L, 3L, 224L, 224L) 205 | ``` 206 | 207 | Here’s our input pictures. 208 | 209 | _Guitarist: images/sample-image0.jpeg_ 210 | ![Input picture 448x336 (Source: metaltraveller.com)](images/sample-image0.jpeg) 211 | 212 | _Clownfish: images/sample-image1.jpeg_ 213 | ![Input picture 1500x630 (Source: kidszoo.org](images/sample-image1.jpeg) 214 | 215 | There imput sizes are 448x336 / 1500x630 and they are in full colour. Remember our model needs images of 224x224 and in RGB. 216 | 217 | Once processed, these pictures have been resized and split into RGB channels stored in array[0] 218 | 219 | _Guitarist_ 220 | ![array[0][0] : 224x224 red channel](images/image0-red.jpeg) ![array[0][1] : 224x224 green channel](images/image0-green.jpeg) ![array[0][2] : 224x224 blue channel](images/image0-blue.jpeg) 221 | 222 | _Clown Fish_ 223 | ![array[0][0] : 224x224 red channel](images/image1-red.jpeg) ![array[0][1] : 224x224 red channel](images/image1-green.jpeg) ![array[0][2] : 224x224 blue channel](images/image1-blue.jpeg) 224 | 225 | If you choose to increase the batch size to higher than 1 and run both images through the model together, then we would have a second image in array[1], a third in array[2] and so on. 226 | 227 | Now your data is prepared let’s predict! 228 | 229 | ### Predicting 230 | 231 | Normally we'd use a module object and we must feed data to a model in batches, the common way to do this is to use a data iterator (specifically, we used an NDArrayIter object). 232 | 233 | Here, we’d like to predict a single image, so although we could use data iterator, it’d probably be overkill. Instead, we’re going to create a named tuple, called Batch, which will act as a fake iterator by returning our input NDArray when its data attribute is referenced. 234 | 235 | ```python 236 | from collections import namedtuple 237 | Batch = namedtuple('Batch', ['data']) 238 | ``` 239 | 240 | Now we can pass this “batch” to the model and let it predict. 241 | 242 | ```python 243 | mod.forward(Batch([array])) 244 | ``` 245 | 246 | This may give a *malloc* message - ignore it. The model will output an NDArray holding the 1000 probabilities, corresponding to the 1000 categories. It has only one line since batch size is equal to 1. 247 | 248 | ```python 249 | prob = mod.get_outputs()[0].asnumpy() 250 | 251 | >>> prob.shape 252 | (1, 1000) 253 | ``` 254 | 255 | Let’s turn this into an array with squeeze(). Then, using argsort(), we’re creating a second array holding the index of these probabilities sorted in descending order. 256 | 257 | ```python 258 | prob = np.squeeze(prob) 259 | 260 | >>> prob.shape 261 | (1000,) 262 | >> prob 263 | [ 4.14978594e-08 1.31608676e-05 2.51907986e-05 2.24045834e-05 264 | 2.30327873e-06 3.40798979e-05 7.41563645e-06 3.04062659e-08 etc. 265 | 266 | sortedprob = np.argsort(prob)[::-1] 267 | 268 | >> sortedprob.shape 269 | (1000,) 270 | ``` 271 | 272 | According to the model, the most likely category for this picture is #546 (if you are using image0.jpeg), with a probability of over 65%. 273 | 274 | ```python 275 | >> sortedprob 276 | [546 819 862 818 542 402 650 420 983 632 733 644 513 875 776 917 795 277 | etc. 278 | >> prob[546] 279 | 0.6544399 280 | ``` 281 | 282 | Let’s find the name of this category. Using the synset.txt file, we can build a list of categories and find the one at index 546. 283 | 284 | ```python 285 | synsetfile = open('synset.txt', 'r') 286 | categorylist = [] 287 | for line in synsetfile: 288 | categorylist.append(line.rstrip()) 289 | 290 | >>> categorylist[546] 291 | 'n03272010 electric guitar' 292 | ``` 293 | 294 | The model has correctly identified there is an electric guitar in the image, pretty impressive. 295 | 296 | What about the second highest category? 297 | 298 | ```python 299 | >>> prob[819] 300 | 0.27168664 301 | >>> categorylist[819] 302 | 'n04296562 stage' 303 | ``` 304 | 305 | Now you know how to use a pre-trained, state of the art model for image classification. All it took was a few lines of code and the rest was just data preparation. You can now try this with the other images (image1.jpeg and your own images) by starting at the data preparation stage again. 306 | 307 | ## Exercise 308 | 309 | Now you know how to load a model and run a test against it, lets try with two other popular models. The following models are all trained against the ImageNet data set so in terms of code it's simply about replacing the model. 310 | 311 | Make a note of the categories that it returns and compare the results 312 | 313 | ### VGG16 314 | 315 | Published in 2014, VGG16 is a model built from 16 layers (research paper). It won the 2014 ImageNet challenge by achieving a 7.4% error rate on object classification. As a bonus you can also download and test VGG19. 316 | 317 | ### ResNet-152 318 | 319 | Published in 2015, ResNet-152 is a model built from 152 layers (research paper). It won the 2015 ImageNet challenge by achieving a record 3.57% error rate on object detection. That’s much better than the typical human error rate which is usually measured at 5%. 320 | 321 | ### Downloading the models 322 | 323 | Time to visit the model zoo once again. Just like for Inception v3, we need to download model definitions and parameters. All three models have been trained on the same categories, so we can reuse our synset.txt file. 324 | 325 | ```bash 326 | $ wget http://data.mxnet.io/models/imagenet/vgg/vgg16-symbol.json 327 | ``` 328 | 329 | __NOTE:__ You'll need to edit this file and swap ```prob_label``` and ```prob``` to ```softmax_label``` and ```softmax``` respectively 330 | 331 | ```bash 332 | $ wget http://data.mxnet.io/models/imagenet/vgg/vgg16-0000.params 333 | 334 | $ wget http://data.mxnet.io/models/imagenet/resnet/152-layers/resnet-152-symbol.json 335 | 336 | $ wget http://data.mxnet.io/models/imagenet/resnet/152-layers/resnet-152-0000.params 337 | ``` -------------------------------------------------------------------------------- /lab-3-Hands_on_with_Apache_MXNet/images/image0-blue.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-3-Hands_on_with_Apache_MXNet/images/image0-blue.jpeg -------------------------------------------------------------------------------- /lab-3-Hands_on_with_Apache_MXNet/images/image0-green.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-3-Hands_on_with_Apache_MXNet/images/image0-green.jpeg -------------------------------------------------------------------------------- /lab-3-Hands_on_with_Apache_MXNet/images/image0-red.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-3-Hands_on_with_Apache_MXNet/images/image0-red.jpeg -------------------------------------------------------------------------------- /lab-3-Hands_on_with_Apache_MXNet/images/image1-blue.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-3-Hands_on_with_Apache_MXNet/images/image1-blue.jpeg -------------------------------------------------------------------------------- /lab-3-Hands_on_with_Apache_MXNet/images/image1-green.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-3-Hands_on_with_Apache_MXNet/images/image1-green.jpeg -------------------------------------------------------------------------------- /lab-3-Hands_on_with_Apache_MXNet/images/image1-red.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-3-Hands_on_with_Apache_MXNet/images/image1-red.jpeg -------------------------------------------------------------------------------- /lab-3-Hands_on_with_Apache_MXNet/images/mxnet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-3-Hands_on_with_Apache_MXNet/images/mxnet.png -------------------------------------------------------------------------------- /lab-3-Hands_on_with_Apache_MXNet/images/sample-image0.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-3-Hands_on_with_Apache_MXNet/images/sample-image0.jpeg -------------------------------------------------------------------------------- /lab-3-Hands_on_with_Apache_MXNet/images/sample-image1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-3-Hands_on_with_Apache_MXNet/images/sample-image1.jpeg -------------------------------------------------------------------------------- /lab-4-Getting_started_with_Sagemaker/README.md: -------------------------------------------------------------------------------- 1 | ![Workshops](../banners/aws.png) ![Workshops](images/sagemaker.png) 2 | **Last Updated:** December 2018 3 | # Getting Started with Sagemaker MXNet and Jupyter 4 | 5 | ## Overview 6 | 7 | Apache MXNet is a lean, flexible, and ultra-scalable deep learning framework that supports state of the art in deep learning models, including convolutional neural networks (CNNs) and long short-term memory networks (LSTMs). The framework has its roots in academia and came about through the collaboration and contributions of researchers at several top universities. It has been designed to excel at computer vision, speech, language processing and understanding, generative models, concurrent neural networks, and recurrent neural networks. 8 | 9 | Amazon SageMaker is a fully-managed service that enables developers and data scientists to quickly and easily build, train, and deploy machine learning models at any scale. Amazon SageMaker removes all the barriers that typically slow down developers who want to use machine learning. 10 | 11 | This lab is designed to demonstrate how to run the Amazon Deep Learning AMI using Amazon SageMaker, and will introduce the audience to: 12 | 13 | - Launching Jupyter Notebook instance using Amazon SageMaker 14 | - Stepping through and modifying Jupyter Notebooks 15 | - Hands-on experience with: 16 | - Multi-Layer Perceptron 17 | - Convolutional Neural Networks 18 | - Long Short Term Memory artificial neural networks. 19 | 20 | # Launching a Jupyter Notebook Server using Amazon SageMaker 21 | 22 | 1. Sign into the AWS Management Console [https://console.aws.amazon.com/](https://console.aws.amazon.com/). 23 | 2. Click on **Amazon SageMaker** from the list of all services. This will bring you to the Amazon SageMaker console homepage. 24 | 25 | ![Sagemaker console](images/Picture01.png) 26 | 27 | 3. To create a new Jupyter notebook instance, go to **Notebook instances** , and click the **Create notebook instance** button at the top of the browser window. 28 | 29 | ![Create notebook instance](images/Picture02.png) 30 | 31 | 4. Type _[First Name]-[Last Name]-Lab-Server_ into the **Notebook instance name** text box, _ml.p2.xlarge_ into **the Notebook instance type** _._ 32 | 5. Under the **IAM Role** dropdown, choose **Create a new role** , and select _None_ under the **S3 Buckets you specify – _optional_**. Click on **Create role** to close the dialog. 33 | 34 | ![Create IAM role](images/Picture03.png) 35 | 36 | 6. This will display a success message for the creation of the new role. Leaving everything else at their default values – _Elastic inference_, _VPC_, _Lifecycle configuration_, _Encryption_ and _Volume Size_ – and click on **Create notebook instance** at the bottom of the screen. This will launch a p2.xlarge instance running the Amazon Deep Learning AMI 37 | 38 | # Launching the Jupyter Notebook Instance 39 | 40 | 1. Make sure that the server status is **InService**. This will take a few minutes once the creation process has started. 41 | 42 | ![Open Jupyter](images/Picture04.png) 43 | 44 | 2. Click **Open Jupyter**. You now have access to the Jupyter Notebok. 45 | 3. We are going to log on to the server by launching a terminal console. Click **New** and select **Terminal.** This will open up the terminal on a new browser tab. 46 | 47 | ![New notebook terminal](images/Picture05.png) 48 | 49 | 4. To download all the lab files and supporting file execute the following commands 50 | 51 | ```bash 52 | cd SageMaker 53 | wget https://s3.amazonaws.com/sa-imd/sa_ml_lab.tar.gz 54 | ``` 55 | 56 | 5. Once the download is complete, extract the files by typing 57 | 58 | ```bash 59 | tar xf sa_ml_lab.tar.gz 60 | ``` 61 | 62 | 6. Close this window, go to the previous browser tab, ensure that you are on the **Files** tab, and go into the **ml_labs** folder and you should see the following 3 note books available for use: 63 | 64 | - Sentiment\_MLP\_MXNet.ipynb 65 | - Sentiment\_CNN\_MXNet.ipynb 66 | - time-series-lstm.ipynb 67 | 68 | ![Files list in ml_labs](images/Picture06.png) 69 | 70 | 7. We will start by looking at the notebook for Sentiment Analysis using Convolutional Neural Networks in the MXNet framework _Sentiment\_CNN\_MXNet.ipynb_. Click on the notebook filename, which will automtically open that notebook in a new window. 71 | 72 | ![Notebook opened](images/Picture08.png) 73 | 74 | 8. Switch back to the previous window - you will see that only the Convolutional Neural Network notebook is open, as indicaed by the green icon. If you select the other two then they will be opened in their own windows. 75 | 76 | ![Show opened notebooks](images/Picture09.png) 77 | 78 | 9. Go to the Convolutional Neural Network tab and change the kernel to _"Environment (conda\_mxnet\_p27)"._ 79 | 80 | ![Choose conda_mxnet_p27](images/Picture10.png) 81 | 82 | # Working Through the Jupyter Notebook Lab 83 | 84 | 1. You can now work through the notebook lab. A notebook consisted of a number of cells; in SageMaker these will typically either be _Code_ or _Markdown_ cell. Markdown is used to allow for documentation to be defined inline with the code, giving the author a rich set of markdown formatting options. The first few cells in this notebook are Markdown, and if you select one then the whole cell is highlighted. 85 | 86 | ![What is CNN](images/Picture11.png) 87 | 88 | 2. After scrolling past the introduction to the lab you will find the first Code cell, which is called **Load Modules**. The previous Markdown cells ought to have described what this next Code cell is going to do – for the sake of this lab you do not have to understand the code that is being run in the Code cell, rather you should just appreciate what the notebook is doing and how you interact with a Jupyter notebook. 89 | 90 | ![Load modules](images/Picture12.png) 91 | 92 | 3. To the left of the Code module is a _Play_ icon, along with a set of empty braces ('[]'). By clicking the _Play_ icon, or selecting the _Run_ command in the menu bar, the Jupyter notebook will execute this code, outputting and code outputs to the notebook screen and keeping any results data internally for re-use in future steps. 93 | 4. Click on the _Play_ icon to execute this Code cell. Whilst the code is executing the braces will change to be **[\*]**, indicating that it is executing, and once complete will change to **[1]**. Future cells will have increasing numbers inside the braces, so the braces after the execution of the second Code cell, _Process Movie Review Data_, shows as **[2]**. 94 | 95 | ![Process movie review data](images/Picture13.png) 96 | 97 | 5. Note, as subsequent cells use the processing results of the previous cells, you should not start to execute a cell until its predecessor has completed. Also, please be aware that re-running previous code cells will almost certainly invalidate all future cells, so you will have to re-run those as well. 98 | 6. You will see at code cell 3, _Data Summarization_, that the code cell can take advantage of any other in-language features – in this case it will use the Python library **pyplot** to output a simple plot showing the distribution of review word lengths. 99 | 100 | ![pyplot image](images/Picture14.png) 101 | 102 | 7. In cell 6, _Build a CNN Network_, you are instructed to edit the code – this shows one of the powerful features of Jupyter, as Data Scientists are able edit, update and otherwise evolve their algorithms and parameters on-the-fly within the notebook. 103 | 8. Continue through to cell 7, _Model Training_ – with an **ml.p3.2xlarge** instance type this will take around 2 minutes, longer if you use a **c5** or **m5** instance type. The training algorithm will run through 10 iterations – or _epochs_ – of the training algorithm. After a few epochs of progress have been output you will be able to estimate the time to completion. Eventually, you will have values for training and validation accuracy. 104 | 105 | ![Validation accuracy](images/Picture15.png) 106 | 107 | 9. We save the trained model, and complete up to Coding cell 11 – we are now ready for some interactive testing. The _Input Screen for Model Testing_ Code cell can be run multiple times – each time it will show a dialog, asking you enter a movie review paragraph and to hit the **Predict Sentiment** button – it will then give you its probability score estimate for this being a review with positive or negative sentiment. Type in your own reviews, or use one of the four example reviews in the previous Markdown cell. 108 | 109 | # Terminating the Notebook Instance 110 | 111 | 1. Open the Amazon SageMaker console [https://console.aws.amazon.com/sagemaker/](https://console.aws.amazon.com/sagemaker/) and click on **Notebook instances** 112 | 2. Find the notebook instance listed as _[First Name]-[Last Name]-Lab-Server_, select its radio button and then click the **Actions** dropdown. 113 | 114 | ![Terminate instance](images/Picture16.png) 115 | 116 | 3. Click **Stop** to stop the Notebook Instance. This does not delete the underlying data and resources, and once the instance status has changed to _Stopped_ you can click on the **Actions** dropdown again, but this time select **Delete**. 117 | 118 | Note that by selecting the name of the Notebook instance on this dialog you are taken to a more detailed information page regarding that instance, which also has **Stop** and **Delete** buttons present – notebooks can also be deleted using this method. -------------------------------------------------------------------------------- /lab-4-Getting_started_with_Sagemaker/images/Picture01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-4-Getting_started_with_Sagemaker/images/Picture01.png -------------------------------------------------------------------------------- /lab-4-Getting_started_with_Sagemaker/images/Picture02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-4-Getting_started_with_Sagemaker/images/Picture02.png -------------------------------------------------------------------------------- /lab-4-Getting_started_with_Sagemaker/images/Picture03.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-4-Getting_started_with_Sagemaker/images/Picture03.png -------------------------------------------------------------------------------- /lab-4-Getting_started_with_Sagemaker/images/Picture04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-4-Getting_started_with_Sagemaker/images/Picture04.png -------------------------------------------------------------------------------- /lab-4-Getting_started_with_Sagemaker/images/Picture05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-4-Getting_started_with_Sagemaker/images/Picture05.png -------------------------------------------------------------------------------- /lab-4-Getting_started_with_Sagemaker/images/Picture06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-4-Getting_started_with_Sagemaker/images/Picture06.png -------------------------------------------------------------------------------- /lab-4-Getting_started_with_Sagemaker/images/Picture08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-4-Getting_started_with_Sagemaker/images/Picture08.png -------------------------------------------------------------------------------- /lab-4-Getting_started_with_Sagemaker/images/Picture09.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-4-Getting_started_with_Sagemaker/images/Picture09.png -------------------------------------------------------------------------------- /lab-4-Getting_started_with_Sagemaker/images/Picture10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-4-Getting_started_with_Sagemaker/images/Picture10.png -------------------------------------------------------------------------------- /lab-4-Getting_started_with_Sagemaker/images/Picture11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-4-Getting_started_with_Sagemaker/images/Picture11.png -------------------------------------------------------------------------------- /lab-4-Getting_started_with_Sagemaker/images/Picture12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-4-Getting_started_with_Sagemaker/images/Picture12.png -------------------------------------------------------------------------------- /lab-4-Getting_started_with_Sagemaker/images/Picture13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-4-Getting_started_with_Sagemaker/images/Picture13.png -------------------------------------------------------------------------------- /lab-4-Getting_started_with_Sagemaker/images/Picture14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-4-Getting_started_with_Sagemaker/images/Picture14.png -------------------------------------------------------------------------------- /lab-4-Getting_started_with_Sagemaker/images/Picture15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-4-Getting_started_with_Sagemaker/images/Picture15.png -------------------------------------------------------------------------------- /lab-4-Getting_started_with_Sagemaker/images/Picture16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-4-Getting_started_with_Sagemaker/images/Picture16.png -------------------------------------------------------------------------------- /lab-4-Getting_started_with_Sagemaker/images/sagemaker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-4-Getting_started_with_Sagemaker/images/sagemaker.png -------------------------------------------------------------------------------- /lab-4-Getting_started_with_Sagemaker/sa_ml_lab.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-4-Getting_started_with_Sagemaker/sa_ml_lab.tar.gz -------------------------------------------------------------------------------- /lab-5-Hands_on_with_Rekognition_API/README.md: -------------------------------------------------------------------------------- 1 | ![Workshops](../banners/aws.png) ![Workshops](images/rekognition.png) 2 | **Last Updated:** March 2019 3 | 4 | # Hands on with the Amazon Rekognition API 5 | 6 | ### Requirements 7 | 8 | - AWS Account with local access credentials 9 | - AWS CLI tools installed 10 | - python + boto3 + some python skills 11 | 12 | ## Why Amazon Rekognition? 13 | 14 | A different AI/ML in this series looked at image recognition using Apache MXNet and we analysed an image of a rock guitarist - see [here](). In order to do that we had to prepare the data, this takes time when dealing with thousands or millions of images. Rekognition does this for you automatically. The screen shot below shows the same image processed by rekognition. 15 | 16 | ![demo0.png](demo0.png) 17 | 18 | As you can see AWS has already done the heavy lifting of data preparation for you. Also it extends much further with object detection. It can detect faces, guess the age of the person, compare faces and even process video streams in the same way. 19 | 20 | ## Using Amazon Rekognition 21 | 22 | To start with let's look at the AWS CLI for Rekognition, we'll want a few sample images, ones you can use easily are from this repository, but feel free to subsitute with your own. 23 | 24 | Create an S3 bucket and upload the sample images - we are using photos of one our Evangelists, Ric Harvey, but you could use any photo sets that you like. 25 | 26 | First of all lets scan a picture to find a face - note, use your bucket name, not literally *YOURBUCKETNAME*: 27 | 28 | ```bash 29 | aws rekognition detect-faces --image "S3Object={Bucket="YOURBUCKETNAME", Name="ric_harvey.jpeg"}" 30 | ``` 31 | The output of this shows there is indeed a face detected, and you can see details on the Landmarks it used to detect the face and its confidence. 32 | 33 | ```json 34 | { 35 | "FaceDetails": [ 36 | { 37 | "BoundingBox": { 38 | "Width": 0.3238965570926666, 39 | "Top": 0.1666368991136551, 40 | "Left": 0.33553096652030945, 41 | "Height": 0.3074423372745514 42 | }, 43 | "Landmarks": [ 44 | { 45 | "Y": 0.2876920998096466, 46 | "X": 0.4322119653224945, 47 | "Type": "eyeLeft" 48 | }, 49 | { 50 | "Y": 0.2862488329410553, 51 | "X": 0.5742876529693604, 52 | "Type": "eyeRight" 53 | }, 54 | { 55 | "Y": 0.3933376669883728, 56 | "X": 0.45016252994537354, 57 | "Type": "mouthLeft" 58 | }, 59 | { 60 | "Y": 0.3922717273235321, 61 | "X": 0.5671025514602661, 62 | "Type": "mouthRight" 63 | }, 64 | { 65 | "Y": 0.3384993076324463, 66 | "X": 0.508313000202179, 67 | "Type": "nose" 68 | } 69 | ], 70 | "Pose": { 71 | "Yaw": 1.473808765411377, 72 | "Roll": -2.1248180866241455, 73 | "Pitch": -3.9496753215789795 74 | }, 75 | "Quality": { 76 | "Sharpness": 89.85481262207031, 77 | "Brightness": 84.628662109375 78 | }, 79 | "Confidence": 100.0 80 | } 81 | ] 82 | } 83 | ``` 84 | However in most cases you don't want to just find a face you want some information about that face, gender and defining features, maybe the sentiment. We can do this by calling __detect-labels__. 85 | 86 | ```bash 87 | aws rekognition detect-labels --image "S3Object={Bucket="YOURBUCKETNAME", Name="ric_harvey.jpeg"}" 88 | ``` 89 | 90 | The resulting output determines that Ric is, indeed, human - we've removed some of the output JSON for the sake of clarity: 91 | 92 | ```json 93 | { 94 | "LabelModelVersion": "2.0", 95 | "Labels": [ 96 | { 97 | "Confidence": 97.83240509033203, 98 | "Instances": [], 99 | "Name": "Human", 100 | "Parents": [] 101 | }, 102 | { 103 | "Confidence": 97.83240509033203, 104 | "Instances": [ 105 | { 106 | "BoundingBox": { }, 107 | "Confidence": 97.83240509033203 108 | } 109 | ], 110 | "Name": "Person", 111 | "Parents": [] 112 | }, 113 | { 114 | "Confidence": 96.00078582763672, 115 | "Instances": [], 116 | "Name": "Clothing", 117 | "Parents": [] 118 | }, 119 | { 120 | "Confidence": 96.00078582763672, 121 | "Instances": [], 122 | "Name": "Apparel", 123 | "Parents": [] 124 | }, 125 | { 126 | "Confidence": 96.00078582763672, 127 | "Instances": [ 128 | { 129 | "BoundingBox": { }, 130 | "Confidence": 96.00078582763672 131 | } 132 | ], 133 | "Name": "Sweater", 134 | "Parents": [ 135 | { 136 | "Name": "Clothing" 137 | } 138 | ] 139 | }, 140 | { 141 | "Confidence": 95.15144348144531, 142 | "Instances": [], 143 | "Name": "Man", 144 | "Parents": [ 145 | { 146 | "Name": "Person" 147 | } 148 | ] 149 | }, 150 | { 151 | "Confidence": 67.18626403808594, 152 | "Instances": [], 153 | "Name": "Sleeve", 154 | "Parents": [ 155 | { 156 | "Name": "Clothing" 157 | } 158 | ] 159 | } 160 | ] 161 | } 162 | ``` 163 | 164 | In a recent upgrade to the Rekognition model AWS introduced the idea of *parent* objects - in this example for Ric, the label *Man* links back to the object for *Person*, and *Sleeve* links back to *Clothing*. 165 | 166 | So if we can find faces in images and identify key objects about that face we should be able to compare faces and find the same person in multiple photos. Heres an example using __compare-faces__. 167 | 168 | ```bash 169 | aws rekognition compare-faces --source-image '{"S3Object":{"Bucket":"YOURBUCKETNAME","Name":"ric_harvey.jpeg"}}' --target-image '{"S3Object":{"Bucket":"YOURBUCKETNAME","Name":"ric.jpg"}}' 170 | ``` 171 | 172 | In the first example we have a match - we are 100% confident that we have found a face in both images, and then 99.27% confident that they match. We're pretty confident that this is Ric. 173 | 174 | ```json 175 | { 176 | "UnmatchedFaces": [], 177 | "FaceMatches": [ 178 | { 179 | "Face": { 180 | "BoundingBox": { 181 | "Width": 0.41572150588035583, 182 | "Top": 0.12303049862384796, 183 | "Left": 0.316993772983551, 184 | "Height": 0.6135206818580627 185 | }, 186 | "Confidence": 100.0, 187 | "Pose": { 188 | "Yaw": 0.6714985966682434, 189 | "Roll": -0.0029848809354007244, 190 | "Pitch": 0.6671947240829468 191 | }, 192 | "Quality": { 193 | "Sharpness": 78.64350128173828, 194 | "Brightness": 50.17827606201172 195 | }, 196 | "Landmarks": [ 197 | { 198 | "Y": 0.33426931500434875, 199 | "X": 0.42318251729011536, 200 | "Type": "eyeLeft" 201 | }, 202 | { 203 | "Y": 0.33917325735092163, 204 | "X": 0.6163105368614197, 205 | "Type": "eyeRight" 206 | }, 207 | { 208 | "Y": 0.5411261916160583, 209 | "X": 0.4390724301338196, 210 | "Type": "mouthLeft" 211 | }, 212 | { 213 | "Y": 0.5456682443618774, 214 | "X": 0.5989208221435547, 215 | "Type": "mouthRight" 216 | }, 217 | { 218 | "Y": 0.44001856446266174, 219 | "X": 0.5190205574035645, 220 | "Type": "nose" 221 | } 222 | ] 223 | }, 224 | "Similarity": 99.27263641357422 225 | } 226 | ], 227 | "SourceImageFace": { 228 | "BoundingBox": { 229 | "Width": 0.3238965570926666, 230 | "Top": 0.1666368991136551, 231 | "Left": 0.33553096652030945, 232 | "Height": 0.3074423372745514 233 | }, 234 | "Confidence": 100.0 235 | } 236 | } 237 | ``` 238 | 239 | However lets look at a picture with more than one person in it. 240 | 241 | ```bash 242 | aws rekognition compare-faces --source-image '{"S3Object":{"Bucket":"YOURBUCKETNAME","Name":"ric_harvey.jpeg"}}' --target-image '{"S3Object":{"Bucket":"YOURBUCKETNAME","Name":"ric_crowd1.jpg"}}' 243 | ``` 244 | 245 | The output from this shows 1 matched face in the *FaceMatches* section, and several unmatched faces in the *UnmatchedFaces* section - however, you do get useful metadata about every face that have been picked out. Rekognition gives 98.74% similarity rating for the matched face, and that's despite the image being slightly blurry and with poor lighting. 246 | 247 | ```json 248 | { 249 | "UnmatchedFaces": [ 250 | { 251 | "BoundingBox": { 252 | "Width": 0.23272210359573364, 253 | "Top": 0.510046124458313, 254 | "Left": 0.7415141463279724, 255 | "Height": 0.4028888940811157 256 | }, 257 | "Confidence": 99.99987030029297, 258 | "Pose": { 259 | "Yaw": -23.619752883911133, 260 | "Roll": -5.818271636962891, 261 | "Pitch": 9.376426696777344 262 | }, 263 | "Quality": { 264 | "Sharpness": 53.330047607421875, 265 | "Brightness": 29.453981399536133 266 | }, 267 | "Landmarks": [ 268 | { 269 | "Y": 0.6929454803466797, 270 | "X": 0.8373074531555176, 271 | "Type": "eyeLeft" 272 | }, 273 | { 274 | "Y": 0.6977059245109558, 275 | "X": 0.945943295955658, 276 | "Type": "eyeRight" 277 | }, 278 | { 279 | "Y": 0.8433666229248047, 280 | "X": 0.8459407687187195, 281 | "Type": "mouthLeft" 282 | }, 283 | { 284 | "Y": 0.8482340574264526, 285 | "X": 0.93607497215271, 286 | "Type": "mouthRight" 287 | }, 288 | { 289 | "Y": 0.7731311917304993, 290 | "X": 0.8804974555969238, 291 | "Type": "nose" 292 | } 293 | ] 294 | }, 295 | { 296 | "BoundingBox": { 297 | "Width": 0.12664306163787842, 298 | "Top": 0.4119572043418884, 299 | "Left": 0.48019713163375854, 300 | "Height": 0.24227945506572723 301 | }, 302 | "Confidence": 99.99998474121094, 303 | "Pose": { 304 | "Yaw": 4.041987419128418, 305 | "Roll": 3.3684937953948975, 306 | "Pitch": -20.516826629638672 307 | }, 308 | "Quality": { 309 | "Sharpness": 38.89601135253906, 310 | "Brightness": 26.469314575195312 311 | }, 312 | "Landmarks": [ 313 | { 314 | "Y": 0.5068619847297668, 315 | "X": 0.5267886519432068, 316 | "Type": "eyeLeft" 317 | }, 318 | { 319 | "Y": 0.5116945505142212, 320 | "X": 0.5861381888389587, 321 | "Type": "eyeRight" 322 | }, 323 | { 324 | "Y": 0.5888504385948181, 325 | "X": 0.5275153517723083, 326 | "Type": "mouthLeft" 327 | }, 328 | { 329 | "Y": 0.5928797125816345, 330 | "X": 0.5768627524375916, 331 | "Type": "mouthRight" 332 | }, 333 | { 334 | "Y": 0.5561686754226685, 335 | "X": 0.5575464963912964, 336 | "Type": "nose" 337 | } 338 | ] 339 | }, 340 | { 341 | "BoundingBox": { 342 | "Width": 0.31463298201560974, 343 | "Top": 0.36742377281188965, 344 | "Left": -0.010811327025294304, 345 | "Height": 0.5718353986740112 346 | }, 347 | "Confidence": 100.0, 348 | "Pose": { 349 | "Yaw": 25.575437545776367, 350 | "Roll": 7.750082969665527, 351 | "Pitch": -0.5794483423233032 352 | }, 353 | "Quality": { 354 | "Sharpness": 73.32209777832031, 355 | "Brightness": 26.861705780029297 356 | }, 357 | "Landmarks": [ 358 | { 359 | "Y": 0.6168197393417358, 360 | "X": 0.09621831029653549, 361 | "Type": "eyeLeft" 362 | }, 363 | { 364 | "Y": 0.636400043964386, 365 | "X": 0.24112087488174438, 366 | "Type": "eyeRight" 367 | }, 368 | { 369 | "Y": 0.8429403305053711, 370 | "X": 0.09124591201543808, 371 | "Type": "mouthLeft" 372 | }, 373 | { 374 | "Y": 0.8571742177009583, 375 | "X": 0.21000227332115173, 376 | "Type": "mouthRight" 377 | }, 378 | { 379 | "Y": 0.7462476491928101, 380 | "X": 0.19551265239715576, 381 | "Type": "nose" 382 | } 383 | ] 384 | }, 385 | { 386 | "BoundingBox": { 387 | "Width": 0.1486806869506836, 388 | "Top": 0.31899121403694153, 389 | "Left": 0.6218242049217224, 390 | "Height": 0.2892136871814728 391 | }, 392 | "Confidence": 99.99995422363281, 393 | "Pose": { 394 | "Yaw": -9.001951217651367, 395 | "Roll": -15.084820747375488, 396 | "Pitch": -0.08522148430347443 397 | }, 398 | "Quality": { 399 | "Sharpness": 46.02980041503906, 400 | "Brightness": 36.90113067626953 401 | }, 402 | "Landmarks": [ 403 | { 404 | "Y": 0.45112869143486023, 405 | "X": 0.657817542552948, 406 | "Type": "eyeLeft" 407 | }, 408 | { 409 | "Y": 0.4396614730358124, 410 | "X": 0.7241609692573547, 411 | "Type": "eyeRight" 412 | }, 413 | { 414 | "Y": 0.5389857888221741, 415 | "X": 0.675711452960968, 416 | "Type": "mouthLeft" 417 | }, 418 | { 419 | "Y": 0.5303119421005249, 420 | "X": 0.7309232950210571, 421 | "Type": "mouthRight" 422 | }, 423 | { 424 | "Y": 0.49695348739624023, 425 | "X": 0.6839836239814758, 426 | "Type": "nose" 427 | } 428 | ] 429 | } 430 | ], 431 | "FaceMatches": [ 432 | { 433 | "Face": { 434 | "BoundingBox": { 435 | "Width": 0.1646803468465805, 436 | "Top": 0.38141894340515137, 437 | "Left": 0.2834920287132263, 438 | "Height": 0.31770819425582886 439 | }, 440 | "Confidence": 99.99987030029297, 441 | "Pose": { 442 | "Yaw": 30.25555992126465, 443 | "Roll": 25.35053253173828, 444 | "Pitch": 1.6962026357650757 445 | }, 446 | "Quality": { 447 | "Sharpness": 60.49041748046875, 448 | "Brightness": 25.469776153564453 449 | }, 450 | "Landmarks": [ 451 | { 452 | "Y": 0.4987576901912689, 453 | "X": 0.3558201193809509, 454 | "Type": "eyeLeft" 455 | }, 456 | { 457 | "Y": 0.5361648201942444, 458 | "X": 0.4192109704017639, 459 | "Type": "eyeRight" 460 | }, 461 | { 462 | "Y": 0.6199691295623779, 463 | "X": 0.3274959921836853, 464 | "Type": "mouthLeft" 465 | }, 466 | { 467 | "Y": 0.6497430801391602, 468 | "X": 0.3787180185317993, 469 | "Type": "mouthRight" 470 | }, 471 | { 472 | "Y": 0.5817705392837524, 473 | "X": 0.38942280411720276, 474 | "Type": "nose" 475 | } 476 | ] 477 | }, 478 | "Similarity": 98.74687957763672 479 | } 480 | ], 481 | "SourceImageFace": { 482 | "BoundingBox": { 483 | "Width": 0.3238965570926666, 484 | "Top": 0.1666368991136551, 485 | "Left": 0.33553096652030945, 486 | "Height": 0.3074423372745514 487 | }, 488 | "Confidence": 100.0 489 | } 490 | } 491 | ``` 492 | 493 | Repeating this with __ric_crowd0.jpg__ will show no results - the *FaceMatches* section is completely empty. 494 | 495 | ## Doing this from python 496 | 497 | Using the CLI is fine but if you want to embed this into you system you'll need to make these calls from code. We'll use Python to do this and we'll need boto3 installed for accessing the AWS API: 498 | 499 | ```bash 500 | pip install boto3 501 | ``` 502 | 503 | __Note:__ OSX may need to run ```sudo -H pip install boto3``` 504 | 505 | ### Sample code 506 | 507 | Lets look at some same code that allows you to detect faces and the labels for each face. Try running this on a few of the sample images. 508 | 509 | ```python 510 | #!/usr/bin/env python 511 | 512 | import sys 513 | import boto3 514 | 515 | defaultRegion = 'eu-west-1' 516 | defaultUrl = 'https://rekognition.eu-west-1.amazonaws.com' 517 | 518 | def connectToRekognitionService(regionName=defaultRegion, endpointUrl=defaultUrl): 519 | return boto3.client('rekognition', region_name=regionName, endpoint_url=endpointUrl) 520 | 521 | def detectFaces(rekognition, imageBucket, imageFilename, attributes='ALL'): 522 | resp = rekognition.detect_faces( 523 | Image = {"S3Object" : {'Bucket' : imageBucket, 'Name' : imageFilename}}, 524 | Attributes=[attributes]) 525 | return resp['FaceDetails'] 526 | 527 | def detectLabels(rekognition, imageBucket, imageFilename, maxLabels=100, minConfidence=0): 528 | resp = rekognition.detect_labels( 529 | Image = {"S3Object" : {'Bucket' : imageBucket, 'Name' : imageFilename}}, 530 | MaxLabels = maxLabels, MinConfidence = minConfidence) 531 | return resp['Labels'] 532 | 533 | def printFaceInformation(face, faceCounter): 534 | print('*** Face ' + str(faceCounter) + ' detected, confidence: ')+str(face['Confidence']) 535 | print('Gender: ')+face['Gender']['Value'] 536 | # You need boto3>=1.4.4 for AgeRange 537 | print('Age: ')+str(face['AgeRange']['Low'])+"-"+str(face['AgeRange']['High']) 538 | if (face['Beard']['Value']): 539 | print ('Beard') 540 | if (face['Mustache']['Value']): 541 | print ('Mustache') 542 | if (face['Eyeglasses']['Value']): 543 | print ('Eyeglasses') 544 | if (face['Sunglasses']['Value']): 545 | print ('Sunglasses') 546 | for e in face['Emotions']: 547 | print e['Type']+' '+str(e['Confidence']) 548 | 549 | def printLabelsInformation(labels): 550 | for l in labels: 551 | print('Label ' + l['Name'] + ', confidence: ' + str(l['Confidence'])) 552 | 553 | def usage(): 554 | print('\nrekognitionDetect \n') 555 | print('S3BucketName : the S3 bucket where Rekognition will find the image') 556 | print('image : the image to process') 557 | print('Output : labels & face information (stdout)\n') 558 | 559 | if (len(sys.argv) != 3): 560 | usage() 561 | sys.exit() 562 | 563 | imageBucket = str(sys.argv[1]) 564 | image = str(sys.argv[2]) 565 | 566 | reko = connectToRekognitionService() 567 | 568 | labels = detectLabels(reko, imageBucket, image, maxLabels=10, minConfidence=70.0) 569 | printLabelsInformation(labels) 570 | 571 | faceList = detectFaces(reko, imageBucket, image) 572 | faceCounter = 0 573 | for face in faceList: 574 | printFaceInformation(face, faceCounter) 575 | faceCounter=faceCounter+1 576 | 577 | labelText = '' 578 | for l in labels: 579 | if (l['Confidence'] > 80.0): 580 | labelText = labelText + l['Name'] + ", " 581 | ``` 582 | 583 | Copy and paste into a python file, such as **rekLab.py**. You can now run this against any image that you have uploaded into your bucket, as follows: 584 | 585 | ```bash 586 | $ python rekLab.py YOURBUCKETNAME ric_harvey.jpeg 587 | ``` 588 | 589 | This code will call **detect-labels** and **detect-faces** and try and provide some intelligible output. For instance, if you run this against **ric_harvey.jpeg** then you should the following output: 590 | 591 | ``` 592 | Label Human, confidence: 97.8324050903 593 | Label Person, confidence: 97.8324050903 594 | Label Sweater, confidence: 96.0007858276 595 | Label Clothing, confidence: 96.0007858276 596 | Label Apparel, confidence: 96.0007858276 597 | Label Man, confidence: 95.151473999 598 | *** Face 0 detected, confidence: 100.0 599 | Gender: Male 600 | Age: 35-52 601 | Beard 602 | CALM 93.4333953857 603 | SURPRISED 0.905286967754 604 | SAD 2.43520069122 605 | CONFUSED 1.0990087986 606 | HAPPY 0.299568772316 607 | DISGUSTED 0.428233355284 608 | ANGRY 1.39930927753 609 | ``` 610 | 611 | ## Challenge - Where's Ric? 612 | 613 | Durring registration at a recent AWS event we took some photos and uploaded them to a public S3 bucket **s3://image-demo-lab** there is also an array of asorted images in the bucket. Your challenge is to: 614 | 615 | - find how many pictures in the bucket contain a photo of Ric 616 | 617 | You'll need to create a compare-faces function and also get a list of all the objects in the S3 bucket (warning they may not all be images!) Extra points for the fastest way of doing this. 618 | 619 | ## Resources 620 | 621 | [http://boto3.readthedocs.io/en/latest/reference/services/rekognition.html](http://boto3.readthedocs.io/en/latest/reference/services/rekognition.html) 622 | 623 | Let an instructor know when you've completed this. 624 | 625 | -------------------------------------------------------------------------------- /lab-5-Hands_on_with_Rekognition_API/compare.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | images=`aws s3 ls s3://image-demo-lab | grep jp | cut -c32-` 4 | c=0 5 | 6 | for i in $images 7 | do 8 | echo -n Comparing $i : matches found =" " 9 | aws rekognition compare-faces --source-image "{\"S3Object\":{\"Bucket\":\"image-demo-lab\",\"Name\":\"ric_harvey.jpeg\"}}" --target-image "{\"S3Object\":{\"Bucket\":\"image-demo-lab\",\"Name\":\"$i\"}}" --output json > o.txt 10 | matches=$(cat o.txt | jq '.FaceMatches | length') 11 | echo $matches 12 | c=$(($c + $matches)) 13 | done 14 | echo Total images matched:" "$c 15 | -------------------------------------------------------------------------------- /lab-5-Hands_on_with_Rekognition_API/demo0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-5-Hands_on_with_Rekognition_API/demo0.png -------------------------------------------------------------------------------- /lab-5-Hands_on_with_Rekognition_API/images/rekognition.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-5-Hands_on_with_Rekognition_API/images/rekognition.png -------------------------------------------------------------------------------- /lab-5-Hands_on_with_Rekognition_API/rekognitionDetect.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import sys 4 | import boto3 5 | import pprint 6 | 7 | pp = pprint.PrettyPrinter(indent=4) 8 | 9 | defaultRegion = 'eu-west-1' 10 | defaultUrl = 'https://rekognition.eu-west-1.amazonaws.com' 11 | 12 | def connectToRekognitionService(regionName=defaultRegion, endpointUrl=defaultUrl): 13 | return boto3.client('rekognition', region_name=regionName, endpoint_url=endpointUrl) 14 | 15 | def detectFaces(rekognition, imageBucket, imageFilename, attributes='ALL'): 16 | resp = rekognition.detect_faces( 17 | Image = {"S3Object" : {'Bucket' : imageBucket, 'Name' : imageFilename}}, 18 | Attributes=[attributes]) 19 | return resp['FaceDetails'] 20 | 21 | def compareFaces(rekognition, imageBucket, imageSourceFilename, imageTargetFilename): 22 | resp = rekognition.compare_faces( 23 | SourceImage = {"S3Object" : {'Bucket' : imageBucket, 'Name' : imageSourceFilename}}, 24 | TargetImage = {"S3Object" : {'Bucket' : imageBucket, 'Name' : imageTargetFilename}}) 25 | return resp['FaceMatches'] 26 | 27 | def detectLabels(rekognition, imageBucket, imageFilename, maxLabels=100, minConfidence=0): 28 | resp = rekognition.detect_labels( 29 | Image = {"S3Object" : {'Bucket' : imageBucket, 'Name' : imageFilename}}, 30 | MaxLabels = maxLabels, MinConfidence = minConfidence) 31 | return resp['Labels'] 32 | 33 | def printFaceInformation(face, faceCounter): 34 | print('*** Face ' + str(faceCounter) + ' detected, confidence: ')+str(face['Confidence']) 35 | print('Gender: ')+face['Gender']['Value'] 36 | # You need boto3>=1.4.4 for AgeRange 37 | print('Age: ')+str(face['AgeRange']['Low'])+"-"+str(face['AgeRange']['High']) 38 | pp.pprint(face) 39 | # if (face['Beard']['Value']): 40 | # print ('Beard') 41 | # if (face['Mustache']['Value']): 42 | # print ('Mustache') 43 | # if (face['Eyeglasses']['Value']): 44 | # print ('Eyeglasses') 45 | # if (face['Sunglasses']['Value']): 46 | # print ('Sunglasses') 47 | for e in face['Emotions']: 48 | print e['Type']+' '+str(e['Confidence']) 49 | 50 | def printLabelsInformation(labels): 51 | for l in labels: 52 | print('Label ' + l['Name'] + ', confidence: ' + str(l['Confidence'])) 53 | 54 | def usage(): 55 | print('\nrekognitionDetect \n') 56 | print('S3BucketName : the S3 bucket where Rekognition will find the image') 57 | print('image : the image to process') 58 | print('Output : labels & face information (stdout)\n') 59 | 60 | if (len(sys.argv) != 3): 61 | usage() 62 | sys.exit() 63 | 64 | imageBucket = str(sys.argv[1]) 65 | image = str(sys.argv[2]) 66 | 67 | reko = connectToRekognitionService() 68 | 69 | labels = detectLabels(reko, imageBucket, image, maxLabels=10, minConfidence=70.0) 70 | printLabelsInformation(labels) 71 | 72 | faceList = detectFaces(reko, imageBucket, image) 73 | faceCounter = 0 74 | for face in faceList: 75 | printFaceInformation(face, faceCounter) 76 | faceCounter=faceCounter+1 77 | 78 | labelText = '' 79 | for l in labels: 80 | if (l['Confidence'] > 80.0): 81 | labelText = labelText + l['Name'] + ", " 82 | 83 | -------------------------------------------------------------------------------- /lab-5-Hands_on_with_Rekognition_API/ric.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-5-Hands_on_with_Rekognition_API/ric.jpg -------------------------------------------------------------------------------- /lab-5-Hands_on_with_Rekognition_API/ric_crowd0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-5-Hands_on_with_Rekognition_API/ric_crowd0.jpg -------------------------------------------------------------------------------- /lab-5-Hands_on_with_Rekognition_API/ric_crowd1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-5-Hands_on_with_Rekognition_API/ric_crowd1.jpg -------------------------------------------------------------------------------- /lab-5-Hands_on_with_Rekognition_API/ric_crowd2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-5-Hands_on_with_Rekognition_API/ric_crowd2.jpg -------------------------------------------------------------------------------- /lab-5-Hands_on_with_Rekognition_API/ric_harvey.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-5-Hands_on_with_Rekognition_API/ric_harvey.jpeg -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/README.md: -------------------------------------------------------------------------------- 1 | ![Workshops](../banners/aws.png) ![Workshops](images/personalize.png) 2 | **Last Updated:** September 2019 3 | 4 | # Personalize Your Recommendations 5 | 6 | ## Overview 7 | 8 | Amazon Personalize is a machine learning service that makes it easy for developers to create individualized recommendations for customers using their applications. 9 | 10 | Machine learning is being increasingly used to improve customer engagement by powering personalized product and content recommendations, tailored search results, and targeted marketing promotions. However, developing the machine-learning capabilities necessary to produce these sophisticated recommendation systems has been beyond the reach of most organizations today due to the complexity of developing machine learning functionality. Amazon Personalize allows developers with no prior machine learning experience to easily build sophisticated personalization capabilities into their applications, using machine learning technology perfected from years of use on Amazon.com. 11 | 12 | This lab will walk you through the following: 13 | 14 | - Deploy and configure a *Video Recommendation* application 15 | - Setting up a Jupyter Notebook environment for the Amazon Personalize Service Preview 16 | - Downloading and preparing training data, based on the Movie Lens 100k data set 17 | - Importing prepared data into Amazon Personalize 18 | - Building an ML model based upon the Hierarchical Recurrent Neural Network algorithm (HRNN) 19 | - Testing your model by deploying an Amazon Personalize campaign 20 | - Adding your campaign to Video Recommendation application 21 | 22 | # Deploy the Video Recommendation App 23 | 24 | ## Deploy the "Video Recommendation" Application 25 | 26 | 1. Whilst this application could be deployed anywhere, it uses both an EC2 Amazon Machine Image (AMI) and RDS Snapshot that have been stored in the North Virgina Region of AWS (us-east-1). Hence, please make sure that the Region selected in the AWS Console is alway **US East (N.Virginia)**, as shown in the following diagram. The workshop will only function correctly if the EC2 configuration, CloudFormation template executiion and SageMaker notebook are all using this AWS Region. 27 | 28 | ![EC2 Select](images/changeRegion.png) 29 | 30 | 2. The appication will run on an EC2 instance, but at some point we will need to connect to the server in order to carry out some configuration task. To do this we need to have an *EC2 Key Pair* configured on the server that you also have access to on your computer; hence, we need to create and download a new one. Click on **EC2** from the list of all services by entering EC2 into the Find services box. This will bring you to the Amazon EC2 console home page. 31 | 32 | ![EC2 Select](images/consoleEC2Select.png) 33 | 34 | 3. On the left-hand menu scroll down until you see **Key Pairs** and select it, and in the resulting dialog click on the **Create Key Pair** button. This will bring up a **Create Key Pair** dialog, where you need to enter the name of a new key pair - call it *myLabKey* and hit **Create**. This should automatically download the file, or you may need to manually do so. 35 | 36 | ![Create key pair](images/createKeyPair.png) 37 | 38 | 4. **Optional** - should you wish to later SSH in to your instance, you need to have your downloaded key-pair from earlier in an accessible location. It also must not be publicly readable, so if you are on a Mac or Linux system you can fix this with the following command run from the folder where you stored the key, remembering to replace **myLabKey.pem** with your key name! 39 | 40 | ```bash 41 | $ chmod 400 myLabKey.pem 42 | ``` 43 | 44 | 5. Click on the **Services** dropdown and select **CloudFormation** from the list of all services by entering CloudFormation into the Find services box. This will bring you to the Amazon CloudFormation console home page. 45 | 46 | ![CFN Service Selection](images/consoleCfnSelect.png) 47 | 48 | 6. We are going to deploy a pre-built application via a CloudFormation template - this will be a fully-functioning recommendation system, allowing access to multiple Amazon Personalize features. But it has one drawback - there are no models built into it! So we will create them in this lab, and when they are ready we will re-configure this application to use them. But first we need to deploy this skeleton application by downloading this file from the workshop repository. Right-click on the following link and download the template to a file on your local computer, remembering to keep it as a text file with a **.yml** extention. 49 | 50 | https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/master/lab-6-Personalize_your_Recommendations/cloudformation_template.yml 51 | 52 | 7. Click on the **Create Stack** button to start the deployment wizard, and in the **Specify template** section select **Upload a template file**, click on the **Choose file** button, and select the template file that you just downloaded. Then click on **Next**. 53 | 54 | ![Select CFN Template](images/cfnSelectTemplate.png) 55 | 56 | 8. The next screen asks for more configuration parameters, but only a few of these are required. Enter you values for the following, then click **Next** when you're done - you are free to enter new values for the *DjangoAdminLogin* or *DjangoEmailAddress*, but the defaults for these and other parameters are fine. 57 | 58 | 1. **Stack name** - enter something simple, as as *LabStack* 59 | 2. **Parameter - DjangoAdminPassword** - enter a password of your choice 60 | 3. **Parameter - KeyName** - select your previously-defined EC2 kay-pair 61 | 62 | 9. There then follows two more screens. The first is called *Options*, but we have none to enter so just click on **Next**. The second is the final *Review* screen - verify the parameters, hit the checkbox next to the statement *"I acknowledge that AWS CloudFormation might create IAM resources with custom names."* and then click on **Create stack**. This will then go and create the environment, which will take around 10-15 minutes minutes. Unfortunately, we are creating IAM resources, so we cannot continue until it has completed - so read ahead and get a feel for what's coming up next. 63 | 64 | # Setup your Jupyter Notebook environment 65 | 66 | ## Launching a Jupyter Notebook using Amazon SageMaker 67 | 68 | 1. Click on **Amazon SageMaker** from the list of all services by entering *Sagemaker* into the **Find services** box. This will bring you to the Amazon SageMaker console homepage. In another browser tab or window, navigate to the **IAM** console homepage, as we'll need that shortly. 69 | 70 | ![Sagemaker console](images/consoleSMSelect.png) 71 | 72 | 2. To create a new Jupyter notebook instance, go to **Notebook instances** in the Amazon SageMaker console, and click the **Create notebook instance** button at the top of the browser window. 73 | 74 | ![Create notebook instance](images/Picture02.png)Type _[Name]-lab-notebook_ into the **Notebook instance name** text box, and then _ml.t2.medium_ into the **Notebook instance type** field. Note, for this lab the majority of the work is performed by the Amazon Personalize service and not by your notebook instance, so there is no need to launch a large, compute-optimized C5 or GPU-based instance type - please just use the instance type specified here, as that's all that you need, and using these more powerful, more expensive instance families will not actually help you to complete the workshop any faster. 75 | 76 | 3. In the _IAM role_ field in **Permissions and encryption** section choose _Enter a custom IAM role ARN_. Now go over into your open **IAM** tab or window, click in the *Search IAM* field, and then enter **AmazonLab** as the search term. Select the full role name for SageMaker, being careful not to select the *Delete* or *Edit* options. 77 | 78 | ![Open Notebook](images/findSagemakerRole.png) 79 | 80 | 4. On the resulting detail screen copy the **Role ARN** value - this role will give your SageMaker notebook sufficient IAM priviledges, and full access to both the Personalize and S3 services. Note, for a Production system you would most likely restrict this role to specific S3 buckets as per your internal requirements, but this workshop allows access to any S3 bucket. You can now close this tab or window 81 | 82 | 5. Go back to your SageMaker screen, and in the *Custom IAM role ARN* field paste in the IAM ARN that you copied in the previous step, scroll down and click on **Create Notebook Instance** 83 | 84 | 6. Wait until the notebook instance status is **InService**. This will take a few minutes once the creation process has started. Then click on **Open Jupyter** - whilst you're waiting you can perform step #1 of the next section to copy some files from Git 85 | 86 | ![Open Notebook](images/openNotebook.png) 87 | 88 | ### Downloading required additional files 89 | 90 | We need to download two files before starting work, which are all stored within the Lab's Git repository: 91 | 92 | - the Notebook file that contains the lab - **personalize_sample_notebook.ipynb** 93 | - a file that is part of the MovieLens dataset - **u.item** - that has been edited slightly to remove some control characters that cause on of the _pandas_ library calls to fail, and also to include working URLs for the various movie posters that our application will show later 94 | 95 | 1. Go to the Git repository address, https://github.com/drandrewkane/AI_ML_Workshops, navigate to the **Lab 6** and download the files called **u.item** and **personalize_sample_notebook.ipynb** respectively. Use any method that you are comfortable with, but do not clone the whole repository as it is quite large - for instance, try opening the files within Git in RAW format and saving them locally (be careful to maintain the correct file extentions) 96 | 2. In the notebook, assuming the status is now **InService**, and click on the **Upload** button, and in the dialog select the two files from the location that you stored them and upload them. 97 | 98 | ![Upload files](images/uploadFiles.png) 99 | 100 | 3. Click on each of the two **Upload** buttons to actually upload the files, waiting for the first to complete before starting the second. 101 | 102 | ![Upload files part-2](images/uploadFiles2.png) 103 | 104 | 4. Once both are upload you can click on the notebook **.ipynb** file and the lab notebook will open, and you can now begin to work through the lab notebook. 105 | 106 | ### Working Through a Jupyter Notebook 107 | 108 | 1. A notebook consisted of a number of cells; in SageMaker these will typically either be _Code_ or _Markdown_ cells. Markdown is used to allow for documentation to be defined inline with the code, giving the author a rich set of markdown formatting options. The first cell in this notebook, which is called **Get the Personalize boto3 Client**, is Markdown, and if you select any cell then the whole cell is highlighted. 109 | 110 | ![Example cell types](images/cellTypes.png) 111 | 112 | 2. The first Markdown cell describes what the following Code cell is going to do – for the sake of this lab you do not have to understand the code that is being run in the Code cell, rather you should just appreciate what the notebook is doing and how you interact with a Jupyter notebook. 113 | 114 | ![First code cell](images/loadBoto3Pre.png) 115 | 116 | 3. To the left of a Code module is a set of empty braces **[ ]**. By highlighting the cell and then selecting the _Run_ command in the menu bar, the Jupyter notebook will execute this code, outputting and code outputs to the notebook screen and keeping any results data internally for re-use in future steps. Do this now to execute the first code cell. 117 | 118 | *Note: if a Markdown cell is highlighted, then clicking **Run** will move the highlight to the next cell* 119 | 120 | 3. Whilst the code is executing the braces will change to be **[\*]**, indicating that it is executing, and once complete will change to **[1]**. Future cells will have increasing numbers inside the braces, and this helps you see the order in which cells have been exected within the notebook. Directly below the code, but still within the Code cell, is the output from the code execution - this will include any error messages that your code has thrown. In this example, the code execurion successfully created the specified bucket in S3. 121 | 122 | ![First execution](images/loadBoto3Post.png) 123 | 124 | 5. Now please continue to work through the notebook lab - read the comments prior to each Code cell in order to get an understanding as to what is going on, as these explain why we are doing each step and how it ties in to using the Amazon Personalize service. 125 | 126 | # Creating Parallel Solutions 127 | 128 | ## Create Item-to-Item Similarities Solution 129 | 130 | 1. Using the same methods as before, go to the Services drop-down in the console and navigate to the **Amazon Personalize** service in another tab, and select **Dataset groups**. You will see the dataset group that you created earlier, and click on the name of your dataset group. 131 | 132 | ![Dataset groups](images/datasetGroups.png) 133 | 134 | 2. The left-hand side, which will show you the solution that you're currently creating via your notebook. Then, select **Solutions and recipes**, then click on the **Create solution** button. 135 | 136 | ![Solution list](images/solutionList.png) 137 | 138 | 3. Enter a suitable name for this solution, such as *similar-items-solutions*, select **Manual** recipe selection, then choose the **aws-sims** recipe and click **Next** - we don't need to change anything in the advanced configuration section 139 | 140 | ![Create solution](images/solutionConfig.png) 141 | 142 | 4. In the following screen just hit the **Finish** button and a new solution version will start to be created. 143 | 144 | 145 | 146 | ## Create Personal Ranking Solution 147 | 148 | 1. Let's do exactly the same thing again, but this time we'll create a ranking solition. From the **Solutions and Recipes** screen that you are on, click **Create solution**, give it a name like *rankings-solution*, ensure it's a **Manual** recipe selection but this time select the **aws-personalized-ranking** recipe. Click **Next** and **Finished** as before 149 | 150 | ![](images/recipeRanking.png) 151 | 152 | 2. You now have three solutions being built off of the same dataset, and all three will slot into the application later. Please now go back to the notebook and continue to build your recommendation campaign and do some quick testing - if the notebook solution still hasn't completed then you may continue with the first part of the next section, **Finalise Django Framework Configuration** 153 | 154 | # Using the Video Recommendation App 155 | 156 | #### Notes on the Application 157 | 158 | 1. The RDS database is postgres, and we have included the **pgcli** tool with this deployment, as you may wish to look at the database schema structure, examine the data that was in the RDS snapshot, or potentially update this all after you have customised the Django application for your own needs. There is a startup script for this in the **/home/ec2-user/personalize-video-recs/videorecs/** folder, which is the root of the whole Django framework application. 159 | 160 | ## Running the Video Recommendation App 161 | 162 | 1. The application server should already running running. You can find the two URLs required for different aspects of the app in the CloudFormation outputs panel. 163 | 164 | ![](images/appUrlOutputs.png) 165 | 166 | 2. The URL of the application is your ALB followed by the **/recommend/** path, although there is also an **/admin/** path configured that we'll use later. For now connect to your application using the *AppEntrypoint* URL 167 | 168 | 3. You should see the following screen in your browser - no *Model Precision Metrics* are available, as we haven't added any models yet to the application. You can also see that documentation for this is present, but be aware that it may not be 100% up to date with coding changes on the demo. 169 | 170 | ![](images/appFrontScreen.png) 171 | 172 | 4. If you hit **Select Random User** then you'll be taken to the main Recommendation screen, which starts by showing you a random user's top-25 movie review titles. However, you'll see on the Model dropdown on the left that there are no models available, and if you change the Personalize Mode to either Personal Ranking or Similar Items then it's the same story - you can see the movie reviews, and most-popular titles in a genre, but no recommendations. We need to get the solutions and campaigns built in the notebook, then you can come back and plug in the models. 173 | 174 | ![](images/appRecNoModels.png) 175 | 176 | At this point we require the solution that is being built in the notebook to complete and the associated campaign to have been created - until that time we cannot move forward, so you may wish to get some refreshments if you are still waiting for those two steps to complete. 177 | 178 | ## Create Additional Personalize Campaigns 179 | 180 | If you have built the additional two Personalize models, for Item-to-Item Similarities and Personal Rankings, then you'll need to create the associated campaigns for these solutions, as it is the campaigns that we will add to the application. If those solutions have been built then continue with these steps, but if not you can always come back to these steps later before adding them to the application. 181 | 182 | 1. In the AWS Console, go to the **Amazon Personalize** service console, click on **Dataset groups** link on the left-hand menu, and select the **personalize-recs-dataset-group** link, then click into the **Campaigns** menu item on the left. Select **Create campaign** 183 | 184 | ![](images/campaignSingle.png) 185 | 186 | 2. First, we want to build the campaign for the *Similar Items* model - enter a name for the campaign, such as *similar-items-campaign*, select via the drop-down that solution that you previously built in the console, *similar-items-solution*, and ensure that minimum TPS is set to 1. Hit **Create campaign** 187 | 188 | ![](images/createCampaign.png) 189 | 190 | 3. Now build the campaign for the *Personal Rankings* model - follow the same steps as before, but this time use *rankings-campaign* for the campaign name and select the *rankings-solution* model in the drop-down control. 191 | 192 | ## Plug In the Recommendation Model(s) 193 | 194 | The application uses the Django Administration feature to define models that are available to the application. This allows multiple models of different types to be configured, and injected or removed from the application at any time. There are three modes of operation of the application: 195 | 196 | - **Recommendations** - standard recommendations, allowing different 2 models to be compared at once 197 | - **Personal Ranking** - re-ranks popular films in a genre, with a single model on-screen at once 198 | - **Similar Items** - shows items similar to others, with a single model on-screen at once. You can optionally send this list through a *Personal Ranking* model if you have one defined 199 | 200 | Each of these modes allows multiple models of their type to be used, but each mode can only show one or two different models simultaneously - however, you can choose any configured model at any time. 201 | 202 | 1. Login to the Django Administration site. This is at the same URL as the main application, but replace **/recommend** with **/admin** at the end of the URL, as shown previously in the CloudFormation Outputs panel. This will bring up the following screen, so login now with the credentials that you supplied when you ran the CloudFormation template: 203 | 204 | ![](images/djangoAdmin.png) 205 | 206 | 3. This brings up the *Site Administration* screen, which show entries for Groups and Users (which we don't need), but also a section called **Recommend** where you can add **Personalize models** to the app. Click on **+Add** link to begin to add a new model 207 | 208 | 4. Back on the AWS Console, go to the **Amazon Personalize** service console, select the **personalize-recs-dataset-group** and then on the left-hand menu click **Campaigns**. This will show your **personalize-lab-recs-campaign**, along with the campaigns for the other two solutions if you've created them. If you've created all three then you should see something like this, but for your other two campaigns may already have been created 209 | 210 | ![](images/campaignList.png) 211 | 212 | 5. Click on the **personalize-lab-recs-campaign** and you'll see the **Campaign ARN** - copy this, and head back to the admin screen. Enter **Personal Recommendations** for the model name, enter the ARN where it asks, ensure that the **Model type** is set for recommendations and set the **Model sort order** to 1. Click on **SAVE** to save the definition. 213 | 214 | ![](images/djangoAddModel.png) 215 | 216 | 6. The application will use the sort order field to decide how to order models in the on-screen drop-downs. Only models of the right type are shown on the relevant screen, but there is no validation that you have entered the correct model type, and if you put a SIMS model on the Rankings screen then the application will throw errors. 217 | 218 | 7. If you also have a SIMS or Personal Ranking campaign then go ahead and add them now in the same way - if they haven't yet completed then you can come back and add them later. You can then close the admin screen and head back to the main application web page 219 | 220 | 8. The main screen now shows the three models (or maybe just one) that we've built - it lists the precision metrics for each one, and as you add or remove models from the Django Administration page the changes will be reflected here. Now click on the **Select Random User** button 221 | 222 | ![](images/appFrontScreenWithModels.png) 223 | 224 | 9. The screen will look as before, but now if you click on the **Model 1** drop-down you will see that our one Recommendation model is present - if you select it then the screen will refresh to show recommendations for this user using that model. 225 | 226 | ![](images/appRecWithModels.png) 227 | 228 | 10. You can step through users to see how these look for different demographics of users. If you had mutiple Recommendation models defined then they would also be in the two model drop-downs, and you'd be able to show two completely different recommendation models, based upon different user demographic or item metadata, allowing you to compare and contrast different approaches. 229 | 230 | 11. Try out the **Personal Ranking** personalize mode - this takes a list of the most popular movies in the dataset, either as a whole or in just a single genre. This will take that list and re-rank it into an order for this particular user, ensuring that the ones that are most likely to be interested in are shown first. 231 | 232 | 12. Finally, try the **Similar Items** personalize mode - this starts with the user's top-ranked film, and finds a list of films that people who watched this also watched. This is done without reference to the user's preferences, and the list is generated based upon what's in the dataset as a whole. However, if you also have a **Personal Ranking** model defined then the **Ordering** drop-down will re-rank this list into one that is more suited to the user's preferences. 233 | 234 | ## Additional Campaigns to Build 235 | 236 | If you look at the embedded documentation you'll see that it talks about 3 other models, which there isn't time to build during this Lab. They involve the user of additional data files - a user demographic file, and a item metadata file, all of which are supplied with the Movie Lens data set in your Sagemaker Notebook. Because they required additional data-sets, you need to create each of these within their own Personalize Dataset Group, and you also need to re-import the original interactions file **DEMO-movie-lens-100k.csv** that you uploaded into S3 during the notebook - this is because Personalize trains solutions on all data files witin the Dataset Group. 237 | 238 | The three models that you should build are as follows: 239 | 240 | - Using a USERS file, create a model that takes into account user's demographic details such as age, gender and occupation 241 | - Using an ITEMS metadata file, create a model that also takes into account the movie year and the top-4 genres associated with that movie as 4 separate metadata fields 242 | - Using an ITEMS metadata file, create a model that also takes into account the movie year and then compounds the top-4 genres into a single metadata field 243 | 244 | Observations are that demographics are absolutely not a good indicator for movies recommendations, nor for things like book recommendations - this isn't an issue with Amazon Personalize, rather it is a know issue with using age and gender to predict likes and dislikes of media. Also, the single, compound genre certainly seems more accurate for the first 5 or 10 responses, but for the set of 25 response as a whole the multiple genre model probably gets a better list of movies than the compound one. 245 | 246 | # Closing Down Your Resources 247 | 248 | ## Terminating the Notebook Instance 249 | 250 | 1. Open the Amazon SageMaker console and click on **Notebook instances** 251 | 2. Find the notebook instance listed as _[Name]-lab-notebook_, select its radio button and then click the **Actions** dropdown. 252 | 253 | ![Terminate instance](images/terminateNotebook.png) 254 | 255 | 3. Click **Stop** to stop the Notebook Instance. This does not delete the underlying data and resources. After a few minutes the instance status will change to _Stopped_, and you can now click on the **Actions** dropdown again, but this time select **Delete**. 256 | 257 | Note that by selecting the name of the Notebook instance on this dialog you are taken to a more detailed information page regarding that instance, which also has **Stop** and **Delete** buttons present – notebooks can also be deleted using this method. 258 | 259 | ## Conclusion 260 | 261 | Upon completion of this lab you will have performed the following: 262 | 263 | - Launched a Jupyter notebook from with the Amazon SageMaker service 264 | - Imported external files into the notebook environment 265 | - Seen how to enable Preview services within a notebook (assuming your account has been whitelisted for Preview access) 266 | - Used the **pandas** libraries to do some pre-processing of the source data 267 | - Built and deployed an ML model based upon the HRNN algorithm 268 | - Tested your model via just a few lines of code 269 | - Deployed your model into a live application 270 | 271 | You should now be able to embed this model from within your own application code, using any language that is supported by the AWS SDK. Happy recommending! 272 | -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/cloudformation_template.yml: -------------------------------------------------------------------------------- 1 | Description: 2 | This template deploys a VPC, with a pair of public and private subnets spread 3 | across two Availability Zones. It deploys an Internet Gateway, with a default 4 | route on the public subnets. 5 | 6 | Parameters: 7 | AppEnvironmentName: 8 | Description: An environment name that will be prefixed to resource names 9 | Type: String 10 | Default: "Amazon Personalize Lab" 11 | 12 | DjangoAdminLogin: 13 | Description: Please enter the name of your Django Administrator User (case sensitive) 14 | Type: String 15 | Default: "Admin" 16 | 17 | DjangoAdminPassword: 18 | NoEcho: true 19 | Description: Please enter the login password for your Django Administrator User (4-12 chars; lower-case, upper-case and numerals) 20 | Type: String 21 | MinLength: 4 22 | MaxLength: 12 23 | AllowedPattern: ^[a-zA-Z0-9]*$ 24 | 25 | DjangoEmailAddress: 26 | Description: Please enter the email address for your Django Administrator User 27 | Type: String 28 | Default: "blank@example.com" 29 | 30 | VpcCIDR: 31 | Description: Please enter the IP range (CIDR notation) for this VPC 32 | Type: String 33 | Default: 10.192.0.0/16 34 | 35 | PublicSubnet1CIDR: 36 | Description: Please enter the IP range (CIDR notation) for the public subnet in the first Availability Zone 37 | Type: String 38 | Default: 10.192.10.0/24 39 | 40 | PublicSubnet2CIDR: 41 | Description: Please enter the IP range (CIDR notation) for the public subnet in the second Availability Zone 42 | Type: String 43 | Default: 10.192.11.0/24 44 | 45 | PrivateSubnet1CIDR: 46 | Description: Please enter the IP range (CIDR notation) for the private subnet in the first Availability Zone 47 | Type: String 48 | Default: 10.192.20.0/24 49 | 50 | PrivateSubnet2CIDR: 51 | Description: Please enter the IP range (CIDR notation) for the private subnet in the second Availability Zone 52 | Type: String 53 | Default: 10.192.21.0/24 54 | 55 | KeyName: 56 | Description: Name of an existing EC2 KeyPair to enable SSH access to the instances 57 | Type: 'AWS::EC2::KeyPair::KeyName' 58 | ConstraintDescription: must be the name of an existing EC2 KeyPair. 59 | 60 | SSHLocation: 61 | Description: Lockdown SSH access to the servers 62 | Type: String 63 | MinLength: '9' 64 | MaxLength: '18' 65 | Default: 0.0.0.0/0 66 | AllowedPattern: '(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})' 67 | ConstraintDescription: must be a valid CIDR range of the form x.x.x.x/x. 68 | 69 | InstanceAMI: 70 | Description: EC2 instance AMI for auto-scaling 71 | Type: String 72 | Default: ami-0a1bbc59157225e69 73 | 74 | SnapshotArn: 75 | Description: ARN of the Public RDS Snapshot 76 | Type: String 77 | Default: arn:aws:rds:us-east-1:302460114512:snapshot:londonsummitbaseline 78 | 79 | Resources: 80 | VPC: 81 | Type: AWS::EC2::VPC 82 | Properties: 83 | CidrBlock: !Ref VpcCIDR 84 | EnableDnsSupport: true 85 | EnableDnsHostnames: true 86 | Tags: 87 | - Key: Name 88 | Value: !Ref AppEnvironmentName 89 | 90 | InternetGateway: 91 | Type: AWS::EC2::InternetGateway 92 | Properties: 93 | Tags: 94 | - Key: Name 95 | Value: !Ref AppEnvironmentName 96 | 97 | InternetGatewayAttachment: 98 | Type: AWS::EC2::VPCGatewayAttachment 99 | Properties: 100 | InternetGatewayId: !Ref InternetGateway 101 | VpcId: !Ref VPC 102 | 103 | PublicSubnet1: 104 | Type: AWS::EC2::Subnet 105 | Properties: 106 | VpcId: !Ref VPC 107 | AvailabilityZone: !Select [ 0, !GetAZs '' ] 108 | CidrBlock: !Ref PublicSubnet1CIDR 109 | MapPublicIpOnLaunch: true 110 | Tags: 111 | - Key: Name 112 | Value: !Sub ${AppEnvironmentName} Public Subnet (AZ1) 113 | 114 | PublicSubnet2: 115 | Type: AWS::EC2::Subnet 116 | Properties: 117 | VpcId: !Ref VPC 118 | AvailabilityZone: !Select [ 1, !GetAZs '' ] 119 | CidrBlock: !Ref PublicSubnet2CIDR 120 | MapPublicIpOnLaunch: true 121 | Tags: 122 | - Key: Name 123 | Value: !Sub ${AppEnvironmentName} Public Subnet (AZ2) 124 | 125 | PrivateSubnet1: 126 | Type: AWS::EC2::Subnet 127 | Properties: 128 | VpcId: !Ref VPC 129 | AvailabilityZone: !Select [ 0, !GetAZs '' ] 130 | CidrBlock: !Ref PrivateSubnet1CIDR 131 | MapPublicIpOnLaunch: false 132 | Tags: 133 | - Key: Name 134 | Value: !Sub ${AppEnvironmentName} Private Subnet (AZ1) 135 | 136 | PrivateSubnet2: 137 | Type: AWS::EC2::Subnet 138 | Properties: 139 | VpcId: !Ref VPC 140 | AvailabilityZone: !Select [ 1, !GetAZs '' ] 141 | CidrBlock: !Ref PrivateSubnet2CIDR 142 | MapPublicIpOnLaunch: false 143 | Tags: 144 | - Key: Name 145 | Value: !Sub ${AppEnvironmentName} Private Subnet (AZ2) 146 | 147 | NatGateway1EIP: 148 | Type: AWS::EC2::EIP 149 | DependsOn: InternetGatewayAttachment 150 | Properties: 151 | Domain: vpc 152 | 153 | NatGateway1: 154 | Type: AWS::EC2::NatGateway 155 | Properties: 156 | AllocationId: !GetAtt NatGateway1EIP.AllocationId 157 | SubnetId: !Ref PublicSubnet1 158 | 159 | PublicRouteTable: 160 | Type: AWS::EC2::RouteTable 161 | Properties: 162 | VpcId: !Ref VPC 163 | Tags: 164 | - Key: Name 165 | Value: !Sub ${AppEnvironmentName} Public Routes 166 | 167 | DefaultPublicRoute: 168 | Type: AWS::EC2::Route 169 | DependsOn: InternetGatewayAttachment 170 | Properties: 171 | RouteTableId: !Ref PublicRouteTable 172 | DestinationCidrBlock: 0.0.0.0/0 173 | GatewayId: !Ref InternetGateway 174 | 175 | PublicSubnet1RouteTableAssociation: 176 | Type: AWS::EC2::SubnetRouteTableAssociation 177 | Properties: 178 | RouteTableId: !Ref PublicRouteTable 179 | SubnetId: !Ref PublicSubnet1 180 | 181 | PublicSubnet2RouteTableAssociation: 182 | Type: AWS::EC2::SubnetRouteTableAssociation 183 | Properties: 184 | RouteTableId: !Ref PublicRouteTable 185 | SubnetId: !Ref PublicSubnet2 186 | 187 | PrivateRouteTable1: 188 | Type: AWS::EC2::RouteTable 189 | Properties: 190 | VpcId: !Ref VPC 191 | Tags: 192 | - Key: Name 193 | Value: !Sub ${AppEnvironmentName} Private Routes (AZ1) 194 | 195 | DefaultPrivateRoute1: 196 | Type: AWS::EC2::Route 197 | Properties: 198 | RouteTableId: !Ref PrivateRouteTable1 199 | DestinationCidrBlock: 0.0.0.0/0 200 | NatGatewayId: !Ref NatGateway1 201 | 202 | PrivateSubnet1RouteTableAssociation: 203 | Type: AWS::EC2::SubnetRouteTableAssociation 204 | Properties: 205 | RouteTableId: !Ref PrivateRouteTable1 206 | SubnetId: !Ref PrivateSubnet1 207 | 208 | PrivateRouteTable2: 209 | Type: AWS::EC2::RouteTable 210 | Properties: 211 | VpcId: !Ref VPC 212 | Tags: 213 | - Key: Name 214 | Value: !Sub ${AppEnvironmentName} Private Routes (AZ2) 215 | 216 | DefaultPrivateRoute2: 217 | Type: AWS::EC2::Route 218 | Properties: 219 | RouteTableId: !Ref PrivateRouteTable2 220 | DestinationCidrBlock: 0.0.0.0/0 221 | NatGatewayId: !Ref NatGateway1 222 | 223 | PrivateSubnet2RouteTableAssociation: 224 | Type: AWS::EC2::SubnetRouteTableAssociation 225 | Properties: 226 | RouteTableId: !Ref PrivateRouteTable2 227 | SubnetId: !Ref PrivateSubnet2 228 | 229 | ApplicationLoadBalancer: 230 | Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer' 231 | Properties: 232 | Name: 'Personalize-Demo-ALB' 233 | Subnets: [!Ref PublicSubnet1, !Ref PublicSubnet2] 234 | SecurityGroups: 235 | - !Ref LoadBalancerSecurityGroup 236 | Tags: 237 | - Key: Name 238 | Value: !Sub ${AppEnvironmentName} Personalize ALB 239 | 240 | ALBListener: 241 | Type: 'AWS::ElasticLoadBalancingV2::Listener' 242 | Properties: 243 | DefaultActions: 244 | - Type: forward 245 | TargetGroupArn: !Ref ALBTargetGroup 246 | LoadBalancerArn: !Ref ApplicationLoadBalancer 247 | Port: '80' 248 | Protocol: HTTP 249 | 250 | ALBTargetGroup: 251 | Type: 'AWS::ElasticLoadBalancingV2::TargetGroup' 252 | Properties: 253 | HealthCheckPath: /recommend/ 254 | HealthCheckIntervalSeconds: 30 255 | HealthCheckTimeoutSeconds: 25 256 | HealthyThresholdCount: 3 257 | Port: 8000 258 | Protocol: HTTP 259 | UnhealthyThresholdCount: 5 260 | VpcId: !Ref VPC 261 | 262 | LoadBalancerSecurityGroup: 263 | Type: 'AWS::EC2::SecurityGroup' 264 | Properties: 265 | GroupDescription: Enable HTTP access on port 80 266 | VpcId: !Ref VPC 267 | SecurityGroupIngress: 268 | - IpProtocol: tcp 269 | FromPort: '80' 270 | ToPort: '80' 271 | CidrIp: 0.0.0.0/0 272 | SecurityGroupEgress: 273 | - IpProtocol: tcp 274 | FromPort: '0' 275 | ToPort: '65535' 276 | CidrIp: 0.0.0.0/0 277 | Tags: 278 | - Key: Name 279 | Value: !Sub ${AppEnvironmentName} ALB Security Group 280 | 281 | InstanceSecurityGroup: 282 | Type: 'AWS::EC2::SecurityGroup' 283 | Properties: 284 | GroupDescription: Enable HTTP access and SSH access 285 | VpcId: !Ref VPC 286 | SecurityGroupIngress: 287 | - IpProtocol: tcp 288 | FromPort: '8000' 289 | ToPort: '8000' 290 | SourceSecurityGroupId: !Ref LoadBalancerSecurityGroup 291 | - IpProtocol: tcp 292 | FromPort: '22' 293 | ToPort: '22' 294 | CidrIp: !Ref SSHLocation 295 | SecurityGroupEgress: 296 | - IpProtocol: tcp 297 | FromPort: '0' 298 | ToPort: '65535' 299 | CidrIp: 0.0.0.0/0 300 | Tags: 301 | - Key: Name 302 | Value: !Sub ${AppEnvironmentName} Server Security Group 303 | 304 | AutoScalingGroup: 305 | Type: 'AWS::AutoScaling::AutoScalingGroup' 306 | DependsOn: DbInstance 307 | Properties: 308 | VPCZoneIdentifier: [!Ref PublicSubnet1, !Ref PublicSubnet2] 309 | LaunchConfigurationName: !Ref LaunchConfig 310 | MinSize: '1' 311 | MaxSize: '1' 312 | DesiredCapacity: '1' 313 | Tags: 314 | - Key: Name 315 | Value: !Sub ${AppEnvironmentName} 316 | PropagateAtLaunch: 'true' 317 | - Key: Personalize 318 | Value: Demo 319 | PropagateAtLaunch: 'true' 320 | TargetGroupARNs: 321 | - !Ref ALBTargetGroup 322 | 323 | LaunchConfig: 324 | Type: AWS::AutoScaling::LaunchConfiguration 325 | Properties: 326 | ImageId: !Ref InstanceAMI 327 | InstanceType: m4.xlarge 328 | AssociatePublicIpAddress: 'true' 329 | KeyName: !Ref KeyName 330 | IamInstanceProfile: !Ref PersonalizeInstanceProfile 331 | SecurityGroups: 332 | - !Ref InstanceSecurityGroup 333 | UserData: 334 | Fn::Base64: !Sub | 335 | #!/bin/sh 336 | privateIP=$(curl http://169.254.169.254/latest/meta-data/local-ipv4) 337 | albDNS=$(aws elbv2 describe-load-balancers --region us-east-1 --query 'LoadBalancers[?LoadBalancerName==`Personalize-Demo-ALB`].DNSName' --output text) 338 | rdsDNS=$(aws rds describe-db-instances --region us-east-1 --query 'DBInstances[?DBName==`videorec`].Endpoint.Address' --output text) 339 | cd /home/ec2-user/personalize-video-recs/videorecs 340 | sed -i s/PRIVATEIP/$privateIP/g runmyserver 341 | sed -i s/POSTGRESDNS/$rdsDNS/g pgcli 342 | cd videorecs 343 | sed -i s/PRIVATEIP/$privateIP/g settings.py 344 | sed -i s/ALBDNS/$albDNS/g settings.py 345 | sed -i s/POSTGRESDNS/$rdsDNS/g settings.py 346 | runuser -l ec2-user -c 'cd personalize-video-recs/videorecs; ./runmyserver > /dev/null 2>&1 &' 347 | sleep 5 348 | runuser -l ec2-user -c 'cd personalize-video-recs/videorecs; python manage.py createsuperuser2 --username ${DjangoAdminLogin} --password ${DjangoAdminPassword} --noinput --email "${DjangoEmailAddress}" ' 349 | 350 | PersonalizeEC2Role: 351 | Type: 'AWS::IAM::Role' 352 | Properties: 353 | RoleName: "AmazonLabInstanceRole" 354 | AssumeRolePolicyDocument: 355 | Statement: 356 | - Effect: Allow 357 | Principal: 358 | Service: 359 | - ec2.amazonaws.com 360 | Action: 361 | - 'sts:AssumeRole' 362 | Path: / 363 | ManagedPolicyArns: 364 | - arn:aws:iam::aws:policy/AmazonS3FullAccess 365 | - arn:aws:iam::aws:policy/service-role/AmazonPersonalizeFullAccess 366 | - arn:aws:iam::aws:policy/IAMReadOnlyAccess 367 | - arn:aws:iam::aws:policy/AmazonEC2ReadOnlyAccess 368 | - arn:aws:iam::aws:policy/AmazonRDSReadOnlyAccess 369 | 370 | PersonalizeInstanceProfile: 371 | Type: 'AWS::IAM::InstanceProfile' 372 | Properties: 373 | Path: / 374 | Roles: 375 | - !Ref PersonalizeEC2Role 376 | 377 | ExtraIamForSagemaker: 378 | Type: 'AWS::IAM::Policy' 379 | Properties: 380 | PolicyName: ExtraIamForSagemaker 381 | PolicyDocument: 382 | Statement: 383 | - Effect: Allow 384 | Action: 385 | - 'iam:CreateRole' 386 | - 'iam:AttachRolePolicy' 387 | - 'iam:PassRole' 388 | Resource: '*' 389 | Roles: 390 | - !Ref PersonalizeSagemakeRole 391 | 392 | PersonalizeSagemakeRole: 393 | Type: 'AWS::IAM::Role' 394 | Properties: 395 | RoleName: "AmazonLabSageMakerRole" 396 | AssumeRolePolicyDocument: 397 | Statement: 398 | - Effect: Allow 399 | Principal: 400 | Service: 401 | - sagemaker.amazonaws.com 402 | Action: 403 | - 'sts:AssumeRole' 404 | Path: / 405 | ManagedPolicyArns: 406 | - arn:aws:iam::aws:policy/AmazonS3FullAccess 407 | - arn:aws:iam::aws:policy/service-role/AmazonPersonalizeFullAccess 408 | - arn:aws:iam::aws:policy/AmazonSageMakerFullAccess 409 | 410 | RDSAccessSecurityGroup: 411 | Type: AWS::EC2::SecurityGroup 412 | Properties: 413 | GroupDescription: Instance to RDS Access 414 | VpcId: !Ref VPC 415 | SecurityGroupIngress: 416 | - IpProtocol: tcp 417 | FromPort: '0' 418 | ToPort: '65535' 419 | SourceSecurityGroupId: !Ref InstanceSecurityGroup 420 | Tags: 421 | - Key: Name 422 | Value: !Sub ${AppEnvironmentName} RDS Security Group 423 | 424 | DbSubnetGroup: 425 | Type: AWS::RDS::DBSubnetGroup 426 | Properties: 427 | DBSubnetGroupDescription: !Join [ "", [ "RDS Subnet Group for ", !Ref AppEnvironmentName ] ] 428 | SubnetIds: [!Ref PrivateSubnet1, !Ref PrivateSubnet2] 429 | Tags: 430 | - Key: Name 431 | Value: !Ref AppEnvironmentName 432 | 433 | DbInstance: 434 | Type: AWS::RDS::DBInstance 435 | DeletionPolicy: Snapshot 436 | DependsOn: 437 | - DbSubnetGroup 438 | - RDSAccessSecurityGroup 439 | Properties: 440 | DBSnapshotIdentifier: !Ref SnapshotArn 441 | AllowMajorVersionUpgrade: false 442 | AutoMinorVersionUpgrade: true 443 | BackupRetentionPeriod: 7 444 | DBInstanceClass: db.m4.xlarge 445 | DBInstanceIdentifier: amazonPersonalizeLab 446 | DBSubnetGroupName: !Ref DbSubnetGroup 447 | MultiAZ: false 448 | Port: 5432 449 | PubliclyAccessible: false 450 | StorageEncrypted: false 451 | StorageType: gp2 452 | VPCSecurityGroups: 453 | - !Ref RDSAccessSecurityGroup 454 | Tags: 455 | - Key: Name 456 | Value: !Ref AppEnvironmentName 457 | 458 | Outputs: 459 | AppEntrypoint: 460 | Description: User entrypoint for web-application 461 | Value: !Join [ "", [ !GetAtt ApplicationLoadBalancer.DNSName, "/recommend" ] ] 462 | 463 | AppEntrypointAdmin: 464 | Description: Admin entrypoint for web-application 465 | Value: !Join [ "", [ !GetAtt ApplicationLoadBalancer.DNSName, "/admin" ] ] 466 | -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/docs/personalize-demo-docs.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/docs/personalize-demo-docs.docx -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/docs/personalize-demo-docs.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/docs/personalize-demo-docs.pdf -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/Picture02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/Picture02.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/appFrontScreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/appFrontScreen.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/appFrontScreenWithModels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/appFrontScreenWithModels.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/appRecNoModels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/appRecNoModels.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/appRecWithModels.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/appRecWithModels.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/appUrlOutputs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/appUrlOutputs.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/campaignList.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/campaignList.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/campaignSingle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/campaignSingle.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/cellTypes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/cellTypes.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/cfnSelectTemplate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/cfnSelectTemplate.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/changeRegion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/changeRegion.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/consoleCfnSelect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/consoleCfnSelect.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/consoleEC2Select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/consoleEC2Select.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/consoleSMSelect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/consoleSMSelect.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/createCampaign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/createCampaign.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/createKeyPair.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/createKeyPair.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/datasetGroups.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/datasetGroups.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/djangoAddModel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/djangoAddModel.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/djangoAdmin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/djangoAdmin.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/findSagemakerRole.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/findSagemakerRole.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/loadBoto3Post.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/loadBoto3Post.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/loadBoto3Pre.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/loadBoto3Pre.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/loadbalancerDNS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/loadbalancerDNS.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/openNotebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/openNotebook.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/personalize.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/personalize.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/rdsDNS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/rdsDNS.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/recipeRanking.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/recipeRanking.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/selectEC2details.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/selectEC2details.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/solutionConfig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/solutionConfig.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/solutionList.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/solutionList.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/terminateNotebook.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/terminateNotebook.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/uploadFiles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/uploadFiles.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/images/uploadFiles2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-6-Personalize_your_Recommendations/images/uploadFiles2.png -------------------------------------------------------------------------------- /lab-6-Personalize_your_Recommendations/personalize_sample_notebook.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "### Get the Personalize boto3 Client\n", 8 | "\n", 9 | "All of the code that we are using in this lab is Python, but any langugage supported by SageMaker could be used. In this initial piece of code we are loading in the library dependencies that we need for the rest of the lab:\n", 10 | "\n", 11 | "- **boto3** - standard Pything CLI library for the AWS SDK\n", 12 | "- **json** - used to manipulate JSON structures used by our API calls\n", 13 | "- **numpy** and **pandas** - standard libraries used by Data Scientists everywhere\n", 14 | "- **time**, **datetime** and **pytz** - used for some time manipulation calls\n", 15 | "\n", 16 | "These client handlers will be used throughout the lab, and you will see examples of other client handlers being instantiated for services such as Amazon S3 and IAM." 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": null, 22 | "metadata": {}, 23 | "outputs": [], 24 | "source": [ 25 | "import boto3\n", 26 | "\n", 27 | "import json\n", 28 | "import numpy as np\n", 29 | "import pandas as pd\n", 30 | "import time\n", 31 | "import pytz\n", 32 | "from datetime import datetime\n", 33 | "\n", 34 | "personalize = boto3.client('personalize')\n", 35 | "personalize_runtime = boto3.client('personalize-runtime')" 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "metadata": {}, 41 | "source": [ 42 | "### Specify a Bucket for Our Data\n", 43 | "\n", 44 | "Amazon Personalize only supports the uploading of data from an S3 bucket. Hence, you need to create a new bucket or re-use an existing one. If you need to create one the you **MUST** name your bucket before running this Code cell by editing the value for *bucket* in the code below. You need to ensure that the bucket name is globally unique; for this lab we recommend using your name or initials, followed by *-amazon-personalize-lab*, as that is likely to be unique. Also, you **MUST** ensure that the bucket name contains the work *personalize*, as some IAM permissions later rely on this.\n", 45 | "\n", 46 | "If the bucket already exists - such as if you execute this code cell a second time - then it will not create a new bucket, and will not make any changes to the existing bucket. If this happens unexpectedly then please check your own S3 page in the console to ensure that the bucket is in your account." 47 | ] 48 | }, 49 | { 50 | "cell_type": "code", 51 | "execution_count": null, 52 | "metadata": {}, 53 | "outputs": [], 54 | "source": [ 55 | "bucket = \"{prefix}-amazon-personalize-lab\" # replace with the name of your S3 bucket\n", 56 | "\n", 57 | "s3 = boto3.client('s3')\n", 58 | "if boto3.resource('s3').Bucket(bucket).creation_date is None:\n", 59 | " s3.create_bucket(ACL = \"private\", Bucket = bucket)\n", 60 | " print(\"Creating bucket: {}\".format(bucket))\n", 61 | "else:\n", 62 | " print(\"Bucket {} already exists\".format(bucket))" 63 | ] 64 | }, 65 | { 66 | "cell_type": "markdown", 67 | "metadata": {}, 68 | "source": [ 69 | "### Download, Prepare, and Upload Training Data\n", 70 | "\n", 71 | "#### Download and Explore the Dataset\n", 72 | "\n", 73 | "In this step we download the entire Movie Lens data set zip-file and unzip it - it will go in the same location as the physical notebook *.ipynb* file and the *u.item* file that you downloaded earlier. We use the **pandas** library to read in the *u.data* file, which contains all of the movie reviews; this file consists of a movie ID, a user ID, a timestamp and a rating of between 1 and 5, and there are 100,000 unique reviews." 74 | ] 75 | }, 76 | { 77 | "cell_type": "code", 78 | "execution_count": null, 79 | "metadata": {}, 80 | "outputs": [], 81 | "source": [ 82 | "!wget -N http://files.grouplens.org/datasets/movielens/ml-100k.zip\n", 83 | "!unzip -o ml-100k.zip\n", 84 | "data = pd.read_csv('./ml-100k/u.data', sep='\\t', names=['USER_ID', 'ITEM_ID', 'RATING', 'TIMESTAMP'])\n", 85 | "pd.set_option('display.max_rows', 5)\n", 86 | "data" 87 | ] 88 | }, 89 | { 90 | "cell_type": "markdown", 91 | "metadata": {}, 92 | "source": [ 93 | "#### Prepare and Upload Data\n", 94 | "\n", 95 | "We don't actually need all of the review data. We would like to recommend movies that a user would actually watch based upon what they and others have liked - we don't want to provide a rating between 1 and 5 for every movie in the data set! Hence, we're going to use **pandas** to drop any reviews that are not > 3.6, and once we've done that also drop the review column - we're left with a subset of the original that contain 4- and 5-star ratings, which is basically indicating movies that the user really liked. Of course, we could use all of the data, but this would lead to far greater data import and model training time.\n", 96 | "\n", 97 | "Additionally, the reviews are quite old - they are from August 1997 to April 1998. Some of the Amazon Personalize recipies react differently depending upon the age of the interactions - for instance, the _Similar Items_ recipe has several hyperparameters around how to handle 'decaying' interactions. In order to make this lab easier, and not have to worry about these hyperparameters, we are shifting all of the review timestamps to be from August 2018 up until April 2019.\n", 98 | "\n", 99 | "We then write that out to a file named as per that defined two steps previously, and upload it into our S3 bucket. \n", 100 | "\n", 101 | "This is the minimum amount of data that Amazon Personalize needs to train a model on - you need just 1000 rows of user/item/timestamp interactions, but we still have many 10s of thousands of entries left from our original 100,000 review dataset. This file is known in Amazon Personalize as an **Interactions** data file. Other data files are usable, such as ones that define additional metadata about the movies (such as year and genre) and another that defines demographic data about the user (such as age, gender and location). In this lab we do not need them, but this information is available in the Movie Lens data set files that you have downloaded - you can create your own models based upon those at a later date." 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": null, 107 | "metadata": {}, 108 | "outputs": [], 109 | "source": [ 110 | "filename = \"DEMO-movie-lens-100k.csv\" # file in S3 that will hold our model training data\n", 111 | "data = data[data['RATING'] > 3.6] # keep only movies rated 3.6 and above\n", 112 | "data = data[['USER_ID', 'ITEM_ID', 'TIMESTAMP']] # select columns that match the columns in the schema below\n", 113 | "data['TIMESTAMP'] = data['TIMESTAMP'] + 660833618 # make reviews end 1st April 2019 rather than 23rd April 1998\n", 114 | "data.to_csv(filename, index=False)\n", 115 | "\n", 116 | "boto3.Session().resource('s3').Bucket(bucket).Object(filename).upload_file(filename)" 117 | ] 118 | }, 119 | { 120 | "cell_type": "markdown", 121 | "metadata": {}, 122 | "source": [ 123 | "### Create Schema\n", 124 | "\n", 125 | "Amazon Personalize uses *Schemas* to tell it how to interpret your data files. This step defines the schema for our Interations file, which consists solely of a `USER_ID`, `ITEM_ID` and `TIMESTAMP`. Once defined we pass it into Personalize for use." 126 | ] 127 | }, 128 | { 129 | "cell_type": "code", 130 | "execution_count": null, 131 | "metadata": {}, 132 | "outputs": [], 133 | "source": [ 134 | "schema = {\n", 135 | " \"type\": \"record\",\n", 136 | " \"name\": \"Interactions\",\n", 137 | " \"namespace\": \"com.amazonaws.personalize.schema\",\n", 138 | " \"fields\": [\n", 139 | " {\n", 140 | " \"name\": \"USER_ID\",\n", 141 | " \"type\": \"string\"\n", 142 | " },\n", 143 | " {\n", 144 | " \"name\": \"ITEM_ID\",\n", 145 | " \"type\": \"string\"\n", 146 | " },\n", 147 | " {\n", 148 | " \"name\": \"TIMESTAMP\",\n", 149 | " \"type\": \"long\"\n", 150 | " }\n", 151 | " ],\n", 152 | " \"version\": \"1.0\"\n", 153 | "}\n", 154 | "\n", 155 | "create_schema_response = personalize.create_schema(\n", 156 | " name = \"personalize-lab-recs-schema\",\n", 157 | " schema = json.dumps(schema)\n", 158 | ")\n", 159 | "\n", 160 | "schema_arn = create_schema_response['schemaArn']\n", 161 | "print(json.dumps(create_schema_response, indent=2))" 162 | ] 163 | }, 164 | { 165 | "cell_type": "markdown", 166 | "metadata": {}, 167 | "source": [ 168 | "### Create and Wait for Dataset Group\n", 169 | "\n", 170 | "Now that we have defined a schema, and we have our Interactions data file, we can import the data into Personalize. But first we have to define a *Dataset Group*, which is essentially a collection of imported data files, trained models and campaigns - each Dataset Group can contain one, and only one, Interaction, Item Metadata and User Demographic file. When you train a model Personalize will use **all** data files present within its Dataset Group.\n", 171 | "\n", 172 | "#### Create Dataset Group" 173 | ] 174 | }, 175 | { 176 | "cell_type": "code", 177 | "execution_count": null, 178 | "metadata": {}, 179 | "outputs": [], 180 | "source": [ 181 | "create_dataset_group_response = personalize.create_dataset_group(\n", 182 | " name = \"personalize-recs-dataset-group\"\n", 183 | ")\n", 184 | "\n", 185 | "dataset_group_arn = create_dataset_group_response['datasetGroupArn']\n", 186 | "print(json.dumps(create_dataset_group_response, indent=2))" 187 | ] 188 | }, 189 | { 190 | "cell_type": "markdown", 191 | "metadata": {}, 192 | "source": [ 193 | "#### Wait for Dataset Group to Have ACTIVE Status\n", 194 | "\n", 195 | "A number of Personalize API calls do take time, hence the calls are asynchronous. Before we can continue with the next stage we need to poll the status of the `create_dataset_group()` call from the previous code cell - once the Dataset Group is active then we can continue. **NOTE: this step should not take more than 1-2 minutes to complete**" 196 | ] 197 | }, 198 | { 199 | "cell_type": "code", 200 | "execution_count": null, 201 | "metadata": {}, 202 | "outputs": [], 203 | "source": [ 204 | "status = None\n", 205 | "max_time = time.time() + 3*60*60 # 3 hours\n", 206 | "while time.time() < max_time:\n", 207 | " describe_dataset_group_response = personalize.describe_dataset_group(\n", 208 | " datasetGroupArn = dataset_group_arn\n", 209 | " )\n", 210 | " status = describe_dataset_group_response[\"datasetGroup\"][\"status\"]\n", 211 | " now = datetime.now(pytz.utc)\n", 212 | " elapsed = now - describe_dataset_group_response[\"datasetGroup\"][\"creationDateTime\"]\n", 213 | " print(\"DatasetGroup: {} (elapsed = {})\".format(status, elapsed))\n", 214 | " \n", 215 | " if status == \"ACTIVE\" or status == \"CREATE FAILED\":\n", 216 | " break\n", 217 | " \n", 218 | " time.sleep(15)" 219 | ] 220 | }, 221 | { 222 | "cell_type": "markdown", 223 | "metadata": {}, 224 | "source": [ 225 | "### Create Dataset\n", 226 | "\n", 227 | "We now have to create our dataset for the Interactions file. This step does not actually import any data, rather it creates an internal structure for the data to be imported into." 228 | ] 229 | }, 230 | { 231 | "cell_type": "code", 232 | "execution_count": null, 233 | "metadata": {}, 234 | "outputs": [], 235 | "source": [ 236 | "dataset_type = \"INTERACTIONS\"\n", 237 | "create_dataset_response = personalize.create_dataset(\n", 238 | " datasetType = dataset_type,\n", 239 | " datasetGroupArn = dataset_group_arn,\n", 240 | " schemaArn = schema_arn,\n", 241 | " name=\"personalize-recs-dataset\"\n", 242 | ")\n", 243 | "\n", 244 | "dataset_arn = create_dataset_response['datasetArn']\n", 245 | "print(json.dumps(create_dataset_response, indent=2))" 246 | ] 247 | }, 248 | { 249 | "cell_type": "markdown", 250 | "metadata": {}, 251 | "source": [ 252 | "### Prepare, Create, and Wait for Dataset Import Job" 253 | ] 254 | }, 255 | { 256 | "cell_type": "markdown", 257 | "metadata": {}, 258 | "source": [ 259 | "#### Attach policy to S3 bucket\n", 260 | "\n", 261 | "Whilst we have created an S3 bucket, and our Interactions data file is sat there waiting to be imported, we have a problem - you may have full access to the bucket via the AWS console or APIs, but the Amazon Personalize service does not. Hence, you have to create an S3 bucket policy that explicitly grants the service access to the `GetObject` and `ListBucket` commands in S3. This code step creates such a policy and attaches it to your S3 bucket.\n", 262 | "\n", 263 | "Note, any Personalize API calls that need to access you S3 bucket need to be done under the auspices of an IAM role that gives it permission - this step simply allows the service to access the bucket if, and only if, roles with appropriate permissions are used." 264 | ] 265 | }, 266 | { 267 | "cell_type": "code", 268 | "execution_count": null, 269 | "metadata": {}, 270 | "outputs": [], 271 | "source": [ 272 | "s3 = boto3.client(\"s3\")\n", 273 | "\n", 274 | "policy = {\n", 275 | " \"Version\": \"2012-10-17\",\n", 276 | " \"Id\": \"PersonalizeS3BucketAccessPolicy\",\n", 277 | " \"Statement\": [\n", 278 | " {\n", 279 | " \"Sid\": \"PersonalizeS3BucketAccessPolicy\",\n", 280 | " \"Effect\": \"Allow\",\n", 281 | " \"Principal\": {\n", 282 | " \"Service\": \"personalize.amazonaws.com\"\n", 283 | " },\n", 284 | " \"Action\": [\n", 285 | " \"s3:GetObject\",\n", 286 | " \"s3:ListBucket\"\n", 287 | " ],\n", 288 | " \"Resource\": [\n", 289 | " \"arn:aws:s3:::{}\".format(bucket),\n", 290 | " \"arn:aws:s3:::{}/*\".format(bucket)\n", 291 | " ]\n", 292 | " }\n", 293 | " ]\n", 294 | "}\n", 295 | "\n", 296 | "s3.put_bucket_policy(Bucket=bucket, Policy=json.dumps(policy));" 297 | ] 298 | }, 299 | { 300 | "cell_type": "markdown", 301 | "metadata": {}, 302 | "source": [ 303 | "#### Create Personalize S3 Role ARN\n", 304 | "\n", 305 | "We need to define an IAM role that gives Personalize the ability to access S3 buckets - this is needed as well as the S3 bucket policy." 306 | ] 307 | }, 308 | { 309 | "cell_type": "code", 310 | "execution_count": null, 311 | "metadata": {}, 312 | "outputs": [], 313 | "source": [ 314 | "iam = boto3.client(\"iam\")\n", 315 | "\n", 316 | "role_name = \"PersonalizeRole\"\n", 317 | "assume_role_policy_document = {\n", 318 | " \"Version\": \"2012-10-17\",\n", 319 | " \"Statement\": [\n", 320 | " {\n", 321 | " \"Effect\": \"Allow\",\n", 322 | " \"Principal\": {\n", 323 | " \"Service\": \"personalize.amazonaws.com\"\n", 324 | " },\n", 325 | " \"Action\": \"sts:AssumeRole\"\n", 326 | " }\n", 327 | " ]\n", 328 | "}\n", 329 | "\n", 330 | "create_role_response = iam.create_role(\n", 331 | " RoleName = role_name,\n", 332 | " AssumeRolePolicyDocument = json.dumps(assume_role_policy_document)\n", 333 | ")\n", 334 | "\n", 335 | "# AmazonPersonalizeFullAccess provides access to any S3 bucket with a name that includes\n", 336 | "# \"personalize\" or \"Personalize\". If you would like to use a bucket with a different name,\n", 337 | "# please consider creating and attaching a new policy that provides read access to your\n", 338 | "# bucket or attaching the AmazonS3ReadOnlyAccess policy to this role\n", 339 | "policy_arn = \"arn:aws:iam::aws:policy/service-role/AmazonPersonalizeFullAccess\"\n", 340 | "iam.attach_role_policy(\n", 341 | " RoleName = role_name,\n", 342 | " PolicyArn = policy_arn\n", 343 | ")\n", 344 | "\n", 345 | "time.sleep(60) # wait for a minute to allow IAM role policy attachment to propagate\n", 346 | "\n", 347 | "role_arn = create_role_response[\"Role\"][\"Arn\"]\n", 348 | "print(role_arn)" 349 | ] 350 | }, 351 | { 352 | "cell_type": "markdown", 353 | "metadata": {}, 354 | "source": [ 355 | "#### Create Dataset Import Job\n", 356 | "\n", 357 | "This pulls together the information that we have on our Dataset, on our S3 bucket, on our Interactions file and a suitable role for Personalize, and then triggers the actual data import process." 358 | ] 359 | }, 360 | { 361 | "cell_type": "code", 362 | "execution_count": null, 363 | "metadata": {}, 364 | "outputs": [], 365 | "source": [ 366 | "create_dataset_import_job_response = personalize.create_dataset_import_job(\n", 367 | " jobName = \"personalize-recs-dataset-import-job\",\n", 368 | " datasetArn = dataset_arn,\n", 369 | " dataSource = {\n", 370 | " \"dataLocation\": \"s3://{}/{}\".format(bucket, filename)\n", 371 | " },\n", 372 | " roleArn = role_arn\n", 373 | ")\n", 374 | "\n", 375 | "dataset_import_job_arn = create_dataset_import_job_response['datasetImportJobArn']\n", 376 | "print(json.dumps(create_dataset_import_job_response, indent=2))" 377 | ] 378 | }, 379 | { 380 | "cell_type": "markdown", 381 | "metadata": {}, 382 | "source": [ 383 | "#### Wait for Dataset Import Job and Dataset Import Job Run to Have ACTIVE Status\n", 384 | "\n", 385 | "We now poll the status of Interactions file import job, as until it is complete we cannot continue. **Note: this can take anything between 12-25 minutes to complete**" 386 | ] 387 | }, 388 | { 389 | "cell_type": "code", 390 | "execution_count": null, 391 | "metadata": {}, 392 | "outputs": [], 393 | "source": [ 394 | "status = None\n", 395 | "max_time = time.time() + 3*60*60 # 3 hours\n", 396 | "while time.time() < max_time:\n", 397 | " describe_dataset_import_job_response = personalize.describe_dataset_import_job(\n", 398 | " datasetImportJobArn = dataset_import_job_arn\n", 399 | " )\n", 400 | " \n", 401 | " dataset_import_job = describe_dataset_import_job_response[\"datasetImportJob\"]\n", 402 | " now = datetime.now(pytz.utc)\n", 403 | " elapsed = now - dataset_import_job[\"creationDateTime\"]\n", 404 | " if \"latestDatasetImportJobRun\" not in dataset_import_job:\n", 405 | " status = dataset_import_job[\"status\"]\n", 406 | " print(\"DatasetImportJob: {} (elapsed = {})\".format(status, elapsed))\n", 407 | " else:\n", 408 | " status = dataset_import_job[\"latestDatasetImportJobRun\"][\"status\"]\n", 409 | " print(\"LatestDatasetImportJobRun: {}\".format(status))\n", 410 | " \n", 411 | " if status == \"ACTIVE\" or status == \"CREATE FAILED\":\n", 412 | " break\n", 413 | " \n", 414 | " time.sleep(60)" 415 | ] 416 | }, 417 | { 418 | "cell_type": "markdown", 419 | "metadata": {}, 420 | "source": [ 421 | "### Select Recipe\n", 422 | "\n", 423 | "There are many different algorithm recipes available within Personalize, and this is a list of all supported algorithms. We are going to select the standard HRNN recipe, which only needs the Interactions file and not the Item metadata or User demographic files. Amazon Personalize has implemented a hierarchical recurrent neural network (HRNN), which is able to model the changes in user behavior.\n", 424 | "\n", 425 | "Temporal modeling is important in recommendation systems because user intent and interests tend to change or drift with time. This drifting is hard to model with traditional approaches. For example, a common approach in factorization machines is to manually come up with a discounting function for distant interactions (distance measured by time). Such manual engineering of weights is human-effort intensive and is prone to inaccuracy.\n", 426 | "\n", 427 | "The Amazon Personalize HRNN model, on the other hand, can take ordered user histories and make correct inferences. HRNN uses a gating mechanism to model the discount weights as a learnable function of the items and timestamp. The hierarchical component further improves temporal model efficiency and leads to higher accuracy." 428 | ] 429 | }, 430 | { 431 | "cell_type": "code", 432 | "execution_count": null, 433 | "metadata": {}, 434 | "outputs": [], 435 | "source": [ 436 | "recipe_list = [\n", 437 | " \"arn:aws:personalize:::recipe/aws-hrnn\",\n", 438 | " \"arn:aws:personalize:::recipe/aws-hrnn-coldstart\",\n", 439 | " \"arn:aws:personalize:::recipe/aws-hrnn-metadata\",\n", 440 | " \"arn:aws:personalize:::recipe/aws-personalized-ranking\",\n", 441 | " \"arn:aws:personalize:::recipe/aws-popularity-count\",\n", 442 | " \"arn:aws:personalize:::recipe/aws-sims\"\n", 443 | "]\n", 444 | "\n", 445 | "recipe_arn = recipe_list[0]\n", 446 | "print(recipe_arn)" 447 | ] 448 | }, 449 | { 450 | "cell_type": "markdown", 451 | "metadata": {}, 452 | "source": [ 453 | "### Create and Wait for Solution\n", 454 | "\n", 455 | "With our data imported we can now train our ML solution. This consists of just a single API to Personalise, where we specify the Dataset to use.\n", 456 | "\n", 457 | "#### Create Solution" 458 | ] 459 | }, 460 | { 461 | "cell_type": "code", 462 | "execution_count": null, 463 | "metadata": {}, 464 | "outputs": [], 465 | "source": [ 466 | "create_solution_response = personalize.create_solution(\n", 467 | " name = \"personalize-lab-recs-solution\",\n", 468 | " datasetGroupArn = dataset_group_arn,\n", 469 | " recipeArn = recipe_arn\n", 470 | ")\n", 471 | "\n", 472 | "solution_arn = create_solution_response['solutionArn']\n", 473 | "print(json.dumps(create_solution_response, indent=2))" 474 | ] 475 | }, 476 | { 477 | "cell_type": "markdown", 478 | "metadata": {}, 479 | "source": [ 480 | "#### Create Solution Version" 481 | ] 482 | }, 483 | { 484 | "cell_type": "code", 485 | "execution_count": null, 486 | "metadata": {}, 487 | "outputs": [], 488 | "source": [ 489 | "create_solution_version_response = personalize.create_solution_version(\n", 490 | " solutionArn = solution_arn\n", 491 | ")\n", 492 | "\n", 493 | "solution_version_arn = create_solution_version_response['solutionVersionArn']\n", 494 | "print(json.dumps(create_solution_version_response, indent=2))" 495 | ] 496 | }, 497 | { 498 | "cell_type": "markdown", 499 | "metadata": {}, 500 | "source": [ 501 | "#### Wait for Solution to Have ACTIVE Status\n", 502 | "\n", 503 | "We now poll the status of solution creation job, as until it is complete we cannot continue. **Note: this can take anything between 25-50 minutes to complete**" 504 | ] 505 | }, 506 | { 507 | "cell_type": "code", 508 | "execution_count": null, 509 | "metadata": {}, 510 | "outputs": [], 511 | "source": [ 512 | "status = None\n", 513 | "max_time = time.time() + 3*60*60 # 3 hours\n", 514 | "while time.time() < max_time:\n", 515 | " describe_solution_version_response = personalize.describe_solution_version(\n", 516 | " solutionVersionArn = solution_version_arn\n", 517 | " )\n", 518 | " status = describe_solution_version_response[\"solutionVersion\"][\"status\"]\n", 519 | " now = datetime.now(pytz.utc)\n", 520 | " elapsed = now - describe_solution_version_response[\"solutionVersion\"][\"creationDateTime\"]\n", 521 | " print(\"SolutionVersion: {} (elapsed = {})\".format(status, elapsed))\n", 522 | " \n", 523 | " if status == \"ACTIVE\" or status == \"CREATE FAILED\":\n", 524 | " break\n", 525 | " \n", 526 | " time.sleep(60)" 527 | ] 528 | }, 529 | { 530 | "cell_type": "markdown", 531 | "metadata": {}, 532 | "source": [ 533 | "#### Create Additional Solutions in the Console\n", 534 | "\n", 535 | "Whilst you're waiting for this to complete, jump back into the original Lab Guidebook - there we will walk you through creating two additional solutions in parallel using the same dataset; one for Personalized Rankings and one for Item-to-Item Similarities (or SIMS), both of which can be used in the final application. Once you've begun to create both additional solutions you can come back here and continue.\n", 536 | "\n", 537 | "#### Get Metrics of Solution\n", 538 | "\n", 539 | "Once the soluition is built you can look up the various metrics that Personalize provides - this allows you to see how well a model has been trained. If you are re-training models after the acquisition of new data then these metrics can tell you if the models are training equally as well as before, better than before or worse than before, giving you the information that you need in order to decide whether or not to push a new model into Production. You can also compare results across multiple different algorithm recipes, helping you choose the best performing one for you particular dataset.\n", 540 | "\n", 541 | "You can find details on each of the metrics in our [documentation](https://docs.aws.amazon.com/personalize/latest/dg/working-with-training-metrics.html)." 542 | ] 543 | }, 544 | { 545 | "cell_type": "code", 546 | "execution_count": null, 547 | "metadata": {}, 548 | "outputs": [], 549 | "source": [ 550 | "get_solution_metrics_response = personalize.get_solution_metrics(\n", 551 | " solutionVersionArn = solution_version_arn\n", 552 | ")\n", 553 | "\n", 554 | "print(json.dumps(get_solution_metrics_response, indent=2))" 555 | ] 556 | }, 557 | { 558 | "cell_type": "markdown", 559 | "metadata": {}, 560 | "source": [ 561 | "### Create and Wait for Campaign\n", 562 | "\n", 563 | "A trained model is exactly that - just a model. In order to use it you need to create an API endpoint, and you do this by creating a *Campaign*. A Campaign simply provides the endpoint for a specific version of your model, and as such you are able to host endpoint for multiple versions of your models simultaneously, allowing you to do things like A/B testing of new models.\n", 564 | "\n", 565 | "At the campaign level we specift the the minimum deployed size of the inference engine in terms of transactions per second - whilst this engine can scale up and down dynamically it will never scale below this level, but please note that pricing for Personalize is heavily based around the number of TPS currently deployed.\n", 566 | "\n", 567 | "#### Create campaign" 568 | ] 569 | }, 570 | { 571 | "cell_type": "code", 572 | "execution_count": null, 573 | "metadata": {}, 574 | "outputs": [], 575 | "source": [ 576 | "create_campaign_response = personalize.create_campaign(\n", 577 | " name = \"personalize-lab-recs-campaign\",\n", 578 | " solutionVersionArn = solution_version_arn,\n", 579 | " minProvisionedTPS = 1\n", 580 | ")\n", 581 | "\n", 582 | "campaign_arn = create_campaign_response['campaignArn']\n", 583 | "print(json.dumps(create_campaign_response, indent=2))" 584 | ] 585 | }, 586 | { 587 | "cell_type": "markdown", 588 | "metadata": {}, 589 | "source": [ 590 | "#### Wait for Campaign to Have ACTIVE Status\n", 591 | "\n", 592 | "We now poll the status of Campaign creation job, as until it is complete we cannot continue. **Note: this can take anything between 3-15 minutes to complete**" 593 | ] 594 | }, 595 | { 596 | "cell_type": "code", 597 | "execution_count": null, 598 | "metadata": {}, 599 | "outputs": [], 600 | "source": [ 601 | "status = None\n", 602 | "max_time = time.time() + 3*60*60 # 3 hours\n", 603 | "while time.time() < max_time:\n", 604 | " describe_campaign_response = personalize.describe_campaign(\n", 605 | " campaignArn = campaign_arn\n", 606 | " )\n", 607 | " status = describe_campaign_response[\"campaign\"][\"status\"]\n", 608 | " now = datetime.now(pytz.utc)\n", 609 | " elapsed = now - describe_campaign_response[\"campaign\"][\"creationDateTime\"]\n", 610 | " print(\"Campaign: {} (elapsed = {})\".format(status, elapsed))\n", 611 | " \n", 612 | " if status == \"ACTIVE\" or status == \"CREATE FAILED\":\n", 613 | " break\n", 614 | " \n", 615 | " time.sleep(30)" 616 | ] 617 | }, 618 | { 619 | "cell_type": "markdown", 620 | "metadata": {}, 621 | "source": [ 622 | "### Get Recommendations\n", 623 | "\n", 624 | "Finally, we have a deployed Campaign end-point, which hosts a specific version of our trained model - we are now able to make recommendation inference requests against it. However, the Personalize recommendation calls just return itemID values - it returns no context around the title of the movie, Hence, we use our pre-loaded version of the *u.item* file that contains the movie titles - we load in the file via **pandas** library calls and pick out a random userID and movieID from our training date. This info is displayed, along with the name of the movie.\n", 625 | "\n", 626 | "#### Select a User and an Item" 627 | ] 628 | }, 629 | { 630 | "cell_type": "code", 631 | "execution_count": null, 632 | "metadata": {}, 633 | "outputs": [], 634 | "source": [ 635 | "items = pd.read_csv('./u.item', sep='\\t', usecols=[0,1], header=None)\n", 636 | "items.columns = ['ITEM_ID', 'TITLE']\n", 637 | "\n", 638 | "user_id, item_id, _ = data.sample().values[0]\n", 639 | "item_title = items.loc[items['ITEM_ID'] == item_id].values[0][-1]\n", 640 | "print(\"USER: {}\".format(user_id))\n", 641 | "print(\"ITEM: {}\".format(item_title))\n", 642 | "\n", 643 | "items" 644 | ] 645 | }, 646 | { 647 | "cell_type": "markdown", 648 | "metadata": {}, 649 | "source": [ 650 | "#### Call GetRecommendations\n", 651 | "\n", 652 | "The last thing we do is actually make a Personalize recommendations inference call - as you can see from the Code cell this is literally a single line of code with a userID and itemID as input variables (although, strictly speaking, you only need the userID for the datasets that we have).\n", 653 | "\n", 654 | "The inference call returns a list of up to 25 itemIDs from the training set - we take that and look up the corresponding movie titles from the *u.item* file and display them; this is far more useful than just a list of ID values." 655 | ] 656 | }, 657 | { 658 | "cell_type": "code", 659 | "execution_count": null, 660 | "metadata": { 661 | "scrolled": true 662 | }, 663 | "outputs": [], 664 | "source": [ 665 | "get_recommendations_response = personalize_runtime.get_recommendations(\n", 666 | " campaignArn = campaign_arn,\n", 667 | " userId = str(user_id)\n", 668 | ")\n", 669 | "\n", 670 | "item_list = get_recommendations_response['itemList']\n", 671 | "title_list = [items.loc[items['ITEM_ID'] == np.int(item['itemId'])].values[0][-1] for item in item_list]\n", 672 | "\n", 673 | "print(\"Recommendations: {}\".format(json.dumps(title_list, indent=2)))" 674 | ] 675 | } 676 | ], 677 | "metadata": { 678 | "kernelspec": { 679 | "display_name": "conda_python3", 680 | "language": "python", 681 | "name": "conda_python3" 682 | }, 683 | "language_info": { 684 | "codemirror_mode": { 685 | "name": "ipython", 686 | "version": 3 687 | }, 688 | "file_extension": ".py", 689 | "mimetype": "text/x-python", 690 | "name": "python", 691 | "nbconvert_exporter": "python", 692 | "pygments_lexer": "ipython3", 693 | "version": "3.6.5" 694 | } 695 | }, 696 | "nbformat": 4, 697 | "nbformat_minor": 2 698 | } 699 | -------------------------------------------------------------------------------- /lab-7-Forecast_your_retail_sales/merged-forecast-with-actuals.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/drandrewkane/AI_ML_Workshops/436dcd67137d1fc0de5281746d5729eb163652a9/lab-7-Forecast_your_retail_sales/merged-forecast-with-actuals.xlsx --------------------------------------------------------------------------------