mirror of
https://github.com/GoogleCloudPlatform/vertex-ai-samples.git
synced 2026-09-26 22:51:56 +00:00
Compare commits
468
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1044b6ef0c | ||
|
|
2c433c8643 | ||
|
|
e974c034ba | ||
|
|
d2d4493397 | ||
|
|
0cd6146a6c | ||
|
|
b61395f465 | ||
|
|
649b209577 | ||
|
|
f42a184171 | ||
|
|
03f0647b76 | ||
|
|
1f39732ae9 | ||
|
|
7dd0b31b58 | ||
|
|
ff843173cf | ||
|
|
da19b116e9 | ||
|
|
d8b365dfd4 | ||
|
|
1247c80fed | ||
|
|
2cf2bf1080 | ||
|
|
5e9e8139c1 | ||
|
|
0728a0036f | ||
|
|
aa52d21643 | ||
|
|
103888d75e | ||
|
|
13d0d5d9b0 | ||
|
|
79c6669686 | ||
|
|
dceb0c4c1c | ||
|
|
0c9cdca713 | ||
|
|
6124092681 | ||
|
|
629e739327 | ||
|
|
b68cbd8255 | ||
|
|
7fec30c12f | ||
|
|
a754843c39 | ||
|
|
436db4a35b | ||
|
|
8dba2d040b | ||
|
|
560dd9da15 | ||
|
|
8ec17d6aca | ||
|
|
f545282c36 | ||
|
|
7e4e14e084 | ||
|
|
be5933115d | ||
|
|
be46140138 | ||
|
|
79f4dbaafe | ||
|
|
9577f8324c | ||
|
|
01274fe767 | ||
|
|
1690b07e4a | ||
|
|
6b0de60a5c | ||
|
|
19b541b6cb | ||
|
|
22f6841079 | ||
|
|
7c47c95e3a | ||
|
|
7735e6ae69 | ||
|
|
247a540625 | ||
|
|
f017606f77 | ||
|
|
1d7024341a | ||
|
|
9181dba316 | ||
|
|
a8320f3943 | ||
|
|
6fc34ae4f1 | ||
|
|
5566346fdc | ||
|
|
e584acdb48 | ||
|
|
4022811d3c | ||
|
|
4d29c490f8 | ||
|
|
2315942901 | ||
|
|
505d5049e0 | ||
|
|
20411db737 | ||
|
|
39dbbde22c | ||
|
|
34251594a4 | ||
|
|
7342673331 | ||
|
|
b5d19719e0 | ||
|
|
59536e9e61 | ||
|
|
7d74bc3caa | ||
|
|
b5b65198a6 | ||
|
|
3a5a14f1d8 | ||
|
|
157f8538ed | ||
|
|
532bf04933 | ||
|
|
99547ccb73 | ||
|
|
2e0cd74533 | ||
|
|
b9a9d76e8b | ||
|
|
fb61e0631c | ||
|
|
16c38c8fbf | ||
|
|
8888e8ad7f | ||
|
|
c787a0e99e | ||
|
|
8ea77a7cb0 | ||
|
|
2bb6d6deb2 | ||
|
|
7b235c935a | ||
|
|
3c88e9284c | ||
|
|
e967b02a22 | ||
|
|
aeb87cbc44 | ||
|
|
1f39a8f892 | ||
|
|
d2a6508379 | ||
|
|
39d1646b3c | ||
|
|
4c5ede0130 | ||
|
|
7c8eeeb1d1 | ||
|
|
99929ca018 | ||
|
|
6aadabd967 | ||
|
|
9b5be742c1 | ||
|
|
63d5b5e3bf | ||
|
|
1448645ba4 | ||
|
|
3d19ffb131 | ||
|
|
b6018551a5 | ||
|
|
65fbf0ee0b | ||
|
|
427bd3d5ea | ||
|
|
5f41599745 | ||
|
|
67fbd84832 | ||
|
|
9b427b6a1f | ||
|
|
37d5d5b992 | ||
|
|
287911b681 | ||
|
|
c83387181a | ||
|
|
236d45b87e | ||
|
|
4eb7b3ce39 | ||
|
|
d74554f641 | ||
|
|
3e70c63899 | ||
|
|
4c79ab91e2 | ||
|
|
dd8a7ad325 | ||
|
|
a3bb273e78 | ||
|
|
1ff0872546 | ||
|
|
91144b8476 | ||
|
|
9822bd64a1 | ||
|
|
f14ff50d2b | ||
|
|
fdc30dab67 | ||
|
|
93229f62c9 | ||
|
|
b37f474255 | ||
|
|
344b0dd6d7 | ||
|
|
48b7cdb21d | ||
|
|
cefdc32d5f | ||
|
|
7558411fbc | ||
|
|
1c0487c459 | ||
|
|
6b7bb867ab | ||
|
|
8c7363c2b0 | ||
|
|
2ea22da2bf | ||
|
|
1d96584421 | ||
|
|
06d57da90c | ||
|
|
355105218d | ||
|
|
3090a312c0 | ||
|
|
8c265ee1ef | ||
|
|
fdbfecda04 | ||
|
|
7050730b6a | ||
|
|
4ec962d277 | ||
|
|
3185ca7d48 | ||
|
|
32ac97982c | ||
|
|
1aa94377f5 | ||
|
|
7271fde2d7 | ||
|
|
71431d5520 | ||
|
|
0f58771ecd | ||
|
|
05c5b68db1 | ||
|
|
ee2744a7de | ||
|
|
a32db129ea | ||
|
|
c9aff9a5d9 | ||
|
|
7324b084ee | ||
|
|
095b0bd628 | ||
|
|
7a1b262154 | ||
|
|
ad1a827824 | ||
|
|
1ff7fc70ed | ||
|
|
b45e17efc0 | ||
|
|
98ad1655a7 | ||
|
|
19d56c0fa0 | ||
|
|
a3ee3687da | ||
|
|
ff1b9ad8ef | ||
|
|
64b4f5103c | ||
|
|
a2e8f933dc | ||
|
|
5cb368585c | ||
|
|
db96de8266 | ||
|
|
18440f5c0c | ||
|
|
95d44d0cf5 | ||
|
|
fc2e91d050 | ||
|
|
b8872c1e03 | ||
|
|
b21ec83ba3 | ||
|
|
d42cc4fbc9 | ||
|
|
28ceb8085e | ||
|
|
a460dcbdd8 | ||
|
|
a76ffe0103 | ||
|
|
168c5c2f15 | ||
|
|
d45bbb36df | ||
|
|
a83ee780f1 | ||
|
|
6011ca5127 | ||
|
|
d53ebed734 | ||
|
|
38f29064a0 | ||
|
|
6ccad69a18 | ||
|
|
0ca86220f8 | ||
|
|
25e004b60e | ||
|
|
871288ac1a | ||
|
|
753bb0e120 | ||
|
|
3893b76486 | ||
|
|
ee805e7846 | ||
|
|
eb605b7943 | ||
|
|
6f0cab7bbb | ||
|
|
eb9b6552d8 | ||
|
|
35d71b410e | ||
|
|
b7d98d6f9a | ||
|
|
a421dc1354 | ||
|
|
a4a72c8b22 | ||
|
|
5c403dde3d | ||
|
|
a85753e1b7 | ||
|
|
2f54cf23cc | ||
|
|
ffc7427265 | ||
|
|
0e738b506f | ||
|
|
fdeaaa37fb | ||
|
|
fed7df10ac | ||
|
|
725b2fdd32 | ||
|
|
a70f93fe0a | ||
|
|
d03697cf73 | ||
|
|
191f2a11bc | ||
|
|
1b692bc330 | ||
|
|
edfd7727d3 | ||
|
|
5d32bd8835 | ||
|
|
8b188a11b2 | ||
|
|
83546a8e7b | ||
|
|
c9aa6df17b | ||
|
|
b4955b28a2 | ||
|
|
f33d9eb6d1 | ||
|
|
1bdbb7921a | ||
|
|
7154a722ba | ||
|
|
99d4a8c31a | ||
|
|
f9cedf2850 | ||
|
|
f4f0112a6a | ||
|
|
e441568c38 | ||
|
|
fee8c969e4 | ||
|
|
bdac091e3f | ||
|
|
ff9338ed3c | ||
|
|
1a538fd249 | ||
|
|
4f09c94b5f | ||
|
|
bcf3e6b0f5 | ||
|
|
fbcf783064 | ||
|
|
2f5fe80f34 | ||
|
|
a850f3a88a | ||
|
|
de91feaca1 | ||
|
|
a7fd0734dc | ||
|
|
ce1b167f08 | ||
|
|
65ff3cab60 | ||
|
|
c7ef72f1d3 | ||
|
|
bd58428857 | ||
|
|
a4a6ed848b | ||
|
|
b753bc58d3 | ||
|
|
6279e12eea | ||
|
|
fc0c34c905 | ||
|
|
ef388ecf30 | ||
|
|
bafb2c6f59 | ||
|
|
f2d431c182 | ||
|
|
2e4b6a1b1b | ||
|
|
95cebcbf8f | ||
|
|
08f1b659c5 | ||
|
|
86ba71931d | ||
|
|
d811306fb9 | ||
|
|
4173e6d561 | ||
|
|
499d25055a | ||
|
|
60776de953 | ||
|
|
182ebcf285 | ||
|
|
39359a5b21 | ||
|
|
0d72cfe070 | ||
|
|
64fef140a2 | ||
|
|
015809b948 | ||
|
|
faccdd081f | ||
|
|
3e8a3cf28e | ||
|
|
821bba2776 | ||
|
|
c82bdee299 | ||
|
|
c27381139b | ||
|
|
01236836f2 | ||
|
|
b8380650bd | ||
|
|
69d266cb90 | ||
|
|
530524ac6e | ||
|
|
ba66961fce | ||
|
|
aca7035482 | ||
|
|
3d270cde69 | ||
|
|
9754c265ff | ||
|
|
f5d730c9f1 | ||
|
|
acd42a3a1b | ||
|
|
eb7cf4b6bc | ||
|
|
34b1011ac9 | ||
|
|
239d664990 | ||
|
|
920c771238 | ||
|
|
e7c68ecb78 | ||
|
|
cfeb118d8b | ||
|
|
2c9c8db15c | ||
|
|
6778a2cfbf | ||
|
|
26c5d56e6d | ||
|
|
527fe79f15 | ||
|
|
2a003fa9c3 | ||
|
|
03b9b6026f | ||
|
|
ff8d6a9d56 | ||
|
|
874c881ad6 | ||
|
|
f484429a89 | ||
|
|
7fa2502c09 | ||
|
|
056791fadd | ||
|
|
154be75dce | ||
|
|
ed7e900a02 | ||
|
|
f109fefbfa | ||
|
|
6f9c99d1df | ||
|
|
ed39d78005 | ||
|
|
d6466ab1a6 | ||
|
|
65e310e4e6 | ||
|
|
0e773ba90f | ||
|
|
0747f9efb8 | ||
|
|
53785fb812 | ||
|
|
b85c24dca5 | ||
|
|
ea5c12c22a | ||
|
|
392c1b7361 | ||
|
|
c5980e636e | ||
|
|
8859e9426d | ||
|
|
db7cc9000a | ||
|
|
d1fe50a1d9 | ||
|
|
9e3597692f | ||
|
|
45c8deebe5 | ||
|
|
6705c4e4ee | ||
|
|
b7f06d948a | ||
|
|
7f3c1a9a36 | ||
|
|
2e991c5b9d | ||
|
|
f8ac68ec10 | ||
|
|
a591c39367 | ||
|
|
d938982aae | ||
|
|
1397c9a64a | ||
|
|
da97ec2244 | ||
|
|
c3f1f8239b | ||
|
|
73878dcc01 | ||
|
|
13bad2e6a3 | ||
|
|
0172f2b11b | ||
|
|
1ba0c17555 | ||
|
|
399c961074 | ||
|
|
d44836db9d | ||
|
|
4e236fd553 | ||
|
|
352b1e2710 | ||
|
|
13cc94d3b7 | ||
|
|
632362ed80 | ||
|
|
90b5575b0c | ||
|
|
2d96cc0af1 | ||
|
|
aadc80752b | ||
|
|
e8745a750b | ||
|
|
67b54685be | ||
|
|
738dc1e01c | ||
|
|
3b481a43a9 | ||
|
|
35d0dee5a0 | ||
|
|
90a0bb9b4c | ||
|
|
e59d5462b4 | ||
|
|
f94bf3c7e1 | ||
|
|
dd215b8cf0 | ||
|
|
784c94a72f | ||
|
|
dccfed9743 | ||
|
|
8346855cfe | ||
|
|
64ad5123d0 | ||
|
|
0025e56323 | ||
|
|
8fd9f35aca | ||
|
|
658abd6d5b | ||
|
|
1243f736e2 | ||
|
|
dd0b07163f | ||
|
|
c6b268ba89 | ||
|
|
fd4f7d2641 | ||
|
|
5c513e20c9 | ||
|
|
d78dba7941 | ||
|
|
42fa88547b | ||
|
|
c4cda29095 | ||
|
|
47725449b5 | ||
|
|
2eba462437 | ||
|
|
97a3f4d3dd | ||
|
|
531e567358 | ||
|
|
1f7c7105bb | ||
|
|
b109183d52 | ||
|
|
72ed00fe0f | ||
|
|
e8d9137a50 | ||
|
|
872e98c544 | ||
|
|
0966fc56a4 | ||
|
|
e8d1e58fc6 | ||
|
|
f73ff8a44f | ||
|
|
48c5810bd7 | ||
|
|
9821d8986c | ||
|
|
207d5db3e9 | ||
|
|
9deb05e3e7 | ||
|
|
8dafc25aab | ||
|
|
ef10e7db53 | ||
|
|
cba907ddf8 | ||
|
|
16c3d7fe25 | ||
|
|
60e1f92ef4 | ||
|
|
fb392c6375 | ||
|
|
b4d10bc5aa | ||
|
|
4049250bad | ||
|
|
923db816e1 | ||
|
|
872f1561cd | ||
|
|
8d7832de6e | ||
|
|
f840017cda | ||
|
|
de3f35b8a6 | ||
|
|
b746bc80d4 | ||
|
|
2001604d37 | ||
|
|
faa148f66e | ||
|
|
f184411666 | ||
|
|
bdad774100 | ||
|
|
9abe9a643b | ||
|
|
ab230ca06f | ||
|
|
dcbd3702d2 | ||
|
|
0505d0c04c | ||
|
|
a47e0aacbb | ||
|
|
15912adeaf | ||
|
|
35fdba7e1c | ||
|
|
f403fa9051 | ||
|
|
0d346b136e | ||
|
|
60d71d29cc | ||
|
|
28c872f4b6 | ||
|
|
c637d693b7 | ||
|
|
5c3a216eb7 | ||
|
|
0b7831b0f4 | ||
|
|
6a6f077ae4 | ||
|
|
27f0a4bb63 | ||
|
|
1476453603 | ||
|
|
dbafcb47ea | ||
|
|
f20700f25a | ||
|
|
d1ca1cd7f8 | ||
|
|
9b03fb7f8e | ||
|
|
aac271eacc | ||
|
|
68b53e0d32 | ||
|
|
bf354adfd3 | ||
|
|
55ad5701e4 | ||
|
|
918564dcc7 | ||
|
|
19b3b5da0f | ||
|
|
2bdab9a9b8 | ||
|
|
88e5d5d236 | ||
|
|
79730d191f | ||
|
|
019040e4cb | ||
|
|
4fd3514d6d | ||
|
|
bb17381b03 | ||
|
|
3f06f48282 | ||
|
|
33abd1e427 | ||
|
|
1d9bfe9934 | ||
|
|
fb9defa985 | ||
|
|
824fb689e4 | ||
|
|
c48dd8662b | ||
|
|
f251721d23 | ||
|
|
e949eb128f | ||
|
|
df48e74f59 | ||
|
|
ce9e6ecf62 | ||
|
|
9ab5f4274a | ||
|
|
29e584a422 | ||
|
|
beabb87cff | ||
|
|
e3f6717ff6 | ||
|
|
fd30c4014a | ||
|
|
4be8b0a59a | ||
|
|
aa09d46265 | ||
|
|
5667967131 | ||
|
|
4e4f532658 | ||
|
|
14b2ce4f2e | ||
|
|
c14b98c92d | ||
|
|
8275ea6c49 | ||
|
|
40fbffcc95 | ||
|
|
08bb513488 | ||
|
|
b298f83cd3 | ||
|
|
659cbb54c4 | ||
|
|
6ddcaa540a | ||
|
|
1656c57b18 | ||
|
|
6cac60f74a | ||
|
|
55e37f795c | ||
|
|
c030d7ef74 | ||
|
|
96be449c69 | ||
|
|
e40ddab4d5 | ||
|
|
d02bc2d56b | ||
|
|
76b641b23d | ||
|
|
bbf4345e76 | ||
|
|
058358a795 | ||
|
|
1c2f75f680 | ||
|
|
999866fad1 | ||
|
|
89f4571a2c | ||
|
|
eaff81fd97 | ||
|
|
976ed94cf2 | ||
|
|
8ab8ef9ca9 | ||
|
|
eaff80a920 | ||
|
|
8646285c26 | ||
|
|
7cad8680e1 | ||
|
|
4f66324883 | ||
|
|
468fd144d2 | ||
|
|
2503c3411c | ||
|
|
94b27550e8 | ||
|
|
3542e0b0a3 | ||
|
|
5cf3b64618 | ||
|
|
885bfd56e5 | ||
|
|
3a5eec64af | ||
|
|
8cdc7f1f79 | ||
|
|
84fd10f408 | ||
|
|
7804c860c5 | ||
|
|
ed0c1c74a6 |
@@ -5,6 +5,8 @@ from resource_cleanup_manager import (
|
||||
ModelResourceCleanupManager,
|
||||
EndpointResourceCleanupManager,
|
||||
ResourceCleanupManager,
|
||||
MatchingEngineIndexEndpointResourceCleanupManager,
|
||||
MatchingEngineIndexResourceCleanupManager,
|
||||
)
|
||||
|
||||
rate_limit = RateLimit(max_count=25, per=60, greedy=False)
|
||||
@@ -40,10 +42,12 @@ if is_dry_run:
|
||||
print("Starting cleanup in dry run mode...")
|
||||
|
||||
# List of all cleanup managers
|
||||
managers = [
|
||||
managers: List[ResourceCleanupManager] = [
|
||||
DatasetResourceCleanupManager(),
|
||||
EndpointResourceCleanupManager(),
|
||||
ModelResourceCleanupManager(), # ModelResourceCleanupManager must follow EndpointResourceCleanupManager due to deployed models blocking model deletion.
|
||||
MatchingEngineIndexEndpointResourceCleanupManager(),
|
||||
MatchingEngineIndexResourceCleanupManager(),
|
||||
]
|
||||
|
||||
run_cleanup_managers(managers=managers, is_dry_run=is_dry_run)
|
||||
|
||||
@@ -109,3 +109,11 @@ class EndpointResourceCleanupManager(VertexAIResourceCleanupManager):
|
||||
|
||||
class ModelResourceCleanupManager(VertexAIResourceCleanupManager):
|
||||
vertex_ai_resource = aiplatform.Model
|
||||
|
||||
|
||||
class MatchingEngineIndexResourceCleanupManager(VertexAIResourceCleanupManager):
|
||||
vertex_ai_resource = aiplatform.MatchingEngineIndex
|
||||
|
||||
|
||||
class MatchingEngineIndexEndpointResourceCleanupManager(VertexAIResourceCleanupManager):
|
||||
vertex_ai_resource = aiplatform.MatchingEngineIndexEndpoint
|
||||
|
||||
@@ -17,13 +17,16 @@ import concurrent
|
||||
import dataclasses
|
||||
import datetime
|
||||
import functools
|
||||
import json
|
||||
import git
|
||||
import operator
|
||||
import os
|
||||
import pathlib
|
||||
import re
|
||||
import subprocess
|
||||
import utils
|
||||
from typing import List, Optional
|
||||
from utils import util
|
||||
|
||||
import execute_notebook_helper
|
||||
import execute_notebook_remote
|
||||
@@ -35,6 +38,7 @@ from utils import NotebookProcessors, util
|
||||
|
||||
# A buffer so that workers finish before the orchestrating job
|
||||
WORKER_TIMEOUT_BUFFER_IN_SECONDS: int = 60 * 60
|
||||
PYTHON_VERSION = "3.9" # Set default python version
|
||||
|
||||
|
||||
def format_timedelta(delta: datetime.timedelta) -> str:
|
||||
@@ -66,6 +70,7 @@ class NotebookExecutionResult:
|
||||
log_url: str
|
||||
output_uri: str
|
||||
build_id: str
|
||||
logs_bucket: str
|
||||
error_message: Optional[str]
|
||||
|
||||
@property
|
||||
@@ -97,6 +102,7 @@ def _process_notebook(
|
||||
"VPC_NETWORK": variable_vpc_network,
|
||||
},
|
||||
)
|
||||
unique_strings_preprocessor = NotebookProcessors.UniqueStringsPreprocessor()
|
||||
|
||||
# Use no-execute preprocessor
|
||||
(
|
||||
@@ -105,11 +111,41 @@ def _process_notebook(
|
||||
) = remove_no_execute_cells_preprocessor.preprocess(nb)
|
||||
|
||||
(nb, resources) = update_variables_preprocessor.preprocess(nb, resources)
|
||||
(nb, resources) = unique_strings_preprocessor.preprocess(nb, resources)
|
||||
|
||||
with open(notebook_path, mode="w", encoding="utf-8") as new_file:
|
||||
nbformat.write(nb, new_file)
|
||||
|
||||
|
||||
def _get_notebook_python_version(notebook_path: str) -> str:
|
||||
"""
|
||||
Get the python version for running the notebook if it is specified in
|
||||
the notebook.
|
||||
"""
|
||||
python_version = PYTHON_VERSION
|
||||
|
||||
# Load the notebook
|
||||
file = open(notebook_path)
|
||||
src = file.read()
|
||||
nb_json = json.loads(src)
|
||||
|
||||
# Iterate over the cells in the ipynb
|
||||
for cell in nb_json["cells"]:
|
||||
if cell["cell_type"] == "markdown":
|
||||
markdown = str.join("", cell["source"])
|
||||
|
||||
# Look for the python version specification pattern
|
||||
re_match = re.search(
|
||||
"python version = (\d\.\d)", markdown, flags=re.IGNORECASE
|
||||
)
|
||||
if re_match:
|
||||
# get the version number
|
||||
python_version = re_match.group(1)
|
||||
break
|
||||
|
||||
return python_version
|
||||
|
||||
|
||||
def _create_tag(filepath: str) -> str:
|
||||
tag = os.path.basename(os.path.normpath(filepath))
|
||||
tag = re.sub("[^0-9a-zA-Z_.-]+", "-", tag)
|
||||
@@ -160,6 +196,7 @@ def process_and_execute_notebook(
|
||||
output_uri=notebook_output_uri,
|
||||
log_url="",
|
||||
build_id="",
|
||||
logs_bucket="",
|
||||
error_message=None,
|
||||
)
|
||||
|
||||
@@ -167,6 +204,12 @@ def process_and_execute_notebook(
|
||||
time_start = datetime.datetime.now()
|
||||
operation = None
|
||||
try:
|
||||
# Get the python version for running the notebook if specified
|
||||
notebook_exec_python_version = _get_notebook_python_version(
|
||||
notebook_path=notebook
|
||||
)
|
||||
print(f"Running notebook with python {notebook_exec_python_version}")
|
||||
|
||||
# Pre-process notebook by substituting variable names
|
||||
_process_notebook(
|
||||
notebook_path=notebook,
|
||||
@@ -193,14 +236,16 @@ def process_and_execute_notebook(
|
||||
private_pool_id=private_pool_id,
|
||||
private_pool_region=variable_region,
|
||||
timeout_in_seconds=timeout_in_seconds,
|
||||
python_version=notebook_exec_python_version,
|
||||
)
|
||||
|
||||
operation_metadata = BuildOperationMetadata(mapping=operation.metadata)
|
||||
result.build_id = operation_metadata.build.id
|
||||
result.log_url = operation_metadata.build.log_url
|
||||
result.logs_bucket = operation_metadata.build.logs_bucket
|
||||
|
||||
# Block and wait for the result
|
||||
operation_result = operation.result()
|
||||
operation_result = operation.result(timeout=timeout_in_seconds)
|
||||
|
||||
result.duration = datetime.datetime.now() - time_start
|
||||
result.is_pass = True
|
||||
@@ -339,7 +384,7 @@ def process_and_execute_notebooks(
|
||||
seconds=max(timeout - WORKER_TIMEOUT_BUFFER_IN_SECONDS, 0)
|
||||
)
|
||||
|
||||
if len(notebooks) > 1:
|
||||
if len(notebooks) >= 1:
|
||||
notebook_execution_results: List[NotebookExecutionResult] = []
|
||||
|
||||
print(f"Found {len(notebooks)} modified notebooks: {notebooks}")
|
||||
@@ -404,6 +449,7 @@ def process_and_execute_notebooks(
|
||||
result.log_url,
|
||||
result.output_uri,
|
||||
result.output_uri_web,
|
||||
result.logs_bucket,
|
||||
]
|
||||
for result in results_sorted
|
||||
],
|
||||
@@ -414,10 +460,35 @@ def process_and_execute_notebooks(
|
||||
"log_url",
|
||||
"output_uri",
|
||||
"output_uri_web",
|
||||
"logs_bucket",
|
||||
],
|
||||
)
|
||||
)
|
||||
|
||||
if len(notebooks) == 1:
|
||||
print("=" * 100)
|
||||
print("The notebook execution build log:\n")
|
||||
print("=" * 100)
|
||||
|
||||
build_id = results_sorted[0].build_id
|
||||
logs_bucket_name = (results_sorted[0].logs_bucket).removeprefix("gs://")
|
||||
log_file_name = f"log-{build_id}.txt"
|
||||
|
||||
log_contents = util.download_blob_into_memory(
|
||||
bucket_name=logs_bucket_name,
|
||||
blob_name=log_file_name,
|
||||
download_as_text=True,
|
||||
)
|
||||
|
||||
# Remove extra steps from the log
|
||||
match = re.search("starting Step #4", log_contents, flags=re.IGNORECASE)
|
||||
|
||||
if match is not None:
|
||||
match_index = match.span()[0]
|
||||
print(log_contents[match_index:])
|
||||
else:
|
||||
print(log_contents)
|
||||
|
||||
print("\n=== END RESULTS===\n")
|
||||
|
||||
total_notebook_duration = functools.reduce(
|
||||
@@ -433,25 +504,5 @@ def process_and_execute_notebooks(
|
||||
# Raise error if any notebooks failed
|
||||
if not all([result.is_pass for result in results_sorted]):
|
||||
raise RuntimeError("Notebook failures detected. See logs for details")
|
||||
|
||||
elif len(notebooks) == 1:
|
||||
notebook = notebooks[0]
|
||||
|
||||
# Pre-process notebook by substituting variable names
|
||||
_process_notebook(
|
||||
notebook_path=notebook,
|
||||
variable_project_id=variable_project_id,
|
||||
variable_region=variable_region,
|
||||
variable_service_account=variable_service_account,
|
||||
variable_vpc_network=variable_vpc_network,
|
||||
)
|
||||
|
||||
execute_notebook_helper.execute_notebook(
|
||||
notebook_source=notebook,
|
||||
output_file_or_uri="/".join(
|
||||
[artifacts_bucket, pathlib.Path(notebook).name]
|
||||
),
|
||||
should_log_output=True,
|
||||
)
|
||||
else:
|
||||
print("No notebooks modified in this pull request.")
|
||||
|
||||
@@ -26,6 +26,9 @@ from utils import util
|
||||
|
||||
# This script is used to execute a notebook and write out the output notebook.
|
||||
|
||||
# This is used to force papermill to use this kernel to run the notebook instead of any defined inside the notebook itself
|
||||
DEFAULT_KERNEL_NAME = "python3"
|
||||
|
||||
|
||||
def execute_notebook(
|
||||
notebook_source: str,
|
||||
@@ -50,14 +53,11 @@ def execute_notebook(
|
||||
|
||||
execution_exception = None
|
||||
|
||||
|
||||
print("\n=== DOWNLOAD EXECUTED NOTEBOOK ===\n")
|
||||
print(
|
||||
f"Please debug the executed notebook by downloading the executed notebook:"
|
||||
)
|
||||
print(f"Please debug the executed notebook by downloading the executed notebook:")
|
||||
|
||||
print("Option 1. Using gsutil. Run the following command in your terminal.")
|
||||
print(f"\tgsutil cp \"{output_file_or_uri}\" .")
|
||||
print(f'\tgsutil cp "{output_file_or_uri}" .')
|
||||
|
||||
print("Option 2. Using this link.")
|
||||
print(f"\thttps://storage.googleapis.com/{output_file_or_uri[5:]}")
|
||||
@@ -72,6 +72,7 @@ def execute_notebook(
|
||||
output_path=notebook_source,
|
||||
progress_bar=should_log_output,
|
||||
request_save_on_cell_execute=should_log_output,
|
||||
kernel_name=DEFAULT_KERNEL_NAME,
|
||||
log_output=should_log_output,
|
||||
stdout_file=sys.stdout if should_log_output else None,
|
||||
stderr_file=sys.stderr if should_log_output else None,
|
||||
|
||||
@@ -40,6 +40,7 @@ def execute_notebook_remote(
|
||||
private_pool_region: Optional[str],
|
||||
tag: Optional[str],
|
||||
timeout_in_seconds: Optional[int] = None,
|
||||
python_version: Optional[str] = None
|
||||
) -> operation.Operation:
|
||||
"""Create and execute a single notebook on Google Cloud Build"""
|
||||
# Load build steps from YAML
|
||||
@@ -50,8 +51,12 @@ def execute_notebook_remote(
|
||||
"_PYTHON_IMAGE": container_uri,
|
||||
"_NOTEBOOK_GCS_URI": notebook_uri,
|
||||
"_NOTEBOOK_OUTPUT_GCS_URI": notebook_output_uri,
|
||||
"_PYTHON_VERSION" : f"python{python_version}"
|
||||
}
|
||||
|
||||
if python_version is not None:
|
||||
substitutions["_PYTHON_VERSION"] = "python" + python_version
|
||||
|
||||
build = cloudbuild_v1.Build()
|
||||
|
||||
options: Optional[client_options.ClientOptions] = None
|
||||
|
||||
@@ -10,21 +10,21 @@ steps:
|
||||
entrypoint: /bin/sh
|
||||
args:
|
||||
- -c
|
||||
- python3 .cloud-build/CheckPythonVersion.py -q
|
||||
- ${_PYTHON_VERSION} .cloud-build/CheckPythonVersion.py -q
|
||||
# Create a virtual environment
|
||||
- name: ${_PYTHON_IMAGE}
|
||||
entrypoint: /bin/sh
|
||||
args:
|
||||
- -c
|
||||
- python3 -m venv workspace/env
|
||||
- ${_PYTHON_VERSION} -m venv workspace/env
|
||||
# Install Python dependencies
|
||||
- name: ${_PYTHON_IMAGE}
|
||||
entrypoint: /bin/sh
|
||||
args:
|
||||
- -c
|
||||
- . workspace/env/bin/activate &&
|
||||
python3 -m pip -q install -U pip &&
|
||||
python3 -m pip -q install -U -r .cloud-build/requirements.txt
|
||||
python -m pip -q install -U pip &&
|
||||
python -m pip -q install -U -r .cloud-build/requirements.txt
|
||||
# Install Python dependencies and run testing script
|
||||
- name: ${_PYTHON_IMAGE}
|
||||
entrypoint: /bin/sh
|
||||
@@ -32,7 +32,7 @@ steps:
|
||||
- -c
|
||||
- |
|
||||
. workspace/env/bin/activate &&
|
||||
python3 .cloud-build/execute_notebook_cli.py --notebook_source "${_NOTEBOOK_GCS_URI}" --output_file_or_uri "${_NOTEBOOK_OUTPUT_GCS_URI}"
|
||||
python .cloud-build/execute_notebook_cli.py --notebook_source "${_NOTEBOOK_GCS_URI}" --output_file_or_uri "${_NOTEBOOK_OUTPUT_GCS_URI}"
|
||||
env:
|
||||
- 'IS_TESTING=1'
|
||||
timeout: 86400s
|
||||
|
||||
@@ -10,4 +10,4 @@ google-cloud-aiplatform
|
||||
google-cloud-storage
|
||||
google-cloud-build
|
||||
ratemate
|
||||
GitPython
|
||||
GitPython
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
notebooks/official/vizier/gapic-vizier-multi-objective-optimization.ipynb
|
||||
notebooks/official/pipelines/lightweight_functions_component_io_kfp.ipynb
|
||||
notebooks/official/ml_metadata/sdk-metric-parameter-tracking-for-locally-trained-models.ipynb
|
||||
notebooks/official/pipelines/metrics_viz_run_compare_kfp.ipynb
|
||||
notebooks/official/matching_engine/sdk_matching_engine_for_indexing.ipynb
|
||||
notebooks/official/custom/custom-tabular-bq-managed-dataset.ipynb
|
||||
.cloud-build/tests/python_version_test.ipynb
|
||||
|
||||
@@ -1 +1 @@
|
||||
notebooks/official/pipelines/metrics_viz_run_compare_kfp.ipynb
|
||||
notebooks/official/custom/custom-tabular-bq-managed-dataset.ipynb
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "57a3d44ed8a8"
|
||||
},
|
||||
"source": [
|
||||
"### Set up your Google Cloud project\n",
|
||||
"\n",
|
||||
"**_NOTE_**: This notebook has been tested in the following environment:\n",
|
||||
"\n",
|
||||
"* Python version = 3.7\n",
|
||||
"\n",
|
||||
"**The following steps are required, regardless of your notebook environment.**\n",
|
||||
"\n",
|
||||
"1. [Select or create a Google Cloud project](https://console.cloud.google.com/cloud-resource-manager). When you first create an account, you get a $300 free credit towards your compute/storage costs.\n",
|
||||
"\n",
|
||||
"1. [Make sure that billing is enabled for your project](https://cloud.google.com/billing/docs/how-to/modify-project).\n",
|
||||
"\n",
|
||||
"1. [Enable the Vertex AI API and Compute Engine API](https://console.cloud.google.com/flows/enableapi?apiid=aiplatform.googleapis.com,compute_component).\n",
|
||||
"\n",
|
||||
"1. If you are running this notebook locally, you will need to install the [Cloud SDK](https://cloud.google.com/sdk).\n",
|
||||
"\n",
|
||||
"1. Enter your project ID in the cell below. Then run the cell to make sure the\n",
|
||||
"Cloud SDK uses the right project for all the commands in this notebook.\n",
|
||||
"\n",
|
||||
"**Note**: Jupyter runs lines prefixed with `!` as shell commands, and it interpolates Python variables prefixed with `$` into these commands."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "c6516f90311b"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# test if the right python version is being used\n",
|
||||
"import sys\n",
|
||||
"\n",
|
||||
"actual_python_version = f\"{sys.version_info.major}.{sys.version_info.minor}\"\n",
|
||||
"print(f\"Runtime python version: {actual_python_version}\")\n",
|
||||
"\n",
|
||||
"assert actual_python_version == \"3.7\", \"Wrong python version!\""
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"colab": {
|
||||
"name": "python_version_test.ipynb",
|
||||
"toc_visible": true
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"name": "python3"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 0
|
||||
}
|
||||
@@ -14,6 +14,8 @@
|
||||
# limitations under the License.
|
||||
|
||||
from typing import Dict
|
||||
import random
|
||||
import string
|
||||
|
||||
from nbconvert.preprocessors import Preprocessor
|
||||
|
||||
@@ -63,3 +65,36 @@ class UpdateVariablesPreprocessor(Preprocessor):
|
||||
executable_cells.append(cell)
|
||||
notebook.cells = executable_cells
|
||||
return notebook, resources
|
||||
|
||||
|
||||
# Generate a uuid of a specifed length
|
||||
def generate_uuid(length: int = 8) -> str:
|
||||
return "".join(random.choices(string.ascii_lowercase + string.digits, k=length))
|
||||
|
||||
|
||||
class UniqueStringsPreprocessor(Preprocessor):
|
||||
# A preprocessor that replaces strings that end with "-unique" or "_unique" with a uuid.
|
||||
|
||||
@staticmethod
|
||||
def update_unique_strings(content: str):
|
||||
# Replace strings that end with "-unique" or "_unique" with a uuid.
|
||||
|
||||
unique_id = generate_uuid()
|
||||
return (
|
||||
content.replace('-unique"', f'-{unique_id}"')
|
||||
.replace("-unique'", f'-{unique_id}"')
|
||||
.replace('_unique"', f'_{unique_id}"')
|
||||
.replace("_unique'", f'_{unique_id}"')
|
||||
)
|
||||
|
||||
def preprocess(self, notebook, resources=None):
|
||||
executable_cells = []
|
||||
for cell in notebook.cells:
|
||||
if cell.cell_type == "code":
|
||||
cell.source = self.update_unique_strings(
|
||||
content=cell.source,
|
||||
)
|
||||
|
||||
executable_cells.append(cell)
|
||||
notebook.cells = executable_cells
|
||||
return notebook, resources
|
||||
|
||||
@@ -40,65 +40,3 @@ def get_updated_value(content: str, variable_name: str, variable_value: str) ->
|
||||
content,
|
||||
flags=re.M,
|
||||
)
|
||||
|
||||
|
||||
def test_update_value():
|
||||
new_content = get_updated_value(
|
||||
content='asdf\nPROJECT_ID = "[your-project-id]" #@param {type:"string"} \nasdf',
|
||||
variable_name="PROJECT_ID",
|
||||
variable_value="sample-project",
|
||||
)
|
||||
assert (
|
||||
new_content
|
||||
== 'asdf\nPROJECT_ID = "sample-project" #@param {type:"string"} \nasdf'
|
||||
)
|
||||
|
||||
|
||||
def test_update_value_single_quotes():
|
||||
new_content = get_updated_value(
|
||||
content="PROJECT_ID = '[your-project-id]'",
|
||||
variable_name="PROJECT_ID",
|
||||
variable_value="sample-project",
|
||||
)
|
||||
assert new_content == "PROJECT_ID = 'sample-project'"
|
||||
|
||||
|
||||
def test_update_value_avoidance():
|
||||
new_content = get_updated_value(
|
||||
content="PROJECT_ID = shell_output[0] ",
|
||||
variable_name="PROJECT_ID",
|
||||
variable_value="sample-project",
|
||||
)
|
||||
assert new_content == "PROJECT_ID = shell_output[0] "
|
||||
|
||||
|
||||
def test_region():
|
||||
new_content = get_updated_value(
|
||||
content='REGION = "[your-region]" # @param {type:"string"}',
|
||||
variable_name="REGION",
|
||||
variable_value="us-central1",
|
||||
)
|
||||
assert new_content == 'REGION = "us-central1" # @param {type:"string"}'
|
||||
|
||||
|
||||
def test_region_equal_equals_ignore():
|
||||
# Tests that == is ignored
|
||||
new_content = get_updated_value(
|
||||
content='REGION == "[your-region]" # @param {type:"string"}',
|
||||
variable_name="REGION",
|
||||
variable_value="us-central1",
|
||||
)
|
||||
assert new_content == 'REGION == "[your-region]" # @param {type:"string"}'
|
||||
|
||||
|
||||
def test_service_account():
|
||||
# Tests that == is ignored
|
||||
new_content = get_updated_value(
|
||||
content='SERVICE_ACCOUNT = "[your-service-account]" # @param {type:"string"}',
|
||||
variable_name="SERVICE_ACCOUNT",
|
||||
variable_value="12345-compute@developer.gserviceaccount.com",
|
||||
)
|
||||
assert (
|
||||
new_content
|
||||
== 'SERVICE_ACCOUNT = "12345-compute@developer.gserviceaccount.com" # @param {type:"string"}'
|
||||
)
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
from utils import NotebookProcessors
|
||||
|
||||
|
||||
def test_update_value():
|
||||
# Test that the content was updated
|
||||
preprocessor = NotebookProcessors.UniqueStringsPreprocessor()
|
||||
|
||||
content = 'PROJECT_ID = "your-project-id-unique"'
|
||||
|
||||
new_content = preprocessor.update_unique_strings(content)
|
||||
|
||||
assert new_content != content
|
||||
assert new_content.startswith('PROJECT_ID = "your-project-id-')
|
||||
assert new_content.endswith('"')
|
||||
@@ -0,0 +1,63 @@
|
||||
from utils import UpdateNotebookVariables
|
||||
|
||||
|
||||
def test_update_value():
|
||||
new_content = UpdateNotebookVariables.get_updated_value(
|
||||
content='asdf\nPROJECT_ID = "[your-project-id]" #@param {type:"string"} \nasdf',
|
||||
variable_name="PROJECT_ID",
|
||||
variable_value="sample-project",
|
||||
)
|
||||
assert (
|
||||
new_content
|
||||
== 'asdf\nPROJECT_ID = "sample-project" #@param {type:"string"} \nasdf'
|
||||
)
|
||||
|
||||
|
||||
def test_update_value_single_quotes():
|
||||
new_content = UpdateNotebookVariables.get_updated_value(
|
||||
content="PROJECT_ID = '[your-project-id]'",
|
||||
variable_name="PROJECT_ID",
|
||||
variable_value="sample-project",
|
||||
)
|
||||
assert new_content == "PROJECT_ID = 'sample-project'"
|
||||
|
||||
|
||||
def test_update_value_avoidance():
|
||||
new_content = UpdateNotebookVariables.get_updated_value(
|
||||
content="PROJECT_ID = shell_output[0] ",
|
||||
variable_name="PROJECT_ID",
|
||||
variable_value="sample-project",
|
||||
)
|
||||
assert new_content == "PROJECT_ID = shell_output[0] "
|
||||
|
||||
|
||||
def test_region():
|
||||
new_content = UpdateNotebookVariables.get_updated_value(
|
||||
content='REGION = "[your-region]" # @param {type:"string"}',
|
||||
variable_name="REGION",
|
||||
variable_value="us-central1",
|
||||
)
|
||||
assert new_content == 'REGION = "us-central1" # @param {type:"string"}'
|
||||
|
||||
|
||||
def test_region_equal_equals_ignore():
|
||||
# Tests that == is ignored
|
||||
new_content = UpdateNotebookVariables.get_updated_value(
|
||||
content='REGION == "[your-region]" # @param {type:"string"}',
|
||||
variable_name="REGION",
|
||||
variable_value="us-central1",
|
||||
)
|
||||
assert new_content == 'REGION == "[your-region]" # @param {type:"string"}'
|
||||
|
||||
|
||||
def test_service_account():
|
||||
# Tests that == is ignored
|
||||
new_content = UpdateNotebookVariables.get_updated_value(
|
||||
content='SERVICE_ACCOUNT = "[your-service-account]" # @param {type:"string"}',
|
||||
variable_name="SERVICE_ACCOUNT",
|
||||
variable_value="12345-compute@developer.gserviceaccount.com",
|
||||
)
|
||||
assert (
|
||||
new_content
|
||||
== 'SERVICE_ACCOUNT = "12345-compute@developer.gserviceaccount.com" # @param {type:"string"}'
|
||||
)
|
||||
@@ -3,7 +3,7 @@ import subprocess
|
||||
import tarfile
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
from typing import Optional, Union
|
||||
|
||||
from google.auth import credentials as auth_credentials
|
||||
from google.cloud import storage
|
||||
@@ -58,3 +58,29 @@ def archive_code_and_upload(staging_bucket: str):
|
||||
print(f"Uploaded source code archive to {source_archived_file_gcs}")
|
||||
|
||||
return source_archived_file_gcs
|
||||
|
||||
|
||||
def download_blob_into_memory(
|
||||
bucket_name: str, blob_name: str, download_as_text: Optional[bool] = False
|
||||
) -> Union[bytes, str]:
|
||||
"""
|
||||
Downloads a blob into memory as byte or as text if
|
||||
download_as_text is set to True.
|
||||
"""
|
||||
|
||||
storage_client = storage.Client()
|
||||
|
||||
bucket = storage_client.bucket(bucket_name)
|
||||
|
||||
# Construct a client side representation of a blob.
|
||||
blob = bucket.blob(blob_name)
|
||||
|
||||
# Download the blob content
|
||||
if download_as_text:
|
||||
contents = blob.download_as_text()
|
||||
else:
|
||||
contents = blob.download_as_bytes()
|
||||
|
||||
print(f"Downloaded storage object {blob_name} from bucket {bucket_name}.")
|
||||
|
||||
return contents
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
# To use this image, run this command with the desired notebook args from the top-level vertex-ai-samples directory:
|
||||
# 1. To lint all changed notebooks:
|
||||
# docker run -v ${PWD}:/setup/app gcr.io/python-docs-samples-tests/notebook_linter:latest
|
||||
# 2. To lint specific notebooks:
|
||||
# docker run -v ${PWD}:/setup/app gcr.io/python-docs-samples-tests/notebook_linter:latest notebooks/1.ipynb notebooks/2.ipynb
|
||||
|
||||
FROM python:3.10
|
||||
|
||||
WORKDIR setup
|
||||
|
||||
COPY ./requirements.txt .
|
||||
COPY ./run_linter.sh .
|
||||
|
||||
# Install dependencies.
|
||||
RUN pip install --upgrade pip
|
||||
RUN pip install -r requirements.txt
|
||||
|
||||
WORKDIR app
|
||||
|
||||
ENTRYPOINT ["/setup/run_linter.sh"]
|
||||
@@ -2,9 +2,9 @@ git+https://github.com/tensorflow/docs
|
||||
ipython
|
||||
jupyter
|
||||
nbconvert
|
||||
black==22.6.0
|
||||
pyupgrade==2.34.0
|
||||
black==22.10.0
|
||||
pyupgrade==2.38.4
|
||||
isort==5.10.1
|
||||
flake8==4.0.1
|
||||
nbqa==1.4.0
|
||||
nbqa==1.5.3
|
||||
|
||||
|
||||
@@ -47,12 +47,22 @@ done
|
||||
|
||||
echo "Test mode: $is_test"
|
||||
|
||||
# Read in user-provided notebooks
|
||||
notebooks=()
|
||||
for arg in "$@"; do
|
||||
if [[ $arg == *.ipynb ]]; then
|
||||
notebooks+=("$arg")
|
||||
fi
|
||||
done
|
||||
|
||||
# Only check notebooks in test folders modified in this pull request.
|
||||
# Note: Use process substitution to persist the data in the array
|
||||
notebooks=()
|
||||
while read -r file || [ -n "$line" ]; do
|
||||
notebooks+=("$file")
|
||||
done < <(git diff --name-only main... | grep '\.ipynb$')
|
||||
if [ ${#notebooks[@]} -eq 0 ]; then
|
||||
echo "Checking for changed notebooked using git"
|
||||
while read -r file || [ -n "$line" ]; do
|
||||
notebooks+=("$file")
|
||||
done < <(git diff --name-only main... | grep '\.ipynb$')
|
||||
fi
|
||||
|
||||
problematic_notebooks=()
|
||||
if [ ${#notebooks[@]} -gt 0 ]; then
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
* @vertex-ai-samples-contributors @GoogleCloudPlatform/cloudml-samples-owners
|
||||
/tf_agents_bandits_movie_recommendation_with_kfp_and_vertex_sdk @yinghsienwu
|
||||
/pytorch_pre_built_images_deployment @googleapis/vertex-prediction-team
|
||||
/pytorch_text_classification_using_vertex_sdk_and_gcloud @RajeshThallam
|
||||
/pytorch_text_classification_using_vertex_sdk_and_gcloud @RajeshThallam @ultrons
|
||||
/sklearn_text_classification_from_script_using_vertex_sdk @maxhardt
|
||||
/pluto_on_workbench @wkharold
|
||||
/cpr-examples @samthrasher
|
||||
/Train_tabular_models_with_many_frameworks_and_import_to_Vertex_AI_using_Pipelines @Ark-kun
|
||||
/pipeline_components @Ark-kun
|
||||
/pipeline_components/image_ml_model_training @lakeyk
|
||||
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
name: Train tabular classification logistic regression model using Scikit learn pipeline
|
||||
metadata:
|
||||
annotations:
|
||||
author: Alexey Volkov <alexey.volkov@ark-kun.com>
|
||||
canonical_location: https://raw.githubusercontent.com/Ark-kun/pipeline_components/master/samples/Google_Cloud_Vertex_AI/Train_tabular_classification_logistic_regression_model_using_Scikit_learn_and_import_to_Vertex_AI/pipeline.component.yaml
|
||||
sdk: https://cloud-pipelines.net/pipeline-editor/
|
||||
implementation:
|
||||
graph:
|
||||
tasks:
|
||||
Download from GCS:
|
||||
componentRef:
|
||||
digest: 4175c9ff143cb8cc75d05451c0a0ebdf5a0d6d020816e29f5e9cefbb7d56f241
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/27a5ea25e849c9e8c0cb6ed65518bc3ece259aaf/components/google-cloud/storage/download/workaround_for_buggy_KFPv2_compiler/component.yaml
|
||||
arguments:
|
||||
GCS path: gs://ml-pipeline-dataset/Chicago_taxi_trips/chicago_taxi_trips_2019-01-01_-_2019-02-01_limit=10000.csv
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":40,"width":180,"height":40}'
|
||||
Select columns using Pandas on CSV data:
|
||||
componentRef:
|
||||
digest: 9b9500f461c1d04f1e48992de9138db14a6800f23649d73048673d5ea6dc56ad
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/8c78aae096806cff3bc331a40566f42f5c3e9d4b/components/pandas/Select_columns/in_CSV_format/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: Data
|
||||
taskId: Download from GCS
|
||||
column_names: '["tips", "trip_seconds", "trip_miles", "pickup_community_area", "dropoff_community_area", "fare", "tolls", "extras"]'
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":140,"width":180,"height":54}'
|
||||
Fill all missing values using Pandas on CSV data:
|
||||
componentRef:
|
||||
digest: a1b0c29a4615f2e3652aa5d31b9255fa15700e146627c755f8fc172f82e71af7
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/23405971f5f16a41b16c343129b893c52e4d1d48/components/pandas/Fill_all_missing_values/in_CSV_format/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: transformed_table
|
||||
taskId: Select columns using Pandas on CSV data
|
||||
type: CSV
|
||||
replacement_value: '0'
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":250,"width":180,"height":54}'
|
||||
Binarize column using Pandas on CSV data:
|
||||
componentRef:
|
||||
digest: d699afd4d7cae862708717cc160f4394ed0c04e536e9515923ef1e8865f01d44
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/1e2558325f4c708aca75827c8acc13d230ee7e9f/components/pandas/Binarize_column/in_CSV_format/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: transformed_table
|
||||
taskId: Fill all missing values using Pandas on CSV data
|
||||
type: CSV
|
||||
column_name: tips
|
||||
predicate: '> 0'
|
||||
new_column_name: class
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":380,"width":180,"height":54}'
|
||||
Train logistic regression model using scikit learn from CSV:
|
||||
componentRef:
|
||||
digest: a864625a822e4b1c8ef6fe4ae1454fd90f15438f70a6712bb4c30e0dda4d35b7
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/cb44b75c9c062fcc40c2b905b2024b4493dbc62b/components/ML_frameworks/Scikit_learn/Train_logistic_regression_model/from_CSV/component.yaml
|
||||
arguments:
|
||||
dataset:
|
||||
taskOutput:
|
||||
outputName: transformed_table
|
||||
taskId: Binarize column using Pandas on CSV data
|
||||
type: CSV
|
||||
label_column_name: class
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":510,"width":180,"height":70}'
|
||||
Upload Scikit learn pickle model to Google Cloud Vertex AI:
|
||||
componentRef:
|
||||
digest: 81c91c8d7d21ec97e0872f669d68bd89edea87279d703685db54aa94743bebcd
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/c6a8b67d1ada2cc17665c99ff6b410df588bee28/components/google-cloud/Vertex_AI/Models/Upload_Scikit-learn_pickle_model/workaround_for_buggy_KFPv2_compiler/component.yaml
|
||||
arguments:
|
||||
model:
|
||||
taskOutput:
|
||||
outputName: model
|
||||
taskId: Train logistic regression model using scikit learn from CSV
|
||||
type: ScikitLearnPickleModel
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":660,"width":180,"height":70}'
|
||||
outputValues: {}
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
# python3 -m pip install "kfp<2.0.0" "google-cloud-aiplatform>=1.16.0" --upgrade --quiet
|
||||
from kfp import components
|
||||
|
||||
# %% Loading components
|
||||
download_from_gcs_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/storage/download/component.yaml")
|
||||
select_columns_using_Pandas_on_CSV_data_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/pandas/Select_columns/in_CSV_format/component.yaml")
|
||||
fill_all_missing_values_using_Pandas_on_CSV_data_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/pandas/Fill_all_missing_values/in_CSV_format/component.yaml")
|
||||
binarize_column_using_Pandas_on_CSV_data_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/pandas/Binarize_column/in_CSV_format/component.yaml")
|
||||
train_logistic_regression_model_using_scikit_learn_from_CSV_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/1f5cf6e06409b704064b2086c0a705e4e6b4fcde/community-content/pipeline_components/ML_frameworks/Scikit_learn/Train_logistic_regression_model/from_CSV/component.yaml")
|
||||
upload_Scikit_learn_pickle_model_to_Google_Cloud_Vertex_AI_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/Vertex_AI/Models/Upload_Scikit-learn_pickle_model/component.yaml")
|
||||
deploy_model_to_endpoint_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/Vertex_AI/Models/Deploy_to_endpoint/component.yaml")
|
||||
|
||||
# %% Pipeline definition
|
||||
def train_tabular_classification_logistic_regression_model_using_Scikit_learn_pipeline():
|
||||
dataset_gcs_uri = "gs://ml-pipeline-dataset/Chicago_taxi_trips/chicago_taxi_trips_2019-01-01_-_2019-02-01_limit=10000.csv"
|
||||
feature_columns = ["trip_seconds", "trip_miles", "pickup_community_area", "dropoff_community_area", "fare", "tolls", "extras"] # Excluded "trip_total"
|
||||
label_column = "tips"
|
||||
# Deploying the model might incur additional costs over time
|
||||
deploy_model = False
|
||||
|
||||
classification_label_column = "class"
|
||||
all_columns = [label_column] + feature_columns
|
||||
|
||||
training_data = download_from_gcs_op(
|
||||
gcs_path=dataset_gcs_uri
|
||||
).outputs["Data"]
|
||||
|
||||
training_data = select_columns_using_Pandas_on_CSV_data_op(
|
||||
table=training_data,
|
||||
column_names=all_columns,
|
||||
).outputs["transformed_table"]
|
||||
|
||||
# Cleaning the NaN values.
|
||||
training_data = fill_all_missing_values_using_Pandas_on_CSV_data_op(
|
||||
table=training_data,
|
||||
replacement_value="0",
|
||||
#replacement_type_name="float",
|
||||
).outputs["transformed_table"]
|
||||
|
||||
classification_training_data = binarize_column_using_Pandas_on_CSV_data_op(
|
||||
table=training_data,
|
||||
column_name=label_column,
|
||||
predicate="> 0",
|
||||
new_column_name=classification_label_column,
|
||||
).outputs["transformed_table"]
|
||||
|
||||
model = train_logistic_regression_model_using_scikit_learn_from_CSV_op(
|
||||
dataset=classification_training_data,
|
||||
label_column_name=classification_label_column,
|
||||
# Optional:
|
||||
#penalty="l2",
|
||||
#solver="lbfgs",
|
||||
#max_iterations=100,
|
||||
#multi_class_mode="auto",
|
||||
#random_seed=0,
|
||||
).outputs["model"]
|
||||
|
||||
vertex_model_name = upload_Scikit_learn_pickle_model_to_Google_Cloud_Vertex_AI_op(
|
||||
model=model,
|
||||
).outputs["model_name"]
|
||||
|
||||
# Deploying the model might incur additional costs over time
|
||||
if deploy_model:
|
||||
sklearn_vertex_endpoint_name = deploy_model_to_endpoint_op(
|
||||
model_name=vertex_model_name,
|
||||
).outputs["endpoint_name"]
|
||||
|
||||
pipeline_func = train_tabular_classification_logistic_regression_model_using_Scikit_learn_pipeline
|
||||
|
||||
# %% Pipeline submission
|
||||
if __name__ == '__main__':
|
||||
from google.cloud import aiplatform
|
||||
aiplatform.PipelineJob.from_pipeline_func(pipeline_func=pipeline_func).submit()
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
name: Train tabular classification model using PyTorch pipeline
|
||||
metadata:
|
||||
annotations:
|
||||
author: Alexey Volkov <alexey.volkov@ark-kun.com>
|
||||
canonical_location: https://raw.githubusercontent.com/Ark-kun/pipeline_components/master/samples/Google_Cloud_Vertex_AI/Train_tabular_classification_model_using_PyTorch_and_import_to_Vertex_AI/pipeline.component.yaml
|
||||
sdk: https://cloud-pipelines.net/pipeline-editor/
|
||||
implementation:
|
||||
graph:
|
||||
tasks:
|
||||
Download from GCS:
|
||||
componentRef:
|
||||
digest: 4175c9ff143cb8cc75d05451c0a0ebdf5a0d6d020816e29f5e9cefbb7d56f241
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/27a5ea25e849c9e8c0cb6ed65518bc3ece259aaf/components/google-cloud/storage/download/workaround_for_buggy_KFPv2_compiler/component.yaml
|
||||
arguments:
|
||||
GCS path: gs://ml-pipeline-dataset/Chicago_taxi_trips/chicago_taxi_trips_2019-01-01_-_2019-02-01_limit=10000.csv
|
||||
annotations:
|
||||
editor.position: '{"x":240,"y":40,"width":180,"height":40}'
|
||||
Select columns using Pandas on CSV data:
|
||||
componentRef:
|
||||
digest: 9b9500f461c1d04f1e48992de9138db14a6800f23649d73048673d5ea6dc56ad
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/8c78aae096806cff3bc331a40566f42f5c3e9d4b/components/pandas/Select_columns/in_CSV_format/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: Data
|
||||
taskId: Download from GCS
|
||||
column_names: '["tips", "trip_seconds", "trip_miles", "pickup_community_area", "dropoff_community_area", "fare", "tolls", "extras"]'
|
||||
annotations:
|
||||
editor.position: '{"x":240,"y":140,"width":180,"height":54}'
|
||||
Fill all missing values using Pandas on CSV data:
|
||||
componentRef:
|
||||
digest: a1b0c29a4615f2e3652aa5d31b9255fa15700e146627c755f8fc172f82e71af7
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/23405971f5f16a41b16c343129b893c52e4d1d48/components/pandas/Fill_all_missing_values/in_CSV_format/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: transformed_table
|
||||
taskId: Select columns using Pandas on CSV data
|
||||
type: CSV
|
||||
replacement_value: '0'
|
||||
annotations:
|
||||
editor.position: '{"x":240,"y":250,"width":180,"height":54}'
|
||||
Create fully connected pytorch network:
|
||||
componentRef:
|
||||
digest: d03d8248fd358a0275ec33568ee7dd7dce576cc112b09dfafe2651e4d97e04a9
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/1a2ef3eeb77bc278f33cad0dd29008ea2431e191/components/PyTorch/Create_fully_connected_network/component.yaml
|
||||
arguments:
|
||||
input_size: '7'
|
||||
hidden_layer_sizes: '[10]'
|
||||
activation_name: elu
|
||||
output_activation_name: sigmoid
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":360,"width":180,"height":54}'
|
||||
Binarize column using Pandas on CSV data:
|
||||
componentRef:
|
||||
digest: d699afd4d7cae862708717cc160f4394ed0c04e536e9515923ef1e8865f01d44
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/1e2558325f4c708aca75827c8acc13d230ee7e9f/components/pandas/Binarize_column/in_CSV_format/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: transformed_table
|
||||
taskId: Fill all missing values using Pandas on CSV data
|
||||
type: CSV
|
||||
column_name: tips
|
||||
predicate: ' > 0'
|
||||
new_column_name: class
|
||||
annotations:
|
||||
editor.position: '{"x":240,"y":360,"width":180,"height":54}'
|
||||
Train pytorch model from csv:
|
||||
componentRef:
|
||||
digest: 40f3185eb61e9727f41a4e0c05dd3d3b44bd802aa0f378cfc31756560033949a
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/d8c4cf5e6403bc65bcf8d606e6baf87e2528a3dc/components/PyTorch/Train_PyTorch_model/from_CSV/component.yaml
|
||||
arguments:
|
||||
model:
|
||||
taskOutput:
|
||||
outputName: model
|
||||
taskId: Create fully connected pytorch network
|
||||
type: PyTorchScriptModule
|
||||
training_data:
|
||||
taskOutput:
|
||||
outputName: transformed_table
|
||||
taskId: Binarize column using Pandas on CSV data
|
||||
type: CSV
|
||||
label_column_name: class
|
||||
loss_function_name: binary_cross_entropy
|
||||
annotations:
|
||||
editor.position: '{"x":240,"y":490,"width":180,"height":40}'
|
||||
Create PyTorch Model Archive with base handler:
|
||||
componentRef:
|
||||
digest: 8298b5ee1b0f0879f893add4cf352c8dec7cf9e21bb9db134c91a2d046cdb0ec
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/46d51383e6554b7f3ab4fd8cf614d8c2b422fb22/components/PyTorch/Create_PyTorch_Model_Archive/with_base_handler/component.yaml
|
||||
arguments:
|
||||
Model:
|
||||
taskOutput:
|
||||
outputName: trained_model
|
||||
taskId: Train pytorch model from csv
|
||||
type: PyTorchScriptModule
|
||||
Model name: model
|
||||
Model version: '1.0'
|
||||
annotations:
|
||||
editor.position: '{"x":240,"y":590,"width":180,"height":54}'
|
||||
Upload PyTorch model archive to Google Cloud Vertex AI:
|
||||
componentRef:
|
||||
digest: 4450212fae7b9001482aca7eb78b28413c205506eccf08a04e7754a8dfa99004
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/c6a8b67d1ada2cc17665c99ff6b410df588bee28/components/google-cloud/Vertex_AI/Models/Upload_PyTorch_model_archive/workaround_for_buggy_KFPv2_compiler/component.yaml
|
||||
arguments:
|
||||
model_archive:
|
||||
taskOutput:
|
||||
outputName: Model archive
|
||||
taskId: Create PyTorch Model Archive with base handler
|
||||
type: PyTorchModelArchive
|
||||
annotations:
|
||||
editor.position: '{"x":240,"y":720,"width":180,"height":70}'
|
||||
outputValues: {}
|
||||
+95
@@ -0,0 +1,95 @@
|
||||
# python3 -m pip install "kfp<2.0.0" "google-cloud-aiplatform>=1.16.0" --upgrade --quiet
|
||||
from kfp import components
|
||||
|
||||
# %% Loading components
|
||||
download_from_gcs_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/storage/download/component.yaml")
|
||||
select_columns_using_Pandas_on_CSV_data_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/pandas/Select_columns/in_CSV_format/component.yaml")
|
||||
fill_all_missing_values_using_Pandas_on_CSV_data_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/pandas/Fill_all_missing_values/in_CSV_format/component.yaml")
|
||||
binarize_column_using_Pandas_on_CSV_data_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/pandas/Binarize_column/in_CSV_format/component.yaml")
|
||||
create_fully_connected_pytorch_network_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/PyTorch/Create_fully_connected_network/component.yaml")
|
||||
train_pytorch_model_from_csv_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/PyTorch/Train_PyTorch_model/from_CSV/component.yaml")
|
||||
create_pytorch_model_archive_with_base_handler_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/PyTorch/Create_PyTorch_Model_Archive/with_base_handler/component.yaml")
|
||||
upload_PyTorch_model_archive_to_Google_Cloud_Vertex_AI_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/Vertex_AI/Models/Upload_PyTorch_model_archive/component.yaml")
|
||||
deploy_model_to_endpoint_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/Vertex_AI/Models/Deploy_to_endpoint/component.yaml")
|
||||
|
||||
# %% Pipeline definition
|
||||
def train_tabular_classification_model_using_PyTorch_pipeline():
|
||||
dataset_gcs_uri = "gs://ml-pipeline-dataset/Chicago_taxi_trips/chicago_taxi_trips_2019-01-01_-_2019-02-01_limit=10000.csv"
|
||||
feature_columns = ["trip_seconds", "trip_miles", "pickup_community_area", "dropoff_community_area", "fare", "tolls", "extras"] # Excluded "trip_total"
|
||||
label_column = "tips"
|
||||
# Deploying the model might incur additional costs over time
|
||||
deploy_model = False
|
||||
|
||||
classification_label_column = "class"
|
||||
all_columns = [label_column] + feature_columns
|
||||
|
||||
training_data = download_from_gcs_op(
|
||||
gcs_path=dataset_gcs_uri
|
||||
).outputs["Data"]
|
||||
|
||||
training_data = select_columns_using_Pandas_on_CSV_data_op(
|
||||
table=training_data,
|
||||
column_names=all_columns,
|
||||
).outputs["transformed_table"]
|
||||
|
||||
# Cleaning the NaN values.
|
||||
training_data = fill_all_missing_values_using_Pandas_on_CSV_data_op(
|
||||
table=training_data,
|
||||
replacement_value="0",
|
||||
#replacement_type_name="float",
|
||||
).outputs["transformed_table"]
|
||||
|
||||
classification_training_data = binarize_column_using_Pandas_on_CSV_data_op(
|
||||
table=training_data,
|
||||
column_name=label_column,
|
||||
predicate=" > 0",
|
||||
new_column_name=classification_label_column,
|
||||
).outputs["transformed_table"]
|
||||
|
||||
network = create_fully_connected_pytorch_network_op(
|
||||
input_size=len(feature_columns),
|
||||
# Optional:
|
||||
hidden_layer_sizes=[10],
|
||||
activation_name="elu",
|
||||
output_activation_name="sigmoid",
|
||||
# output_size=1,
|
||||
).outputs["model"]
|
||||
|
||||
model = train_pytorch_model_from_csv_op(
|
||||
model=network,
|
||||
training_data=classification_training_data,
|
||||
label_column_name=classification_label_column,
|
||||
loss_function_name="binary_cross_entropy",
|
||||
# Optional:
|
||||
#number_of_epochs=1,
|
||||
#learning_rate=0.1,
|
||||
#optimizer_name="Adadelta",
|
||||
#optimizer_parameters={},
|
||||
#batch_size=32,
|
||||
#batch_log_interval=100,
|
||||
#random_seed=0,
|
||||
).outputs["trained_model"]
|
||||
|
||||
model_archive = create_pytorch_model_archive_with_base_handler_op(
|
||||
model=model,
|
||||
# Optional:
|
||||
# model_name="model",
|
||||
# model_version="1.0",
|
||||
).outputs["Model archive"]
|
||||
|
||||
vertex_model_name = upload_PyTorch_model_archive_to_Google_Cloud_Vertex_AI_op(
|
||||
model_archive=model_archive,
|
||||
).outputs["model_name"]
|
||||
|
||||
# Deploying the model might incur additional costs over time
|
||||
if deploy_model:
|
||||
vertex_endpoint_name = deploy_model_to_endpoint_op(
|
||||
model_name=vertex_model_name,
|
||||
).outputs["endpoint_name"]
|
||||
|
||||
pipeline_func=train_tabular_classification_model_using_PyTorch_pipeline
|
||||
|
||||
# %% Pipeline submission
|
||||
if __name__ == '__main__':
|
||||
from google.cloud import aiplatform
|
||||
aiplatform.PipelineJob.from_pipeline_func(pipeline_func=pipeline_func).submit()
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
name: Train tabular classification model using TensorFlow pipeline
|
||||
metadata:
|
||||
annotations:
|
||||
author: Alexey Volkov <alexey.volkov@ark-kun.com>
|
||||
canonical_location: https://raw.githubusercontent.com/Ark-kun/pipeline_components/master/samples/Google_Cloud_Vertex_AI/Train_tabular_classification_model_using_TensorFlow_and_import_to_Vertex_AI/pipeline.component.yaml
|
||||
sdk: https://cloud-pipelines.net/pipeline-editor/
|
||||
implementation:
|
||||
graph:
|
||||
tasks:
|
||||
Download from GCS:
|
||||
componentRef:
|
||||
digest: 4175c9ff143cb8cc75d05451c0a0ebdf5a0d6d020816e29f5e9cefbb7d56f241
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/27a5ea25e849c9e8c0cb6ed65518bc3ece259aaf/components/google-cloud/storage/download/workaround_for_buggy_KFPv2_compiler/component.yaml
|
||||
arguments:
|
||||
GCS path: gs://ml-pipeline-dataset/Chicago_taxi_trips/chicago_taxi_trips_2019-01-01_-_2019-02-01_limit=10000.csv
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":40,"width":180,"height":40}'
|
||||
Select columns using Pandas on CSV data:
|
||||
componentRef:
|
||||
digest: 9b9500f461c1d04f1e48992de9138db14a6800f23649d73048673d5ea6dc56ad
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/8c78aae096806cff3bc331a40566f42f5c3e9d4b/components/pandas/Select_columns/in_CSV_format/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: Data
|
||||
taskId: Download from GCS
|
||||
column_names: '["tips", "trip_seconds", "trip_miles", "pickup_community_area", "dropoff_community_area", "fare", "tolls", "extras"]'
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":140,"width":180,"height":54}'
|
||||
Fill all missing values using Pandas on CSV data:
|
||||
componentRef:
|
||||
digest: a1b0c29a4615f2e3652aa5d31b9255fa15700e146627c755f8fc172f82e71af7
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/23405971f5f16a41b16c343129b893c52e4d1d48/components/pandas/Fill_all_missing_values/in_CSV_format/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: transformed_table
|
||||
taskId: Select columns using Pandas on CSV data
|
||||
type: CSV
|
||||
replacement_value: '0'
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":250,"width":180,"height":54}'
|
||||
Binarize column using Pandas on CSV data:
|
||||
componentRef:
|
||||
digest: d699afd4d7cae862708717cc160f4394ed0c04e536e9515923ef1e8865f01d44
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/1e2558325f4c708aca75827c8acc13d230ee7e9f/components/pandas/Binarize_column/in_CSV_format/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: transformed_table
|
||||
taskId: Fill all missing values using Pandas on CSV data
|
||||
type: CSV
|
||||
column_name: tips
|
||||
predicate: ' > 0'
|
||||
new_column_name: class
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":370,"width":180,"height":54}'
|
||||
Split rows into subsets:
|
||||
componentRef:
|
||||
digest: a609c3c9196484290f24a1174955f95b27f07a7b458aa5cb8cde28866cb2cb46
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/daae5a4abaa35e44501818b1534ed7827d7da073/components/dataset_manipulation/Split_rows_into_subsets/in_CSV/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: transformed_table
|
||||
taskId: Binarize column using Pandas on CSV data
|
||||
type: CSV
|
||||
fraction_1: '0.8'
|
||||
annotations:
|
||||
editor.position: '{"x":170,"y":500,"width":180,"height":40}'
|
||||
Create fully connected tensorflow network:
|
||||
componentRef:
|
||||
digest: bfcafbc5ce711b1f69cabf1338212d10d50136a73db9f9f7c984de7b80b4bfb0
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/9ca0f9eecf5f896f65b8538bbd809747052617d1/components/tensorflow/Create_fully_connected_network/component.yaml
|
||||
arguments:
|
||||
input_size: '7'
|
||||
hidden_layer_sizes: '[10]'
|
||||
activation_name: elu
|
||||
output_activation_name: sigmoid
|
||||
annotations:
|
||||
editor.position: '{"x":370,"y":500,"width":180,"height":54}'
|
||||
Train model using Keras on CSV:
|
||||
componentRef:
|
||||
digest: 42ae60c889034dbad74815653e95b4f7d576b5f47f803173e8679c7b54984609
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/c504a4010348c50eaaf6d4337586ccc008f4dcef/components/tensorflow/Train_model_using_Keras/on_CSV/component.yaml
|
||||
arguments:
|
||||
training_data:
|
||||
taskOutput:
|
||||
outputName: split_1
|
||||
taskId: Split rows into subsets
|
||||
type: CSV
|
||||
model:
|
||||
taskOutput:
|
||||
outputName: model
|
||||
taskId: Create fully connected tensorflow network
|
||||
type: TensorflowSavedModel
|
||||
label_column_name: class
|
||||
loss_function_name: binary_crossentropy
|
||||
number_of_epochs: '10'
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":620,"width":180,"height":54}'
|
||||
Upload Tensorflow model to Google Cloud Vertex AI:
|
||||
componentRef:
|
||||
digest: 2e45263ff640b1a688e359b6936e27a81b2407749a84f340af2aa5547e0cb92c
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/c6a8b67d1ada2cc17665c99ff6b410df588bee28/components/google-cloud/Vertex_AI/Models/Upload_Tensorflow_model/workaround_for_buggy_KFPv2_compiler/component.yaml
|
||||
arguments:
|
||||
model:
|
||||
taskOutput:
|
||||
outputName: trained_model
|
||||
taskId: Train model using Keras on CSV
|
||||
type: TensorflowSavedModel
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":750,"width":180,"height":54}'
|
||||
Predict with TensorFlow model on CSV data:
|
||||
componentRef:
|
||||
digest: 921bb1563e93a78233b8acceab87055b9154ccf5595d056028cf0396ca224cd4
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/59c759ce6f543184e30db6817d2a703879bc0f39/components/tensorflow/Predict/on_CSV/component.yaml
|
||||
arguments:
|
||||
dataset:
|
||||
taskOutput:
|
||||
outputName: split_2
|
||||
taskId: Split rows into subsets
|
||||
type: CSV
|
||||
model:
|
||||
taskOutput:
|
||||
outputName: trained_model
|
||||
taskId: Train model using Keras on CSV
|
||||
type: TensorflowSavedModel
|
||||
label_column_name: class
|
||||
annotations:
|
||||
editor.position: '{"x":240,"y":750,"width":180,"height":54}'
|
||||
outputValues: {}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
# python3 -m pip install "kfp<2.0.0" "google-cloud-aiplatform>=1.16.0" --upgrade --quiet
|
||||
from kfp import components
|
||||
|
||||
# %% Loading components
|
||||
download_from_gcs_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/storage/download/component.yaml")
|
||||
select_columns_using_Pandas_on_CSV_data_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/pandas/Select_columns/in_CSV_format/component.yaml")
|
||||
fill_all_missing_values_using_Pandas_on_CSV_data_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/pandas/Fill_all_missing_values/in_CSV_format/component.yaml")
|
||||
binarize_column_using_Pandas_on_CSV_data_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/pandas/Binarize_column/in_CSV_format/component.yaml")
|
||||
split_rows_into_subsets_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/dataset_manipulation/Split_rows_into_subsets/in_CSV/component.yaml")
|
||||
create_fully_connected_tensorflow_network_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/tensorflow/Create_fully_connected_network/component.yaml")
|
||||
train_model_using_Keras_on_CSV_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/tensorflow/Train_model_using_Keras/on_CSV/component.yaml")
|
||||
predict_with_TensorFlow_model_on_CSV_data_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/tensorflow/Predict/on_CSV/component.yaml")
|
||||
upload_Tensorflow_model_to_Google_Cloud_Vertex_AI_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/Vertex_AI/Models/Upload_Tensorflow_model/component.yaml")
|
||||
deploy_model_to_endpoint_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/Vertex_AI/Models/Deploy_to_endpoint/component.yaml")
|
||||
|
||||
# %% Pipeline definition
|
||||
def train_tabular_classification_model_using_TensorFlow_pipeline():
|
||||
dataset_gcs_uri = "gs://ml-pipeline-dataset/Chicago_taxi_trips/chicago_taxi_trips_2019-01-01_-_2019-02-01_limit=10000.csv"
|
||||
feature_columns = ["trip_seconds", "trip_miles", "pickup_community_area", "dropoff_community_area", "fare", "tolls", "extras"] # Excluded "trip_total"
|
||||
label_column = "tips"
|
||||
training_set_fraction = 0.8
|
||||
# Deploying the model might incur additional costs over time
|
||||
deploy_model = False
|
||||
|
||||
classification_label_column = "class"
|
||||
all_columns = [label_column] + feature_columns
|
||||
|
||||
dataset = download_from_gcs_op(
|
||||
gcs_path=dataset_gcs_uri
|
||||
).outputs["Data"]
|
||||
|
||||
dataset = select_columns_using_Pandas_on_CSV_data_op(
|
||||
table=dataset,
|
||||
column_names=all_columns,
|
||||
).outputs["transformed_table"]
|
||||
|
||||
dataset = fill_all_missing_values_using_Pandas_on_CSV_data_op(
|
||||
table=dataset,
|
||||
replacement_value="0",
|
||||
# # Optional:
|
||||
# column_names=None, # =[...]
|
||||
).outputs["transformed_table"]
|
||||
|
||||
classification_dataset = binarize_column_using_Pandas_on_CSV_data_op(
|
||||
table=dataset,
|
||||
column_name=label_column,
|
||||
predicate=" > 0",
|
||||
new_column_name=classification_label_column,
|
||||
).outputs["transformed_table"]
|
||||
|
||||
split_task = split_rows_into_subsets_op(
|
||||
table=classification_dataset,
|
||||
fraction_1=training_set_fraction,
|
||||
)
|
||||
classification_training_data = split_task.outputs["split_1"]
|
||||
classification_testing_data = split_task.outputs["split_2"]
|
||||
|
||||
network = create_fully_connected_tensorflow_network_op(
|
||||
input_size=len(feature_columns),
|
||||
# Optional:
|
||||
hidden_layer_sizes=[10],
|
||||
activation_name="elu",
|
||||
output_activation_name="sigmoid",
|
||||
# output_size=1,
|
||||
).outputs["model"]
|
||||
|
||||
model = train_model_using_Keras_on_CSV_op(
|
||||
training_data=classification_training_data,
|
||||
model=network,
|
||||
label_column_name=classification_label_column,
|
||||
# Optional:
|
||||
loss_function_name="binary_crossentropy",
|
||||
number_of_epochs=10,
|
||||
#learning_rate=0.1,
|
||||
#optimizer_name="Adadelta",
|
||||
#optimizer_parameters={},
|
||||
#batch_size=32,
|
||||
#metric_names=["mean_absolute_error"],
|
||||
#random_seed=0,
|
||||
).outputs["trained_model"]
|
||||
|
||||
predictions = predict_with_TensorFlow_model_on_CSV_data_op(
|
||||
dataset=classification_testing_data,
|
||||
model=model,
|
||||
# label_column_name needs to be set when doing prediction on a dataset that has labels
|
||||
label_column_name=classification_label_column,
|
||||
# Optional:
|
||||
# batch_size=1000,
|
||||
).outputs["predictions"]
|
||||
|
||||
vertex_model_name = upload_Tensorflow_model_to_Google_Cloud_Vertex_AI_op(
|
||||
model=model,
|
||||
).outputs["model_name"]
|
||||
|
||||
# Deploying the model might incur additional costs over time
|
||||
if deploy_model:
|
||||
vertex_endpoint_name = deploy_model_to_endpoint_op(
|
||||
model_name=vertex_model_name,
|
||||
).outputs["endpoint_name"]
|
||||
|
||||
pipeline_func = train_tabular_classification_model_using_TensorFlow_pipeline
|
||||
|
||||
# %% Pipeline submission
|
||||
if __name__ == '__main__':
|
||||
from google.cloud import aiplatform
|
||||
aiplatform.PipelineJob.from_pipeline_func(pipeline_func=pipeline_func).submit()
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
name: Train tabular classification model using XGBoost pipeline
|
||||
metadata:
|
||||
annotations:
|
||||
author: Alexey Volkov <alexey.volkov@ark-kun.com>
|
||||
canonical_location: https://raw.githubusercontent.com/Ark-kun/pipeline_components/master/samples/Google_Cloud_Vertex_AI/Train_tabular_classification_model_using_XGBoost_and_import_to_Vertex_AI/pipeline.component.yaml
|
||||
sdk: https://cloud-pipelines.net/pipeline-editor/
|
||||
implementation:
|
||||
graph:
|
||||
tasks:
|
||||
Download from GCS:
|
||||
componentRef:
|
||||
digest: 4175c9ff143cb8cc75d05451c0a0ebdf5a0d6d020816e29f5e9cefbb7d56f241
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/27a5ea25e849c9e8c0cb6ed65518bc3ece259aaf/components/google-cloud/storage/download/workaround_for_buggy_KFPv2_compiler/component.yaml
|
||||
arguments:
|
||||
GCS path: gs://ml-pipeline-dataset/Chicago_taxi_trips/chicago_taxi_trips_2019-01-01_-_2019-02-01_limit=10000.csv
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":40,"width":180,"height":40}'
|
||||
Select columns using Pandas on CSV data:
|
||||
componentRef:
|
||||
digest: 9b9500f461c1d04f1e48992de9138db14a6800f23649d73048673d5ea6dc56ad
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/8c78aae096806cff3bc331a40566f42f5c3e9d4b/components/pandas/Select_columns/in_CSV_format/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: Data
|
||||
taskId: Download from GCS
|
||||
column_names: '["tips", "trip_seconds", "trip_miles", "pickup_community_area", "dropoff_community_area", "fare", "tolls", "extras"]'
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":140,"width":180,"height":54}'
|
||||
Fill all missing values using Pandas on CSV data:
|
||||
componentRef:
|
||||
digest: a1b0c29a4615f2e3652aa5d31b9255fa15700e146627c755f8fc172f82e71af7
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/23405971f5f16a41b16c343129b893c52e4d1d48/components/pandas/Fill_all_missing_values/in_CSV_format/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: transformed_table
|
||||
taskId: Select columns using Pandas on CSV data
|
||||
type: CSV
|
||||
replacement_value: '0'
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":250,"width":180,"height":54}'
|
||||
Binarize column using Pandas on CSV data:
|
||||
componentRef:
|
||||
digest: d699afd4d7cae862708717cc160f4394ed0c04e536e9515923ef1e8865f01d44
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/1e2558325f4c708aca75827c8acc13d230ee7e9f/components/pandas/Binarize_column/in_CSV_format/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: transformed_table
|
||||
taskId: Fill all missing values using Pandas on CSV data
|
||||
type: CSV
|
||||
column_name: tips
|
||||
predicate: '> 0'
|
||||
new_column_name: class
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":380,"width":180,"height":54}'
|
||||
Split rows into subsets:
|
||||
componentRef:
|
||||
digest: a609c3c9196484290f24a1174955f95b27f07a7b458aa5cb8cde28866cb2cb46
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/daae5a4abaa35e44501818b1534ed7827d7da073/components/dataset_manipulation/Split_rows_into_subsets/in_CSV/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: transformed_table
|
||||
taskId: Binarize column using Pandas on CSV data
|
||||
type: CSV
|
||||
fraction_1: '0.8'
|
||||
annotations:
|
||||
editor.position: '{"x":170,"y":510,"width":180,"height":40}'
|
||||
Train XGBoost model on CSV:
|
||||
componentRef:
|
||||
digest: 538c5a01eb38deaf532d619f0bbeaff4efc550fe1f0f776fc06791097b68ceac
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/58d3a47f904f32a64af8403330ba7e2134cae46d/components/XGBoost/Train/component.yaml
|
||||
arguments:
|
||||
training_data:
|
||||
taskOutput:
|
||||
outputName: split_1
|
||||
taskId: Split rows into subsets
|
||||
type: CSV
|
||||
label_column_name: class
|
||||
objective: binary:logistic
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":630,"width":180,"height":40}'
|
||||
Upload XGBoost model to Google Cloud Vertex AI:
|
||||
componentRef:
|
||||
digest: 5a5a273c403670743820986c03a4175b7cb4595a556524fefcce403656286977
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/c6a8b67d1ada2cc17665c99ff6b410df588bee28/components/google-cloud/Vertex_AI/Models/Upload_XGBoost_model/workaround_for_buggy_KFPv2_compiler/component.yaml
|
||||
arguments:
|
||||
model:
|
||||
taskOutput:
|
||||
outputName: model
|
||||
taskId: Train XGBoost model on CSV
|
||||
type: XGBoostModel
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":750,"width":180,"height":54}'
|
||||
Xgboost predict on CSV:
|
||||
componentRef:
|
||||
digest: 0876233a0c7306fefec188bd70f059b46d1fb5aa57be231799570e3bbbdd0d95
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/4694ec97baccf59284c2a1db4aa2250c22291eab/components/XGBoost/Predict/component.yaml
|
||||
arguments:
|
||||
data:
|
||||
taskOutput:
|
||||
outputName: split_2
|
||||
taskId: Split rows into subsets
|
||||
type: CSV
|
||||
model:
|
||||
taskOutput:
|
||||
outputName: model
|
||||
taskId: Train XGBoost model on CSV
|
||||
type: XGBoostModel
|
||||
label_column_name: class
|
||||
annotations:
|
||||
editor.position: '{"x":240,"y":750,"width":180,"height":40}'
|
||||
outputValues: {}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
# python3 -m pip install "kfp<2.0.0" "google-cloud-aiplatform>=1.16.0" --upgrade --quiet
|
||||
from kfp import components
|
||||
|
||||
# %% Loading components
|
||||
download_from_gcs_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/storage/download/component.yaml")
|
||||
select_columns_using_Pandas_on_CSV_data_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/pandas/Select_columns/in_CSV_format/component.yaml")
|
||||
fill_all_missing_values_using_Pandas_on_CSV_data_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/pandas/Fill_all_missing_values/in_CSV_format/component.yaml")
|
||||
binarize_column_using_Pandas_on_CSV_data_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/pandas/Binarize_column/in_CSV_format/component.yaml")
|
||||
split_rows_into_subsets_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/dataset_manipulation/Split_rows_into_subsets/in_CSV/component.yaml")
|
||||
train_XGBoost_model_on_CSV_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/XGBoost/Train/component.yaml")
|
||||
xgboost_predict_on_CSV_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/XGBoost/Predict/component.yaml")
|
||||
upload_XGBoost_model_to_Google_Cloud_Vertex_AI_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/Vertex_AI/Models/Upload_XGBoost_model/component.yaml")
|
||||
deploy_model_to_endpoint_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/Vertex_AI/Models/Deploy_to_endpoint/component.yaml")
|
||||
|
||||
# %% Pipeline definition
|
||||
def train_tabular_classification_model_using_XGBoost_pipeline():
|
||||
dataset_gcs_uri = "gs://ml-pipeline-dataset/Chicago_taxi_trips/chicago_taxi_trips_2019-01-01_-_2019-02-01_limit=10000.csv"
|
||||
feature_columns = ["trip_seconds", "trip_miles", "pickup_community_area", "dropoff_community_area", "fare", "tolls", "extras"] # Excluded "trip_total"
|
||||
label_column = "tips"
|
||||
training_set_fraction = 0.8
|
||||
# Deploying the model might incur additional costs over time
|
||||
deploy_model = False
|
||||
|
||||
classification_label_column = "class"
|
||||
all_columns = [label_column] + feature_columns
|
||||
|
||||
dataset = download_from_gcs_op(
|
||||
gcs_path=dataset_gcs_uri
|
||||
).outputs["Data"]
|
||||
|
||||
dataset = select_columns_using_Pandas_on_CSV_data_op(
|
||||
table=dataset,
|
||||
column_names=all_columns,
|
||||
).outputs["transformed_table"]
|
||||
|
||||
dataset = fill_all_missing_values_using_Pandas_on_CSV_data_op(
|
||||
table=dataset,
|
||||
replacement_value="0",
|
||||
# # Optional:
|
||||
# column_names=None, # =[...]
|
||||
).outputs["transformed_table"]
|
||||
|
||||
classification_dataset = binarize_column_using_Pandas_on_CSV_data_op(
|
||||
table=dataset,
|
||||
column_name=label_column,
|
||||
predicate="> 0",
|
||||
new_column_name=classification_label_column,
|
||||
).outputs["transformed_table"]
|
||||
|
||||
split_task = split_rows_into_subsets_op(
|
||||
table=classification_dataset,
|
||||
fraction_1=training_set_fraction,
|
||||
)
|
||||
classification_training_data = split_task.outputs["split_1"]
|
||||
classification_testing_data = split_task.outputs["split_2"]
|
||||
|
||||
model = train_XGBoost_model_on_CSV_op(
|
||||
training_data=classification_training_data,
|
||||
label_column_name=classification_label_column,
|
||||
objective="binary:logistic",
|
||||
# Optional:
|
||||
#starting_model=None,
|
||||
#num_iterations=10,
|
||||
#booster_params={},
|
||||
#booster="gbtree",
|
||||
#learning_rate=0.3,
|
||||
#min_split_loss=0,
|
||||
#max_depth=6,
|
||||
).outputs["model"]
|
||||
|
||||
# Predicting on the testing data
|
||||
predictions = xgboost_predict_on_CSV_op(
|
||||
data=classification_testing_data,
|
||||
model=model,
|
||||
# label_column needs to be set when doing prediction on a dataset that has labels
|
||||
label_column_name=classification_label_column,
|
||||
).outputs["predictions"]
|
||||
|
||||
vertex_model_name = upload_XGBoost_model_to_Google_Cloud_Vertex_AI_op(
|
||||
model=model,
|
||||
).outputs["model_name"]
|
||||
|
||||
# Deploying the model might incur additional costs over time
|
||||
if deploy_model:
|
||||
vertex_endpoint_name = deploy_model_to_endpoint_op(
|
||||
model_name=vertex_model_name,
|
||||
).outputs["endpoint_name"]
|
||||
|
||||
pipeline_func = train_tabular_classification_model_using_XGBoost_pipeline
|
||||
|
||||
# %% Pipeline submission
|
||||
if __name__ == '__main__':
|
||||
from google.cloud import aiplatform
|
||||
aiplatform.PipelineJob.from_pipeline_func(pipeline_func=pipeline_func).submit()
|
||||
+257
@@ -0,0 +1,257 @@
|
||||
name: Train tabular classification model using all frameworks pipeline
|
||||
metadata:
|
||||
annotations:
|
||||
author: Alexey Volkov <alexey.volkov@ark-kun.com>
|
||||
canonical_location: https://raw.githubusercontent.com/Ark-kun/pipeline_components/master/samples/Google_Cloud_Vertex_AI/Train_tabular_classification_model_using_all_frameworks_and_import_to_Vertex_AI/pipeline.component.yaml
|
||||
sdk: https://cloud-pipelines.net/pipeline-editor/
|
||||
implementation:
|
||||
graph:
|
||||
tasks:
|
||||
Download from GCS:
|
||||
componentRef:
|
||||
digest: 4175c9ff143cb8cc75d05451c0a0ebdf5a0d6d020816e29f5e9cefbb7d56f241
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/27a5ea25e849c9e8c0cb6ed65518bc3ece259aaf/components/google-cloud/storage/download/workaround_for_buggy_KFPv2_compiler/component.yaml
|
||||
arguments:
|
||||
GCS path: gs://ml-pipeline-dataset/Chicago_taxi_trips/chicago_taxi_trips_2019-01-01_-_2019-02-01_limit=10000.csv
|
||||
annotations:
|
||||
editor.position: '{"x":550,"y":40,"width":180,"height":40}'
|
||||
Select columns using Pandas on CSV data:
|
||||
componentRef:
|
||||
digest: 9b9500f461c1d04f1e48992de9138db14a6800f23649d73048673d5ea6dc56ad
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/8c78aae096806cff3bc331a40566f42f5c3e9d4b/components/pandas/Select_columns/in_CSV_format/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: Data
|
||||
taskId: Download from GCS
|
||||
column_names: '["tips", "trip_seconds", "trip_miles", "pickup_community_area", "dropoff_community_area", "fare", "tolls", "extras"]'
|
||||
annotations:
|
||||
editor.position: '{"x":550,"y":140,"width":180,"height":54}'
|
||||
Fill all missing values using Pandas on CSV data:
|
||||
componentRef:
|
||||
digest: a1b0c29a4615f2e3652aa5d31b9255fa15700e146627c755f8fc172f82e71af7
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/23405971f5f16a41b16c343129b893c52e4d1d48/components/pandas/Fill_all_missing_values/in_CSV_format/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: transformed_table
|
||||
taskId: Select columns using Pandas on CSV data
|
||||
type: CSV
|
||||
replacement_value: '0'
|
||||
annotations:
|
||||
editor.position: '{"x":550,"y":250,"width":180,"height":54}'
|
||||
Binarize column using Pandas on CSV data:
|
||||
componentRef:
|
||||
digest: d699afd4d7cae862708717cc160f4394ed0c04e536e9515923ef1e8865f01d44
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/1e2558325f4c708aca75827c8acc13d230ee7e9f/components/pandas/Binarize_column/in_CSV_format/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: transformed_table
|
||||
taskId: Fill all missing values using Pandas on CSV data
|
||||
type: CSV
|
||||
column_name: tips
|
||||
predicate: ' > 0'
|
||||
new_column_name: class
|
||||
annotations:
|
||||
editor.position: '{"x":550,"y":380,"width":180,"height":54}'
|
||||
Split rows into subsets:
|
||||
componentRef:
|
||||
digest: a609c3c9196484290f24a1174955f95b27f07a7b458aa5cb8cde28866cb2cb46
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/daae5a4abaa35e44501818b1534ed7827d7da073/components/dataset_manipulation/Split_rows_into_subsets/in_CSV/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: transformed_table
|
||||
taskId: Binarize column using Pandas on CSV data
|
||||
type: CSV
|
||||
fraction_1: '0.8'
|
||||
annotations:
|
||||
editor.position: '{"x":550,"y":490,"width":180,"height":40}'
|
||||
Create fully connected pytorch network:
|
||||
componentRef:
|
||||
digest: d03d8248fd358a0275ec33568ee7dd7dce576cc112b09dfafe2651e4d97e04a9
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/1a2ef3eeb77bc278f33cad0dd29008ea2431e191/components/PyTorch/Create_fully_connected_network/component.yaml
|
||||
arguments:
|
||||
input_size: '7'
|
||||
hidden_layer_sizes: '[10]'
|
||||
activation_name: elu
|
||||
output_activation_name: sigmoid
|
||||
annotations:
|
||||
editor.position: '{"x":380,"y":620,"width":180,"height":54}'
|
||||
Create fully connected tensorflow network:
|
||||
componentRef:
|
||||
digest: bfcafbc5ce711b1f69cabf1338212d10d50136a73db9f9f7c984de7b80b4bfb0
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/9ca0f9eecf5f896f65b8538bbd809747052617d1/components/tensorflow/Create_fully_connected_network/component.yaml
|
||||
arguments:
|
||||
input_size: '7'
|
||||
hidden_layer_sizes: '[10]'
|
||||
activation_name: elu
|
||||
output_activation_name: sigmoid
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":630,"width":180,"height":54}'
|
||||
Train model using Keras on CSV:
|
||||
componentRef:
|
||||
digest: 42ae60c889034dbad74815653e95b4f7d576b5f47f803173e8679c7b54984609
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/c504a4010348c50eaaf6d4337586ccc008f4dcef/components/tensorflow/Train_model_using_Keras/on_CSV/component.yaml
|
||||
arguments:
|
||||
training_data:
|
||||
taskOutput:
|
||||
outputName: split_1
|
||||
taskId: Split rows into subsets
|
||||
type: CSV
|
||||
model:
|
||||
taskOutput:
|
||||
outputName: model
|
||||
taskId: Create fully connected tensorflow network
|
||||
type: TensorflowSavedModel
|
||||
label_column_name: class
|
||||
loss_function_name: binary_crossentropy
|
||||
number_of_epochs: '10'
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":750,"width":180,"height":54}'
|
||||
Train pytorch model from csv:
|
||||
componentRef:
|
||||
digest: 40f3185eb61e9727f41a4e0c05dd3d3b44bd802aa0f378cfc31756560033949a
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/d8c4cf5e6403bc65bcf8d606e6baf87e2528a3dc/components/PyTorch/Train_PyTorch_model/from_CSV/component.yaml
|
||||
arguments:
|
||||
model:
|
||||
taskOutput:
|
||||
outputName: model
|
||||
taskId: Create fully connected pytorch network
|
||||
type: PyTorchScriptModule
|
||||
training_data:
|
||||
taskOutput:
|
||||
outputName: split_1
|
||||
taskId: Split rows into subsets
|
||||
type: CSV
|
||||
label_column_name: class
|
||||
loss_function_name: binary_cross_entropy
|
||||
annotations:
|
||||
editor.position: '{"x":380,"y":750,"width":180,"height":40}'
|
||||
Train XGBoost model on CSV:
|
||||
componentRef:
|
||||
digest: 538c5a01eb38deaf532d619f0bbeaff4efc550fe1f0f776fc06791097b68ceac
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/58d3a47f904f32a64af8403330ba7e2134cae46d/components/XGBoost/Train/component.yaml
|
||||
arguments:
|
||||
training_data:
|
||||
taskOutput:
|
||||
outputName: split_1
|
||||
taskId: Split rows into subsets
|
||||
type: CSV
|
||||
label_column_name: class
|
||||
objective: binary:logistic
|
||||
annotations:
|
||||
editor.position: '{"x":720,"y":750,"width":180,"height":40}'
|
||||
Train logistic regression model using scikit learn from CSV:
|
||||
componentRef:
|
||||
digest: a864625a822e4b1c8ef6fe4ae1454fd90f15438f70a6712bb4c30e0dda4d35b7
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/cb44b75c9c062fcc40c2b905b2024b4493dbc62b/components/ML_frameworks/Scikit_learn/Train_logistic_regression_model/from_CSV/component.yaml
|
||||
arguments:
|
||||
dataset:
|
||||
taskOutput:
|
||||
outputName: split_1
|
||||
taskId: Split rows into subsets
|
||||
type: CSV
|
||||
label_column_name: class
|
||||
annotations:
|
||||
editor.position: '{"x":1030,"y":750,"width":180,"height":70}'
|
||||
Predict with TensorFlow model on CSV data:
|
||||
componentRef:
|
||||
digest: 921bb1563e93a78233b8acceab87055b9154ccf5595d056028cf0396ca224cd4
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/59c759ce6f543184e30db6817d2a703879bc0f39/components/tensorflow/Predict/on_CSV/component.yaml
|
||||
arguments:
|
||||
dataset:
|
||||
taskOutput:
|
||||
outputName: split_2
|
||||
taskId: Split rows into subsets
|
||||
type: CSV
|
||||
model:
|
||||
taskOutput:
|
||||
outputName: trained_model
|
||||
taskId: Train model using Keras on CSV
|
||||
type: TensorflowSavedModel
|
||||
label_column_name: class
|
||||
annotations:
|
||||
editor.position: '{"x":160,"y":880,"width":180,"height":54}'
|
||||
Create PyTorch Model Archive with base handler:
|
||||
componentRef:
|
||||
digest: 8298b5ee1b0f0879f893add4cf352c8dec7cf9e21bb9db134c91a2d046cdb0ec
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/46d51383e6554b7f3ab4fd8cf614d8c2b422fb22/components/PyTorch/Create_PyTorch_Model_Archive/with_base_handler/component.yaml
|
||||
arguments:
|
||||
Model:
|
||||
taskOutput:
|
||||
outputName: trained_model
|
||||
taskId: Train pytorch model from csv
|
||||
type: PyTorchScriptModule
|
||||
Model name: model
|
||||
Model version: '1.0'
|
||||
annotations:
|
||||
editor.position: '{"x":380,"y":880,"width":180,"height":54}'
|
||||
Xgboost predict on CSV:
|
||||
componentRef:
|
||||
digest: 0876233a0c7306fefec188bd70f059b46d1fb5aa57be231799570e3bbbdd0d95
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/4694ec97baccf59284c2a1db4aa2250c22291eab/components/XGBoost/Predict/component.yaml
|
||||
arguments:
|
||||
data:
|
||||
taskOutput:
|
||||
outputName: split_2
|
||||
taskId: Split rows into subsets
|
||||
type: CSV
|
||||
model:
|
||||
taskOutput:
|
||||
outputName: model
|
||||
taskId: Train XGBoost model on CSV
|
||||
type: XGBoostModel
|
||||
label_column_name: class
|
||||
annotations:
|
||||
editor.position: '{"x":810,"y":880,"width":180,"height":40}'
|
||||
Upload Scikit learn pickle model to Google Cloud Vertex AI:
|
||||
componentRef:
|
||||
digest: 81c91c8d7d21ec97e0872f669d68bd89edea87279d703685db54aa94743bebcd
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/c6a8b67d1ada2cc17665c99ff6b410df588bee28/components/google-cloud/Vertex_AI/Models/Upload_Scikit-learn_pickle_model/workaround_for_buggy_KFPv2_compiler/component.yaml
|
||||
arguments:
|
||||
model:
|
||||
taskOutput:
|
||||
outputName: model
|
||||
taskId: Train logistic regression model using scikit learn from CSV
|
||||
type: ScikitLearnPickleModel
|
||||
annotations:
|
||||
editor.position: '{"x":1030,"y":880,"width":180,"height":70}'
|
||||
Upload Tensorflow model to Google Cloud Vertex AI:
|
||||
componentRef:
|
||||
digest: 2e45263ff640b1a688e359b6936e27a81b2407749a84f340af2aa5547e0cb92c
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/c6a8b67d1ada2cc17665c99ff6b410df588bee28/components/google-cloud/Vertex_AI/Models/Upload_Tensorflow_model/workaround_for_buggy_KFPv2_compiler/component.yaml
|
||||
arguments:
|
||||
model:
|
||||
taskOutput:
|
||||
outputName: trained_model
|
||||
taskId: Train model using Keras on CSV
|
||||
type: TensorflowSavedModel
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":1010,"width":180,"height":54}'
|
||||
Upload PyTorch model archive to Google Cloud Vertex AI:
|
||||
componentRef:
|
||||
digest: 4450212fae7b9001482aca7eb78b28413c205506eccf08a04e7754a8dfa99004
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/c6a8b67d1ada2cc17665c99ff6b410df588bee28/components/google-cloud/Vertex_AI/Models/Upload_PyTorch_model_archive/workaround_for_buggy_KFPv2_compiler/component.yaml
|
||||
arguments:
|
||||
model_archive:
|
||||
taskOutput:
|
||||
outputName: Model archive
|
||||
taskId: Create PyTorch Model Archive with base handler
|
||||
type: PyTorchModelArchive
|
||||
annotations:
|
||||
editor.position: '{"x":380,"y":1010,"width":180,"height":70}'
|
||||
Upload XGBoost model to Google Cloud Vertex AI:
|
||||
componentRef:
|
||||
digest: 5a5a273c403670743820986c03a4175b7cb4595a556524fefcce403656286977
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/c6a8b67d1ada2cc17665c99ff6b410df588bee28/components/google-cloud/Vertex_AI/Models/Upload_XGBoost_model/workaround_for_buggy_KFPv2_compiler/component.yaml
|
||||
arguments:
|
||||
model:
|
||||
taskOutput:
|
||||
outputName: model
|
||||
taskId: Train XGBoost model on CSV
|
||||
type: XGBoostModel
|
||||
annotations:
|
||||
editor.position: '{"x":720,"y":1010,"width":180,"height":54}'
|
||||
outputValues: {}
|
||||
+224
@@ -0,0 +1,224 @@
|
||||
# python3 -m pip install "kfp<2.0.0" "google-cloud-aiplatform>=1.16.0" --upgrade --quiet
|
||||
from kfp import components
|
||||
|
||||
# %% Loading components
|
||||
download_from_gcs_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/storage/download/component.yaml")
|
||||
select_columns_using_Pandas_on_CSV_data_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/pandas/Select_columns/in_CSV_format/component.yaml")
|
||||
fill_all_missing_values_using_Pandas_on_CSV_data_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/pandas/Fill_all_missing_values/in_CSV_format/component.yaml")
|
||||
binarize_column_using_Pandas_on_CSV_data_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/pandas/Binarize_column/in_CSV_format/component.yaml")
|
||||
split_rows_into_subsets_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/dataset_manipulation/Split_rows_into_subsets/in_CSV/component.yaml")
|
||||
|
||||
# TensorFlow
|
||||
create_fully_connected_tensorflow_network_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/tensorflow/Create_fully_connected_network/component.yaml")
|
||||
train_model_using_Keras_on_CSV_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/tensorflow/Train_model_using_Keras/on_CSV/component.yaml")
|
||||
predict_with_TensorFlow_model_on_CSV_data_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/tensorflow/Predict/on_CSV/component.yaml")
|
||||
upload_Tensorflow_model_to_Google_Cloud_Vertex_AI_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/Vertex_AI/Models/Upload_Tensorflow_model/component.yaml")
|
||||
|
||||
# PyTorch
|
||||
create_fully_connected_pytorch_network_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/PyTorch/Create_fully_connected_network/component.yaml")
|
||||
train_pytorch_model_from_csv_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/PyTorch/Train_PyTorch_model/from_CSV/component.yaml")
|
||||
create_pytorch_model_archive_with_base_handler_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/PyTorch/Create_PyTorch_Model_Archive/with_base_handler/component.yaml")
|
||||
upload_PyTorch_model_archive_to_Google_Cloud_Vertex_AI_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/Vertex_AI/Models/Upload_PyTorch_model_archive/component.yaml")
|
||||
|
||||
# XGBoost
|
||||
train_XGBoost_model_on_CSV_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/XGBoost/Train/component.yaml")
|
||||
xgboost_predict_on_CSV_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/XGBoost/Predict/component.yaml")
|
||||
upload_XGBoost_model_to_Google_Cloud_Vertex_AI_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/Vertex_AI/Models/Upload_XGBoost_model/component.yaml")
|
||||
|
||||
# Scikit-learn
|
||||
#train_linear_regression_model_using_scikit_learn_from_CSV_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/1f5cf6e06409b704064b2086c0a705e4e6b4fcde/community-content/pipeline_components/ML_frameworks/Scikit_learn/Train_linear_regression_model/from_CSV/component.yaml")
|
||||
train_logistic_regression_model_using_scikit_learn_from_CSV_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/1f5cf6e06409b704064b2086c0a705e4e6b4fcde/community-content/pipeline_components/ML_frameworks/Scikit_learn/Train_logistic_regression_model/from_CSV/component.yaml")
|
||||
upload_Scikit_learn_pickle_model_to_Google_Cloud_Vertex_AI_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/Vertex_AI/Models/Upload_Scikit-learn_pickle_model/component.yaml")
|
||||
|
||||
# Vertex AI
|
||||
deploy_model_to_endpoint_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/Vertex_AI/Models/Deploy_to_endpoint/component.yaml")
|
||||
|
||||
# %% Pipeline definition
|
||||
def train_tabular_classification_model_using_all_frameworks_pipeline():
|
||||
dataset_gcs_uri = "gs://ml-pipeline-dataset/Chicago_taxi_trips/chicago_taxi_trips_2019-01-01_-_2019-02-01_limit=10000.csv"
|
||||
feature_columns = ["trip_seconds", "trip_miles", "pickup_community_area", "dropoff_community_area", "fare", "tolls", "extras"] # Excluded "trip_total"
|
||||
label_column = "tips"
|
||||
training_set_fraction = 0.8
|
||||
# Deploying the model might incur additional costs over time
|
||||
deploy_model = False
|
||||
|
||||
classification_label_column = "class"
|
||||
all_columns = [label_column] + feature_columns
|
||||
|
||||
dataset = download_from_gcs_op(
|
||||
gcs_path=dataset_gcs_uri
|
||||
).outputs["Data"]
|
||||
|
||||
dataset = select_columns_using_Pandas_on_CSV_data_op(
|
||||
table=dataset,
|
||||
column_names=all_columns,
|
||||
).outputs["transformed_table"]
|
||||
|
||||
dataset = fill_all_missing_values_using_Pandas_on_CSV_data_op(
|
||||
table=dataset,
|
||||
replacement_value="0",
|
||||
# # Optional:
|
||||
# column_names=None, # =[...]
|
||||
).outputs["transformed_table"]
|
||||
|
||||
classification_dataset = binarize_column_using_Pandas_on_CSV_data_op(
|
||||
table=dataset,
|
||||
column_name=label_column,
|
||||
predicate=" > 0",
|
||||
new_column_name=classification_label_column,
|
||||
).outputs["transformed_table"]
|
||||
|
||||
split_task = split_rows_into_subsets_op(
|
||||
table=classification_dataset,
|
||||
fraction_1=training_set_fraction,
|
||||
)
|
||||
classification_training_data = split_task.outputs["split_1"]
|
||||
classification_testing_data = split_task.outputs["split_2"]
|
||||
|
||||
# TensorFlow
|
||||
tensorflow_network = create_fully_connected_tensorflow_network_op(
|
||||
input_size=len(feature_columns),
|
||||
# Optional:
|
||||
hidden_layer_sizes=[10],
|
||||
activation_name="elu",
|
||||
output_activation_name="sigmoid",
|
||||
# output_size=1,
|
||||
).outputs["model"]
|
||||
|
||||
tensorflow_model = train_model_using_Keras_on_CSV_op(
|
||||
training_data=classification_training_data,
|
||||
model=tensorflow_network,
|
||||
label_column_name=classification_label_column,
|
||||
# Optional:
|
||||
loss_function_name="binary_crossentropy",
|
||||
number_of_epochs=10,
|
||||
#learning_rate=0.1,
|
||||
#optimizer_name="Adadelta",
|
||||
#optimizer_parameters={},
|
||||
#batch_size=32,
|
||||
#metric_names=["mean_absolute_error"],
|
||||
#random_seed=0,
|
||||
).outputs["trained_model"]
|
||||
|
||||
tensorflow_predictions = predict_with_TensorFlow_model_on_CSV_data_op(
|
||||
dataset=classification_testing_data,
|
||||
model=tensorflow_model,
|
||||
# label_column_name needs to be set when doing prediction on a dataset that has labels
|
||||
label_column_name=classification_label_column,
|
||||
# Optional:
|
||||
# batch_size=1000,
|
||||
).outputs["predictions"]
|
||||
|
||||
tensorflow_vertex_model_name = upload_Tensorflow_model_to_Google_Cloud_Vertex_AI_op(
|
||||
model=tensorflow_model,
|
||||
).outputs["model_name"]
|
||||
|
||||
# Deploying the model might incur additional costs over time
|
||||
if deploy_model:
|
||||
tensorflow_vertex_endpoint_name = deploy_model_to_endpoint_op(
|
||||
model_name=tensorflow_vertex_model_name,
|
||||
).outputs["endpoint_name"]
|
||||
|
||||
# PyTorch
|
||||
pytorch_network = create_fully_connected_pytorch_network_op(
|
||||
input_size=len(feature_columns),
|
||||
# Optional:
|
||||
hidden_layer_sizes=[10],
|
||||
activation_name="elu",
|
||||
output_activation_name="sigmoid",
|
||||
# output_size=1,
|
||||
).outputs["model"]
|
||||
|
||||
pytorch_model = train_pytorch_model_from_csv_op(
|
||||
model=pytorch_network,
|
||||
training_data=classification_training_data,
|
||||
label_column_name=classification_label_column,
|
||||
loss_function_name="binary_cross_entropy",
|
||||
# Optional:
|
||||
#number_of_epochs=1,
|
||||
#learning_rate=0.1,
|
||||
#optimizer_name="Adadelta",
|
||||
#optimizer_parameters={},
|
||||
#batch_size=32,
|
||||
#batch_log_interval=100,
|
||||
#random_seed=0,
|
||||
).outputs["trained_model"]
|
||||
|
||||
pytorch_model_archive = create_pytorch_model_archive_with_base_handler_op(
|
||||
model=pytorch_model,
|
||||
# Optional:
|
||||
# model_name="model",
|
||||
# model_version="1.0",
|
||||
).outputs["Model archive"]
|
||||
|
||||
pytorch_vertex_model_name = upload_PyTorch_model_archive_to_Google_Cloud_Vertex_AI_op(
|
||||
model_archive=pytorch_model_archive,
|
||||
).outputs["model_name"]
|
||||
|
||||
# Deploying the model might incur additional costs over time
|
||||
if deploy_model:
|
||||
pytorch_vertex_endpoint_name = deploy_model_to_endpoint_op(
|
||||
model_name=pytorch_vertex_model_name,
|
||||
).outputs["endpoint_name"]
|
||||
|
||||
# XGBoost
|
||||
xgboost_model = train_XGBoost_model_on_CSV_op(
|
||||
training_data=classification_training_data,
|
||||
label_column_name=classification_label_column,
|
||||
objective="binary:logistic",
|
||||
# Optional:
|
||||
#starting_model=None,
|
||||
#num_iterations=10,
|
||||
#booster_params={},
|
||||
#booster="gbtree",
|
||||
#learning_rate=0.3,
|
||||
#min_split_loss=0,
|
||||
#max_depth=6,
|
||||
).outputs["model"]
|
||||
|
||||
# Predicting on the testing data
|
||||
xgboost_predictions = xgboost_predict_on_CSV_op(
|
||||
data=classification_testing_data,
|
||||
model=xgboost_model,
|
||||
# label_column needs to be set when doing prediction on a dataset that has labels
|
||||
label_column_name=classification_label_column,
|
||||
).outputs["predictions"]
|
||||
|
||||
xgboost_vertex_model_name = upload_XGBoost_model_to_Google_Cloud_Vertex_AI_op(
|
||||
model=xgboost_model,
|
||||
).outputs["model_name"]
|
||||
|
||||
# Deploying the model might incur additional costs over time
|
||||
if deploy_model:
|
||||
xgboost_vertex_endpoint_name = deploy_model_to_endpoint_op(
|
||||
model_name=xgboost_vertex_model_name,
|
||||
).outputs["endpoint_name"]
|
||||
|
||||
# Scikit-learn
|
||||
sklearn_model = train_logistic_regression_model_using_scikit_learn_from_CSV_op(
|
||||
dataset=classification_training_data,
|
||||
label_column_name=classification_label_column,
|
||||
# Optional:
|
||||
#penalty="l2",
|
||||
#solver="lbfgs",
|
||||
#max_iterations=100,
|
||||
#multi_class_mode="auto",
|
||||
#random_seed=0,
|
||||
).outputs["model"]
|
||||
|
||||
sklearn_vertex_model_name = upload_Scikit_learn_pickle_model_to_Google_Cloud_Vertex_AI_op(
|
||||
model=sklearn_model,
|
||||
).outputs["model_name"]
|
||||
|
||||
# Deploying the model might incur additional costs over time
|
||||
if deploy_model:
|
||||
sklearn_vertex_endpoint_name = deploy_model_to_endpoint_op(
|
||||
model_name=sklearn_vertex_model_name,
|
||||
).outputs["endpoint_name"]
|
||||
|
||||
pipeline_func=train_tabular_classification_model_using_all_frameworks_pipeline
|
||||
|
||||
# %% Pipeline submission
|
||||
if __name__ == '__main__':
|
||||
from google.cloud import aiplatform
|
||||
aiplatform.PipelineJob.from_pipeline_func(pipeline_func=pipeline_func).submit()
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
name: Train tabular regression linear model using Scikit learn pipeline
|
||||
metadata:
|
||||
annotations:
|
||||
author: Alexey Volkov <alexey.volkov@ark-kun.com>
|
||||
canonical_location: https://raw.githubusercontent.com/Ark-kun/pipeline_components/master/samples/Google_Cloud_Vertex_AI/Train_tabular_regression_linear_model_using_Scikit_learn_and_import_to_Vertex_AI/pipeline.component.yaml
|
||||
sdk: https://cloud-pipelines.net/pipeline-editor/
|
||||
implementation:
|
||||
graph:
|
||||
tasks:
|
||||
Download from GCS:
|
||||
componentRef:
|
||||
digest: 4175c9ff143cb8cc75d05451c0a0ebdf5a0d6d020816e29f5e9cefbb7d56f241
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/27a5ea25e849c9e8c0cb6ed65518bc3ece259aaf/components/google-cloud/storage/download/workaround_for_buggy_KFPv2_compiler/component.yaml
|
||||
arguments:
|
||||
GCS path: gs://ml-pipeline-dataset/Chicago_taxi_trips/chicago_taxi_trips_2019-01-01_-_2019-02-01_limit=10000.csv
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":40,"width":180,"height":40}'
|
||||
Select columns using Pandas on CSV data:
|
||||
componentRef:
|
||||
digest: 9b9500f461c1d04f1e48992de9138db14a6800f23649d73048673d5ea6dc56ad
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/8c78aae096806cff3bc331a40566f42f5c3e9d4b/components/pandas/Select_columns/in_CSV_format/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: Data
|
||||
taskId: Download from GCS
|
||||
column_names: '["tips", "trip_seconds", "trip_miles", "pickup_community_area", "dropoff_community_area", "fare", "tolls", "extras"]'
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":140,"width":180,"height":54}'
|
||||
Fill all missing values using Pandas on CSV data:
|
||||
componentRef:
|
||||
digest: a1b0c29a4615f2e3652aa5d31b9255fa15700e146627c755f8fc172f82e71af7
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/23405971f5f16a41b16c343129b893c52e4d1d48/components/pandas/Fill_all_missing_values/in_CSV_format/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: transformed_table
|
||||
taskId: Select columns using Pandas on CSV data
|
||||
type: CSV
|
||||
replacement_value: '0'
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":250,"width":180,"height":54}'
|
||||
Train linear regression model using scikit learn from CSV:
|
||||
componentRef:
|
||||
digest: c7fe7912ab0d1fb45d201d452e9ce6be5544e7d8c6d229db7a4b931ff58560f3
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/f807e02b54d4886c65a05f40848fd51c72407f40/components/ML_frameworks/Scikit_learn/Train_linear_regression_model/from_CSV/component.yaml
|
||||
arguments:
|
||||
dataset:
|
||||
taskOutput:
|
||||
outputName: transformed_table
|
||||
taskId: Fill all missing values using Pandas on CSV data
|
||||
type: CSV
|
||||
label_column_name: tips
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":360,"width":180,"height":54}'
|
||||
Upload Scikit learn pickle model to Google Cloud Vertex AI:
|
||||
componentRef:
|
||||
digest: 81c91c8d7d21ec97e0872f669d68bd89edea87279d703685db54aa94743bebcd
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/c6a8b67d1ada2cc17665c99ff6b410df588bee28/components/google-cloud/Vertex_AI/Models/Upload_Scikit-learn_pickle_model/workaround_for_buggy_KFPv2_compiler/component.yaml
|
||||
arguments:
|
||||
model:
|
||||
taskOutput:
|
||||
outputName: model
|
||||
taskId: Train linear regression model using scikit learn from CSV
|
||||
type: ScikitLearnPickleModel
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":490,"width":180,"height":70}'
|
||||
outputValues: {}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
# python3 -m pip install "kfp<2.0.0" "google-cloud-aiplatform>=1.16.0" --upgrade --quiet
|
||||
from kfp import components
|
||||
|
||||
# %% Loading components
|
||||
download_from_gcs_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/storage/download/component.yaml")
|
||||
select_columns_using_Pandas_on_CSV_data_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/pandas/Select_columns/in_CSV_format/component.yaml")
|
||||
fill_all_missing_values_using_Pandas_on_CSV_data_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/pandas/Fill_all_missing_values/in_CSV_format/component.yaml")
|
||||
train_linear_regression_model_using_scikit_learn_from_CSV_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/1f5cf6e06409b704064b2086c0a705e4e6b4fcde/community-content/pipeline_components/ML_frameworks/Scikit_learn/Train_linear_regression_model/from_CSV/component.yaml")
|
||||
upload_Scikit_learn_pickle_model_to_Google_Cloud_Vertex_AI_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/Vertex_AI/Models/Upload_Scikit-learn_pickle_model/component.yaml")
|
||||
deploy_model_to_endpoint_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/Vertex_AI/Models/Deploy_to_endpoint/component.yaml")
|
||||
|
||||
# %% Pipeline definition
|
||||
def train_tabular_regression_linear_model_using_Scikit_learn_pipeline():
|
||||
dataset_gcs_uri = "gs://ml-pipeline-dataset/Chicago_taxi_trips/chicago_taxi_trips_2019-01-01_-_2019-02-01_limit=10000.csv"
|
||||
feature_columns = ["trip_seconds", "trip_miles", "pickup_community_area", "dropoff_community_area", "fare", "tolls", "extras"] # Excluded "trip_total"
|
||||
label_column = "tips"
|
||||
all_columns = [label_column] + feature_columns
|
||||
# Deploying the model might incur additional costs over time
|
||||
deploy_model = False
|
||||
|
||||
training_data = download_from_gcs_op(
|
||||
gcs_path=dataset_gcs_uri
|
||||
).outputs["Data"]
|
||||
|
||||
training_data = select_columns_using_Pandas_on_CSV_data_op(
|
||||
table=training_data,
|
||||
column_names=all_columns,
|
||||
).outputs["transformed_table"]
|
||||
|
||||
# Cleaning the NaN values.
|
||||
training_data = fill_all_missing_values_using_Pandas_on_CSV_data_op(
|
||||
table=training_data,
|
||||
replacement_value="0",
|
||||
#replacement_type_name="float",
|
||||
).outputs["transformed_table"]
|
||||
|
||||
model = train_linear_regression_model_using_scikit_learn_from_CSV_op(
|
||||
dataset=training_data,
|
||||
label_column_name=label_column,
|
||||
).outputs["model"]
|
||||
|
||||
vertex_model_name = upload_Scikit_learn_pickle_model_to_Google_Cloud_Vertex_AI_op(
|
||||
model=model,
|
||||
).outputs["model_name"]
|
||||
|
||||
# Deploying the model might incur additional costs over time
|
||||
if deploy_model:
|
||||
sklearn_vertex_endpoint_name = deploy_model_to_endpoint_op(
|
||||
model_name=vertex_model_name,
|
||||
).outputs["endpoint_name"]
|
||||
|
||||
pipeline_func = train_tabular_regression_linear_model_using_Scikit_learn_pipeline
|
||||
|
||||
# %% Pipeline submission
|
||||
if __name__ == '__main__':
|
||||
from google.cloud import aiplatform
|
||||
aiplatform.PipelineJob.from_pipeline_func(pipeline_func=pipeline_func).submit()
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
name: Train tabular regression model using PyTorch pipeline
|
||||
metadata:
|
||||
annotations:
|
||||
author: Alexey Volkov <alexey.volkov@ark-kun.com>
|
||||
canonical_location: https://raw.githubusercontent.com/Ark-kun/pipeline_components/master/samples/Google_Cloud_Vertex_AI/Train_tabular_regression_model_using_PyTorch_and_import_to_Vertex_AI/pipeline.component.yaml
|
||||
sdk: https://cloud-pipelines.net/pipeline-editor/
|
||||
implementation:
|
||||
graph:
|
||||
tasks:
|
||||
Download from GCS:
|
||||
componentRef:
|
||||
digest: 4175c9ff143cb8cc75d05451c0a0ebdf5a0d6d020816e29f5e9cefbb7d56f241
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/27a5ea25e849c9e8c0cb6ed65518bc3ece259aaf/components/google-cloud/storage/download/workaround_for_buggy_KFPv2_compiler/component.yaml
|
||||
arguments:
|
||||
GCS path: gs://ml-pipeline-dataset/Chicago_taxi_trips/chicago_taxi_trips_2019-01-01_-_2019-02-01_limit=10000.csv
|
||||
annotations:
|
||||
editor.position: '{"x":240,"y":40,"width":180,"height":40}'
|
||||
Select columns using Pandas on CSV data:
|
||||
componentRef:
|
||||
digest: 9b9500f461c1d04f1e48992de9138db14a6800f23649d73048673d5ea6dc56ad
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/8c78aae096806cff3bc331a40566f42f5c3e9d4b/components/pandas/Select_columns/in_CSV_format/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: Data
|
||||
taskId: Download from GCS
|
||||
column_names: '["tips", "trip_seconds", "trip_miles", "pickup_community_area", "dropoff_community_area", "fare", "tolls", "extras"]'
|
||||
annotations:
|
||||
editor.position: '{"x":240,"y":130,"width":180,"height":54}'
|
||||
Create fully connected pytorch network:
|
||||
componentRef:
|
||||
digest: d03d8248fd358a0275ec33568ee7dd7dce576cc112b09dfafe2651e4d97e04a9
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/1a2ef3eeb77bc278f33cad0dd29008ea2431e191/components/PyTorch/Create_fully_connected_network/component.yaml
|
||||
arguments:
|
||||
input_size: '7'
|
||||
hidden_layer_sizes: '[10]'
|
||||
activation_name: elu
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":240,"width":180,"height":54}'
|
||||
Fill all missing values using Pandas on CSV data:
|
||||
componentRef:
|
||||
digest: a1b0c29a4615f2e3652aa5d31b9255fa15700e146627c755f8fc172f82e71af7
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/23405971f5f16a41b16c343129b893c52e4d1d48/components/pandas/Fill_all_missing_values/in_CSV_format/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: transformed_table
|
||||
taskId: Select columns using Pandas on CSV data
|
||||
type: CSV
|
||||
replacement_value: '0'
|
||||
annotations:
|
||||
editor.position: '{"x":240,"y":240,"width":180,"height":54}'
|
||||
Train pytorch model from csv:
|
||||
componentRef:
|
||||
digest: 40f3185eb61e9727f41a4e0c05dd3d3b44bd802aa0f378cfc31756560033949a
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/d8c4cf5e6403bc65bcf8d606e6baf87e2528a3dc/components/PyTorch/Train_PyTorch_model/from_CSV/component.yaml
|
||||
arguments:
|
||||
model:
|
||||
taskOutput:
|
||||
outputName: model
|
||||
taskId: Create fully connected pytorch network
|
||||
type: PyTorchScriptModule
|
||||
training_data:
|
||||
taskOutput:
|
||||
outputName: transformed_table
|
||||
taskId: Fill all missing values using Pandas on CSV data
|
||||
type: CSV
|
||||
label_column_name: tips
|
||||
annotations:
|
||||
editor.position: '{"x":240,"y":380,"width":180,"height":40}'
|
||||
Create PyTorch Model Archive with base handler:
|
||||
componentRef:
|
||||
digest: 8298b5ee1b0f0879f893add4cf352c8dec7cf9e21bb9db134c91a2d046cdb0ec
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/46d51383e6554b7f3ab4fd8cf614d8c2b422fb22/components/PyTorch/Create_PyTorch_Model_Archive/with_base_handler/component.yaml
|
||||
arguments:
|
||||
Model:
|
||||
taskOutput:
|
||||
outputName: trained_model
|
||||
taskId: Train pytorch model from csv
|
||||
type: PyTorchScriptModule
|
||||
Model name: model
|
||||
Model version: '1.0'
|
||||
annotations:
|
||||
editor.position: '{"x":240,"y":500,"width":180,"height":54}'
|
||||
Upload PyTorch model archive to Google Cloud Vertex AI:
|
||||
componentRef:
|
||||
digest: 4450212fae7b9001482aca7eb78b28413c205506eccf08a04e7754a8dfa99004
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/c6a8b67d1ada2cc17665c99ff6b410df588bee28/components/google-cloud/Vertex_AI/Models/Upload_PyTorch_model_archive/workaround_for_buggy_KFPv2_compiler/component.yaml
|
||||
arguments:
|
||||
model_archive:
|
||||
taskOutput:
|
||||
outputName: Model archive
|
||||
taskId: Create PyTorch Model Archive with base handler
|
||||
type: PyTorchModelArchive
|
||||
annotations:
|
||||
editor.position: '{"x":240,"y":630,"width":180,"height":70}'
|
||||
outputValues: {}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
# python3 -m pip install "kfp<2.0.0" "google-cloud-aiplatform>=1.16.0" --upgrade --quiet
|
||||
from kfp import components
|
||||
|
||||
# %% Loading components
|
||||
download_from_gcs_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/storage/download/component.yaml")
|
||||
select_columns_using_Pandas_on_CSV_data_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/pandas/Select_columns/in_CSV_format/component.yaml")
|
||||
fill_all_missing_values_using_Pandas_on_CSV_data_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/pandas/Fill_all_missing_values/in_CSV_format/component.yaml")
|
||||
create_fully_connected_pytorch_network_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/PyTorch/Create_fully_connected_network/component.yaml")
|
||||
train_pytorch_model_from_csv_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/PyTorch/Train_PyTorch_model/from_CSV/component.yaml")
|
||||
create_pytorch_model_archive_with_base_handler_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/PyTorch/Create_PyTorch_Model_Archive/with_base_handler/component.yaml")
|
||||
upload_PyTorch_model_archive_to_Google_Cloud_Vertex_AI_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/Vertex_AI/Models/Upload_PyTorch_model_archive/component.yaml")
|
||||
deploy_model_to_endpoint_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/Vertex_AI/Models/Deploy_to_endpoint/component.yaml")
|
||||
|
||||
# %% Pipeline definition
|
||||
def train_tabular_regression_model_using_PyTorch_pipeline():
|
||||
dataset_gcs_uri = "gs://ml-pipeline-dataset/Chicago_taxi_trips/chicago_taxi_trips_2019-01-01_-_2019-02-01_limit=10000.csv"
|
||||
feature_columns = ["trip_seconds", "trip_miles", "pickup_community_area", "dropoff_community_area", "fare", "tolls", "extras"] # Excluded "trip_total"
|
||||
label_column = "tips"
|
||||
all_columns = [label_column] + feature_columns
|
||||
# Deploying the model might incur additional costs over time
|
||||
deploy_model = False
|
||||
|
||||
training_data = download_from_gcs_op(
|
||||
gcs_path=dataset_gcs_uri
|
||||
).outputs["Data"]
|
||||
|
||||
training_data = select_columns_using_Pandas_on_CSV_data_op(
|
||||
table=training_data,
|
||||
column_names=all_columns,
|
||||
).outputs["transformed_table"]
|
||||
|
||||
# Cleaning the NaN values.
|
||||
training_data = fill_all_missing_values_using_Pandas_on_CSV_data_op(
|
||||
table=training_data,
|
||||
replacement_value="0",
|
||||
#replacement_type_name="float",
|
||||
).outputs["transformed_table"]
|
||||
|
||||
network = create_fully_connected_pytorch_network_op(
|
||||
input_size=len(feature_columns),
|
||||
# Optional:
|
||||
hidden_layer_sizes=[10],
|
||||
activation_name="elu",
|
||||
# output_activation_name=None,
|
||||
# output_size=1,
|
||||
).outputs["model"]
|
||||
|
||||
model = train_pytorch_model_from_csv_op(
|
||||
model=network,
|
||||
training_data=training_data,
|
||||
label_column_name=label_column,
|
||||
# Optional:
|
||||
#loss_function_name="mse_loss",
|
||||
#number_of_epochs=1,
|
||||
#learning_rate=0.1,
|
||||
#optimizer_name="Adadelta",
|
||||
#optimizer_parameters={},
|
||||
#batch_size=32,
|
||||
#batch_log_interval=100,
|
||||
#random_seed=0,
|
||||
).outputs["trained_model"]
|
||||
|
||||
model_archive = create_pytorch_model_archive_with_base_handler_op(
|
||||
model=model,
|
||||
# Optional:
|
||||
# model_name="model",
|
||||
# model_version="1.0",
|
||||
).outputs["Model archive"]
|
||||
|
||||
vertex_model_name = upload_PyTorch_model_archive_to_Google_Cloud_Vertex_AI_op(
|
||||
model_archive=model_archive,
|
||||
).outputs["model_name"]
|
||||
|
||||
# Deploying the model might incur additional costs over time
|
||||
if deploy_model:
|
||||
vertex_endpoint_name = deploy_model_to_endpoint_op(
|
||||
model_name=vertex_model_name,
|
||||
).outputs["endpoint_name"]
|
||||
|
||||
pipeline_func=train_tabular_regression_model_using_PyTorch_pipeline
|
||||
|
||||
# %% Pipeline submission
|
||||
if __name__ == '__main__':
|
||||
from google.cloud import aiplatform
|
||||
aiplatform.PipelineJob.from_pipeline_func(pipeline_func=pipeline_func).submit()
|
||||
+116
@@ -0,0 +1,116 @@
|
||||
name: Train tabular regression model using Tensorflow pipeline
|
||||
metadata:
|
||||
annotations:
|
||||
author: Alexey Volkov <alexey.volkov@ark-kun.com>
|
||||
canonical_location: https://raw.githubusercontent.com/Ark-kun/pipeline_components/master/samples/Google_Cloud_Vertex_AI/Train_tabular_regression_model_using_TensorFlow_and_import_to_Vertex_AI/pipeline.component.yaml
|
||||
sdk: https://cloud-pipelines.net/pipeline-editor/
|
||||
implementation:
|
||||
graph:
|
||||
tasks:
|
||||
Download from GCS:
|
||||
componentRef:
|
||||
digest: 4175c9ff143cb8cc75d05451c0a0ebdf5a0d6d020816e29f5e9cefbb7d56f241
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/27a5ea25e849c9e8c0cb6ed65518bc3ece259aaf/components/google-cloud/storage/download/workaround_for_buggy_KFPv2_compiler/component.yaml
|
||||
arguments:
|
||||
GCS path: gs://ml-pipeline-dataset/Chicago_taxi_trips/chicago_taxi_trips_2019-01-01_-_2019-02-01_limit=10000.csv
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":40,"width":180,"height":40}'
|
||||
Select columns using Pandas on CSV data:
|
||||
componentRef:
|
||||
digest: 9b9500f461c1d04f1e48992de9138db14a6800f23649d73048673d5ea6dc56ad
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/8c78aae096806cff3bc331a40566f42f5c3e9d4b/components/pandas/Select_columns/in_CSV_format/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: Data
|
||||
taskId: Download from GCS
|
||||
column_names: '["tips", "trip_seconds", "trip_miles", "pickup_community_area", "dropoff_community_area", "fare", "tolls", "extras"]'
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":140,"width":180,"height":54}'
|
||||
Fill all missing values using Pandas on CSV data:
|
||||
componentRef:
|
||||
digest: a1b0c29a4615f2e3652aa5d31b9255fa15700e146627c755f8fc172f82e71af7
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/23405971f5f16a41b16c343129b893c52e4d1d48/components/pandas/Fill_all_missing_values/in_CSV_format/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: transformed_table
|
||||
taskId: Select columns using Pandas on CSV data
|
||||
type: CSV
|
||||
replacement_value: '0'
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":250,"width":180,"height":54}'
|
||||
Split rows into subsets:
|
||||
componentRef:
|
||||
digest: a609c3c9196484290f24a1174955f95b27f07a7b458aa5cb8cde28866cb2cb46
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/daae5a4abaa35e44501818b1534ed7827d7da073/components/dataset_manipulation/Split_rows_into_subsets/in_CSV/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: transformed_table
|
||||
taskId: Fill all missing values using Pandas on CSV data
|
||||
type: CSV
|
||||
fraction_1: '0.8'
|
||||
annotations:
|
||||
editor.position: '{"x":170,"y":380,"width":180,"height":40}'
|
||||
Create fully connected tensorflow network:
|
||||
componentRef:
|
||||
digest: bfcafbc5ce711b1f69cabf1338212d10d50136a73db9f9f7c984de7b80b4bfb0
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/9ca0f9eecf5f896f65b8538bbd809747052617d1/components/tensorflow/Create_fully_connected_network/component.yaml
|
||||
arguments:
|
||||
input_size: '7'
|
||||
hidden_layer_sizes: '[10]'
|
||||
activation_name: elu
|
||||
annotations:
|
||||
editor.position: '{"x":370,"y":380,"width":180,"height":54}'
|
||||
Train model using Keras on CSV:
|
||||
componentRef:
|
||||
digest: 42ae60c889034dbad74815653e95b4f7d576b5f47f803173e8679c7b54984609
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/c504a4010348c50eaaf6d4337586ccc008f4dcef/components/tensorflow/Train_model_using_Keras/on_CSV/component.yaml
|
||||
arguments:
|
||||
training_data:
|
||||
taskOutput:
|
||||
outputName: split_1
|
||||
taskId: Split rows into subsets
|
||||
type: CSV
|
||||
model:
|
||||
taskOutput:
|
||||
outputName: model
|
||||
taskId: Create fully connected tensorflow network
|
||||
type: TensorflowSavedModel
|
||||
label_column_name: tips
|
||||
number_of_epochs: '10'
|
||||
metric_names: '["mean_absolute_error"]'
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":500,"width":180,"height":54}'
|
||||
Upload Tensorflow model to Google Cloud Vertex AI:
|
||||
componentRef:
|
||||
digest: 2e45263ff640b1a688e359b6936e27a81b2407749a84f340af2aa5547e0cb92c
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/c6a8b67d1ada2cc17665c99ff6b410df588bee28/components/google-cloud/Vertex_AI/Models/Upload_Tensorflow_model/workaround_for_buggy_KFPv2_compiler/component.yaml
|
||||
arguments:
|
||||
model:
|
||||
taskOutput:
|
||||
outputName: trained_model
|
||||
taskId: Train model using Keras on CSV
|
||||
type: TensorflowSavedModel
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":630,"width":180,"height":54}'
|
||||
Predict with TensorFlow model on CSV data:
|
||||
componentRef:
|
||||
digest: 921bb1563e93a78233b8acceab87055b9154ccf5595d056028cf0396ca224cd4
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/59c759ce6f543184e30db6817d2a703879bc0f39/components/tensorflow/Predict/on_CSV/component.yaml
|
||||
arguments:
|
||||
dataset:
|
||||
taskOutput:
|
||||
outputName: split_2
|
||||
taskId: Split rows into subsets
|
||||
type: CSV
|
||||
model:
|
||||
taskOutput:
|
||||
outputName: trained_model
|
||||
taskId: Train model using Keras on CSV
|
||||
type: TensorflowSavedModel
|
||||
label_column_name: tips
|
||||
annotations:
|
||||
editor.position: '{"x":240,"y":630,"width":180,"height":54}'
|
||||
outputValues: {}
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
# python3 -m pip install "kfp<2.0.0" "google-cloud-aiplatform>=1.16.0" --upgrade --quiet
|
||||
from kfp import components
|
||||
|
||||
# %% Loading components
|
||||
download_from_gcs_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/storage/download/component.yaml")
|
||||
select_columns_using_Pandas_on_CSV_data_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/pandas/Select_columns/in_CSV_format/component.yaml")
|
||||
fill_all_missing_values_using_Pandas_on_CSV_data_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/pandas/Fill_all_missing_values/in_CSV_format/component.yaml")
|
||||
split_rows_into_subsets_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/dataset_manipulation/Split_rows_into_subsets/in_CSV/component.yaml")
|
||||
create_fully_connected_tensorflow_network_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/tensorflow/Create_fully_connected_network/component.yaml")
|
||||
train_model_using_Keras_on_CSV_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/tensorflow/Train_model_using_Keras/on_CSV/component.yaml")
|
||||
predict_with_TensorFlow_model_on_CSV_data_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/tensorflow/Predict/on_CSV/component.yaml")
|
||||
upload_Tensorflow_model_to_Google_Cloud_Vertex_AI_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/Vertex_AI/Models/Upload_Tensorflow_model/component.yaml")
|
||||
deploy_model_to_endpoint_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/Vertex_AI/Models/Deploy_to_endpoint/component.yaml")
|
||||
|
||||
# %% Pipeline definition
|
||||
def train_tabular_regression_model_using_Tensorflow_pipeline():
|
||||
dataset_gcs_uri = "gs://ml-pipeline-dataset/Chicago_taxi_trips/chicago_taxi_trips_2019-01-01_-_2019-02-01_limit=10000.csv"
|
||||
feature_columns = ["trip_seconds", "trip_miles", "pickup_community_area", "dropoff_community_area", "fare", "tolls", "extras"] # Excluded "trip_total"
|
||||
label_column = "tips"
|
||||
training_set_fraction = 0.8
|
||||
# Deploying the model might incur additional costs over time
|
||||
deploy_model = False
|
||||
|
||||
all_columns = [label_column] + feature_columns
|
||||
|
||||
dataset = download_from_gcs_op(
|
||||
gcs_path=dataset_gcs_uri
|
||||
).outputs["Data"]
|
||||
|
||||
dataset = select_columns_using_Pandas_on_CSV_data_op(
|
||||
table=dataset,
|
||||
column_names=all_columns,
|
||||
).outputs["transformed_table"]
|
||||
|
||||
dataset = fill_all_missing_values_using_Pandas_on_CSV_data_op(
|
||||
table=dataset,
|
||||
replacement_value="0",
|
||||
# # Optional:
|
||||
# column_names=None, # =[...]
|
||||
).outputs["transformed_table"]
|
||||
|
||||
split_task = split_rows_into_subsets_op(
|
||||
table=dataset,
|
||||
fraction_1=training_set_fraction,
|
||||
)
|
||||
training_data = split_task.outputs["split_1"]
|
||||
testing_data = split_task.outputs["split_2"]
|
||||
|
||||
network = create_fully_connected_tensorflow_network_op(
|
||||
input_size=len(feature_columns),
|
||||
# Optional:
|
||||
hidden_layer_sizes=[10],
|
||||
activation_name="elu",
|
||||
# output_activation_name=None,
|
||||
# output_size=1,
|
||||
).outputs["model"]
|
||||
|
||||
model = train_model_using_Keras_on_CSV_op(
|
||||
training_data=training_data,
|
||||
model=network,
|
||||
label_column_name=label_column,
|
||||
# Optional:
|
||||
#loss_function_name="mean_squared_error",
|
||||
number_of_epochs=10,
|
||||
#learning_rate=0.1,
|
||||
#optimizer_name="Adadelta",
|
||||
#optimizer_parameters={},
|
||||
#batch_size=32,
|
||||
metric_names=["mean_absolute_error"],
|
||||
#random_seed=0,
|
||||
).outputs["trained_model"]
|
||||
|
||||
predictions = predict_with_TensorFlow_model_on_CSV_data_op(
|
||||
dataset=testing_data,
|
||||
model=model,
|
||||
# label_column_name needs to be set when doing prediction on a dataset that has labels
|
||||
label_column_name=label_column,
|
||||
# Optional:
|
||||
# batch_size=1000,
|
||||
).outputs["predictions"]
|
||||
|
||||
vertex_model_name = upload_Tensorflow_model_to_Google_Cloud_Vertex_AI_op(
|
||||
model=model,
|
||||
).outputs["model_name"]
|
||||
|
||||
# Deploying the model might incur additional costs over time
|
||||
if deploy_model:
|
||||
vertex_endpoint_name = deploy_model_to_endpoint_op(
|
||||
model_name=vertex_model_name,
|
||||
).outputs["endpoint_name"]
|
||||
|
||||
pipeline_func=train_tabular_regression_model_using_Tensorflow_pipeline
|
||||
|
||||
# %% Pipeline submission
|
||||
if __name__ == '__main__':
|
||||
from google.cloud import aiplatform
|
||||
aiplatform.PipelineJob.from_pipeline_func(pipeline_func=pipeline_func).submit()
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
name: Train tabular regression model using XGBoost pipeline
|
||||
metadata:
|
||||
annotations:
|
||||
author: Alexey Volkov <alexey.volkov@ark-kun.com>
|
||||
canonical_location: https://raw.githubusercontent.com/Ark-kun/pipeline_components/master/samples/Google_Cloud_Vertex_AI/Train_tabular_regression_model_using_XGBoost_and_import_to_Vertex_AI/pipeline.component.yaml
|
||||
sdk: https://cloud-pipelines.net/pipeline-editor/
|
||||
implementation:
|
||||
graph:
|
||||
tasks:
|
||||
Download from GCS:
|
||||
componentRef:
|
||||
digest: 4175c9ff143cb8cc75d05451c0a0ebdf5a0d6d020816e29f5e9cefbb7d56f241
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/27a5ea25e849c9e8c0cb6ed65518bc3ece259aaf/components/google-cloud/storage/download/workaround_for_buggy_KFPv2_compiler/component.yaml
|
||||
arguments:
|
||||
GCS path: gs://ml-pipeline-dataset/Chicago_taxi_trips/chicago_taxi_trips_2019-01-01_-_2019-02-01_limit=10000.csv
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":40,"width":180,"height":40}'
|
||||
Select columns using Pandas on CSV data:
|
||||
componentRef:
|
||||
digest: 9b9500f461c1d04f1e48992de9138db14a6800f23649d73048673d5ea6dc56ad
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/8c78aae096806cff3bc331a40566f42f5c3e9d4b/components/pandas/Select_columns/in_CSV_format/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: Data
|
||||
taskId: Download from GCS
|
||||
column_names: '["tips", "trip_seconds", "trip_miles", "pickup_community_area", "dropoff_community_area", "fare", "tolls", "extras"]'
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":140,"width":180,"height":54}'
|
||||
Fill all missing values using Pandas on CSV data:
|
||||
componentRef:
|
||||
digest: a1b0c29a4615f2e3652aa5d31b9255fa15700e146627c755f8fc172f82e71af7
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/23405971f5f16a41b16c343129b893c52e4d1d48/components/pandas/Fill_all_missing_values/in_CSV_format/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: transformed_table
|
||||
taskId: Select columns using Pandas on CSV data
|
||||
type: CSV
|
||||
replacement_value: '0'
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":250,"width":180,"height":54}'
|
||||
Split rows into subsets:
|
||||
componentRef:
|
||||
digest: a609c3c9196484290f24a1174955f95b27f07a7b458aa5cb8cde28866cb2cb46
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/daae5a4abaa35e44501818b1534ed7827d7da073/components/dataset_manipulation/Split_rows_into_subsets/in_CSV/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: transformed_table
|
||||
taskId: Fill all missing values using Pandas on CSV data
|
||||
type: CSV
|
||||
fraction_1: '0.8'
|
||||
annotations:
|
||||
editor.position: '{"x":170,"y":360,"width":180,"height":40}'
|
||||
Train XGBoost model on CSV:
|
||||
componentRef:
|
||||
digest: 538c5a01eb38deaf532d619f0bbeaff4efc550fe1f0f776fc06791097b68ceac
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/58d3a47f904f32a64af8403330ba7e2134cae46d/components/XGBoost/Train/component.yaml
|
||||
arguments:
|
||||
training_data:
|
||||
taskOutput:
|
||||
outputName: split_1
|
||||
taskId: Split rows into subsets
|
||||
type: CSV
|
||||
label_column_name: tips
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":480,"width":180,"height":40}'
|
||||
Upload XGBoost model to Google Cloud Vertex AI:
|
||||
componentRef:
|
||||
digest: 5a5a273c403670743820986c03a4175b7cb4595a556524fefcce403656286977
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/c6a8b67d1ada2cc17665c99ff6b410df588bee28/components/google-cloud/Vertex_AI/Models/Upload_XGBoost_model/workaround_for_buggy_KFPv2_compiler/component.yaml
|
||||
arguments:
|
||||
model:
|
||||
taskOutput:
|
||||
outputName: model
|
||||
taskId: Train XGBoost model on CSV
|
||||
type: XGBoostModel
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":600,"width":180,"height":54}'
|
||||
Xgboost predict on CSV:
|
||||
componentRef:
|
||||
digest: 0876233a0c7306fefec188bd70f059b46d1fb5aa57be231799570e3bbbdd0d95
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/4694ec97baccf59284c2a1db4aa2250c22291eab/components/XGBoost/Predict/component.yaml
|
||||
arguments:
|
||||
data:
|
||||
taskOutput:
|
||||
outputName: split_2
|
||||
taskId: Split rows into subsets
|
||||
type: CSV
|
||||
model:
|
||||
taskOutput:
|
||||
outputName: model
|
||||
taskId: Train XGBoost model on CSV
|
||||
type: XGBoostModel
|
||||
label_column_name: tips
|
||||
annotations:
|
||||
editor.position: '{"x":240,"y":600,"width":180,"height":40}'
|
||||
outputValues: {}
|
||||
+85
@@ -0,0 +1,85 @@
|
||||
# python3 -m pip install "kfp<2.0.0" "google-cloud-aiplatform>=1.16.0" --upgrade --quiet
|
||||
from kfp import components
|
||||
|
||||
# %% Loading components
|
||||
download_from_gcs_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/storage/download/component.yaml")
|
||||
select_columns_using_Pandas_on_CSV_data_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/pandas/Select_columns/in_CSV_format/component.yaml")
|
||||
fill_all_missing_values_using_Pandas_on_CSV_data_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/pandas/Fill_all_missing_values/in_CSV_format/component.yaml")
|
||||
split_rows_into_subsets_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/dataset_manipulation/Split_rows_into_subsets/in_CSV/component.yaml")
|
||||
train_XGBoost_model_on_CSV_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/XGBoost/Train/component.yaml")
|
||||
xgboost_predict_on_CSV_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/XGBoost/Predict/component.yaml")
|
||||
upload_XGBoost_model_to_Google_Cloud_Vertex_AI_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/Vertex_AI/Models/Upload_XGBoost_model/component.yaml")
|
||||
deploy_model_to_endpoint_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/Vertex_AI/Models/Deploy_to_endpoint/component.yaml")
|
||||
|
||||
# %% Pipeline definition
|
||||
def train_tabular_regression_model_using_XGBoost_pipeline():
|
||||
dataset_gcs_uri = "gs://ml-pipeline-dataset/Chicago_taxi_trips/chicago_taxi_trips_2019-01-01_-_2019-02-01_limit=10000.csv"
|
||||
feature_columns = ["trip_seconds", "trip_miles", "pickup_community_area", "dropoff_community_area", "fare", "tolls", "extras"] # Excluded "trip_total"
|
||||
label_column = "tips"
|
||||
training_set_fraction = 0.8
|
||||
# Deploying the model might incur additional costs over time
|
||||
deploy_model = False
|
||||
|
||||
all_columns = [label_column] + feature_columns
|
||||
|
||||
dataset = download_from_gcs_op(
|
||||
gcs_path=dataset_gcs_uri
|
||||
).outputs["Data"]
|
||||
|
||||
dataset = select_columns_using_Pandas_on_CSV_data_op(
|
||||
table=dataset,
|
||||
column_names=all_columns,
|
||||
).outputs["transformed_table"]
|
||||
|
||||
dataset = fill_all_missing_values_using_Pandas_on_CSV_data_op(
|
||||
table=dataset,
|
||||
replacement_value="0",
|
||||
# # Optional:
|
||||
# column_names=None, # =[...]
|
||||
).outputs["transformed_table"]
|
||||
|
||||
split_task = split_rows_into_subsets_op(
|
||||
table=dataset,
|
||||
fraction_1=training_set_fraction,
|
||||
)
|
||||
training_data = split_task.outputs["split_1"]
|
||||
testing_data = split_task.outputs["split_2"]
|
||||
|
||||
model = train_XGBoost_model_on_CSV_op(
|
||||
training_data=training_data,
|
||||
label_column_name=label_column,
|
||||
# Optional:
|
||||
#starting_model=None,
|
||||
#num_iterations=10,
|
||||
#booster_params={},
|
||||
#objective="reg:squarederror",
|
||||
#booster="gbtree",
|
||||
#learning_rate=0.3,
|
||||
#min_split_loss=0,
|
||||
#max_depth=6,
|
||||
).outputs["model"]
|
||||
|
||||
# Predicting on the testing data
|
||||
predictions = xgboost_predict_on_CSV_op(
|
||||
data=testing_data,
|
||||
model=model,
|
||||
# label_column needs to be set when doing prediction on a dataset that has labels
|
||||
label_column_name=label_column,
|
||||
).outputs["predictions"]
|
||||
|
||||
vertex_model_name = upload_XGBoost_model_to_Google_Cloud_Vertex_AI_op(
|
||||
model=model,
|
||||
).outputs["model_name"]
|
||||
|
||||
# Deploying the model might incur additional costs over time
|
||||
if deploy_model:
|
||||
vertex_endpoint_name = deploy_model_to_endpoint_op(
|
||||
model_name=vertex_model_name,
|
||||
).outputs["endpoint_name"]
|
||||
|
||||
pipeline_func = train_tabular_regression_model_using_XGBoost_pipeline
|
||||
|
||||
# %% Pipeline submission
|
||||
if __name__ == '__main__':
|
||||
from google.cloud import aiplatform
|
||||
aiplatform.PipelineJob.from_pipeline_func(pipeline_func=pipeline_func).submit()
|
||||
+238
@@ -0,0 +1,238 @@
|
||||
name: Train tabular regression model using all frameworks pipeline
|
||||
metadata:
|
||||
annotations:
|
||||
author: Alexey Volkov <alexey.volkov@ark-kun.com>
|
||||
canonical_location: https://raw.githubusercontent.com/Ark-kun/pipeline_components/master/samples/Google_Cloud_Vertex_AI/Train_tabular_regression_model_using_all_frameworks_and_import_to_Vertex_AI/pipeline.component.yaml
|
||||
sdk: https://cloud-pipelines.net/pipeline-editor/
|
||||
implementation:
|
||||
graph:
|
||||
tasks:
|
||||
Download from GCS:
|
||||
componentRef:
|
||||
digest: 4175c9ff143cb8cc75d05451c0a0ebdf5a0d6d020816e29f5e9cefbb7d56f241
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/27a5ea25e849c9e8c0cb6ed65518bc3ece259aaf/components/google-cloud/storage/download/workaround_for_buggy_KFPv2_compiler/component.yaml
|
||||
arguments:
|
||||
GCS path: gs://ml-pipeline-dataset/Chicago_taxi_trips/chicago_taxi_trips_2019-01-01_-_2019-02-01_limit=10000.csv
|
||||
annotations:
|
||||
editor.position: '{"x":550,"y":40,"width":180,"height":40}'
|
||||
Select columns using Pandas on CSV data:
|
||||
componentRef:
|
||||
digest: 9b9500f461c1d04f1e48992de9138db14a6800f23649d73048673d5ea6dc56ad
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/8c78aae096806cff3bc331a40566f42f5c3e9d4b/components/pandas/Select_columns/in_CSV_format/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: Data
|
||||
taskId: Download from GCS
|
||||
column_names: '["tips", "trip_seconds", "trip_miles", "pickup_community_area", "dropoff_community_area", "fare", "tolls", "extras"]'
|
||||
annotations:
|
||||
editor.position: '{"x":550,"y":140,"width":180,"height":54}'
|
||||
Fill all missing values using Pandas on CSV data:
|
||||
componentRef:
|
||||
digest: a1b0c29a4615f2e3652aa5d31b9255fa15700e146627c755f8fc172f82e71af7
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/23405971f5f16a41b16c343129b893c52e4d1d48/components/pandas/Fill_all_missing_values/in_CSV_format/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: transformed_table
|
||||
taskId: Select columns using Pandas on CSV data
|
||||
type: CSV
|
||||
replacement_value: '0'
|
||||
annotations:
|
||||
editor.position: '{"x":550,"y":250,"width":180,"height":54}'
|
||||
Split rows into subsets:
|
||||
componentRef:
|
||||
digest: a609c3c9196484290f24a1174955f95b27f07a7b458aa5cb8cde28866cb2cb46
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/daae5a4abaa35e44501818b1534ed7827d7da073/components/dataset_manipulation/Split_rows_into_subsets/in_CSV/component.yaml
|
||||
arguments:
|
||||
table:
|
||||
taskOutput:
|
||||
outputName: transformed_table
|
||||
taskId: Fill all missing values using Pandas on CSV data
|
||||
type: CSV
|
||||
fraction_1: '0.8'
|
||||
annotations:
|
||||
editor.position: '{"x":550,"y":360,"width":180,"height":40}'
|
||||
Create fully connected pytorch network:
|
||||
componentRef:
|
||||
digest: d03d8248fd358a0275ec33568ee7dd7dce576cc112b09dfafe2651e4d97e04a9
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/1a2ef3eeb77bc278f33cad0dd29008ea2431e191/components/PyTorch/Create_fully_connected_network/component.yaml
|
||||
arguments:
|
||||
input_size: '7'
|
||||
hidden_layer_sizes: '[10]'
|
||||
activation_name: elu
|
||||
annotations:
|
||||
editor.position: '{"x":380,"y":490,"width":180,"height":54}'
|
||||
Create fully connected tensorflow network:
|
||||
componentRef:
|
||||
digest: bfcafbc5ce711b1f69cabf1338212d10d50136a73db9f9f7c984de7b80b4bfb0
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/9ca0f9eecf5f896f65b8538bbd809747052617d1/components/tensorflow/Create_fully_connected_network/component.yaml
|
||||
arguments:
|
||||
input_size: '7'
|
||||
hidden_layer_sizes: '[10]'
|
||||
activation_name: elu
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":500,"width":180,"height":54}'
|
||||
Train model using Keras on CSV:
|
||||
componentRef:
|
||||
digest: 42ae60c889034dbad74815653e95b4f7d576b5f47f803173e8679c7b54984609
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/c504a4010348c50eaaf6d4337586ccc008f4dcef/components/tensorflow/Train_model_using_Keras/on_CSV/component.yaml
|
||||
arguments:
|
||||
training_data:
|
||||
taskOutput:
|
||||
outputName: split_1
|
||||
taskId: Split rows into subsets
|
||||
type: CSV
|
||||
model:
|
||||
taskOutput:
|
||||
outputName: model
|
||||
taskId: Create fully connected tensorflow network
|
||||
type: TensorflowSavedModel
|
||||
label_column_name: tips
|
||||
number_of_epochs: '10'
|
||||
metric_names: '["mean_absolute_error"]'
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":620,"width":180,"height":54}'
|
||||
Train pytorch model from csv:
|
||||
componentRef:
|
||||
digest: 40f3185eb61e9727f41a4e0c05dd3d3b44bd802aa0f378cfc31756560033949a
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/d8c4cf5e6403bc65bcf8d606e6baf87e2528a3dc/components/PyTorch/Train_PyTorch_model/from_CSV/component.yaml
|
||||
arguments:
|
||||
model:
|
||||
taskOutput:
|
||||
outputName: model
|
||||
taskId: Create fully connected pytorch network
|
||||
type: PyTorchScriptModule
|
||||
training_data:
|
||||
taskOutput:
|
||||
outputName: split_1
|
||||
taskId: Split rows into subsets
|
||||
type: CSV
|
||||
label_column_name: tips
|
||||
annotations:
|
||||
editor.position: '{"x":380,"y":620,"width":180,"height":40}'
|
||||
Train XGBoost model on CSV:
|
||||
componentRef:
|
||||
digest: 538c5a01eb38deaf532d619f0bbeaff4efc550fe1f0f776fc06791097b68ceac
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/58d3a47f904f32a64af8403330ba7e2134cae46d/components/XGBoost/Train/component.yaml
|
||||
arguments:
|
||||
training_data:
|
||||
taskOutput:
|
||||
outputName: split_1
|
||||
taskId: Split rows into subsets
|
||||
type: CSV
|
||||
label_column_name: tips
|
||||
annotations:
|
||||
editor.position: '{"x":720,"y":620,"width":180,"height":40}'
|
||||
Train linear regression model using scikit learn from CSV:
|
||||
componentRef:
|
||||
digest: c7fe7912ab0d1fb45d201d452e9ce6be5544e7d8c6d229db7a4b931ff58560f3
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/f807e02b54d4886c65a05f40848fd51c72407f40/components/ML_frameworks/Scikit_learn/Train_linear_regression_model/from_CSV/component.yaml
|
||||
arguments:
|
||||
dataset:
|
||||
taskOutput:
|
||||
outputName: split_1
|
||||
taskId: Split rows into subsets
|
||||
type: CSV
|
||||
label_column_name: tips
|
||||
annotations:
|
||||
editor.position: '{"x":1030,"y":620,"width":180,"height":54}'
|
||||
Predict with TensorFlow model on CSV data:
|
||||
componentRef:
|
||||
digest: 921bb1563e93a78233b8acceab87055b9154ccf5595d056028cf0396ca224cd4
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/59c759ce6f543184e30db6817d2a703879bc0f39/components/tensorflow/Predict/on_CSV/component.yaml
|
||||
arguments:
|
||||
dataset:
|
||||
taskOutput:
|
||||
outputName: split_2
|
||||
taskId: Split rows into subsets
|
||||
type: CSV
|
||||
model:
|
||||
taskOutput:
|
||||
outputName: trained_model
|
||||
taskId: Train model using Keras on CSV
|
||||
type: TensorflowSavedModel
|
||||
label_column_name: tips
|
||||
annotations:
|
||||
editor.position: '{"x":160,"y":750,"width":180,"height":54}'
|
||||
Create PyTorch Model Archive with base handler:
|
||||
componentRef:
|
||||
digest: 8298b5ee1b0f0879f893add4cf352c8dec7cf9e21bb9db134c91a2d046cdb0ec
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/46d51383e6554b7f3ab4fd8cf614d8c2b422fb22/components/PyTorch/Create_PyTorch_Model_Archive/with_base_handler/component.yaml
|
||||
arguments:
|
||||
Model:
|
||||
taskOutput:
|
||||
outputName: trained_model
|
||||
taskId: Train pytorch model from csv
|
||||
type: PyTorchScriptModule
|
||||
Model name: model
|
||||
Model version: '1.0'
|
||||
annotations:
|
||||
editor.position: '{"x":380,"y":750,"width":180,"height":54}'
|
||||
Xgboost predict on CSV:
|
||||
componentRef:
|
||||
digest: 0876233a0c7306fefec188bd70f059b46d1fb5aa57be231799570e3bbbdd0d95
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/4694ec97baccf59284c2a1db4aa2250c22291eab/components/XGBoost/Predict/component.yaml
|
||||
arguments:
|
||||
data:
|
||||
taskOutput:
|
||||
outputName: split_2
|
||||
taskId: Split rows into subsets
|
||||
type: CSV
|
||||
model:
|
||||
taskOutput:
|
||||
outputName: model
|
||||
taskId: Train XGBoost model on CSV
|
||||
type: XGBoostModel
|
||||
label_column_name: tips
|
||||
annotations:
|
||||
editor.position: '{"x":810,"y":750,"width":180,"height":40}'
|
||||
Upload Scikit learn pickle model to Google Cloud Vertex AI:
|
||||
componentRef:
|
||||
digest: 81c91c8d7d21ec97e0872f669d68bd89edea87279d703685db54aa94743bebcd
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/c6a8b67d1ada2cc17665c99ff6b410df588bee28/components/google-cloud/Vertex_AI/Models/Upload_Scikit-learn_pickle_model/workaround_for_buggy_KFPv2_compiler/component.yaml
|
||||
arguments:
|
||||
model:
|
||||
taskOutput:
|
||||
outputName: model
|
||||
taskId: Train linear regression model using scikit learn from CSV
|
||||
type: ScikitLearnPickleModel
|
||||
annotations:
|
||||
editor.position: '{"x":1030,"y":750,"width":180,"height":70}'
|
||||
Upload Tensorflow model to Google Cloud Vertex AI:
|
||||
componentRef:
|
||||
digest: 2e45263ff640b1a688e359b6936e27a81b2407749a84f340af2aa5547e0cb92c
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/c6a8b67d1ada2cc17665c99ff6b410df588bee28/components/google-cloud/Vertex_AI/Models/Upload_Tensorflow_model/workaround_for_buggy_KFPv2_compiler/component.yaml
|
||||
arguments:
|
||||
model:
|
||||
taskOutput:
|
||||
outputName: trained_model
|
||||
taskId: Train model using Keras on CSV
|
||||
type: TensorflowSavedModel
|
||||
annotations:
|
||||
editor.position: '{"x":40,"y":880,"width":180,"height":54}'
|
||||
Upload PyTorch model archive to Google Cloud Vertex AI:
|
||||
componentRef:
|
||||
digest: 4450212fae7b9001482aca7eb78b28413c205506eccf08a04e7754a8dfa99004
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/c6a8b67d1ada2cc17665c99ff6b410df588bee28/components/google-cloud/Vertex_AI/Models/Upload_PyTorch_model_archive/workaround_for_buggy_KFPv2_compiler/component.yaml
|
||||
arguments:
|
||||
model_archive:
|
||||
taskOutput:
|
||||
outputName: Model archive
|
||||
taskId: Create PyTorch Model Archive with base handler
|
||||
type: PyTorchModelArchive
|
||||
annotations:
|
||||
editor.position: '{"x":380,"y":880,"width":180,"height":70}'
|
||||
Upload XGBoost model to Google Cloud Vertex AI:
|
||||
componentRef:
|
||||
digest: 5a5a273c403670743820986c03a4175b7cb4595a556524fefcce403656286977
|
||||
url: https://raw.githubusercontent.com/Ark-kun/pipeline_components/c6a8b67d1ada2cc17665c99ff6b410df588bee28/components/google-cloud/Vertex_AI/Models/Upload_XGBoost_model/workaround_for_buggy_KFPv2_compiler/component.yaml
|
||||
arguments:
|
||||
model:
|
||||
taskOutput:
|
||||
outputName: model
|
||||
taskId: Train XGBoost model on CSV
|
||||
type: XGBoostModel
|
||||
annotations:
|
||||
editor.position: '{"x":720,"y":880,"width":180,"height":54}'
|
||||
outputValues: {}
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
# python3 -m pip install "kfp<2.0.0" "google-cloud-aiplatform>=1.16.0" --upgrade --quiet
|
||||
from kfp import components
|
||||
|
||||
# %% Loading components
|
||||
download_from_gcs_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/storage/download/component.yaml")
|
||||
select_columns_using_Pandas_on_CSV_data_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/pandas/Select_columns/in_CSV_format/component.yaml")
|
||||
fill_all_missing_values_using_Pandas_on_CSV_data_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/pandas/Fill_all_missing_values/in_CSV_format/component.yaml")
|
||||
split_rows_into_subsets_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/dataset_manipulation/Split_rows_into_subsets/in_CSV/component.yaml")
|
||||
|
||||
# TensorFlow
|
||||
create_fully_connected_tensorflow_network_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/tensorflow/Create_fully_connected_network/component.yaml")
|
||||
train_model_using_Keras_on_CSV_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/tensorflow/Train_model_using_Keras/on_CSV/component.yaml")
|
||||
predict_with_TensorFlow_model_on_CSV_data_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/tensorflow/Predict/on_CSV/component.yaml")
|
||||
upload_Tensorflow_model_to_Google_Cloud_Vertex_AI_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/Vertex_AI/Models/Upload_Tensorflow_model/component.yaml")
|
||||
|
||||
# PyTorch
|
||||
create_fully_connected_pytorch_network_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/PyTorch/Create_fully_connected_network/component.yaml")
|
||||
train_pytorch_model_from_csv_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/PyTorch/Train_PyTorch_model/from_CSV/component.yaml")
|
||||
create_pytorch_model_archive_with_base_handler_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/PyTorch/Create_PyTorch_Model_Archive/with_base_handler/component.yaml")
|
||||
upload_PyTorch_model_archive_to_Google_Cloud_Vertex_AI_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/Vertex_AI/Models/Upload_PyTorch_model_archive/component.yaml")
|
||||
|
||||
# XGBoost
|
||||
train_XGBoost_model_on_CSV_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/XGBoost/Train/component.yaml")
|
||||
xgboost_predict_on_CSV_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/XGBoost/Predict/component.yaml")
|
||||
upload_XGBoost_model_to_Google_Cloud_Vertex_AI_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/Vertex_AI/Models/Upload_XGBoost_model/component.yaml")
|
||||
|
||||
# Scikit-learn
|
||||
train_linear_regression_model_using_scikit_learn_from_CSV_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/1f5cf6e06409b704064b2086c0a705e4e6b4fcde/community-content/pipeline_components/ML_frameworks/Scikit_learn/Train_linear_regression_model/from_CSV/component.yaml")
|
||||
upload_Scikit_learn_pickle_model_to_Google_Cloud_Vertex_AI_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/Vertex_AI/Models/Upload_Scikit-learn_pickle_model/component.yaml")
|
||||
|
||||
# Vertex AI
|
||||
deploy_model_to_endpoint_op = components.load_component_from_url("https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/Vertex_AI/Models/Deploy_to_endpoint/component.yaml")
|
||||
|
||||
# %% Pipeline definition
|
||||
def train_tabular_regression_model_using_all_frameworks_pipeline():
|
||||
dataset_gcs_uri = "gs://ml-pipeline-dataset/Chicago_taxi_trips/chicago_taxi_trips_2019-01-01_-_2019-02-01_limit=10000.csv"
|
||||
feature_columns = ["trip_seconds", "trip_miles", "pickup_community_area", "dropoff_community_area", "fare", "tolls", "extras"] # Excluded "trip_total"
|
||||
label_column = "tips"
|
||||
training_set_fraction = 0.8
|
||||
# Deploying the model might incur additional costs over time
|
||||
deploy_model = False
|
||||
|
||||
all_columns = [label_column] + feature_columns
|
||||
|
||||
dataset = download_from_gcs_op(
|
||||
gcs_path=dataset_gcs_uri
|
||||
).outputs["Data"]
|
||||
|
||||
dataset = select_columns_using_Pandas_on_CSV_data_op(
|
||||
table=dataset,
|
||||
column_names=all_columns,
|
||||
).outputs["transformed_table"]
|
||||
|
||||
dataset = fill_all_missing_values_using_Pandas_on_CSV_data_op(
|
||||
table=dataset,
|
||||
replacement_value="0",
|
||||
# # Optional:
|
||||
# column_names=None, # =[...]
|
||||
).outputs["transformed_table"]
|
||||
|
||||
split_task = split_rows_into_subsets_op(
|
||||
table=dataset,
|
||||
fraction_1=training_set_fraction,
|
||||
)
|
||||
training_data = split_task.outputs["split_1"]
|
||||
testing_data = split_task.outputs["split_2"]
|
||||
|
||||
# TensorFlow
|
||||
tensorflow_network = create_fully_connected_tensorflow_network_op(
|
||||
input_size=len(feature_columns),
|
||||
# Optional:
|
||||
hidden_layer_sizes=[10],
|
||||
activation_name="elu",
|
||||
# output_activation_name=None,
|
||||
# output_size=1,
|
||||
).outputs["model"]
|
||||
|
||||
tensorflow_model = train_model_using_Keras_on_CSV_op(
|
||||
training_data=training_data,
|
||||
model=tensorflow_network,
|
||||
label_column_name=label_column,
|
||||
# Optional:
|
||||
#loss_function_name="mean_squared_error",
|
||||
number_of_epochs=10,
|
||||
#learning_rate=0.1,
|
||||
#optimizer_name="Adadelta",
|
||||
#optimizer_parameters={},
|
||||
#batch_size=32,
|
||||
metric_names=["mean_absolute_error"],
|
||||
#random_seed=0,
|
||||
).outputs["trained_model"]
|
||||
|
||||
tensorflow_predictions = predict_with_TensorFlow_model_on_CSV_data_op(
|
||||
dataset=testing_data,
|
||||
model=tensorflow_model,
|
||||
# label_column_name needs to be set when doing prediction on a dataset that has labels
|
||||
label_column_name=label_column,
|
||||
# Optional:
|
||||
# batch_size=1000,
|
||||
).outputs["predictions"]
|
||||
|
||||
tensorflow_vertex_model_name = upload_Tensorflow_model_to_Google_Cloud_Vertex_AI_op(
|
||||
model=tensorflow_model,
|
||||
).outputs["model_name"]
|
||||
|
||||
# Deploying the model might incur additional costs over time
|
||||
if deploy_model:
|
||||
tensorflow_vertex_endpoint_name = deploy_model_to_endpoint_op(
|
||||
model_name=tensorflow_vertex_model_name,
|
||||
).outputs["endpoint_name"]
|
||||
|
||||
# PyTorch
|
||||
pytorch_network = create_fully_connected_pytorch_network_op(
|
||||
input_size=len(feature_columns),
|
||||
# Optional:
|
||||
hidden_layer_sizes=[10],
|
||||
activation_name="elu",
|
||||
# output_activation_name=None,
|
||||
# output_size=1,
|
||||
).outputs["model"]
|
||||
|
||||
pytorch_model = train_pytorch_model_from_csv_op(
|
||||
model=pytorch_network,
|
||||
training_data=training_data,
|
||||
label_column_name=label_column,
|
||||
# Optional:
|
||||
#loss_function_name="mse_loss",
|
||||
#number_of_epochs=1,
|
||||
#learning_rate=0.1,
|
||||
#optimizer_name="Adadelta",
|
||||
#optimizer_parameters={},
|
||||
#batch_size=32,
|
||||
#batch_log_interval=100,
|
||||
#random_seed=0,
|
||||
).outputs["trained_model"]
|
||||
|
||||
pytorch_model_archive = create_pytorch_model_archive_with_base_handler_op(
|
||||
model=pytorch_model,
|
||||
# Optional:
|
||||
# model_name="model",
|
||||
# model_version="1.0",
|
||||
).outputs["Model archive"]
|
||||
|
||||
pytorch_vertex_model_name = upload_PyTorch_model_archive_to_Google_Cloud_Vertex_AI_op(
|
||||
model_archive=pytorch_model_archive,
|
||||
).outputs["model_name"]
|
||||
|
||||
# Deploying the model might incur additional costs over time
|
||||
if deploy_model:
|
||||
pytorch_vertex_endpoint_name = deploy_model_to_endpoint_op(
|
||||
model_name=pytorch_vertex_model_name,
|
||||
).outputs["endpoint_name"]
|
||||
|
||||
# XGBoost
|
||||
xgboost_model = train_XGBoost_model_on_CSV_op(
|
||||
training_data=training_data,
|
||||
label_column_name=label_column,
|
||||
# Optional:
|
||||
#starting_model=None,
|
||||
#num_iterations=10,
|
||||
#booster_params={},
|
||||
#objective="reg:squarederror",
|
||||
#booster="gbtree",
|
||||
#learning_rate=0.3,
|
||||
#min_split_loss=0,
|
||||
#max_depth=6,
|
||||
).outputs["model"]
|
||||
|
||||
# Predicting on the testing data
|
||||
xgboost_predictions = xgboost_predict_on_CSV_op(
|
||||
data=testing_data,
|
||||
model=xgboost_model,
|
||||
# label_column needs to be set when doing prediction on a dataset that has labels
|
||||
label_column_name=label_column,
|
||||
).outputs["predictions"]
|
||||
|
||||
xgboost_vertex_model_name = upload_XGBoost_model_to_Google_Cloud_Vertex_AI_op(
|
||||
model=xgboost_model,
|
||||
).outputs["model_name"]
|
||||
|
||||
# Deploying the model might incur additional costs over time
|
||||
if deploy_model:
|
||||
xgboost_vertex_endpoint_name = deploy_model_to_endpoint_op(
|
||||
model_name=xgboost_vertex_model_name,
|
||||
).outputs["endpoint_name"]
|
||||
|
||||
# Scikit-learn
|
||||
sklearn_model = train_linear_regression_model_using_scikit_learn_from_CSV_op(
|
||||
dataset=training_data,
|
||||
label_column_name=label_column,
|
||||
).outputs["model"]
|
||||
|
||||
sklearn_vertex_model_name = upload_Scikit_learn_pickle_model_to_Google_Cloud_Vertex_AI_op(
|
||||
model=sklearn_model,
|
||||
).outputs["model_name"]
|
||||
|
||||
# Deploying the model might incur additional costs over time
|
||||
if deploy_model:
|
||||
sklearn_vertex_endpoint_name = deploy_model_to_endpoint_op(
|
||||
model_name=sklearn_vertex_model_name,
|
||||
).outputs["endpoint_name"]
|
||||
|
||||
pipeline_func=train_tabular_regression_model_using_all_frameworks_pipeline
|
||||
|
||||
# %% Pipeline submission
|
||||
if __name__ == '__main__':
|
||||
from google.cloud import aiplatform
|
||||
aiplatform.PipelineJob.from_pipeline_func(pipeline_func=pipeline_func).submit()
|
||||
@@ -2,4 +2,5 @@ cpr_model_server.py
|
||||
entrypoint.py
|
||||
state_dict.pth
|
||||
config.json
|
||||
**/__pycache__
|
||||
**/__pycache__
|
||||
!testdata/**
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## About CPR
|
||||
|
||||
CPR ([custom prediction routines](https://github.com/googleapis/python-aiplatform/blob/custom-prediction-routine/google/cloud/aiplatform/prediction/README.md)) is a framework designed by Google Cloud developers to make it easier to combine machine learning models with custom preprocessing and postprocessing logic in a real-time serving application.
|
||||
CPR ([custom prediction routines](https://github.com/googleapis/python-aiplatform/blob/main/google/cloud/aiplatform/prediction/README.md)) is a framework designed by Google Cloud developers to make it easier to combine machine learning models with custom preprocessing and postprocessing logic in a real-time serving application.
|
||||
|
||||
## Using this example
|
||||
|
||||
@@ -34,6 +34,23 @@ Finally, install the Python modules required to build and run the model server:
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
### Auth
|
||||
|
||||
This example uses Google Cloud Storage for hosting model artifacts and Artifact Registry to store the container image.
|
||||
You'll need to authorize yourself before you can interact with these.
|
||||
|
||||
First, log in to GCP with application default credentials:
|
||||
```sh
|
||||
gcloud auth application-default login
|
||||
```
|
||||
|
||||
Next, if you haven't done so already, set up the [gcloud credential helper](https://cloud.google.com/artifact-registry/docs/docker/authentication)
|
||||
for the Artifact Registry region where you intend to host the image.
|
||||
```
|
||||
gcloud auth configure-docker <region>-docker.pkg.dev
|
||||
```
|
||||
|
||||
|
||||
### Predictor
|
||||
|
||||
The `TimmPredictor` class in `timm_serving/predictor.py` implements most of the important logic for the server.
|
||||
|
||||
@@ -60,9 +60,9 @@ class CPRConfig(object):
|
||||
image: str = "timm_predictor:latest"
|
||||
artifact_local_dir: str = ""
|
||||
region: str = "us-central1"
|
||||
project_id: str = "samthrasher-experimental"
|
||||
project_id: str = "<your project ID here>"
|
||||
repository: str = "cpr-images"
|
||||
artifact_gcs_dir: str = "gs://samthrasher-cpr-example/timm-vit224/"
|
||||
artifact_gcs_dir: str = "gs://<your bucket ID here>/timm-vit224/"
|
||||
model_name: str = ""
|
||||
endpoint_name: str = ""
|
||||
machine_type: str = "n1-standard-2"
|
||||
|
||||
@@ -5,4 +5,4 @@ timm==0.5.4
|
||||
smart_open==6.0.0
|
||||
|
||||
google-cloud-storage>=1.26.0,<2.0.0dev
|
||||
google-cloud-aiplatform[prediction] @ git+https://github.com/googleapis/python-aiplatform.git@custom-prediction-routine
|
||||
google-cloud-aiplatform[prediction]>=1.16.0
|
||||
@@ -70,7 +70,10 @@ class PredictorUnitTests(absltest.TestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.config = CPRConfig()
|
||||
self.config.load()
|
||||
try:
|
||||
self.config.load()
|
||||
except FileNotFoundError:
|
||||
logging.info("No saved config file found, using default values.")
|
||||
self.predictor = predictor.TimmPredictor()
|
||||
|
||||
def test_load_from_saved_state_dict_ok(self):
|
||||
@@ -170,7 +173,10 @@ class ServerEndToEndTests(absltest.TestCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.config = CPRConfig()
|
||||
self.config.load()
|
||||
try:
|
||||
self.config.load()
|
||||
except FileNotFoundError:
|
||||
logging.info("No saved config file found, using default values.")
|
||||
self.local_model = cpr.LocalModel(
|
||||
serving_container_spec=aiplatform.gapic.ModelContainerSpec(
|
||||
image_uri=self.config.image
|
||||
|
||||
+1
@@ -0,0 +1 @@
|
||||
blah
|
||||
BIN
Binary file not shown.
+64
@@ -0,0 +1,64 @@
|
||||
name: Train linear regression model using scikit learn from CSV
|
||||
metadata:
|
||||
annotations: {author: Alexey Volkov <alexey.volkov@ark-kun.com>, canonical_location: 'https://raw.githubusercontent.com/Ark-kun/pipeline_components/master/components/ML_frameworks/Scikit_learn/Train_linear_regression_model/from_CSV/component.yaml'}
|
||||
inputs:
|
||||
- {name: dataset, type: CSV}
|
||||
- {name: label_column_name, type: String}
|
||||
outputs:
|
||||
- {name: model, type: ScikitLearnPickleModel}
|
||||
implementation:
|
||||
container:
|
||||
image: python:3.9
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- (PIP_DISABLE_PIP_VERSION_CHECK=1 python3 -m pip install --quiet --no-warn-script-location
|
||||
'scikit-learn==1.0.2' 'pandas==1.4.3' || PIP_DISABLE_PIP_VERSION_CHECK=1 python3
|
||||
-m pip install --quiet --no-warn-script-location 'scikit-learn==1.0.2' 'pandas==1.4.3'
|
||||
--user) && "$0" "$@"
|
||||
- sh
|
||||
- -ec
|
||||
- |
|
||||
program_path=$(mktemp)
|
||||
printf "%s" "$0" > "$program_path"
|
||||
python3 -u "$program_path" "$@"
|
||||
- |
|
||||
def _make_parent_dirs_and_return_path(file_path: str):
|
||||
import os
|
||||
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
||||
return file_path
|
||||
|
||||
def train_linear_regression_model_using_scikit_learn_from_CSV(
|
||||
dataset_path,
|
||||
model_path,
|
||||
label_column_name,
|
||||
):
|
||||
import pandas
|
||||
import pickle
|
||||
from sklearn import linear_model
|
||||
|
||||
df = pandas.read_csv(dataset_path)
|
||||
model = linear_model.LinearRegression()
|
||||
model.fit(
|
||||
X=df.drop(columns=label_column_name),
|
||||
y=df[label_column_name],
|
||||
)
|
||||
|
||||
with open(model_path, "wb") as f:
|
||||
pickle.dump(model, f)
|
||||
|
||||
import argparse
|
||||
_parser = argparse.ArgumentParser(prog='Train linear regression model using scikit learn from CSV', description='')
|
||||
_parser.add_argument("--dataset", dest="dataset_path", type=str, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--label-column-name", dest="label_column_name", type=str, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--model", dest="model_path", type=_make_parent_dirs_and_return_path, required=True, default=argparse.SUPPRESS)
|
||||
_parsed_args = vars(_parser.parse_args())
|
||||
|
||||
_outputs = train_linear_regression_model_using_scikit_learn_from_CSV(**_parsed_args)
|
||||
args:
|
||||
- --dataset
|
||||
- {inputPath: dataset}
|
||||
- --label-column-name
|
||||
- {inputValue: label_column_name}
|
||||
- --model
|
||||
- {outputPath: model}
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
name: Train logistic regression model using scikit learn from CSV
|
||||
description: Train logistic regression model using Scikit-learn
|
||||
metadata:
|
||||
annotations: {author: Alexey Volkov <alexey.volkov@ark-kun.com>, canonical_location: 'https://raw.githubusercontent.com/Ark-kun/pipeline_components/master/components/ML_frameworks/Scikit_learn/Train_logistic_regression_model/from_CSV/component.yaml'}
|
||||
inputs:
|
||||
- {name: dataset, type: CSV}
|
||||
- {name: label_column_name, type: String}
|
||||
- {name: penalty, type: String, default: l2, optional: true}
|
||||
- {name: solver, type: String, default: lbfgs, optional: true}
|
||||
- {name: max_iterations, type: Integer, default: '100', optional: true}
|
||||
- {name: multi_class_mode, type: String, default: auto, optional: true}
|
||||
- {name: random_seed, type: Integer, default: '0', optional: true}
|
||||
outputs:
|
||||
- {name: model, type: ScikitLearnPickleModel}
|
||||
- {name: model_parameters, type: JsonObject}
|
||||
implementation:
|
||||
container:
|
||||
image: python:3.9
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- (PIP_DISABLE_PIP_VERSION_CHECK=1 python3 -m pip install --quiet --no-warn-script-location
|
||||
'scikit-learn==1.0.2' 'pandas==1.4.3' || PIP_DISABLE_PIP_VERSION_CHECK=1 python3
|
||||
-m pip install --quiet --no-warn-script-location 'scikit-learn==1.0.2' 'pandas==1.4.3'
|
||||
--user) && "$0" "$@"
|
||||
- sh
|
||||
- -ec
|
||||
- |
|
||||
program_path=$(mktemp)
|
||||
printf "%s" "$0" > "$program_path"
|
||||
python3 -u "$program_path" "$@"
|
||||
- |
|
||||
def _make_parent_dirs_and_return_path(file_path: str):
|
||||
import os
|
||||
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
||||
return file_path
|
||||
|
||||
def train_logistic_regression_model_using_scikit_learn_from_CSV(
|
||||
dataset_path,
|
||||
model_path,
|
||||
label_column_name,
|
||||
penalty = "l2", # l1, l2, elasticnet, none
|
||||
solver = "lbfgs", # newton-cg, lbfgs, liblinear, sag, saga
|
||||
max_iterations = 100,
|
||||
multi_class_mode = "auto", # auto, ovr, multinomial
|
||||
random_seed = 0,
|
||||
):
|
||||
"""Train logistic regression model using Scikit-learn
|
||||
|
||||
See https://scikit-learn.org/stable/modules/generated/sklearn.linear_model.LogisticRegression.html
|
||||
"""
|
||||
import json
|
||||
import pandas
|
||||
import pickle
|
||||
from sklearn import linear_model
|
||||
|
||||
df = pandas.read_csv(dataset_path)
|
||||
model = linear_model.LogisticRegression(
|
||||
penalty=penalty,
|
||||
#dual=False,
|
||||
#tol=1e-4,
|
||||
#C=1.0,
|
||||
#fit_intercept=True,
|
||||
#intercept_scaling=1,
|
||||
#class_weight=None,
|
||||
random_state=random_seed,
|
||||
solver=solver,
|
||||
max_iter=max_iterations,
|
||||
multi_class=multi_class_mode,
|
||||
#l1_ratio=None,
|
||||
verbose=1,
|
||||
)
|
||||
|
||||
model_parameters = model.get_params()
|
||||
model_parameters_json = json.dumps(model_parameters, indent=2)
|
||||
print("Model parameters:")
|
||||
print(model_parameters_json)
|
||||
print()
|
||||
|
||||
model.fit(
|
||||
X=df.drop(columns=label_column_name),
|
||||
y=df[label_column_name],
|
||||
)
|
||||
|
||||
with open(model_path, "wb") as f:
|
||||
pickle.dump(model, f)
|
||||
|
||||
return (model_parameters_json,)
|
||||
|
||||
def _serialize_json(obj) -> str:
|
||||
if isinstance(obj, str):
|
||||
return obj
|
||||
import json
|
||||
def default_serializer(obj):
|
||||
if hasattr(obj, 'to_struct'):
|
||||
return obj.to_struct()
|
||||
else:
|
||||
raise TypeError("Object of type '%s' is not JSON serializable and does not have .to_struct() method." % obj.__class__.__name__)
|
||||
return json.dumps(obj, default=default_serializer, sort_keys=True)
|
||||
|
||||
import argparse
|
||||
_parser = argparse.ArgumentParser(prog='Train logistic regression model using scikit learn from CSV', description='Train logistic regression model using Scikit-learn')
|
||||
_parser.add_argument("--dataset", dest="dataset_path", type=str, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--label-column-name", dest="label_column_name", type=str, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--penalty", dest="penalty", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--solver", dest="solver", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--max-iterations", dest="max_iterations", type=int, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--multi-class-mode", dest="multi_class_mode", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--random-seed", dest="random_seed", type=int, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--model", dest="model_path", type=_make_parent_dirs_and_return_path, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("----output-paths", dest="_output_paths", type=str, nargs=1)
|
||||
_parsed_args = vars(_parser.parse_args())
|
||||
_output_files = _parsed_args.pop("_output_paths", [])
|
||||
|
||||
_outputs = train_logistic_regression_model_using_scikit_learn_from_CSV(**_parsed_args)
|
||||
|
||||
_output_serializers = [
|
||||
_serialize_json,
|
||||
|
||||
]
|
||||
|
||||
import os
|
||||
for idx, output_file in enumerate(_output_files):
|
||||
try:
|
||||
os.makedirs(os.path.dirname(output_file))
|
||||
except OSError:
|
||||
pass
|
||||
with open(output_file, 'w') as f:
|
||||
f.write(_output_serializers[idx](_outputs[idx]))
|
||||
args:
|
||||
- --dataset
|
||||
- {inputPath: dataset}
|
||||
- --label-column-name
|
||||
- {inputValue: label_column_name}
|
||||
- if:
|
||||
cond: {isPresent: penalty}
|
||||
then:
|
||||
- --penalty
|
||||
- {inputValue: penalty}
|
||||
- if:
|
||||
cond: {isPresent: solver}
|
||||
then:
|
||||
- --solver
|
||||
- {inputValue: solver}
|
||||
- if:
|
||||
cond: {isPresent: max_iterations}
|
||||
then:
|
||||
- --max-iterations
|
||||
- {inputValue: max_iterations}
|
||||
- if:
|
||||
cond: {isPresent: multi_class_mode}
|
||||
then:
|
||||
- --multi-class-mode
|
||||
- {inputValue: multi_class_mode}
|
||||
- if:
|
||||
cond: {isPresent: random_seed}
|
||||
then:
|
||||
- --random-seed
|
||||
- {inputValue: random_seed}
|
||||
- --model
|
||||
- {outputPath: model}
|
||||
- '----output-paths'
|
||||
- {outputPath: model_parameters}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
name: Create PyTorch Model Archive with base handler
|
||||
inputs:
|
||||
- {name: Model, type: PyTorchScriptModule}
|
||||
- {name: Model name, type: String, default: model}
|
||||
- {name: Model version, type: String, default: "1.0"}
|
||||
outputs:
|
||||
- {name: Model archive, type: PyTorchModelArchive}
|
||||
metadata:
|
||||
annotations:
|
||||
author: Alexey Volkov <alexey.volkov@ark-kun.com>
|
||||
canonical_location: 'https://raw.githubusercontent.com/Ark-kun/pipeline_components/master/components/PyTorch/Create_PyTorch_Model_Archive/with_base_handler/component.yaml'
|
||||
implementation:
|
||||
container:
|
||||
image: pytorch/torchserve:0.6.0-cpu
|
||||
command:
|
||||
- bash
|
||||
- -exc
|
||||
- |
|
||||
model_path=$0
|
||||
model_name=$1
|
||||
model_version=$2
|
||||
output_model_archive_path=$3
|
||||
|
||||
mkdir -p "$(dirname "$output_model_archive_path")"
|
||||
|
||||
# TODO: Use the built-in base_handler once my fix is merged: https://github.com/pytorch/serve/pull/1682
|
||||
echo '
|
||||
from ts.torch_handler import base_handler
|
||||
class BaseHandler(base_handler.BaseHandler):
|
||||
pass
|
||||
' > base_handler.py # torch-model-archiver needs the handler to have .py extension
|
||||
torch-model-archiver --model-name "$model_name" --version "$model_version" --serialized-file "$model_path" --handler base_handler.py
|
||||
|
||||
# torch-model-archiver does not allow specifying the output path, but always writes to "${model_name}.<format>"
|
||||
expected_model_archive_path="${model_name}.mar"
|
||||
mv "$expected_model_archive_path" "$output_model_archive_path"
|
||||
|
||||
- {inputPath: Model}
|
||||
- {inputValue: Model name}
|
||||
- {inputValue: Model version}
|
||||
- {outputPath: Model archive}
|
||||
+117
@@ -0,0 +1,117 @@
|
||||
name: Create fully connected pytorch network
|
||||
description: Creates fully-connected network in PyTorch ScriptModule format
|
||||
metadata:
|
||||
annotations: {author: Alexey Volkov <alexey.volkov@ark-kun.com>, canonical_location: 'https://raw.githubusercontent.com/Ark-kun/pipeline_components/master/components/PyTorch/Create_fully_connected_network/component.yaml'}
|
||||
inputs:
|
||||
- {name: input_size, type: Integer}
|
||||
- {name: hidden_layer_sizes, type: JsonArray, default: '[]', optional: true}
|
||||
- {name: output_size, type: Integer, default: '1', optional: true}
|
||||
- {name: activation_name, type: String, default: relu, optional: true}
|
||||
- {name: output_activation_name, type: String, optional: true}
|
||||
- {name: random_seed, type: Integer, default: '0', optional: true}
|
||||
outputs:
|
||||
- {name: model, type: PyTorchScriptModule}
|
||||
implementation:
|
||||
container:
|
||||
image: pytorch/pytorch:1.7.1-cuda11.0-cudnn8-runtime
|
||||
command:
|
||||
- sh
|
||||
- -ec
|
||||
- |
|
||||
program_path=$(mktemp)
|
||||
printf "%s" "$0" > "$program_path"
|
||||
python3 -u "$program_path" "$@"
|
||||
- |
|
||||
def _make_parent_dirs_and_return_path(file_path: str):
|
||||
import os
|
||||
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
||||
return file_path
|
||||
|
||||
def create_fully_connected_pytorch_network(
|
||||
input_size,
|
||||
model_path,
|
||||
hidden_layer_sizes = [],
|
||||
output_size = 1,
|
||||
activation_name = 'relu',
|
||||
output_activation_name = None,
|
||||
random_seed = 0,
|
||||
):
|
||||
'''Creates fully-connected network in PyTorch ScriptModule format'''
|
||||
import torch
|
||||
torch.manual_seed(random_seed)
|
||||
|
||||
activation = getattr(torch, activation_name, None) or getattr(torch.nn.functional, activation_name, None)
|
||||
if not activation:
|
||||
raise ValueError(f'Activation "{activation_name}" was not found.')
|
||||
|
||||
class ActivationLayer(torch.nn.Module):
|
||||
def forward(self, input):
|
||||
return activation(input)
|
||||
|
||||
layers = []
|
||||
prev_layer_size = input_size
|
||||
for layer_size in hidden_layer_sizes:
|
||||
layer = torch.nn.Linear(prev_layer_size, layer_size)
|
||||
prev_layer_size = layer_size
|
||||
layers.append(layer)
|
||||
layers.append(ActivationLayer())
|
||||
|
||||
# Adding the output layer
|
||||
layers.append(torch.nn.Linear(prev_layer_size, output_size))
|
||||
|
||||
# Adding the optional activation after the output layer
|
||||
if output_activation_name:
|
||||
output_activation = getattr(torch, output_activation_name, None) or getattr(torch.nn.functional, output_activation_name, None)
|
||||
class OutputActivationLayer(torch.nn.Module):
|
||||
def forward(self, input):
|
||||
return output_activation(input)
|
||||
layers.append(OutputActivationLayer())
|
||||
|
||||
network = torch.nn.Sequential(*layers)
|
||||
script_module = torch.jit.script(network)
|
||||
print(script_module)
|
||||
script_module.save(model_path)
|
||||
|
||||
import json
|
||||
import argparse
|
||||
_parser = argparse.ArgumentParser(prog='Create fully connected pytorch network', description='Creates fully-connected network in PyTorch ScriptModule format')
|
||||
_parser.add_argument("--input-size", dest="input_size", type=int, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--hidden-layer-sizes", dest="hidden_layer_sizes", type=json.loads, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--output-size", dest="output_size", type=int, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--activation-name", dest="activation_name", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--output-activation-name", dest="output_activation_name", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--random-seed", dest="random_seed", type=int, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--model", dest="model_path", type=_make_parent_dirs_and_return_path, required=True, default=argparse.SUPPRESS)
|
||||
_parsed_args = vars(_parser.parse_args())
|
||||
|
||||
_outputs = create_fully_connected_pytorch_network(**_parsed_args)
|
||||
args:
|
||||
- --input-size
|
||||
- {inputValue: input_size}
|
||||
- if:
|
||||
cond: {isPresent: hidden_layer_sizes}
|
||||
then:
|
||||
- --hidden-layer-sizes
|
||||
- {inputValue: hidden_layer_sizes}
|
||||
- if:
|
||||
cond: {isPresent: output_size}
|
||||
then:
|
||||
- --output-size
|
||||
- {inputValue: output_size}
|
||||
- if:
|
||||
cond: {isPresent: activation_name}
|
||||
then:
|
||||
- --activation-name
|
||||
- {inputValue: activation_name}
|
||||
- if:
|
||||
cond: {isPresent: output_activation_name}
|
||||
then:
|
||||
- --output-activation-name
|
||||
- {inputValue: output_activation_name}
|
||||
- if:
|
||||
cond: {isPresent: random_seed}
|
||||
then:
|
||||
- --random-seed
|
||||
- {inputValue: random_seed}
|
||||
- --model
|
||||
- {outputPath: model}
|
||||
+209
@@ -0,0 +1,209 @@
|
||||
name: Train pytorch model from csv
|
||||
description: Trains PyTorch model
|
||||
metadata:
|
||||
annotations:
|
||||
author: Alexey Volkov <alexey.volkov@ark-kun.com>
|
||||
canonical_location: 'https://raw.githubusercontent.com/Ark-kun/pipeline_components/master/components/PyTorch/Train_PyTorch_model/from_CSV/component.yaml'
|
||||
inputs:
|
||||
- {name: model, type: PyTorchScriptModule}
|
||||
- {name: training_data, type: CSV}
|
||||
- {name: label_column_name, type: String}
|
||||
- {name: loss_function_name, type: String, default: mse_loss, optional: true}
|
||||
- {name: number_of_epochs, type: Integer, default: '1', optional: true}
|
||||
- {name: learning_rate, type: Float, default: '0.1', optional: true}
|
||||
- {name: optimizer_name, type: String, default: Adadelta, optional: true}
|
||||
- {name: optimizer_parameters, type: JsonObject, optional: true}
|
||||
- {name: batch_size, type: Integer, default: '32', optional: true}
|
||||
- {name: batch_log_interval, type: Integer, default: '100', optional: true}
|
||||
- {name: random_seed, type: Integer, default: '0', optional: true}
|
||||
outputs:
|
||||
- {name: trained_model, type: PyTorchScriptModule}
|
||||
implementation:
|
||||
container:
|
||||
image: pytorch/pytorch:1.7.1-cuda11.0-cudnn8-runtime
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- (PIP_DISABLE_PIP_VERSION_CHECK=1 python3 -m pip install --quiet --no-warn-script-location
|
||||
'pandas==1.4.3' || PIP_DISABLE_PIP_VERSION_CHECK=1 python3 -m pip install --quiet
|
||||
--no-warn-script-location 'pandas==1.4.3' --user) && "$0" "$@"
|
||||
- sh
|
||||
- -ec
|
||||
- |
|
||||
program_path=$(mktemp)
|
||||
printf "%s" "$0" > "$program_path"
|
||||
python3 -u "$program_path" "$@"
|
||||
- |
|
||||
def _make_parent_dirs_and_return_path(file_path: str):
|
||||
import os
|
||||
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
||||
return file_path
|
||||
|
||||
def train_pytorch_model_from_csv(
|
||||
model_path,
|
||||
training_data_path,
|
||||
trained_model_path,
|
||||
label_column_name,
|
||||
loss_function_name = 'mse_loss',
|
||||
number_of_epochs = 1,
|
||||
learning_rate = 0.1,
|
||||
optimizer_name = 'Adadelta',
|
||||
optimizer_parameters = None,
|
||||
batch_size = 32,
|
||||
batch_log_interval = 100,
|
||||
random_seed = 0,
|
||||
):
|
||||
'''Trains PyTorch model'''
|
||||
import pandas
|
||||
import torch
|
||||
|
||||
torch.manual_seed(random_seed)
|
||||
|
||||
use_cuda = torch.cuda.is_available()
|
||||
device = torch.device("cuda" if use_cuda else "cpu")
|
||||
|
||||
model = torch.jit.load(model_path)
|
||||
model.to(device)
|
||||
model.train()
|
||||
|
||||
optimizer_class = getattr(torch.optim, optimizer_name, None)
|
||||
if not optimizer_class:
|
||||
raise ValueError(f'Optimizer "{optimizer_name}" was not found.')
|
||||
|
||||
optimizer_parameters = optimizer_parameters or {}
|
||||
optimizer_parameters['lr'] = learning_rate
|
||||
optimizer = optimizer_class(model.parameters(), **optimizer_parameters)
|
||||
|
||||
loss_function = getattr(torch, loss_function_name, None) or getattr(torch.nn, loss_function_name, None) or getattr(torch.nn.functional, loss_function_name, None)
|
||||
if not loss_function:
|
||||
raise ValueError(f'Loss function "{loss_function_name}" was not found.')
|
||||
|
||||
class CsvDataset(torch.utils.data.Dataset):
|
||||
|
||||
def __init__(self, file_path, label_column_name, drop_nan_columns_or_rows = 'columns'):
|
||||
dataframe = pandas.read_csv(file_path).convert_dtypes()
|
||||
# Preventing error: default_collate: batch must contain tensors, numpy arrays, numbers, dicts or lists; found object
|
||||
if drop_nan_columns_or_rows == 'columns':
|
||||
non_nan_data = dataframe.dropna(axis='columns')
|
||||
removed_columns = set(dataframe.columns) - set(non_nan_data.columns)
|
||||
if removed_columns:
|
||||
print('Skipping columns with NaNs: ' + str(removed_columns))
|
||||
dataframe = non_nan_data
|
||||
if drop_nan_columns_or_rows == 'rows':
|
||||
non_nan_data = dataframe.dropna(axis='index')
|
||||
number_of_removed_rows = len(dataframe) - len(non_nan_data)
|
||||
if number_of_removed_rows:
|
||||
print(f'Skipped {number_of_removed_rows} rows with NaNs.')
|
||||
dataframe = non_nan_data
|
||||
numerical_data = dataframe.select_dtypes(include='number')
|
||||
non_numerical_data = dataframe.select_dtypes(exclude='number')
|
||||
if not non_numerical_data.empty:
|
||||
print('Skipping non-number columns:')
|
||||
print(non_numerical_data.dtypes)
|
||||
self._dataframe = dataframe
|
||||
self.labels = numerical_data[[label_column_name]]
|
||||
self.features = numerical_data.drop(columns=[label_column_name])
|
||||
|
||||
def __len__(self):
|
||||
return len(self._dataframe)
|
||||
|
||||
def __getitem__(self, index):
|
||||
return [self.features.loc[index].to_numpy(dtype='float32'), self.labels.loc[index].to_numpy(dtype='float32')]
|
||||
|
||||
dataset = CsvDataset(
|
||||
file_path=training_data_path,
|
||||
label_column_name=label_column_name,
|
||||
)
|
||||
train_loader = torch.utils.data.DataLoader(
|
||||
dataset=dataset,
|
||||
batch_size=batch_size,
|
||||
shuffle=True,
|
||||
)
|
||||
|
||||
last_full_batch_loss = None
|
||||
for epoch in range(1, number_of_epochs + 1):
|
||||
for batch_idx, (data, target) in enumerate(train_loader):
|
||||
data, target = data.to(device), target.to(device)
|
||||
optimizer.zero_grad()
|
||||
output = model(data)
|
||||
loss = loss_function(output, target)
|
||||
loss.backward()
|
||||
optimizer.step()
|
||||
if len(data) == batch_size:
|
||||
last_full_batch_loss = loss.item()
|
||||
if batch_idx % batch_log_interval == 0:
|
||||
print('Train Epoch: {} [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
|
||||
epoch, batch_idx * len(data), len(train_loader.dataset),
|
||||
100. * batch_idx / len(train_loader), loss.item()))
|
||||
print(f'Training epoch {epoch} completed. Last full batch loss: {last_full_batch_loss:.6f}')
|
||||
|
||||
# print(optimizer.state_dict())
|
||||
model.save(trained_model_path)
|
||||
|
||||
import json
|
||||
import argparse
|
||||
_parser = argparse.ArgumentParser(prog='Train pytorch model from csv', description='Trains PyTorch model')
|
||||
_parser.add_argument("--model", dest="model_path", type=str, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--training-data", dest="training_data_path", type=str, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--label-column-name", dest="label_column_name", type=str, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--loss-function-name", dest="loss_function_name", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--number-of-epochs", dest="number_of_epochs", type=int, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--learning-rate", dest="learning_rate", type=float, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--optimizer-name", dest="optimizer_name", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--optimizer-parameters", dest="optimizer_parameters", type=json.loads, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--batch-size", dest="batch_size", type=int, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--batch-log-interval", dest="batch_log_interval", type=int, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--random-seed", dest="random_seed", type=int, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--trained-model", dest="trained_model_path", type=_make_parent_dirs_and_return_path, required=True, default=argparse.SUPPRESS)
|
||||
_parsed_args = vars(_parser.parse_args())
|
||||
|
||||
_outputs = train_pytorch_model_from_csv(**_parsed_args)
|
||||
args:
|
||||
- --model
|
||||
- {inputPath: model}
|
||||
- --training-data
|
||||
- {inputPath: training_data}
|
||||
- --label-column-name
|
||||
- {inputValue: label_column_name}
|
||||
- if:
|
||||
cond: {isPresent: loss_function_name}
|
||||
then:
|
||||
- --loss-function-name
|
||||
- {inputValue: loss_function_name}
|
||||
- if:
|
||||
cond: {isPresent: number_of_epochs}
|
||||
then:
|
||||
- --number-of-epochs
|
||||
- {inputValue: number_of_epochs}
|
||||
- if:
|
||||
cond: {isPresent: learning_rate}
|
||||
then:
|
||||
- --learning-rate
|
||||
- {inputValue: learning_rate}
|
||||
- if:
|
||||
cond: {isPresent: optimizer_name}
|
||||
then:
|
||||
- --optimizer-name
|
||||
- {inputValue: optimizer_name}
|
||||
- if:
|
||||
cond: {isPresent: optimizer_parameters}
|
||||
then:
|
||||
- --optimizer-parameters
|
||||
- {inputValue: optimizer_parameters}
|
||||
- if:
|
||||
cond: {isPresent: batch_size}
|
||||
then:
|
||||
- --batch-size
|
||||
- {inputValue: batch_size}
|
||||
- if:
|
||||
cond: {isPresent: batch_log_interval}
|
||||
then:
|
||||
- --batch-log-interval
|
||||
- {inputValue: batch_log_interval}
|
||||
- if:
|
||||
cond: {isPresent: random_seed}
|
||||
then:
|
||||
- --random-seed
|
||||
- {inputValue: random_seed}
|
||||
- --trained-model
|
||||
- {outputPath: trained_model}
|
||||
@@ -0,0 +1,110 @@
|
||||
name: Xgboost predict on CSV
|
||||
description: Makes predictions using a trained XGBoost model.
|
||||
metadata:
|
||||
annotations: {author: Alexey Volkov <alexey.volkov@ark-kun.com>, canonical_location: 'https://raw.githubusercontent.com/Ark-kun/pipeline_components/master/components/XGBoost/Predict/component.yaml'}
|
||||
inputs:
|
||||
- {name: data, type: CSV, description: Feature data in Apache Parquet format.}
|
||||
- {name: model, type: XGBoostModel, description: Trained model in binary XGBoost format.}
|
||||
- {name: label_column_name, type: String, description: Optional. Name of the column
|
||||
containing the label data that is excluded during the prediction., optional: true}
|
||||
outputs:
|
||||
- {name: predictions, description: Model predictions.}
|
||||
implementation:
|
||||
container:
|
||||
image: python:3.10
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- (PIP_DISABLE_PIP_VERSION_CHECK=1 python3 -m pip install --quiet --no-warn-script-location
|
||||
'xgboost==1.6.1' 'pandas==1.4.3' || PIP_DISABLE_PIP_VERSION_CHECK=1 python3
|
||||
-m pip install --quiet --no-warn-script-location 'xgboost==1.6.1' 'pandas==1.4.3'
|
||||
--user) && "$0" "$@"
|
||||
- sh
|
||||
- -ec
|
||||
- |
|
||||
program_path=$(mktemp)
|
||||
printf "%s" "$0" > "$program_path"
|
||||
python3 -u "$program_path" "$@"
|
||||
- |
|
||||
def _make_parent_dirs_and_return_path(file_path: str):
|
||||
import os
|
||||
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
||||
return file_path
|
||||
|
||||
def xgboost_predict_on_CSV(
|
||||
data_path,
|
||||
model_path,
|
||||
predictions_path,
|
||||
label_column_name = None,
|
||||
):
|
||||
"""Makes predictions using a trained XGBoost model.
|
||||
|
||||
Args:
|
||||
data_path: Feature data in Apache Parquet format.
|
||||
model_path: Trained model in binary XGBoost format.
|
||||
predictions_path: Model predictions.
|
||||
label_column_name: Optional. Name of the column containing the label data that is excluded during the prediction.
|
||||
|
||||
Annotations:
|
||||
author: Alexey Volkov <alexey.volkov@ark-kun.com>
|
||||
"""
|
||||
from pathlib import Path
|
||||
|
||||
import numpy
|
||||
import pandas
|
||||
import xgboost
|
||||
|
||||
df = pandas.read_csv(
|
||||
data_path,
|
||||
).convert_dtypes()
|
||||
print("Evaluation data information:")
|
||||
df.info(verbose=True)
|
||||
# Converting column types that XGBoost does not support
|
||||
for column_name, dtype in df.dtypes.items():
|
||||
if dtype in ["string", "object"]:
|
||||
print(f"Treating the {dtype.name} column '{column_name}' as categorical.")
|
||||
df[column_name] = df[column_name].astype("category")
|
||||
print(f"Inferred {len(df[column_name].cat.categories)} categories for the '{column_name}' column.")
|
||||
# Working around the XGBoost issue with nullable floats: https://github.com/dmlc/xgboost/issues/8213
|
||||
if pandas.api.types.is_float_dtype(dtype):
|
||||
# Converting from "Float64" to "float64"
|
||||
df[column_name] = df[column_name].astype(dtype.name.lower())
|
||||
print("Final evaluation data information:")
|
||||
df.info(verbose=True)
|
||||
|
||||
if label_column_name is not None:
|
||||
df = df.drop(columns=[label_column_name])
|
||||
|
||||
testing_data = xgboost.DMatrix(
|
||||
data=df,
|
||||
enable_categorical=True,
|
||||
)
|
||||
|
||||
model = xgboost.Booster(model_file=model_path)
|
||||
|
||||
predictions = model.predict(testing_data)
|
||||
|
||||
Path(predictions_path).parent.mkdir(parents=True, exist_ok=True)
|
||||
numpy.savetxt(predictions_path, predictions)
|
||||
|
||||
import argparse
|
||||
_parser = argparse.ArgumentParser(prog='Xgboost predict on CSV', description='Makes predictions using a trained XGBoost model.')
|
||||
_parser.add_argument("--data", dest="data_path", type=str, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--model", dest="model_path", type=str, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--label-column-name", dest="label_column_name", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--predictions", dest="predictions_path", type=_make_parent_dirs_and_return_path, required=True, default=argparse.SUPPRESS)
|
||||
_parsed_args = vars(_parser.parse_args())
|
||||
|
||||
_outputs = xgboost_predict_on_CSV(**_parsed_args)
|
||||
args:
|
||||
- --data
|
||||
- {inputPath: data}
|
||||
- --model
|
||||
- {inputPath: model}
|
||||
- if:
|
||||
cond: {isPresent: label_column_name}
|
||||
then:
|
||||
- --label-column-name
|
||||
- {inputValue: label_column_name}
|
||||
- --predictions
|
||||
- {outputPath: predictions}
|
||||
@@ -0,0 +1,241 @@
|
||||
name: Train XGBoost model on CSV
|
||||
description: Trains an XGBoost model.
|
||||
metadata:
|
||||
annotations: {author: Alexey Volkov <alexey.volkov@ark-kun.com>, canonical_location: 'https://raw.githubusercontent.com/Ark-kun/pipeline_components/master/components/XGBoost/Train/component.yaml'}
|
||||
inputs:
|
||||
- {name: training_data, type: CSV, description: Training data in CSV format.}
|
||||
- {name: label_column_name, type: String, description: Name of the column containing
|
||||
the label data.}
|
||||
- {name: starting_model, type: XGBoostModel, description: Existing trained model to
|
||||
start from (in the binary XGBoost format)., optional: true}
|
||||
- {name: num_iterations, type: Integer, description: Number of boosting iterations.,
|
||||
default: '10', optional: true}
|
||||
- name: objective
|
||||
type: String
|
||||
description: |-
|
||||
The learning task and the corresponding learning objective.
|
||||
See https://xgboost.readthedocs.io/en/latest/parameter.html#learning-task-parameters
|
||||
The most common values are:
|
||||
"reg:squarederror" - Regression with squared loss (default).
|
||||
"reg:logistic" - Logistic regression.
|
||||
"binary:logistic" - Logistic regression for binary classification, output probability.
|
||||
"binary:logitraw" - Logistic regression for binary classification, output score before logistic transformation
|
||||
"rank:pairwise" - Use LambdaMART to perform pairwise ranking where the pairwise loss is minimized
|
||||
"rank:ndcg" - Use LambdaMART to perform list-wise ranking where Normalized Discounted Cumulative Gain (NDCG) is maximized
|
||||
default: reg:squarederror
|
||||
optional: true
|
||||
- {name: booster, type: String, description: 'The booster to use. Can be `gbtree`,
|
||||
`gblinear` or `dart`; `gbtree` and `dart` use tree based models while `gblinear`
|
||||
uses linear functions.', default: gbtree, optional: true}
|
||||
- {name: learning_rate, type: Float, description: 'Step size shrinkage used in update
|
||||
to prevents overfitting. Range: [0,1].', default: '0.3', optional: true}
|
||||
- name: min_split_loss
|
||||
type: Float
|
||||
description: |-
|
||||
Minimum loss reduction required to make a further partition on a leaf node of the tree.
|
||||
The larger `min_split_loss` is, the more conservative the algorithm will be. Range: [0,Inf].
|
||||
default: '0'
|
||||
optional: true
|
||||
- name: max_depth
|
||||
type: Integer
|
||||
description: |-
|
||||
Maximum depth of a tree. Increasing this value will make the model more complex and more likely to overfit.
|
||||
0 indicates no limit on depth. Range: [0,Inf].
|
||||
default: '6'
|
||||
optional: true
|
||||
- {name: booster_params, type: JsonObject, description: 'Parameters for the booster.
|
||||
See https://xgboost.readthedocs.io/en/latest/parameter.html', optional: true}
|
||||
outputs:
|
||||
- {name: model, type: XGBoostModel, description: Trained model in the binary XGBoost
|
||||
format.}
|
||||
- {name: model_config, type: XGBoostModelConfig, description: The internal parameter
|
||||
configuration of Booster as a JSON string.}
|
||||
implementation:
|
||||
container:
|
||||
image: python:3.10
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- (PIP_DISABLE_PIP_VERSION_CHECK=1 python3 -m pip install --quiet --no-warn-script-location
|
||||
'xgboost==1.6.1' 'pandas==1.4.3' || PIP_DISABLE_PIP_VERSION_CHECK=1 python3
|
||||
-m pip install --quiet --no-warn-script-location 'xgboost==1.6.1' 'pandas==1.4.3'
|
||||
--user) && "$0" "$@"
|
||||
- sh
|
||||
- -ec
|
||||
- |
|
||||
program_path=$(mktemp)
|
||||
printf "%s" "$0" > "$program_path"
|
||||
python3 -u "$program_path" "$@"
|
||||
- |
|
||||
def _make_parent_dirs_and_return_path(file_path: str):
|
||||
import os
|
||||
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
||||
return file_path
|
||||
|
||||
def train_XGBoost_model_on_CSV(
|
||||
training_data_path,
|
||||
model_path,
|
||||
model_config_path,
|
||||
label_column_name,
|
||||
starting_model_path = None,
|
||||
num_iterations = 10,
|
||||
# Booster parameters
|
||||
objective = "reg:squarederror",
|
||||
booster = "gbtree",
|
||||
learning_rate = 0.3,
|
||||
min_split_loss = 0,
|
||||
max_depth = 6,
|
||||
booster_params = None,
|
||||
):
|
||||
"""Trains an XGBoost model.
|
||||
|
||||
Args:
|
||||
training_data_path: Training data in CSV format.
|
||||
model_path: Trained model in the binary XGBoost format.
|
||||
model_config_path: The internal parameter configuration of Booster as a JSON string.
|
||||
starting_model_path: Existing trained model to start from (in the binary XGBoost format).
|
||||
label_column_name: Name of the column containing the label data.
|
||||
num_iterations: Number of boosting iterations.
|
||||
booster_params: Parameters for the booster. See https://xgboost.readthedocs.io/en/latest/parameter.html
|
||||
objective: The learning task and the corresponding learning objective.
|
||||
See https://xgboost.readthedocs.io/en/latest/parameter.html#learning-task-parameters
|
||||
The most common values are:
|
||||
"reg:squarederror" - Regression with squared loss (default).
|
||||
"reg:logistic" - Logistic regression.
|
||||
"binary:logistic" - Logistic regression for binary classification, output probability.
|
||||
"binary:logitraw" - Logistic regression for binary classification, output score before logistic transformation
|
||||
"rank:pairwise" - Use LambdaMART to perform pairwise ranking where the pairwise loss is minimized
|
||||
"rank:ndcg" - Use LambdaMART to perform list-wise ranking where Normalized Discounted Cumulative Gain (NDCG) is maximized
|
||||
booster: The booster to use. Can be `gbtree`, `gblinear` or `dart`; `gbtree` and `dart` use tree based models while `gblinear` uses linear functions.
|
||||
learning_rate: Step size shrinkage used in update to prevents overfitting. Range: [0,1].
|
||||
min_split_loss: Minimum loss reduction required to make a further partition on a leaf node of the tree.
|
||||
The larger `min_split_loss` is, the more conservative the algorithm will be. Range: [0,Inf].
|
||||
max_depth: Maximum depth of a tree. Increasing this value will make the model more complex and more likely to overfit.
|
||||
0 indicates no limit on depth. Range: [0,Inf].
|
||||
|
||||
Annotations:
|
||||
author: Alexey Volkov <alexey.volkov@ark-kun.com>
|
||||
"""
|
||||
import pandas
|
||||
import xgboost
|
||||
|
||||
df = pandas.read_csv(
|
||||
training_data_path,
|
||||
).convert_dtypes()
|
||||
print("Training data information:")
|
||||
df.info(verbose=True)
|
||||
# Converting column types that XGBoost does not support
|
||||
for column_name, dtype in df.dtypes.items():
|
||||
if dtype in ["string", "object"]:
|
||||
print(f"Treating the {dtype.name} column '{column_name}' as categorical.")
|
||||
df[column_name] = df[column_name].astype("category")
|
||||
print(f"Inferred {len(df[column_name].cat.categories)} categories for the '{column_name}' column.")
|
||||
# Working around the XGBoost issue with nullable floats: https://github.com/dmlc/xgboost/issues/8213
|
||||
if pandas.api.types.is_float_dtype(dtype):
|
||||
# Converting from "Float64" to "float64"
|
||||
df[column_name] = df[column_name].astype(dtype.name.lower())
|
||||
print()
|
||||
print("Final training data information:")
|
||||
df.info(verbose=True)
|
||||
|
||||
training_data = xgboost.DMatrix(
|
||||
data=df.drop(columns=[label_column_name]),
|
||||
label=df[[label_column_name]],
|
||||
enable_categorical=True,
|
||||
)
|
||||
|
||||
booster_params = booster_params or {}
|
||||
booster_params.setdefault("objective", objective)
|
||||
booster_params.setdefault("booster", booster)
|
||||
booster_params.setdefault("learning_rate", learning_rate)
|
||||
booster_params.setdefault("min_split_loss", min_split_loss)
|
||||
booster_params.setdefault("max_depth", max_depth)
|
||||
|
||||
starting_model = None
|
||||
if starting_model_path:
|
||||
starting_model = xgboost.Booster(model_file=starting_model_path)
|
||||
|
||||
print()
|
||||
print("Training the model:")
|
||||
model = xgboost.train(
|
||||
params=booster_params,
|
||||
dtrain=training_data,
|
||||
num_boost_round=num_iterations,
|
||||
xgb_model=starting_model,
|
||||
evals=[(training_data, "training_data")],
|
||||
)
|
||||
|
||||
# Saving the model in binary format
|
||||
model.save_model(model_path)
|
||||
|
||||
model_config_str = model.save_config()
|
||||
with open(model_config_path, "w") as model_config_file:
|
||||
model_config_file.write(model_config_str)
|
||||
|
||||
import json
|
||||
import argparse
|
||||
_parser = argparse.ArgumentParser(prog='Train XGBoost model on CSV', description='Trains an XGBoost model.')
|
||||
_parser.add_argument("--training-data", dest="training_data_path", type=str, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--label-column-name", dest="label_column_name", type=str, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--starting-model", dest="starting_model_path", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--num-iterations", dest="num_iterations", type=int, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--objective", dest="objective", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--booster", dest="booster", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--learning-rate", dest="learning_rate", type=float, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--min-split-loss", dest="min_split_loss", type=float, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--max-depth", dest="max_depth", type=int, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--booster-params", dest="booster_params", type=json.loads, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--model", dest="model_path", type=_make_parent_dirs_and_return_path, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--model-config", dest="model_config_path", type=_make_parent_dirs_and_return_path, required=True, default=argparse.SUPPRESS)
|
||||
_parsed_args = vars(_parser.parse_args())
|
||||
|
||||
_outputs = train_XGBoost_model_on_CSV(**_parsed_args)
|
||||
args:
|
||||
- --training-data
|
||||
- {inputPath: training_data}
|
||||
- --label-column-name
|
||||
- {inputValue: label_column_name}
|
||||
- if:
|
||||
cond: {isPresent: starting_model}
|
||||
then:
|
||||
- --starting-model
|
||||
- {inputPath: starting_model}
|
||||
- if:
|
||||
cond: {isPresent: num_iterations}
|
||||
then:
|
||||
- --num-iterations
|
||||
- {inputValue: num_iterations}
|
||||
- if:
|
||||
cond: {isPresent: objective}
|
||||
then:
|
||||
- --objective
|
||||
- {inputValue: objective}
|
||||
- if:
|
||||
cond: {isPresent: booster}
|
||||
then:
|
||||
- --booster
|
||||
- {inputValue: booster}
|
||||
- if:
|
||||
cond: {isPresent: learning_rate}
|
||||
then:
|
||||
- --learning-rate
|
||||
- {inputValue: learning_rate}
|
||||
- if:
|
||||
cond: {isPresent: min_split_loss}
|
||||
then:
|
||||
- --min-split-loss
|
||||
- {inputValue: min_split_loss}
|
||||
- if:
|
||||
cond: {isPresent: max_depth}
|
||||
then:
|
||||
- --max-depth
|
||||
- {inputValue: max_depth}
|
||||
- if:
|
||||
cond: {isPresent: booster_params}
|
||||
then:
|
||||
- --booster-params
|
||||
- {inputValue: booster_params}
|
||||
- --model
|
||||
- {outputPath: model}
|
||||
- --model-config
|
||||
- {outputPath: model_config}
|
||||
+204
@@ -0,0 +1,204 @@
|
||||
name: Split rows into subsets
|
||||
description: Splits the data table according to the split fractions.
|
||||
metadata:
|
||||
annotations: {author: Alexey Volkov <alexey.volkov@ark-kun.com>, canonical_location: 'https://raw.githubusercontent.com/Ark-kun/pipeline_components/master/components/dataset_manipulation/Split_rows_into_subsets/in_CSV/component.yaml'}
|
||||
inputs:
|
||||
- {name: table, type: CSV}
|
||||
- {name: fraction_1, type: Float, description: 'The proportion of the lines to put
|
||||
into the 1st split. Range: [0, 1]'}
|
||||
- name: fraction_2
|
||||
type: Float
|
||||
description: |-
|
||||
The proportion of the lines to put into the 2nd split. Range: [0, 1]
|
||||
If fraction_2 is not specified, then fraction_2 = 1 - fraction_1.
|
||||
The remaining lines go to the 3rd split (if any).
|
||||
optional: true
|
||||
- {name: random_seed, type: Integer, default: '0', optional: true}
|
||||
outputs:
|
||||
- {name: split_1, type: CSV}
|
||||
- {name: split_2, type: CSV}
|
||||
- {name: split_3, type: CSV}
|
||||
- {name: split_1_count, type: Integer}
|
||||
- {name: split_2_count, type: Integer}
|
||||
- {name: split_3_count, type: Integer}
|
||||
implementation:
|
||||
container:
|
||||
image: python:3.9
|
||||
command:
|
||||
- sh
|
||||
- -ec
|
||||
- |
|
||||
program_path=$(mktemp)
|
||||
printf "%s" "$0" > "$program_path"
|
||||
python3 -u "$program_path" "$@"
|
||||
- |
|
||||
def _make_parent_dirs_and_return_path(file_path: str):
|
||||
import os
|
||||
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
||||
return file_path
|
||||
|
||||
def split_rows_into_subsets(
|
||||
table_path,
|
||||
split_1_path,
|
||||
split_2_path,
|
||||
split_3_path,
|
||||
fraction_1,
|
||||
fraction_2 = None,
|
||||
random_seed = 0,
|
||||
):
|
||||
"""Splits the data table according to the split fractions.
|
||||
|
||||
Args:
|
||||
fraction_1: The proportion of the lines to put into the 1st split. Range: [0, 1]
|
||||
fraction_2: The proportion of the lines to put into the 2nd split. Range: [0, 1]
|
||||
If fraction_2 is not specified, then fraction_2 = 1 - fraction_1.
|
||||
The remaining lines go to the 3rd split (if any).
|
||||
"""
|
||||
import random
|
||||
|
||||
random.seed(random_seed)
|
||||
|
||||
SHUFFLE_BUFFER_SIZE = 10000
|
||||
|
||||
num_splits = 3
|
||||
|
||||
if fraction_1 < 0 or fraction_1 > 1:
|
||||
raise ValueError("fraction_1 must be in between 0 and 1.")
|
||||
|
||||
if fraction_2 is None:
|
||||
fraction_2 = 1 - fraction_1
|
||||
if fraction_2 < 0 or fraction_2 > 1:
|
||||
raise ValueError("fraction_2 must be in between 0 and 1.")
|
||||
|
||||
fraction_3 = 1 - fraction_1 - fraction_2
|
||||
|
||||
fractions = [
|
||||
fraction_1,
|
||||
fraction_2,
|
||||
fraction_3,
|
||||
]
|
||||
|
||||
assert sum(fractions) == 1
|
||||
|
||||
written_line_counts = [0] * num_splits
|
||||
|
||||
output_files = [
|
||||
open(split_1_path, "wb"),
|
||||
open(split_2_path, "wb"),
|
||||
open(split_3_path, "wb"),
|
||||
]
|
||||
|
||||
with open(table_path, "rb") as input_file:
|
||||
# Writing the headers
|
||||
header_line = input_file.readline()
|
||||
for output_file in output_files:
|
||||
output_file.write(header_line)
|
||||
|
||||
while True:
|
||||
line_buffer = []
|
||||
for i in range(SHUFFLE_BUFFER_SIZE):
|
||||
line = input_file.readline()
|
||||
if not line:
|
||||
break
|
||||
line_buffer.append(line)
|
||||
|
||||
# We need to exactly partition the lines between the output files
|
||||
# To overcome possible systematic bias, we could calculate the total numbers
|
||||
# of lines written to each file and take that into account.
|
||||
num_read_lines = len(line_buffer)
|
||||
number_of_lines_for_files = [0] * num_splits
|
||||
# List that will have the index of the destination file for each line
|
||||
file_index_for_line = []
|
||||
remaining_lines = num_read_lines
|
||||
remaining_fraction = 1
|
||||
for i in range(num_splits):
|
||||
number_of_lines_for_file = (
|
||||
round(remaining_lines * (fractions[i] / remaining_fraction))
|
||||
if remaining_fraction > 0
|
||||
else 0
|
||||
)
|
||||
number_of_lines_for_files[i] = number_of_lines_for_file
|
||||
remaining_lines -= number_of_lines_for_file
|
||||
remaining_fraction -= fractions[i]
|
||||
file_index_for_line.extend([i] * number_of_lines_for_file)
|
||||
|
||||
assert remaining_lines == 0, f"{remaining_lines}"
|
||||
assert len(file_index_for_line) == num_read_lines
|
||||
|
||||
random.shuffle(file_index_for_line)
|
||||
|
||||
for i in range(num_read_lines):
|
||||
output_files[file_index_for_line[i]].write(line_buffer[i])
|
||||
written_line_counts[file_index_for_line[i]] += 1
|
||||
|
||||
# Exit if the file ended before we were able to fully fill the buffer
|
||||
if len(line_buffer) != SHUFFLE_BUFFER_SIZE:
|
||||
break
|
||||
|
||||
for output_file in output_files:
|
||||
output_file.close()
|
||||
|
||||
return written_line_counts
|
||||
|
||||
def _serialize_int(int_value: int) -> str:
|
||||
if isinstance(int_value, str):
|
||||
return int_value
|
||||
if not isinstance(int_value, int):
|
||||
raise TypeError('Value "{}" has type "{}" instead of int.'.format(str(int_value), str(type(int_value))))
|
||||
return str(int_value)
|
||||
|
||||
import argparse
|
||||
_parser = argparse.ArgumentParser(prog='Split rows into subsets', description='Splits the data table according to the split fractions.')
|
||||
_parser.add_argument("--table", dest="table_path", type=str, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--fraction-1", dest="fraction_1", type=float, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--fraction-2", dest="fraction_2", type=float, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--random-seed", dest="random_seed", type=int, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--split-1", dest="split_1_path", type=_make_parent_dirs_and_return_path, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--split-2", dest="split_2_path", type=_make_parent_dirs_and_return_path, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--split-3", dest="split_3_path", type=_make_parent_dirs_and_return_path, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("----output-paths", dest="_output_paths", type=str, nargs=3)
|
||||
_parsed_args = vars(_parser.parse_args())
|
||||
_output_files = _parsed_args.pop("_output_paths", [])
|
||||
|
||||
_outputs = split_rows_into_subsets(**_parsed_args)
|
||||
|
||||
_output_serializers = [
|
||||
_serialize_int,
|
||||
_serialize_int,
|
||||
_serialize_int,
|
||||
|
||||
]
|
||||
|
||||
import os
|
||||
for idx, output_file in enumerate(_output_files):
|
||||
try:
|
||||
os.makedirs(os.path.dirname(output_file))
|
||||
except OSError:
|
||||
pass
|
||||
with open(output_file, 'w') as f:
|
||||
f.write(_output_serializers[idx](_outputs[idx]))
|
||||
args:
|
||||
- --table
|
||||
- {inputPath: table}
|
||||
- --fraction-1
|
||||
- {inputValue: fraction_1}
|
||||
- if:
|
||||
cond: {isPresent: fraction_2}
|
||||
then:
|
||||
- --fraction-2
|
||||
- {inputValue: fraction_2}
|
||||
- if:
|
||||
cond: {isPresent: random_seed}
|
||||
then:
|
||||
- --random-seed
|
||||
- {inputValue: random_seed}
|
||||
- --split-1
|
||||
- {outputPath: split_1}
|
||||
- --split-2
|
||||
- {outputPath: split_2}
|
||||
- --split-3
|
||||
- {outputPath: split_3}
|
||||
- '----output-paths'
|
||||
- {outputPath: split_1_count}
|
||||
- {outputPath: split_2_count}
|
||||
- {outputPath: split_3_count}
|
||||
+241
@@ -0,0 +1,241 @@
|
||||
name: Deploy model to endpoint for Google Cloud Vertex AI Model
|
||||
description: Deploys Google Cloud Vertex AI Model to a Google Cloud Vertex AI Endpoint.
|
||||
metadata:
|
||||
annotations: {author: Alexey Volkov <alexey.volkov@ark-kun.com>, canonical_location: 'https://raw.githubusercontent.com/Ark-kun/pipeline_components/KFPv2_hell/components/google-cloud/Vertex_AI/Models/Deploy_to_endpoint/workaround_for_buggy_KFPv2_compiler/component.yaml'}
|
||||
inputs:
|
||||
- {name: model_name, type: String, description: Full resource name of a Google Cloud
|
||||
Vertex AI Model}
|
||||
- name: endpoint_name
|
||||
type: String
|
||||
description: |-
|
||||
Optional. Full name of Google Cloud Vertex Endpoint. A new
|
||||
endpoint is created if the name is not passed.
|
||||
optional: true
|
||||
- name: machine_type
|
||||
type: String
|
||||
description: |-
|
||||
The type of the machine. See the [list of machine types
|
||||
supported for prediction
|
||||
](https://cloud.google.com/vertex-ai/docs/predictions/configure-compute#machine-types).
|
||||
Defaults to "n1-standard-2"
|
||||
default: n1-standard-2
|
||||
optional: true
|
||||
- name: min_replica_count
|
||||
type: Integer
|
||||
description: |-
|
||||
Optional. The minimum number of machine replicas this deployed
|
||||
model will be always deployed on. If traffic against it increases,
|
||||
it may dynamically be deployed onto more replicas, and as traffic
|
||||
decreases, some of these extra replicas may be freed.
|
||||
default: '1'
|
||||
optional: true
|
||||
- name: max_replica_count
|
||||
type: Integer
|
||||
description: |-
|
||||
Optional. The maximum number of replicas this deployed model may
|
||||
be deployed on when the traffic against it increases. If requested
|
||||
value is too large, the deployment will error, but if deployment
|
||||
succeeds then the ability to scale the model to that many replicas
|
||||
is guaranteed (barring service outages). If traffic against the
|
||||
deployed model increases beyond what its replicas at maximum may
|
||||
handle, a portion of the traffic will be dropped. If this value
|
||||
is not provided, the smaller value of min_replica_count or 1 will
|
||||
be used.
|
||||
default: '1'
|
||||
optional: true
|
||||
- name: accelerator_type
|
||||
type: String
|
||||
description: |-
|
||||
Optional. Hardware accelerator type. Must also set accelerator_count if used.
|
||||
One of ACCELERATOR_TYPE_UNSPECIFIED, NVIDIA_TESLA_K80, NVIDIA_TESLA_P100,
|
||||
NVIDIA_TESLA_V100, NVIDIA_TESLA_P4, NVIDIA_TESLA_T4
|
||||
optional: true
|
||||
- {name: accelerator_count, type: Integer, description: Optional. The number of accelerators
|
||||
to attach to a worker replica., optional: true}
|
||||
outputs:
|
||||
- {name: endpoint_name, type: String}
|
||||
- {name: endpoint_dict, type: JsonObject}
|
||||
implementation:
|
||||
container:
|
||||
image: python:3.9
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- (PIP_DISABLE_PIP_VERSION_CHECK=1 python3 -m pip install --quiet --no-warn-script-location
|
||||
'google-cloud-aiplatform==1.7.0' || PIP_DISABLE_PIP_VERSION_CHECK=1 python3
|
||||
-m pip install --quiet --no-warn-script-location 'google-cloud-aiplatform==1.7.0'
|
||||
--user) && "$0" "$@"
|
||||
- sh
|
||||
- -ec
|
||||
- |
|
||||
program_path=$(mktemp)
|
||||
printf "%s" "$0" > "$program_path"
|
||||
python3 -u "$program_path" "$@"
|
||||
- |
|
||||
def deploy_model_to_endpoint_for_Google_Cloud_Vertex_AI_Model(
|
||||
model_name,
|
||||
endpoint_name = None,
|
||||
machine_type = "n1-standard-2",
|
||||
min_replica_count = 1,
|
||||
max_replica_count = 1,
|
||||
accelerator_type = None,
|
||||
accelerator_count = None,
|
||||
#
|
||||
# Uncomment when anyone requests these:
|
||||
# deployed_model_display_name: str = None,
|
||||
# traffic_percentage: int = 0,
|
||||
# traffic_split: dict = None,
|
||||
# service_account: str = None,
|
||||
# explanation_metadata: "google.cloud.aiplatform_v1.types.explanation_metadata.ExplanationMetadata" = None,
|
||||
# explanation_parameters: "google.cloud.aiplatform_v1.types.explanation.ExplanationParameters" = None,
|
||||
#
|
||||
# encryption_spec_key_name: str = None,
|
||||
):
|
||||
"""Deploys Google Cloud Vertex AI Model to a Google Cloud Vertex AI Endpoint.
|
||||
|
||||
Args:
|
||||
model_name: Full resource name of a Google Cloud Vertex AI Model
|
||||
endpoint_name: Optional. Full name of Google Cloud Vertex Endpoint. A new
|
||||
endpoint is created if the name is not passed.
|
||||
machine_type: The type of the machine. See the [list of machine types
|
||||
supported for prediction
|
||||
](https://cloud.google.com/vertex-ai/docs/predictions/configure-compute#machine-types).
|
||||
Defaults to "n1-standard-2"
|
||||
min_replica_count (int):
|
||||
Optional. The minimum number of machine replicas this deployed
|
||||
model will be always deployed on. If traffic against it increases,
|
||||
it may dynamically be deployed onto more replicas, and as traffic
|
||||
decreases, some of these extra replicas may be freed.
|
||||
max_replica_count (int):
|
||||
Optional. The maximum number of replicas this deployed model may
|
||||
be deployed on when the traffic against it increases. If requested
|
||||
value is too large, the deployment will error, but if deployment
|
||||
succeeds then the ability to scale the model to that many replicas
|
||||
is guaranteed (barring service outages). If traffic against the
|
||||
deployed model increases beyond what its replicas at maximum may
|
||||
handle, a portion of the traffic will be dropped. If this value
|
||||
is not provided, the smaller value of min_replica_count or 1 will
|
||||
be used.
|
||||
accelerator_type (str):
|
||||
Optional. Hardware accelerator type. Must also set accelerator_count if used.
|
||||
One of ACCELERATOR_TYPE_UNSPECIFIED, NVIDIA_TESLA_K80, NVIDIA_TESLA_P100,
|
||||
NVIDIA_TESLA_V100, NVIDIA_TESLA_P4, NVIDIA_TESLA_T4
|
||||
accelerator_count (int):
|
||||
Optional. The number of accelerators to attach to a worker replica.
|
||||
"""
|
||||
import json
|
||||
from google.cloud import aiplatform
|
||||
|
||||
model = aiplatform.Model(model_name=model_name)
|
||||
|
||||
if endpoint_name:
|
||||
endpoint = aiplatform.Endpoint(endpoint_name=endpoint_name)
|
||||
else:
|
||||
endpoint_display_name = model.display_name[:118] + "_endpoint"
|
||||
endpoint = aiplatform.Endpoint.create(
|
||||
display_name=endpoint_display_name,
|
||||
project=model.project,
|
||||
location=model.location,
|
||||
# encryption_spec_key_name=encryption_spec_key_name,
|
||||
labels={"component-source": "github-com-ark-kun-pipeline-components"},
|
||||
)
|
||||
|
||||
endpoint = model.deploy(
|
||||
endpoint=endpoint,
|
||||
# deployed_model_display_name=deployed_model_display_name,
|
||||
machine_type=machine_type,
|
||||
min_replica_count=min_replica_count,
|
||||
max_replica_count=max_replica_count,
|
||||
accelerator_type=accelerator_type,
|
||||
accelerator_count=accelerator_count,
|
||||
# service_account=service_account,
|
||||
# explanation_metadata=explanation_metadata,
|
||||
# explanation_parameters=explanation_parameters,
|
||||
# encryption_spec_key_name=encryption_spec_key_name,
|
||||
)
|
||||
|
||||
endpoint_json = json.dumps(endpoint.to_dict(), indent=2)
|
||||
print(endpoint_json)
|
||||
return (endpoint.resource_name, endpoint_json)
|
||||
|
||||
def _serialize_json(obj) -> str:
|
||||
if isinstance(obj, str):
|
||||
return obj
|
||||
import json
|
||||
def default_serializer(obj):
|
||||
if hasattr(obj, 'to_struct'):
|
||||
return obj.to_struct()
|
||||
else:
|
||||
raise TypeError("Object of type '%s' is not JSON serializable and does not have .to_struct() method." % obj.__class__.__name__)
|
||||
return json.dumps(obj, default=default_serializer, sort_keys=True)
|
||||
|
||||
def _serialize_str(str_value: str) -> str:
|
||||
if not isinstance(str_value, str):
|
||||
raise TypeError('Value "{}" has type "{}" instead of str.'.format(str(str_value), str(type(str_value))))
|
||||
return str_value
|
||||
|
||||
import argparse
|
||||
_parser = argparse.ArgumentParser(prog='Deploy model to endpoint for Google Cloud Vertex AI Model', description='Deploys Google Cloud Vertex AI Model to a Google Cloud Vertex AI Endpoint.')
|
||||
_parser.add_argument("--model-name", dest="model_name", type=str, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--endpoint-name", dest="endpoint_name", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--machine-type", dest="machine_type", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--min-replica-count", dest="min_replica_count", type=int, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--max-replica-count", dest="max_replica_count", type=int, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--accelerator-type", dest="accelerator_type", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--accelerator-count", dest="accelerator_count", type=int, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("----output-paths", dest="_output_paths", type=str, nargs=2)
|
||||
_parsed_args = vars(_parser.parse_args())
|
||||
_output_files = _parsed_args.pop("_output_paths", [])
|
||||
|
||||
_outputs = deploy_model_to_endpoint_for_Google_Cloud_Vertex_AI_Model(**_parsed_args)
|
||||
|
||||
_output_serializers = [
|
||||
_serialize_str,
|
||||
_serialize_json,
|
||||
|
||||
]
|
||||
|
||||
import os
|
||||
for idx, output_file in enumerate(_output_files):
|
||||
try:
|
||||
os.makedirs(os.path.dirname(output_file))
|
||||
except OSError:
|
||||
pass
|
||||
with open(output_file, 'w') as f:
|
||||
f.write(_output_serializers[idx](_outputs[idx]))
|
||||
args:
|
||||
- --model-name
|
||||
- {inputValue: model_name}
|
||||
- if:
|
||||
cond: {isPresent: endpoint_name}
|
||||
then:
|
||||
- --endpoint-name
|
||||
- {inputValue: endpoint_name}
|
||||
- if:
|
||||
cond: {isPresent: machine_type}
|
||||
then:
|
||||
- --machine-type
|
||||
- {inputValue: machine_type}
|
||||
- if:
|
||||
cond: {isPresent: min_replica_count}
|
||||
then:
|
||||
- --min-replica-count
|
||||
- {inputValue: min_replica_count}
|
||||
- if:
|
||||
cond: {isPresent: max_replica_count}
|
||||
then:
|
||||
- --max-replica-count
|
||||
- {inputValue: max_replica_count}
|
||||
- if:
|
||||
cond: {isPresent: accelerator_type}
|
||||
then:
|
||||
- --accelerator-type
|
||||
- {inputValue: accelerator_type}
|
||||
- if:
|
||||
cond: {isPresent: accelerator_count}
|
||||
then:
|
||||
- --accelerator-count
|
||||
- {inputValue: accelerator_count}
|
||||
- '----output-paths'
|
||||
- {outputPath: endpoint_name}
|
||||
- {outputPath: endpoint_dict}
|
||||
+297
@@ -0,0 +1,297 @@
|
||||
name: Upload PyTorch model archive to Google Cloud Vertex AI
|
||||
metadata:
|
||||
annotations: {author: Alexey Volkov <alexey.volkov@ark-kun.com>, canonical_location: 'https://raw.githubusercontent.com/Ark-kun/pipeline_components/KFPv2_hell/components/google-cloud/Vertex_AI/Models/Upload_PyTorch_model_archive/workaround_for_buggy_KFPv2_compiler/component.yaml'}
|
||||
inputs:
|
||||
- {name: model_archive, type: PyTorchModelArchive}
|
||||
- {name: torchserve_version, type: String, default: 0.6.0, optional: true}
|
||||
- name: use_gpu
|
||||
type: Boolean
|
||||
default: "False"
|
||||
optional: true
|
||||
- {name: display_name, type: String, optional: true}
|
||||
- {name: description, type: String, optional: true}
|
||||
- {name: project, type: String, optional: true}
|
||||
- {name: location, type: String, optional: true}
|
||||
- {name: labels, type: JsonObject, optional: true}
|
||||
- {name: staging_bucket, type: String, optional: true}
|
||||
outputs:
|
||||
- {name: model_name, type: String}
|
||||
- {name: model_dict, type: JsonObject}
|
||||
implementation:
|
||||
container:
|
||||
image: python:3.9
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- (PIP_DISABLE_PIP_VERSION_CHECK=1 python3 -m pip install --quiet --no-warn-script-location
|
||||
'google-cloud-aiplatform==1.13.1' 'google-cloud-build==3.8.3' || PIP_DISABLE_PIP_VERSION_CHECK=1
|
||||
python3 -m pip install --quiet --no-warn-script-location 'google-cloud-aiplatform==1.13.1'
|
||||
'google-cloud-build==3.8.3' --user) && "$0" "$@"
|
||||
- sh
|
||||
- -ec
|
||||
- |
|
||||
program_path=$(mktemp)
|
||||
printf "%s" "$0" > "$program_path"
|
||||
python3 -u "$program_path" "$@"
|
||||
- |
|
||||
def upload_PyTorch_model_archive_to_Google_Cloud_Vertex_AI(
|
||||
model_archive_path,
|
||||
torchserve_version = "0.6.0",
|
||||
use_gpu = False,
|
||||
|
||||
display_name = None,
|
||||
description = None,
|
||||
|
||||
# Uncomment when anyone requests these:
|
||||
# instance_schema_uri: str = None,
|
||||
# parameters_schema_uri: str = None,
|
||||
# prediction_schema_uri: str = None,
|
||||
# explanation_metadata: "google.cloud.aiplatform_v1.types.explanation_metadata.ExplanationMetadata" = None,
|
||||
# explanation_parameters: "google.cloud.aiplatform_v1.types.explanation.ExplanationParameters" = None,
|
||||
|
||||
project = None,
|
||||
location = None,
|
||||
labels = None,
|
||||
# encryption_spec_key_name: str = None,
|
||||
staging_bucket = None,
|
||||
):
|
||||
import json
|
||||
import os
|
||||
from google.cloud import aiplatform
|
||||
|
||||
if not location:
|
||||
location = os.environ.get("CLOUD_ML_REGION")
|
||||
|
||||
if not labels:
|
||||
labels = {}
|
||||
labels["component-source"] = "github-com-ark-kun-pipeline-components"
|
||||
|
||||
container_image_tag = torchserve_version + "-" + ("gpu" if use_gpu else "cpu")
|
||||
container_image_uri = f"pytorch/torchserve:{container_image_tag}"
|
||||
|
||||
# Vertex Endpoints refuse to support non-Google container registries.
|
||||
# We have to work around this to reduce user frustration
|
||||
# TODO: Remove this code when Vertex Endpoints service starts supporting other container registries.
|
||||
def copy_container_image(
|
||||
src_container_image_uri,
|
||||
dst_container_image_uri,
|
||||
project_id,
|
||||
):
|
||||
from google.cloud.devtools import cloudbuild
|
||||
from google import protobuf
|
||||
build_client = cloudbuild.CloudBuildClient()
|
||||
build_config = cloudbuild.Build(
|
||||
images=[dst_container_image_uri],
|
||||
steps=[
|
||||
cloudbuild.BuildStep(
|
||||
name="gcr.io/cloud-builders/docker",
|
||||
entrypoint="bash",
|
||||
args=[
|
||||
"-exc",
|
||||
'docker pull --quiet "$0" && docker tag "$0" "$1"',
|
||||
src_container_image_uri,
|
||||
dst_container_image_uri,
|
||||
],
|
||||
),
|
||||
],
|
||||
timeout=protobuf.duration_pb2.Duration(
|
||||
seconds=1800,
|
||||
),
|
||||
)
|
||||
build_operation = build_client.create_build(
|
||||
project_id=project_id,
|
||||
build=build_config,
|
||||
)
|
||||
try:
|
||||
result = build_operation.result()
|
||||
except:
|
||||
print(f"Logs are available at [{build_operation.metadata.build.log_url}].")
|
||||
raise
|
||||
return result
|
||||
|
||||
project_id = aiplatform.initializer.global_config.project
|
||||
mirrored_container_uri = f"gcr.io/{project_id}/container_mirror/{container_image_uri}"
|
||||
# FIX: Only mirror when image does not exist
|
||||
# docker does is unable to get the registry data from inside container (it cannot connecto to docker socket):
|
||||
# docker.errors.DockerException: Error while fetching server API version: ('Connection aborted.', FileNotFoundError(2, 'No such file or directory'))
|
||||
# import docker
|
||||
# try:
|
||||
# docker_client = docker.from_env()
|
||||
# docker_client.images.get_registry_data(mirrored_container_uri)
|
||||
# except docker.errors.NotFound:
|
||||
if True:
|
||||
print(f"Mirroring {container_image_uri} to {mirrored_container_uri}")
|
||||
copy_container_image(
|
||||
src_container_image_uri=container_image_uri,
|
||||
dst_container_image_uri=mirrored_container_uri,
|
||||
project_id=project_id,
|
||||
)
|
||||
container_image_uri = mirrored_container_uri
|
||||
# End of container image mirroring code
|
||||
|
||||
model_archive_file_name = os.path.basename(model_archive_path)
|
||||
model_archive_dir = os.path.dirname(model_archive_path)
|
||||
|
||||
model = aiplatform.Model.upload(
|
||||
# FIX: Use public image or mirror the official image
|
||||
#serving_container_image_uri="gcr.io/avolkov-31337/mirror/pytorch/torchserve",
|
||||
serving_container_image_uri=container_image_uri,
|
||||
artifact_uri=model_archive_dir,
|
||||
serving_container_command=[
|
||||
"bash",
|
||||
"-exc",
|
||||
'''
|
||||
model_archive_uri="$0"
|
||||
#model_archive_local_path=$(mktemp --suffix ".mar")
|
||||
# For some reason the model must already be inside the model-store directory.
|
||||
model_archive_local_path=./model-store/model.mar
|
||||
|
||||
# Downloading the model archive from GCS
|
||||
# TODO: Fix gsutil bugs (requires project ID, has auth issues) and use gsutil instead.
|
||||
# gsutil cp "$model_archive_uri" "$model_archive_local_path"
|
||||
pip install google-cloud-storage
|
||||
python -c '
|
||||
import sys
|
||||
from google.cloud import storage
|
||||
|
||||
model_archive_uri = sys.argv[1]
|
||||
model_archive_local_path = sys.argv[2]
|
||||
|
||||
storage_client = storage.Client()
|
||||
blob = storage.Blob.from_string(uri=model_archive_uri, client=storage_client)
|
||||
blob.download_to_filename(filename=model_archive_local_path)
|
||||
' "$model_archive_uri" "$model_archive_local_path"
|
||||
|
||||
#Note: config.properties is owned by root. Our user is not root.
|
||||
echo "
|
||||
service_envelope=json
|
||||
# Needed for external access
|
||||
inference_address=http://0.0.0.0:8080
|
||||
management_address=http://0.0.0.0:8081
|
||||
" > config2.properties
|
||||
torchserve --start --foreground --no-config-snapshots --models main-model="$model_archive_local_path" --model-store ./model-store/ --ts-config config2.properties
|
||||
''',
|
||||
"$(AIP_STORAGE_URI)/" + model_archive_file_name,
|
||||
],
|
||||
serving_container_predict_route="/predictions/main-model",
|
||||
#serving_container_predict_route="/v1/models/main-model:predict",
|
||||
serving_container_health_route="/ping",
|
||||
serving_container_ports=[8080],
|
||||
|
||||
display_name=display_name,
|
||||
description=description,
|
||||
|
||||
# instance_schema_uri=instance_schema_uri,
|
||||
# parameters_schema_uri=parameters_schema_uri,
|
||||
# prediction_schema_uri=prediction_schema_uri,
|
||||
# explanation_metadata=explanation_metadata,
|
||||
# explanation_parameters=explanation_parameters,
|
||||
|
||||
project=project,
|
||||
location=location,
|
||||
labels=labels,
|
||||
# encryption_spec_key_name=encryption_spec_key_name,
|
||||
staging_bucket=staging_bucket,
|
||||
)
|
||||
model_json = json.dumps(model.to_dict(), indent=2)
|
||||
print(model_json)
|
||||
return (model.resource_name, model_json)
|
||||
|
||||
def _deserialize_bool(s) -> bool:
|
||||
from distutils.util import strtobool
|
||||
return strtobool(s) == 1
|
||||
|
||||
def _serialize_json(obj) -> str:
|
||||
if isinstance(obj, str):
|
||||
return obj
|
||||
import json
|
||||
def default_serializer(obj):
|
||||
if hasattr(obj, 'to_struct'):
|
||||
return obj.to_struct()
|
||||
else:
|
||||
raise TypeError("Object of type '%s' is not JSON serializable and does not have .to_struct() method." % obj.__class__.__name__)
|
||||
return json.dumps(obj, default=default_serializer, sort_keys=True)
|
||||
|
||||
def _serialize_str(str_value: str) -> str:
|
||||
if not isinstance(str_value, str):
|
||||
raise TypeError('Value "{}" has type "{}" instead of str.'.format(str(str_value), str(type(str_value))))
|
||||
return str_value
|
||||
|
||||
import json
|
||||
import argparse
|
||||
_parser = argparse.ArgumentParser(prog='Upload PyTorch model archive to Google Cloud Vertex AI', description='')
|
||||
_parser.add_argument("--model-archive", dest="model_archive_path", type=str, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--torchserve-version", dest="torchserve_version", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--use-gpu", dest="use_gpu", type=_deserialize_bool, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--display-name", dest="display_name", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--description", dest="description", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--project", dest="project", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--location", dest="location", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--labels", dest="labels", type=json.loads, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--staging-bucket", dest="staging_bucket", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("----output-paths", dest="_output_paths", type=str, nargs=2)
|
||||
_parsed_args = vars(_parser.parse_args())
|
||||
_output_files = _parsed_args.pop("_output_paths", [])
|
||||
|
||||
_outputs = upload_PyTorch_model_archive_to_Google_Cloud_Vertex_AI(**_parsed_args)
|
||||
|
||||
_output_serializers = [
|
||||
_serialize_str,
|
||||
_serialize_json,
|
||||
|
||||
]
|
||||
|
||||
import os
|
||||
for idx, output_file in enumerate(_output_files):
|
||||
try:
|
||||
os.makedirs(os.path.dirname(output_file))
|
||||
except OSError:
|
||||
pass
|
||||
with open(output_file, 'w') as f:
|
||||
f.write(_output_serializers[idx](_outputs[idx]))
|
||||
args:
|
||||
- --model-archive
|
||||
- {inputPath: model_archive}
|
||||
- if:
|
||||
cond: {isPresent: torchserve_version}
|
||||
then:
|
||||
- --torchserve-version
|
||||
- {inputValue: torchserve_version}
|
||||
- if:
|
||||
cond: {isPresent: use_gpu}
|
||||
then:
|
||||
- --use-gpu
|
||||
- {inputValue: use_gpu}
|
||||
- if:
|
||||
cond: {isPresent: display_name}
|
||||
then:
|
||||
- --display-name
|
||||
- {inputValue: display_name}
|
||||
- if:
|
||||
cond: {isPresent: description}
|
||||
then:
|
||||
- --description
|
||||
- {inputValue: description}
|
||||
- if:
|
||||
cond: {isPresent: project}
|
||||
then:
|
||||
- --project
|
||||
- {inputValue: project}
|
||||
- if:
|
||||
cond: {isPresent: location}
|
||||
then:
|
||||
- --location
|
||||
- {inputValue: location}
|
||||
- if:
|
||||
cond: {isPresent: labels}
|
||||
then:
|
||||
- --labels
|
||||
- {inputValue: labels}
|
||||
- if:
|
||||
cond: {isPresent: staging_bucket}
|
||||
then:
|
||||
- --staging-bucket
|
||||
- {inputValue: staging_bucket}
|
||||
- '----output-paths'
|
||||
- {outputPath: model_name}
|
||||
- {outputPath: model_dict}
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
name: Upload Scikit learn pickle model to Google Cloud Vertex AI
|
||||
metadata:
|
||||
annotations: {author: Alexey Volkov <alexey.volkov@ark-kun.com>, canonical_location: 'https://raw.githubusercontent.com/Ark-kun/pipeline_components/KFPv2_hell/components/google-cloud/Vertex_AI/Models/Upload_Scikit-learn_pickle_model/workaround_for_buggy_KFPv2_compiler/component.yaml'}
|
||||
inputs:
|
||||
- {name: model, type: ScikitLearnPickleModel}
|
||||
- {name: sklearn_version, type: String, optional: true}
|
||||
- {name: display_name, type: String, optional: true}
|
||||
- {name: description, type: String, optional: true}
|
||||
- {name: project, type: String, optional: true}
|
||||
- {name: location, type: String, optional: true}
|
||||
- {name: labels, type: JsonObject, optional: true}
|
||||
- {name: staging_bucket, type: String, optional: true}
|
||||
outputs:
|
||||
- {name: model_name, type: String}
|
||||
- {name: model_dict, type: JsonObject}
|
||||
implementation:
|
||||
container:
|
||||
image: python:3.9
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- (PIP_DISABLE_PIP_VERSION_CHECK=1 python3 -m pip install --quiet --no-warn-script-location
|
||||
'google-cloud-aiplatform==1.16.0' || PIP_DISABLE_PIP_VERSION_CHECK=1 python3
|
||||
-m pip install --quiet --no-warn-script-location 'google-cloud-aiplatform==1.16.0'
|
||||
--user) && "$0" "$@"
|
||||
- sh
|
||||
- -ec
|
||||
- |
|
||||
program_path=$(mktemp)
|
||||
printf "%s" "$0" > "$program_path"
|
||||
python3 -u "$program_path" "$@"
|
||||
- |
|
||||
def upload_Scikit_learn_pickle_model_to_Google_Cloud_Vertex_AI(
|
||||
model_path,
|
||||
sklearn_version = None,
|
||||
|
||||
display_name = None,
|
||||
description = None,
|
||||
|
||||
# Uncomment when anyone requests these:
|
||||
# instance_schema_uri: str = None,
|
||||
# parameters_schema_uri: str = None,
|
||||
# prediction_schema_uri: str = None,
|
||||
# explanation_metadata: "google.cloud.aiplatform_v1.types.explanation_metadata.ExplanationMetadata" = None,
|
||||
# explanation_parameters: "google.cloud.aiplatform_v1.types.explanation.ExplanationParameters" = None,
|
||||
|
||||
project = None,
|
||||
location = None,
|
||||
labels = None,
|
||||
# encryption_spec_key_name: str = None,
|
||||
staging_bucket = None,
|
||||
):
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
from google.cloud import aiplatform
|
||||
|
||||
if not location:
|
||||
location = os.environ.get("CLOUD_ML_REGION")
|
||||
|
||||
if not labels:
|
||||
labels = {}
|
||||
labels["component-source"] = "github-com-ark-kun-pipeline-components"
|
||||
|
||||
# The serving container decides the model type based on the model file extension.
|
||||
# So we need to rename the mode file (e.g. /tmp/inputs/model/data) to *.pkl
|
||||
_, renamed_model_path = tempfile.mkstemp(suffix=".pkl")
|
||||
shutil.copyfile(src=model_path, dst=renamed_model_path)
|
||||
|
||||
model = aiplatform.Model.upload_scikit_learn_model_file(
|
||||
model_file_path=renamed_model_path,
|
||||
sklearn_version=sklearn_version,
|
||||
|
||||
display_name=display_name,
|
||||
description=description,
|
||||
|
||||
# instance_schema_uri=instance_schema_uri,
|
||||
# parameters_schema_uri=parameters_schema_uri,
|
||||
# prediction_schema_uri=prediction_schema_uri,
|
||||
# explanation_metadata=explanation_metadata,
|
||||
# explanation_parameters=explanation_parameters,
|
||||
|
||||
project=project,
|
||||
location=location,
|
||||
labels=labels,
|
||||
# encryption_spec_key_name=encryption_spec_key_name,
|
||||
staging_bucket=staging_bucket,
|
||||
)
|
||||
model_json = json.dumps(model.to_dict(), indent=2)
|
||||
print(model_json)
|
||||
return (model.resource_name, model_json)
|
||||
|
||||
def _serialize_json(obj) -> str:
|
||||
if isinstance(obj, str):
|
||||
return obj
|
||||
import json
|
||||
def default_serializer(obj):
|
||||
if hasattr(obj, 'to_struct'):
|
||||
return obj.to_struct()
|
||||
else:
|
||||
raise TypeError("Object of type '%s' is not JSON serializable and does not have .to_struct() method." % obj.__class__.__name__)
|
||||
return json.dumps(obj, default=default_serializer, sort_keys=True)
|
||||
|
||||
def _serialize_str(str_value: str) -> str:
|
||||
if not isinstance(str_value, str):
|
||||
raise TypeError('Value "{}" has type "{}" instead of str.'.format(str(str_value), str(type(str_value))))
|
||||
return str_value
|
||||
|
||||
import json
|
||||
import argparse
|
||||
_parser = argparse.ArgumentParser(prog='Upload Scikit learn pickle model to Google Cloud Vertex AI', description='')
|
||||
_parser.add_argument("--model", dest="model_path", type=str, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--sklearn-version", dest="sklearn_version", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--display-name", dest="display_name", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--description", dest="description", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--project", dest="project", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--location", dest="location", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--labels", dest="labels", type=json.loads, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--staging-bucket", dest="staging_bucket", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("----output-paths", dest="_output_paths", type=str, nargs=2)
|
||||
_parsed_args = vars(_parser.parse_args())
|
||||
_output_files = _parsed_args.pop("_output_paths", [])
|
||||
|
||||
_outputs = upload_Scikit_learn_pickle_model_to_Google_Cloud_Vertex_AI(**_parsed_args)
|
||||
|
||||
_output_serializers = [
|
||||
_serialize_str,
|
||||
_serialize_json,
|
||||
|
||||
]
|
||||
|
||||
import os
|
||||
for idx, output_file in enumerate(_output_files):
|
||||
try:
|
||||
os.makedirs(os.path.dirname(output_file))
|
||||
except OSError:
|
||||
pass
|
||||
with open(output_file, 'w') as f:
|
||||
f.write(_output_serializers[idx](_outputs[idx]))
|
||||
args:
|
||||
- --model
|
||||
- {inputPath: model}
|
||||
- if:
|
||||
cond: {isPresent: sklearn_version}
|
||||
then:
|
||||
- --sklearn-version
|
||||
- {inputValue: sklearn_version}
|
||||
- if:
|
||||
cond: {isPresent: display_name}
|
||||
then:
|
||||
- --display-name
|
||||
- {inputValue: display_name}
|
||||
- if:
|
||||
cond: {isPresent: description}
|
||||
then:
|
||||
- --description
|
||||
- {inputValue: description}
|
||||
- if:
|
||||
cond: {isPresent: project}
|
||||
then:
|
||||
- --project
|
||||
- {inputValue: project}
|
||||
- if:
|
||||
cond: {isPresent: location}
|
||||
then:
|
||||
- --location
|
||||
- {inputValue: location}
|
||||
- if:
|
||||
cond: {isPresent: labels}
|
||||
then:
|
||||
- --labels
|
||||
- {inputValue: labels}
|
||||
- if:
|
||||
cond: {isPresent: staging_bucket}
|
||||
then:
|
||||
- --staging-bucket
|
||||
- {inputValue: staging_bucket}
|
||||
- '----output-paths'
|
||||
- {outputPath: model_name}
|
||||
- {outputPath: model_dict}
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
name: Upload Tensorflow model to Google Cloud Vertex AI
|
||||
metadata:
|
||||
annotations: {author: Alexey Volkov <alexey.volkov@ark-kun.com>, canonical_location: 'https://raw.githubusercontent.com/Ark-kun/pipeline_components/KFPv2_hell/components/google-cloud/Vertex_AI/Models/Upload_Tensorflow_model/workaround_for_buggy_KFPv2_compiler/component.yaml'}
|
||||
inputs:
|
||||
- {name: model, type: TensorflowSavedModel}
|
||||
- {name: tensorflow_version, type: String, optional: true}
|
||||
- name: use_gpu
|
||||
type: Boolean
|
||||
default: "False"
|
||||
optional: true
|
||||
- {name: display_name, type: String, optional: true}
|
||||
- {name: description, type: String, optional: true}
|
||||
- {name: project, type: String, optional: true}
|
||||
- {name: location, type: String, optional: true}
|
||||
- {name: labels, type: JsonObject, optional: true}
|
||||
- {name: staging_bucket, type: String, optional: true}
|
||||
outputs:
|
||||
- {name: model_name, type: String}
|
||||
- {name: model_dict, type: JsonObject}
|
||||
implementation:
|
||||
container:
|
||||
image: python:3.9
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- (PIP_DISABLE_PIP_VERSION_CHECK=1 python3 -m pip install --quiet --no-warn-script-location
|
||||
'google-cloud-aiplatform==1.16.0' || PIP_DISABLE_PIP_VERSION_CHECK=1 python3
|
||||
-m pip install --quiet --no-warn-script-location 'google-cloud-aiplatform==1.16.0'
|
||||
--user) && "$0" "$@"
|
||||
- sh
|
||||
- -ec
|
||||
- |
|
||||
program_path=$(mktemp)
|
||||
printf "%s" "$0" > "$program_path"
|
||||
python3 -u "$program_path" "$@"
|
||||
- |
|
||||
def upload_Tensorflow_model_to_Google_Cloud_Vertex_AI(
|
||||
model_path,
|
||||
tensorflow_version = None,
|
||||
use_gpu = False,
|
||||
|
||||
display_name = None,
|
||||
description = None,
|
||||
|
||||
# Uncomment when anyone requests these:
|
||||
# instance_schema_uri: str = None,
|
||||
# parameters_schema_uri: str = None,
|
||||
# prediction_schema_uri: str = None,
|
||||
# explanation_metadata: "google.cloud.aiplatform_v1.types.explanation_metadata.ExplanationMetadata" = None,
|
||||
# explanation_parameters: "google.cloud.aiplatform_v1.types.explanation.ExplanationParameters" = None,
|
||||
|
||||
project = None,
|
||||
location = None,
|
||||
labels = None,
|
||||
# encryption_spec_key_name: str = None,
|
||||
staging_bucket = None,
|
||||
):
|
||||
import json
|
||||
import os
|
||||
from google.cloud import aiplatform
|
||||
|
||||
if not location:
|
||||
location = os.environ.get("CLOUD_ML_REGION")
|
||||
|
||||
if not labels:
|
||||
labels = {}
|
||||
labels["component-source"] = "github-com-ark-kun-pipeline-components"
|
||||
|
||||
model = aiplatform.Model.upload_tensorflow_saved_model(
|
||||
saved_model_dir=model_path,
|
||||
tensorflow_version=tensorflow_version,
|
||||
use_gpu=use_gpu,
|
||||
|
||||
display_name=display_name,
|
||||
description=description,
|
||||
|
||||
# instance_schema_uri=instance_schema_uri,
|
||||
# parameters_schema_uri=parameters_schema_uri,
|
||||
# prediction_schema_uri=prediction_schema_uri,
|
||||
# explanation_metadata=explanation_metadata,
|
||||
# explanation_parameters=explanation_parameters,
|
||||
|
||||
project=project,
|
||||
location=location,
|
||||
labels=labels,
|
||||
# encryption_spec_key_name=encryption_spec_key_name,
|
||||
staging_bucket=staging_bucket,
|
||||
)
|
||||
model_json = json.dumps(model.to_dict(), indent=2)
|
||||
print(model_json)
|
||||
return (model.resource_name, model_json)
|
||||
|
||||
def _deserialize_bool(s) -> bool:
|
||||
from distutils.util import strtobool
|
||||
return strtobool(s) == 1
|
||||
|
||||
def _serialize_json(obj) -> str:
|
||||
if isinstance(obj, str):
|
||||
return obj
|
||||
import json
|
||||
def default_serializer(obj):
|
||||
if hasattr(obj, 'to_struct'):
|
||||
return obj.to_struct()
|
||||
else:
|
||||
raise TypeError("Object of type '%s' is not JSON serializable and does not have .to_struct() method." % obj.__class__.__name__)
|
||||
return json.dumps(obj, default=default_serializer, sort_keys=True)
|
||||
|
||||
def _serialize_str(str_value: str) -> str:
|
||||
if not isinstance(str_value, str):
|
||||
raise TypeError('Value "{}" has type "{}" instead of str.'.format(str(str_value), str(type(str_value))))
|
||||
return str_value
|
||||
|
||||
import json
|
||||
import argparse
|
||||
_parser = argparse.ArgumentParser(prog='Upload Tensorflow model to Google Cloud Vertex AI', description='')
|
||||
_parser.add_argument("--model", dest="model_path", type=str, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--tensorflow-version", dest="tensorflow_version", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--use-gpu", dest="use_gpu", type=_deserialize_bool, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--display-name", dest="display_name", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--description", dest="description", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--project", dest="project", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--location", dest="location", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--labels", dest="labels", type=json.loads, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--staging-bucket", dest="staging_bucket", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("----output-paths", dest="_output_paths", type=str, nargs=2)
|
||||
_parsed_args = vars(_parser.parse_args())
|
||||
_output_files = _parsed_args.pop("_output_paths", [])
|
||||
|
||||
_outputs = upload_Tensorflow_model_to_Google_Cloud_Vertex_AI(**_parsed_args)
|
||||
|
||||
_output_serializers = [
|
||||
_serialize_str,
|
||||
_serialize_json,
|
||||
|
||||
]
|
||||
|
||||
import os
|
||||
for idx, output_file in enumerate(_output_files):
|
||||
try:
|
||||
os.makedirs(os.path.dirname(output_file))
|
||||
except OSError:
|
||||
pass
|
||||
with open(output_file, 'w') as f:
|
||||
f.write(_output_serializers[idx](_outputs[idx]))
|
||||
args:
|
||||
- --model
|
||||
- {inputPath: model}
|
||||
- if:
|
||||
cond: {isPresent: tensorflow_version}
|
||||
then:
|
||||
- --tensorflow-version
|
||||
- {inputValue: tensorflow_version}
|
||||
- if:
|
||||
cond: {isPresent: use_gpu}
|
||||
then:
|
||||
- --use-gpu
|
||||
- {inputValue: use_gpu}
|
||||
- if:
|
||||
cond: {isPresent: display_name}
|
||||
then:
|
||||
- --display-name
|
||||
- {inputValue: display_name}
|
||||
- if:
|
||||
cond: {isPresent: description}
|
||||
then:
|
||||
- --description
|
||||
- {inputValue: description}
|
||||
- if:
|
||||
cond: {isPresent: project}
|
||||
then:
|
||||
- --project
|
||||
- {inputValue: project}
|
||||
- if:
|
||||
cond: {isPresent: location}
|
||||
then:
|
||||
- --location
|
||||
- {inputValue: location}
|
||||
- if:
|
||||
cond: {isPresent: labels}
|
||||
then:
|
||||
- --labels
|
||||
- {inputValue: labels}
|
||||
- if:
|
||||
cond: {isPresent: staging_bucket}
|
||||
then:
|
||||
- --staging-bucket
|
||||
- {inputValue: staging_bucket}
|
||||
- '----output-paths'
|
||||
- {outputPath: model_name}
|
||||
- {outputPath: model_dict}
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
name: Upload XGBoost model to Google Cloud Vertex AI
|
||||
metadata:
|
||||
annotations: {author: Alexey Volkov <alexey.volkov@ark-kun.com>, canonical_location: 'https://raw.githubusercontent.com/Ark-kun/pipeline_components/master/components/google-cloud/Vertex_AI/Models/Upload_XGBoost_model/workaround_for_buggy_KFPv2_compiler/component.yaml'}
|
||||
inputs:
|
||||
- {name: model, type: XGBoostModel}
|
||||
- {name: xgboost_version, type: String, optional: true}
|
||||
- {name: display_name, type: String, optional: true}
|
||||
- {name: description, type: String, optional: true}
|
||||
- {name: project, type: String, optional: true}
|
||||
- {name: location, type: String, optional: true}
|
||||
- {name: labels, type: JsonObject, optional: true}
|
||||
- {name: staging_bucket, type: String, optional: true}
|
||||
outputs:
|
||||
- {name: model_name, type: String}
|
||||
- {name: model_dict, type: JsonObject}
|
||||
implementation:
|
||||
container:
|
||||
image: python:3.9
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- (PIP_DISABLE_PIP_VERSION_CHECK=1 python3 -m pip install --quiet --no-warn-script-location
|
||||
'google-cloud-aiplatform==1.16.0' || PIP_DISABLE_PIP_VERSION_CHECK=1 python3
|
||||
-m pip install --quiet --no-warn-script-location 'google-cloud-aiplatform==1.16.0'
|
||||
--user) && "$0" "$@"
|
||||
- sh
|
||||
- -ec
|
||||
- |
|
||||
program_path=$(mktemp)
|
||||
printf "%s" "$0" > "$program_path"
|
||||
python3 -u "$program_path" "$@"
|
||||
- |
|
||||
def upload_XGBoost_model_to_Google_Cloud_Vertex_AI(
|
||||
model_path,
|
||||
xgboost_version = None,
|
||||
|
||||
display_name = None,
|
||||
description = None,
|
||||
|
||||
# Uncomment when anyone requests these:
|
||||
# instance_schema_uri: str = None,
|
||||
# parameters_schema_uri: str = None,
|
||||
# prediction_schema_uri: str = None,
|
||||
# explanation_metadata: "google.cloud.aiplatform_v1.types.explanation_metadata.ExplanationMetadata" = None,
|
||||
# explanation_parameters: "google.cloud.aiplatform_v1.types.explanation.ExplanationParameters" = None,
|
||||
|
||||
project = None,
|
||||
location = None,
|
||||
labels = None,
|
||||
# encryption_spec_key_name: str = None,
|
||||
staging_bucket = None,
|
||||
):
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
from google.cloud import aiplatform
|
||||
|
||||
if not location:
|
||||
location = os.environ.get("CLOUD_ML_REGION")
|
||||
|
||||
if not labels:
|
||||
labels = {}
|
||||
labels["component-source"] = "github-com-ark-kun-pipeline-components"
|
||||
|
||||
# The serving container decides the model type based on the model file extension.
|
||||
# So we need to rename the mode file (e.g. /tmp/inputs/model/data) to *.pkl
|
||||
_, renamed_model_path = tempfile.mkstemp(suffix=".pkl")
|
||||
shutil.copyfile(src=model_path, dst=renamed_model_path)
|
||||
|
||||
model = aiplatform.Model.upload_xgboost_model_file(
|
||||
model_file_path=renamed_model_path,
|
||||
xgboost_version=xgboost_version,
|
||||
|
||||
display_name=display_name,
|
||||
description=description,
|
||||
|
||||
# instance_schema_uri=instance_schema_uri,
|
||||
# parameters_schema_uri=parameters_schema_uri,
|
||||
# prediction_schema_uri=prediction_schema_uri,
|
||||
# explanation_metadata=explanation_metadata,
|
||||
# explanation_parameters=explanation_parameters,
|
||||
|
||||
project=project,
|
||||
location=location,
|
||||
labels=labels,
|
||||
# encryption_spec_key_name=encryption_spec_key_name,
|
||||
staging_bucket=staging_bucket,
|
||||
)
|
||||
model_json = json.dumps(model.to_dict(), indent=2)
|
||||
print(model_json)
|
||||
return (model.resource_name, model_json)
|
||||
|
||||
def _serialize_json(obj) -> str:
|
||||
if isinstance(obj, str):
|
||||
return obj
|
||||
import json
|
||||
def default_serializer(obj):
|
||||
if hasattr(obj, 'to_struct'):
|
||||
return obj.to_struct()
|
||||
else:
|
||||
raise TypeError("Object of type '%s' is not JSON serializable and does not have .to_struct() method." % obj.__class__.__name__)
|
||||
return json.dumps(obj, default=default_serializer, sort_keys=True)
|
||||
|
||||
def _serialize_str(str_value: str) -> str:
|
||||
if not isinstance(str_value, str):
|
||||
raise TypeError('Value "{}" has type "{}" instead of str.'.format(str(str_value), str(type(str_value))))
|
||||
return str_value
|
||||
|
||||
import json
|
||||
import argparse
|
||||
_parser = argparse.ArgumentParser(prog='Upload XGBoost model to Google Cloud Vertex AI', description='')
|
||||
_parser.add_argument("--model", dest="model_path", type=str, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--xgboost-version", dest="xgboost_version", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--display-name", dest="display_name", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--description", dest="description", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--project", dest="project", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--location", dest="location", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--labels", dest="labels", type=json.loads, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--staging-bucket", dest="staging_bucket", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("----output-paths", dest="_output_paths", type=str, nargs=2)
|
||||
_parsed_args = vars(_parser.parse_args())
|
||||
_output_files = _parsed_args.pop("_output_paths", [])
|
||||
|
||||
_outputs = upload_XGBoost_model_to_Google_Cloud_Vertex_AI(**_parsed_args)
|
||||
|
||||
_output_serializers = [
|
||||
_serialize_str,
|
||||
_serialize_json,
|
||||
|
||||
]
|
||||
|
||||
import os
|
||||
for idx, output_file in enumerate(_output_files):
|
||||
try:
|
||||
os.makedirs(os.path.dirname(output_file))
|
||||
except OSError:
|
||||
pass
|
||||
with open(output_file, 'w') as f:
|
||||
f.write(_output_serializers[idx](_outputs[idx]))
|
||||
args:
|
||||
- --model
|
||||
- {inputPath: model}
|
||||
- if:
|
||||
cond: {isPresent: xgboost_version}
|
||||
then:
|
||||
- --xgboost-version
|
||||
- {inputValue: xgboost_version}
|
||||
- if:
|
||||
cond: {isPresent: display_name}
|
||||
then:
|
||||
- --display-name
|
||||
- {inputValue: display_name}
|
||||
- if:
|
||||
cond: {isPresent: description}
|
||||
then:
|
||||
- --description
|
||||
- {inputValue: description}
|
||||
- if:
|
||||
cond: {isPresent: project}
|
||||
then:
|
||||
- --project
|
||||
- {inputValue: project}
|
||||
- if:
|
||||
cond: {isPresent: location}
|
||||
then:
|
||||
- --location
|
||||
- {inputValue: location}
|
||||
- if:
|
||||
cond: {isPresent: labels}
|
||||
then:
|
||||
- --labels
|
||||
- {inputValue: labels}
|
||||
- if:
|
||||
cond: {isPresent: staging_bucket}
|
||||
then:
|
||||
- --staging-bucket
|
||||
- {inputValue: staging_bucket}
|
||||
- '----output-paths'
|
||||
- {outputPath: model_name}
|
||||
- {outputPath: model_dict}
|
||||
@@ -0,0 +1,35 @@
|
||||
name: Download from GCS
|
||||
inputs:
|
||||
- {name: GCS path, type: String}
|
||||
outputs:
|
||||
- {name: Data}
|
||||
metadata:
|
||||
annotations:
|
||||
author: Alexey Volkov <alexey.volkov@ark-kun.com>
|
||||
canonical_location: 'https://raw.githubusercontent.com/Ark-kun/pipeline_components/master/components/google-cloud/storage/download/workaround_for_buggy_KFPv2_compiler/component.yaml'
|
||||
implementation:
|
||||
container:
|
||||
image: google/cloud-sdk
|
||||
command:
|
||||
- bash # Pattern comparison only works in Bash
|
||||
- -ex
|
||||
- -c
|
||||
- |
|
||||
if [ -n "${GOOGLE_APPLICATION_CREDENTIALS}" ]; then
|
||||
gcloud auth activate-service-account --key-file="${GOOGLE_APPLICATION_CREDENTIALS}"
|
||||
fi
|
||||
|
||||
uri="$0"
|
||||
output_path="$1"
|
||||
|
||||
# Checking whether the URI points to a single blob, a directory or a URI pattern
|
||||
# URI points to a blob when that URI does not end with slash and listing that URI only yields the same URI
|
||||
if [[ "$uri" != */ ]] && (gsutil ls "$uri" | grep --fixed-strings --line-regexp "$uri"); then
|
||||
mkdir -p "$(dirname "$output_path")"
|
||||
gsutil -m cp -r "$uri" "$output_path"
|
||||
else
|
||||
mkdir -p "$output_path" # When source path is a directory, gsutil requires the destination to also be a directory
|
||||
gsutil -m rsync -r "$uri" "$output_path" # gsutil cp has different path handling than Linux cp. It always puts the source directory (name) inside the destination directory. gsutil rsync does not have that problem.
|
||||
fi
|
||||
- inputValue: GCS path
|
||||
- outputPath: Data
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
name: Load image classification model from tfhub
|
||||
description: |
|
||||
Loads specified model from TFHub, creates layer to receive additional (3 channel) imagery data.
|
||||
Args:
|
||||
class_names (Sequence[str]):
|
||||
Sequence of strings of categories for classification corresponding to input data.
|
||||
loaded_model_path (str):
|
||||
Output path for the loaded model.
|
||||
image_size_path (str):
|
||||
Output path for the model expected image size.
|
||||
model_name (Optional[str]):
|
||||
Name of the pre-trained image classification model to load from TFHub.
|
||||
Eligible model_name:
|
||||
- efficientnetv2-s
|
||||
- efficientnetv2-m
|
||||
- efficientnetv2-l
|
||||
- efficientnetv2-s-21k
|
||||
- efficientnetv2-m-21k
|
||||
- efficientnetv2-l-21k
|
||||
- efficientnetv2-xl-21k
|
||||
- efficientnetv2-b0-21k
|
||||
- efficientnetv2-b1-21k
|
||||
- efficientnetv2-b2-21k
|
||||
- efficientnetv2-b3-21k
|
||||
- efficientnetv2-s-21k-ft1k
|
||||
- efficientnetv2-m-21k-ft1k
|
||||
- efficientnetv2-l-21k-ft1k
|
||||
- efficientnetv2-xl-21k-ft1k
|
||||
- efficientnetv2-b0-21k-ft1k
|
||||
- efficientnetv2-b1-21k-ft1k
|
||||
- efficientnetv2-b2-21k-ft1k
|
||||
- efficientnetv2-b3-21k-ft1k
|
||||
- efficientnetv2-b0
|
||||
- efficientnetv2-b1
|
||||
- efficientnetv2-b2
|
||||
- efficientnetv2-b3
|
||||
- efficientnet_b0
|
||||
- efficientnet_b1
|
||||
- efficientnet_b2
|
||||
- efficientnet_b3
|
||||
- efficientnet_b4
|
||||
- efficientnet_b5
|
||||
- efficientnet_b6
|
||||
- efficientnet_b7
|
||||
- bit_s-r50x1
|
||||
- inception_v3
|
||||
- inception_resnet_v2
|
||||
- resnet_v1_50
|
||||
- resnet_v1_101
|
||||
- resnet_v1_152
|
||||
- resnet_v2_50
|
||||
- resnet_v2_101
|
||||
- resnet_v2_152
|
||||
- nasnet_large
|
||||
- nasnet_mobile
|
||||
- pnasnet_large
|
||||
- mobilenet_v2_100_224
|
||||
- mobilenet_v2_130_224
|
||||
- mobilenet_v2_140_224
|
||||
- mobilenet_v3_small_100_224
|
||||
- mobilenet_v3_small_075_224
|
||||
- mobilenet_v3_large_100_224
|
||||
- mobilenet_v3_large_075_224
|
||||
dropout_rate (Optional[float]):
|
||||
Fraction of input units to drop in the last layer. Value should be between 0.0 and 1.0.
|
||||
trainable (Optional[bool]):
|
||||
If true fine tuning will be performed on entire Hub model. If false only additional
|
||||
layers will be trained.
|
||||
l2_regularization_penalty (Optional[float]):
|
||||
l2 regularization penalty.
|
||||
inputs:
|
||||
- {name: class_names, type: 'typing.List[str]', description: List of class names corresponding
|
||||
to the input image data}
|
||||
- {name: model_name, type: String, description: Name of the TFHub model to load, default: efficientnetv2-xl-21k,
|
||||
optional: true}
|
||||
- {name: dropout_rate, type: Float, description: Dropout rate, default: '0.2', optional: true}
|
||||
- name: trainable
|
||||
type: Boolean
|
||||
description: True if fine tuning should be performed
|
||||
default: "True"
|
||||
optional: true
|
||||
- {name: l2_regularization_penalty, type: Float, description: Regularization penalty,
|
||||
default: '0.0001', optional: true}
|
||||
outputs:
|
||||
- {name: loaded_model_path, type: TensorflowSavedModel, description: Output path for
|
||||
the loaded model}
|
||||
- {name: image_size_path, type: HeightWidth}
|
||||
implementation:
|
||||
container:
|
||||
image: us-docker.pkg.dev/vertex-ai/ready-to-go-image-classification/image-components:v0.1
|
||||
# command is a list of strings (command-line arguments).
|
||||
# The YAML language has two syntaxes for lists and you can use either of them.
|
||||
# Here we use the "flow syntax" - comma-separated strings inside square brackets.
|
||||
command: [
|
||||
python3,
|
||||
# Path of the program inside the container
|
||||
/pipelines/component/src/loading_component.py,
|
||||
--loaded-model-path,
|
||||
{outputPath: loaded_model_path},
|
||||
--class-names,
|
||||
{inputValue: class_names},
|
||||
--model-name,
|
||||
{inputValue: model_name},
|
||||
--dropout-rate,
|
||||
{inputValue: dropout_rate},
|
||||
--trainable,
|
||||
{inputValue: trainable},
|
||||
--l2-regularization-penalty,
|
||||
{inputValue: l2_regularization_penalty},
|
||||
--image-size-path,
|
||||
{outputPath: image_size_path},
|
||||
]
|
||||
@@ -0,0 +1,62 @@
|
||||
# python3 -m pip install "kfp<2.0.0" "google-cloud-aiplatform>=1.16.0" --upgrade --quiet
|
||||
from kfp import components
|
||||
from kfp.v2 import dsl
|
||||
|
||||
# %% Loading components
|
||||
upload_Tensorflow_model_to_Google_Cloud_Vertex_AI_op = components.load_component_from_url('https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/Vertex_AI/Models/Upload_Tensorflow_model/component.yaml')
|
||||
deploy_model_to_endpoint_op = components.load_component_from_url('https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/399405402d95f4a011e2d2e967c96f8508ba5688/community-content/pipeline_components/google-cloud/Vertex_AI/Models/Deploy_to_endpoint/component.yaml')
|
||||
transcode_imagedataset_tfrecord_from_csv_op = components.load_component_from_url('https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/community-content/pipeline_components/image_ml_model_training/transcode_tfrecord_image_dataset_from_csv/component.yaml')
|
||||
load_image_classification_model_from_tfhub_op = components.load_component_from_url('https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/b5b65198a6c2ffe8c0fa2aa70127e3325752df68/community-content/pipeline_components/image_ml_model_training/load_image_classification_model/component.yaml')
|
||||
preprocess_image_data_op = components.load_component_from_url('https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/community-content/pipeline_components/image_ml_model_training/preprocess_image_data/component.yaml')
|
||||
train_tensorflow_image_classification_model_op = components.load_component_from_url('https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/community-content/pipeline_components/image_ml_model_training/train_image_classification_model/component.yaml')
|
||||
|
||||
|
||||
# %% Pipeline definition
|
||||
def image_classification_pipeline():
|
||||
class_names = ['daisy', 'dandelion', 'roses', 'sunflowers', 'tulips']
|
||||
csv_image_data_path = 'gs://cloud-samples-data/ai-platform/flowers/flowers.csv'
|
||||
deploy_model = False
|
||||
|
||||
image_data = dsl.importer(
|
||||
artifact_uri=csv_image_data_path, artifact_class=dsl.Dataset).output
|
||||
|
||||
image_tfrecord_data = transcode_imagedataset_tfrecord_from_csv_op(
|
||||
csv_image_data_path=image_data,
|
||||
class_names=class_names
|
||||
).outputs['tfrecord_image_data_path']
|
||||
|
||||
loaded_model_outputs = load_image_classification_model_from_tfhub_op(
|
||||
class_names=class_names,
|
||||
).outputs
|
||||
|
||||
preprocessed_data = preprocess_image_data_op(
|
||||
image_tfrecord_data,
|
||||
height_width_path=loaded_model_outputs['image_size_path'],
|
||||
).outputs
|
||||
|
||||
trained_model = (train_tensorflow_image_classification_model_op(
|
||||
preprocessed_training_data_path = preprocessed_data['preprocessed_training_data_path'],
|
||||
preprocessed_validation_data_path = preprocessed_data['preprocessed_validation_data_path'],
|
||||
model_path=loaded_model_outputs['loaded_model_path']).
|
||||
set_cpu_limit('96').
|
||||
set_memory_limit('128G').
|
||||
add_node_selector_constraint('cloud.google.com/gke-accelerator', 'NVIDIA_TESLA_A100').
|
||||
set_gpu_limit('8').
|
||||
outputs['trained_model_path'])
|
||||
|
||||
vertex_model_name = upload_Tensorflow_model_to_Google_Cloud_Vertex_AI_op(
|
||||
model=trained_model,
|
||||
).outputs['model_name']
|
||||
|
||||
# Deploying the model might incur additional costs over time
|
||||
if deploy_model:
|
||||
vertex_endpoint_name = deploy_model_to_endpoint_op(
|
||||
model_name=vertex_model_name,
|
||||
).outputs['endpoint_name']
|
||||
|
||||
pipeline_func = image_classification_pipeline
|
||||
|
||||
# %% Pipeline submission
|
||||
if __name__ == '__main__':
|
||||
from google.cloud import aiplatform
|
||||
aiplatform.PipelineJob.from_pipeline_func(pipeline_func=pipeline_func).submit()
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
name: Preprocess image data
|
||||
description: |
|
||||
Preprocess the image data and split between train and validation.
|
||||
Args:
|
||||
input_data_path (str):
|
||||
Input path for the TFRecord image data. Data will be formatted as 'label' (encoded image
|
||||
label), and 'image_raw' (the binary string of the image data).
|
||||
height_width_path (str):
|
||||
Path to square height and width to resize images to. File should contain single float value.
|
||||
Value is dependent on training model.
|
||||
preprocessed_training_data_path (str):
|
||||
Output path for the TFRecord training data. Data will be formatted as 'label' (encoded image
|
||||
label), and 'image_raw' (the binary string of the image data).
|
||||
preprocessed_validation_data_path (str):
|
||||
Output path for the TFRecord validation data. Data will be formatted as 'label' (encoded
|
||||
image label), and 'image_raw' (the binary string of the image data).
|
||||
validation_split (Optional[float]):
|
||||
Fraction of data that will make up validation dataset. Value should be between 0.0 and 1.0.
|
||||
seed (Optional[int]):
|
||||
The global random seed to ensure the system gets a unique random sequence
|
||||
that is deterministic (https://www.tensorflow.org/api_docs/python/tf/random/set_seed).
|
||||
inputs:
|
||||
- {name: input_data_path, type: ImageDatasetTFRecord, description: 'Input path for
|
||||
the TFRecord image data,'}
|
||||
- {name: height_width_path, type: HeightWidth, description: 'Path to square height and width to
|
||||
resize images to,'}
|
||||
- {name: validation_split, type: Float, description: 'Fraction of data that will make
|
||||
up validation dataset,', default: '0.2', optional: true}
|
||||
- {name: seed, type: Integer, description: Random seed, default: '0', optional: true}
|
||||
outputs:
|
||||
- {name: preprocessed_training_data_path, type: ImageDatasetTFRecord, description: 'Output
|
||||
path for the training data,'}
|
||||
- {name: preprocessed_validation_data_path, type: ImageDatasetTFRecord, description: 'Output
|
||||
path for the validation data,'}
|
||||
implementation:
|
||||
container:
|
||||
image: us-docker.pkg.dev/vertex-ai/ready-to-go-image-classification/image-components:v0.1
|
||||
# command is a list of strings (command-line arguments).
|
||||
# The YAML language has two syntaxes for lists and you can use either of them.
|
||||
# Here we use the "flow syntax" - comma-separated strings inside square brackets.
|
||||
command: [
|
||||
python3,
|
||||
# Path of the program inside the container
|
||||
/pipelines/component/src/preprocessing_component.py,
|
||||
--input-data-path,
|
||||
{inputPath: input_data_path},
|
||||
--height-width-path,
|
||||
{inputPath: height_width_path},
|
||||
--validation-split,
|
||||
{inputValue: validation_split},
|
||||
--seed,
|
||||
{inputValue: seed},
|
||||
--preprocessed-training-data-path,
|
||||
{outputPath: preprocessed_training_data_path},
|
||||
--preprocessed-validation-data-path,
|
||||
{outputPath: preprocessed_validation_data_path},
|
||||
]
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
name: Train tensorflow image classification model
|
||||
description: |
|
||||
Creates a trained image classification TensorFlow model.
|
||||
Args:
|
||||
preprocessed_training_data_path (str):
|
||||
Input path to the TFRecord training data. Data will be formatted as 'label' (encoded image
|
||||
label), and 'image_raw' (the binary string of the image data).
|
||||
preprocessed_validation_data_path (str):
|
||||
Input path to the TFRecord validation data. Data will be formatted as 'label' (encoded
|
||||
image label), and 'image_raw' (the binary string of the image data).
|
||||
model_path (str):
|
||||
Input path to the loaded pre-trained model.
|
||||
trained_model_path (str):
|
||||
Output path to save the trained model to.
|
||||
optimizer_name (Optional[str]):
|
||||
Name of the tf.keras optimizer. Available optimizers are listed at
|
||||
https://keras.io/api/optimizers/
|
||||
optimizer_parameters (Optional[Dict[str, str]]):
|
||||
Optimizer parameters.
|
||||
loss_function_name (Optional[str]):
|
||||
Name of the loss function.
|
||||
loss_function_parameters (Optional[Dict[str, str]]):
|
||||
Loss function parameters.
|
||||
number_of_epochs (Optional[int]):
|
||||
Number of training iterations over data.
|
||||
metric_names (Optional[Sequence[str]]):
|
||||
List of tf.keras.metrics to be evaluated by the model during training and testing. Available
|
||||
metrics are listed at https://keras.io/api/metrics/.
|
||||
seed Optional(int):
|
||||
The global random seed to ensure the system gets a unique random sequence
|
||||
that is deterministic (https://www.tensorflow.org/api_docs/python/tf/random/set_seed).
|
||||
inputs:
|
||||
- {name: preprocessed_training_data_path, type: ImageDatasetTFRecord, description: 'Input
|
||||
path for the training data,'}
|
||||
- {name: preprocessed_validation_data_path, type: ImageDatasetTFRecord, description: 'Input
|
||||
path for the validation data,'}
|
||||
- {name: model_path, type: TensorflowSavedModel, description: 'Input path for the
|
||||
model,'}
|
||||
- {name: optimizer_name, type: String, description: 'Name of the optimizer,', default: SGD,
|
||||
optional: true}
|
||||
- {name: optimizer_parameters, type: 'typing.Dict[str, str]', description: 'Optimizer
|
||||
parameters,', default: '{}', optional: true}
|
||||
- {name: loss_function_name, type: String, description: 'Name of the loss function,',
|
||||
default: CategoricalCrossentropy, optional: true}
|
||||
- {name: loss_function_parameters, type: 'typing.Dict[str, str]', description: 'Loss
|
||||
function parameters,', default: '{}', optional: true}
|
||||
- {name: number_of_epochs, type: Integer, description: 'Number of epochs,', default: '10',
|
||||
optional: true}
|
||||
- {name: metric_names, type: 'typing.List[str]', description: 'List of metrics to
|
||||
use,', default: '["accuracy"]', optional: true}
|
||||
- {name: seed, type: Integer, description: 'Random seed,', default: '0', optional: true}
|
||||
- {name: batch_size, type: Integer, description: Batch size, default: '16', optional: true}
|
||||
outputs:
|
||||
- {name: trained_model_path, type: TensorflowSavedModel, description: 'Output path
|
||||
for the saved model,'}
|
||||
implementation:
|
||||
container:
|
||||
image: us-docker.pkg.dev/vertex-ai/ready-to-go-image-classification/image-components:v0.1
|
||||
# command is a list of strings (command-line arguments).
|
||||
# The YAML language has two syntaxes for lists and you can use either of them.
|
||||
# Here we use the "flow syntax" - comma-separated strings inside square brackets.
|
||||
command: [
|
||||
python3,
|
||||
# Path of the program inside the container
|
||||
/pipelines/component/src/training_component.py,
|
||||
--preprocessed-training-data-path,
|
||||
{inputPath: preprocessed_training_data_path},
|
||||
--preprocessed-validation-data-path,
|
||||
{inputPath: preprocessed_validation_data_path},
|
||||
--model-path,
|
||||
{inputPath: model_path},
|
||||
--trained-model-path,
|
||||
{outputPath: trained_model_path},
|
||||
--optimizer-name,
|
||||
{inputValue: optimizer_name},
|
||||
--loss-function-name,
|
||||
{inputValue: loss_function_name},
|
||||
--number-of-epochs,
|
||||
{inputValue: number_of_epochs},
|
||||
--seed,
|
||||
{inputValue: seed},
|
||||
--batch-size,
|
||||
{inputValue: batch_size},
|
||||
--metric-names,
|
||||
{inputValue: metric_names},
|
||||
--optimizer-parameters,
|
||||
{inputValue: optimizer_parameters},
|
||||
--loss-function-parameters,
|
||||
{inputValue: loss_function_parameters},
|
||||
]
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
name: Transcode imagedataset tfrecord from csv
|
||||
description: |
|
||||
Transcodes CSV Data into TFRecord file of TFExamples.
|
||||
Args:
|
||||
csv_image_data_path (str):
|
||||
Path to the CSV image data. Data must include 'image_filepath' (Path to image file) and
|
||||
'image_label' (output for a prediction) fields.
|
||||
class_names (Sequence[str]):
|
||||
Sequence of strings of categories for classification corresponding to input data.
|
||||
tfrecord_image_data_path (str):
|
||||
Output path for the TFRecord image data. Data will be formatted as 'label' (encoded image
|
||||
label), and 'image_raw' (the binary string of the image data).
|
||||
inputs:
|
||||
- {name: csv_image_data_path, type: ImageDatasetCSV, description: Input path for the
|
||||
CSV image data}
|
||||
- {name: class_names, type: 'typing.List[str]', description: List of class names corresponding
|
||||
to the input image data}
|
||||
outputs:
|
||||
- {name: tfrecord_image_data_path, type: ImageDatasetTFRecord, description: Output
|
||||
path for the TFRecord image data}
|
||||
implementation:
|
||||
container:
|
||||
image: us-docker.pkg.dev/vertex-ai/ready-to-go-image-classification/image-components:v0.1
|
||||
# command is a list of strings (command-line arguments).
|
||||
# The YAML language has two syntaxes for lists and you can use either of them.
|
||||
# Here we use the "flow syntax" - comma-separated strings inside square brackets.
|
||||
command: [
|
||||
python3,
|
||||
# Path of the program inside the container
|
||||
/pipelines/component/src/transcoding_csv_component.py,
|
||||
--csv-image-data-path,
|
||||
{inputPath: csv_image_data_path},
|
||||
--tfrecord-image-data-path,
|
||||
{outputPath: tfrecord_image_data_path},
|
||||
--class-names,
|
||||
{inputValue: class_names},
|
||||
]
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
name: Transcode imagedataset tfrecord from jsonlines
|
||||
description: |
|
||||
Transcodes JSONL Data into TFRecord file of TFExamples.
|
||||
Args:
|
||||
jsonl_image_data_path (str):
|
||||
Input path for the JSONL image data
|
||||
Path to the JSONL image data. Each line corresponds to a JSON input describing an image.
|
||||
Schema follows AutoML image classification JSONL format
|
||||
https://cloud.google.com/vertex-ai/docs/image-data/classification/prepare-data#json-lines.
|
||||
class_names (Sequence[str]):
|
||||
Sequence of strings of categories for classification corresponding to input data.
|
||||
tfrecord_image_data_path (str):
|
||||
Output path for the TFRecord image data. Data will be formatted as 'label' (encoded image
|
||||
label), and 'image_raw' (the binary string of the image data).
|
||||
inputs:
|
||||
- {name: jsonl_image_data_path, type: ImageDatasetJsonLines, description: Input path
|
||||
for the JSONL image data}
|
||||
- {name: class_names, type: 'typing.List[str]', description: List of class names corresponding
|
||||
to the input image data}
|
||||
outputs:
|
||||
- {name: tfrecord_image_data_path, type: ImageDatasetTFRecord, description: Output
|
||||
path for the TFRecord image data}
|
||||
implementation:
|
||||
container:
|
||||
image: us-docker.pkg.dev/vertex-ai/ready-to-go-image-classification/image-components:v0.1
|
||||
# command is a list of strings (command-line arguments).
|
||||
# The YAML language has two syntaxes for lists and you can use either of them.
|
||||
# Here we use the "flow syntax" - comma-separated strings inside square brackets.
|
||||
command: [
|
||||
python3,
|
||||
# Path of the program inside the container
|
||||
/pipelines/component/src/transcoding_jsonl_component.py,
|
||||
--jsonl-image-data-path,
|
||||
{inputPath: jsonl_image_data_path},
|
||||
--tfrecord-image-data-path,
|
||||
{outputPath: tfrecord_image_data_path},
|
||||
--class-names,
|
||||
{inputValue: class_names},
|
||||
]
|
||||
+113
@@ -0,0 +1,113 @@
|
||||
name: Binarize column using Pandas on CSV data
|
||||
metadata:
|
||||
annotations: {author: Alexey Volkov <alexey.volkov@ark-kun.com>, canonical_location: 'https://raw.githubusercontent.com/Ark-kun/pipeline_components/master/components/pandas/Binarize_column/in_CSV_format/component.yaml'}
|
||||
inputs:
|
||||
- {name: table, type: CSV}
|
||||
- {name: column_name, type: String}
|
||||
- {name: predicate, type: String, default: '> 0', optional: true}
|
||||
- {name: new_column_name, type: String, optional: true}
|
||||
- name: keep_original_column
|
||||
type: Boolean
|
||||
default: "False"
|
||||
optional: true
|
||||
outputs:
|
||||
- {name: transformed_table, type: CSV}
|
||||
implementation:
|
||||
container:
|
||||
image: python:3.9
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- (PIP_DISABLE_PIP_VERSION_CHECK=1 python3 -m pip install --quiet --no-warn-script-location
|
||||
'pandas==1.4.3' || PIP_DISABLE_PIP_VERSION_CHECK=1 python3 -m pip install --quiet
|
||||
--no-warn-script-location 'pandas==1.4.3' --user) && "$0" "$@"
|
||||
- sh
|
||||
- -ec
|
||||
- |
|
||||
program_path=$(mktemp)
|
||||
printf "%s" "$0" > "$program_path"
|
||||
python3 -u "$program_path" "$@"
|
||||
- |
|
||||
def _make_parent_dirs_and_return_path(file_path: str):
|
||||
import os
|
||||
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
||||
return file_path
|
||||
|
||||
def binarize_column_using_Pandas_on_CSV_data(
|
||||
table_path,
|
||||
transformed_table_path,
|
||||
column_name,
|
||||
predicate = "> 0",
|
||||
new_column_name = None,
|
||||
keep_original_column = False,
|
||||
):
|
||||
import pandas
|
||||
|
||||
df = pandas.read_csv(table_path).convert_dtypes()
|
||||
original_series = df[column_name]
|
||||
|
||||
# Dynamically executing the predicate code
|
||||
# Variable namespace for code execution
|
||||
namespace = dict(x=original_series)
|
||||
# I though that there should be no space before `predicate` so that "dot" predicate methods like ".between(min, max)" work.
|
||||
# However Python allows spaces before dot: `df .isna()`.
|
||||
# So having a space is not a problem
|
||||
transform_code = f"""new_series_boolean = x {predicate}"""
|
||||
# Note: exec() takes no keyword arguments
|
||||
# exec(__source=transform_code, __globals=namespace)
|
||||
exec(transform_code, namespace)
|
||||
new_series_boolean = namespace["new_series_boolean"]
|
||||
|
||||
# There are multiple ways to convert boolean column to integer.
|
||||
# .apply(int) might be faster. https://stackoverflow.com/a/49804868/1497385
|
||||
# TODO: Do a proper benchmark.
|
||||
new_series = new_series_boolean.apply(int)
|
||||
# new_series = new_series_boolean.astype(int)
|
||||
# new_series = new_series_boolean.replace({False: 0, True: 1})
|
||||
|
||||
if new_column_name:
|
||||
df.insert(loc=0, column=new_column_name, value=new_series)
|
||||
if not keep_original_column:
|
||||
df = df.drop(columns=[column_name])
|
||||
else:
|
||||
df[column_name] = new_series
|
||||
|
||||
df.to_csv(transformed_table_path, index=False)
|
||||
|
||||
def _deserialize_bool(s) -> bool:
|
||||
from distutils.util import strtobool
|
||||
return strtobool(s) == 1
|
||||
|
||||
import argparse
|
||||
_parser = argparse.ArgumentParser(prog='Binarize column using Pandas on CSV data', description='')
|
||||
_parser.add_argument("--table", dest="table_path", type=str, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--column-name", dest="column_name", type=str, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--predicate", dest="predicate", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--new-column-name", dest="new_column_name", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--keep-original-column", dest="keep_original_column", type=_deserialize_bool, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--transformed-table", dest="transformed_table_path", type=_make_parent_dirs_and_return_path, required=True, default=argparse.SUPPRESS)
|
||||
_parsed_args = vars(_parser.parse_args())
|
||||
|
||||
_outputs = binarize_column_using_Pandas_on_CSV_data(**_parsed_args)
|
||||
args:
|
||||
- --table
|
||||
- {inputPath: table}
|
||||
- --column-name
|
||||
- {inputValue: column_name}
|
||||
- if:
|
||||
cond: {isPresent: predicate}
|
||||
then:
|
||||
- --predicate
|
||||
- {inputValue: predicate}
|
||||
- if:
|
||||
cond: {isPresent: new_column_name}
|
||||
then:
|
||||
- --new-column-name
|
||||
- {inputValue: new_column_name}
|
||||
- if:
|
||||
cond: {isPresent: keep_original_column}
|
||||
then:
|
||||
- --keep-original-column
|
||||
- {inputValue: keep_original_column}
|
||||
- --transformed-table
|
||||
- {outputPath: transformed_table}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
name: Fill all missing values using Pandas on CSV data
|
||||
metadata:
|
||||
annotations: {author: Alexey Volkov <alexey.volkov@ark-kun.com>, canonical_location: 'https://raw.githubusercontent.com/Ark-kun/pipeline_components/master/components/pandas/Fill_all_missing_values/in_CSV_format/component.yaml'}
|
||||
inputs:
|
||||
- {name: table, type: CSV}
|
||||
- {name: replacement_value, type: String, default: '0', optional: true}
|
||||
- {name: column_names, type: JsonArray, optional: true}
|
||||
outputs:
|
||||
- {name: transformed_table, type: CSV}
|
||||
implementation:
|
||||
container:
|
||||
image: python:3.9
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- (PIP_DISABLE_PIP_VERSION_CHECK=1 python3 -m pip install --quiet --no-warn-script-location
|
||||
'pandas==1.4.1' || PIP_DISABLE_PIP_VERSION_CHECK=1 python3 -m pip install --quiet
|
||||
--no-warn-script-location 'pandas==1.4.1' --user) && "$0" "$@"
|
||||
- sh
|
||||
- -ec
|
||||
- |
|
||||
program_path=$(mktemp)
|
||||
printf "%s" "$0" > "$program_path"
|
||||
python3 -u "$program_path" "$@"
|
||||
- |
|
||||
def _make_parent_dirs_and_return_path(file_path: str):
|
||||
import os
|
||||
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
||||
return file_path
|
||||
|
||||
def fill_all_missing_values_using_Pandas_on_CSV_data(
|
||||
table_path,
|
||||
transformed_table_path,
|
||||
replacement_value = "0",
|
||||
column_names = None,
|
||||
):
|
||||
import pandas
|
||||
|
||||
df = pandas.read_csv(
|
||||
table_path,
|
||||
dtype="string",
|
||||
)
|
||||
|
||||
for column_name in column_names or df.columns:
|
||||
df[column_name] = df[column_name].fillna(value=replacement_value)
|
||||
|
||||
df.to_csv(
|
||||
transformed_table_path, index=False,
|
||||
)
|
||||
|
||||
import json
|
||||
import argparse
|
||||
_parser = argparse.ArgumentParser(prog='Fill all missing values using Pandas on CSV data', description='')
|
||||
_parser.add_argument("--table", dest="table_path", type=str, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--replacement-value", dest="replacement_value", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--column-names", dest="column_names", type=json.loads, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--transformed-table", dest="transformed_table_path", type=_make_parent_dirs_and_return_path, required=True, default=argparse.SUPPRESS)
|
||||
_parsed_args = vars(_parser.parse_args())
|
||||
|
||||
_outputs = fill_all_missing_values_using_Pandas_on_CSV_data(**_parsed_args)
|
||||
args:
|
||||
- --table
|
||||
- {inputPath: table}
|
||||
- if:
|
||||
cond: {isPresent: replacement_value}
|
||||
then:
|
||||
- --replacement-value
|
||||
- {inputValue: replacement_value}
|
||||
- if:
|
||||
cond: {isPresent: column_names}
|
||||
then:
|
||||
- --column-names
|
||||
- {inputValue: column_names}
|
||||
- --transformed-table
|
||||
- {outputPath: transformed_table}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
name: Select columns using Pandas on CSV data
|
||||
metadata:
|
||||
annotations: {author: Alexey Volkov <alexey.volkov@ark-kun.com>, canonical_location: 'https://raw.githubusercontent.com/Ark-kun/pipeline_components/master/components/pandas/Select_columns/in_CSV_format/component.yaml'}
|
||||
inputs:
|
||||
- {name: table, type: CSV}
|
||||
- {name: column_names, type: JsonArray}
|
||||
outputs:
|
||||
- {name: transformed_table, type: CSV}
|
||||
implementation:
|
||||
container:
|
||||
image: python:3.9
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- (PIP_DISABLE_PIP_VERSION_CHECK=1 python3 -m pip install --quiet --no-warn-script-location
|
||||
'pandas==1.4.2' || PIP_DISABLE_PIP_VERSION_CHECK=1 python3 -m pip install --quiet
|
||||
--no-warn-script-location 'pandas==1.4.2' --user) && "$0" "$@"
|
||||
- sh
|
||||
- -ec
|
||||
- |
|
||||
program_path=$(mktemp)
|
||||
printf "%s" "$0" > "$program_path"
|
||||
python3 -u "$program_path" "$@"
|
||||
- |
|
||||
def _make_parent_dirs_and_return_path(file_path: str):
|
||||
import os
|
||||
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
||||
return file_path
|
||||
|
||||
def select_columns_using_Pandas_on_CSV_data(
|
||||
table_path,
|
||||
transformed_table_path,
|
||||
column_names,
|
||||
):
|
||||
import pandas
|
||||
|
||||
df = pandas.read_csv(
|
||||
table_path,
|
||||
dtype="string",
|
||||
)
|
||||
df = df[column_names]
|
||||
df.to_csv(transformed_table_path, index=False)
|
||||
|
||||
import json
|
||||
import argparse
|
||||
_parser = argparse.ArgumentParser(prog='Select columns using Pandas on CSV data', description='')
|
||||
_parser.add_argument("--table", dest="table_path", type=str, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--column-names", dest="column_names", type=json.loads, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--transformed-table", dest="transformed_table_path", type=_make_parent_dirs_and_return_path, required=True, default=argparse.SUPPRESS)
|
||||
_parsed_args = vars(_parser.parse_args())
|
||||
|
||||
_outputs = select_columns_using_Pandas_on_CSV_data(**_parsed_args)
|
||||
args:
|
||||
- --table
|
||||
- {inputPath: table}
|
||||
- --column-names
|
||||
- {inputValue: column_names}
|
||||
- --transformed-table
|
||||
- {outputPath: transformed_table}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
name: Create fully connected tensorflow network
|
||||
description: Creates fully-connected network in Tensorflow SavedModel format
|
||||
metadata:
|
||||
annotations: {author: Alexey Volkov <alexey.volkov@ark-kun.com>, canonical_location: 'https://raw.githubusercontent.com/Ark-kun/pipeline_components/master/components/tensorflow/Create_fully_connected_network/component.yaml'}
|
||||
inputs:
|
||||
- {name: input_size, type: Integer}
|
||||
- {name: hidden_layer_sizes, type: JsonArray, default: '[]', optional: true}
|
||||
- {name: output_size, type: Integer, default: '1', optional: true}
|
||||
- {name: activation_name, type: String, default: relu, optional: true}
|
||||
- {name: output_activation_name, type: String, optional: true}
|
||||
- {name: random_seed, type: Integer, default: '0', optional: true}
|
||||
outputs:
|
||||
- {name: model, type: TensorflowSavedModel}
|
||||
implementation:
|
||||
container:
|
||||
image: tensorflow/tensorflow:2.7.0
|
||||
command:
|
||||
- sh
|
||||
- -ec
|
||||
- |
|
||||
program_path=$(mktemp)
|
||||
printf "%s" "$0" > "$program_path"
|
||||
python3 -u "$program_path" "$@"
|
||||
- |
|
||||
def _make_parent_dirs_and_return_path(file_path: str):
|
||||
import os
|
||||
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
||||
return file_path
|
||||
|
||||
def create_fully_connected_tensorflow_network(
|
||||
input_size,
|
||||
model_path,
|
||||
hidden_layer_sizes = [],
|
||||
output_size = 1,
|
||||
activation_name = "relu",
|
||||
output_activation_name = None,
|
||||
random_seed = 0,
|
||||
):
|
||||
"""Creates fully-connected network in Tensorflow SavedModel format"""
|
||||
import tensorflow as tf
|
||||
tf.random.set_seed(seed=random_seed)
|
||||
|
||||
model = tf.keras.models.Sequential()
|
||||
model.add(tf.keras.Input(shape=(input_size,)))
|
||||
for layer_size in hidden_layer_sizes:
|
||||
model.add(tf.keras.layers.Dense(units=layer_size, activation=activation_name))
|
||||
# The last layer is left without activation
|
||||
model.add(tf.keras.layers.Dense(units=output_size, activation=output_activation_name))
|
||||
|
||||
print(model.summary())
|
||||
|
||||
# Using tf.keras.models.save_model instead of tf.saved_model.save to prevent downstream error:
|
||||
#tf.saved_model.save(model, model_path)
|
||||
# ValueError: Unable to create a Keras model from this SavedModel.
|
||||
# This SavedModel was created with `tf.saved_model.save`, and lacks the Keras metadata.
|
||||
# Please save your Keras model by calling `model.save`or `tf.keras.models.save_model`.
|
||||
# See https://github.com/keras-team/keras/issues/16451
|
||||
tf.keras.models.save_model(model, model_path)
|
||||
|
||||
import json
|
||||
import argparse
|
||||
_parser = argparse.ArgumentParser(prog='Create fully connected tensorflow network', description='Creates fully-connected network in Tensorflow SavedModel format')
|
||||
_parser.add_argument("--input-size", dest="input_size", type=int, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--hidden-layer-sizes", dest="hidden_layer_sizes", type=json.loads, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--output-size", dest="output_size", type=int, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--activation-name", dest="activation_name", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--output-activation-name", dest="output_activation_name", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--random-seed", dest="random_seed", type=int, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--model", dest="model_path", type=_make_parent_dirs_and_return_path, required=True, default=argparse.SUPPRESS)
|
||||
_parsed_args = vars(_parser.parse_args())
|
||||
|
||||
_outputs = create_fully_connected_tensorflow_network(**_parsed_args)
|
||||
args:
|
||||
- --input-size
|
||||
- {inputValue: input_size}
|
||||
- if:
|
||||
cond: {isPresent: hidden_layer_sizes}
|
||||
then:
|
||||
- --hidden-layer-sizes
|
||||
- {inputValue: hidden_layer_sizes}
|
||||
- if:
|
||||
cond: {isPresent: output_size}
|
||||
then:
|
||||
- --output-size
|
||||
- {inputValue: output_size}
|
||||
- if:
|
||||
cond: {isPresent: activation_name}
|
||||
then:
|
||||
- --activation-name
|
||||
- {inputValue: activation_name}
|
||||
- if:
|
||||
cond: {isPresent: output_activation_name}
|
||||
then:
|
||||
- --output-activation-name
|
||||
- {inputValue: output_activation_name}
|
||||
- if:
|
||||
cond: {isPresent: random_seed}
|
||||
then:
|
||||
- --random-seed
|
||||
- {inputValue: random_seed}
|
||||
- --model
|
||||
- {outputPath: model}
|
||||
@@ -0,0 +1,100 @@
|
||||
name: Predict with TensorFlow model on CSV data
|
||||
metadata:
|
||||
annotations: {author: Alexey Volkov <alexey.volkov@ark-kun.com>, canonical_location: 'https://raw.githubusercontent.com/Ark-kun/pipeline_components/master/components/tensorflow/Predict/on_CSV/component.yaml'}
|
||||
inputs:
|
||||
- {name: dataset, type: CSV}
|
||||
- {name: model, type: TensorflowSavedModel}
|
||||
- {name: label_column_name, type: String, optional: true}
|
||||
- {name: batch_size, type: Integer, default: '1000', optional: true}
|
||||
outputs:
|
||||
- {name: predictions}
|
||||
implementation:
|
||||
container:
|
||||
image: tensorflow/tensorflow:2.9.1
|
||||
command:
|
||||
- sh
|
||||
- -ec
|
||||
- |
|
||||
program_path=$(mktemp)
|
||||
printf "%s" "$0" > "$program_path"
|
||||
python3 -u "$program_path" "$@"
|
||||
- |
|
||||
def _make_parent_dirs_and_return_path(file_path: str):
|
||||
import os
|
||||
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
||||
return file_path
|
||||
|
||||
def predict_with_TensorFlow_model_on_CSV_data(
|
||||
dataset_path,
|
||||
model_path,
|
||||
predictions_path,
|
||||
label_column_name = None,
|
||||
batch_size = 1000,
|
||||
):
|
||||
import numpy
|
||||
import tensorflow as tf
|
||||
|
||||
model = tf.saved_model.load(export_dir=model_path)
|
||||
|
||||
dataset = tf.data.experimental.make_csv_dataset(
|
||||
file_pattern=dataset_path,
|
||||
batch_size=batch_size,
|
||||
label_name=label_column_name,
|
||||
header=True,
|
||||
num_epochs=1,
|
||||
shuffle=False,
|
||||
ignore_errors=False,
|
||||
)
|
||||
|
||||
def stack_feature_batches(features_batch):
|
||||
# Need to stack individual feature columns to create a single feature tensor
|
||||
# Need to cast all column tensor types to float to prevent errors.
|
||||
list_of_feature_batches = list(
|
||||
tf.cast(x=feature_batch, dtype=tf.float32)
|
||||
for feature_batch in features_batch.values()
|
||||
)
|
||||
return tf.stack(list_of_feature_batches, axis=-1)
|
||||
|
||||
def transform_features_and_drop_labels(features_batch, labels_batch):
|
||||
return stack_feature_batches(features_batch)
|
||||
|
||||
dataset_map_fn = (
|
||||
transform_features_and_drop_labels
|
||||
if label_column_name
|
||||
else stack_feature_batches
|
||||
)
|
||||
|
||||
dataset = dataset.map(dataset_map_fn)
|
||||
|
||||
with open(predictions_path, "w") as predictions_file:
|
||||
for features_batch in dataset:
|
||||
predictions_tensor = model(features_batch)
|
||||
numpy.savetxt(predictions_file, predictions_tensor.numpy())
|
||||
|
||||
import argparse
|
||||
_parser = argparse.ArgumentParser(prog='Predict with TensorFlow model on CSV data', description='')
|
||||
_parser.add_argument("--dataset", dest="dataset_path", type=str, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--model", dest="model_path", type=str, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--label-column-name", dest="label_column_name", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--batch-size", dest="batch_size", type=int, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--predictions", dest="predictions_path", type=_make_parent_dirs_and_return_path, required=True, default=argparse.SUPPRESS)
|
||||
_parsed_args = vars(_parser.parse_args())
|
||||
|
||||
_outputs = predict_with_TensorFlow_model_on_CSV_data(**_parsed_args)
|
||||
args:
|
||||
- --dataset
|
||||
- {inputPath: dataset}
|
||||
- --model
|
||||
- {inputPath: model}
|
||||
- if:
|
||||
cond: {isPresent: label_column_name}
|
||||
then:
|
||||
- --label-column-name
|
||||
- {inputValue: label_column_name}
|
||||
- if:
|
||||
cond: {isPresent: batch_size}
|
||||
then:
|
||||
- --batch-size
|
||||
- {inputValue: batch_size}
|
||||
- --predictions
|
||||
- {outputPath: predictions}
|
||||
+170
@@ -0,0 +1,170 @@
|
||||
name: Train model using Keras on CSV
|
||||
metadata:
|
||||
annotations: {author: Alexey Volkov <alexey.volkov@ark-kun.com>, canonical_location: 'https://raw.githubusercontent.com/Ark-kun/pipeline_components/master/components/tensorflow/Train_model_using_Keras/on_CSV/component.yaml'}
|
||||
inputs:
|
||||
- {name: training_data, type: CSV}
|
||||
- {name: model, type: TensorflowSavedModel}
|
||||
- {name: label_column_name, type: String}
|
||||
- {name: loss_function_name, type: String, default: mean_squared_error, optional: true}
|
||||
- {name: number_of_epochs, type: Integer, default: '1', optional: true}
|
||||
- {name: learning_rate, type: Float, default: '0.1', optional: true}
|
||||
- {name: optimizer_name, type: String, default: Adadelta, optional: true}
|
||||
- {name: optimizer_parameters, type: JsonObject, optional: true}
|
||||
- {name: batch_size, type: Integer, default: '32', optional: true}
|
||||
- {name: metric_names, type: JsonArray, optional: true}
|
||||
- {name: random_seed, type: Integer, default: '0', optional: true}
|
||||
outputs:
|
||||
- {name: trained_model, type: TensorflowSavedModel}
|
||||
implementation:
|
||||
container:
|
||||
image: tensorflow/tensorflow:2.8.0
|
||||
command:
|
||||
- sh
|
||||
- -ec
|
||||
- |
|
||||
program_path=$(mktemp)
|
||||
printf "%s" "$0" > "$program_path"
|
||||
python3 -u "$program_path" "$@"
|
||||
- |
|
||||
def _make_parent_dirs_and_return_path(file_path: str):
|
||||
import os
|
||||
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
||||
return file_path
|
||||
|
||||
def train_model_using_Keras_on_CSV(
|
||||
training_data_path,
|
||||
model_path,
|
||||
trained_model_path,
|
||||
label_column_name,
|
||||
loss_function_name = "mean_squared_error",
|
||||
number_of_epochs = 1,
|
||||
learning_rate = 0.1,
|
||||
optimizer_name = "Adadelta",
|
||||
optimizer_parameters = None,
|
||||
batch_size = 32,
|
||||
metric_names = None,
|
||||
random_seed = 0,
|
||||
):
|
||||
import tensorflow as tf
|
||||
tf.random.set_seed(seed=random_seed)
|
||||
|
||||
# Loading model using Keras. Model loaded using TensorFlow does not have .fit.
|
||||
#model = tf.saved_model.load(export_dir=model_path)
|
||||
keras_model = tf.keras.models.load_model(filepath=model_path)
|
||||
|
||||
optimizer_parameters = optimizer_parameters or {}
|
||||
optimizer_parameters["learning_rate"] = learning_rate
|
||||
optimizer_config = {
|
||||
"class_name": optimizer_name,
|
||||
"config": optimizer_parameters,
|
||||
}
|
||||
optimizer = tf.keras.optimizers.get(optimizer_config)
|
||||
loss = tf.keras.losses.get(loss_function_name)
|
||||
|
||||
training_dataset = tf.data.experimental.make_csv_dataset(
|
||||
file_pattern=training_data_path,
|
||||
batch_size=batch_size,
|
||||
label_name=label_column_name,
|
||||
header=True,
|
||||
# Need to specify num_epochs=1 otherwise the training becomes infinite
|
||||
num_epochs=1,
|
||||
shuffle=True,
|
||||
shuffle_seed=random_seed,
|
||||
ignore_errors=True,
|
||||
)
|
||||
def stack_feature_batches(features_batch, labels_batch):
|
||||
# Need to stack individual feature columns to create a single feature tensor
|
||||
# Need to cast all column tensor types to float to prevent error:
|
||||
# TypeError: Tensors in list passed to 'values' of 'Pack' Op have types [int32, float32, float32, int32, int32] that don't all match.
|
||||
list_of_feature_batches = list(tf.cast(x=feature_batch, dtype=tf.float32) for feature_batch in features_batch.values())
|
||||
return tf.stack(list_of_feature_batches, axis=-1), labels_batch
|
||||
|
||||
training_dataset = training_dataset.map(stack_feature_batches)
|
||||
|
||||
# Need to compile the model to prevent error:
|
||||
# ValueError: No gradients provided for any variable: [..., ...].
|
||||
keras_model.compile(
|
||||
optimizer=optimizer,
|
||||
loss=loss,
|
||||
metrics=metric_names,
|
||||
)
|
||||
keras_model.fit(
|
||||
training_dataset,
|
||||
epochs=number_of_epochs,
|
||||
)
|
||||
|
||||
# Using tf.keras.models.save_model instead of tf.saved_model.save to prevent downstream error:
|
||||
#tf.saved_model.save(keras_model, trained_model_path)
|
||||
# ValueError: Unable to create a Keras model from this SavedModel.
|
||||
# This SavedModel was created with `tf.saved_model.save`, and lacks the Keras metadata.
|
||||
# Please save your Keras model by calling `model.save`or `tf.keras.models.save_model`.
|
||||
# See https://github.com/keras-team/keras/issues/16451
|
||||
tf.keras.models.save_model(keras_model, trained_model_path)
|
||||
|
||||
import json
|
||||
import argparse
|
||||
_parser = argparse.ArgumentParser(prog='Train model using Keras on CSV', description='')
|
||||
_parser.add_argument("--training-data", dest="training_data_path", type=str, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--model", dest="model_path", type=str, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--label-column-name", dest="label_column_name", type=str, required=True, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--loss-function-name", dest="loss_function_name", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--number-of-epochs", dest="number_of_epochs", type=int, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--learning-rate", dest="learning_rate", type=float, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--optimizer-name", dest="optimizer_name", type=str, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--optimizer-parameters", dest="optimizer_parameters", type=json.loads, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--batch-size", dest="batch_size", type=int, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--metric-names", dest="metric_names", type=json.loads, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--random-seed", dest="random_seed", type=int, required=False, default=argparse.SUPPRESS)
|
||||
_parser.add_argument("--trained-model", dest="trained_model_path", type=_make_parent_dirs_and_return_path, required=True, default=argparse.SUPPRESS)
|
||||
_parsed_args = vars(_parser.parse_args())
|
||||
|
||||
_outputs = train_model_using_Keras_on_CSV(**_parsed_args)
|
||||
args:
|
||||
- --training-data
|
||||
- {inputPath: training_data}
|
||||
- --model
|
||||
- {inputPath: model}
|
||||
- --label-column-name
|
||||
- {inputValue: label_column_name}
|
||||
- if:
|
||||
cond: {isPresent: loss_function_name}
|
||||
then:
|
||||
- --loss-function-name
|
||||
- {inputValue: loss_function_name}
|
||||
- if:
|
||||
cond: {isPresent: number_of_epochs}
|
||||
then:
|
||||
- --number-of-epochs
|
||||
- {inputValue: number_of_epochs}
|
||||
- if:
|
||||
cond: {isPresent: learning_rate}
|
||||
then:
|
||||
- --learning-rate
|
||||
- {inputValue: learning_rate}
|
||||
- if:
|
||||
cond: {isPresent: optimizer_name}
|
||||
then:
|
||||
- --optimizer-name
|
||||
- {inputValue: optimizer_name}
|
||||
- if:
|
||||
cond: {isPresent: optimizer_parameters}
|
||||
then:
|
||||
- --optimizer-parameters
|
||||
- {inputValue: optimizer_parameters}
|
||||
- if:
|
||||
cond: {isPresent: batch_size}
|
||||
then:
|
||||
- --batch-size
|
||||
- {inputValue: batch_size}
|
||||
- if:
|
||||
cond: {isPresent: metric_names}
|
||||
then:
|
||||
- --metric-names
|
||||
- {inputValue: metric_names}
|
||||
- if:
|
||||
cond: {isPresent: random_seed}
|
||||
then:
|
||||
- --random-seed
|
||||
- {inputValue: random_seed}
|
||||
- --trained-model
|
||||
- {outputPath: trained_model}
|
||||
@@ -0,0 +1,33 @@
|
||||
# PyTorch Efficient Training Examples
|
||||
|
||||
This folder provides PyTorch efficient training examples using ResNet-50 and ImageNet data.
|
||||
|
||||
## Requirements
|
||||
|
||||
```shell
|
||||
pip install --upgrade pip
|
||||
pip install -r requirements.txt
|
||||
```
|
||||
|
||||
## Description
|
||||
|
||||
* resnet.py - Train ResNet-50 on single GPU.
|
||||
* resnet_dp.py - Train ResNet-50 on single node multiple GPUs with `DataParallel` strategy.
|
||||
* resnet_ddp.py - Train ResNet-50 on single node multiple GPUs with `DistributedDataParallel` strategy.
|
||||
* resnet_ddp_wds.py - Train ResNet-50 on single node multiple GPUs with `DistributedDataParallel` strategy and `Webdataset`.
|
||||
* resnet_fsdp.py - Train ResNet-50 on single node multiple GPUs with `FullyShardedDataParallel` strategy.
|
||||
* resnet_fsdp_wds.py - Train ResNet-50 on single node multiple GPUs with `FullyShardedDataParallel` strategy and `Webdataset`.
|
||||
* shard_imagenet.py - Shard ImagNet individual files into `tar` files.
|
||||
|
||||
## Benchmark
|
||||
|
||||
When run the benchmark on Nvidia T4 GPUs using ImageNet validation dataset, you can get the result like:
|
||||
Strategy | Seconds/Epoch - Local Data | Seconds/Epoch - Cloud Data
|
||||
---------------------- | -------------------------- | --------------------------
|
||||
On 1 GPU | 489 | 804 (2x slower)
|
||||
On 4 GPUs (DP) | 157 | 738 (5x slower)
|
||||
On 4 GPUs (DDP) | 134 | 432 (3x slower)
|
||||
On 4 GPUs (DDP + WDS) | 131 | 133 (same performance)
|
||||
On 4 GPUs (FSDP) | 139 | 353 (3x slower)
|
||||
On 4 GPUs (FSDP + WDS) | 138 | 135 (same performance)
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
webdataset == 0.2.26
|
||||
@@ -0,0 +1,197 @@
|
||||
# Copyright 2022 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the \"License\");
|
||||
# you may not use this file except in compliance with the License.\n",
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an \"AS IS\" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Train resnet on single GPU."""
|
||||
|
||||
import argparse
|
||||
import time
|
||||
|
||||
from PIL import Image
|
||||
import torch
|
||||
from torch import nn
|
||||
import torchmetrics
|
||||
import torchvision
|
||||
from torchvision.models import resnet50
|
||||
|
||||
|
||||
class ImageFolder(torchvision.datasets.ImageFolder):
|
||||
"""Class for loading imagenet."""
|
||||
|
||||
def __init__(self, image_list_file, transform=None, target_transform=None):
|
||||
self.samples = self._make_dataset(image_list_file)
|
||||
self.loader = self._loader
|
||||
|
||||
self.imgs = self.samples
|
||||
self.targets = [s[1] for s in self.samples]
|
||||
|
||||
self.transform = transform
|
||||
self.target_transform = target_transform
|
||||
|
||||
def _make_dataset(self, image_list_file):
|
||||
items = []
|
||||
with open(image_list_file, 'r') as f:
|
||||
for line in f:
|
||||
item = line.strip().split(' ')
|
||||
items.append((item[0], int(item[1])))
|
||||
return items
|
||||
|
||||
def _loader(self, image_path):
|
||||
with open(image_path, 'rb') as f:
|
||||
img = Image.open(f)
|
||||
img = img.convert('RGB')
|
||||
return img
|
||||
|
||||
|
||||
def train(model, device, dataloader, optimizer):
|
||||
model.train()
|
||||
for image, target in dataloader:
|
||||
image, target = image.to(device), target.to(device)
|
||||
pred = model(image)
|
||||
# pred.shape (N, C), target.shape (N)
|
||||
loss = nn.functional.cross_entropy(pred, target)
|
||||
optimizer.zero_grad()
|
||||
loss.backward()
|
||||
optimizer.step()
|
||||
return loss
|
||||
|
||||
|
||||
def evaluate(model, device, dataloader, metric):
|
||||
model.eval()
|
||||
with torch.no_grad():
|
||||
for image, target in dataloader:
|
||||
image, target = image.to(device), target.to(device)
|
||||
pred = model(image)
|
||||
metric.update(pred, target)
|
||||
accuracy = metric.compute()
|
||||
metric.reset()
|
||||
return accuracy
|
||||
|
||||
|
||||
def run_training(args):
|
||||
"""Run training and evaluation."""
|
||||
# Create model.
|
||||
model = resnet50(weights=None)
|
||||
model = model.to(args.device)
|
||||
|
||||
# Create train dataloader.
|
||||
train_dataset = ImageFolder(
|
||||
image_list_file=args.train_data_path,
|
||||
transform=torchvision.transforms.Compose([
|
||||
torchvision.transforms.RandomResizedCrop(224),
|
||||
torchvision.transforms.RandomHorizontalFlip(),
|
||||
torchvision.transforms.ToTensor(),
|
||||
torchvision.transforms.Normalize(
|
||||
mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
|
||||
]))
|
||||
train_dataloader = torch.utils.data.DataLoader(
|
||||
dataset=train_dataset,
|
||||
batch_size=args.train_batch_size,
|
||||
shuffle=True,
|
||||
num_workers=args.dataloader_num_workers,
|
||||
pin_memory=True)
|
||||
print(f'Train dataloader | samples: {len(train_dataloader.dataset)}, '
|
||||
f'num workers: {train_dataloader.num_workers}, '
|
||||
f'batch size: {args.train_batch_size}, '
|
||||
f'batches/epoch: {len(train_dataloader)}')
|
||||
|
||||
# Create eval dataloader.
|
||||
eval_dataset = ImageFolder(
|
||||
image_list_file=args.eval_data_path,
|
||||
transform=torchvision.transforms.Compose([
|
||||
torchvision.transforms.Resize(256),
|
||||
torchvision.transforms.CenterCrop(224),
|
||||
torchvision.transforms.ToTensor(),
|
||||
torchvision.transforms.Normalize(
|
||||
mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
|
||||
]))
|
||||
eval_dataloader = torch.utils.data.DataLoader(
|
||||
dataset=eval_dataset,
|
||||
batch_size=args.eval_batch_size,
|
||||
shuffle=False,
|
||||
num_workers=args.dataloader_num_workers,
|
||||
pin_memory=True,
|
||||
drop_last=True)
|
||||
print(f'Eval dataloader | samples: {len(eval_dataloader.dataset)}, '
|
||||
f'num workers: {eval_dataloader.num_workers}, '
|
||||
f'batch size: {args.eval_batch_size}, '
|
||||
f'batches/epoch: {len(eval_dataloader)}')
|
||||
|
||||
# Optimizer.
|
||||
optimizer = torch.optim.SGD(model.parameters(), 0.1)
|
||||
|
||||
# Main loop.
|
||||
metric = torchmetrics.classification.Accuracy(top_k=1).to(args.device)
|
||||
for epoch in range(1, args.epochs + 1):
|
||||
print(f'Running epoch {epoch}')
|
||||
|
||||
start = time.time()
|
||||
train(model, args.device, train_dataloader, optimizer)
|
||||
end = time.time()
|
||||
print(f'Training finished in {(end - start):>0.3f} seconds')
|
||||
|
||||
start = time.time()
|
||||
evaluate(model, args.device, eval_dataloader, metric)
|
||||
end = time.time()
|
||||
print(f'Evaluation finished in {(end - start):>0.3f} seconds')
|
||||
print('Done')
|
||||
|
||||
|
||||
def create_args():
|
||||
"""Create main args."""
|
||||
parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
parser.add_argument(
|
||||
'--epochs',
|
||||
default=1,
|
||||
type=int,
|
||||
help='number of total epochs to run')
|
||||
parser.add_argument(
|
||||
'--dataloader_num_workers',
|
||||
default=2,
|
||||
type=int,
|
||||
help='number of workders for dataloader')
|
||||
parser.add_argument(
|
||||
'--train_data_path',
|
||||
default='',
|
||||
type=str,
|
||||
help='path to training data')
|
||||
parser.add_argument(
|
||||
'--train_batch_size',
|
||||
default=32,
|
||||
type=int,
|
||||
help='batch size for training')
|
||||
parser.add_argument(
|
||||
'--eval_data_path',
|
||||
default='',
|
||||
type=str,
|
||||
help='path to evaluation data')
|
||||
parser.add_argument(
|
||||
'--eval_batch_size',
|
||||
default=32,
|
||||
type=int,
|
||||
help='batch size for evaluation')
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
|
||||
def main():
|
||||
args = create_args()
|
||||
args.device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
|
||||
|
||||
print('Launch job on 1 GPU')
|
||||
run_training(args)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,234 @@
|
||||
# Copyright 2022 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the \"License\");
|
||||
# you may not use this file except in compliance with the License.\n",
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an \"AS IS\" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Train resnet on multiple GPUs with DDP."""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import time
|
||||
|
||||
from PIL import Image
|
||||
import torch
|
||||
from torch import nn
|
||||
import torch.distributed as dist
|
||||
import torch.multiprocessing as mp
|
||||
import torchmetrics
|
||||
import torchvision
|
||||
from torchvision.models import resnet50
|
||||
|
||||
|
||||
class ImageFolder(torchvision.datasets.ImageFolder):
|
||||
"""Class for loading imagenet."""
|
||||
|
||||
def __init__(self, image_list_file, transform=None, target_transform=None):
|
||||
self.samples = self._make_dataset(image_list_file)
|
||||
self.loader = self._loader
|
||||
|
||||
self.imgs = self.samples
|
||||
self.targets = [s[1] for s in self.samples]
|
||||
|
||||
self.transform = transform
|
||||
self.target_transform = target_transform
|
||||
|
||||
def _make_dataset(self, image_list_file):
|
||||
items = []
|
||||
with open(image_list_file, 'r') as f:
|
||||
for line in f:
|
||||
item = line.strip().split(' ')
|
||||
items.append((item[0], int(item[1])))
|
||||
return items
|
||||
|
||||
def _loader(self, image_path):
|
||||
with open(image_path, 'rb') as f:
|
||||
img = Image.open(f)
|
||||
img = img.convert('RGB')
|
||||
return img
|
||||
|
||||
|
||||
def train(model, device, dataloader, optimizer):
|
||||
model.train()
|
||||
for image, target in dataloader:
|
||||
image = image.to(device, non_blocking=True)
|
||||
target = target.to(device, non_blocking=True)
|
||||
pred = model(image)
|
||||
# pred.shape (N, C), target.shape (N)
|
||||
loss = nn.functional.cross_entropy(pred, target)
|
||||
optimizer.zero_grad()
|
||||
loss.backward()
|
||||
optimizer.step()
|
||||
return loss
|
||||
|
||||
|
||||
def evaluate(model, device, dataloader, metric):
|
||||
model.eval()
|
||||
with torch.no_grad():
|
||||
for image, target in dataloader:
|
||||
image = image.to(device, non_blocking=True)
|
||||
target = target.to(device, non_blocking=True)
|
||||
pred = model(image)
|
||||
metric.update(pred, target)
|
||||
accuracy = metric.compute()
|
||||
metric.reset()
|
||||
return accuracy
|
||||
|
||||
|
||||
def worker(gpu, args):
|
||||
"""Run training and evaluation."""
|
||||
# Init process group.
|
||||
print(f'Initiating process {gpu}')
|
||||
dist.init_process_group(
|
||||
backend='nccl',
|
||||
init_method='env://',
|
||||
world_size=args.gpus,
|
||||
rank=gpu)
|
||||
|
||||
# Create model.
|
||||
model = resnet50(weights=None)
|
||||
torch.cuda.set_device(gpu)
|
||||
model.to(args.device)
|
||||
model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model)
|
||||
model = nn.parallel.DistributedDataParallel(model, device_ids=[gpu])
|
||||
|
||||
# Create train dataloader.
|
||||
train_dataset = ImageFolder(
|
||||
image_list_file=args.train_data_path,
|
||||
transform=torchvision.transforms.Compose([
|
||||
torchvision.transforms.RandomResizedCrop(224),
|
||||
torchvision.transforms.RandomHorizontalFlip(),
|
||||
torchvision.transforms.ToTensor(),
|
||||
torchvision.transforms.Normalize(
|
||||
mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
|
||||
]))
|
||||
train_sampler = torch.utils.data.distributed.DistributedSampler(
|
||||
train_dataset, num_replicas=args.gpus, rank=gpu)
|
||||
train_dataloader = torch.utils.data.DataLoader(
|
||||
dataset=train_dataset,
|
||||
batch_size=args.train_batch_size,
|
||||
shuffle=False,
|
||||
num_workers=args.dataloader_num_workers,
|
||||
pin_memory=True,
|
||||
sampler=train_sampler)
|
||||
if gpu == 0:
|
||||
print(f'Train dataloader | samples: {len(train_dataloader.dataset)}, '
|
||||
f'num workers: {train_dataloader.num_workers}, '
|
||||
f'global batch size: {args.train_batch_size * args.gpus}, '
|
||||
f'batches/epoch: {len(train_dataloader)}')
|
||||
|
||||
# Create eval dataloader.
|
||||
eval_dataset = ImageFolder(
|
||||
image_list_file=args.eval_data_path,
|
||||
transform=torchvision.transforms.Compose([
|
||||
torchvision.transforms.Resize(256),
|
||||
torchvision.transforms.CenterCrop(224),
|
||||
torchvision.transforms.ToTensor(),
|
||||
torchvision.transforms.Normalize(
|
||||
mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
|
||||
]))
|
||||
eval_sampler = torch.utils.data.distributed.DistributedSampler(
|
||||
eval_dataset, num_replicas=args.gpus, rank=gpu)
|
||||
eval_dataloader = torch.utils.data.DataLoader(
|
||||
dataset=eval_dataset,
|
||||
batch_size=args.eval_batch_size,
|
||||
shuffle=False,
|
||||
num_workers=args.dataloader_num_workers,
|
||||
pin_memory=True,
|
||||
drop_last=True,
|
||||
sampler=eval_sampler)
|
||||
if gpu == 0:
|
||||
print(f'Eval dataloader | samples: {len(eval_dataloader.dataset)}, '
|
||||
f'num workers: {eval_dataloader.num_workers}, '
|
||||
f'batch size: {args.eval_batch_size}, '
|
||||
f'batches/epoch: {len(eval_dataloader)}')
|
||||
|
||||
# Optimizer.
|
||||
optimizer = torch.optim.SGD(model.parameters(), 0.1)
|
||||
|
||||
# Main loop.
|
||||
metric = torchmetrics.classification.Accuracy(top_k=1).to(args.device)
|
||||
for epoch in range(1, args.epochs + 1):
|
||||
if gpu == 0:
|
||||
print(f'Running epoch {epoch}')
|
||||
train_sampler.set_epoch(epoch)
|
||||
|
||||
start = time.time()
|
||||
train(model, args.device, train_dataloader, optimizer)
|
||||
end = time.time()
|
||||
if gpu == 0:
|
||||
print(f'Training finished in {(end - start):>0.3f} seconds')
|
||||
|
||||
start = time.time()
|
||||
evaluate(model, args.device, eval_dataloader, metric)
|
||||
end = time.time()
|
||||
if gpu == 0:
|
||||
print(f'Evaluation finished in {(end - start):>0.3f} seconds')
|
||||
|
||||
if gpu == 0:
|
||||
print('Done')
|
||||
|
||||
|
||||
def create_args():
|
||||
"""Create main args."""
|
||||
parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
parser.add_argument(
|
||||
'--gpus',
|
||||
default=4,
|
||||
type=int,
|
||||
help='number of gpus to use')
|
||||
parser.add_argument(
|
||||
'--epochs',
|
||||
default=1,
|
||||
type=int,
|
||||
help='number of total epochs to run')
|
||||
parser.add_argument(
|
||||
'--dataloader_num_workers',
|
||||
default=2,
|
||||
type=int,
|
||||
help='number of workders for dataloader')
|
||||
parser.add_argument(
|
||||
'--train_data_path',
|
||||
default='',
|
||||
type=str,
|
||||
help='path to training data')
|
||||
parser.add_argument(
|
||||
'--train_batch_size',
|
||||
default=32,
|
||||
type=int,
|
||||
help='batch size for training per gpu')
|
||||
parser.add_argument(
|
||||
'--eval_data_path',
|
||||
default='',
|
||||
type=str,
|
||||
help='path to evaluation data')
|
||||
parser.add_argument(
|
||||
'--eval_batch_size',
|
||||
default=32,
|
||||
type=int,
|
||||
help='batch size for evaluation per gpu')
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
|
||||
def main():
|
||||
args = create_args()
|
||||
os.environ['MASTER_ADDR'] = 'localhost'
|
||||
os.environ['MASTER_PORT'] = '8888'
|
||||
|
||||
args.device = 'cuda' if torch.cuda.is_available() else 'cpu'
|
||||
print(f'Launch job on {args.gpus} GPUs with DDP')
|
||||
mp.spawn(worker, nprocs=args.gpus, args=(args,))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,249 @@
|
||||
# Copyright 2022 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the \"License\");
|
||||
# you may not use this file except in compliance with the License.\n",
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an \"AS IS\" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Train resnet on multiple GPUs with DDP."""
|
||||
|
||||
import argparse
|
||||
import functools
|
||||
import itertools
|
||||
import math
|
||||
import os
|
||||
import time
|
||||
|
||||
import torch
|
||||
from torch import nn
|
||||
import torch.distributed as dist
|
||||
import torch.multiprocessing as mp
|
||||
import torchmetrics
|
||||
from torchvision.models import resnet50
|
||||
from torchvision.transforms import transforms
|
||||
import webdataset as wds
|
||||
|
||||
|
||||
def wds_split(src, rank, world_size):
|
||||
"""Shards split function for webdataset."""
|
||||
# The context of caller of this function is within multiple processes
|
||||
# (by DDP world_size) and multiple workers (by dataloader_num_workers).
|
||||
# So we totally have (world_size * num_workers) workers for processing data.
|
||||
# NOTE: Raw data should be sharded to enough shards to make sure one process
|
||||
# can handle at least one shard, otherwise the process may hang.
|
||||
worker_id = 0
|
||||
num_workers = 1
|
||||
worker_info = torch.utils.data.get_worker_info()
|
||||
if worker_info:
|
||||
worker_id = worker_info.id
|
||||
num_workers = worker_info.num_workers
|
||||
for s in itertools.islice(src, rank * num_workers + worker_id, None,
|
||||
world_size * num_workers):
|
||||
yield s
|
||||
|
||||
|
||||
def identity(x):
|
||||
return x
|
||||
|
||||
|
||||
def create_wds_dataloader(rank, args, mode):
|
||||
"""Create webdataset dataset and dataloader."""
|
||||
if mode == 'train':
|
||||
transform = transforms.Compose([
|
||||
transforms.RandomResizedCrop(224),
|
||||
transforms.RandomHorizontalFlip(),
|
||||
transforms.ToTensor(),
|
||||
transforms.Normalize(
|
||||
mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
|
||||
])
|
||||
data_path = args.train_data_path
|
||||
data_size = args.train_data_size
|
||||
batch_size_local = args.train_batch_size
|
||||
batch_size_global = args.train_batch_size * args.gpus
|
||||
# Since webdataset disallows partial batch, we pad the last batch for train.
|
||||
batches = int(math.ceil(data_size / batch_size_global))
|
||||
else:
|
||||
transform = transforms.Compose([
|
||||
transforms.Resize(256),
|
||||
transforms.CenterCrop(224),
|
||||
transforms.ToTensor(),
|
||||
transforms.Normalize(
|
||||
mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
|
||||
])
|
||||
data_path = args.eval_data_path
|
||||
data_size = args.eval_data_size
|
||||
batch_size_local = args.eval_batch_size
|
||||
batch_size_global = args.eval_batch_size * args.gpus
|
||||
# Since webdataset disallows partial batch, we drop the last batch for eval.
|
||||
batches = int(data_size / batch_size_global)
|
||||
|
||||
dataset = wds.DataPipeline(
|
||||
wds.SimpleShardList(data_path),
|
||||
functools.partial(wds_split, rank=rank, world_size=args.gpus),
|
||||
wds.tarfile_to_samples(),
|
||||
wds.decode('pil'),
|
||||
wds.to_tuple('jpg;png;jpeg cls'),
|
||||
wds.map_tuple(transform, identity),
|
||||
wds.batched(batch_size_local, partial=False),
|
||||
)
|
||||
num_workers = args.dataloader_num_workers
|
||||
dataloader = wds.WebLoader(
|
||||
dataset=dataset,
|
||||
batch_size=None,
|
||||
shuffle=False,
|
||||
num_workers=num_workers,
|
||||
persistent_workers=True if num_workers > 0 else False,
|
||||
pin_memory=True).repeat(nbatches=batches)
|
||||
print(f'{mode} dataloader | samples: {data_size}, '
|
||||
f'num_workers: {num_workers}, '
|
||||
f'local batch size: {batch_size_local}, '
|
||||
f'global batch size: {batch_size_global}, '
|
||||
f'batches: {batches}')
|
||||
return dataloader
|
||||
|
||||
|
||||
def train(model, device, dataloader, optimizer):
|
||||
model.train()
|
||||
for image, target in dataloader:
|
||||
image = image.to(device, non_blocking=True)
|
||||
target = target.to(device, non_blocking=True)
|
||||
pred = model(image)
|
||||
# pred.shape (N, C), target.shape (N)
|
||||
loss = nn.functional.cross_entropy(pred, target)
|
||||
optimizer.zero_grad()
|
||||
loss.backward()
|
||||
optimizer.step()
|
||||
return loss
|
||||
|
||||
|
||||
def evaluate(model, device, dataloader, metric):
|
||||
model.eval()
|
||||
with torch.no_grad():
|
||||
for image, target in dataloader:
|
||||
image = image.to(device, non_blocking=True)
|
||||
target = target.to(device, non_blocking=True)
|
||||
pred = model(image)
|
||||
metric.update(pred, target)
|
||||
accuracy = metric.compute()
|
||||
metric.reset()
|
||||
return accuracy
|
||||
|
||||
|
||||
def worker(gpu, args):
|
||||
"""Run training and evaluation."""
|
||||
# Init process group.
|
||||
print(f'Initiating process {gpu}')
|
||||
dist.init_process_group(
|
||||
backend='nccl',
|
||||
init_method='env://',
|
||||
world_size=args.gpus,
|
||||
rank=gpu)
|
||||
|
||||
# Create model.
|
||||
model = resnet50(weights=None)
|
||||
torch.cuda.set_device(gpu)
|
||||
model.to(args.device)
|
||||
model = torch.nn.SyncBatchNorm.convert_sync_batchnorm(model)
|
||||
model = nn.parallel.DistributedDataParallel(model, device_ids=[gpu])
|
||||
|
||||
# Create dataloader.
|
||||
train_dataloader = create_wds_dataloader(gpu, args, 'train')
|
||||
eval_dataloader = create_wds_dataloader(gpu, args, 'eval')
|
||||
|
||||
# Optimizer.
|
||||
optimizer = torch.optim.SGD(model.parameters(), 0.1)
|
||||
|
||||
# Main loop.
|
||||
metric = torchmetrics.classification.Accuracy(top_k=1).to(args.device)
|
||||
for epoch in range(1, args.epochs + 1):
|
||||
if gpu == 0:
|
||||
print(f'Running epoch {epoch}')
|
||||
|
||||
start = time.time()
|
||||
train(model, args.device, train_dataloader, optimizer)
|
||||
end = time.time()
|
||||
if gpu == 0:
|
||||
print(f'Training finished in {(end - start):>0.3f} seconds')
|
||||
|
||||
start = time.time()
|
||||
evaluate(model, args.device, eval_dataloader, metric)
|
||||
end = time.time()
|
||||
if gpu == 0:
|
||||
print(f'Evaluation finished in {(end - start):>0.3f} seconds')
|
||||
|
||||
if gpu == 0:
|
||||
print('Done')
|
||||
|
||||
|
||||
def create_args():
|
||||
"""Create main args."""
|
||||
parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
parser.add_argument(
|
||||
'--gpus',
|
||||
default=4,
|
||||
type=int,
|
||||
help='number of gpus to use')
|
||||
parser.add_argument(
|
||||
'--epochs',
|
||||
default=1,
|
||||
type=int,
|
||||
help='number of total epochs to run')
|
||||
parser.add_argument(
|
||||
'--dataloader_num_workers',
|
||||
default=2,
|
||||
type=int,
|
||||
help='number of workders for dataloader')
|
||||
parser.add_argument(
|
||||
'--train_data_path',
|
||||
default='',
|
||||
type=str,
|
||||
help='path to training data')
|
||||
parser.add_argument(
|
||||
'--train_batch_size',
|
||||
default=32,
|
||||
type=int,
|
||||
help='batch size for training per gpu')
|
||||
parser.add_argument(
|
||||
'--train_data_size',
|
||||
default=50000,
|
||||
type=int,
|
||||
help='data size for training')
|
||||
parser.add_argument(
|
||||
'--eval_data_path',
|
||||
default='',
|
||||
type=str,
|
||||
help='path to evaluation data')
|
||||
parser.add_argument(
|
||||
'--eval_batch_size',
|
||||
default=32,
|
||||
type=int,
|
||||
help='batch size for evaluation per gpu')
|
||||
parser.add_argument(
|
||||
'--eval_data_size',
|
||||
default=50000,
|
||||
type=int,
|
||||
help='data size for evaluation')
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
|
||||
def main():
|
||||
args = create_args()
|
||||
os.environ['MASTER_ADDR'] = 'localhost'
|
||||
os.environ['MASTER_PORT'] = '8888'
|
||||
|
||||
args.device = 'cuda' if torch.cuda.is_available() else 'cpu'
|
||||
print(f'Launch job on {args.gpus} GPUs with DDP')
|
||||
mp.spawn(worker, nprocs=args.gpus, args=(args,))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,207 @@
|
||||
# Copyright 2022 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the \"License\");
|
||||
# you may not use this file except in compliance with the License.\n",
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an \"AS IS\" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Train resnet on multiple GPUs with DP."""
|
||||
|
||||
import argparse
|
||||
import time
|
||||
|
||||
from PIL import Image
|
||||
import torch
|
||||
from torch import nn
|
||||
import torchmetrics
|
||||
import torchvision
|
||||
from torchvision.models import resnet50
|
||||
|
||||
|
||||
class ImageFolder(torchvision.datasets.ImageFolder):
|
||||
"""Class for loading imagenet."""
|
||||
|
||||
def __init__(self, image_list_file, transform=None, target_transform=None):
|
||||
self.samples = self._make_dataset(image_list_file)
|
||||
self.loader = self._loader
|
||||
|
||||
self.imgs = self.samples
|
||||
self.targets = [s[1] for s in self.samples]
|
||||
|
||||
self.transform = transform
|
||||
self.target_transform = target_transform
|
||||
|
||||
def _make_dataset(self, image_list_file):
|
||||
items = []
|
||||
with open(image_list_file, 'r') as f:
|
||||
for line in f:
|
||||
item = line.strip().split(' ')
|
||||
items.append((item[0], int(item[1])))
|
||||
return items
|
||||
|
||||
def _loader(self, image_path):
|
||||
with open(image_path, 'rb') as f:
|
||||
img = Image.open(f)
|
||||
img = img.convert('RGB')
|
||||
return img
|
||||
|
||||
|
||||
def train(model, device, dataloader, optimizer):
|
||||
model.train()
|
||||
for image, target in dataloader:
|
||||
image, target = image.to(device), target.to(device)
|
||||
pred = model(image)
|
||||
# pred.shape (N, C), target.shape (N)
|
||||
loss = nn.functional.cross_entropy(pred, target)
|
||||
optimizer.zero_grad()
|
||||
loss.backward()
|
||||
optimizer.step()
|
||||
return loss
|
||||
|
||||
|
||||
def evaluate(model, device, dataloader, metric):
|
||||
model.eval()
|
||||
with torch.no_grad():
|
||||
for image, target in dataloader:
|
||||
image, target = image.to(device), target.to(device)
|
||||
pred = model(image)
|
||||
metric.update(pred, target)
|
||||
accuracy = metric.compute()
|
||||
metric.reset()
|
||||
return accuracy
|
||||
|
||||
|
||||
def run_training(args):
|
||||
"""Run training and evaluation."""
|
||||
# Create model.
|
||||
model = resnet50(weights=None)
|
||||
model = nn.DataParallel(model)
|
||||
model = model.to(args.device)
|
||||
|
||||
# Create train dataloader.
|
||||
train_dataset = ImageFolder(
|
||||
image_list_file=args.train_data_path,
|
||||
transform=torchvision.transforms.Compose([
|
||||
torchvision.transforms.RandomResizedCrop(224),
|
||||
torchvision.transforms.RandomHorizontalFlip(),
|
||||
torchvision.transforms.ToTensor(),
|
||||
torchvision.transforms.Normalize(
|
||||
mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
|
||||
]))
|
||||
train_dataloader = torch.utils.data.DataLoader(
|
||||
dataset=train_dataset,
|
||||
batch_size=args.train_batch_size,
|
||||
shuffle=True,
|
||||
num_workers=args.dataloader_num_workers,
|
||||
pin_memory=True)
|
||||
print(f'Train dataloader | samples: {len(train_dataloader.dataset)}, '
|
||||
f'num workers: {train_dataloader.num_workers}, '
|
||||
f'global batch size: {args.train_batch_size}, '
|
||||
f'batches/epoch: {len(train_dataloader)}')
|
||||
|
||||
# Create eval dataloader.
|
||||
eval_dataset = ImageFolder(
|
||||
image_list_file=args.eval_data_path,
|
||||
transform=torchvision.transforms.Compose([
|
||||
torchvision.transforms.Resize(256),
|
||||
torchvision.transforms.CenterCrop(224),
|
||||
torchvision.transforms.ToTensor(),
|
||||
torchvision.transforms.Normalize(
|
||||
mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
|
||||
]))
|
||||
eval_dataloader = torch.utils.data.DataLoader(
|
||||
dataset=eval_dataset,
|
||||
batch_size=args.eval_batch_size,
|
||||
shuffle=False,
|
||||
num_workers=args.dataloader_num_workers,
|
||||
pin_memory=True,
|
||||
drop_last=True)
|
||||
print(f'Eval dataloader | samples: {len(eval_dataloader.dataset)}, '
|
||||
f'num workers: {eval_dataloader.num_workers}, '
|
||||
f'global batch size: {args.eval_batch_size}, '
|
||||
f'batches/epoch: {len(eval_dataloader)}')
|
||||
|
||||
# Optimizer.
|
||||
optimizer = torch.optim.SGD(model.parameters(), 0.1)
|
||||
|
||||
# Main loop.
|
||||
metric = torchmetrics.classification.Accuracy(top_k=1).to(args.device)
|
||||
for epoch in range(1, args.epochs + 1):
|
||||
print(f'Running epoch {epoch}')
|
||||
|
||||
start = time.time()
|
||||
train(model, args.device, train_dataloader, optimizer)
|
||||
end = time.time()
|
||||
print(f'Training finished in {(end - start):>0.3f} seconds')
|
||||
|
||||
start = time.time()
|
||||
evaluate(model, args.device, eval_dataloader, metric)
|
||||
end = time.time()
|
||||
print(f'Evaluation finished in {(end - start):>0.3f} seconds')
|
||||
print('Done')
|
||||
|
||||
|
||||
def create_args():
|
||||
"""Create main args."""
|
||||
parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
parser.add_argument(
|
||||
'--gpus',
|
||||
default=4,
|
||||
type=int,
|
||||
help='number of gpus to use')
|
||||
parser.add_argument(
|
||||
'--epochs',
|
||||
default=1,
|
||||
type=int,
|
||||
help='number of total epochs to run')
|
||||
parser.add_argument(
|
||||
'--dataloader_num_workers',
|
||||
default=2,
|
||||
type=int,
|
||||
help='number of workders for dataloader')
|
||||
parser.add_argument(
|
||||
'--train_data_path',
|
||||
default='',
|
||||
type=str,
|
||||
help='path to training data')
|
||||
parser.add_argument(
|
||||
'--train_batch_size',
|
||||
default=32,
|
||||
type=int,
|
||||
help='batch size for training per gpu')
|
||||
parser.add_argument(
|
||||
'--eval_data_path',
|
||||
default='',
|
||||
type=str,
|
||||
help='path to evaluation data')
|
||||
parser.add_argument(
|
||||
'--eval_batch_size',
|
||||
default=32,
|
||||
type=int,
|
||||
help='batch size for evaluation per gpu')
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
|
||||
def main():
|
||||
args = create_args()
|
||||
|
||||
args.device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
|
||||
args.train_batch_size *= args.gpus
|
||||
args.eval_batch_size *= args.gpus
|
||||
args.dataloader_num_workers *= args.gpus
|
||||
|
||||
print(f'Launch job on {args.gpus} GPU with nn.DataParallel')
|
||||
run_training(args)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,242 @@
|
||||
# Copyright 2022 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the \"License\");
|
||||
# you may not use this file except in compliance with the License.\n",
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an \"AS IS\" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Train resnet on multiple GPUs with FSDP."""
|
||||
|
||||
import argparse
|
||||
import functools
|
||||
import os
|
||||
import time
|
||||
|
||||
from PIL import Image
|
||||
import torch
|
||||
from torch import nn
|
||||
import torch.distributed as dist
|
||||
from torch.distributed.fsdp import FullyShardedDataParallel as FSDP
|
||||
from torch.distributed.fsdp.wrap import size_based_auto_wrap_policy
|
||||
import torch.multiprocessing as mp
|
||||
import torchmetrics
|
||||
import torchvision
|
||||
from torchvision.models import resnet50
|
||||
|
||||
|
||||
class ImageFolder(torchvision.datasets.ImageFolder):
|
||||
"""Class for loading imagenet."""
|
||||
|
||||
def __init__(self, image_list_file, transform=None, target_transform=None):
|
||||
self.samples = self._make_dataset(image_list_file)
|
||||
self.loader = self._loader
|
||||
|
||||
self.imgs = self.samples
|
||||
self.targets = [s[1] for s in self.samples]
|
||||
|
||||
self.transform = transform
|
||||
self.target_transform = target_transform
|
||||
|
||||
def _make_dataset(self, image_list_file):
|
||||
items = []
|
||||
with open(image_list_file, 'r') as f:
|
||||
for line in f:
|
||||
item = line.strip().split(' ')
|
||||
items.append((item[0], int(item[1])))
|
||||
return items
|
||||
|
||||
def _loader(self, image_path):
|
||||
with open(image_path, 'rb') as f:
|
||||
img = Image.open(f)
|
||||
img = img.convert('RGB')
|
||||
return img
|
||||
|
||||
|
||||
def train(model, device, dataloader, optimizer):
|
||||
model.train()
|
||||
for image, target in dataloader:
|
||||
image = image.to(device, non_blocking=True)
|
||||
target = target.to(device, non_blocking=True)
|
||||
pred = model(image)
|
||||
# pred.shape (N, C), target.shape (N)
|
||||
loss = nn.functional.cross_entropy(pred, target)
|
||||
optimizer.zero_grad()
|
||||
loss.backward()
|
||||
optimizer.step()
|
||||
return loss
|
||||
|
||||
|
||||
def evaluate(model, device, dataloader, metric):
|
||||
model.eval()
|
||||
with torch.no_grad():
|
||||
for image, target in dataloader:
|
||||
image = image.to(device, non_blocking=True)
|
||||
target = target.to(device, non_blocking=True)
|
||||
pred = model(image)
|
||||
metric.update(pred, target)
|
||||
accuracy = metric.compute()
|
||||
metric.reset()
|
||||
return accuracy
|
||||
|
||||
|
||||
def worker(gpu, args):
|
||||
"""Run training and evaluation."""
|
||||
# Init process group.
|
||||
print(f'Initiating process {gpu}')
|
||||
dist.init_process_group(
|
||||
backend='nccl',
|
||||
init_method='env://',
|
||||
world_size=args.gpus,
|
||||
rank=gpu)
|
||||
|
||||
# Create train dataloader.
|
||||
train_dataset = ImageFolder(
|
||||
image_list_file=args.train_data_path,
|
||||
transform=torchvision.transforms.Compose([
|
||||
torchvision.transforms.RandomResizedCrop(224),
|
||||
torchvision.transforms.RandomHorizontalFlip(),
|
||||
torchvision.transforms.ToTensor(),
|
||||
torchvision.transforms.Normalize(
|
||||
mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
|
||||
]))
|
||||
train_sampler = torch.utils.data.distributed.DistributedSampler(
|
||||
train_dataset, num_replicas=args.gpus, rank=gpu)
|
||||
train_dataloader = torch.utils.data.DataLoader(
|
||||
dataset=train_dataset,
|
||||
batch_size=args.train_batch_size,
|
||||
shuffle=False,
|
||||
num_workers=args.dataloader_num_workers,
|
||||
pin_memory=True,
|
||||
sampler=train_sampler)
|
||||
if gpu == 0:
|
||||
print(f'Train dataloader | samples: {len(train_dataloader.dataset)}, '
|
||||
f'num workers: {train_dataloader.num_workers}, '
|
||||
f'global batch size: {args.train_batch_size * args.gpus}, '
|
||||
f'batches/epoch: {len(train_dataloader)}')
|
||||
|
||||
# Create eval dataloader.
|
||||
eval_dataset = ImageFolder(
|
||||
image_list_file=args.eval_data_path,
|
||||
transform=torchvision.transforms.Compose([
|
||||
torchvision.transforms.Resize(256),
|
||||
torchvision.transforms.CenterCrop(224),
|
||||
torchvision.transforms.ToTensor(),
|
||||
torchvision.transforms.Normalize(
|
||||
mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
|
||||
]))
|
||||
eval_sampler = torch.utils.data.distributed.DistributedSampler(
|
||||
eval_dataset, num_replicas=args.gpus, rank=gpu)
|
||||
eval_dataloader = torch.utils.data.DataLoader(
|
||||
dataset=eval_dataset,
|
||||
batch_size=args.eval_batch_size,
|
||||
shuffle=False,
|
||||
num_workers=args.dataloader_num_workers,
|
||||
pin_memory=True,
|
||||
drop_last=True,
|
||||
sampler=eval_sampler)
|
||||
if gpu == 0:
|
||||
print(f'Eval dataloader | samples: {len(eval_dataloader.dataset)}, '
|
||||
f'num workers: {eval_dataloader.num_workers}, '
|
||||
f'batch size: {args.eval_batch_size}, '
|
||||
f'batches/epoch: {len(eval_dataloader)}')
|
||||
|
||||
# Wrap policy.
|
||||
my_auto_wrap_policy = functools.partial(
|
||||
size_based_auto_wrap_policy, min_num_params=100)
|
||||
torch.cuda.set_device(gpu)
|
||||
|
||||
# Create model.
|
||||
model = resnet50(weights=None)
|
||||
model.to(args.device)
|
||||
model = FSDP(model, auto_wrap_policy=my_auto_wrap_policy)
|
||||
|
||||
# Optimizer.
|
||||
optimizer = torch.optim.SGD(model.parameters(), 0.1)
|
||||
|
||||
# Main loop.
|
||||
metric = torchmetrics.classification.Accuracy(top_k=1).to(args.device)
|
||||
for epoch in range(1, args.epochs + 1):
|
||||
if gpu == 0:
|
||||
print(f'Running epoch {epoch}')
|
||||
train_sampler.set_epoch(epoch)
|
||||
|
||||
start = time.time()
|
||||
train(model, args.device, train_dataloader, optimizer)
|
||||
end = time.time()
|
||||
if gpu == 0:
|
||||
print(f'Training finished in {(end - start):>0.3f} seconds')
|
||||
|
||||
start = time.time()
|
||||
evaluate(model, args.device, eval_dataloader, metric)
|
||||
end = time.time()
|
||||
if gpu == 0:
|
||||
print(f'Evaluation finished in {(end - start):>0.3f} seconds')
|
||||
|
||||
if gpu == 0:
|
||||
print('Done')
|
||||
dist.destroy_process_group()
|
||||
|
||||
|
||||
def create_args():
|
||||
"""Create main args."""
|
||||
parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
parser.add_argument(
|
||||
'--gpus',
|
||||
default=4,
|
||||
type=int,
|
||||
help='number of gpus to use')
|
||||
parser.add_argument(
|
||||
'--epochs',
|
||||
default=2,
|
||||
type=int,
|
||||
help='number of total epochs to run')
|
||||
parser.add_argument(
|
||||
'--dataloader_num_workers',
|
||||
default=2,
|
||||
type=int,
|
||||
help='number of workders for dataloader')
|
||||
parser.add_argument(
|
||||
'--train_data_path',
|
||||
default='',
|
||||
type=str,
|
||||
help='path to training data')
|
||||
parser.add_argument(
|
||||
'--train_batch_size',
|
||||
default=32,
|
||||
type=int,
|
||||
help='batch size for training per gpu')
|
||||
parser.add_argument(
|
||||
'--eval_data_path',
|
||||
default='',
|
||||
type=str,
|
||||
help='path to evaluation data')
|
||||
parser.add_argument(
|
||||
'--eval_batch_size',
|
||||
default=32,
|
||||
type=int,
|
||||
help='batch size for evaluation per gpu')
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
|
||||
def main():
|
||||
args = create_args()
|
||||
|
||||
os.environ['MASTER_ADDR'] = 'localhost'
|
||||
os.environ['MASTER_PORT'] = '8888'
|
||||
|
||||
args.device = 'cuda' if torch.cuda.is_available() else 'cpu'
|
||||
print(f'Launch job on {args.gpus} GPUs with FSDP')
|
||||
mp.spawn(worker, nprocs=args.gpus, args=(args,))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,240 @@
|
||||
"""Train resnet on multiple GPUs with DDP."""
|
||||
|
||||
import argparse
|
||||
import functools
|
||||
import itertools
|
||||
import math
|
||||
import os
|
||||
import time
|
||||
|
||||
import torch
|
||||
from torch import nn
|
||||
import torch.distributed as dist
|
||||
from torch.distributed.fsdp import FullyShardedDataParallel as FSDP
|
||||
from torch.distributed.fsdp.wrap import size_based_auto_wrap_policy
|
||||
import torch.multiprocessing as mp
|
||||
import torchmetrics
|
||||
from torchvision.models import resnet50
|
||||
from torchvision.transforms import transforms
|
||||
import webdataset as wds
|
||||
|
||||
|
||||
def wds_split(src, rank, world_size):
|
||||
"""Shards split function for webdataset."""
|
||||
# The context of caller of this function is within multiple processes
|
||||
# (by DDP world_size) and multiple workers (by dataloader_num_workers).
|
||||
# So we totally have (world_size * num_workers) workers for processing data.
|
||||
# NOTE: Raw data should be sharded to enough shards to make sure one process
|
||||
# can handle at least one shard, otherwise the process may hang.
|
||||
worker_id = 0
|
||||
num_workers = 1
|
||||
worker_info = torch.utils.data.get_worker_info()
|
||||
if worker_info:
|
||||
worker_id = worker_info.id
|
||||
num_workers = worker_info.num_workers
|
||||
for s in itertools.islice(src, rank * num_workers + worker_id, None,
|
||||
world_size * num_workers):
|
||||
yield s
|
||||
|
||||
|
||||
def identity(x):
|
||||
return x
|
||||
|
||||
|
||||
def create_wds_dataloader(rank, args, mode):
|
||||
"""Create webdataset dataset and dataloader."""
|
||||
if mode == 'train':
|
||||
transform = transforms.Compose([
|
||||
transforms.RandomResizedCrop(224),
|
||||
transforms.RandomHorizontalFlip(),
|
||||
transforms.ToTensor(),
|
||||
transforms.Normalize(
|
||||
mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
|
||||
])
|
||||
data_path = args.train_data_path
|
||||
data_size = args.train_data_size
|
||||
batch_size_local = args.train_batch_size
|
||||
batch_size_global = args.train_batch_size * args.gpus
|
||||
# Since webdataset disallows partial batch, we pad the last batch for train.
|
||||
batches = int(math.ceil(data_size / batch_size_global))
|
||||
else:
|
||||
transform = transforms.Compose([
|
||||
transforms.Resize(256),
|
||||
transforms.CenterCrop(224),
|
||||
transforms.ToTensor(),
|
||||
transforms.Normalize(
|
||||
mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]),
|
||||
])
|
||||
data_path = args.eval_data_path
|
||||
data_size = args.eval_data_size
|
||||
batch_size_local = args.eval_batch_size
|
||||
batch_size_global = args.eval_batch_size * args.gpus
|
||||
# Since webdataset disallows partial batch, we drop the last batch for eval.
|
||||
batches = int(data_size / batch_size_global)
|
||||
|
||||
dataset = wds.DataPipeline(
|
||||
wds.SimpleShardList(data_path),
|
||||
functools.partial(wds_split, rank=rank, world_size=args.gpus),
|
||||
wds.tarfile_to_samples(),
|
||||
wds.decode('pil'),
|
||||
wds.to_tuple('jpg;png;jpeg cls'),
|
||||
wds.map_tuple(transform, identity),
|
||||
wds.batched(batch_size_local, partial=False),
|
||||
)
|
||||
num_workers = args.dataloader_num_workers
|
||||
dataloader = wds.WebLoader(
|
||||
dataset=dataset,
|
||||
batch_size=None,
|
||||
shuffle=False,
|
||||
num_workers=num_workers,
|
||||
persistent_workers=True if num_workers > 0 else False,
|
||||
pin_memory=True).repeat(nbatches=batches)
|
||||
print(f'{mode} dataloader | samples: {data_size}, '
|
||||
f'num_workers: {num_workers}, '
|
||||
f'local batch size: {batch_size_local}, '
|
||||
f'global batch size: {batch_size_global}, '
|
||||
f'batches: {batches}')
|
||||
return dataloader
|
||||
|
||||
|
||||
def train(model, device, dataloader, optimizer):
|
||||
model.train()
|
||||
for image, target in dataloader:
|
||||
image = image.to(device, non_blocking=True)
|
||||
target = target.to(device, non_blocking=True)
|
||||
pred = model(image)
|
||||
# pred.shape (N, C), target.shape (N)
|
||||
loss = nn.functional.cross_entropy(pred, target)
|
||||
optimizer.zero_grad()
|
||||
loss.backward()
|
||||
optimizer.step()
|
||||
return loss
|
||||
|
||||
|
||||
def evaluate(model, device, dataloader, metric):
|
||||
model.eval()
|
||||
with torch.no_grad():
|
||||
for image, target in dataloader:
|
||||
image = image.to(device, non_blocking=True)
|
||||
target = target.to(device, non_blocking=True)
|
||||
pred = model(image)
|
||||
metric.update(pred, target)
|
||||
accuracy = metric.compute()
|
||||
metric.reset()
|
||||
return accuracy
|
||||
|
||||
|
||||
def worker(gpu, args):
|
||||
"""Run training and evaluation."""
|
||||
# Init process group.
|
||||
print(f'Initiating process {gpu}')
|
||||
dist.init_process_group(
|
||||
backend='nccl',
|
||||
init_method='env://',
|
||||
world_size=args.gpus,
|
||||
rank=gpu)
|
||||
|
||||
# Create dataloader.
|
||||
train_dataloader = create_wds_dataloader(gpu, args, 'train')
|
||||
eval_dataloader = create_wds_dataloader(gpu, args, 'eval')
|
||||
|
||||
# Wrap policy.
|
||||
my_auto_wrap_policy = functools.partial(
|
||||
size_based_auto_wrap_policy, min_num_params=100)
|
||||
torch.cuda.set_device(gpu)
|
||||
|
||||
# Create model.
|
||||
model = resnet50(weights=None)
|
||||
model.to(args.device)
|
||||
model = FSDP(model, auto_wrap_policy=my_auto_wrap_policy)
|
||||
|
||||
# Optimizer.
|
||||
optimizer = torch.optim.SGD(model.parameters(), 0.1)
|
||||
|
||||
# Main loop.
|
||||
metric = torchmetrics.classification.Accuracy(top_k=1).to(args.device)
|
||||
for epoch in range(1, args.epochs + 1):
|
||||
if gpu == 0:
|
||||
print(f'Running epoch {epoch}')
|
||||
|
||||
start = time.time()
|
||||
train(model, args.device, train_dataloader, optimizer)
|
||||
end = time.time()
|
||||
if gpu == 0:
|
||||
print(f'Training finished in {(end - start):>0.3f} seconds')
|
||||
|
||||
start = time.time()
|
||||
evaluate(model, args.device, eval_dataloader, metric)
|
||||
end = time.time()
|
||||
if gpu == 0:
|
||||
print(f'Evaluation finished in {(end - start):>0.3f} seconds')
|
||||
|
||||
if gpu == 0:
|
||||
print('Done')
|
||||
|
||||
|
||||
def create_args():
|
||||
"""Create main args."""
|
||||
parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
parser.add_argument(
|
||||
'--gpus',
|
||||
default=4,
|
||||
type=int,
|
||||
help='number of gpus to use')
|
||||
parser.add_argument(
|
||||
'--epochs',
|
||||
default=2,
|
||||
type=int,
|
||||
help='number of total epochs to run')
|
||||
parser.add_argument(
|
||||
'--dataloader_num_workers',
|
||||
default=2,
|
||||
type=int,
|
||||
help='number of workders for dataloader')
|
||||
parser.add_argument(
|
||||
'--train_data_path',
|
||||
default='',
|
||||
type=str,
|
||||
help='path to training data')
|
||||
parser.add_argument(
|
||||
'--train_batch_size',
|
||||
default=32,
|
||||
type=int,
|
||||
help='batch size for training per gpu')
|
||||
parser.add_argument(
|
||||
'--train_data_size',
|
||||
default=50000,
|
||||
type=int,
|
||||
help='data size for training')
|
||||
parser.add_argument(
|
||||
'--eval_data_path',
|
||||
default='',
|
||||
type=str,
|
||||
help='path to evaluation data')
|
||||
parser.add_argument(
|
||||
'--eval_batch_size',
|
||||
default=32,
|
||||
type=int,
|
||||
help='batch size for evaluation per gpu')
|
||||
parser.add_argument(
|
||||
'--eval_data_size',
|
||||
default=50000,
|
||||
type=int,
|
||||
help='data size for evaluation')
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
|
||||
def main():
|
||||
args = create_args()
|
||||
os.environ['MASTER_ADDR'] = 'localhost'
|
||||
os.environ['MASTER_PORT'] = '8888'
|
||||
|
||||
args.device = 'cuda' if torch.cuda.is_available() else 'cpu'
|
||||
print(f'Launch job on {args.gpus} GPUs with FSDP')
|
||||
mp.spawn(worker, nprocs=args.gpus, args=(args,))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,98 @@
|
||||
# Copyright 2022 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the \"License\");
|
||||
# you may not use this file except in compliance with the License.\n",
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an \"AS IS\" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
r"""Main function to shard ImageNet dataset.
|
||||
|
||||
Example usage:
|
||||
python3 -u shard_imagenet.py \
|
||||
--image_list_file=/home/jupyter/data/imagenet/train_list.txt \
|
||||
--output_pattern=/home/jupyter/data/imagenet/validation-%06d.tar
|
||||
"""
|
||||
|
||||
import argparse
|
||||
import os
|
||||
import random
|
||||
import webdataset as wds # version: 0.2.26
|
||||
|
||||
|
||||
# NOTE: only supports writing to local path,
|
||||
# need gcsfuse mounting if want to write to gcs bucket.
|
||||
def write_shards(args):
|
||||
"""Shard individual data files."""
|
||||
output_dir = os.path.dirname(args.output_pattern)
|
||||
if not os.path.isdir(output_dir):
|
||||
os.makedirs(output_dir)
|
||||
|
||||
items = []
|
||||
# Image list file is a text file, each line is a pair (image_path, label).
|
||||
with open(args.image_list_file, 'r') as f:
|
||||
for line in f:
|
||||
item = line.strip().split(' ')
|
||||
items.append((item[0], int(item[1])))
|
||||
# Shuffle items to avoid any large sequences of a single class
|
||||
# in the dataset.
|
||||
random.shuffle(items)
|
||||
|
||||
def _read_image(image_path):
|
||||
with open(image_path, 'rb') as f:
|
||||
return f.read()
|
||||
|
||||
with wds.ShardWriter(pattern=args.output_pattern,
|
||||
maxcount=args.max_images_per_shard,
|
||||
maxsize=args.max_bytes_per_shard) as sink:
|
||||
for i, (image_path, target) in enumerate(items):
|
||||
key = str(i)
|
||||
image = _read_image(image_path)
|
||||
sample = {'__key__': key, 'jpg': image, 'cls': target}
|
||||
sink.write(sample)
|
||||
if len(items) != sink.total:
|
||||
raise ValueError('Items read {} != items written {}'.format(
|
||||
len(items), sink.total))
|
||||
|
||||
|
||||
def create_args():
|
||||
"""Creates arg parser."""
|
||||
parser = argparse.ArgumentParser(
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
||||
parser.add_argument(
|
||||
'--image_list_file',
|
||||
default='',
|
||||
type=str,
|
||||
help='path to image list file')
|
||||
parser.add_argument(
|
||||
'--output_pattern',
|
||||
default='',
|
||||
type=str,
|
||||
help='the pattern for output shards, like /path/to/train-%06d.tar')
|
||||
parser.add_argument(
|
||||
'--max_images_per_shard',
|
||||
default=10 * 1024,
|
||||
type=int,
|
||||
help='max number of images per shard')
|
||||
parser.add_argument(
|
||||
'--max_bytes_per_shard',
|
||||
default=300 * 1024 * 1024,
|
||||
type=int,
|
||||
help='max bytes per shard')
|
||||
args = parser.parse_args()
|
||||
return args
|
||||
|
||||
|
||||
def main():
|
||||
args = create_args()
|
||||
write_shards(args)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,30 @@
|
||||
# PyTorch Deployment on Google Cloud: Text Classification
|
||||
|
||||
**This is an Experimental release**, covered by the Pre-GA Offerings Terms of your Google Cloud Platform [Terms of Service](https://cloud.google.com/terms).
|
||||
|
||||
Experiments are focused on validating a prototype and are not guaranteed to be released. They are not intended for production use or covered by any SLA, support obligation, or deprecation policy and might be subject to backward-incompatible changes.
|
||||
|
||||
**Kindly drop us a note before you run any scale tests.**
|
||||
|
||||
**Do not hesitate to contact vertexai-prediction-preview-feedback@google.com if you have any questions or run into any issues.**
|
||||
|
||||
The projects need to be added to the allowlist in order to deploy PyTorch models using Vertex AI Prediction pre-built PyTorch images. If you are interested in the feature, please send an email to vertexai-prediction-preview-feedback@google.com to provide your project numbers OR project ids.
|
||||
|
||||
## Overview
|
||||
|
||||
In the PyTorch on Google Cloud series of blog posts, we aim to share how to deploy PyTorch models at scale on [Vertex AI](https://cloud.google.com/vertex-ai).
|
||||
|
||||
This tutorial on text classification shows how to deploy a PyTorch based text classification model on [Vertex AI](https://cloud.google.com/vertex-ai/docs/start/client-libraries#python) using Vertex SDK and [`gcloud ai`](https://cloud.google.com/sdk/gcloud/reference/beta/ai).
|
||||
|
||||
## Notebooks
|
||||
|
||||
| <h4>Notebook</h4> | <h4>Description</h4> |
|
||||
| :-------- | :------- |
|
||||
| [pytorch-text-classification-vertex-ai-deploy.ipynb](./pytorch-text-classification-vertex-ai-deploy.ipynb) | Notebook to show deploying a PyTorch model on Vertex AI |
|
||||
|
||||
## Folders
|
||||
|
||||
|
||||
| <h4>Folder Name</h4> | <h4>Description</h4> |
|
||||
| :-------- | :------- |
|
||||
| [`predictor`](./predictor) | Folder with custom prediction handler to deploy a PyTorch model to Vertex Prediction. In the [notebook](./pytorch-text-classification-vertex-ai-deploy.ipynb), this folder is used for deploying a PyTorch model on Vertex AI using Vertex Prediction pre-built PyTorch images |
|
||||
@@ -0,0 +1,91 @@
|
||||
|
||||
import os
|
||||
import json
|
||||
import logging
|
||||
|
||||
import torch
|
||||
from transformers import AutoModelForSequenceClassification, AutoTokenizer
|
||||
from ts.torch_handler.base_handler import BaseHandler
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class TransformersClassifierHandler(BaseHandler):
|
||||
"""
|
||||
The handler takes an input string and returns the classification text
|
||||
based on the serialized transformers checkpoint.
|
||||
"""
|
||||
def __init__(self):
|
||||
super(TransformersClassifierHandler, self).__init__()
|
||||
self.initialized = False
|
||||
|
||||
def initialize(self, ctx):
|
||||
""" Loads the model.pt file and initialized the model object.
|
||||
Instantiates Tokenizer for preprocessor to use
|
||||
Loads labels to name mapping file for post-processing inference response
|
||||
"""
|
||||
self.manifest = ctx.manifest
|
||||
|
||||
properties = ctx.system_properties
|
||||
model_dir = properties.get("model_dir")
|
||||
self.device = torch.device("cuda:" + str(properties.get("gpu_id")) if torch.cuda.is_available() else "cpu")
|
||||
|
||||
# Read model serialize/pt file
|
||||
serialized_file = self.manifest["model"]["serializedFile"]
|
||||
model_pt_path = os.path.join(model_dir, serialized_file)
|
||||
if not os.path.isfile(model_pt_path):
|
||||
raise RuntimeError("Missing the model.pt or pytorch_model.bin file")
|
||||
|
||||
# Load model
|
||||
self.model = AutoModelForSequenceClassification.from_pretrained(model_dir)
|
||||
self.model.to(self.device)
|
||||
self.model.eval()
|
||||
logger.debug('Transformer model from path {0} loaded successfully'.format(model_dir))
|
||||
|
||||
# Ensure to use the same tokenizer used during training
|
||||
self.tokenizer = AutoTokenizer.from_pretrained('bert-base-cased')
|
||||
|
||||
# Read the mapping file, index to object name
|
||||
mapping_file_path = os.path.join(model_dir, "index_to_name.json")
|
||||
|
||||
if os.path.isfile(mapping_file_path):
|
||||
with open(mapping_file_path) as f:
|
||||
self.mapping = json.load(f)
|
||||
else:
|
||||
logger.warning('Missing the index_to_name.json file. Inference output will default.')
|
||||
self.mapping = {"0": "Negative", "1": "Positive"}
|
||||
|
||||
self.initialized = True
|
||||
|
||||
def preprocess(self, data):
|
||||
""" Preprocessing input request by tokenizing
|
||||
Extend with your own preprocessing steps as needed
|
||||
"""
|
||||
text = data[0].get("data")
|
||||
if text is None:
|
||||
text = data[0].get("body")
|
||||
sentences = text.decode('utf-8')
|
||||
logger.info("Received text: '%s'", sentences)
|
||||
|
||||
# Tokenize the texts
|
||||
tokenizer_args = ((sentences,))
|
||||
inputs = self.tokenizer(*tokenizer_args,
|
||||
padding='max_length',
|
||||
max_length=128,
|
||||
truncation=True,
|
||||
return_tensors = "pt")
|
||||
return inputs
|
||||
|
||||
def inference(self, inputs):
|
||||
""" Predict the class of a text using a trained transformer model.
|
||||
"""
|
||||
prediction = self.model(inputs['input_ids'].to(self.device))[0].argmax().item()
|
||||
|
||||
if self.mapping:
|
||||
prediction = self.mapping[str(prediction)]
|
||||
|
||||
logger.info("Model predicted: '%s'", prediction)
|
||||
return [prediction]
|
||||
|
||||
def postprocess(self, inference_output):
|
||||
return inference_output
|
||||
@@ -0,0 +1,5 @@
|
||||
|
||||
{
|
||||
"0": "Negative",
|
||||
"1": "Positive"
|
||||
}
|
||||
+1625
File diff suppressed because it is too large
Load Diff
+13
-13
@@ -658,8 +658,8 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"datasets = load_dataset(\"imdb\")\n",
|
||||
"datasets"
|
||||
"dataset = load_dataset(\"imdb\")\n",
|
||||
"dataset"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -668,7 +668,7 @@
|
||||
"id": "RzfPtOMoIrIu"
|
||||
},
|
||||
"source": [
|
||||
"The `datasets` object itself is [`DatasetDict`](https://huggingface.co/docs/datasets/package_reference/main_classes.html#datasetdict), which contains one key for the training, validation and test set."
|
||||
"The `dataset` object itself is [`DatasetDict`](https://huggingface.co/docs/datasets/package_reference/main_classes.html#datasetdict), which contains one key for the training, validation and test set."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -681,12 +681,12 @@
|
||||
"source": [
|
||||
"print(\n",
|
||||
" \"Total # of rows in training dataset {} and size {:5.2f} MB\".format(\n",
|
||||
" datasets[\"train\"].shape[0], datasets[\"train\"].size_in_bytes / (1024 * 1024)\n",
|
||||
" dataset[\"train\"].shape[0], dataset[\"train\"].size_in_bytes / (1024 * 1024)\n",
|
||||
" )\n",
|
||||
")\n",
|
||||
"print(\n",
|
||||
" \"Total # of rows in test dataset {} and size {:5.2f} MB\".format(\n",
|
||||
" datasets[\"test\"].shape[0], datasets[\"test\"].size_in_bytes / (1024 * 1024)\n",
|
||||
" dataset[\"test\"].shape[0], dataset[\"test\"].size_in_bytes / (1024 * 1024)\n",
|
||||
" )\n",
|
||||
")"
|
||||
]
|
||||
@@ -708,7 +708,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"datasets[\"train\"][0]"
|
||||
"dataset[\"train\"][0]"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -728,7 +728,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"label_list = datasets[\"train\"].unique(\"label\")\n",
|
||||
"label_list = dataset[\"train\"].unique(\"label\")\n",
|
||||
"label_list"
|
||||
]
|
||||
},
|
||||
@@ -779,7 +779,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"show_random_elements(datasets[\"train\"])"
|
||||
"show_random_elements(dataset[\"train\"])"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -883,7 +883,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"example = datasets[\"train\"][4]\n",
|
||||
"example = dataset[\"train\"][4]\n",
|
||||
"print(example)"
|
||||
]
|
||||
},
|
||||
@@ -920,7 +920,7 @@
|
||||
"source": [
|
||||
"# Dataset loading repeated here to make this cell idempotent\n",
|
||||
"# Since we are over-writing datasets variable\n",
|
||||
"datasets = load_dataset(\"imdb\")\n",
|
||||
"dataset = load_dataset(\"imdb\")\n",
|
||||
"\n",
|
||||
"# Mapping labels to ids\n",
|
||||
"# NOTE: We can extract this automatically but the `Unique` method of the datasets\n",
|
||||
@@ -948,7 +948,7 @@
|
||||
"\n",
|
||||
"\n",
|
||||
"# apply preprocessing function to input examples\n",
|
||||
"datasets = datasets.map(preprocess_function, batched=True, load_from_cache_file=True)"
|
||||
"dataset = dataset.map(preprocess_function, batched=True, load_from_cache_file=True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1091,8 +1091,8 @@
|
||||
"trainer = Trainer(\n",
|
||||
" model,\n",
|
||||
" args,\n",
|
||||
" train_dataset=datasets[\"train\"],\n",
|
||||
" eval_dataset=datasets[\"test\"],\n",
|
||||
" train_dataset=dataset[\"train\"],\n",
|
||||
" eval_dataset=dataset[\"test\"],\n",
|
||||
" data_collator=default_data_collator,\n",
|
||||
" tokenizer=tokenizer,\n",
|
||||
" compute_metrics=compute_metrics,\n",
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
# Administrative Howto notes on CI Notebook Ingestion
|
||||
|
||||
|
||||
This readme covers administrative actions that are performed on an as-needed basis.
|
||||
|
||||
## Team: vertex-ai-owners
|
||||
|
||||
Members of the vertex-ai-owners (git team) have administrative privileges.
|
||||
|
||||
|
||||
### Viewing members
|
||||
|
||||
1. Goto the repo
|
||||
2. From top-level menu, select: (Settings -> Collaborators and Teams)[https://github.com/GoogleCloudPlatform/vertex-ai-samples/settings/access]
|
||||
|
||||
|
||||
### Adding a new member
|
||||
|
||||
If another member needs to be added:
|
||||
- Have the new member make a request to join the team.
|
||||
- vertex-ai-owners with the `Maintainer` tag may add the new member.
|
||||
|
||||
|
||||
## Executing CI notebook ingestion checks on a PR
|
||||
|
||||
### Killing a stuck PR
|
||||
|
||||
If the CI notebook ingestion test is stuck (not terminating), you can kill the process by:
|
||||
|
||||
1. Goto the PR
|
||||
2. Under checks, find the entry: vertex-ai-notebook-execution-test (python-docs-samples-tests) In progress —> Summary
|
||||
3. Select Details
|
||||
4. At bottom of details page, select: View more details on Google Cloud Build
|
||||
5. In Cloud Build history page, select Cancel on the top menu bar.
|
||||
|
||||
### Restart a PR test
|
||||
|
||||
There are two ways to restart the CI notebook ingestion tests on an open PR.
|
||||
|
||||
1. In Cloud Build history page, select Rebuild on the top menu bar.
|
||||
2. or, in a comment in the PR enter: /gcbrun
|
||||
|
||||
## Bypassing CI notebook ingestion checks on a PR
|
||||
|
||||
We strongly discourage this, unless there is a compelling reason that would impact the integrity of the quality process.
|
||||
|
||||
There are two ways of doing this. In both cases, you do:
|
||||
|
||||
1. Goto the repo
|
||||
2. From top-level menu, select: (Settings -> Branches)[https://github.com/GoogleCloudPlatform/vertex-ai-samples/settings/branches]
|
||||
3. Under Branch Protection Rules, select the `main` branch.
|
||||
|
||||
### Allowing a member to disable requirements for merging
|
||||
|
||||
Specific member(s) can be assigned the ability to override requirements and merge a PR, by:
|
||||
|
||||
1. Select Edit for the `main` branch in Branch Protection Rules.
|
||||
2. Find the entry "Allow specified actors to bypass required pull requests".
|
||||
3. Under this entry, add the member's git LDAP.
|
||||
4. Select SAVE.
|
||||
5. The "Squash and Merge" button will now be enabled on all PRs viewed by that member.
|
||||
|
||||
### Temporarily disable checks.
|
||||
|
||||
You can disable requirement checks temporarily on all PRs.
|
||||
|
||||
1. Select Edit for the `main` branch in Branch Protection Rules.
|
||||
2. Uncheck:
|
||||
- Require approvals
|
||||
- Require review from Code Owners
|
||||
- Require status checks to pass before merging
|
||||
3. Select SAVE
|
||||
4. Now all members will see a green "Squash and Merge" on all PRs viewed by that member.
|
||||
|
||||
To reverse, recheck the settings you unchecked above.
|
||||
|
||||
## Linting
|
||||
|
||||
To execute the identical lint image locally, from the CI notebook ingestion checks, do:
|
||||
|
||||
1. Goto the corresponding local folder in the repo.
|
||||
2. Run: `docker run -v ${PWD}:/setup/app gcr.io/python-docs-samples-tests/notebook_linter:latest <your_notebooks>`
|
||||
|
||||
## Install dependency issues
|
||||
|
||||
Some packages (and combinations) have dependencies that fail on the virgin VM image used for the CI notebook ingestion test.
|
||||
|
||||
### TFDV
|
||||
|
||||
If the notebook installs and uses tensorflow_data_validation, install as follows:
|
||||
|
||||
! pip3 install -q {USER_FLAG} google-cloud-aiplatform \
|
||||
tensorflow-data-validation \
|
||||
protobuf==3.20.3
|
||||
|
||||
! pip3 install -q {USER_FLAG} cachetools==5.2.0
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -7,24 +7,37 @@
|
||||
/gapic @andrewferlitsch
|
||||
/gapic/custom/showcase_custom_image_classification_online_explain_example_based_api.ipynb @inardini
|
||||
/ml_ops @andrewferlitsch
|
||||
/model_monitoring/* @mco-gh
|
||||
/model_monitoring/* @andrewferlitsch
|
||||
/structured_data/rapid_prototyping_* @rafael-carvalho
|
||||
|
||||
/managed_notebooks/
|
||||
/bigquery_ml/ @polong
|
||||
/sdk/SDK_FBProphet_Forecasting_Online.ipynb @brianchunkang
|
||||
/pipelines/google_cloud_pipeline_components_TPU_model_train_upload_deploy.ipynb @brianchunkang
|
||||
/explainable_ai/SDK_Custom_Container_XAI.ipynb @brianchunkang
|
||||
/matching_engine/sdk_matching_engine_for_indexing.ipynb @ivanmkc
|
||||
/matching_engine/matching_engine_for_indexing.ipynb @yinghsienwu
|
||||
/matching_engine/stream_update_for_matching_engine.ipynb @peterping666
|
||||
/sdk/pytorch_lightning_custom_container_training.ipynb @brianchunkang
|
||||
/sdk/sdk_pytorch_torchrun_custom_container_training_imagenet.ipynb @brianchunkang
|
||||
/tensorboard @yfang1
|
||||
/feature_store @nayaknishant @morgandu
|
||||
/prediction @googleapis/vertex-prediction-team
|
||||
/vertex_endpoints/tf_hub_obj_detection/deploy_tfhub_object_detection_on_vertex_endpoints.ipynb @entrpn
|
||||
/vertex_endpoints/find_ideal_machine_type/find_ideal_machine_type/find_ideal_machine_type.ipynb @entrpn
|
||||
/vertex_endpoints/nvidia-triton/nvidia-triton-custom-container-prediction.ipynb @RajeshThallam
|
||||
/vertex_endpoints/optimized_tensorflow_runtime @vlasenkoalexey
|
||||
/notebooks/community/ml_ops/stage2/get_started_with_visionapi_and_automl.ipynb @mansari
|
||||
/notebooks/community/neo4j/graph_paysim.ipynb @benofben @laeg
|
||||
/notebooks/community/ml_ops/stage1/get_started_with_visionapi_and_vertex_datasets.ipynb @mansari
|
||||
/notebooks/community/pipelines/google_cloud_pipeline_components_bqml_pipeline_demand_forecasting.ipynb @inardini
|
||||
/notebooks/community/pipelines/google_cloud_pipeline_components_bqml_pipeline_demand_forecasting.ipynb @inardini
|
||||
/notebooks/community/cohere/cohere_embedding_with_matching_engine.ipynb @stewart-co
|
||||
/notebooks/community/ml_ops/stage2/get_started_vertex_hpt_r_kernel.ipynb @fhirschmann
|
||||
/notebooks/community/ml_ops/stage2/get_started_vertex_training_r_using_r_kernel.ipynb @fhirschmann
|
||||
/notebooks/community/vertex-ai-samples/notebooks/community/model_registry/vertex_ai_model_registry_bqml_custom_model_versioning.ipynb @inardini
|
||||
/notebooks/community/vertex-ai-samples/notebooks/community/model_registry/vertex_ai_model_registry_automl_model_versioning.ipynb @inardini
|
||||
/notebooks/community/vizier/conversions_vertex_vizier_and_open_source_vizier.ipynb @halio-g
|
||||
/notebooks/community/experiments/vertex_ai_model_experimentation.ipynb @inardini @asobran
|
||||
/notebooks/community/pipelines/google_cloud_pipeline_components_bqml_pipeline_anomaly_detection.ipynb @inardini
|
||||
/notebooks/community/pipelines/google_cloud_pipeline_components_cloud_natural_language_pipeline.ipynb @Narwhalprime
|
||||
/notebooks/community/pipelines/google_cloud_pipeline_components_ready_to_go_text_classification_pipeline.ipynb @Narwhalprime
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
# README
|
||||
|
||||
These are notebooks [Cohere](https://cohere.ai/) built in collaboration with Google. They demonstrate how to use Cohere's modeling API along with Vertex AI.
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+80
-52
@@ -292,6 +292,37 @@
|
||||
" PROJECT_ID = \"python-docs-samples-tests\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d9f118b92c74"
|
||||
},
|
||||
"source": [
|
||||
"#### UUID\n",
|
||||
"\n",
|
||||
"If you are in a live tutorial session, you might be using a shared test account or project. To avoid name collisions between users on resources created, you create a uuid for each instance session, and append it onto the name of resources you create in this tutorial."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "3ee72715c0fd"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import random\n",
|
||||
"import string\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# Generate a uuid of a specifed length(default=8)\n",
|
||||
"def generate_uuid(length: int = 8) -> str:\n",
|
||||
" return \"\".join(random.choices(string.ascii_lowercase + string.digits, k=length))\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"UUID = generate_uuid()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
@@ -478,7 +509,6 @@
|
||||
"from google.cloud.aiplatform_v1.types import \\\n",
|
||||
" featurestore_service as featurestore_service_pb2\n",
|
||||
"from google.cloud.aiplatform_v1.types import io as io_pb2\n",
|
||||
"from google.protobuf.duration_pb2 import Duration\n",
|
||||
"\n",
|
||||
"# Create admin_client for CRUD and data_client for reading feature values.\n",
|
||||
"admin_client = FeaturestoreServiceClient(client_options={\"api_endpoint\": API_ENDPOINT})\n",
|
||||
@@ -542,7 +572,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"FEATURESTORE_ID = \"movie_prediction\"\n",
|
||||
"FEATURESTORE_ID = f\"movie_prediction_{UUID}\"\n",
|
||||
"try:\n",
|
||||
" create_lro = admin_client.create_featurestore(\n",
|
||||
" featurestore_service_pb2.CreateFeaturestoreRequest(\n",
|
||||
@@ -567,7 +597,7 @@
|
||||
"id": "ag8pCQ7rNjVf"
|
||||
},
|
||||
"source": [
|
||||
"You can use [GetFeaturestore](https://cloud.google.com/vertex-ai/docs/reference/rpc/google.cloud.aiplatform.v1beta1#google.cloud.aiplatform.v1beta1.FeaturestoreService.GetFeaturestore) or [ListFeaturestores](https://cloud.google.com/vertex-ai/docs/reference/rpc/google.cloud.aiplatform.v1beta1#google.cloud.aiplatform.v1beta1.FeaturestoreService.ListFeaturestores) to check if the Featurestore was successfully created. The following example gets the details of the Featurestore.\n"
|
||||
"You can use [GetFeaturestore](https://cloud.google.com/vertex-ai/docs/reference/rpc/google.cloud.aiplatform.v1#google.cloud.aiplatform.v1.FeaturestoreService.GetFeaturestore) or [ListFeaturestores](https://cloud.google.com/vertex-ai/docs/reference/rpc/google.cloud.aiplatform.v1#google.cloud.aiplatform.v1.FeaturestoreService.ListFeaturestores) to check if the Featurestore was successfully created. The following example gets the details of the Featurestore.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -589,7 +619,7 @@
|
||||
"id": "018ab19d934f"
|
||||
},
|
||||
"source": [
|
||||
"Auto scaling is available in v1beta1 since v1.11. Below is the example for the `CreateFeaturestoreRequest` with auto-scaling, use it with `aiplatform_v1beta1.FeaturestoreServiceClient` to create Featurestore:"
|
||||
"Auto scaling is available in v1 since v1.11. Below is the example for the `CreateFeaturestoreRequest` with auto-scaling, use it with `aiplatform_v1.FeaturestoreServiceClient` to create Featurestore:"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -600,17 +630,17 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud.aiplatform_v1beta1.types import \\\n",
|
||||
" featurestore as v1beta1_featurestore_pb2\n",
|
||||
"from google.cloud.aiplatform_v1beta1.types import \\\n",
|
||||
" featurestore_service as v1beta1_featurestore_service_pb2\n",
|
||||
"from google.cloud.aiplatform_v1.types import \\\n",
|
||||
" featurestore as v1_featurestore_pb2\n",
|
||||
"from google.cloud.aiplatform_v1.types import \\\n",
|
||||
" featurestore_service as v1_featurestore_service_pb2\n",
|
||||
"\n",
|
||||
"create_featurestore_request = v1beta1_featurestore_service_pb2.CreateFeaturestoreRequest(\n",
|
||||
"create_featurestore_request = v1_featurestore_service_pb2.CreateFeaturestoreRequest(\n",
|
||||
" parent=BASE_RESOURCE_PATH,\n",
|
||||
" featurestore_id=FEATURESTORE_ID,\n",
|
||||
" featurestore=v1beta1_featurestore_pb2.Featurestore(\n",
|
||||
" online_serving_config=v1beta1_featurestore_pb2.Featurestore.OnlineServingConfig(\n",
|
||||
" scaling=v1beta1_featurestore_pb2.Featurestore.OnlineServingConfig.Scaling(\n",
|
||||
" featurestore=v1_featurestore_pb2.Featurestore(\n",
|
||||
" online_serving_config=v1_featurestore_pb2.Featurestore.OnlineServingConfig(\n",
|
||||
" scaling=v1_featurestore_pb2.Featurestore.OnlineServingConfig.Scaling(\n",
|
||||
" min_node_count=1, max_node_count=5\n",
|
||||
" )\n",
|
||||
" ),\n",
|
||||
@@ -681,7 +711,7 @@
|
||||
"id": "dPkT7KDuEvWv"
|
||||
},
|
||||
"source": [
|
||||
"Feature [monitoring](https://cloud.google.com/vertex-ai/docs/featurestore/monitoring) is in preview, so you need to use v1beta1 Python. Import feature analysis is only available through SDK for now."
|
||||
"Feature [monitoring](https://cloud.google.com/vertex-ai/docs/featurestore/monitoring) is in preview, so you need to use v1 Python. Import feature analysis is only available through SDK for now."
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -692,36 +722,35 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud.aiplatform_v1beta1 import \\\n",
|
||||
" FeaturestoreServiceClient as v1beta1_FeaturestoreServiceClient\n",
|
||||
"from google.cloud.aiplatform_v1beta1.types import \\\n",
|
||||
" entity_type as v1beta1_entity_type_pb2\n",
|
||||
"from google.cloud.aiplatform_v1beta1.types import \\\n",
|
||||
" featurestore_monitoring as v1beta1_featurestore_monitoring_pb2\n",
|
||||
"from google.cloud.aiplatform_v1beta1.types import \\\n",
|
||||
" featurestore_service as v1beta1_featurestore_service_pb2\n",
|
||||
"from google.cloud.aiplatform_v1 import \\\n",
|
||||
" FeaturestoreServiceClient as v1_FeaturestoreServiceClient\n",
|
||||
"from google.cloud.aiplatform_v1.types import entity_type as v1_entity_type_pb2\n",
|
||||
"from google.cloud.aiplatform_v1.types import \\\n",
|
||||
" featurestore_monitoring as v1_featurestore_monitoring_pb2\n",
|
||||
"from google.cloud.aiplatform_v1.types import \\\n",
|
||||
" featurestore_service as v1_featurestore_service_pb2\n",
|
||||
"\n",
|
||||
"v1beta1_admin_client = v1beta1_FeaturestoreServiceClient(\n",
|
||||
"v1_admin_client = v1_FeaturestoreServiceClient(\n",
|
||||
" client_options={\"api_endpoint\": API_ENDPOINT}\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Enable import feature analysis for users entity type.\n",
|
||||
"# All Features belonging to this EntityType will by default inherit the monitoring config.\n",
|
||||
"v1beta1_admin_client.update_entity_type(\n",
|
||||
" v1beta1_featurestore_service_pb2.UpdateEntityTypeRequest(\n",
|
||||
" entity_type=v1beta1_entity_type_pb2.EntityType(\n",
|
||||
"v1_admin_client.update_entity_type(\n",
|
||||
" v1_featurestore_service_pb2.UpdateEntityTypeRequest(\n",
|
||||
" entity_type=v1_entity_type_pb2.EntityType(\n",
|
||||
" name=admin_client.entity_type_path(\n",
|
||||
" PROJECT_ID, REGION, FEATURESTORE_ID, \"users\"\n",
|
||||
" ),\n",
|
||||
" monitoring_config=v1beta1_featurestore_monitoring_pb2.FeaturestoreMonitoringConfig(\n",
|
||||
" import_features_analysis=v1beta1_featurestore_monitoring_pb2.FeaturestoreMonitoringConfig.ImportFeaturesAnalysis(\n",
|
||||
" anomaly_detection_baseline=v1beta1_featurestore_monitoring_pb2.FeaturestoreMonitoringConfig.ImportFeaturesAnalysis.Baseline.LATEST_STATS,\n",
|
||||
" state=v1beta1_featurestore_monitoring_pb2.FeaturestoreMonitoringConfig.ImportFeaturesAnalysis.State.ENABLED,\n",
|
||||
" monitoring_config=v1_featurestore_monitoring_pb2.FeaturestoreMonitoringConfig(\n",
|
||||
" import_features_analysis=v1_featurestore_monitoring_pb2.FeaturestoreMonitoringConfig.ImportFeaturesAnalysis(\n",
|
||||
" anomaly_detection_baseline=v1_featurestore_monitoring_pb2.FeaturestoreMonitoringConfig.ImportFeaturesAnalysis.Baseline.LATEST_STATS,\n",
|
||||
" state=v1_featurestore_monitoring_pb2.FeaturestoreMonitoringConfig.ImportFeaturesAnalysis.State.ENABLED,\n",
|
||||
" ),\n",
|
||||
" numerical_threshold_config=v1beta1_featurestore_monitoring_pb2.FeaturestoreMonitoringConfig.ThresholdConfig(\n",
|
||||
" numerical_threshold_config=v1_featurestore_monitoring_pb2.FeaturestoreMonitoringConfig.ThresholdConfig(\n",
|
||||
" value=0.001,\n",
|
||||
" ),\n",
|
||||
" categorical_threshold_config=v1beta1_featurestore_monitoring_pb2.FeaturestoreMonitoringConfig.ThresholdConfig(\n",
|
||||
" categorical_threshold_config=v1_featurestore_monitoring_pb2.FeaturestoreMonitoringConfig.ThresholdConfig(\n",
|
||||
" value=0.001,\n",
|
||||
" ),\n",
|
||||
" ),\n",
|
||||
@@ -736,7 +765,7 @@
|
||||
"id": "85b1f59fbf6d"
|
||||
},
|
||||
"source": [
|
||||
"The easiest way to set up snapshot analysis for now is using [console UI](https://console.cloud.google.com/vertex-ai/features). For completeness, below is example to do this using v1beta1 SDK.\n",
|
||||
"The easiest way to set up snapshot analysis for now is using [console UI](https://console.cloud.google.com/vertex-ai/features). For completeness, below is example to do this using v1 SDK.\n",
|
||||
"\n",
|
||||
"You can view monitoring statistics on [console UI](https://console.cloud.google.com/vertex-ai/features)."
|
||||
]
|
||||
@@ -749,36 +778,35 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.cloud.aiplatform_v1beta1 import \\\n",
|
||||
" FeaturestoreServiceClient as v1beta1_FeaturestoreServiceClient\n",
|
||||
"from google.cloud.aiplatform_v1beta1.types import \\\n",
|
||||
" entity_type as v1beta1_entity_type_pb2\n",
|
||||
"from google.cloud.aiplatform_v1beta1.types import \\\n",
|
||||
" featurestore_monitoring as v1beta1_featurestore_monitoring_pb2\n",
|
||||
"from google.cloud.aiplatform_v1beta1.types import \\\n",
|
||||
" featurestore_service as v1beta1_featurestore_service_pb2\n",
|
||||
"from google.cloud.aiplatform_v1 import \\\n",
|
||||
" FeaturestoreServiceClient as v1_FeaturestoreServiceClient\n",
|
||||
"from google.cloud.aiplatform_v1.types import entity_type as v1_entity_type_pb2\n",
|
||||
"from google.cloud.aiplatform_v1.types import \\\n",
|
||||
" featurestore_monitoring as v1_featurestore_monitoring_pb2\n",
|
||||
"from google.cloud.aiplatform_v1.types import \\\n",
|
||||
" featurestore_service as v1_featurestore_service_pb2\n",
|
||||
"\n",
|
||||
"v1beta1_admin_client = v1beta1_FeaturestoreServiceClient(\n",
|
||||
"v1_admin_client = v1_FeaturestoreServiceClient(\n",
|
||||
" client_options={\"api_endpoint\": API_ENDPOINT}\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Enable snapshot analysis for users entity type.\n",
|
||||
"# All Features belonging to this EntityType will by default inherit the monitoring config.\n",
|
||||
"v1beta1_admin_client.update_entity_type(\n",
|
||||
" v1beta1_featurestore_service_pb2.UpdateEntityTypeRequest(\n",
|
||||
" entity_type=v1beta1_entity_type_pb2.EntityType(\n",
|
||||
"v1_admin_client.update_entity_type(\n",
|
||||
" v1_featurestore_service_pb2.UpdateEntityTypeRequest(\n",
|
||||
" entity_type=v1_entity_type_pb2.EntityType(\n",
|
||||
" name=admin_client.entity_type_path(\n",
|
||||
" PROJECT_ID, REGION, FEATURESTORE_ID, \"users\"\n",
|
||||
" ),\n",
|
||||
" monitoring_config=v1beta1_featurestore_monitoring_pb2.FeaturestoreMonitoringConfig(\n",
|
||||
" snapshot_analysis=v1beta1_featurestore_monitoring_pb2.FeaturestoreMonitoringConfig.SnapshotAnalysis(\n",
|
||||
" monitoring_interval=Duration(seconds=86400), # 1 day\n",
|
||||
" monitoring_config=v1_featurestore_monitoring_pb2.FeaturestoreMonitoringConfig(\n",
|
||||
" snapshot_analysis=v1_featurestore_monitoring_pb2.FeaturestoreMonitoringConfig.SnapshotAnalysis(\n",
|
||||
" monitoring_interval_days=1, # 1 day\n",
|
||||
" staleness_days=30,\n",
|
||||
" ),\n",
|
||||
" numerical_threshold_config=v1beta1_featurestore_monitoring_pb2.FeaturestoreMonitoringConfig.ThresholdConfig(\n",
|
||||
" numerical_threshold_config=v1_featurestore_monitoring_pb2.FeaturestoreMonitoringConfig.ThresholdConfig(\n",
|
||||
" value=0.001,\n",
|
||||
" ),\n",
|
||||
" categorical_threshold_config=v1beta1_featurestore_monitoring_pb2.FeaturestoreMonitoringConfig.ThresholdConfig(\n",
|
||||
" categorical_threshold_config=v1_featurestore_monitoring_pb2.FeaturestoreMonitoringConfig.ThresholdConfig(\n",
|
||||
" value=0.001,\n",
|
||||
" ),\n",
|
||||
" ),\n",
|
||||
@@ -891,8 +919,8 @@
|
||||
"source": [
|
||||
"## Search created features\n",
|
||||
"\n",
|
||||
"While the [ListFeatures](https://cloud.google.com/vertex-ai/docs/reference/rpc/google.cloud.aiplatform.v1beta1#google.cloud.aiplatform.v1beta1.FeaturestoreService.ListFeatures) method allows you to easily view all features of a single\n",
|
||||
"entity type, the [SearchFeatures](https://cloud.google.com/vertex-ai/docs/reference/rpc/google.cloud.aiplatform.v1beta1#google.cloud.aiplatform.v1beta1.FeaturestoreService.SearchFeatures) method searches across all featurestores\n",
|
||||
"While the [ListFeatures](https://cloud.google.com/vertex-ai/docs/reference/rpc/google.cloud.aiplatform.v1#google.cloud.aiplatform.v1.FeaturestoreService.ListFeatures) method allows you to easily view all features of a single\n",
|
||||
"entity type, the [SearchFeatures](https://cloud.google.com/vertex-ai/docs/reference/rpc/google.cloud.aiplatform.v1#google.cloud.aiplatform.v1.FeaturestoreService.SearchFeatures) method searches across all featurestores\n",
|
||||
"and entity types in a given location (such as `us-central1`). This can help you discover features that were created by someone else.\n",
|
||||
"\n",
|
||||
"You can query based on feature properties including feature ID, entity type ID,\n",
|
||||
@@ -1206,7 +1234,7 @@
|
||||
},
|
||||
"source": [
|
||||
"The\n",
|
||||
"[Online Serving APIs](https://cloud.google.com/vertex-ai/docs/reference/rpc/google.cloud.aiplatform.v1beta1#featurestoreonlineservingservice)\n",
|
||||
"[Online Serving APIs](https://cloud.google.com/vertex-ai/docs/reference/rpc/google.cloud.aiplatform.v1#featurestoreonlineservingservice)\n",
|
||||
"lets you serve feature values for small batches of entities. It's designed for latency-sensitive service, such as online model prediction. For example, for a movie service, you might want to quickly shows movies that the current user would most likely watch by using online predictions."
|
||||
]
|
||||
},
|
||||
@@ -8,7 +8,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Copyright 2021 Google LLC\n",
|
||||
"# Copyright 2023 Google LLC\n",
|
||||
"#\n",
|
||||
"# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
|
||||
"# you may not use this file except in compliance with the License.\n",
|
||||
@@ -24,6 +24,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "JAPoU8Sm5E6e"
|
||||
@@ -32,20 +33,28 @@
|
||||
"<table align=\"left\">\n",
|
||||
"\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://console.cloud.google.com/vertex-ai/notebooks/deploy-notebook?download_url=https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/master/notebooks/community/matching_engine/matching_engine_for_indexing.ipynb\">\n",
|
||||
" Run in Google Cloud Notebooks\n",
|
||||
" <a href=\"https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/matching_engine/matching_engine_for_indexing.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/colab-logo-32px.png\" alt=\"Colab logo\">\n",
|
||||
" Run in Colab\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/master/notebooks/community/matching_engine/matching_engine_for_indexing.ipynb\">\n",
|
||||
" <a href=\"https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/matching_engine/matching_engine_for_indexing.ipynb\">\n",
|
||||
" <img src=\"https://cloud.google.com/ml-engine/images/github-logo-32px.png\" alt=\"GitHub logo\">\n",
|
||||
" View on GitHub\n",
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://console.cloud.google.com/vertex-ai/workbench/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/matching_engine/matching_engine_for_indexing.ipynb\">\n",
|
||||
" <img src=\"https://lh3.googleusercontent.com/UiNooY4LUgW_oTvpsNhPpQzsstV5W8F7rYgxgGBD85cWJoLmrOzhVs_ksK_vgx40SHs7jCqkTkCk=e14-rj-sc0xffffff-h130-w32\" alt=\"Vertex AI logo\">\n",
|
||||
" Open in Vertex AI Workbench\n",
|
||||
" </a>\n",
|
||||
" </td> \n",
|
||||
"</table>"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "tvgnzT1CKxrO"
|
||||
@@ -53,25 +62,49 @@
|
||||
"source": [
|
||||
"## Overview\n",
|
||||
"\n",
|
||||
"This example demonstrates how to use the GCP ANN Service. It is a high scale, low latency solution, to find similar vectors (or more specifically \"embeddings\") for a large corpus. Moreover, it is a fully managed offering, further reducing operational overhead. It is built upon [Approximate Nearest Neighbor (ANN) technology](https://ai.googleblog.com/2020/07/announcing-scann-efficient-vector.html) developed by Google Research.\n",
|
||||
"\n",
|
||||
"### Dataset\n",
|
||||
"\n",
|
||||
"The dataset used for this tutorial is the [GloVe dataset](https://nlp.stanford.edu/projects/glove/).\n",
|
||||
"\n",
|
||||
"This example demonstrates how to use Vertex AI Matching Engine. It is a high scale, low latency solution, to find similar vectors (or more specifically \"embeddings\") for a large corpus. Moreover, it is a fully managed offering, further reducing operational overhead. It is built upon [Approximate Nearest Neighbor (ANN) technology](https://ai.googleblog.com/2020/07/announcing-scann-efficient-vector.html) developed by Google Research."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "56e5f9699c6c"
|
||||
},
|
||||
"source": [
|
||||
"### Objective\n",
|
||||
"\n",
|
||||
"In this notebook, you will learn how to create Approximate Nearest Neighbor (ANN) Index, query against indexes, and validate the performance of the index. \n",
|
||||
"\n",
|
||||
"The steps performed include:\n",
|
||||
"\n",
|
||||
"* Create ANN Index and Brute Force Index\n",
|
||||
"* Create a Vertex AI Matching Engine Index and Brute Force Index\n",
|
||||
"* Create an IndexEndpoint with VPC Network\n",
|
||||
"* Deploy ANN Index and Brute Force Index\n",
|
||||
"* Perform online query\n",
|
||||
"* Compute recall\n",
|
||||
"\n",
|
||||
"* Deploy a Vertex AI Matching Engine Index and Brute Force Index\n",
|
||||
"* Perform online queries\n",
|
||||
"* Submit batch queries\n",
|
||||
"* Compute recall metric"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "0aaef374550b"
|
||||
},
|
||||
"source": [
|
||||
"### Dataset\n",
|
||||
"\n",
|
||||
"The dataset used for this tutorial is the [GloVe dataset](https://nlp.stanford.edu/projects/glove/)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "5e2eba58ad71"
|
||||
},
|
||||
"source": [
|
||||
"### Costs \n",
|
||||
"\n",
|
||||
"This tutorial uses billable components of Google Cloud:\n",
|
||||
@@ -87,6 +120,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "S5zc4kbEiYCm"
|
||||
@@ -94,79 +128,47 @@
|
||||
"source": [
|
||||
"## Before you begin\n",
|
||||
"\n",
|
||||
"* **Prepare a VPC network**. To reduce any network overhead that might lead to unnecessary increase in overhead latency, it is best to call the ANN endpoints from your VPC via a direct [VPC Peering](https://cloud.google.com/vertex-ai/docs/general/vpc-peering) connection. The following section describes how to setup a VPC Peering connection if you don't have one. This is a one-time initial setup task. You can also reuse existing VPC network and skip this section.\n",
|
||||
"* **WARNING:** The match service gRPC API (to create online queries against your deployed index) has to be executed in a Google Cloud Notebook instance that is created with the following requirements:\n",
|
||||
" * **In the same region as where your ANN service is deployed** (for example, if you set `REGION = \"us-central1\"` as same as the tutorial, the notebook instance has to be in `us-central1`).\n",
|
||||
" * **Make sure you select the VPC network you created for ANN service** (instead of using the \"default\" one). That is, you will have to create the VPC network below and then create a new notebook instance that uses that VPC. \n",
|
||||
" * If you run it in the colab or a Google Cloud Notebook instance in a different VPC network or region, the gRPC API will fail to peer the network (InactiveRPCError)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "lW2LneA5mmmP"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PROJECT_ID = \"<your_project_id>\" # @param {type:\"string\"}\n",
|
||||
"NETWORK_NAME = \"ucaip-haystack-vpc-network\" # @param {type:\"string\"}\n",
|
||||
"PEERING_RANGE_NAME = \"ucaip-haystack-range\"\n",
|
||||
"### Set up your Google Cloud project\n",
|
||||
"\n",
|
||||
"# Create a VPC network\n",
|
||||
"! gcloud compute networks create {NETWORK_NAME} --bgp-routing-mode=regional --subnet-mode=auto --project={PROJECT_ID}\n",
|
||||
"**The following steps are required, regardless of your notebook environment.**\n",
|
||||
"\n",
|
||||
"# Add necessary firewall rules\n",
|
||||
"! gcloud compute firewall-rules create {NETWORK_NAME}-allow-icmp --network {NETWORK_NAME} --priority 65534 --project {PROJECT_ID} --allow icmp\n",
|
||||
"1. [Select or create a Google Cloud project](https://console.cloud.google.com/cloud-resource-manager). When you first create an account, you get a $300 free credit towards your compute/storage costs.\n",
|
||||
"\n",
|
||||
"! gcloud compute firewall-rules create {NETWORK_NAME}-allow-internal --network {NETWORK_NAME} --priority 65534 --project {PROJECT_ID} --allow all --source-ranges 10.128.0.0/9\n",
|
||||
"2. [Make sure that billing is enabled for your project](https://cloud.google.com/billing/docs/how-to/modify-project).\n",
|
||||
"\n",
|
||||
"! gcloud compute firewall-rules create {NETWORK_NAME}-allow-rdp --network {NETWORK_NAME} --priority 65534 --project {PROJECT_ID} --allow tcp:3389\n",
|
||||
"3. [Enable the Vertex AI API](https://console.cloud.google.com/flows/enableapi?apiid=aiplatform.googleapis.com).\n",
|
||||
"\n",
|
||||
"! gcloud compute firewall-rules create {NETWORK_NAME}-allow-ssh --network {NETWORK_NAME} --priority 65534 --project {PROJECT_ID} --allow tcp:22\n",
|
||||
"\n",
|
||||
"# Reserve IP range\n",
|
||||
"! gcloud compute addresses create {PEERING_RANGE_NAME} --global --prefix-length=16 --network={NETWORK_NAME} --purpose=VPC_PEERING --project={PROJECT_ID} --description=\"peering range for uCAIP Haystack.\"\n",
|
||||
"\n",
|
||||
"# Set up peering with service networking\n",
|
||||
"! gcloud services vpc-peerings connect --service=servicenetworking.googleapis.com --network={NETWORK_NAME} --ranges={PEERING_RANGE_NAME} --project={PROJECT_ID}"
|
||||
"4. If you are running this notebook locally, you need to install the [Cloud SDK](https://cloud.google.com/sdk)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d3uj8x73nDX_"
|
||||
},
|
||||
"source": [
|
||||
"* Authentication: `$ gcloud auth login` rerun this in Google Cloud Notebook terminal when you are logged out and need the credential again."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "i7EUnXsZhAGF"
|
||||
"id": "4700b0e39c5d"
|
||||
},
|
||||
"source": [
|
||||
"### Installation\n",
|
||||
"\n",
|
||||
"Download and install the latest (preview) version of the Vertex SDK for Python."
|
||||
"Download and install the latest version of the Vertex AI SDK for Python."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "wyy5Lbnzg5fi"
|
||||
"id": "014470c6a8de"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! pip install -U git+https://github.com/googleapis/python-aiplatform.git@main-test --user"
|
||||
"! pip install -U git+https://github.com/googleapis/python-aiplatform.git@main --user"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "irSMQn6gZ19l"
|
||||
"id": "cf00462144f7"
|
||||
},
|
||||
"source": [
|
||||
"Install the `h5py` to prepare sample dataset, and the `grpcio-tools` for querying against the index. "
|
||||
@@ -176,11 +178,15 @@
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "-h5sqwOEZ5Yq"
|
||||
"id": "3f3e45e5a1d1"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! pip install -U grpcio-tools --user\n",
|
||||
"! pip install protobuf==3.20.*\n",
|
||||
"! pip install -U google-api-python-client==1.8.0 --user\n",
|
||||
"! pip install -U grpcio-tools==1.47.0 --user\n",
|
||||
"! pip install -U grpcio==1.47.0 --user\n",
|
||||
"! pip install -U grpcio-status==1.47.0 --user\n",
|
||||
"! pip install -U h5py --user"
|
||||
]
|
||||
},
|
||||
@@ -199,7 +205,7 @@
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "EzrelQZ22IZj"
|
||||
"id": "aa1d87bdc90b"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
@@ -215,79 +221,216 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "BF1j6f9HApxa"
|
||||
"id": "249da91c1011"
|
||||
},
|
||||
"source": [
|
||||
"### Set up your Google Cloud project\n",
|
||||
"### Set your project ID\n",
|
||||
"\n",
|
||||
"**The following steps are required, regardless of your notebook environment.**\n",
|
||||
"\n",
|
||||
"1. [Select or create a Google Cloud project](https://console.cloud.google.com/cloud-resource-manager).\n",
|
||||
"\n",
|
||||
"1. [Make sure that billing is enabled for your project](https://cloud.google.com/billing/docs/how-to/modify-project).\n",
|
||||
"\n",
|
||||
"1. [Enable the Vertex AI API and Compute Engine API, and Service Networking API](https://console.cloud.google.com/flows/enableapi?apiid=aiplatform.googleapis.com,compute_component,servicenetworking.googleapis.com).\n",
|
||||
"\n",
|
||||
"1. Enter your project ID in the cell below. Then run the cell to make sure the\n",
|
||||
"Cloud SDK uses the right project for all the commands in this notebook.\n",
|
||||
"\n",
|
||||
"**Note**: Jupyter runs lines prefixed with `!` as shell commands, and it interpolates Python variables prefixed with `$` into these commands."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "WReHDGG5g0XY"
|
||||
},
|
||||
"source": [
|
||||
"#### Set your project ID\n",
|
||||
"\n",
|
||||
"**If you don't know your project ID**, you may be able to get your project ID using `gcloud`."
|
||||
"**If you don't know your project ID**, try the following:\n",
|
||||
"* Run `gcloud config list`.\n",
|
||||
"* Run `gcloud projects list`.\n",
|
||||
"* See the support page: [Locate the project ID](https://support.google.com/googleapi/answer/7014113)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "oM1iC_MfAts1"
|
||||
"id": "10e0d2ee8c45"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"PROJECT_ID = \"[your-project-id]\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"PROJECT_ID = \"\"\n",
|
||||
"\n",
|
||||
"# Get your Google Cloud project ID from gcloud\n",
|
||||
"if not os.getenv(\"IS_TESTING\"):\n",
|
||||
" shell_output=!gcloud config list --format 'value(core.project)' 2>/dev/null\n",
|
||||
" PROJECT_ID = shell_output[0]\n",
|
||||
" print(\"Project ID: \", PROJECT_ID)"
|
||||
"# Set the project id\n",
|
||||
"! gcloud config set project {PROJECT_ID}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "qJYoRfYng0XZ"
|
||||
"id": "3fbfae3ff12a"
|
||||
},
|
||||
"source": [
|
||||
"Otherwise, set your project ID here."
|
||||
"### Set the region\n",
|
||||
"\n",
|
||||
"You can also change the `REGION` variable used by Vertex AI. Learn more about [Vertex AI regions](https://cloud.google.com/vertex-ai/docs/general/locations).\n",
|
||||
"* **WARNING:** \n",
|
||||
" * **Make sure to [choose a region where Vertex AI services are available](https://cloud.google.com/vertex-ai/docs/general/locations#available_regions).**\n",
|
||||
" * **If you use Vertex Workbench, the Notebook instance needs to be in the same region where your Vertex AI Matching Engine is deployed.** (for example, if you set `REGION = \"us-central1\"` as same as the tutorial, the notebook instance has to be in `us-central1`)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "riG_qUokg0XZ"
|
||||
"id": "71c3fd82024e"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if PROJECT_ID == \"\" or PROJECT_ID is None:\n",
|
||||
" PROJECT_ID = \"<your_project_id>\" # @param {type:\"string\"}"
|
||||
"REGION = \"us-central1\" # @param {type: \"string\"}\n",
|
||||
"\n",
|
||||
"# Set the regions\n",
|
||||
"! gcloud config set ai_platform/region {REGION}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "60c5a0f69ad8"
|
||||
},
|
||||
"source": [
|
||||
"### Authenticate your Google Cloud account\n",
|
||||
"\n",
|
||||
"Depending on your Jupyter environment, you may have to manually authenticate. Follow the relevant instructions below."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "d118c95af93f"
|
||||
},
|
||||
"source": [
|
||||
"**1. Vertex AI Workbench**\n",
|
||||
"* Do nothing as you are already authenticated."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "3035286fcdda"
|
||||
},
|
||||
"source": [
|
||||
"**2. Local JupyterLab instance, uncomment and run:**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "455882ec0f11"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# ! gcloud auth login"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "5097f3233d53"
|
||||
},
|
||||
"source": [
|
||||
"**3. Colab, uncomment and run:**"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "2b88e46ac2c8"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# from google.colab import auth\n",
|
||||
"# auth.authenticate_user()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "fcdbb8929927"
|
||||
},
|
||||
"source": [
|
||||
"**4. Service account or other**\n",
|
||||
"* See how to grant Cloud Storage permissions to your service account at https://cloud.google.com/storage/docs/gsutil/commands/iam#ch-examples."
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "7c6eef70dfdb"
|
||||
},
|
||||
"source": [
|
||||
"### Prepare a VPC network\n",
|
||||
"\n",
|
||||
"To reduce any network overhead that might lead to unnecessary increase in overhead latency, it is best to call the Vertex AI Matching Engine endpoints from your VPC via a direct [VPC Peering](https://cloud.google.com/vertex-ai/docs/general/vpc-peering) connection. The following section describes how to setup a VPC Peering connection if you don't have one. This is a one-time initial setup task. You can also reuse existing VPC network and skip this section.\n",
|
||||
"\n",
|
||||
"* **WARNING:** The match service gRPC API (to create online queries against your deployed index) has to be executed in a Google Cloud Notebook instance that is created with the following requirements:\n",
|
||||
" * **Make sure you select the VPC network you created for Vertex AI Matching Engine service** (instead of using the \"default\" one). That is, you will have to create the VPC network below and then create a new notebook instance that uses that VPC. \n",
|
||||
" * If you run it in the colab or a Google Cloud Notebook instance in a different VPC network or region, the gRPC API will fail to peer the network (InactiveRPCError)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "ab38a8cc634c"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"NETWORK_NAME = \"ucaip-haystack-vpc-network\" # @param {type:\"string\"}\n",
|
||||
"PEERING_RANGE_NAME = \"ucaip-haystack-range\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "ec6bf3199835"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Create a VPC network\n",
|
||||
"! gcloud compute networks create {NETWORK_NAME} --bgp-routing-mode=regional --subnet-mode=auto --project={PROJECT_ID}\n",
|
||||
"\n",
|
||||
"# Add necessary firewall rules\n",
|
||||
"! gcloud compute firewall-rules create {NETWORK_NAME}-allow-icmp --network {NETWORK_NAME} --priority 65534 --project {PROJECT_ID} --allow icmp\n",
|
||||
"\n",
|
||||
"! gcloud compute firewall-rules create {NETWORK_NAME}-allow-internal --network {NETWORK_NAME} --priority 65534 --project {PROJECT_ID} --allow all --source-ranges 10.128.0.0/9\n",
|
||||
"\n",
|
||||
"! gcloud compute firewall-rules create {NETWORK_NAME}-allow-rdp --network {NETWORK_NAME} --priority 65534 --project {PROJECT_ID} --allow tcp:3389\n",
|
||||
"\n",
|
||||
"! gcloud compute firewall-rules create {NETWORK_NAME}-allow-ssh --network {NETWORK_NAME} --priority 65534 --project {PROJECT_ID} --allow tcp:22\n",
|
||||
"\n",
|
||||
"# Reserve IP range\n",
|
||||
"! gcloud compute addresses create {PEERING_RANGE_NAME} --global --prefix-length=16 --network={NETWORK_NAME} --purpose=VPC_PEERING --project={PROJECT_ID} --description=\"peering range for uCAIP Haystack.\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ddbace09fe81"
|
||||
},
|
||||
"source": [
|
||||
"Create the VPC Peering. If you are running this from Vertex AI Workbench it is possible you might need your notebook's instance service or user account to have the Service Networking Admin Role"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "d329aa3c54d3"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# Set up peering with service networking\n",
|
||||
"! gcloud services vpc-peerings connect --service=servicenetworking.googleapis.com --network={NETWORK_NAME} --ranges={PEERING_RANGE_NAME} --project={PROJECT_ID}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "zgPO1eR3CYjk"
|
||||
@@ -297,13 +440,11 @@
|
||||
"\n",
|
||||
"**The following steps are required, regardless of your notebook environment.**\n",
|
||||
"\n",
|
||||
"Set the name of your Cloud Storage bucket below. It must be unique across all\n",
|
||||
"Create a storage bucket to store intermediate artifacts such as datasets. Set the name of your Cloud Storage bucket below. It must be unique across all\n",
|
||||
"Cloud Storage buckets.\n",
|
||||
"\n",
|
||||
"You may also change the `REGION` variable, which is used for operations\n",
|
||||
"throughout the rest of this notebook. Make sure to [choose a region where Vertex AI services are\n",
|
||||
"available](https://cloud.google.com/vertex-ai/docs/general/locations#available_regions). You may\n",
|
||||
"not use a Multi-Regional Storage bucket for training with Vertex AI."
|
||||
"* **WARNING:** \n",
|
||||
" * **You may not use a Multi-Regional Storage bucket for training with Vertex AI.**"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -314,8 +455,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"BUCKET_NAME = \"gs://[your-bucket-name]\" # @param {type:\"string\"}\n",
|
||||
"REGION = \"us-central1\" # @param {type:\"string\"}"
|
||||
"BUCKET_NAME = \"gs://[your-bucket-name-unique]\" # @param {type:\"string\"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -328,10 +468,14 @@
|
||||
"source": [
|
||||
"from datetime import datetime\n",
|
||||
"\n",
|
||||
"TIMESTAMP = datetime.now().strftime(\"%Y%m%d%H%M%S\")\n",
|
||||
"UUID = datetime.now().strftime(\"%Y%m%d%H%M%S\")\n",
|
||||
"\n",
|
||||
"if BUCKET_NAME == \"\" or BUCKET_NAME is None or BUCKET_NAME == \"gs://[your-bucket-name]\":\n",
|
||||
" BUCKET_NAME = \"gs://\" + PROJECT_ID + \"aip-\" + TIMESTAMP"
|
||||
"if (\n",
|
||||
" BUCKET_NAME == \"\"\n",
|
||||
" or BUCKET_NAME is None\n",
|
||||
" or BUCKET_NAME == \"gs://[your-bucket-name-unique]\"\n",
|
||||
"):\n",
|
||||
" BUCKET_NAME = \"gs://\" + PROJECT_ID + \"aip-\" + UUID"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -351,7 +495,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! gsutil mb -l $REGION $BUCKET_NAME"
|
||||
"! gsutil mb -l $REGION -p $PROJECT_ID $BUCKET_NAME"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -416,10 +560,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"REGION = \"us-central1\"\n",
|
||||
"ENDPOINT = \"{}-aiplatform.googleapis.com\".format(REGION)\n",
|
||||
"NETWORK_NAME = \"ucaip-haystack-vpc-network\" # @param {type:\"string\"}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"AUTH_TOKEN = !gcloud auth print-access-token\n",
|
||||
"PROJECT_NUMBER = !gcloud projects list --filter=\"PROJECT_ID:'{PROJECT_ID}'\" --format='value(PROJECT_NUMBER)'\n",
|
||||
@@ -429,10 +570,7 @@
|
||||
"\n",
|
||||
"print(\"ENDPOINT: {}\".format(ENDPOINT))\n",
|
||||
"print(\"PROJECT_ID: {}\".format(PROJECT_ID))\n",
|
||||
"print(\"REGION: {}\".format(REGION))\n",
|
||||
"\n",
|
||||
"!gcloud config set project {PROJECT_ID}\n",
|
||||
"!gcloud config set ai_platform/region {REGION}"
|
||||
"print(\"REGION: {}\".format(REGION))"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -523,12 +661,13 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "QuVl8DrWG8NS"
|
||||
},
|
||||
"source": [
|
||||
"Upload the training data to GCS."
|
||||
"Upload the training data to Google Cloud Storage"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -539,9 +678,9 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# NOTE: Everything in this GCS DIR will be DELETED before uploading the data.\n",
|
||||
"# NOTE: Everything in this Google Cloud Storage directory will be DELETED before uploading the data\n",
|
||||
"\n",
|
||||
"! gsutil rm -rf {BUCKET_NAME}/*"
|
||||
"! gsutil rm -raf {BUCKET_NAME}/** 2> /dev/null || true"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -567,21 +706,23 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "mglUPwHpJH98"
|
||||
},
|
||||
"source": [
|
||||
"## Create Indexes\n"
|
||||
"## Create the indexes\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "qhIBCQ7dDSbW"
|
||||
},
|
||||
"source": [
|
||||
"### Create ANN Index (for Production Usage)"
|
||||
"### Create Vertex AI Matching Engine index (for production usage)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -597,6 +738,16 @@
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "14e1ed031d66"
|
||||
},
|
||||
"source": [
|
||||
"Set constants"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
@@ -611,14 +762,15 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "svLYiDf0OD2G"
|
||||
},
|
||||
"source": [
|
||||
"Create the ANN index configuration:\n",
|
||||
"#### Create the Vertex AI Matching Engine index configuration\n",
|
||||
"\n",
|
||||
"Please read the documentation to understand the various configuration parameters that can be used to tune the index\n"
|
||||
"Please read the [documentation](https://cloud.google.com/vertex-ai/docs/matching-engine/configuring-indexes) to understand the various configuration parameters that can be used to tune the index"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -656,9 +808,9 @@
|
||||
" }\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"ann_index = {\n",
|
||||
"matching_engine_index = {\n",
|
||||
" \"display_name\": DISPLAY_NAME,\n",
|
||||
" \"description\": \"Glove 100 ANN index\",\n",
|
||||
" \"description\": \"Glove 100 Vertex AI Matching Engine Index\",\n",
|
||||
" \"metadata\": struct_pb2.Value(struct_value=metadata),\n",
|
||||
"}"
|
||||
]
|
||||
@@ -671,7 +823,9 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"ann_index = index_client.create_index(parent=PARENT, index=ann_index)"
|
||||
"matching_engine_index = index_client.create_index(\n",
|
||||
" parent=PARENT, index=matching_engine_index\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -686,7 +840,7 @@
|
||||
"# This will take ~45 min.\n",
|
||||
"\n",
|
||||
"while True:\n",
|
||||
" if ann_index.done():\n",
|
||||
" if matching_engine_index.done():\n",
|
||||
" break\n",
|
||||
" print(\"Poll the operation to create index...\")\n",
|
||||
" time.sleep(60)"
|
||||
@@ -700,17 +854,18 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"INDEX_RESOURCE_NAME = ann_index.result().name\n",
|
||||
"INDEX_RESOURCE_NAME = matching_engine_index.result().name\n",
|
||||
"INDEX_RESOURCE_NAME"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "kSsqZuyoA1SG"
|
||||
},
|
||||
"source": [
|
||||
"### Create Brute Force Index (for Ground Truth)\n",
|
||||
"### Create brute force index (for ground truth)\n",
|
||||
"\n",
|
||||
"The brute force index uses a naive brute force method to find the nearest neighbors. This method is not fast or efficient. Hence brute force indices are not recommended for production usage. They are to be used to find the \"ground truth\" set of neighbors, so that the \"ground truth\" set can be used to measure recall of the indices being tuned for production usage. To ensure an apples to apples comparison, the `distanceMeasureType` and `featureNormType`, `dimensions` of the brute force index should match those of the production indices being tuned.\n",
|
||||
"\n",
|
||||
@@ -725,8 +880,6 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from google.protobuf import *\n",
|
||||
"\n",
|
||||
"algorithmConfig = struct_pb2.Struct(\n",
|
||||
" fields={\"bruteForceConfig\": struct_pb2.Value(struct_value=struct_pb2.Struct())}\n",
|
||||
")\n",
|
||||
@@ -796,12 +949,13 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "mglUPwHpJH98"
|
||||
},
|
||||
"source": [
|
||||
"## Update Indexes\n",
|
||||
"## Update the indexes\n",
|
||||
"\n",
|
||||
"Create incremental data file.\n"
|
||||
]
|
||||
@@ -863,10 +1017,10 @@
|
||||
" }\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"ann_index = {\n",
|
||||
"matching_engine_index = {\n",
|
||||
" \"name\": INDEX_RESOURCE_NAME,\n",
|
||||
" \"display_name\": DISPLAY_NAME,\n",
|
||||
" \"description\": \"Glove 100 ANN index\",\n",
|
||||
" \"description\": \"Glove 100 Vertex AI Matching Engine Index\",\n",
|
||||
" \"metadata\": struct_pb2.Value(struct_value=metadata),\n",
|
||||
"}"
|
||||
]
|
||||
@@ -879,7 +1033,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"ann_index = index_client.update_index(index=ann_index)"
|
||||
"matching_engine_index = index_client.update_index(index=matching_engine_index)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -894,7 +1048,7 @@
|
||||
"# This will take ~45 min.\n",
|
||||
"\n",
|
||||
"while True:\n",
|
||||
" if ann_index.done():\n",
|
||||
" if matching_engine_index.done():\n",
|
||||
" break\n",
|
||||
" print(\"Poll the operation to update index...\")\n",
|
||||
" time.sleep(60)"
|
||||
@@ -908,17 +1062,18 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"INDEX_RESOURCE_NAME = ann_index.result().name\n",
|
||||
"INDEX_RESOURCE_NAME = matching_engine_index.result().name\n",
|
||||
"INDEX_RESOURCE_NAME"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "qV2xjAnDDObD"
|
||||
},
|
||||
"source": [
|
||||
"## Create an IndexEndpoint with VPC Network"
|
||||
"## Create an index endpoint with VPC network"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -997,21 +1152,23 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "np2cgVuuIe9k"
|
||||
},
|
||||
"source": [
|
||||
"## Deploy Indexes"
|
||||
"## Deploy the indexes"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "8Ew1UgcIIiJG"
|
||||
},
|
||||
"source": [
|
||||
"### Deploy ANN Index"
|
||||
"### Deploy a Vertex AI Matching Engine index"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1022,7 +1179,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"DEPLOYED_INDEX_ID = \"ann_glove_deployed\""
|
||||
"DEPLOYED_INDEX_ID = \"matching_engine_glove_deployed\""
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1033,13 +1190,23 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"deploy_ann_index = {\n",
|
||||
"deploy_matching_engine_index = {\n",
|
||||
" \"id\": DEPLOYED_INDEX_ID,\n",
|
||||
" \"display_name\": DEPLOYED_INDEX_ID,\n",
|
||||
" \"index\": INDEX_RESOURCE_NAME,\n",
|
||||
"}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "cb6d956d7419"
|
||||
},
|
||||
"source": [
|
||||
"If errors occur with the next command wait some minutes for the index endpoint to be created and retry."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
@@ -1049,7 +1216,7 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"r = index_endpoint_client.deploy_index(\n",
|
||||
" index_endpoint=INDEX_ENDPOINT_NAME, deployed_index=deploy_ann_index\n",
|
||||
" index_endpoint=INDEX_ENDPOINT_NAME, deployed_index=deploy_matching_engine_index\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
@@ -1082,12 +1249,13 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "RNZnXmO5AhDO"
|
||||
},
|
||||
"source": [
|
||||
"### Deploy Brute Force Index"
|
||||
"### Deploy brute force index"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1158,12 +1326,13 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "6LCGvBNvBd8D"
|
||||
},
|
||||
"source": [
|
||||
"## Create Online Queries\n",
|
||||
"## Create online queries\n",
|
||||
"\n",
|
||||
"After you built your indexes, you may query against the deployed index through the online querying gRPC API (Match service) within the virtual machine instances from the same region (for example 'us-central1' in this tutorial). \n",
|
||||
"\n",
|
||||
@@ -1178,7 +1347,15 @@
|
||||
"\n",
|
||||
"* Compile the protocal buffer (see below)\n",
|
||||
"* Obtain the index endpoint\n",
|
||||
"* Use a code-generated stub to make the call, passing the parameter values"
|
||||
"* Use a code-generated stub to make the call, passing the parameter values\n",
|
||||
"\n",
|
||||
"### Troubleshooting connectivity issues\n",
|
||||
"\n",
|
||||
"In case you have connectivity errors please perform the following:\n",
|
||||
"\n",
|
||||
"* Verify that the index endpoint, index, and VPC are all in the same Google Cloud project\n",
|
||||
"* Verify that the index endpoint, index, and VPC are all in the same region and it is a valid (e.g. us-central1)\n",
|
||||
"* Verify the Network does not have a firewall rule which denies all egress connections. Else, disable this rule or overwrite it with another rule that allows connection to the index endpoint IP"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1351,12 +1528,13 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "8wXTSgz1Bl0x"
|
||||
},
|
||||
"source": [
|
||||
"Obtain the Private Endpoint: "
|
||||
"Obtain the private endpoint: "
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1521,12 +1699,13 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "_mNwdU9_B_Ez"
|
||||
},
|
||||
"source": [
|
||||
"### Batch Query\n",
|
||||
"## Submit a batch query\n",
|
||||
"\n",
|
||||
"You can run multiple queries in a single RPC call using the BatchMatch API:"
|
||||
]
|
||||
@@ -1764,18 +1943,20 @@
|
||||
"]\n",
|
||||
"\n",
|
||||
"batch_request = match_service_pb2.BatchMatchRequest()\n",
|
||||
"batch_request_ann = match_service_pb2.BatchMatchRequest.BatchMatchRequestPerIndex()\n",
|
||||
"batch_request_matching_engine = (\n",
|
||||
" match_service_pb2.BatchMatchRequest.BatchMatchRequestPerIndex()\n",
|
||||
")\n",
|
||||
"batch_request_brute_force = (\n",
|
||||
" match_service_pb2.BatchMatchRequest.BatchMatchRequestPerIndex()\n",
|
||||
")\n",
|
||||
"batch_request_ann.deployed_index_id = DEPLOYED_INDEX_ID\n",
|
||||
"batch_request_matching_engine.deployed_index_id = DEPLOYED_INDEX_ID\n",
|
||||
"batch_request_brute_force.deployed_index_id = DEPLOYED_BRUTE_FORCE_INDEX_ID\n",
|
||||
"for query in queries:\n",
|
||||
" batch_request_ann.requests.append(get_request(query, DEPLOYED_INDEX_ID))\n",
|
||||
" batch_request_matching_engine.requests.append(get_request(query, DEPLOYED_INDEX_ID))\n",
|
||||
" batch_request_brute_force.requests.append(\n",
|
||||
" get_request(query, DEPLOYED_BRUTE_FORCE_INDEX_ID)\n",
|
||||
" )\n",
|
||||
"batch_request.requests.append(batch_request_ann)\n",
|
||||
"batch_request.requests.append(batch_request_matching_engine)\n",
|
||||
"batch_request.requests.append(batch_request_brute_force)\n",
|
||||
"\n",
|
||||
"response = stub.BatchMatch(batch_request)\n",
|
||||
@@ -1783,14 +1964,15 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "_mNwdU9_B_Ez"
|
||||
},
|
||||
"source": [
|
||||
"### Compute Recall\n",
|
||||
"### Compute the recall metric\n",
|
||||
"\n",
|
||||
"Use deployed brute force Index as the ground truth to calculate the recall of ANN Index:"
|
||||
"Use the deployed brute force index as the ground truth to calculate the recall of the Vertex AI Matching Engine index:"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1835,6 +2017,7 @@
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "TpV-iwP9qw9c"
|
||||
@@ -1844,7 +2027,18 @@
|
||||
"\n",
|
||||
"To clean up all Google Cloud resources used in this project, you can [delete the Google Cloud\n",
|
||||
"project](https://cloud.google.com/resource-manager/docs/creating-managing-projects#shutting_down_projects) you used for the tutorial.\n",
|
||||
"You can also manually delete resources that you created by running the following code."
|
||||
"\n",
|
||||
"Otherwise, you can delete the individual resources you created in this tutorial:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "390c331dc7d9"
|
||||
},
|
||||
"source": [
|
||||
"### Delete the Vertex AI Matching Engine resources"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1869,6 +2063,31 @@
|
||||
"source": [
|
||||
"index_endpoint_client.delete_index_endpoint(name=INDEX_ENDPOINT_NAME)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"attachments": {},
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "ff14a85c85fb"
|
||||
},
|
||||
"source": [
|
||||
"### Delete the Google Cloud Storage bucket"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "68d4781faac4"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"\n",
|
||||
"delete_bucket = False\n",
|
||||
"if delete_bucket or os.getenv(\"IS_TESTING\"):\n",
|
||||
" ! gsutil -m rm -r $BUCKET_NAME"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -14,5 +14,5 @@ The purpose of this set of notebooks and markdown files is to demonstrate Google
|
||||
4. [Evaluation](stage4)
|
||||
5. [Deployment](stage5)
|
||||
6. [Serving](stage6)
|
||||
7. Monitoring
|
||||
7. Monitoring(stage7)
|
||||
8. Continuous Training
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
## Before you begin
|
||||
|
||||
### Set up your Google Cloud project
|
||||
|
||||
**The following steps are required, regardless of your notebook environment.**
|
||||
|
||||
1. [Select or create a Google Cloud project](https://console.cloud.google.com/cloud-resource-manager). When you first create an account, you get a $300 free credit towards your compute/storage costs.
|
||||
|
||||
1. [Make sure that billing is enabled for your project](https://cloud.google.com/billing/docs/how-to/modify-project).
|
||||
|
||||
1. [Enable the Vertex AI, BigQuery, Compute Engine and Cloud Storage APIs](https://console.cloud.google.com/flows/enableapi?apiid=aiplatform.googleapis.com,bigquery,compute_component,storage_component).
|
||||
|
||||
1. If you are running this notebook locally, you need to install the [Cloud SDK](https://cloud.google.com/sdk).
|
||||
|
||||
1. Enter your project ID in the cell below. Then run the cell to make sure the
|
||||
Cloud SDK uses the right project for all the commands in this notebook.
|
||||
|
||||
**Note**: Jupyter runs lines prefixed with `!` as shell commands, and it interpolates Python variables prefixed with `$` into these commands.
|
||||
|
||||
### Set up your local development environment
|
||||
|
||||
**If you are using Colab or Vertex AI Workbench Notebooks**, your environment already meets all the requirements to run this notebook. You can skip this step.
|
||||
|
||||
**Otherwise**, make sure your environment meets this notebook's requirements. You need the following:
|
||||
|
||||
- The Cloud Storage SDK
|
||||
- Python 3
|
||||
- virtualenv
|
||||
- Jupyter notebook running in a virtual environment with Python 3
|
||||
|
||||
The Cloud Storage guide to [Setting up a Python development environment](https://cloud.google.com/python/setup) and the [Jupyter installation guide](https://jupyter.org/install) provide detailed instructions for meeting these requirements. The following steps provide a condensed set of instructions:
|
||||
|
||||
1. [Install and initialize the SDK](https://cloud.google.com/sdk/docs/).
|
||||
|
||||
2. [Install Python 3](https://cloud.google.com/python/setup#installing_python).
|
||||
|
||||
3. [Install virtualenv](https://cloud.google.com/python/setup#installing_and_using_virtualenv) and create a virtual environment that uses Python 3. Activate the virtual environment.
|
||||
|
||||
4. To install Jupyter, run `pip3 install jupyter` on the command-line in a terminal shell.
|
||||
|
||||
5. To launch Jupyter, run `jupyter notebook` on the command-line in a terminal shell.
|
||||
|
||||
6. Open this notebook in the Jupyter Notebook Dashboard.
|
||||
@@ -0,0 +1,112 @@
|
||||
import os
|
||||
import sys
|
||||
import argparse
|
||||
import subprocess
|
||||
import random
|
||||
import string
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--bucket', dest='bucket_required', action='store_true',
|
||||
default=False, help='Bucket required')
|
||||
parser.add_argument('--email', dest='email_required', action='store_true',
|
||||
default=False, help='Email required')
|
||||
parser.add_argument('--sa', dest='sa_required', action='store_true',
|
||||
default=False, help='Service account required')
|
||||
parser.add_argument('--packages', dest='extra_packages',
|
||||
default='', type=str, help='additional required packages')
|
||||
args = parser.parse_args()
|
||||
|
||||
extra_pkgs = args.extra_packages
|
||||
|
||||
|
||||
# Installation
|
||||
|
||||
|
||||
# The Vertex AI Workbench Notebook product has specific requirements
|
||||
IS_WORKBENCH_NOTEBOOK = os.getenv("DL_ANACONDA_HOME")
|
||||
IS_USER_MANAGED_WORKBENCH_NOTEBOOK = os.path.exists(
|
||||
"/opt/deeplearning/metadata/env_version"
|
||||
)
|
||||
IS_COLAB = "google.colab" in sys.modules
|
||||
|
||||
# Vertex AI Notebook requires dependencies to be installed with '--user'
|
||||
USER_FLAG = ""
|
||||
if IS_WORKBENCH_NOTEBOOK:
|
||||
USER_FLAG = "--user"
|
||||
|
||||
# not used
|
||||
'''
|
||||
print("Installing packages")
|
||||
os.system(f"pip3 install --upgrade --quiet {USER_FLAG} google-cloud-aiplatform {args.extra_packages}")
|
||||
print("Done installation")
|
||||
'''
|
||||
|
||||
# Authenticate
|
||||
if IS_COLAB:
|
||||
from google.colab import auth as google_auth
|
||||
|
||||
google_auth.authenticate_user()
|
||||
|
||||
|
||||
# project ID
|
||||
if IS_WORKBENCH_NOTEBOOK:
|
||||
shell_output = subprocess.check_output("gcloud config list --format 'value(core.project)' 2>/dev/null", shell=True)
|
||||
PROJECT_ID = shell_output[0:-1].decode('utf-8')
|
||||
print("PROJECT ID: ", PROJECT_ID)
|
||||
else:
|
||||
PROJECT_ID = input("Enter PROJECT_ID: ")
|
||||
os.system(f"gcloud config set project {PROJECT_ID}")
|
||||
|
||||
# email
|
||||
if args.email_required:
|
||||
shell_output = subprocess.check_output("gcloud config list --format 'value(core.account)' 2>/dev/null", shell=True)
|
||||
EMAIL_ADDR = shell_output[0:-1].decode('utf-8')
|
||||
if EMAIL_ADDR == '':
|
||||
EMAIL_ADDR = input("Enter Email Address: ")
|
||||
print("EMAIL_ADDR: ", EMAIL_ADDR)
|
||||
|
||||
# region
|
||||
shell_output = subprocess.check_output("gcloud config list --format 'value(ai.region)'", shell=True)
|
||||
REGION = shell_output[0:-1].decode('utf-8')
|
||||
if REGION == '':
|
||||
REGION = input("Enter REGION: ")
|
||||
print("REGION: ", REGION)
|
||||
|
||||
# multi-region
|
||||
MULTI_REGION = REGION.split('-')[0]
|
||||
|
||||
|
||||
# UUID
|
||||
# Generate a uuid of a specifed length(default=8)
|
||||
def generate_uuid(length: int = 8) -> str:
|
||||
return "".join(random.choices(string.ascii_lowercase + string.digits, k=length))
|
||||
|
||||
|
||||
UUID = generate_uuid()
|
||||
print("UUID", UUID)
|
||||
|
||||
# Bucket
|
||||
if args.bucket_required:
|
||||
BUCKET_NAME = PROJECT_ID + "aip-" + UUID
|
||||
BUCKET_URI = f"gs://{BUCKET_NAME}"
|
||||
os.system(f"gsutil mb -l {REGION} {BUCKET_URI}")
|
||||
print("BUCKET_URI", BUCKET_URI)
|
||||
|
||||
|
||||
# Project Number
|
||||
if args.sa_required:
|
||||
if IS_WORKBENCH_NOTEBOOK:
|
||||
shell_output = subprocess.check_output("gcloud auth list 2>/dev/null", shell=True)
|
||||
SERVICE_ACCOUNT = shell_output[:-1].decode('utf-8').split('\n')[2].strip()
|
||||
PROJECT_NUMBER = SERVICE_ACCOUNT.split('-')[0]
|
||||
else:
|
||||
shell_output = subprocess.check_output(f"gcloud projects describe {PROJECT_ID}", shell=True)
|
||||
try:
|
||||
PROJECT_NUMBER = shell_output[:-1].decode('utf-8').split('\n')[7].split(':')[-1].strip().replace("'", "")
|
||||
SERVICE_ACCOUNT = f"{PROJECT_NUMBER}-compute@developer.gserviceaccount.com"
|
||||
except:
|
||||
PROJECT_NUMBER = input("Enter project number: ")
|
||||
SERVICE_ACCOUNT = f"{PROJECT_NUMBER}-compute@developer.gserviceaccount.com"
|
||||
|
||||
print("SERVICE_ACCOUNT", SERVICE_ACCOUNT)
|
||||
print("PROJECT_NUMBER", PROJECT_NUMBER)
|
||||
@@ -28,28 +28,14 @@ The first stage in MLOps is the collection and preparation for the purpose of de
|
||||
|
||||
### Get Started
|
||||
|
||||
[Get started with Vertex AI datasets](get_started_vertex_datasets.ipynb)
|
||||
|
||||
[Get started with Dataflow](https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/ml_ops/stage1/get_started_dataflow.ipynb)
|
||||
|
||||
```
|
||||
Learn how to use `Dataflow` for training with `Vertex AI`.
|
||||
|
||||
The steps performed include:
|
||||
- Create a Vertex AI `Dataset` resource for:
|
||||
- image data
|
||||
- text data
|
||||
- video data
|
||||
- tabular data
|
||||
- forecasting data
|
||||
- Search `Dataset` resources using a filter.
|
||||
- Read a sample of a `BigQuery` dataset into a dataframe.
|
||||
- Generate statistics and data schema using TensorFlow Data Validation from the samples in the dataframe.
|
||||
- Detect anomalies in new data using TensorFlow Data Validation.
|
||||
- Generate a TFRecord feature specification using TensorFlow Transform from the data schema.
|
||||
- Export a dataset and convert to TFRecords.
|
||||
```
|
||||
|
||||
[Get started with Dataflow](get_started_dataflow.ipynb)
|
||||
|
||||
```
|
||||
The steps performed include:
|
||||
- Offline preprocessing of data:
|
||||
- Serially - w/o dataflow
|
||||
- Parallel - with dataflow
|
||||
@@ -58,20 +44,38 @@ The steps performed include:
|
||||
- image data
|
||||
```
|
||||
|
||||
[Create an unlabelled Vertex AI AutoML text entity extraction dataset from pdfs using Vision API](get_started_with_visionapi_and_vertex_datasets.ipynb)
|
||||
|
||||
[Get started with Vertex AI datasets](https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/ml_ops/stage1/get_started_vertex_datasets.ipynb)
|
||||
|
||||
```
|
||||
Learn how to use `Vertex AI Dataset` for training with `Vertex AI`.
|
||||
|
||||
The steps performed include:
|
||||
1. Using `Vision API` to perform Optical Character Recognition (OCR) to extract text from PDF files.
|
||||
2. Processing the results and saving them to text files.
|
||||
3. Generating a `Vertex AI Dataset` import file.
|
||||
4. Creating a new unlabelled text entity extraction `Vertex AI Dataset` resource in `Vertex AI`.
|
||||
|
||||
- Create a Vertex AI `Dataset` resource for:
|
||||
- image data
|
||||
- text data
|
||||
- video data
|
||||
- tabular data
|
||||
- forecasting data
|
||||
|
||||
|
||||
- Search `Dataset` resources using a filter.
|
||||
- Read a sample of a `BigQuery` dataset into a dataframe.
|
||||
- Generate statistics and data schema using TensorFlow Data Validation from the samples in the dataframe.
|
||||
- Detect anomalies in new data using TensorFlow Data Validation.
|
||||
- Generate a TFRecord feature specification using TensorFlow Transform from the data schema.
|
||||
- Export a dataset and convert to TFRecords.
|
||||
```
|
||||
|
||||
[Get started with BigQuery datasets](get_started_bq_datasets.ipynb)
|
||||
|
||||
[Get started with BigQuery datasets](https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/ml_ops/stage1/get_started_bq_datasets.ipynb)
|
||||
|
||||
```
|
||||
Learn how to use `BigQuery` as a dataset for training with `Vertex AI`.
|
||||
|
||||
The steps performed include:
|
||||
|
||||
- Create a Vertex AI `Dataset` resource from `BigQuery` table -- compatible for `AutoML` training.
|
||||
- Extract a copy of the dataset from `BigQuery` to a CSV file in Cloud Storage -- compatible for `AutoML` or custom training.
|
||||
- Select rows from a `BigQuery` dataset into a `pandas` dataframe -- compatible for custom training.
|
||||
@@ -81,25 +85,44 @@ The steps performed include:
|
||||
- Extract data from `BigQuery` table into a `DMatrix` -- compatible for custom training `XGBoost` models.
|
||||
```
|
||||
|
||||
[Get started with Vertex AI data labeling](get_started_with_data_labeling.ipynb)
|
||||
|
||||
[Get started with Vertex AI Data Labeling](https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/ml_ops/stage1/get_started_with_data_labeling.ipynb)
|
||||
|
||||
```
|
||||
Learn how to use the `Vertex AI Data Labeling` service/
|
||||
|
||||
The steps performed include:
|
||||
|
||||
- Create a Specialist Pool for data labelers.
|
||||
- Create a data labeling job.
|
||||
- Submit the data labeling job.
|
||||
- List data labeling jobs.
|
||||
- Cancel a data labeling job.
|
||||
```
|
||||
|
||||
|
||||
[Create an unlabelled Vertex AI AutoML text entity extraction dataset from PDFs using Vision API](https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/ml_ops/stage1/get_started_with_visionapi_and_vertex_datasets.ipynb)
|
||||
|
||||
```
|
||||
Learn to use `Vision API` to extract text from PDF files stored on a Cloud Storage bucket.
|
||||
|
||||
The steps performed include:
|
||||
|
||||
1. Using `Vision API` to perform Optical Character Recognition (OCR) to extract text from PDF files.
|
||||
2. Processing the results and saving them to text files.
|
||||
3. Generating a `Vertex AI Dataset` import file.
|
||||
4. Cr
|
||||
|
||||
|
||||
### E2E Stage Example
|
||||
|
||||
[Stage 1: Data Management](mlops_data_management.ipynb)
|
||||
|
||||
[Data management](https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/ml_ops/stage1/mlops_data_management.ipynb)
|
||||
|
||||
```
|
||||
In this tutorial, you create a MLOps stage 1: data management process.
|
||||
|
||||
The steps performed include:
|
||||
|
||||
- Explore and visualize the data.
|
||||
- Create a Vertex AI `Dataset` resource from `BigQuery` table -- for AutoML training.
|
||||
- Extract a copy of the dataset to a CSV file in Cloud Storage.
|
||||
@@ -108,4 +131,4 @@ The steps performed include:
|
||||
- Generate statistics and data schema using TensorFlow Data Validation from the samples in the dataframe.
|
||||
- Generate a TFRecord feature specification using TensorFlow Data Validation from the data schema.
|
||||
- Preprocess a portion of the BigQuery data using `Dataflow` -- for custom training.
|
||||
```
|
||||
```
|
||||
@@ -120,7 +120,7 @@
|
||||
" - XGBoost model training:\n",
|
||||
" - Use BigQuery ML built-in XGBoost training.\n",
|
||||
" - Alternatively, create a DMatrix generator from CSV files extracted from BigQuery table.\n",
|
||||
" - Pytorch model training:\n",
|
||||
" - PyTorch model training:\n",
|
||||
" - Extract the BigQuery to a pandas dataframe.\n",
|
||||
" - Preprocess the data in the dataframe.\n",
|
||||
" - Create a DataLoader generator from the pandas dataframe.\n",
|
||||
@@ -191,13 +191,8 @@
|
||||
"if IS_WORKBENCH_NOTEBOOK:\n",
|
||||
" USER_FLAG = \"--user\"\n",
|
||||
"\n",
|
||||
"# Install the packages\n",
|
||||
"! pip3 install --upgrade pyarrow $USER_FLAG -q\n",
|
||||
"! pip3 install --upgrade google-cloud-bigquery $USER_FLAG -q\n",
|
||||
"! pip3 install --upgrade google-cloud-aiplatform $USER_FLAG -q\n",
|
||||
"! pip3 install -U xgboost $USER_FLAG -q\n",
|
||||
"! pip3 install -U tensorflow $USER_FLAG -q\n",
|
||||
"! pip3 install -U tensorflow-io==0.18 $USER_FLAG -q"
|
||||
"extra_pkgs = \"tensorflow tensorflow-io==0.18 pyarrow xgboost google-cloud-bigquery\"\n",
|
||||
"! pip3 install --upgrade --quiet {USER_FLAG} google-cloud-aiplatform $extra_pkgs"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -219,9 +214,9 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"import sys\n",
|
||||
"\n",
|
||||
"if not os.getenv(\"IS_TESTING\"):\n",
|
||||
"if \"google.colab\" in sys.modules:\n",
|
||||
" # Automatically restart kernel after installs\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
@@ -232,274 +227,42 @@
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "84cd83853240"
|
||||
"id": "fc8fb52b5cca"
|
||||
},
|
||||
"source": [
|
||||
"## Before you begin\n",
|
||||
"### Common setup\n",
|
||||
"\n",
|
||||
"### Set up your Google Cloud project\n",
|
||||
"\n",
|
||||
"**The following steps are required, regardless of your notebook environment.**\n",
|
||||
"\n",
|
||||
"1. [Select or create a Google Cloud project](https://console.cloud.google.com/cloud-resource-manager). When you first create an account, you get a $300 free credit towards your compute/storage costs.\n",
|
||||
"\n",
|
||||
"1. [Make sure that billing is enabled for your project](https://cloud.google.com/billing/docs/how-to/modify-project).\n",
|
||||
"\n",
|
||||
"1. [Enable the Vertex AI, BigQuery, Compute Engine and Cloud Storage APIs](https://console.cloud.google.com/flows/enableapi?apiid=aiplatform.googleapis.com,bigquery,compute_component,storage_component).\n",
|
||||
"\n",
|
||||
"1. If you are running this notebook locally, you need to install the [Cloud SDK](https://cloud.google.com/sdk).\n",
|
||||
"\n",
|
||||
"1. Enter your project ID in the cell below. Then run the cell to make sure the\n",
|
||||
"Cloud SDK uses the right project for all the commands in this notebook.\n",
|
||||
"\n",
|
||||
"**Note**: Jupyter runs lines prefixed with `!` as shell commands, and it interpolates Python variables prefixed with `$` into these commands."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "project_id"
|
||||
},
|
||||
"source": [
|
||||
"#### Set your project ID\n",
|
||||
"\n",
|
||||
"**If you don't know your project ID**, you may be able to get your project ID using `gcloud`."
|
||||
"Now, execute the common setup for the notebook tutorials."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "set_project_id"
|
||||
"id": "001a0fcd5d78"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PROJECT_ID = \"[your-project-id]\" # @param {type:\"string\"}"
|
||||
"# Common code setup for notebook tutorials\n",
|
||||
"\n",
|
||||
"! wget https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/ml_ops/setup.py -O setup.py\n",
|
||||
"\n",
|
||||
"%run setup.py --bucket"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "autoset_project_id"
|
||||
"id": "d809f07a8935"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if PROJECT_ID == \"\" or PROJECT_ID is None or PROJECT_ID == \"[your-project-id]\":\n",
|
||||
" # Get your GCP project id from gcloud\n",
|
||||
" shell_output = ! gcloud config list --format 'value(core.project)' 2>/dev/null\n",
|
||||
" PROJECT_ID = shell_output[0]\n",
|
||||
" print(\"Project ID:\", PROJECT_ID)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "set_gcloud_project_id"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! gcloud config set project $PROJECT_ID"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "region"
|
||||
},
|
||||
"source": [
|
||||
"#### Region\n",
|
||||
"# Other Common setup instructions for notebook tutorials\n",
|
||||
"\n",
|
||||
"You can also change the `REGION` variable, which is used for operations\n",
|
||||
"throughout the rest of this notebook. Below are regions supported for Vertex AI. We recommend that you choose the region closest to you.\n",
|
||||
"! wget https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/ml_ops/setup.md -O setup.md\n",
|
||||
"\n",
|
||||
"- Americas: `us-central1`\n",
|
||||
"- Europe: `europe-west4`\n",
|
||||
"- Asia Pacific: `asia-east1`\n",
|
||||
"\n",
|
||||
"You may not use a multi-regional bucket for training with Vertex AI. Not all regions provide support for all Vertex AI services.\n",
|
||||
"\n",
|
||||
"Learn more about [Vertex AI regions](https://cloud.google.com/vertex-ai/docs/general/locations)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "region"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"REGION = \"[your-region]\" # @param {type: \"string\"}\n",
|
||||
"\n",
|
||||
"if REGION == \"[your-region]\":\n",
|
||||
" REGION = \"us-central1\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "timestamp"
|
||||
},
|
||||
"source": [
|
||||
"#### Timestamp\n",
|
||||
"\n",
|
||||
"If you are in a live tutorial session, you might be using a shared test account or project. To avoid name collisions between users on resources created, you create a timestamp for each instance session, and append the timestamp onto the name of resources you create in this tutorial."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "timestamp"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from datetime import datetime\n",
|
||||
"\n",
|
||||
"TIMESTAMP = datetime.now().strftime(\"%Y%m%d%H%M%S\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "77c385f0db59"
|
||||
},
|
||||
"source": [
|
||||
"### Authenticate your Google Cloud account\n",
|
||||
"\n",
|
||||
"**If you are using Vertex AI Workbench Notebooks**, your environment is already authenticated. Skip this step.\n",
|
||||
"\n",
|
||||
"**If you are using Colab**, run the cell below and follow the instructions when prompted to authenticate your account via oAuth.\n",
|
||||
"\n",
|
||||
"**Otherwise**, follow these steps:\n",
|
||||
"\n",
|
||||
"In the Cloud Console, go to the [Create service account key](https://console.cloud.google.com/apis/credentials/serviceaccountkey) page.\n",
|
||||
"\n",
|
||||
"1. **Click Create service account**.\n",
|
||||
"\n",
|
||||
"2. In the **Service account name** field, enter a name, and click **Create**.\n",
|
||||
"\n",
|
||||
"3. In the **Grant this service account access to project** section, click the Role drop-down list. Type \"Vertex AI\" into the filter box, and select **Vertex AI Administrator**. Type \"Storage Object Admin\" into the filter box, and select **Storage Object Admin**.\n",
|
||||
"\n",
|
||||
"4. Click Create. A JSON file that contains your key downloads to your local environment.\n",
|
||||
"\n",
|
||||
"5. Enter the path to your service account key as the GOOGLE_APPLICATION_CREDENTIALS variable in the cell below and run the cell."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "535223fa4b84"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# If you are running this notebook in Colab, run this cell and follow the\n",
|
||||
"# instructions to authenticate your GCP account. This provides access to your\n",
|
||||
"# Cloud Storage bucket and lets you submit training jobs and prediction\n",
|
||||
"# requests.\n",
|
||||
"\n",
|
||||
"import os\n",
|
||||
"import sys\n",
|
||||
"\n",
|
||||
"# If on Vertex AI Workbench, then don't execute this code\n",
|
||||
"IS_COLAB = False\n",
|
||||
"if not os.path.exists(\"/opt/deeplearning/metadata/env_version\") and not os.getenv(\n",
|
||||
" \"DL_ANACONDA_HOME\"\n",
|
||||
"):\n",
|
||||
" if \"google.colab\" in sys.modules:\n",
|
||||
" IS_COLAB = True\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()\n",
|
||||
"\n",
|
||||
" # If you are running this notebook locally, replace the string below with the\n",
|
||||
" # path to your service account key and run this cell to authenticate your GCP\n",
|
||||
" # account.\n",
|
||||
" elif not os.getenv(\"IS_TESTING\"):\n",
|
||||
" %env GOOGLE_APPLICATION_CREDENTIALS ''"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bucket:custom"
|
||||
},
|
||||
"source": [
|
||||
"### Create a Cloud Storage bucket\n",
|
||||
"\n",
|
||||
"**The following steps are required, regardless of your notebook environment.**\n",
|
||||
"\n",
|
||||
"When you create a dataset resource using the Vertex SDK, you can provide a Cloud Storage bucket that contains the data. Vertex AI creates the dataset resource from the data. In this tutorial, Vertex AI also creates a dataset resource from your data in the Cloud Storage bucket.\n",
|
||||
"\n",
|
||||
"Set the name of your Cloud Storage bucket below. Bucket names must be globally unique across all Google Cloud projects, including those outside of your organization."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "bucket"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"BUCKET_NAME = \"[your-bucket-name]\" # @param {type:\"string\"}\n",
|
||||
"BUCKET_URI = f\"gs://{BUCKET_NAME}\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "autoset_bucket"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if BUCKET_URI == \"\" or BUCKET_URI is None or BUCKET_URI == \"gs://[your-bucket-name]\":\n",
|
||||
" BUCKET_NAME = PROJECT_ID + \"aip-\" + TIMESTAMP\n",
|
||||
" BUCKET_URI = \"gs://\" + BUCKET_NAME"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "create_bucket"
|
||||
},
|
||||
"source": [
|
||||
"**Only if your bucket doesn't already exist**: Run the following cell to create your Cloud Storage bucket."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "create_bucket"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! gsutil mb -l $REGION $BUCKET_URI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "validate_bucket"
|
||||
},
|
||||
"source": [
|
||||
"Finally, validate access to your Cloud Storage bucket by examining its contents:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "validate_bucket"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! gsutil ls -al $BUCKET_URI"
|
||||
"%load setup.md"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -620,7 +383,7 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"dataset = aiplatform.TabularDataset.create(\n",
|
||||
" display_name=\"NOAA historical weather data\" + \"_\" + TIMESTAMP,\n",
|
||||
" display_name=\"NOAA historical weather data\" + \"_\" + UUID,\n",
|
||||
" bq_source=[IMPORT_FILE],\n",
|
||||
" labels={\"user_metadata\": BUCKET_URI[5:]},\n",
|
||||
")\n",
|
||||
@@ -695,7 +458,7 @@
|
||||
"gcs_source = IMPORT_FILES\n",
|
||||
"\n",
|
||||
"dataset = aiplatform.TabularDataset.create(\n",
|
||||
" display_name=\"NOAA historical weather data\" + \"_\" + TIMESTAMP,\n",
|
||||
" display_name=\"NOAA historical weather data\" + \"_\" + UUID,\n",
|
||||
" gcs_source=gcs_source,\n",
|
||||
" labels={\"user_metadata\": BUCKET_URI[5:]},\n",
|
||||
")\n",
|
||||
@@ -737,10 +500,10 @@
|
||||
" or BQ_MY_DATASET is None\n",
|
||||
" or BQ_MY_DATASET == \"[your-dataset-name]\"\n",
|
||||
"):\n",
|
||||
" BQ_MY_DATASET = \"mlops_dataset_\" + TIMESTAMP\n",
|
||||
" BQ_MY_DATASET = \"mlops_dataset_\" + UUID\n",
|
||||
"\n",
|
||||
"if BQ_MY_TABLE == \"\" or BQ_MY_TABLE is None or BQ_MY_TABLE == \"[your-view-name]\":\n",
|
||||
" BQ_MY_TABLE = \"mlops_view_\" + TIMESTAMP"
|
||||
" BQ_MY_TABLE = \"mlops_view_\" + UUID"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
" </a>\n",
|
||||
" </td>\n",
|
||||
" <td>\n",
|
||||
" <a href=\"https://console.cloud.google.com/vertex-ai/workbench/deploy-notebook?download_url=https://github.com/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/ml_ops/stage1/get_started_dataflow.ipynb\">\n",
|
||||
" <a href=\"https://console.cloud.google.com/vertex-ai/workbench/deploy-notebook?download_url=https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/ml_ops/stage1/get_started_dataflow.ipynb\">\n",
|
||||
" <img src=\"https://lh3.googleusercontent.com/UiNooY4LUgW_oTvpsNhPpQzsstV5W8F7rYgxgGBD85cWJoLmrOzhVs_ksK_vgx40SHs7jCqkTkCk=e14-rj-sc0xffffff-h130-w32\" alt=\"Vertex AI logo\">\n",
|
||||
" Open in Vertex AI Workbench\n",
|
||||
" </a>\n",
|
||||
@@ -186,13 +186,9 @@
|
||||
"if IS_WORKBENCH_NOTEBOOK:\n",
|
||||
" USER_FLAG = \"--user\"\n",
|
||||
"\n",
|
||||
"! pip3 install -U tensorflow==2.5 $USER_FLAG -q\n",
|
||||
"! pip3 install -U tensorflow-data-validation==1.2 $USER_FLAG -q\n",
|
||||
"! pip3 install -U tensorflow-transform==1.2 $USER_FLAG -q\n",
|
||||
"! pip3 install -U tensorflow-io==0.18 $USER_FLAG -q\n",
|
||||
"! pip3 install --upgrade google-cloud-aiplatform $USER_FLAG -q\n",
|
||||
"! pip3 install --upgrade google-cloud-bigquery $USER_FLAG -q\n",
|
||||
"! pip3 install --upgrade apache-beam[gcp] $USER_FLAG -q"
|
||||
"extra_pkgs = \"tensorflow==2.5 tensorflow-data-validation==1.2 tensorflow-transform==1.2 \\\n",
|
||||
" tensorflow-io==0.18 pyarrow pandas apache-beam[gcp] google-cloud-bigquery\"\n",
|
||||
"! pip3 install --upgrade --quiet {USER_FLAG} google-cloud-aiplatform $extra_pkgs"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -214,9 +210,9 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"import sys\n",
|
||||
"\n",
|
||||
"if not os.getenv(\"IS_TESTING\"):\n",
|
||||
"if \"google.colab\" in sys.modules:\n",
|
||||
" # Automatically restart kernel after installs\n",
|
||||
" import IPython\n",
|
||||
"\n",
|
||||
@@ -227,279 +223,42 @@
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "84cd83853240"
|
||||
"id": "fc8fb52b5cca"
|
||||
},
|
||||
"source": [
|
||||
"## Before you begin\n",
|
||||
"### Common setup\n",
|
||||
"\n",
|
||||
"### Set up your Google Cloud project\n",
|
||||
"\n",
|
||||
"**The following steps are required, regardless of your notebook environment.**\n",
|
||||
"\n",
|
||||
"1. [Select or create a Google Cloud project](https://console.cloud.google.com/cloud-resource-manager). When you first create an account, you get a $300 free credit towards your compute/storage costs.\n",
|
||||
"\n",
|
||||
"1. [Make sure that billing is enabled for your project](https://cloud.google.com/billing/docs/how-to/modify-project).\n",
|
||||
"\n",
|
||||
"1. [Enable the Vertex AI, BigQuery, Compute Engine and Cloud Storage APIs](https://console.cloud.google.com/flows/enableapi?apiid=aiplatform.googleapis.com,bigquery,compute_component,storage_component).\n",
|
||||
"\n",
|
||||
"1. If you are running this notebook locally, you need to install the [Cloud SDK](https://cloud.google.com/sdk).\n",
|
||||
"\n",
|
||||
"1. Enter your project ID in the cell below. Then run the cell to make sure the\n",
|
||||
"Cloud SDK uses the right project for all the commands in this notebook.\n",
|
||||
"\n",
|
||||
"**Note**: Jupyter runs lines prefixed with `!` as shell commands, and it interpolates Python variables prefixed with `$` into these commands."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "project_id"
|
||||
},
|
||||
"source": [
|
||||
"#### Set your project ID\n",
|
||||
"\n",
|
||||
"**If you don't know your project ID**, you may be able to get your project ID using `gcloud`."
|
||||
"Now, execute the common setup for the notebook tutorials."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "set_project_id"
|
||||
"id": "001a0fcd5d78"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PROJECT_ID = \"[your-project-id]\" # @param {type:\"string\"}"
|
||||
"# Common code setup for notebook tutorials\n",
|
||||
"\n",
|
||||
"! wget https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/ml_ops/setup.py -O setup.py\n",
|
||||
"\n",
|
||||
"%run setup.py --bucket"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "autoset_project_id"
|
||||
"id": "d809f07a8935"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if PROJECT_ID == \"\" or PROJECT_ID is None or PROJECT_ID == \"[your-project-id]\":\n",
|
||||
" # Get your GCP project id from gcloud\n",
|
||||
" shell_output = ! gcloud config list --format 'value(core.project)' 2>/dev/null\n",
|
||||
" PROJECT_ID = shell_output[0]\n",
|
||||
" print(\"Project ID:\", PROJECT_ID)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "set_gcloud_project_id"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! gcloud config set project $PROJECT_ID"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "region"
|
||||
},
|
||||
"source": [
|
||||
"#### Region\n",
|
||||
"# Other Common setup instructions for notebook tutorials\n",
|
||||
"\n",
|
||||
"You can also change the `REGION` variable, which is used for operations\n",
|
||||
"throughout the rest of this notebook. Below are regions supported for Vertex AI. We recommend that you choose the region closest to you.\n",
|
||||
"! wget https://raw.githubusercontent.com/GoogleCloudPlatform/vertex-ai-samples/main/notebooks/community/ml_ops/setup.md -O setup.md\n",
|
||||
"\n",
|
||||
"- Americas: `us-central1`\n",
|
||||
"- Europe: `europe-west4`\n",
|
||||
"- Asia Pacific: `asia-east1`\n",
|
||||
"\n",
|
||||
"You may not use a multi-regional bucket for training with Vertex AI. Not all regions provide support for all Vertex AI services.\n",
|
||||
"\n",
|
||||
"Learn more about [Vertex AI regions](https://cloud.google.com/vertex-ai/docs/general/locations)."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "region"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"REGION = \"[your-region]\" # @param {type: \"string\"}\n",
|
||||
"\n",
|
||||
"if REGION == \"[your-region]\":\n",
|
||||
" REGION = \"us-central1\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "timestamp"
|
||||
},
|
||||
"source": [
|
||||
"#### Timestamp\n",
|
||||
"\n",
|
||||
"If you are in a live tutorial session, you might be using a shared test account or project. To avoid name collisions between users on resources created, you create a timestamp for each instance session, and append the timestamp onto the name of resources you create in this tutorial."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "timestamp"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from datetime import datetime\n",
|
||||
"\n",
|
||||
"TIMESTAMP = datetime.now().strftime(\"%Y%m%d%H%M%S\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "77c385f0db59"
|
||||
},
|
||||
"source": [
|
||||
"### Authenticate your Google Cloud account\n",
|
||||
"\n",
|
||||
"**If you are using Vertex AI Workbench Notebooks**, your environment is already authenticated. Skip this step.\n",
|
||||
"\n",
|
||||
"**If you are using Colab**, run the cell below and follow the instructions when prompted to authenticate your account via oAuth.\n",
|
||||
"\n",
|
||||
"**Otherwise**, follow these steps:\n",
|
||||
"\n",
|
||||
"In the Cloud Console, go to the [Create service account key](https://console.cloud.google.com/apis/credentials/serviceaccountkey) page.\n",
|
||||
"\n",
|
||||
"1. **Click Create service account**.\n",
|
||||
"\n",
|
||||
"2. In the **Service account name** field, enter a name, and click **Create**.\n",
|
||||
"\n",
|
||||
"3. In the **Grant this service account access to project** section, click the Role drop-down list. Type \"Vertex AI\" into the filter box, and select **Vertex AI Administrator**. Type \"Storage Object Admin\" into the filter box, and select **Storage Object Admin**.\n",
|
||||
"\n",
|
||||
"4. Click Create. A JSON file that contains your key downloads to your local environment.\n",
|
||||
"\n",
|
||||
"5. Enter the path to your service account key as the GOOGLE_APPLICATION_CREDENTIALS variable in the cell below and run the cell."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "535223fa4b84"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# If you are running this notebook in Colab, run this cell and follow the\n",
|
||||
"# instructions to authenticate your GCP account. This provides access to your\n",
|
||||
"# Cloud Storage bucket and lets you submit training jobs and prediction\n",
|
||||
"# requests.\n",
|
||||
"\n",
|
||||
"import os\n",
|
||||
"import sys\n",
|
||||
"\n",
|
||||
"# If on Vertex AI Workbench, then don't execute this code\n",
|
||||
"IS_COLAB = False\n",
|
||||
"if not os.path.exists(\"/opt/deeplearning/metadata/env_version\") and not os.getenv(\n",
|
||||
" \"DL_ANACONDA_HOME\"\n",
|
||||
"):\n",
|
||||
" if \"google.colab\" in sys.modules:\n",
|
||||
" IS_COLAB = True\n",
|
||||
" from google.colab import auth as google_auth\n",
|
||||
"\n",
|
||||
" google_auth.authenticate_user()\n",
|
||||
"\n",
|
||||
" # If you are running this notebook locally, replace the string below with the\n",
|
||||
" # path to your service account key and run this cell to authenticate your GCP\n",
|
||||
" # account.\n",
|
||||
" elif not os.getenv(\"IS_TESTING\"):\n",
|
||||
" %env GOOGLE_APPLICATION_CREDENTIALS ''"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "bucket:custom"
|
||||
},
|
||||
"source": [
|
||||
"### Create a Cloud Storage bucket\n",
|
||||
"\n",
|
||||
"**The following steps are required, regardless of your notebook environment.**\n",
|
||||
"\n",
|
||||
"When you submit a custom training job using the Vertex SDK, you upload a Python package\n",
|
||||
"containing your training code to a Cloud Storage bucket. Vertex AI runs\n",
|
||||
"the code from this package. In this tutorial, Vertex AI also saves the\n",
|
||||
"trained model that results from your job in the same bucket. You can then\n",
|
||||
"create an `Endpoint` resource based on this output in order to serve\n",
|
||||
"online predictions.\n",
|
||||
"\n",
|
||||
"Set the name of your Cloud Storage bucket below. Bucket names must be globally unique across all Google Cloud projects, including those outside of your organization."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "bucket"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"BUCKET_NAME = \"[your-bucket-name]\" # @param {type:\"string\"}\n",
|
||||
"BUCKET_URI = f\"gs://{BUCKET_NAME}\""
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "autoset_bucket"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"if BUCKET_NAME == \"\" or BUCKET_NAME is None or BUCKET_NAME == \"[your-bucket-name]\":\n",
|
||||
" BUCKET_NAME = PROJECT_ID + \"aip-\" + TIMESTAMP\n",
|
||||
" BUCKET_URI = \"gs://\" + BUCKET_NAME"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "create_bucket"
|
||||
},
|
||||
"source": [
|
||||
"**Only if your bucket doesn't already exist**: Run the following cell to create your Cloud Storage bucket."
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "create_bucket"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! gsutil mb -l $REGION $BUCKET_URI"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {
|
||||
"id": "validate_bucket"
|
||||
},
|
||||
"source": [
|
||||
"Finally, validate access to your Cloud Storage bucket by examining its contents:"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"id": "validate_bucket"
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"! gsutil ls -al $BUCKET_URI"
|
||||
"%load setup.md "
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -1319,7 +1078,7 @@
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"delete_storage = True\n",
|
||||
"delete_storage = False\n",
|
||||
"\n",
|
||||
"if delete_storage or os.getenv(\"IS_TESTING\"):\n",
|
||||
" if \"BUCKET_URI\" in globals():\n",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user