├── .gitignore
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── THIRD-PARTY-LICENSES
├── cdk
├── cdk.json
├── index.ts
├── package.json
└── tsconfig.json
├── lambda
└── main.py
└── notebook
├── lambda_efs_ml_demo.ipynb
└── utils.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode,node
2 | # Edit at https://www.gitignore.io/?templates=osx,linux,python,windows,pycharm,visualstudiocode,node
3 |
4 | ### Linux ###
5 | *~
6 |
7 | # temporary files which can be created if a process still has a handle open of a deleted file
8 | .fuse_hidden*
9 |
10 | # KDE directory preferences
11 | .directory
12 |
13 | # Linux trash folder which might appear on any partition or disk
14 | .Trash-*
15 |
16 | # .nfs files are created when an open file is removed but is still being accessed
17 | .nfs*
18 |
19 | ### Node ###
20 | # Logs
21 | logs
22 | *.log
23 | npm-debug.log*
24 | yarn-debug.log*
25 | yarn-error.log*
26 | lerna-debug.log*
27 |
28 | # Diagnostic reports (https://nodejs.org/api/report.html)
29 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
30 |
31 | # Runtime data
32 | pids
33 | *.pid
34 | *.seed
35 | *.pid.lock
36 |
37 | # Directory for instrumented libs generated by jscoverage/JSCover
38 | lib-cov
39 |
40 | # Coverage directory used by tools like istanbul
41 | coverage
42 | *.lcov
43 |
44 | # nyc test coverage
45 | .nyc_output
46 |
47 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
48 | .grunt
49 |
50 | # Bower dependency directory (https://bower.io/)
51 | bower_components
52 |
53 | # node-waf configuration
54 | .lock-wscript
55 |
56 | # Compiled binary addons (https://nodejs.org/api/addons.html)
57 | build/Release
58 |
59 | # Dependency directories
60 | node_modules/
61 | jspm_packages/
62 |
63 | # TypeScript v1 declaration files
64 | typings/
65 |
66 | # TypeScript cache
67 | *.tsbuildinfo
68 |
69 | # Optional npm cache directory
70 | .npm
71 |
72 | # Optional eslint cache
73 | .eslintcache
74 |
75 | # Optional REPL history
76 | .node_repl_history
77 |
78 | # Output of 'npm pack'
79 | *.tgz
80 |
81 | # Yarn Integrity file
82 | .yarn-integrity
83 |
84 | # dotenv environment variables file
85 | .env
86 | .env.test
87 |
88 | # parcel-bundler cache (https://parceljs.org/)
89 | .cache
90 |
91 | # next.js build output
92 | .next
93 |
94 | # nuxt.js build output
95 | .nuxt
96 |
97 | # vuepress build output
98 | .vuepress/dist
99 |
100 | # Serverless directories
101 | .serverless/
102 |
103 | # FuseBox cache
104 | .fusebox/
105 |
106 | # DynamoDB Local files
107 | .dynamodb/
108 |
109 | ### OSX ###
110 | # General
111 | .DS_Store
112 | .AppleDouble
113 | .LSOverride
114 |
115 | # Icon must end with two \r
116 | Icon
117 |
118 | # Thumbnails
119 | ._*
120 |
121 | # Files that might appear in the root of a volume
122 | .DocumentRevisions-V100
123 | .fseventsd
124 | .Spotlight-V100
125 | .TemporaryItems
126 | .Trashes
127 | .VolumeIcon.icns
128 | .com.apple.timemachine.donotpresent
129 |
130 | # Directories potentially created on remote AFP share
131 | .AppleDB
132 | .AppleDesktop
133 | Network Trash Folder
134 | Temporary Items
135 | .apdisk
136 |
137 | ### PyCharm ###
138 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
139 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
140 |
141 | # User-specific stuff
142 | .idea/**/workspace.xml
143 | .idea/**/tasks.xml
144 | .idea/**/usage.statistics.xml
145 | .idea/**/dictionaries
146 | .idea/**/shelf
147 |
148 | # Generated files
149 | .idea/**/contentModel.xml
150 |
151 | # Sensitive or high-churn files
152 | .idea/**/dataSources/
153 | .idea/**/dataSources.ids
154 | .idea/**/dataSources.local.xml
155 | .idea/**/sqlDataSources.xml
156 | .idea/**/dynamic.xml
157 | .idea/**/uiDesigner.xml
158 | .idea/**/dbnavigator.xml
159 |
160 | # Gradle
161 | .idea/**/gradle.xml
162 | .idea/**/libraries
163 |
164 | # Gradle and Maven with auto-import
165 | # When using Gradle or Maven with auto-import, you should exclude module files,
166 | # since they will be recreated, and may cause churn. Uncomment if using
167 | # auto-import.
168 | .idea/*.xml
169 | .idea/*.iml
170 | .idea
171 | # .idea/modules
172 | # *.iml
173 | # *.ipr
174 |
175 | # CMake
176 | cmake-build-*/
177 |
178 | # Mongo Explorer plugin
179 | .idea/**/mongoSettings.xml
180 |
181 | # File-based project format
182 | *.iws
183 |
184 | # IntelliJ
185 | out/
186 |
187 | # mpeltonen/sbt-idea plugin
188 | .idea_modules/
189 |
190 | # JIRA plugin
191 | atlassian-ide-plugin.xml
192 |
193 | # Cursive Clojure plugin
194 | .idea/replstate.xml
195 |
196 | # Crashlytics plugin (for Android Studio and IntelliJ)
197 | com_crashlytics_export_strings.xml
198 | crashlytics.properties
199 | crashlytics-build.properties
200 | fabric.properties
201 |
202 | # Editor-based Rest Client
203 | .idea/httpRequests
204 |
205 | # Android studio 3.1+ serialized cache file
206 | .idea/caches/build_file_checksums.ser
207 |
208 | ### PyCharm Patch ###
209 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
210 |
211 | # *.iml
212 | # modules.xml
213 | # .idea/misc.xml
214 | # *.ipr
215 |
216 | # Sonarlint plugin
217 | .idea/sonarlint
218 |
219 | ### Python ###
220 | # Byte-compiled / optimized / DLL files
221 | __pycache__/
222 | *.py[cod]
223 | *$py.class
224 |
225 | # C extensions
226 | *.so
227 |
228 | # Distribution / packaging
229 | .Python
230 | build/
231 | develop-eggs/
232 | dist/
233 | downloads/
234 | eggs/
235 | .eggs/
236 | lib64/
237 | parts/
238 | sdist/
239 | var/
240 | wheels/
241 | pip-wheel-metadata/
242 | share/python-wheels/
243 | *.egg-info/
244 | .installed.cfg
245 | *.egg
246 | MANIFEST
247 |
248 | # PyInstaller
249 | # Usually these files are written by a python script from a template
250 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
251 | *.manifest
252 | *.spec
253 |
254 | # Installer logs
255 | pip-log.txt
256 | pip-delete-this-directory.txt
257 |
258 | # Unit test / coverage reports
259 | htmlcov/
260 | .tox/
261 | .nox/
262 | .coverage
263 | .coverage.*
264 | nosetests.xml
265 | coverage.xml
266 | *.cover
267 | .hypothesis/
268 | .pytest_cache/
269 |
270 | # Translations
271 | *.mo
272 | *.pot
273 |
274 | # Django stuff:
275 | local_settings.py
276 | db.sqlite3
277 | db.sqlite3-journal
278 |
279 | # Flask stuff:
280 | instance/
281 | .webassets-cache
282 |
283 | # Scrapy stuff:
284 | .scrapy
285 |
286 | # Sphinx documentation
287 | docs/_build/
288 |
289 | # PyBuilder
290 | target/
291 |
292 | # Jupyter Notebook
293 | .ipynb_checkpoints
294 |
295 | # IPython
296 | profile_default/
297 | ipython_config.py
298 |
299 | # pyenv
300 | .python-version
301 |
302 | # pipenv
303 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
304 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
305 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
306 | # install all needed dependencies.
307 | #Pipfile.lock
308 |
309 | # celery beat schedule file
310 | celerybeat-schedule
311 |
312 | # SageMath parsed files
313 | *.sage.py
314 |
315 | # Environments
316 | .venv
317 | env/
318 | venv/
319 | ENV/
320 | env.bak/
321 | venv.bak/
322 |
323 | # Spyder project settings
324 | .spyderproject
325 | .spyproject
326 |
327 | # Rope project settings
328 | .ropeproject
329 |
330 | # mkdocs documentation
331 | /site
332 |
333 | # mypy
334 | .mypy_cache/
335 | .dmypy.json
336 | dmypy.json
337 |
338 | # Pyre type checker
339 | .pyre/
340 |
341 | ### VisualStudioCode ###
342 | .vscode
343 |
344 | ### VisualStudioCode Patch ###
345 | # Ignore all local history of files
346 | .history
347 |
348 | ### Windows ###
349 | # Windows thumbnail cache files
350 | Thumbs.db
351 | Thumbs.db:encryptable
352 | ehthumbs.db
353 | ehthumbs_vista.db
354 |
355 | # Dump file
356 | *.stackdump
357 |
358 | # Folder config file
359 | [Dd]esktop.ini
360 |
361 | # Recycle Bin used on file shares
362 | $RECYCLE.BIN/
363 |
364 | # Windows Installer files
365 | *.cab
366 | *.msi
367 | *.msix
368 | *.msm
369 | *.msp
370 |
371 | # Windows shortcuts
372 | *.lnk
373 |
374 | # End of https://www.gitignore.io/api/osx,linux,python,windows,pycharm,visualstudiocode,node
375 |
376 | ### CDK-specific ignores ###
377 | *.swp
378 | cdk.context.json
379 | package-lock.json
380 | .cdk.staging
381 | cdk.out
382 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | ## Code of Conduct
2 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
3 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
4 | opensource-codeofconduct@amazon.com with any additional questions or comments.
5 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guidelines
2 |
3 | Thank you for your interest in contributing to our project. Whether it's a bug report, new feature, correction, or additional
4 | documentation, we greatly value feedback and contributions from our community.
5 |
6 | Please read through this document before submitting any issues or pull requests to ensure we have all the necessary
7 | information to effectively respond to your bug report or contribution.
8 |
9 |
10 | ## Reporting Bugs/Feature Requests
11 |
12 | We welcome you to use the GitHub issue tracker to report bugs or suggest features.
13 |
14 | When filing an issue, please check existing open, or recently closed, issues to make sure somebody else hasn't already
15 | reported the issue. Please try to include as much information as you can. Details like these are incredibly useful:
16 |
17 | * A reproducible test case or series of steps
18 | * The version of our code being used
19 | * Any modifications you've made relevant to the bug
20 | * Anything unusual about your environment or deployment
21 |
22 |
23 | ## Contributing via Pull Requests
24 | Contributions via pull requests are much appreciated. Before sending us a pull request, please ensure that:
25 |
26 | 1. You are working against the latest source on the *master* branch.
27 | 2. You check existing open, and recently merged, pull requests to make sure someone else hasn't addressed the problem already.
28 | 3. You open an issue to discuss any significant work - we would hate for your time to be wasted.
29 |
30 | To send us a pull request, please:
31 |
32 | 1. Fork the repository.
33 | 2. Modify the source; please focus on the specific change you are contributing. If you also reformat all the code, it will be hard for us to focus on your change.
34 | 3. Ensure local tests pass.
35 | 4. Commit to your fork using clear commit messages.
36 | 5. Send us a pull request, answering any default questions in the pull request interface.
37 | 6. Pay attention to any automated CI failures reported in the pull request, and stay involved in the conversation.
38 |
39 | GitHub provides additional document on [forking a repository](https://help.github.com/articles/fork-a-repo/) and
40 | [creating a pull request](https://help.github.com/articles/creating-a-pull-request/).
41 |
42 |
43 | ## Finding contributions to work on
44 | Looking at the existing issues is a great way to find something to contribute on. As our projects, by default, use the default GitHub issue labels (enhancement/bug/duplicate/help wanted/invalid/question/wontfix), looking at any 'help wanted' issues is a great place to start.
45 |
46 |
47 | ## Code of Conduct
48 | This project has adopted the [Amazon Open Source Code of Conduct](https://aws.github.io/code-of-conduct).
49 | For more information see the [Code of Conduct FAQ](https://aws.github.io/code-of-conduct-faq) or contact
50 | opensource-codeofconduct@amazon.com with any additional questions or comments.
51 |
52 |
53 | ## Security issue notifications
54 | If you discover a potential security issue in this project we ask that you notify AWS/Amazon Security via our [vulnerability reporting page](http://aws.amazon.com/security/vulnerability-reporting/). Please do **not** create a public github issue.
55 |
56 |
57 | ## Licensing
58 |
59 | See the [LICENSE](LICENSE) file for our project's licensing. We will ask you to confirm the licensing of your contribution.
60 |
61 | We may ask you to sign a [Contributor License Agreement (CLA)](http://en.wikipedia.org/wiki/Contributor_License_Agreement) for larger changes.
62 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7 | the Software, and to permit persons to whom the Software is furnished to do so.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
10 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
11 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
12 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
13 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
14 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Deep Learning inference with AWS Lambda and Amazon EFS
2 |
3 | ## Introduction
4 |
5 | [Amazon EFS support for AWS Lambda](https://aws.amazon.com/about-aws/whats-new/2020/06/aws-lambda-support-for-amazon-elastic-file-system-now-generally-/?nc1=h_ls) enables storing large deep learning (DL) framework libraries and models on Amazon EFS and load from Lambda.
6 |
7 | This repository shows and example of how to use such capability to host a pre-trained Tensorflow 2 object detection model (SSD) from TensorFlow Hub and execute inferences.
8 |
9 | Using EFS and Lambda for deep learning inference requires to execute two steps:
10 |
11 | 1. Storing the deep learning libraries and model on EFS
12 | 2. Creating a Lambda function for inference, which loads the libraries and model from the EFS file system
13 |
14 | The steps above are executed by deploying the project via the [AWS CDK](https://aws.amazon.com/cdk/) and then using [AWS CodeBuild](https://aws.amazon.com/codebuild/) for installing the required libraries to EFS.
15 |
16 | ## Running the example
17 |
18 | To run this example:
19 |
20 | ```
21 | # Install the AWS CDK and bootstrap the target account (if this was never done before)
22 | $ npm install -g aws-cdk
23 | $ cdk bootstrap aws://{account_id}/{region}
24 |
25 | ```
26 |
27 | ```
28 | # clone repository
29 | $ git clone https://github.com/giuseppeporcelli/lambda-efs-ml-demo.git
30 | $ cd lambda-efs-ml-demo
31 | ```
32 |
33 | ```
34 | # Install packages for the project, build and deploy
35 | $ cd cdk/
36 | $ npm install
37 | $ npm run build
38 | $ cdk deploy
39 | ```
40 |
41 | After deployment, note the output:
42 |
43 | ```
44 | Outputs:
45 | LambdaEFSMLDemo.LambdaFunctionName =
46 | LambdaEFSMLDemo-LambdaEFSMLExecuteInference17332C2-0546aa45dfXXXXXX
47 | ```
48 |
49 | It takes a few minutes for AWS CodeBuild to deploy the libraries and framework to EFS. To test the Lambda function, run this command, replacing the function name:
50 |
51 | ```
52 | $ aws lambda invoke \
53 | --function-name LambdaEFSMLDemo-LambdaEFSMLExecuteInference17332C2-0546aa45dfXXXXXX \
54 | --region us-east-1 \
55 | --cli-binary-format raw-in-base64-out \
56 | --payload '{"url": "https://images.pexels.com/photos/310983/pexels-photo-310983.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940"}' \
57 | --region us-east-1 \
58 | /tmp/return.json
59 | ```
60 |
61 | This is the output:
62 |
63 | ```
64 | {
65 | "StatusCode": 200,
66 | "ExecutedVersion": "$LATEST"
67 | }
68 | ```
69 | Here you can check the inference’s result:
70 |
71 | ```
72 | $ tail /tmp/return.json
73 |
74 | {"statusCode": 200, "body": "{\"detection_boxes\": [[0.4908023476600647, 0.29575252532958984,
75 | 0.9392691254615784, 0.7548272609710693], [0.2890659272670746, 0.44450390338897705,
76 | 0.8515050411224365, 0.6895579099655151], [0.700944721698761, 0.5776023864746094,
77 | 0.9346526265144348, 0.763191819190979], [0.6840880513191223, 0.3130854070186615,
78 | 0.9327453970909119, 0.4774819314479828], [0.6840880513191223, 0.3130854070186615,
79 | 0.9327453970909119, 0.4774819314479828], [0.700944721698761, 0.5776023864746094,
80 | 0.9346526265144348, 0.763191819190979], [0.2874983847141266, 0.4483768939971924,
81 | 0.8595395088195801, 0.6886227130889893], [0.7860985994338989, 0.06227143853902817,
82 | 0.8351912498474121, 0.1968214511871338], [0.7888965010643005, 0.5248408913612366,
83 | 0.861912190914154, 0.5945348143577576], ...
84 |
85 | ```
86 |
87 | Additionally, you can use the Jupyter notebook in the [notebook](./notebook) folder to print bounding boxes on images.
88 |
89 | ### Destroy the stack
90 |
91 | ```
92 | # Afterwards, you can destroy the stack with
93 | $ cdk destroy
94 | ```
95 |
96 | ## Security
97 |
98 | See [CONTRIBUTING](CONTRIBUTING.md#security-issue-notifications) for more information.
99 |
100 | ## License
101 |
102 | The contents of this repository are licensed under the [MIT-0 License](./LICENSE).
103 |
104 | Third party content is licensed under the [Apache-2.0 License](./THIRD-PARTY-LICENSES).
105 |
106 | ## Authors
107 |
108 | [Giuseppe A. Porcelli](https://it.linkedin.com/in/giuporcelli) - Principal, ML Specialist Solutions Architect - Amazon Web Services EMEA
109 | [Diego Natali](https://www.linkedin.com/in/diego-natali) - Sr. Solutions Architect - Amazon Web Services EMEA
110 |
--------------------------------------------------------------------------------
/THIRD-PARTY-LICENSES:
--------------------------------------------------------------------------------
1 | ** notebook/utils.py
2 |
3 | Apache License
4 | Version 2.0, January 2004
5 | http://www.apache.org/licenses/
6 |
7 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
8 |
9 | 1. Definitions.
10 |
11 | "License" shall mean the terms and conditions for use, reproduction,
12 | and distribution as defined by Sections 1 through 9 of this document.
13 |
14 | "Licensor" shall mean the copyright owner or entity authorized by
15 | the copyright owner that is granting the License.
16 |
17 | "Legal Entity" shall mean the union of the acting entity and all
18 | other entities that control, are controlled by, or are under common
19 | control with that entity. For the purposes of this definition,
20 | "control" means (i) the power, direct or indirect, to cause the
21 | direction or management of such entity, whether by contract or
22 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
23 | outstanding shares, or (iii) beneficial ownership of such entity.
24 |
25 | "You" (or "Your") shall mean an individual or Legal Entity
26 | exercising permissions granted by this License.
27 |
28 | "Source" form shall mean the preferred form for making modifications,
29 | including but not limited to software source code, documentation
30 | source, and configuration files.
31 |
32 | "Object" form shall mean any form resulting from mechanical
33 | transformation or translation of a Source form, including but
34 | not limited to compiled object code, generated documentation,
35 | and conversions to other media types.
36 |
37 | "Work" shall mean the work of authorship, whether in Source or
38 | Object form, made available under the License, as indicated by a
39 | copyright notice that is included in or attached to the work
40 | (an example is provided in the Appendix below).
41 |
42 | "Derivative Works" shall mean any work, whether in Source or Object
43 | form, that is based on (or derived from) the Work and for which the
44 | editorial revisions, annotations, elaborations, or other modifications
45 | represent, as a whole, an original work of authorship. For the purposes
46 | of this License, Derivative Works shall not include works that remain
47 | separable from, or merely link (or bind by name) to the interfaces of,
48 | the Work and Derivative Works thereof.
49 |
50 | "Contribution" shall mean any work of authorship, including
51 | the original version of the Work and any modifications or additions
52 | to that Work or Derivative Works thereof, that is intentionally
53 | submitted to Licensor for inclusion in the Work by the copyright owner
54 | or by an individual or Legal Entity authorized to submit on behalf of
55 | the copyright owner. For the purposes of this definition, "submitted"
56 | means any form of electronic, verbal, or written communication sent
57 | to the Licensor or its representatives, including but not limited to
58 | communication on electronic mailing lists, source code control systems,
59 | and issue tracking systems that are managed by, or on behalf of, the
60 | Licensor for the purpose of discussing and improving the Work, but
61 | excluding communication that is conspicuously marked or otherwise
62 | designated in writing by the copyright owner as "Not a Contribution."
63 |
64 | "Contributor" shall mean Licensor and any individual or Legal Entity
65 | on behalf of whom a Contribution has been received by Licensor and
66 | subsequently incorporated within the Work.
67 |
68 | 2. Grant of Copyright License. Subject to the terms and conditions of
69 | this License, each Contributor hereby grants to You a perpetual,
70 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
71 | copyright license to reproduce, prepare Derivative Works of,
72 | publicly display, publicly perform, sublicense, and distribute the
73 | Work and such Derivative Works in Source or Object form.
74 |
75 | 3. Grant of Patent License. Subject to the terms and conditions of
76 | this License, each Contributor hereby grants to You a perpetual,
77 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
78 | (except as stated in this section) patent license to make, have made,
79 | use, offer to sell, sell, import, and otherwise transfer the Work,
80 | where such license applies only to those patent claims licensable
81 | by such Contributor that are necessarily infringed by their
82 | Contribution(s) alone or by combination of their Contribution(s)
83 | with the Work to which such Contribution(s) was submitted. If You
84 | institute patent litigation against any entity (including a
85 | cross-claim or counterclaim in a lawsuit) alleging that the Work
86 | or a Contribution incorporated within the Work constitutes direct
87 | or contributory patent infringement, then any patent licenses
88 | granted to You under this License for that Work shall terminate
89 | as of the date such litigation is filed.
90 |
91 | 4. Redistribution. You may reproduce and distribute copies of the
92 | Work or Derivative Works thereof in any medium, with or without
93 | modifications, and in Source or Object form, provided that You
94 | meet the following conditions:
95 |
96 | (a) You must give any other recipients of the Work or
97 | Derivative Works a copy of this License; and
98 |
99 | (b) You must cause any modified files to carry prominent notices
100 | stating that You changed the files; and
101 |
102 | (c) You must retain, in the Source form of any Derivative Works
103 | that You distribute, all copyright, patent, trademark, and
104 | attribution notices from the Source form of the Work,
105 | excluding those notices that do not pertain to any part of
106 | the Derivative Works; and
107 |
108 | (d) If the Work includes a "NOTICE" text file as part of its
109 | distribution, then any Derivative Works that You distribute must
110 | include a readable copy of the attribution notices contained
111 | within such NOTICE file, excluding those notices that do not
112 | pertain to any part of the Derivative Works, in at least one
113 | of the following places: within a NOTICE text file distributed
114 | as part of the Derivative Works; within the Source form or
115 | documentation, if provided along with the Derivative Works; or,
116 | within a display generated by the Derivative Works, if and
117 | wherever such third-party notices normally appear. The contents
118 | of the NOTICE file are for informational purposes only and
119 | do not modify the License. You may add Your own attribution
120 | notices within Derivative Works that You distribute, alongside
121 | or as an addendum to the NOTICE text from the Work, provided
122 | that such additional attribution notices cannot be construed
123 | as modifying the License.
124 |
125 | You may add Your own copyright statement to Your modifications and
126 | may provide additional or different license terms and conditions
127 | for use, reproduction, or distribution of Your modifications, or
128 | for any such Derivative Works as a whole, provided Your use,
129 | reproduction, and distribution of the Work otherwise complies with
130 | the conditions stated in this License.
131 |
132 | 5. Submission of Contributions. Unless You explicitly state otherwise,
133 | any Contribution intentionally submitted for inclusion in the Work
134 | by You to the Licensor shall be under the terms and conditions of
135 | this License, without any additional terms or conditions.
136 | Notwithstanding the above, nothing herein shall supersede or modify
137 | the terms of any separate license agreement you may have executed
138 | with Licensor regarding such Contributions.
139 |
140 | 6. Trademarks. This License does not grant permission to use the trade
141 | names, trademarks, service marks, or product names of the Licensor,
142 | except as required for reasonable and customary use in describing the
143 | origin of the Work and reproducing the content of the NOTICE file.
144 |
145 | 7. Disclaimer of Warranty. Unless required by applicable law or
146 | agreed to in writing, Licensor provides the Work (and each
147 | Contributor provides its Contributions) on an "AS IS" BASIS,
148 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
149 | implied, including, without limitation, any warranties or conditions
150 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
151 | PARTICULAR PURPOSE. You are solely responsible for determining the
152 | appropriateness of using or redistributing the Work and assume any
153 | risks associated with Your exercise of permissions under this License.
154 |
155 | 8. Limitation of Liability. In no event and under no legal theory,
156 | whether in tort (including negligence), contract, or otherwise,
157 | unless required by applicable law (such as deliberate and grossly
158 | negligent acts) or agreed to in writing, shall any Contributor be
159 | liable to You for damages, including any direct, indirect, special,
160 | incidental, or consequential damages of any character arising as a
161 | result of this License or out of the use or inability to use the
162 | Work (including but not limited to damages for loss of goodwill,
163 | work stoppage, computer failure or malfunction, or any and all
164 | other commercial damages or losses), even if such Contributor
165 | has been advised of the possibility of such damages.
166 |
167 | 9. Accepting Warranty or Additional Liability. While redistributing
168 | the Work or Derivative Works thereof, You may choose to offer,
169 | and charge a fee for, acceptance of support, warranty, indemnity,
170 | or other liability obligations and/or rights consistent with this
171 | License. However, in accepting such obligations, You may act only
172 | on Your own behalf and on Your sole responsibility, not on behalf
173 | of any other Contributor, and only if You agree to indemnify,
174 | defend, and hold each Contributor harmless for any liability
175 | incurred by, or claims asserted against, such Contributor by reason
176 | of your accepting any such warranty or additional liability.
177 |
178 | END OF TERMS AND CONDITIONS
179 |
180 | APPENDIX: How to apply the Apache License to your work.
181 |
182 | To apply the Apache License to your work, attach the following
183 | boilerplate notice, with the fields enclosed by brackets "[]"
184 | replaced with your own identifying information. (Don't include
185 | the brackets!) The text should be enclosed in the appropriate
186 | comment syntax for the file format. We also recommend that a
187 | file or class name and description of purpose be included on the
188 | same "printed page" as the copyright notice for easier
189 | identification within third-party archives.
190 |
191 | Copyright [yyyy] [name of copyright owner]
192 |
193 | Licensed under the Apache License, Version 2.0 (the "License");
194 | you may not use this file except in compliance with the License.
195 | You may obtain a copy of the License at
196 |
197 | http://www.apache.org/licenses/LICENSE-2.0
198 |
199 | Unless required by applicable law or agreed to in writing, software
200 | distributed under the License is distributed on an "AS IS" BASIS,
201 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
202 | See the License for the specific language governing permissions and
203 | limitations under the License.
--------------------------------------------------------------------------------
/cdk/cdk.json:
--------------------------------------------------------------------------------
1 | {
2 | "app": "node index"
3 | }
--------------------------------------------------------------------------------
/cdk/index.ts:
--------------------------------------------------------------------------------
1 | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | // SPDX-License-Identifier: MIT-0
3 |
4 | import cdk = require('@aws-cdk/core');
5 | import ec2 = require('@aws-cdk/aws-ec2');
6 | import iam = require('@aws-cdk/aws-iam');
7 | import efs = require('@aws-cdk/aws-efs');
8 | import codebuild = require('@aws-cdk/aws-codebuild');
9 | import cr = require('@aws-cdk/custom-resources');
10 | import lambda = require('@aws-cdk/aws-lambda');
11 | import path = require('path');
12 | import { Arn, Size, RemovalPolicy } from '@aws-cdk/core';
13 |
14 | interface LambdaEFSMLStackProps extends cdk.StackProps {
15 | readonly installPackages?: string;
16 | }
17 |
18 | export class LambdaEFSMLStack extends cdk.Stack {
19 | constructor(scope: cdk.App, id: string, props: LambdaEFSMLStackProps) {
20 | super(scope, id, props);
21 |
22 | // VPC definition.
23 | const vpc = new ec2.Vpc(this, 'LambdaEFSMLVPC', {
24 | maxAzs: 2,
25 | natGateways: 1,
26 | });
27 |
28 | // Security Group definitions.
29 | const ec2SecurityGroup = new ec2.SecurityGroup(this, 'LambdaEFSMLEC2SG', {
30 | vpc,
31 | securityGroupName: "LambdaEFSMLEC2SG",
32 | });
33 |
34 | const lambdaSecurityGroup = new ec2.SecurityGroup(this, 'LambdaEFSMLLambdaSG', {
35 | vpc,
36 | securityGroupName: "LambdaEFSMLLambdaSG",
37 | });
38 |
39 | const efsSecurityGroup = new ec2.SecurityGroup(this, 'LambdaEFSMLEFSSG', {
40 | vpc,
41 | securityGroupName: "LambdaEFSMLEFSSG",
42 | });
43 |
44 | ec2SecurityGroup.connections.allowTo(efsSecurityGroup, ec2.Port.tcp(2049));
45 | lambdaSecurityGroup.connections.allowTo(efsSecurityGroup, ec2.Port.tcp(2049));
46 |
47 | // Elastic File System file system.
48 | // For the purpose of cost saving, provisioned troughput has been kept low.
49 | const fs = new efs.FileSystem(this, 'LambdaEFSMLEFS', {
50 | vpc: vpc,
51 | securityGroup: efsSecurityGroup,
52 | throughputMode: efs.ThroughputMode.PROVISIONED,
53 | provisionedThroughputPerSecond: Size.mebibytes(10),
54 | removalPolicy: RemovalPolicy.DESTROY
55 | });
56 |
57 | const EfsAccessPoint = new efs.AccessPoint(this, 'EfsAccessPoint', {
58 | fileSystem: fs,
59 | path: '/lambda',
60 | posixUser: {
61 | gid: '1000',
62 | uid: '1000'
63 | },
64 | createAcl: {
65 | ownerGid: '1000',
66 | ownerUid: '1000',
67 | permissions: '777'
68 | }
69 | })
70 |
71 | // Lambda function to execute inference.
72 | const executeInferenceFunction = new lambda.Function(this, 'LambdaEFSMLExecuteInference', {
73 | runtime: lambda.Runtime.PYTHON_3_8,
74 | handler: 'main.lambda_handler',
75 | code: lambda.Code.fromAsset(path.join(__dirname, '..', 'lambda')),
76 | vpc,
77 | vpcSubnets: vpc.selectSubnets({ subnetType: ec2.SubnetType.PRIVATE }),
78 | securityGroup: lambdaSecurityGroup,
79 | timeout: cdk.Duration.minutes(2),
80 | memorySize: 3008,
81 | reservedConcurrentExecutions: 10,
82 | filesystem: lambda.FileSystem.fromEfsAccessPoint(EfsAccessPoint, '/mnt/python')
83 | });
84 | executeInferenceFunction.role?.addManagedPolicy(iam.ManagedPolicy.fromAwsManagedPolicyName("AmazonElasticFileSystemClientFullAccess"));
85 |
86 | // Leveraging on AWS CodeBuild to install Python libraries to EFS.
87 | const codeBuildProject = new codebuild.Project(this, 'LambdaEFSMLCodeBuildProject', {
88 | projectName: "LambdaEFSMLCodeBuildProject",
89 | description: "Installs Python libraries to EFS.",
90 | vpc,
91 | buildSpec: codebuild.BuildSpec.fromObject({
92 | version: '0.1',
93 | phases: {
94 | build: {
95 | commands: [
96 | 'echo "Downloading and copying model..."',
97 | 'mkdir -p $CODEBUILD_EFS1/lambda/model',
98 | 'curl https://storage.googleapis.com/tfhub-modules/google/openimages_v4/ssd/mobilenet_v2/1.tar.gz --output /tmp/1.tar.gz',
99 | 'tar zxf /tmp/1.tar.gz -C $CODEBUILD_EFS1/lambda/model',
100 | 'echo "Installing virtual environment..."',
101 | 'mkdir -p $CODEBUILD_EFS1/lambda',
102 | 'python3 -m venv $CODEBUILD_EFS1/lambda/tensorflow',
103 | 'echo "Installing Tensorflow..."',
104 | 'source $CODEBUILD_EFS1/lambda/tensorflow/bin/activate && pip3 install ' +
105 | (props.installPackages ? props.installPackages : "tensorflow"),
106 | 'echo "Changing folder permissions..."',
107 | 'chown -R 1000:1000 $CODEBUILD_EFS1/lambda/'
108 | ]
109 | }
110 | },
111 | }),
112 |
113 | environment: {
114 | buildImage: codebuild.LinuxBuildImage.fromDockerRegistry('lambci/lambda:build-python3.8'),
115 | computeType: codebuild.ComputeType.LARGE,
116 | privileged: true,
117 | },
118 | securityGroups: [ec2SecurityGroup],
119 | subnetSelection: vpc.selectSubnets({ subnetType: ec2.SubnetType.PRIVATE }),
120 | timeout: cdk.Duration.minutes(30),
121 | });
122 |
123 | // Configure EFS for CodeBuild.
124 | const cfnProject = codeBuildProject.node.defaultChild as codebuild.CfnProject;
125 | cfnProject.fileSystemLocations = [{
126 | type: "EFS",
127 | //location: fs.mountTargetsAvailable + ".efs." + cdk.Stack.of(this).region + ".amazonaws.com:/",
128 | location: fs.fileSystemId + ".efs." + cdk.Stack.of(this).region + ".amazonaws.com:/",
129 | mountPoint: "/mnt/python",
130 | identifier: "efs1",
131 | mountOptions: "nfsvers=4.1,rsize=1048576,wsize=1048576,hard,timeo=600,retrans=2"
132 | }]
133 | cfnProject.logsConfig = {
134 | cloudWatchLogs: {
135 | status: "ENABLED"
136 | }
137 | }
138 |
139 | // Triggers the CodeBuild project to install the python packages and model to the EFS file system
140 | const triggerBuildProject = new cr.AwsCustomResource(this, 'TriggerCodeBuild', {
141 | onCreate: {
142 | service: 'CodeBuild',
143 | action: 'startBuild',
144 | parameters: {
145 | projectName: codeBuildProject.projectName
146 | },
147 | physicalResourceId: cr.PhysicalResourceId.fromResponse('build.id'),
148 | },
149 | policy: cr.AwsCustomResourcePolicy.fromSdkCalls({ resources: cr.AwsCustomResourcePolicy.ANY_RESOURCE })
150 | });
151 |
152 | // Create dependenct between EFS and Codebuild
153 | codeBuildProject.node.addDependency(EfsAccessPoint);
154 |
155 | // Output Lambda function name.
156 | new cdk.CfnOutput(this, 'LambdaFunctionName', { value: executeInferenceFunction.functionName });
157 | }
158 | }
159 |
160 | const app = new cdk.App();
161 |
162 | var props: LambdaEFSMLStackProps = {
163 | installPackages: undefined,
164 | env: {
165 | region: 'us-east-1'
166 | }
167 | }
168 |
169 | new LambdaEFSMLStack(app, 'LambdaEFSMLDemo', props);
170 | app.synth();
171 |
--------------------------------------------------------------------------------
/cdk/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "lambda-efs-ml-demo",
3 | "version": "1.0.0",
4 | "description": "Host Machine Learning models with AWS Lambda and Amazon EFS",
5 | "private": true,
6 | "scripts": {
7 | "build": "tsc",
8 | "watch": "tsc -w",
9 | "cdk": "cdk"
10 | },
11 | "author": {
12 | "name": "Amazon Web Services",
13 | "url": "https://aws.amazon.com",
14 | "organization": true
15 | },
16 | "license": "Apache-2.0",
17 | "devDependencies": {
18 | "@types/node": "^8.10.40",
19 | "typescript": "~3.7.2",
20 | "aws-cdk": "*"
21 | },
22 | "dependencies": {
23 | "@aws-cdk/core": "*",
24 | "@aws-cdk/aws-ec2": "*",
25 | "@aws-cdk/aws-iam": "*",
26 | "@aws-cdk/aws-efs": "*",
27 | "@aws-cdk/aws-codebuild": "*",
28 | "@aws-cdk/custom-resources": "*",
29 | "@aws-cdk/aws-cloudformation": "*",
30 | "@aws-cdk/aws-lambda": "*",
31 | "source-map-support": "^0.5.9"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/cdk/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target":"ES2018",
4 | "module": "commonjs",
5 | "lib": ["es2016", "es2017.object", "es2017.string"],
6 | "strict": true,
7 | "noImplicitAny": true,
8 | "strictNullChecks": true,
9 | "noImplicitThis": true,
10 | "alwaysStrict": true,
11 | "noUnusedLocals": false,
12 | "noUnusedParameters": false,
13 | "noImplicitReturns": true,
14 | "noFallthroughCasesInSwitch": false,
15 | "inlineSourceMap": true,
16 | "inlineSources": true,
17 | "experimentalDecorators": true,
18 | "strictPropertyInitialization":false
19 | },
20 | "exclude": ["cdk.out"]
21 | }
--------------------------------------------------------------------------------
/lambda/main.py:
--------------------------------------------------------------------------------
1 | # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2 | # SPDX-License-Identifier: MIT-0
3 |
4 | import sys
5 | import os
6 |
7 | # Setting library paths.
8 | efs_path = "/mnt/python"
9 | python_pkg_path = os.path.join(efs_path, "tensorflow/lib/python3.8/site-packages")
10 | sys.path.append(python_pkg_path)
11 |
12 | import json
13 | import string
14 | import time
15 | import io
16 | import requests
17 |
18 | # Importing TensorFlow
19 | import tensorflow as tf
20 |
21 | # Loading model
22 | model_path = os.path.join(efs_path, 'model/')
23 | loaded_model = tf.saved_model.load(model_path)
24 | detector = loaded_model.signatures['default']
25 |
26 | def lambda_handler(event, context):
27 | r = requests.get(event['url'])
28 | img = tf.image.decode_jpeg(r.content, channels=3)
29 |
30 | # Executing inference.
31 | converted_img = tf.image.convert_image_dtype(img, tf.float32)[tf.newaxis, ...]
32 | start_time = time.time()
33 | result = detector(converted_img)
34 | end_time = time.time()
35 |
36 | obj = {
37 | 'detection_boxes' : result['detection_boxes'].numpy().tolist(),
38 | 'detection_scores': result['detection_scores'].numpy().tolist(),
39 | 'detection_class_entities': [el.decode('UTF-8') for el in result['detection_class_entities'].numpy()]
40 | }
41 |
42 | return {
43 | 'statusCode': 200,
44 | 'body': json.dumps(obj)
45 | }
--------------------------------------------------------------------------------
/notebook/utils.py:
--------------------------------------------------------------------------------
1 | # The following code is licensed under: https://www.apache.org/licenses/LICENSE-2.0
2 | # and has been adapted from the TensorFlow object detection tutorial at:
3 | # https://www.tensorflow.org/hub/tutorials/object_detection
4 |
5 | # For downloading the image.
6 | import matplotlib.pyplot as plt
7 | import tempfile
8 | from six.moves.urllib.request import Request
9 | from six.moves.urllib.request import urlopen
10 | from six import BytesIO
11 |
12 | # For drawing onto the image.
13 | import numpy as np
14 | import tensorflow as tf
15 | from PIL import Image
16 | from PIL import ImageColor
17 | from PIL import ImageDraw
18 | from PIL import ImageFont
19 | from PIL import ImageOps
20 |
21 | def load_img(path):
22 | img = tf.io.read_file(path)
23 | img = tf.image.decode_jpeg(img, channels=3)
24 | return img
25 |
26 | def display_image(image):
27 | fig = plt.figure(figsize=(15, 12))
28 | plt.grid(False)
29 | plt.imshow(image)
30 |
31 | def download_and_resize_image(url, new_width=256, new_height=256,
32 | display=False):
33 | _, filename = tempfile.mkstemp(suffix=".jpg")
34 |
35 | req = Request(url, headers={'User-Agent': 'Mozilla/5.0'})
36 |
37 | response = urlopen(req)
38 | image_data = response.read()
39 | image_data = BytesIO(image_data)
40 | pil_image = Image.open(image_data)
41 | pil_image = ImageOps.fit(pil_image, (new_width, new_height), Image.ANTIALIAS)
42 | pil_image_rgb = pil_image.convert("RGB")
43 | pil_image_rgb.save(filename, format="JPEG", quality=90)
44 | print("Image downloaded to %s." % filename)
45 | if display:
46 | display_image(pil_image)
47 | return filename
48 |
49 |
50 | def draw_bounding_box_on_image(image,
51 | ymin,
52 | xmin,
53 | ymax,
54 | xmax,
55 | color,
56 | font,
57 | thickness=2,
58 | display_str_list=()):
59 | """Adds a bounding box to an image."""
60 | draw = ImageDraw.Draw(image)
61 | im_width, im_height = image.size
62 | (left, right, top, bottom) = (xmin * im_width, xmax * im_width,
63 | ymin * im_height, ymax * im_height)
64 | draw.line([(left, top), (left, bottom), (right, bottom), (right, top),
65 | (left, top)],
66 | width=thickness,
67 | fill=color)
68 |
69 | # If the total height of the display strings added to the top of the bounding
70 | # box exceeds the top of the image, stack the strings below the bounding box
71 | # instead of above.
72 | display_str_heights = [font.getsize(ds)[1] for ds in display_str_list]
73 | # Each display_str has a top and bottom margin of 0.05x.
74 | total_display_str_height = (1 + 2 * 0.05) * sum(display_str_heights)
75 |
76 | if top > total_display_str_height:
77 | text_bottom = top
78 | else:
79 | text_bottom = top + total_display_str_height
80 | # Reverse list and print from bottom to top.
81 | for display_str in display_str_list[::-1]:
82 | text_width, text_height = font.getsize(display_str)
83 | margin = np.ceil(0.05 * text_height)
84 | draw.rectangle([(left, text_bottom - text_height - 2 * margin),
85 | (left + text_width, text_bottom)],
86 | fill=color)
87 | draw.text((left + margin, text_bottom - text_height - margin),
88 | display_str,
89 | fill="black",
90 | font=font)
91 | text_bottom -= text_height - 2 * margin
92 |
93 |
94 | def draw_boxes(image, boxes, class_names, scores, max_boxes=10, min_score=0.1):
95 | """Overlay labeled boxes on an image with formatted scores and label names."""
96 | colors = list(ImageColor.colormap.values())
97 |
98 | try:
99 | font = ImageFont.truetype("/usr/share/fonts/dejavu/DejaVuSansMono.ttf",
100 | 12)
101 | except IOError:
102 | print("Font not found, using default font.")
103 | font = ImageFont.load_default()
104 |
105 | for i in range(min(boxes.shape[0], max_boxes)):
106 | if scores[i] >= min_score:
107 | ymin, xmin, ymax, xmax = tuple(boxes[i])
108 | display_str = "{}: {}%".format(class_names[i],
109 | int(100 * scores[i]))
110 | color = colors[hash(class_names[i]) % len(colors)]
111 | image_pil = Image.fromarray(np.uint8(image)).convert("RGB")
112 | draw_bounding_box_on_image(
113 | image_pil,
114 | ymin,
115 | xmin,
116 | ymax,
117 | xmax,
118 | color,
119 | font,
120 | display_str_list=[display_str])
121 | np.copyto(image, np.array(image_pil))
122 | return image
--------------------------------------------------------------------------------