Skip to content

Commit e4ac3cb

Browse files
committed
updated readme (added templating system), swapped demos to use built-in algorithm-python path; renamed serve() to init()
1 parent 2263178 commit e4ac3cb

File tree

11 files changed

+258
-30
lines changed

11 files changed

+258
-30
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,4 @@ dmypy.json
128128
# Pyre type checker
129129
.pyre/
130130
.idea
131+
node_modules/

README.md

Lines changed: 179 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,28 @@
11
# Algorithm Development Kit (ADK), Python edition
22

3+
<!-- embedme examples/hello_world/src/Algorithm.py -->
34
```python
4-
import Algorithmia
5-
from adk import ADK
5+
from Algorithmia import ADK
66

77

88
# API calls will begin at the apply() method, with the request body passed as 'input'
99
# For more details, see algorithmia.com/developers/algorithm-development/languages
1010

1111
def apply(input):
12+
# If your apply function uses state that's loaded into memory via load, you can pass that loaded state to your apply
13+
# function by defining an additional "state" parameter in your apply function; but it's optional!
1214
return "hello {}".format(str(input))
1315

1416

15-
algo = ADK(apply)
16-
algo.serve("Algorithmia")
17-
```
17+
# This turns your library code into an algorithm that can run on the platform.
18+
# If you intend to use loading operations, remember to pass a `load` function as a second variable.
19+
algorithm = ADK(apply)
20+
# The 'serve()' function actually starts the algorithm, you can follow along in the source code
21+
# to see how everything works.
22+
algorithm.init("Algorithmia")
1823

