import%20marimo%0A%0A__generated_with%20%3D%20%220.18.3%22%0Aapp%20%3D%20marimo.App(width%3D%22medium%22)%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20import%20marimo%20as%20mo%0A%20%20%20%20import%20onnxruntime%20as%20ort%0A%20%20%20%20import%20numpy%20as%20np%0A%20%20%20%20from%20torchvision%20import%20transforms%0A%20%20%20%20from%20keras_image_helper%20import%20create_preprocessor%0A%20%20%20%20return%20create_preprocessor%2C%20mo%2C%20np%2C%20ort%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%20Machine%20Learning%20Zoomcamp%0A%0A%20%20%20%20%23%23%20Module%209%3A%20**Deploy%20with%20AWS%20Lambda**%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Homework%0A%0A%20%20%20%20In%20this%20homework%2C%20we'll%20deploy%20the%20Straight%20vs%20Curly%20Hair%20Type%20model%20we%20trained%20in%20the%0A%20%20%20%20%5Bprevious%20homework%5D(..%2F08-deep-learning%2Fhomework.md).%0A%0A%20%20%20%20Download%20the%20model%20files%20from%20here%3A%0A%0A%20%20%20%20*%20https%3A%2F%2Fgithub.com%2Falexeygrigorev%2Flarge-datasets%2Freleases%2Fdownload%2Fhairstyle%2Fhair_classifier_v1.onnx.data%0A%20%20%20%20*%20https%3A%2F%2Fgithub.com%2Falexeygrigorev%2Flarge-datasets%2Freleases%2Fdownload%2Fhairstyle%2Fhair_classifier_v1.onnx%0A%0A%20%20%20%20With%20wget%3A%0A%0A%20%20%20%20%60%60%60bash%0A%20%20%20%20PREFIX%3D%22https%3A%2F%2Fgithub.com%2Falexeygrigorev%2Flarge-datasets%2Freleases%2Fdownload%2Fhairstyle%22%0A%20%20%20%20DATA_URL%3D%22%24%7BPREFIX%7D%2Fhair_classifier_v1.onnx.data%22%0A%20%20%20%20MODEL_URL%3D%22%24%7BPREFIX%7D%2Fhair_classifier_v1.onnx%22%0A%20%20%20%20wget%20%24%7BDATA_URL%7D%0A%20%20%20%20wget%20%24%7BMODEL_URL%7D%0A%20%20%20%20%60%60%60%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Question%201%0A%0A%20%20%20%20To%20be%20able%20to%20use%20this%20model%2C%20we%20need%20to%20know%20the%20name%20of%20the%20input%20and%20output%20nodes.%0A%0A%20%20%20%20What's%20the%20name%20of%20the%20output%3A%0A%0A%20%20%20%20*%20%60output%60%0A%20%20%20%20*%20%60sigmoid%60%0A%20%20%20%20*%20%60softmax%60%0A%20%20%20%20*%20%60prediction%60%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(ort)%3A%0A%20%20%20%20session%20%3D%20ort.InferenceSession(%22module-9%2Fhomework%2Fhair_classifier_v1.onnx%22)%0A%0A%20%20%20%20input_name%20%3D%20session.get_inputs()%5B0%5D.name%0A%20%20%20%20output_name%20%3D%20session.get_outputs()%5B0%5D.name%0A%0A%20%20%20%20(input_name%2C%20output_name)%0A%20%20%20%20return%20input_name%2C%20output_name%2C%20session%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20The%20name%20of%20the%20output%20is%20**output**.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Preparing%20the%20image%0A%0A%20%20%20%20You'll%20need%20some%20code%20for%20downloading%20and%20resizing%20images.%20You%20can%20use%0A%20%20%20%20this%20code%3A%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_()%3A%0A%20%20%20%20from%20io%20import%20BytesIO%0A%20%20%20%20from%20urllib%20import%20request%0A%20%20%20%20from%20PIL%20import%20Image%0A%0A%20%20%20%20def%20download_image(url)%3A%0A%20%20%20%20%20%20%20%20with%20request.urlopen(url)%20as%20resp%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20buffer%20%3D%20resp.read()%0A%20%20%20%20%20%20%20%20stream%20%3D%20BytesIO(buffer)%0A%20%20%20%20%20%20%20%20img%20%3D%20Image.open(stream)%0A%20%20%20%20%20%20%20%20return%20img%0A%0A%0A%20%20%20%20def%20prepare_image(img%2C%20target_size)%3A%0A%20%20%20%20%20%20%20%20if%20img.mode%20!%3D%20'RGB'%3A%0A%20%20%20%20%20%20%20%20%20%20%20%20img%20%3D%20img.convert('RGB')%0A%20%20%20%20%20%20%20%20img%20%3D%20img.resize(target_size%2C%20Image.NEAREST)%0A%20%20%20%20%20%20%20%20return%20img%0A%20%20%20%20return%20download_image%2C%20prepare_image%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Question%202%3A%20Target%20size%0A%0A%20%20%20%20Let's%20download%20and%20resize%20this%20image%3A%0A%0A%20%20%20%20https%3A%2F%2Fhabrastorage.org%2Fwebt%2Fyf%2F_d%2Fok%2Fyf_dokzqy3vcritme8ggnzqlvwa.jpeg%0A%0A%20%20%20%20Based%20on%20the%20previous%20homework%2C%20what%20should%20be%20the%20target%20size%20for%20the%20image%3F%0A%0A%20%20%20%20*%2064x64%0A%20%20%20%20*%20128x128%0A%20%20%20%20*%20200x200%0A%20%20%20%20*%20256x256%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(download_image%2C%20prepare_image)%3A%0A%20%20%20%20image%20%3D%20download_image(%22https%3A%2F%2Fhabrastorage.org%2Fwebt%2Fyf%2F_d%2Fok%2Fyf_dokzqy3vcritme8ggnzqlvwa.jpeg%22)%0A%20%20%20%20prepared_image%20%3D%20prepare_image(image%2C%20(200%2C%20200))%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20The%20target%20size%20for%20the%20image%20is%20**200x200**.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Question%203%0A%0A%20%20%20%20Now%20we%20need%20to%20turn%20the%20image%20into%20numpy%20array%20and%20pre-process%20it.%0A%0A%20%20%20%20%3E%20Tip%3A%20Check%20the%20previous%20homework.%20What%20was%20the%20pre-processing%0A%20%20%20%20%3E%20we%20did%20there%3F%0A%0A%20%20%20%20After%20the%20pre-processing%2C%20what's%20the%20value%20in%20the%20first%20pixel%2C%20the%20R%20channel%3F%0A%0A%20%20%20%20*%20-10.73%0A%20%20%20%20*%20-1.073%0A%20%20%20%20*%201.073%0A%20%20%20%20*%2010.73%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(create_preprocessor%2C%20np)%3A%0A%20%20%20%20def%20preprocess_pytorch(X)%3A%0A%20%20%20%20%20%20%20%20%23%20X%3A%20shape%20(1%2C%20299%2C%20299%2C%203)%2C%20dtype%3Dfloat32%2C%20values%20in%20%5B0%2C%20255%5D%0A%20%20%20%20%20%20%20%20X%20%3D%20X%20%2F%20255.0%0A%0A%20%20%20%20%20%20%20%20mean%20%3D%20np.array(%5B0.485%2C%200.456%2C%200.406%5D).reshape(1%2C%203%2C%201%2C%201)%0A%20%20%20%20%20%20%20%20std%20%3D%20np.array(%5B0.229%2C%200.224%2C%200.225%5D).reshape(1%2C%203%2C%201%2C%201)%0A%0A%20%20%20%20%20%20%20%20%23%20Convert%20NHWC%20%E2%86%92%20NCHW%0A%20%20%20%20%20%20%20%20%23%20from%20(batch%2C%20height%2C%20width%2C%20channels)%20%E2%86%92%20(batch%2C%20channels%2C%20height%2C%20width)%0A%20%20%20%20%20%20%20%20X%20%3D%20X.transpose(0%2C%203%2C%201%2C%202)%0A%0A%20%20%20%20%20%20%20%20%23%20Normalize%0A%20%20%20%20%20%20%20%20X%20%3D%20(X%20-%20mean)%20%2F%20std%0A%0A%20%20%20%20%20%20%20%20return%20X.astype(np.float32)%0A%0A%0A%20%20%20%20preprocessor%20%3D%20create_preprocessor(preprocess_pytorch%2C%20target_size%3D(200%2C%20200))%0A%0A%20%20%20%20X%20%3D%20preprocessor.from_url(%22https%3A%2F%2Fhabrastorage.org%2Fwebt%2Fyf%2F_d%2Fok%2Fyf_dokzqy3vcritme8ggnzqlvwa.jpeg%22)%0A%20%20%20%20X%5B0%5D%5B0%5D%5B0%5D%5B0%5D%0A%20%20%20%20return%20(preprocessor%2C)%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20The%20value%20of%20the%20R%20channel%20of%20the%20firs%20pixel%20is%20**-1.073**.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Question%204%0A%0A%20%20%20%20Now%20let's%20apply%20this%20model%20to%20this%20image.%20What's%20the%20output%20of%20the%20model%3F%0A%0A%20%20%20%20*%200.09%0A%20%20%20%20*%200.49%0A%20%20%20%20*%200.69%0A%20%20%20%20*%200.89%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell%0Adef%20_(input_name%2C%20output_name%2C%20preprocessor%2C%20session)%3A%0A%20%20%20%20classes%20%3D%20%5B%0A%20%20%20%20%20%20%20%20'dress'%2C%0A%20%20%20%20%20%20%20%20'hat'%2C%0A%20%20%20%20%20%20%20%20'longsleeve'%2C%0A%20%20%20%20%20%20%20%20'outwear'%2C%0A%20%20%20%20%20%20%20%20'pants'%2C%0A%20%20%20%20%20%20%20%20'shirt'%2C%0A%20%20%20%20%20%20%20%20'shoes'%2C%0A%20%20%20%20%20%20%20%20'shorts'%2C%0A%20%20%20%20%20%20%20%20'skirt'%2C%0A%20%20%20%20%20%20%20%20't-shirt'%0A%20%20%20%20%5D%0A%0A%20%20%20%20def%20predict(url)%3A%0A%20%20%20%20%20%20%20%20X%20%3D%20preprocessor.from_url(url)%0A%20%20%20%20%20%20%20%20result%20%3D%20session.run(%5Boutput_name%5D%2C%20%7Binput_name%3A%20X%7D)%0A%20%20%20%20%20%20%20%20float_predictions%20%3D%20result%5B0%5D%5B0%5D.tolist()%0A%20%20%20%20%20%20%20%20return%20dict(zip(classes%2C%20float_predictions))%0A%0A%20%20%20%20predict(%22https%3A%2F%2Fhabrastorage.org%2Fwebt%2Fyf%2F_d%2Fok%2Fyf_dokzqy3vcritme8ggnzqlvwa.jpeg%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Prepare%20the%20lambda%20code%0A%0A%20%20%20%20Now%20you%20need%20to%20copy%20all%20the%20code%20into%20a%20separate%20python%20file.%20You%20will%0A%20%20%20%20need%20to%20use%20this%20file%20for%20the%20next%20two%20questions.%0A%0A%20%20%20%20Tip%3A%20you%20can%20test%20this%20file%20locally%20with%20%60ipython%60%20or%20Jupyter%20Notebook%0A%20%20%20%20by%20importing%20the%20file%20and%20invoking%20the%20function%20from%20this%20file.%0A%0A%0A%20%20%20%20%23%23%20Docker%0A%0A%20%20%20%20For%20the%20next%20two%20questions%2C%20we'll%20use%20a%20Docker%20image%20that%20we%20already%0A%20%20%20%20prepared.%20This%20is%20the%20Dockerfile%20that%20we%20used%20for%20creating%20the%20image%3A%0A%0A%20%20%20%20%60%60%60docker%0A%20%20%20%20FROM%20public.ecr.aws%2Flambda%2Fpython%3A3.13%0A%0A%20%20%20%20COPY%20hair_classifier_empty.onnx.data%20.%0A%20%20%20%20COPY%20hair_classifier_empty.onnx%20.%0A%20%20%20%20%60%60%60%0A%0A%20%20%20%20Note%20that%20it%20uses%20Python%203.13.%0A%0A%20%20%20%20The%20docker%20image%20is%20published%20to%20%5B%60agrigorev%2Fmodel-2025-hairstyle%3Av1%60%5D(https%3A%2F%2Fhub.docker.com%2Fr%2Fagrigorev%2Fmodel-2025-hairstyle).%0A%0A%20%20%20%20A%20few%20notes%3A%0A%0A%20%20%20%20*%20The%20image%20already%20contains%20a%20model%20and%20it's%20not%20the%20same%20model%0A%20%20%20%20%20%20as%20the%20one%20we%20used%20for%20questions%201-4.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Question%205%0A%0A%20%20%20%20Download%20the%20base%20image%20%60agrigorev%2Fmodel-2025-hairstyle%3Av1%60.%20You%20can%20do%20it%20with%20%5B%60docker%20pull%60%5D(https%3A%2F%2Fdocs.docker.com%2Fengine%2Freference%2Fcommandline%2Fpull%2F).%0A%0A%20%20%20%20So%20what's%20the%20size%20of%20this%20base%20image%3F%0A%0A%20%20%20%20*%2088%20Mb%0A%20%20%20%20*%20208%20Mb%0A%20%20%20%20*%20608%20Mb%0A%20%20%20%20*%201208%20Mb%0A%0A%20%20%20%20You%20can%20get%20this%20information%20when%20running%20%60docker%20images%60%20-%20it'll%20be%20in%20the%20%22SIZE%22%20column.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%60%60%60bash%0A%20%20%20%20docker%20pull%20agrigorev%2Fmodel-2025-hairstyle%3Av1%0A%20%20%20%20docker%20image%20ls%20%7C%20grep%20agrigorev%2Fmodel-2025-hairstyle%0A%20%20%20%20%60%60%60%0A%0A%20%20%20%20%3E%20agrigorev%2Fmodel-2025-hairstyle%20-%20v1%20-%204528ad1525d5%20-%206%20days%20ago%20-%20608MB%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20The%20size%20of%20the%20downloaded%20image%20is%20**608%20Mb**.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Question%206%0A%0A%20%20%20%20Now%20let's%20extend%20this%20docker%20image%2C%20install%20all%20the%20required%20libraries%0A%20%20%20%20and%20add%20the%20code%20for%20lambda.%0A%0A%20%20%20%20You%20don't%20need%20to%20include%20the%20model%20in%20the%20image.%20It's%20already%20included.%0A%20%20%20%20The%20name%20of%20the%20file%20with%20the%20model%20is%20%60hair_classifier_empty.onnx%60%20and%20it's%0A%20%20%20%20in%20the%20current%20workdir%20in%20the%20image%20(see%20the%20Dockerfile%20above%20for%20the%0A%20%20%20%20reference).%0A%20%20%20%20The%20provided%20model%20requires%20the%20same%20preprocessing%20for%20images%20regarding%20target%20size%20and%20rescaling%20the%20value%20range%20than%20used%20in%20homework%208.%0A%0A%20%20%20%20Now%20run%20the%20container%20locally.%0A%0A%20%20%20%20Score%20this%20image%3A%20https%3A%2F%2Fhabrastorage.org%2Fwebt%2Fyf%2F_d%2Fok%2Fyf_dokzqy3vcritme8ggnzqlvwa.jpeg%0A%0A%20%20%20%20What's%20the%20output%20from%20the%20model%3F%0A%0A%20%20%20%20*%20-1.0%0A%20%20%20%20*%20-0.10%0A%20%20%20%20*%200.10%0A%20%20%20%20*%201.0%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20The%20result%20is%200.09%20which%20is%20almost%20**0.10**.%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0A%40app.cell(hide_code%3DTrue)%0Adef%20_(mo)%3A%0A%20%20%20%20mo.md(r%22%22%22%0A%20%20%20%20%23%23%20Publishing%20it%20to%20AWS%0A%0A%20%20%20%20Now%20you%20can%20deploy%20your%20model%20to%20AWS!%0A%0A%20%20%20%20*%20Publish%20your%20image%20to%20ECR%0A%20%20%20%20*%20Create%20a%20lambda%20function%20in%20AWS%2C%20use%20the%20ECR%20image%0A%20%20%20%20*%20Give%20it%20more%20RAM%20and%20increase%20the%20timeout%0A%20%20%20%20*%20Test%20it%0A%20%20%20%20*%20Expose%20the%20lambda%20function%20using%20API%20Gateway%0A%0A%20%20%20%20This%20is%20optional%20and%20not%20graded.%0A%0A%0A%20%20%20%20%23%23%20Submit%20the%20results%0A%0A%20%20%20%20*%20Submit%20your%20results%20here%3A%20https%3A%2F%2Fcourses.datatalks.club%2Fml-zoomcamp-2025%2Fhomework%2Fhw09%0A%20%20%20%20*%20If%20your%20answer%20doesn't%20match%20options%20exactly%2C%20select%20the%20closest%20one.%20If%20the%20answer%20is%20exactly%20in%20between%20two%20options%2C%20select%20the%20higher%20value.%0A%0A%20%20%20%20%23%23%20Publishing%20to%20Docker%20hub%0A%0A%20%20%20%20Just%20for%20the%20reference%2C%20this%20is%20how%20we%20published%20our%20image%20to%20Docker%20hub%3A%0A%0A%20%20%20%20%60%60%60bash%0A%20%20%20%20docker%20build%20-t%20model-2025-hairstyle%20-f%20homework.dockerfile%20.%0A%20%20%20%20docker%20tag%20model-2025-hairstyle%3Alatest%20agrigorev%2Fmodel-2025-hairstyle%3Av1%0A%20%20%20%20docker%20push%20agrigorev%2Fmodel-2025-hairstyle%3Av1%0A%20%20%20%20%60%60%60%0A%0A%20%20%20%20(You%20don't%20need%20to%20execute%20this%20code)%0A%20%20%20%20%22%22%22)%0A%20%20%20%20return%0A%0A%0Aif%20__name__%20%3D%3D%20%22__main__%22%3A%0A%20%20%20%20app.run()%0A
2c963bcca04083647c62effe99506ba8