Skip to content

Commit 34dcee5

Browse files
Add the model authentication example
1 parent 1e49636 commit 34dcee5

File tree

5 files changed

+529
-0
lines changed

5 files changed

+529
-0
lines changed

model_authentication/deploy.py

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
import Algorithmia
2+
import argparse
3+
from retry import retry
4+
from Algorithmia.errors import AlgorithmException
5+
6+
def parse_arguments():
7+
parser = argparse.ArgumentParser()
8+
parser.add_argument("-k", "--api_key", nargs="?")
9+
parser.add_argument("-u", "--username", nargs="?")
10+
parser.add_argument("-a", "--algoname", nargs="?")
11+
parser.add_argument("-m", "--model_script", nargs="?")
12+
parser.add_argument("-d", "--model_dependency_file", nargs="?")
13+
parser.add_argument("-p", "--data_path", nargs="?", default=".my/mycollection")
14+
parser.add_argument("-c", "--model_checksum", nargs="?")
15+
16+
args = parser.parse_args()
17+
return args
18+
19+
def main(args=None):
20+
if isinstance(args, type(None)):
21+
args = parse_arguments()
22+
deploy(args)
23+
24+
def deploy(args):
25+
# A data collection, where we'll be storing our files
26+
data_path = "data://{}".format(args.data_path)
27+
28+
# Create a new algorithmia client
29+
client = Algorithmia.client(args.api_key)
30+
31+
# Create data collection if it doesn't exist
32+
if not client.dir(data_path).exists():
33+
client.dir(data_path).create()
34+
35+
### 3. Upload model file ###
36+
37+
# Define local work directory
38+
local_dir = "algo"
39+
40+
model_name = "model.h5"
41+
local_model = "{}/{}".format(local_dir, model_name)
42+
data_model = "{}/{}".format(data_path, model_name)
43+
44+
# Upload our model file to our data collection
45+
_ = client.file(data_model).putFile(local_model)
46+
47+
### 4. Create new algorithm ###
48+
49+
# Algorithms are refered with the following schema: username/algoname
50+
algo_namespace = "{}/{}".format(args.username, args.algoname)
51+
52+
# Here are some details you can define for your algorithm
53+
details = {
54+
"summary": "This algorithm classifies 28x28 MNSIT images.",
55+
"label": "MNIST Classifier",
56+
"tagline": "mnist_classifier"
57+
}
58+
59+
# 1. We're making our algorithm closed-sourced – "source_visibility"
60+
#
61+
# 2. We're selecting a package set that has tensorflow-gpu already installed. – "package_set"
62+
# Even though we could manually install it later, using the optimized
63+
# & pre-installed image allows you to compile things faster.
64+
#
65+
# 3. We're selectig the Algorithmia Platform License (aka. "apl"). – "license"
66+
#
67+
# 4. We're giving our algorithm internet access. – "network_access"
68+
#
69+
# 5. We're allowing our algorithm to call other algorithms. – "pipeline_enabled"
70+
settings = {
71+
"source_visibility": "closed",
72+
"package_set": "tensorflow-gpu-2.0",
73+
"license": "apl",
74+
"network_access": "full",
75+
"pipeline_enabled": True
76+
}
77+
78+
# Let's also provide a sample input for our algorithm
79+
version_info = {
80+
"sample_input": '[[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]'
81+
}
82+
83+
print("Algorithm namepace: {}".format(algo_namespace))
84+
85+
# Create a new algorithm
86+
client.algo(algo_namespace).create(details, settings, version_info)
87+
88+
# Print the URL to the algorithm
89+
print("Algorithm URL: https://algorithmia.com/algorithms/{}".format(algo_namespace))
90+
91+
### 5. Git clone our algorithm locally ###
92+
93+
import urllib.parse
94+
from git import Git, Repo, remote
95+
96+
# Encode API key, so we can use it in the git URL
97+
encoded_api_key= urllib.parse.quote_plus(args.api_key)
98+
99+
algo_repo = "https://{}:{}@git.algorithmia.com/git/{}/{}.git".format(args.username, encoded_api_key, args.username, args.algoname)
100+
101+
_ = Repo.clone_from(algo_repo, "{}/{}".format(local_dir, args.algoname))
102+
103+
cloned_repo = Repo("{}/{}".format(local_dir, args.algoname))
104+
105+
### 6. The algorithm script & dependency file ###
106+
107+
algo_script_path = "{}/{}/src/{}.py".format(local_dir, args.algoname, args.algoname)
108+
dependency_file_path = "{}/{}/{}".format(local_dir, args.algoname, "requirements.txt")
109+
110+
# shutil.copyfile(args.model_script, algo_script_path)
111+
# Modify & copy over the script file
112+
with open(args.model_script) as f:
113+
content = f.read()
114+
newText=content.replace('<MODEL_FILE_CHECKSUM>', args.model_checksum)\
115+
.replace('<DATA_DIR>', args.data_path)
116+
117+
with open(algo_script_path, "w") as f:
118+
f.write(newText)
119+
120+
shutil.copyfile(args.model_dependency_file, dependency_file_path)
121+
122+
### 7. Upload our source code ###
123+
124+
files = ["src/{}.py".format(args.algoname), "requirements.txt"]
125+
cloned_repo.index.add(files)
126+
127+
cloned_repo.index.commit("Add algorithm files")
128+
129+
origin = cloned_repo.remote(name='origin')
130+
131+
print("Pushing source code upstream, uploading model file & compiling algorithm...")
132+
133+
_ = origin.push()
134+
135+
# Print the URL to the algorithm source code
136+
print("Algorithm Source Code is available at: https://algorithmia.com/algorithms/{}/source".format(algo_namespace))
137+
138+
### 8. Call & test our algorithm ###
139+
140+
print("Testing new compiled algorithm via API endpoint...")
141+
latest_hash = client.algo(algo_namespace).info().version_info.git_hash
142+
143+
# Call algorithm until the algo hash endpoint becomes available, up to 10 seconds
144+
@retry(AlgorithmException, tries=20, delay=1)
145+
def get_probability(ALGO, VERSION, INPUT):
146+
return client.algo("{}/{}".format(ALGO, VERSION)).pipe(INPUT).result["prob"]
147+
148+
# Let's create a 28x28 array as an input
149+
algo_input = [[0]*28]*28
150+
151+
# Call the algorithm endpoint with the latest hash
152+
prob = get_probability(algo_namespace, latest_hash, algo_input)
153+
154+
print("Test complete!")
155+
156+
### 9. Publish our algorithm ###
157+
158+
print("Publishing and deploying algorithm...")
159+
160+
# Now let's publish/deploy our algorithm
161+
client.algo(algo_namespace).publish()
162+
163+
latest_version = client.algo(algo_namespace).info().version_info.semantic_version
164+
165+
# Call the algorithm endpoint with the latest version
166+
prob = get_probability(algo_namespace, latest_version, algo_input)
167+
168+
print("Algorithm has been deployed!")
169+
170+
if __name__ == "__main__":
171+
main()

