Skip to content

Commit

Permalink
deployment section
Browse files Browse the repository at this point in the history
  • Loading branch information
ariefrahmansyah committed Aug 30, 2024
1 parent c0d847d commit e107202
Show file tree
Hide file tree
Showing 21 changed files with 406 additions and 0 deletions.
1 change: 1 addition & 0 deletions code/deployment/fastapi/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.vercel
22 changes: 22 additions & 0 deletions code/deployment/fastapi/client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import requests
import tensorflow as tf
from tensorflow import keras

# Import the Fashion MNIST dataset
fashion_mnist = keras.datasets.fashion_mnist
(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()

# scale the values to 0.0 to 1.0
train_images = train_images / 255.0
test_images = test_images / 255.0

# reshape for feeding into the model
train_images = train_images.reshape(train_images.shape[0], 28, 28, 1)
test_images = test_images.reshape(test_images.shape[0], 28, 28, 1)


url = "http://localhost:8000"
req = tf.constant(test_images[:1], dtype=tf.float32)

resp = requests.post(f"{url}/predict", json={"instances": req.numpy().tolist()}).json()
print(resp)
49 changes: 49 additions & 0 deletions code/deployment/fastapi/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import numpy as np
import tensorflow as tf
from fastapi import FastAPI
from fastapi.responses import JSONResponse
from pydantic import BaseModel

class_names = [
"T-shirt/top",
"Trouser",
"Pullover",
"Dress",
"Coat",
"Sandal",
"Shirt",
"Sneaker",
"Bag",
"Ankle boot",
]


class PredictInput(BaseModel):
instances: list


app = FastAPI()

model = tf.keras.models.load_model("./model/1.keras")


@app.get("/")
def hello_world():
return "Hello, world!"


@app.post("/predict")
def predict(input: PredictInput):
instances = tf.constant(input.instances, dtype=tf.float32)
predictions = model(instances)
response = {
"predictions": predictions.numpy().tolist(),
"message": f"The model thought this was a {class_names[np.argmax(predictions)]} (class {np.argmax(predictions)})",
}
return JSONResponse(content=response)


if __name__ == "__main__":
import uvicorn

uvicorn.run(app)
Binary file added code/deployment/fastapi/model/1.keras
Binary file not shown.
51 changes: 51 additions & 0 deletions code/deployment/fastapi/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
absl-py==2.1.0
annotated-types==0.7.0
anyio==4.4.0
astunparse==1.6.3
certifi==2024.8.30
charset-normalizer==3.3.2
click==8.1.7
fastapi==0.112.2
flatbuffers==24.3.25
gast==0.6.0
google-pasta==0.2.0
grpcio==1.66.1
h11==0.14.0
h5py==3.11.0
idna==3.8
keras==3.5.0
libclang==18.1.1
Markdown==3.7
markdown-it-py==3.0.0
MarkupSafe==2.1.5
mdurl==0.1.2
ml-dtypes==0.4.0
namex==0.0.8
numpy==1.26.4
opt-einsum==3.3.0
optree==0.12.1
packaging==24.1
pandas==2.2.2
protobuf==4.25.4
pydantic==2.8.2
pydantic_core==2.20.1
Pygments==2.18.0
python-dateutil==2.9.0.post0
pytz==2024.1
requests==2.32.3
rich==13.8.0
setuptools==74.0.0
six==1.16.0
sniffio==1.3.1
starlette==0.38.2
tensorboard==2.17.1
tensorboard-data-server==0.7.2
tensorflow==2.17.0
termcolor==2.4.0
typing_extensions==4.12.2
tzdata==2024.1
urllib3==2.2.2
uvicorn==0.30.6
Werkzeug==3.0.4
wheel==0.44.0
wrapt==1.16.0
89 changes: 89 additions & 0 deletions code/deployment/fastapi/train.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import os

import tensorflow as tf
from tensorflow import keras

print("TensorFlow version: {}".format(tf.__version__))

# Import the Fashion MNIST dataset
fashion_mnist = keras.datasets.fashion_mnist
(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()

# scale the values to 0.0 to 1.0
train_images = train_images / 255.0
test_images = test_images / 255.0

# reshape for feeding into the model
train_images = train_images.reshape(train_images.shape[0], 28, 28, 1)
test_images = test_images.reshape(test_images.shape[0], 28, 28, 1)

class_names = [
"T-shirt/top",
"Trouser",
"Pullover",
"Dress",
"Coat",
"Sandal",
"Shirt",
"Sneaker",
"Bag",
"Ankle boot",
]

print("train_images.shape: {}, of {}".format(train_images.shape, train_images.dtype))
print("test_images.shape: {}, of {}".format(test_images.shape, test_images.dtype))

# Train and evaluate the model
model = keras.Sequential(
[
keras.layers.Conv2D(
input_shape=(28, 28, 1),
filters=8,
kernel_size=3,
strides=2,
activation="relu",
name="Conv1",
),
keras.layers.Flatten(),
keras.layers.Dense(10, name="Dense"),
]
)
model.summary()

testing = False
epochs = 5

model.compile(
optimizer="adam",
loss=tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True),
metrics=[keras.metrics.SparseCategoricalAccuracy()],
)
model.fit(train_images, train_labels, epochs=epochs)
print("Model trained!")

# Fetch the Keras session and save the model
MODEL_DIR = "model"
version = 1
model_format = ".keras"
model_path = os.path.join(MODEL_DIR, str(version) + model_format)
print("model_path = {}\n".format(model_path))

tf.keras.models.save_model(
model,
model_path,
overwrite=True,
include_optimizer=True,
save_format=None,
)
print("Model saved!")

# Test saved model
model = tf.keras.models.load_model(model_path)
print(model.summary())

sample_input = tf.constant(test_images[:1], dtype=tf.float32)
output = model(sample_input)
print(output)

test_loss, test_acc = model.evaluate(test_images, test_labels)
print("\nTest accuracy: {}".format(test_acc))
14 changes: 14 additions & 0 deletions code/deployment/fastapi/vercel.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"builds": [
{
"src": "main.py",
"use": "@vercel/python"
}
],
"routes": [
{
"src": "/(.*)",
"dest": "main.py"
}
]
}
2 changes: 2 additions & 0 deletions code/deployment/fasthtml/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.vercel
.sesskey
40 changes: 40 additions & 0 deletions code/deployment/fasthtml/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# FastHTML Boilerplate