24+
```
25+
## Focus
1926
This document will describe the following:
2027
- What is an Algorithm Development Kit
2128
- Changes to Algorithm development
@@ -37,22 +44,182 @@ This kit, when implemented by an algorithm developer - enables an easy way to ge
3744

3845
Algorithm development does change with this introduction:
3946
- Primary development file has been renamed to `src/Algorithm.py` to aide in understanding around what this file actually does / why it's important
40-
- An additional import (`from adk import ADK`)
47+
- An additional import (`from algorithm import ADK`)
4148
- An optional `load()` function that can be implemented
4249
- This enables a dedicated function for preparing your algorithm for runtime operations, such as model loading, configuration, etc
4350
- A call to the handler function with your `apply` and optional` load` functions as inputs
4451
- ```python
45-
algo = ADK(apply)
46-
algo.serve("Algorithmia")
52+
algorithm = ADK(apply)
53+
algorithm.init("Algorithmia")
4754
```
4855
- Converts the project into an executable, rather than a library
4956
- Which will interact with the `langserver` service on Algorithmia
5057
- But is debuggable via stdin/stdout when executed locally / outside of an Algorithm container
51-
- When a payload is provided to `serve()`, that payload will be directly provided to your algorithm when executed locally, bypassing stdin parsing and simplifying debugging!
58+
- When a payload is provided to `init()`, that payload will be directly provided to your algorithm when executed locally, bypassing stdin parsing and simplifying debugging!
5259
- This includes being able to step through your algorithm code in your IDE of choice! Just execute your `src/Algorithm.py` script and try stepping through your code with your favorite IDE
5360

5461
## Example workflows
5562
Check out these examples to help you get started:
56-
- [hello world example](examples/hello_world)
57-
- [hello world example with loaded state](examples/loaded_state_hello_world)
58-
- [pytorch based image classification](examples/pytorch_image_classification)
63+
### [hello world example](examples/hello_world)
64+
<!-- embedme examples/hello_world/src/Algorithm.py -->
65+
```python
66+
from Algorithmia import ADK
67+
68+
69+
# API calls will begin at the apply() method, with the request body passed as 'input'
70+
# For more details, see algorithmia.com/developers/algorithm-development/languages
71+
72+
def apply(input):
73+
# If your apply function uses state that's loaded into memory via load, you can pass that loaded state to your apply
74+
# function by defining an additional "state" parameter in your apply function; but it's optional!
75+
return "hello {}".format(str(input))
76+
77+
78+
# This turns your library code into an algorithm that can run on the platform.
79+
# If you intend to use loading operations, remember to pass a `load` function as a second variable.
80+
algorithm = ADK(apply)
81+
# The 'serve()' function actually starts the algorithm, you can follow along in the source code
82+
# to see how everything works.
83+
algorithm.init("Algorithmia")
84+
85+
```
86+
87+
### [hello world example with loaded state](examples/loaded_state_hello_world)
88+
<!-- embedme examples/loaded_state_hello_world/src/Algorithm.py -->
89+
```python
90+
from Algorithmia import ADK
91+
92+
93+
# API calls will begin at the apply() method, with the request body passed as 'input'
94+
# For more details, see algorithmia.com/developers/algorithm-development/languages
95+
96+
def apply(input, state):
97+
# If your apply function uses state that's loaded into memory via load, you can pass that loaded state to your apply
98+
# function by defining an additional "state" parameter in your apply function.
99+
return "hello {} {}".format(str(input), str(state))
100+
101+
102+
def load():
103+
# Here you can optionally define a function that will be called when the algorithm is loaded.
104+
# The return object from this function can be passed directly as input to your apply function.
105+
# A great example would be any model files that need to be available to this algorithm
106+
# during runtime.
107+
# Any variables returned here, will be passed as the secondary argument to your 'algorithm' function
108+
109+
return "Loading has been completed."
110+
111+
112+
# This turns your library code into an algorithm that can run on the platform.
113+
# If you intend to use loading operations, remember to pass a `load` function as a second variable.
114+
algorithm = ADK(apply, load)
115+
# The 'serve()' function actually starts the algorithm, you can follow along in the source code
116+
# to see how everything works.
117+
algorithm.init("Algorithmia")
118+
119+
```
120+
## [pytorch based image classification](examples/pytorch_image_classification)
121+
<!-- embedme examples/pytorch_image_classification/src/Algorithm.py -->
122+
```python
123+
from Algorithmia import client, ADK
124+
import torch
125+
from PIL import Image
126+
import json
127+
from torchvision import models, transforms
128+
129+
CLIENT = client()
130+
SMID_ALGO = "algo://util/SmartImageDownloader/0.2.x"
131+
LABEL_PATH = "data://AlgorithmiaSE/image_cassification_demo/imagenet_class_index.json"
132+
MODEL_PATHS = {
133+
"squeezenet": 'data://AlgorithmiaSE/image_cassification_demo/squeezenet1_1-f364aa15.pth',
134+
'alexnet': 'data://AlgorithmiaSE/image_cassification_demo/alexnet-owt-4df8aa71.pth',
135+
}
136+
137+
138+
def load_labels():
139+
local_path = CLIENT.file(LABEL_PATH).getFile().name
140+
with open(local_path) as f:
141+
labels = json.load(f)
142+
labels = [labels[str(k)][1] for k in range(len(labels))]
143+
return labels
144+
145+
146+
def load_model(name):
147+
if name == "squeezenet":
148+
model = models.squeezenet1_1()
149+
models.densenet121()
150+
weights = torch.load(CLIENT.file(MODEL_PATHS['squeezenet']).getFile().name)
151+
else:
152+
model = models.alexnet()
153+
weights = torch.load(CLIENT.file(MODEL_PATHS['alexnet']).getFile().name)
154+
model.load_state_dict(weights)
155+
return model.float().eval()
156+
157+
158+
def get_image(image_url):
159+
input = {"image": image_url, "resize": {'width': 224, 'height': 224}}
160+
result = CLIENT.algo(SMID_ALGO).pipe(input).result["savePath"][0]
161+
local_path = CLIENT.file(result).getFile().name
162+
img_data = Image.open(local_path)
163+
return img_data
164+
165+
166+
def infer_image(image_url, n, state):
167+
model = state['model']
168+
labels = state['labels']
169+
image_data = get_image(image_url)
170+
transformed = transforms.Compose([
171+
transforms.ToTensor(),
172+
transforms.Normalize(mean=[0.485, 0.456, 0.406],
173+
std=[0.229, 0.224, 0.225])])
174+
img_tensor = transformed(image_data).unsqueeze(dim=0)
175+
infered = model.forward(img_tensor)
176+
preds, indicies = torch.sort(torch.softmax(infered.squeeze(), dim=0), descending=True)
177+
predicted_values = preds.detach().numpy()
178+
indicies = indicies.detach().numpy()
179+
result = []
180+
for i in range(n):
181+
label = labels[indicies[i]].lower().replace("_", " ")
182+
confidence = float(predicted_values[i])
183+
result.append({"label": label, "confidence": confidence})
184+
return result
185+
186+
187+
def load():
188+
output = {'model': load_model("squeezenet"), 'labels': load_labels()}
189+
return output
190+
191+
192+
def apply(input, state):
193+
if isinstance(input, dict):
194+
if "n" in input:
195+
n = input['n']
196+
else:
197+
n = 3
198+
if "data" in input:
199+
if isinstance(input['data'], str):
200+
output = infer_image(input['data'], n, state)
201+
elif isinstance(input['data'], list):
202+
for row in input['data']:
203+
row['predictions'] = infer_image(row['image_url'], n, state)
204+
output = input['data']
205+
else:
206+
raise Exception("'data' must be a image url or a list of image urls (with labels)")
207+
return output
208+
else:
209+
raise Exception("'data' must be defined")
210+
else:
211+
raise Exception('input must be a json object')
212+
213+
214+
algorithm = ADK(apply_func=apply, load_func=load)
215+
algorithm.init({"data": "https://i.imgur.com/bXdORXl.jpeg"})
216+
217+
```
218+
219+
### Readme publishing
220+
To compile the template readme, please check out [embedme](https://github.com/zakhenry/embedme) utility
221+
and run the following:
222+
```commandline
223+
npm install -g npx
224+
npx embedme --stdout README_template.md > README.md
225+
```

README_template.md

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# Algorithm Development Kit (ADK), Python edition
2+
3+
<!-- embedme examples/hello_world/src/Algorithm.py -->
4+
```python
5+
```
6+
## Focus
7+
This document will describe the following:
8+
- What is an Algorithm Development Kit
9+
- Changes to Algorithm development
10+
- Example workflows you can use to create your own Algorithms.
11+
12+
13+
## What is an Algorithm Development Kit
14+
An Algorithm Development Kit is a package that contains all of the necessary components to convert a regular application into one that can be executed and run on Algorithmia.
15+
To do that, an ADK must be able to communicate with [langserver](https://github.com/algorithmiaio/langpacks/blob/develop/langpack_guide.md).
16+
To keep things simple, an ADK exposes some optional functions, along with an `apply` function that acts as the explicit entrypoint into your algorithm.
17+
Along with those basics, the ADK also exposes the ability to execute your algorithm locally, without `langserver`; which enables better debuggability.
18+
19+
![adk architecture](assets/adk_architecture.png)
20+
21+
This kit, when implemented by an algorithm developer - enables an easy way to get started with your project, along with well defined hooks to integrate with an existing project.
22+
23+
24+
## Changes to Algorithm Development
25+
26+
Algorithm development does change with this introduction:
27+
- Primary development file has been renamed to `src/Algorithm.py` to aide in understanding around what this file actually does / why it's important
28+
- An additional import (`from algorithm import ADK`)
29+
- An optional `load()` function that can be implemented
30+
- This enables a dedicated function for preparing your algorithm for runtime operations, such as model loading, configuration, etc
31+
- A call to the handler function with your `apply` and optional` load` functions as inputs
32+
- ```python
33+
algorithm = ADK(apply)
34+
algorithm.init("Algorithmia")
35+
```
36+
- Converts the project into an executable, rather than a library
37+
- Which will interact with the `langserver` service on Algorithmia
38+
- But is debuggable via stdin/stdout when executed locally / outside of an Algorithm container
39+
- When a payload is provided to `init()`, that payload will be directly provided to your algorithm when executed locally, bypassing stdin parsing and simplifying debugging!
40+
- This includes being able to step through your algorithm code in your IDE of choice! Just execute your `src/Algorithm.py` script and try stepping through your code with your favorite IDE
41+
42+
## Example workflows
43+
Check out these examples to help you get started:
44+
### [hello world example](examples/hello_world)
45+
<!-- embedme examples/hello_world/src/Algorithm.py -->
46+
```python
47+
```
48+
49+
### [hello world example with loaded state](examples/loaded_state_hello_world)
50+
<!-- embedme examples/loaded_state_hello_world/src/Algorithm.py -->
51+
```python
52+
```
53+
## [pytorch based image classification](examples/pytorch_image_classification)
54+
<!-- embedme examples/pytorch_image_classification/src/Algorithm.py -->
55+
```python
56+
```
57+
58+
### Readme publishing
59+
To compile the template readme, please check out [embedme](https://github.com/zakhenry/embedme) utility
60+
and run the following:
61+
```commandline
62+
npm install -g npx
63+
npx embedme --stdout README_template.md > README.md
64+
```

adk/ADK.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ def process_loop(self):
125125
finally:
126126
self.write_to_pipe(response_obj)
127127

128-
def serve(self, local_payload=None):
128+
def init(self, local_payload=None):
129129
try:
130130
if self.load_func:
131131
self.load()

examples/hello_world/requirements.txt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
algorithmia>=1.7,<2
2-
algorithmia-adk>=1.0,<2
1+
algorithmia>=1.7,<2

examples/hello_world/src/Algorithm.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from adk import ADK
1+
from Algorithmia import ADK
22

33

44
# API calls will begin at the apply() method, with the request body passed as 'input'
@@ -12,7 +12,7 @@ def apply(input):
1212

1313
# This turns your library code into an algorithm that can run on the platform.
1414
# If you intend to use loading operations, remember to pass a `load` function as a second variable.
15-
algo = ADK(apply)
15+
algorithm = ADK(apply)
1616
# The 'serve()' function actually starts the algorithm, you can follow along in the source code
1717
# to see how everything works.
18-
algo.serve("Algorithmia")
18+
algorithm.init("Algorithmia")
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1 @@
1-
algorithmia>=1.7,<2
2-
algorithmia-adk>=1.0,<2
1+
algorithmia>=1.7,<2

examples/loaded_state_hello_world/src/Algorithm.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
from adk import ADK
1+
from Algorithmia import ADK
22

33

44
# API calls will begin at the apply() method, with the request body passed as 'input'
@@ -22,7 +22,7 @@ def load():
2222

2323
# This turns your library code into an algorithm that can run on the platform.
2424
# If you intend to use loading operations, remember to pass a `load` function as a second variable.
25-
algo = ADK(apply, load)
25+
algorithm = ADK(apply, load)
2626
# The 'serve()' function actually starts the algorithm, you can follow along in the source code
2727
# to see how everything works.
28-
algo.serve("Algorithmia")
28+
algorithm.init("Algorithmia")

examples/pytorch_image_classification/requirements.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
algorithmia>=1.7,<2
2-
algorithmia-adk
32
six
43
Pillow==8.1.0
54
torch==1.6.0

examples/pytorch_image_classification/src/Algorithm.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
import Algorithmia
2-
from adk import ADK
1+
from Algorithmia import client, ADK
32
import torch
43
from PIL import Image
54
import json
65
from torchvision import models, transforms
76

8-
CLIENT = Algorithmia.client()
7+
CLIENT = client()
98
SMID_ALGO = "algo://util/SmartImageDownloader/0.2.x"
109
LABEL_PATH = "data://AlgorithmiaSE/image_cassification_demo/imagenet_class_index.json"
1110
MODEL_PATHS = {
@@ -90,5 +89,5 @@ def apply(input, state):
9089
raise Exception('input must be a json object')
9190

9291

93-
algo = ADK(apply_func=apply, load_func=load)
94-
algo.serve({"data": "https://i.imgur.com/bXdORXl.jpeg"})
92+
algorithm = ADK(apply_func=apply, load_func=load)
93+
algorithm.init({"data": "https://i.imgur.com/bXdORXl.jpeg"})

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
name='algorithmia-adk',
77
version='0.1.0',
88
description='adk python ADK client',
9-
long_description='adk Development Kit code used for creating Python algorithms on adk.',
9+
long_description='adk Development Kit code used for creating Python algorithms on adk. Built into the Algorithmia client',
1010
url='http://github.com/algorithmiaio/algorithmia-adk-python',
1111
license='MIT',
1212
author='Algorithmia',

0 commit comments

Comments
 (0)