model_authentication/model.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import Algorithmia
2+
from tensorflow import keras
3+
import numpy as np
4+
import pickle
5+
import hashlib
6+
7+
def sha256_checksum(filename, block_size=65536):
8+
# Let's read in 64KB chunks
9+
sha256 = hashlib.sha256()
10+
with open(filename, "rb") as f:
11+
for block in iter(lambda: f.read(block_size), b""):
12+
sha256.update(block)
13+
return sha256.hexdigest()
14+
15+
def authenticate_model(model_file, checksum):
16+
print("Asserting {}=={}".format(sha256_checksum(model_file), checksum))
17+
assert(sha256_checksum(model_file)==checksum)
18+
19+
# Create our Algorithmia client
20+
client = Algorithmia.client()
21+
22+
# Our model file checksum
23+
model_file_checksum = "<MODEL_FILE_CHECKSUM>"
24+
25+
# Define where our model file lives in our data collection
26+
data_model = "data://<DATA_DIR>/model.h5"
27+
28+
# Download & initialize our model
29+
model_file = client.file(data_model).getFile().name
30+
31+
# Authenticate model file before doing anything
32+
authenticate_model(model_file, model_file_checksum)
33+
34+
model = keras.models.load_model(model_file)
35+
36+
def preprocess_input(two_d_array):
37+
# Check if the dimensions are 28 x 28
38+
assert(len(two_d_array[0])==28)
39+
assert(len(two_d_array[1])==28)
40+
np_array = np.array(two_d_array)
41+
# Expand dimension by 1 for model consumption
42+
np_array = (np.expand_dims(np_array,0))
43+
return np_array
44+
45+
def apply(input):
46+
# Get input text
47+
input_vector = preprocess_input(input)
48+
# Get probability using our model
49+
preds = model.predict(input_vector)
50+
probs = list(map(lambda x: float(x), preds[0]))
51+
# Return result back to user
52+
return {"prob": probs}

model_authentication/model_authentication.ipynb

Lines changed: 295 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
algorithmia>=1.0.0,<2.0
2+
six
3+
tensorflow-gpu==2.0.0

model_authentication/requirements.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
tensorflow==2.0.0
2+
algorithmia==1.2.1
3+
jupyter==1.0.0
4+
gitpython==2.1.11
5+
matplotlib==3.0.2
6+
urllib3==1.24.3
7+
retry==0.9.2
8+
numpy==1.16.1

0 commit comments

Comments
 (0)