Deploy your [FastHTML](https://fastht.ml/) project to Vercel with zero configuration.

[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https://github.com/vercel/vercel/tree/main/examples/fasthtml&template=fasthtml)

_Live Example: https://fasthtml-template.vercel.app_

Visit the [FastHTML documentation](https://docs.fastht.ml/) to learn more.

## Getting Started

Install the required dependencies:

```bash
pip install -r requirements.txt
```

## Running Locally

Start the development server on http://0.0.0.0:5001

```bash
python main.py
```

When you make changes to your project, the server will automatically reload.

## Deploying to Vercel

Deploy your project to Vercel with the following command:

```bash
npm install -g vercel
vercel --prod
```

Or `git push` to your repostory with our [git integration](https://vercel.com/docs/deployments/git).

To view the source code for this template, [visit the example repository](https://github.com/vercel/vercel/tree/main/examples/fasthtml).
85 changes: 85 additions & 0 deletions code/deployment/fasthtml/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
from os import getenv

from openai import OpenAI

from fasthtml.common import *

# Set up the app, including daisyui and tailwind for the chat component
hdrs = (
picolink,
Script(src="https://cdn.tailwindcss.com"),
Link(
rel="stylesheet",
href="https://cdn.jsdelivr.net/npm/daisyui@4.11.1/dist/full.min.css",
),
)
app = FastHTML(hdrs=hdrs, cls="p-4 max-w-lg mx-auto")

# Set up a chat model (https://claudette.answer.ai/)
client = OpenAI(
base_url="https://openrouter.ai/api/v1",
api_key=getenv("OPENROUTER_API_KEY"),
)
model = "mistralai/mistral-7b-instruct:free"


# Chat message component (renders a chat bubble)
def ChatMessage(msg, user):
bubble_class = "chat-bubble-primary" if user else "chat-bubble-secondary"
chat_class = "chat-end" if user else "chat-start"
return Div(cls=f"chat {chat_class}")(
Div("user" if user else "assistant", cls="chat-header"),
Div(msg, cls=f"chat-bubble {bubble_class}"),
Hidden(msg, name="messages"),
)


# The input field for the user message. Also used to clear the
# input field after sending a message via an OOB swap
def ChatInput():
return Input(
name="msg",
id="msg-input",
placeholder="Type a message",
cls="input input-bordered w-full",
hx_swap_oob="true",
)


# The main screen
@app.get
def index():
page = Form(hx_post=send, hx_target="#chatlist", hx_swap="beforeend")(
Div(id="chatlist", cls="chat-box h-[73vh] overflow-y-auto"),
Div(cls="flex space-x-2 mt-2")(
Group(ChatInput(), Button("Send", cls="btn btn-primary"))
),
)
return Titled("Chatbot Demo", page)


# Handle the form submission
@app.post
def send(msg: str, messages: list[str] = None):
if not messages:
messages = []
messages.append(msg.rstrip())
completion = client.chat.completions.create(
model=model,
messages=[
{
"role": "user",
"content": msg.rstrip(),
},
],
)
return (
ChatMessage(msg, True), # The user's message
ChatMessage(
completion.choices[0].message.content.rstrip(), False
), # The chatbot's response
ChatInput(),
) # And clear the input field via an OOB swap


serve()
Binary file added code/deployment/fasthtml/public/favicon.ico
Binary file not shown.
3 changes: 3 additions & 0 deletions code/deployment/fasthtml/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
openai==1.42.0
python-fasthtml==0.4.4
uvicorn==0.30.1
6 changes: 6 additions & 0 deletions docs/_toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,12 @@ parts:
- file: gen_ai/intro_to_llm
- file: gen_ai/local_llms

- caption: Deployment
chapters:
- file: deployment/intro_to_deployment
- file: deployment/deploy_python_apps
- file: deployment/deploy_ml_apps

- caption: Reference
chapters:
- file: reference/ai_domains
Loading

0 comments on commit e107202

Please sign in to comment.